/******************************************************************************* * Copyright (c) 2005, 2010 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.core.util; import java.util.ArrayList; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.Compiler; import org.eclipse.jdt.internal.compiler.ast.ArrayReference; import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.CastExpression; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression; import org.eclipse.jdt.internal.compiler.ast.FieldReference; import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding; import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.CaptureBinding; import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.PackageBinding; import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding; import org.eclipse.jdt.internal.compiler.util.HashtableOfObject; public class BindingKeyResolver extends BindingKeyParser { Compiler compiler; Binding compilerBinding; char[][] compoundName; int dimension; LookupEnvironment environment; ReferenceBinding genericType; MethodBinding methodBinding; AnnotationBinding annotationBinding; char[] secondarySimpleName; CompilationUnitDeclaration parsedUnit; BlockScope scope; TypeBinding typeBinding; TypeDeclaration typeDeclaration; ArrayList types= new ArrayList(); int wildcardRank; CompilationUnitDeclaration outerMostParsedUnit; /* * A hash set of the file names of already resolved units */ HashtableOfObject resolvedUnits; private BindingKeyResolver(BindingKeyParser parser, Compiler compiler, LookupEnvironment environment, CompilationUnitDeclaration outerMostParsedUnit, HashtableOfObject parsedUnits) { super(parser); this.compiler= compiler; this.environment= environment; this.outerMostParsedUnit= outerMostParsedUnit; this.resolvedUnits= parsedUnits; } public BindingKeyResolver(String key, Compiler compiler, LookupEnvironment environment) { super(key); this.compiler= compiler; this.environment= environment; this.resolvedUnits= new HashtableOfObject(); } /* * If not already cached, computes and cache the compound name (pkg name + top level name) of this key. * Returns the package name if key is a pkg key. * Returns an empty array if malformed. * This key's scanner should be positioned on the package or type token. */ public char[][] compoundName() { return this.compoundName; } public void consumeAnnotation() { int size= this.types.size(); if (size == 0) return; Binding annotationType= ((BindingKeyResolver)this.types.get(size - 1)).compilerBinding; AnnotationBinding[] annotationBindings; if (this.compilerBinding == null && this.typeBinding instanceof ReferenceBinding) { annotationBindings= ((ReferenceBinding)this.typeBinding).getAnnotations(); } else if (this.compilerBinding instanceof MethodBinding) { annotationBindings= ((MethodBinding)this.compilerBinding).getAnnotations(); } else if (this.compilerBinding instanceof VariableBinding) { annotationBindings= ((VariableBinding)this.compilerBinding).getAnnotations(); } else { return; } for (int i= 0, length= annotationBindings.length; i < length; i++) { AnnotationBinding binding= annotationBindings[i]; if (binding.getAnnotationType() == annotationType) { this.annotationBinding= binding; break; } } } public void consumeArrayDimension(char[] brakets) { this.dimension= brakets.length; } public void consumeBaseType(char[] baseTypeSig) { this.compoundName= new char[][] { getKey().toCharArray() }; TypeBinding baseTypeBinding= getBaseTypeBinding(baseTypeSig); if (baseTypeBinding != null) { this.typeBinding= baseTypeBinding; } } public void consumeCapture(final int position) { CompilationUnitDeclaration outerParsedUnit= this.outerMostParsedUnit == null ? this.parsedUnit : this.outerMostParsedUnit; if (outerParsedUnit == null) return; final Binding wildcardBinding= ((BindingKeyResolver)this.types.get(0)).compilerBinding; class CaptureFinder extends ASTVisitor { CaptureBinding capture; boolean checkType(TypeBinding binding) { if (binding == null) return false; switch (binding.kind()) { case Binding.PARAMETERIZED_TYPE: TypeBinding[] arguments= ((ParameterizedTypeBinding)binding).arguments; if (arguments == null) return false; for (int i= 0, length= arguments.length; i < length; i++) { if (checkType(arguments[i])) return true; } break; case Binding.WILDCARD_TYPE: return checkType(((WildcardBinding)binding).bound); case Binding.INTERSECTION_TYPE: if (checkType(((WildcardBinding)binding).bound)) return true; TypeBinding[] otherBounds= ((WildcardBinding)binding).otherBounds; // per construction, otherBounds is never null for (int i= 0, length= otherBounds.length; i < length; i++) { if (checkType(otherBounds[i])) return true; } break; case Binding.ARRAY_TYPE: return checkType(((ArrayBinding)binding).leafComponentType); case Binding.TYPE_PARAMETER: if (binding.isCapture()) { CaptureBinding captureBinding= (CaptureBinding)binding; if (captureBinding.position == position && captureBinding.wildcard == wildcardBinding) { this.capture= captureBinding; return true; } } break; } return false; } public boolean visit(SingleNameReference singleNameReference, BlockScope blockScope) { if (checkType(singleNameReference.resolvedType)) return false; return super.visit(singleNameReference, blockScope); } public boolean visit(QualifiedNameReference qualifiedNameReference, BlockScope blockScope) { if (checkType(qualifiedNameReference.resolvedType)) return false; return super.visit(qualifiedNameReference, blockScope); } public boolean visit(MessageSend messageSend, BlockScope blockScope) { if (checkType(messageSend.resolvedType)) return false; return super.visit(messageSend, blockScope); } public boolean visit(FieldReference fieldReference, BlockScope blockScope) { if (checkType(fieldReference.resolvedType)) return false; return super.visit(fieldReference, blockScope); } public boolean visit(ConditionalExpression conditionalExpression, BlockScope blockScope) { if (checkType(conditionalExpression.resolvedType)) return false; return super.visit(conditionalExpression, blockScope); } public boolean visit(CastExpression castExpression, BlockScope blockScope) { if (checkType(castExpression.resolvedType)) return false; return super.visit(castExpression, blockScope); } public boolean visit(Assignment assignment, BlockScope blockScope) { if (checkType(assignment.resolvedType)) return false; return super.visit(assignment, blockScope); } public boolean visit(ArrayReference arrayReference, BlockScope blockScope) { if (checkType(arrayReference.resolvedType)) return false; return super.visit(arrayReference, blockScope); } } CaptureFinder captureFinder= new CaptureFinder(); outerParsedUnit.traverse(captureFinder, outerParsedUnit.scope); this.typeBinding= captureFinder.capture; } public void consumeException() { this.types= new ArrayList(); } public void consumeField(char[] fieldName) { if (this.typeBinding == null) return; FieldBinding[] fields= ((ReferenceBinding)this.typeBinding).availableFields(); // resilience for (int i= 0, length= fields.length; i < length; i++) { FieldBinding field= fields[i]; if (CharOperation.equals(fieldName, field.name)) { this.typeBinding= null; this.compilerBinding= field; return; } } } public void consumeParameterizedGenericMethod() { if (this.methodBinding == null) return; TypeBinding[] arguments= getTypeBindingArguments(); if (arguments == null) { this.methodBinding= null; this.compilerBinding= null; return; } if (arguments.length != this.methodBinding.typeVariables().length) this.methodBinding= this.environment.createParameterizedGenericMethod(this.methodBinding, (RawTypeBinding)null); else this.methodBinding= this.environment.createParameterizedGenericMethod(this.methodBinding, arguments); this.compilerBinding= this.methodBinding; } public void consumeLocalType(char[] uniqueKey) { LocalTypeBinding[] localTypeBindings= this.parsedUnit.localTypes; for (int i= 0; i < this.parsedUnit.localTypeCount; i++) if (CharOperation.equals(uniqueKey, localTypeBindings[i].computeUniqueKey(false/*not a leaf*/))) { this.typeBinding= localTypeBindings[i]; return; } } public void consumeLocalVar(char[] varName, int occurrenceCount) { if (this.scope == null) { if (this.methodBinding == null) return; this.scope= this.methodBinding.sourceMethod().scope; } for (int i= 0; i < this.scope.localIndex; i++) { LocalVariableBinding local= this.scope.locals[i]; if (CharOperation.equals(local.name, varName) && occurrenceCount-- == 0) { this.methodBinding= null; this.compilerBinding= local; return; } } } public void consumeMethod(char[] selector, char[] signature) { if (this.typeBinding == null) return; MethodBinding[] methods= ((ReferenceBinding)this.typeBinding).availableMethods(); // resilience for (int i= 0, methodLength= methods.length; i < methodLength; i++) { MethodBinding method= methods[i]; if (CharOperation.equals(selector, method.selector) || (selector.length == 0 && method.isConstructor())) { char[] methodSignature= method.genericSignature(); if (methodSignature == null) methodSignature= method.signature(); if (CharOperation.equals(signature, methodSignature)) { this.typeBinding= null; this.methodBinding= method; this.compilerBinding= this.methodBinding; return; } } } } public void consumeMemberType(char[] simpleTypeName) { this.typeBinding= getTypeBinding(simpleTypeName); } public void consumePackage(char[] pkgName) { this.compoundName= CharOperation.splitOn('/', pkgName); this.compilerBinding= new PackageBinding(this.compoundName, null, this.environment); } public void consumeParameterizedType(char[] simpleTypeName, boolean isRaw) { if (this.typeBinding == null) return; TypeBinding[] arguments= getTypeBindingArguments(); if (arguments == null) { this.typeBinding= null; this.genericType= null; return; } if (simpleTypeName != null) { if (this.genericType == null) { // parameterized member type with raw enclosing type this.genericType= ((ReferenceBinding)this.typeBinding).getMemberType(simpleTypeName); } else { // parameterized member type with parameterized enclosing type this.genericType= this.genericType.getMemberType(simpleTypeName); } if (!isRaw) this.typeBinding= this.environment.createParameterizedType(this.genericType, arguments, (ReferenceBinding)this.typeBinding); else // raw type this.typeBinding= this.environment.createRawType(this.genericType, (ReferenceBinding)this.typeBinding); } else { // parameterized top level type or parameterized member type with raw enclosing type this.genericType= (ReferenceBinding)this.typeBinding; ReferenceBinding enclosing= this.genericType.enclosingType(); if (enclosing != null) enclosing= (ReferenceBinding)this.environment.convertToRawType(enclosing, false /*do not force conversion of enclosing types*/); this.typeBinding= this.environment.createParameterizedType(this.genericType, arguments, enclosing); } } public void consumeParser(BindingKeyParser parser) { this.types.add(parser); } public void consumeScope(int scopeNumber) { if (this.scope == null) { if (this.methodBinding == null) return; this.scope= this.methodBinding.sourceMethod().scope; } if (scopeNumber >= this.scope.subscopeCount) return; // malformed key this.scope= (BlockScope)this.scope.subscopes[scopeNumber]; } public void consumeRawType() { if (this.typeBinding == null) return; this.typeBinding= this.environment.convertToRawType(this.typeBinding, false /*do not force conversion of enclosing types*/); } public void consumeSecondaryType(char[] simpleTypeName) { this.secondarySimpleName= simpleTypeName; } public void consumeFullyQualifiedName(char[] fullyQualifiedName) { this.compoundName= CharOperation.splitOn('/', fullyQualifiedName); } public void consumeTopLevelType() { char[] fileName; this.parsedUnit= getCompilationUnitDeclaration(); if (this.parsedUnit != null && this.compiler != null && !this.resolvedUnits.containsKey(fileName= this.parsedUnit.getFileName())) { this.compiler.process(this.parsedUnit, this.compiler.totalUnits + 1); // unit is resolved only once thanks to the resolvedUnits protection this.resolvedUnits.put(fileName, fileName); } if (this.parsedUnit == null) { this.typeBinding= getBinaryBinding(); } else { char[] typeName= this.secondarySimpleName == null ? this.compoundName[this.compoundName.length - 1] : this.secondarySimpleName; this.typeBinding= getTypeBinding(typeName); } } public void consumeKey() { if (this.typeBinding != null) { this.typeBinding= getArrayBinding(this.dimension, this.typeBinding); this.compilerBinding= this.typeBinding; } } public void consumeTypeVariable(char[] position, char[] typeVariableName) { if (position.length > 0) { if (this.typeBinding == null) return; int pos= Integer.parseInt(new String(position)); MethodBinding[] methods= ((ReferenceBinding)this.typeBinding).availableMethods(); // resilience if (methods != null && pos < methods.length) { this.methodBinding= methods[pos]; } } TypeVariableBinding[] typeVariableBindings; if (this.methodBinding != null) { typeVariableBindings= this.methodBinding.typeVariables(); } else if (this.typeBinding != null) { typeVariableBindings= this.typeBinding.typeVariables(); } else { return; } for (int i= 0, length= typeVariableBindings.length; i < length; i++) { TypeVariableBinding typeVariableBinding= typeVariableBindings[i]; if (CharOperation.equals(typeVariableName, typeVariableBinding.sourceName())) { this.typeBinding= typeVariableBinding; return; } } } public void consumeTypeWithCapture() { BindingKeyResolver resolver= (BindingKeyResolver)this.types.get(0); this.typeBinding= (TypeBinding)resolver.compilerBinding; } public void consumeWildcardRank(int aRank) { this.wildcardRank= aRank; } public void consumeWildCard(int kind) { switch (kind) { case Wildcard.EXTENDS: case Wildcard.SUPER: BindingKeyResolver boundResolver= (BindingKeyResolver)this.types.get(0); // https://bugs.eclipse.org/bugs/show_bug.cgi?id=157847, do not allow creation of // internally inconsistent wildcards of the form '? super <null>' or '? extends <null>' final Binding boundBinding= boundResolver.compilerBinding; if (boundBinding instanceof TypeBinding) { this.typeBinding= this.environment.createWildcard((ReferenceBinding)this.typeBinding, this.wildcardRank, (TypeBinding)boundBinding, null /*no extra bound*/, kind); } else { this.typeBinding= null; } break; case Wildcard.UNBOUND: this.typeBinding= this.environment.createWildcard((ReferenceBinding)this.typeBinding, this.wildcardRank, null/*no bound*/, null /*no extra bound*/, kind); break; } } public AnnotationBinding getAnnotationBinding() { return this.annotationBinding; } /* * If the given dimension is greater than 0 returns an array binding for the given type binding. * Otherwise return the given type binding. * Returns null if the given type binding is null. */ private TypeBinding getArrayBinding(int dim, TypeBinding binding) { if (binding == null) return null; if (dim == 0) return binding; return this.environment.createArrayType(binding, dim); } private TypeBinding getBaseTypeBinding(char[] signature) { switch (signature[0]) { case 'I': return TypeBinding.INT; case 'Z': return TypeBinding.BOOLEAN; case 'V': return TypeBinding.VOID; case 'C': return TypeBinding.CHAR; case 'D': return TypeBinding.DOUBLE; case 'B': return TypeBinding.BYTE; case 'F': return TypeBinding.FLOAT; case 'J': return TypeBinding.LONG; case 'S': return TypeBinding.SHORT; case 'N': return TypeBinding.NULL; default: return null; } } /* * Returns a binary binding corresonding to this key's compound name. * Returns null if not found. */ private TypeBinding getBinaryBinding() { if (this.compoundName.length == 0) return null; return this.environment.getType(this.compoundName); } /* * Finds the compilation unit declaration corresponding to the key in the given lookup environment. * Returns null if no compilation unit declaration could be found. * This key's scanner should be positioned on the package token. */ public CompilationUnitDeclaration getCompilationUnitDeclaration() { char[][] name= this.compoundName; if (name.length == 0) return null; if (this.environment == null) return null; ReferenceBinding binding= this.environment.getType(name); if (!(binding instanceof SourceTypeBinding)) { if (this.secondarySimpleName == null) return null; // case of a secondary type with no primary type (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=177115) int length= name.length; System.arraycopy(name, 0, name= new char[length][], 0, length - 1); name[length - 1]= this.secondarySimpleName; binding= this.environment.getType(name); if (!(binding instanceof SourceTypeBinding)) return null; } SourceTypeBinding sourceTypeBinding= (SourceTypeBinding)binding; if (sourceTypeBinding.scope == null) return null; return sourceTypeBinding.scope.compilationUnitScope().referenceContext; } /* * Returns the compiler binding corresponding to this key. * Returns null is malformed. * This key's scanner should be positioned on the package token. */ public Binding getCompilerBinding() { try { parse(); return this.compilerBinding; } catch (RuntimeException e) { Util.log(e, "Could not create binding from binding key: " + getKey()); //$NON-NLS-1$ return null; } } private TypeBinding getTypeBinding(char[] simpleTypeName) { if (this.typeBinding instanceof ReferenceBinding) { return ((ReferenceBinding)this.typeBinding).getMemberType(simpleTypeName); } TypeDeclaration[] typeDeclarations= this.typeDeclaration == null ? (this.parsedUnit == null ? null : this.parsedUnit.types) : this.typeDeclaration.memberTypes; if (typeDeclarations == null) return null; for (int i= 0, length= typeDeclarations.length; i < length; i++) { TypeDeclaration declaration= typeDeclarations[i]; if (CharOperation.equals(simpleTypeName, declaration.name)) { this.typeDeclaration= declaration; return declaration.binding; } } return null; } private TypeBinding[] getTypeBindingArguments() { int size= this.types.size(); TypeBinding[] arguments= new TypeBinding[size]; for (int i= 0; i < size; i++) { BindingKeyResolver resolver= (BindingKeyResolver)this.types.get(i); TypeBinding compilerBinding2= (TypeBinding)resolver.compilerBinding; if (compilerBinding2 == null) { this.types= new ArrayList(); return null; } arguments[i]= compilerBinding2; } this.types= new ArrayList(); return arguments; } public void malformedKey() { this.compoundName= CharOperation.NO_CHAR_CHAR; } public BindingKeyParser newParser() { return new BindingKeyResolver(this, this.compiler, this.environment, this.outerMostParsedUnit == null ? this.parsedUnit : this.outerMostParsedUnit, this.resolvedUnits); } public String toString() { return getKey(); } }