/******************************************************************************* * Copyright (c) 2005, 2014 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 * Stephan Herrmann - Contribution for * Bug 425183 - [1.8][inference] make CaptureBinding18 safe *******************************************************************************/ package org.eclipse.jdt.internal.core.util; import java.util.ArrayList; import org.eclipse.jdt.core.Signature; 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.CaptureBinding18; 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.PolymorphicMethodBinding; 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.TagBits; 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; @SuppressWarnings({"rawtypes", "unchecked"}) 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 consumeCapture18ID(int id, int position) { consumeAnyCapture(id, position); } public void consumeCapture(final int position) { consumeAnyCapture(-1, position); } public void consumeAnyCapture(final int capture18id, final int position) { CompilationUnitDeclaration outerParsedUnit = this.outerMostParsedUnit == null ? this.parsedUnit : this.outerMostParsedUnit; if (outerParsedUnit == null) return; final Binding wildcardBinding = this.types.size() > 0 // 0 may happen for CaptureBinding18 ? ((BindingKeyResolver) this.types.get(0)).compilerBinding : null; 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) { if (captureBinding instanceof CaptureBinding18) { if (((CaptureBinding18)captureBinding).captureID != capture18id) return false; } 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; } else if ((method.tagBits & TagBits.AnnotationPolymorphicSignature) != 0) { this.typeBinding = null; char[][] typeParameters = Signature.getParameterTypes(signature); int length = typeParameters.length; TypeBinding[] parameterTypes = new TypeBinding[length]; for (int j = 0; j < length; j++) { parameterTypes[j] = getType(typeParameters[j]); } PolymorphicMethodBinding polymorphicMethod = this.environment.createPolymorphicMethod(method, parameterTypes); this.methodBinding = polymorphicMethod; this.methodBinding = this.environment.updatePolymorphicMethodReturnType( polymorphicMethod, getType(Signature.getReturnType(signature))); this.compilerBinding = this.methodBinding; return; } } } } private TypeBinding getType(char[] type) { TypeBinding binding = null; int length = type.length; switch(length) { case 1 : switch (type[0]) { case 'I' : binding = TypeBinding.INT; break; case 'Z' : binding = TypeBinding.BOOLEAN; break; case 'V' : binding = TypeBinding.VOID; break; case 'C' : binding = TypeBinding.CHAR; break; case 'D' : binding = TypeBinding.DOUBLE; break; case 'B' : binding = TypeBinding.BYTE; break; case 'F' : binding = TypeBinding.FLOAT; break; case 'J' : binding = TypeBinding.LONG; break; case 'S' : binding = TypeBinding.SHORT; break; } break; default: int dimensions = 0; int start = 0; while (type[start] == '[') { start++; dimensions++; } binding = this.environment.getType(CharOperation.splitOn('/', type, start + 1, length - 1)); if (dimensions != 0) { binding = this.environment.createArrayType(binding, dimensions); } } return binding; } 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(); } }