| /******************************************************************************* |
| * Copyright (c) 2000, 2005 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.compiler.lookup; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.Wildcard; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.env.*; |
| import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
| import org.eclipse.jdt.internal.compiler.impl.ITypeRequestor; |
| import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; |
| import org.eclipse.jdt.internal.compiler.util.HashtableOfPackage; |
| import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; |
| |
| public class LookupEnvironment implements BaseTypes, ProblemReasons, TypeConstants { |
| final static int BUILD_FIELDS_AND_METHODS = 4; |
| final static int BUILD_TYPE_HIERARCHY = 1; |
| final static int CHECK_AND_SET_IMPORTS = 2; |
| final static int CONNECT_TYPE_HIERARCHY = 3; |
| static final ProblemPackageBinding TheNotFoundPackage = new ProblemPackageBinding(CharOperation.NO_CHAR, NotFound); |
| static final ProblemReferenceBinding TheNotFoundType = new ProblemReferenceBinding(CharOperation.NO_CHAR, NotFound); |
| |
| /** |
| * Map from typeBinding -> accessRestriction rule |
| */ |
| private Map accessRestrictions; |
| ImportBinding[] defaultImports; |
| |
| PackageBinding defaultPackage; |
| HashtableOfPackage knownPackages; |
| private int lastCompletedUnitIndex = -1; |
| private int lastUnitIndex = -1; |
| |
| public INameEnvironment nameEnvironment; |
| public CompilerOptions options; |
| public ProblemReporter problemReporter; |
| |
| // shared byte[]'s used by ClassFile to avoid allocating MBs during a build |
| public boolean sharedArraysUsed = true; // set to false once actual arrays are allocated |
| public byte[] sharedClassFileContents = null; |
| public byte[] sharedClassFileHeader = null; |
| |
| // indicate in which step on the compilation we are. |
| // step 1 : build the reference binding |
| // step 2 : conect the hierarchy (connect bindings) |
| // step 3 : build fields and method bindings. |
| private int stepCompleted; |
| public ITypeRequestor typeRequestor; |
| private ArrayBinding[][] uniqueArrayBindings; |
| private SimpleLookupTable uniqueParameterizedTypeBindings; |
| private SimpleLookupTable uniqueRawTypeBindings; |
| private SimpleLookupTable uniqueWildcardBindings; |
| |
| public CompilationUnitDeclaration unitBeingCompleted = null; // only set while completing units |
| |
| private CompilationUnitDeclaration[] units = new CompilationUnitDeclaration[4]; |
| private MethodVerifier verifier; |
| |
| public LookupEnvironment(ITypeRequestor typeRequestor, CompilerOptions options, ProblemReporter problemReporter, INameEnvironment nameEnvironment) { |
| this.typeRequestor = typeRequestor; |
| this.options = options; |
| this.problemReporter = problemReporter; |
| this.defaultPackage = new PackageBinding(this); // assume the default package always exists |
| this.defaultImports = null; |
| this.nameEnvironment = nameEnvironment; |
| this.knownPackages = new HashtableOfPackage(); |
| this.uniqueArrayBindings = new ArrayBinding[5][]; |
| this.uniqueArrayBindings[0] = new ArrayBinding[50]; // start off the most common 1 dimension array @ 50 |
| this.uniqueParameterizedTypeBindings = new SimpleLookupTable(3); |
| this.uniqueRawTypeBindings = new SimpleLookupTable(3); |
| this.uniqueWildcardBindings = new SimpleLookupTable(3); |
| this.accessRestrictions = new HashMap(3); |
| } |
| |
| /** |
| * Ask the name environment for a type which corresponds to the compoundName. |
| * Answer null if the name cannot be found. |
| */ |
| |
| public ReferenceBinding askForType(char[][] compoundName) { |
| NameEnvironmentAnswer answer = nameEnvironment.findType(compoundName); |
| if (answer == null) |
| return null; |
| |
| if (answer.isBinaryType()) |
| // the type was found as a .class file |
| typeRequestor.accept(answer.getBinaryType(), computePackageFrom(compoundName), answer.getAccessRestriction()); |
| else if (answer.isCompilationUnit()) |
| // the type was found as a .java file, try to build it then search the cache |
| typeRequestor.accept(answer.getCompilationUnit(), answer.getAccessRestriction()); |
| else if (answer.isSourceType()) |
| // the type was found as a source model |
| typeRequestor.accept(answer.getSourceTypes(), computePackageFrom(compoundName), answer.getAccessRestriction()); |
| |
| return getCachedType(compoundName); |
| } |
| /* Ask the oracle for a type named name in the packageBinding. |
| * Answer null if the name cannot be found. |
| */ |
| |
| ReferenceBinding askForType(PackageBinding packageBinding, char[] name) { |
| if (packageBinding == null) { |
| if (defaultPackage == null) |
| return null; |
| packageBinding = defaultPackage; |
| } |
| NameEnvironmentAnswer answer = nameEnvironment.findType(name, packageBinding.compoundName); |
| if (answer == null) |
| return null; |
| |
| if (answer.isBinaryType()) |
| // the type was found as a .class file |
| typeRequestor.accept(answer.getBinaryType(), packageBinding, answer.getAccessRestriction()); |
| else if (answer.isCompilationUnit()) |
| // the type was found as a .java file, try to build it then search the cache |
| typeRequestor.accept(answer.getCompilationUnit(), answer.getAccessRestriction()); |
| else if (answer.isSourceType()) |
| // the type was found as a source model |
| typeRequestor.accept(answer.getSourceTypes(), packageBinding, answer.getAccessRestriction()); |
| |
| return packageBinding.getType0(name); |
| } |
| /* Create the initial type bindings for the compilation unit. |
| * |
| * See completeTypeBindings() for a description of the remaining steps |
| * |
| * NOTE: This method can be called multiple times as additional source files are needed |
| */ |
| |
| public void buildTypeBindings(CompilationUnitDeclaration unit, AccessRestriction accessRestriction) { |
| CompilationUnitScope scope = new CompilationUnitScope(unit, this); |
| scope.buildTypeBindings(accessRestriction); |
| |
| int unitsLength = units.length; |
| if (++lastUnitIndex >= unitsLength) |
| System.arraycopy(units, 0, units = new CompilationUnitDeclaration[2 * unitsLength], 0, unitsLength); |
| units[lastUnitIndex] = unit; |
| } |
| /* Cache the binary type since we know it is needed during this compile. |
| * |
| * Answer the created BinaryTypeBinding or null if the type is already in the cache. |
| */ |
| |
| public BinaryTypeBinding cacheBinaryType(IBinaryType binaryType, AccessRestriction accessRestriction) { |
| return cacheBinaryType(binaryType, true, accessRestriction); |
| } |
| /* Cache the binary type since we know it is needed during this compile. |
| * |
| * Answer the created BinaryTypeBinding or null if the type is already in the cache. |
| */ |
| |
| public BinaryTypeBinding cacheBinaryType(IBinaryType binaryType, boolean needFieldsAndMethods, AccessRestriction accessRestriction) { |
| char[][] compoundName = CharOperation.splitOn('/', binaryType.getName()); |
| ReferenceBinding existingType = getCachedType(compoundName); |
| |
| if (existingType == null || existingType instanceof UnresolvedReferenceBinding) |
| // only add the binary type if its not already in the cache |
| return createBinaryTypeFrom(binaryType, computePackageFrom(compoundName), needFieldsAndMethods, accessRestriction); |
| return null; // the type already exists & can be retrieved from the cache |
| } |
| /* |
| * 1. Connect the type hierarchy for the type bindings created for parsedUnits. |
| * 2. Create the field bindings |
| * 3. Create the method bindings |
| */ |
| |
| /* We know each known compilationUnit is free of errors at this point... |
| * |
| * Each step will create additional bindings unless a problem is detected, in which |
| * case either the faulty import/superinterface/field/method will be skipped or a |
| * suitable replacement will be substituted (such as Object for a missing superclass) |
| */ |
| |
| public void completeTypeBindings() { |
| stepCompleted = BUILD_TYPE_HIERARCHY; |
| |
| for (int i = this.lastCompletedUnitIndex + 1; i <= this.lastUnitIndex; i++) { |
| (this.unitBeingCompleted = this.units[i]).scope.checkAndSetImports(); |
| } |
| stepCompleted = CHECK_AND_SET_IMPORTS; |
| |
| for (int i = this.lastCompletedUnitIndex + 1; i <= this.lastUnitIndex; i++) { |
| (this.unitBeingCompleted = this.units[i]).scope.connectTypeHierarchy(); |
| } |
| stepCompleted = CONNECT_TYPE_HIERARCHY; |
| |
| for (int i = this.lastCompletedUnitIndex + 1; i <= this.lastUnitIndex; i++) { |
| (this.unitBeingCompleted = this.units[i]).scope.buildFieldsAndMethods(); |
| this.units[i] = null; // release unnecessary reference to the parsed unit |
| } |
| stepCompleted = BUILD_FIELDS_AND_METHODS; |
| this.lastCompletedUnitIndex = this.lastUnitIndex; |
| this.unitBeingCompleted = null; |
| } |
| /* |
| * 1. Connect the type hierarchy for the type bindings created for parsedUnits. |
| * 2. Create the field bindings |
| * 3. Create the method bindings |
| */ |
| |
| /* |
| * Each step will create additional bindings unless a problem is detected, in which |
| * case either the faulty import/superinterface/field/method will be skipped or a |
| * suitable replacement will be substituted (such as Object for a missing superclass) |
| */ |
| |
| public void completeTypeBindings(CompilationUnitDeclaration parsedUnit) { |
| if (stepCompleted == BUILD_FIELDS_AND_METHODS) { |
| // This can only happen because the original set of units are completely built and |
| // are now being processed, so we want to treat all the additional units as a group |
| // until they too are completely processed. |
| completeTypeBindings(); |
| } else { |
| if (parsedUnit.scope == null) return; // parsing errors were too severe |
| |
| if (stepCompleted >= CHECK_AND_SET_IMPORTS) |
| (this.unitBeingCompleted = parsedUnit).scope.checkAndSetImports(); |
| |
| if (stepCompleted >= CONNECT_TYPE_HIERARCHY) |
| (this.unitBeingCompleted = parsedUnit).scope.connectTypeHierarchy(); |
| |
| this.unitBeingCompleted = null; |
| } |
| } |
| /* |
| * Used by other compiler tools which do not start by calling completeTypeBindings(). |
| * |
| * 1. Connect the type hierarchy for the type bindings created for parsedUnits. |
| * 2. Create the field bindings |
| * 3. Create the method bindings |
| */ |
| |
| public void completeTypeBindings(CompilationUnitDeclaration parsedUnit, boolean buildFieldsAndMethods) { |
| if (parsedUnit.scope == null) return; // parsing errors were too severe |
| |
| (this.unitBeingCompleted = parsedUnit).scope.checkAndSetImports(); |
| parsedUnit.scope.connectTypeHierarchy(); |
| if (buildFieldsAndMethods) |
| parsedUnit.scope.buildFieldsAndMethods(); |
| this.unitBeingCompleted = null; |
| } |
| public TypeBinding computeBoxingType(TypeBinding type) { |
| TypeBinding boxedType; |
| switch (type.id) { |
| case TypeIds.T_JavaLangBoolean : |
| return BooleanBinding; |
| case TypeIds.T_JavaLangByte : |
| return ByteBinding; |
| case TypeIds.T_JavaLangCharacter : |
| return CharBinding; |
| case TypeIds.T_JavaLangShort : |
| return ShortBinding; |
| case TypeIds.T_JavaLangDouble : |
| return DoubleBinding; |
| case TypeIds.T_JavaLangFloat : |
| return FloatBinding; |
| case TypeIds.T_JavaLangInteger : |
| return IntBinding; |
| case TypeIds.T_JavaLangLong : |
| return LongBinding; |
| |
| case TypeIds.T_int : |
| boxedType = getType(JAVA_LANG_INTEGER); |
| if (boxedType != null) return boxedType; |
| return new ProblemReferenceBinding( JAVA_LANG_INTEGER, NotFound); |
| case TypeIds.T_byte : |
| boxedType = getType(JAVA_LANG_BYTE); |
| if (boxedType != null) return boxedType; |
| return new ProblemReferenceBinding( JAVA_LANG_BYTE, NotFound); |
| case TypeIds.T_short : |
| boxedType = getType(JAVA_LANG_SHORT); |
| if (boxedType != null) return boxedType; |
| return new ProblemReferenceBinding( JAVA_LANG_SHORT, NotFound); |
| case TypeIds.T_char : |
| boxedType = getType(JAVA_LANG_CHARACTER); |
| if (boxedType != null) return boxedType; |
| return new ProblemReferenceBinding( JAVA_LANG_CHARACTER, NotFound); |
| case TypeIds.T_long : |
| boxedType = getType(JAVA_LANG_LONG); |
| if (boxedType != null) return boxedType; |
| return new ProblemReferenceBinding( JAVA_LANG_LONG, NotFound); |
| case TypeIds.T_float : |
| boxedType = getType(JAVA_LANG_FLOAT); |
| if (boxedType != null) return boxedType; |
| return new ProblemReferenceBinding( JAVA_LANG_FLOAT, NotFound); |
| case TypeIds.T_double : |
| boxedType = getType(JAVA_LANG_DOUBLE); |
| if (boxedType != null) return boxedType; |
| return new ProblemReferenceBinding( JAVA_LANG_DOUBLE, NotFound); |
| case TypeIds.T_boolean : |
| boxedType = getType(JAVA_LANG_BOOLEAN); |
| if (boxedType != null) return boxedType; |
| return new ProblemReferenceBinding( JAVA_LANG_BOOLEAN, NotFound); |
| } |
| // allow indirect unboxing conversion for wildcards and type parameters |
| switch (type.kind()) { |
| case Binding.WILDCARD_TYPE : |
| case Binding.TYPE_PARAMETER : |
| switch (type.erasure().id) { |
| case TypeIds.T_JavaLangBoolean : |
| return BooleanBinding; |
| case TypeIds.T_JavaLangByte : |
| return ByteBinding; |
| case TypeIds.T_JavaLangCharacter : |
| return CharBinding; |
| case TypeIds.T_JavaLangShort : |
| return ShortBinding; |
| case TypeIds.T_JavaLangDouble : |
| return DoubleBinding; |
| case TypeIds.T_JavaLangFloat : |
| return FloatBinding; |
| case TypeIds.T_JavaLangInteger : |
| return IntBinding; |
| case TypeIds.T_JavaLangLong : |
| return LongBinding; |
| } |
| } |
| return type; |
| } |
| private PackageBinding computePackageFrom(char[][] constantPoolName) { |
| if (constantPoolName.length == 1) |
| return defaultPackage; |
| |
| PackageBinding packageBinding = getPackage0(constantPoolName[0]); |
| if (packageBinding == null || packageBinding == TheNotFoundPackage) { |
| packageBinding = new PackageBinding(constantPoolName[0], this); |
| knownPackages.put(constantPoolName[0], packageBinding); |
| } |
| |
| for (int i = 1, length = constantPoolName.length - 1; i < length; i++) { |
| PackageBinding parent = packageBinding; |
| if ((packageBinding = parent.getPackage0(constantPoolName[i])) == null || packageBinding == TheNotFoundPackage) { |
| packageBinding = new PackageBinding(CharOperation.subarray(constantPoolName, 0, i + 1), parent, this); |
| parent.addPackage(packageBinding); |
| } |
| } |
| return packageBinding; |
| } |
| |
| /* Used to guarantee array type identity. |
| */ |
| public ArrayBinding createArrayType(TypeBinding type, int dimensionCount) { |
| if (type instanceof LocalTypeBinding) // cache local type arrays with the local type itself |
| return ((LocalTypeBinding) type).createArrayType(dimensionCount); |
| |
| // find the array binding cache for this dimension |
| int dimIndex = dimensionCount - 1; |
| int length = uniqueArrayBindings.length; |
| ArrayBinding[] arrayBindings; |
| if (dimIndex < length) { |
| if ((arrayBindings = uniqueArrayBindings[dimIndex]) == null) |
| uniqueArrayBindings[dimIndex] = arrayBindings = new ArrayBinding[10]; |
| } else { |
| System.arraycopy( |
| uniqueArrayBindings, 0, |
| uniqueArrayBindings = new ArrayBinding[dimensionCount][], 0, |
| length); |
| uniqueArrayBindings[dimIndex] = arrayBindings = new ArrayBinding[10]; |
| } |
| |
| // find the cached array binding for this leaf component type (if any) |
| int index = -1; |
| length = arrayBindings.length; |
| while (++index < length) { |
| ArrayBinding currentBinding = arrayBindings[index]; |
| if (currentBinding == null) // no matching array, but space left |
| return arrayBindings[index] = new ArrayBinding(type, dimensionCount, this); |
| if (currentBinding.leafComponentType == type) |
| return currentBinding; |
| } |
| |
| // no matching array, no space left |
| System.arraycopy( |
| arrayBindings, 0, |
| (arrayBindings = new ArrayBinding[length * 2]), 0, |
| length); |
| uniqueArrayBindings[dimIndex] = arrayBindings; |
| return arrayBindings[length] = new ArrayBinding(type, dimensionCount, this); |
| } |
| public BinaryTypeBinding createBinaryTypeFrom(IBinaryType binaryType, PackageBinding packageBinding, AccessRestriction accessRestriction) { |
| return createBinaryTypeFrom(binaryType, packageBinding, true, accessRestriction); |
| } |
| public BinaryTypeBinding createBinaryTypeFrom(IBinaryType binaryType, PackageBinding packageBinding, boolean needFieldsAndMethods, AccessRestriction accessRestriction) { |
| BinaryTypeBinding binaryBinding = new BinaryTypeBinding(packageBinding, binaryType, this); |
| |
| // resolve any array bindings which reference the unresolvedType |
| ReferenceBinding cachedType = packageBinding.getType0(binaryBinding.compoundName[binaryBinding.compoundName.length - 1]); |
| if (cachedType != null) { // update reference to unresolved binding after having read classfile (knows whether generic for raw conversion) |
| // TODO (kent) suspect the check below is no longer required, since we should not be requesting a binary which is already in the cache |
| if (cachedType.isBinaryBinding()) // sanity check before the cast... at this point the cache should ONLY contain unresolved types |
| return (BinaryTypeBinding) cachedType; |
| |
| ((UnresolvedReferenceBinding) cachedType).setResolvedType(binaryBinding, this); |
| } |
| |
| packageBinding.addType(binaryBinding); |
| setAccessRestriction(binaryBinding, accessRestriction); |
| binaryBinding.cachePartsFrom(binaryType, needFieldsAndMethods); |
| return binaryBinding; |
| } |
| /* Used to create packages from the package statement. |
| */ |
| |
| PackageBinding createPackage(char[][] compoundName) { |
| PackageBinding packageBinding = getPackage0(compoundName[0]); |
| if (packageBinding == null || packageBinding == TheNotFoundPackage) { |
| packageBinding = new PackageBinding(compoundName[0], this); |
| knownPackages.put(compoundName[0], packageBinding); |
| } |
| |
| for (int i = 1, length = compoundName.length; i < length; i++) { |
| // check to see if it collides with a known type... |
| // this case can only happen if the package does not exist as a directory in the file system |
| // otherwise when the source type was defined, the correct error would have been reported |
| // unless its an unresolved type which is referenced from an inconsistent class file |
| ReferenceBinding type = packageBinding.getType0(compoundName[i]); |
| if (type != null && type != TheNotFoundType && !(type instanceof UnresolvedReferenceBinding)) |
| return null; |
| |
| PackageBinding parent = packageBinding; |
| if ((packageBinding = parent.getPackage0(compoundName[i])) == null || packageBinding == TheNotFoundPackage) { |
| // if the package is unknown, check to see if a type exists which would collide with the new package |
| // catches the case of a package statement of: package java.lang.Object; |
| // since the package can be added after a set of source files have already been compiled, we need |
| // whenever a package statement is encountered |
| if (nameEnvironment.findType(compoundName[i], parent.compoundName) != null) |
| return null; |
| |
| packageBinding = new PackageBinding(CharOperation.subarray(compoundName, 0, i + 1), parent, this); |
| parent.addPackage(packageBinding); |
| } |
| } |
| return packageBinding; |
| } |
| |
| public ParameterizedTypeBinding createParameterizedType(ReferenceBinding genericType, TypeBinding[] typeArguments, ReferenceBinding enclosingType) { |
| |
| // cached info is array of already created parameterized types for this type |
| ParameterizedTypeBinding[] cachedInfo = (ParameterizedTypeBinding[])this.uniqueParameterizedTypeBindings.get(genericType); |
| int argLength = typeArguments == null ? 0: typeArguments.length; |
| boolean needToGrow = false; |
| if (cachedInfo != null){ |
| nextCachedType : |
| // iterate existing parameterized for reusing one with same type arguments if any |
| for (int i = 0, max = cachedInfo.length; i < max; i++){ |
| ParameterizedTypeBinding cachedType = cachedInfo[i]; |
| if (cachedType.type != genericType) continue nextCachedType; // remain of unresolved type |
| if (cachedType.enclosingType() != enclosingType) continue nextCachedType; |
| TypeBinding[] cachedArguments = cachedType.arguments; |
| int cachedArgLength = cachedArguments == null ? 0 : cachedArguments.length; |
| if (argLength != cachedArgLength) continue nextCachedType; // would be an error situation (from unresolved binaries) |
| for (int j = 0; j < cachedArgLength; j++){ |
| if (typeArguments[j] != cachedArguments[j]) continue nextCachedType; |
| } |
| // all arguments match, reuse current |
| return cachedType; |
| } |
| needToGrow = true; |
| } else { |
| cachedInfo = new ParameterizedTypeBinding[1]; |
| this.uniqueParameterizedTypeBindings.put(genericType, cachedInfo); |
| } |
| // grow cache ? |
| if (needToGrow){ |
| int length = cachedInfo.length; |
| System.arraycopy(cachedInfo, 0, cachedInfo = new ParameterizedTypeBinding[length+1], 0, length); |
| this.uniqueParameterizedTypeBindings.put(genericType, cachedInfo); |
| } |
| // add new binding |
| ParameterizedTypeBinding parameterizedType = new ParameterizedTypeBinding(genericType,typeArguments, enclosingType, this); |
| cachedInfo[cachedInfo.length-1] = parameterizedType; |
| return parameterizedType; |
| } |
| |
| public RawTypeBinding createRawType(ReferenceBinding genericType, ReferenceBinding enclosingType) { |
| // cached info is array of already created raw types for this type |
| RawTypeBinding[] cachedInfo = (RawTypeBinding[])this.uniqueRawTypeBindings.get(genericType); |
| boolean needToGrow = false; |
| if (cachedInfo != null){ |
| nextCachedType : |
| // iterate existing parameterized for reusing one with same type arguments if any |
| for (int i = 0, max = cachedInfo.length; i < max; i++){ |
| RawTypeBinding cachedType = cachedInfo[i]; |
| if (cachedType.type != genericType) continue nextCachedType; // remain of unresolved type |
| if (cachedType.enclosingType() != enclosingType) continue nextCachedType; |
| // all enclosing type match, reuse current |
| return cachedType; |
| } |
| needToGrow = true; |
| } else { |
| cachedInfo = new RawTypeBinding[1]; |
| this.uniqueRawTypeBindings.put(genericType, cachedInfo); |
| } |
| // grow cache ? |
| if (needToGrow){ |
| int length = cachedInfo.length; |
| System.arraycopy(cachedInfo, 0, cachedInfo = new RawTypeBinding[length+1], 0, length); |
| this.uniqueRawTypeBindings.put(genericType, cachedInfo); |
| } |
| // add new binding |
| RawTypeBinding rawType = new RawTypeBinding(genericType, enclosingType, this); |
| cachedInfo[cachedInfo.length-1] = rawType; |
| return rawType; |
| |
| } |
| |
| public WildcardBinding createWildcard(ReferenceBinding genericType, int rank, TypeBinding bound, TypeBinding[] otherBounds, int boundKind) { |
| |
| // cached info is array of already created wildcard types for this type |
| if (genericType == null) // pseudo wildcard denoting composite bounds for lub computation |
| genericType = ReferenceBinding.LUB_GENERIC; |
| WildcardBinding[] cachedInfo = (WildcardBinding[])this.uniqueWildcardBindings.get(genericType); |
| boolean needToGrow = false; |
| if (cachedInfo != null){ |
| nextCachedType : |
| // iterate existing wildcards for reusing one with same information if any |
| for (int i = 0, max = cachedInfo.length; i < max; i++){ |
| WildcardBinding cachedType = cachedInfo[i]; |
| if (cachedType.genericType != genericType) continue nextCachedType; // remain of unresolved type |
| if (cachedType.rank != rank) continue nextCachedType; |
| if (cachedType.boundKind != boundKind) continue nextCachedType; |
| if (cachedType.bound != bound) continue nextCachedType; |
| if (cachedType.otherBounds != otherBounds) { |
| int cachedLength = cachedType.otherBounds == null ? 0 : cachedType.otherBounds.length; |
| int length = otherBounds == null ? 0 : otherBounds.length; |
| if (cachedLength != length) continue nextCachedType; |
| for (int j = 0; j < length; j++) { |
| if (cachedType.otherBounds[j] != otherBounds[j]) continue nextCachedType; |
| } |
| } |
| // all match, reuse current |
| return cachedType; |
| } |
| needToGrow = true; |
| } else { |
| cachedInfo = new WildcardBinding[1]; |
| this.uniqueWildcardBindings.put(genericType, cachedInfo); |
| } |
| // grow cache ? |
| if (needToGrow){ |
| int length = cachedInfo.length; |
| System.arraycopy(cachedInfo, 0, cachedInfo = new WildcardBinding[length+1], 0, length); |
| this.uniqueWildcardBindings.put(genericType, cachedInfo); |
| } |
| // add new binding |
| WildcardBinding wildcard = new WildcardBinding(genericType, rank, bound, otherBounds, boundKind, this); |
| cachedInfo[cachedInfo.length-1] = wildcard; |
| return wildcard; |
| } |
| |
| /** |
| * Returns the access restriction associated to a given type, or null if none |
| */ |
| public AccessRestriction getAccessRestriction(TypeBinding type) { |
| return (AccessRestriction) this.accessRestrictions.get(type); |
| } |
| |
| /** |
| * Answer the type for the compoundName if it exists in the cache. |
| * Answer theNotFoundType if it could not be resolved the first time |
| * it was looked up, otherwise answer null. |
| * |
| * NOTE: Do not use for nested types... the answer is NOT the same for a.b.C or a.b.C.D.E |
| * assuming C is a type in both cases. In the a.b.C.D.E case, null is the answer. |
| */ |
| |
| public ReferenceBinding getCachedType(char[][] compoundName) { |
| if (compoundName.length == 1) { |
| if (defaultPackage == null) |
| return null; |
| return defaultPackage.getType0(compoundName[0]); |
| } |
| |
| PackageBinding packageBinding = getPackage0(compoundName[0]); |
| if (packageBinding == null || packageBinding == TheNotFoundPackage) |
| return null; |
| |
| for (int i = 1, packageLength = compoundName.length - 1; i < packageLength; i++) |
| if ((packageBinding = packageBinding.getPackage0(compoundName[i])) == null || packageBinding == TheNotFoundPackage) |
| return null; |
| return packageBinding.getType0(compoundName[compoundName.length - 1]); |
| } |
| /* Answer the top level package named name if it exists in the cache. |
| * Answer theNotFoundPackage if it could not be resolved the first time |
| * it was looked up, otherwise answer null. |
| * |
| * NOTE: Senders must convert theNotFoundPackage into a real problem |
| * package if its to returned. |
| */ |
| |
| PackageBinding getPackage0(char[] name) { |
| return knownPackages.get(name); |
| } |
| /* Answer the top level package named name. |
| * Ask the oracle for the package if its not in the cache. |
| * Answer null if the package cannot be found. |
| */ |
| |
| PackageBinding getTopLevelPackage(char[] name) { |
| PackageBinding packageBinding = getPackage0(name); |
| if (packageBinding != null) { |
| if (packageBinding == TheNotFoundPackage) |
| return null; |
| return packageBinding; |
| } |
| |
| if (nameEnvironment.isPackage(null, name)) { |
| knownPackages.put(name, packageBinding = new PackageBinding(name, this)); |
| return packageBinding; |
| } |
| |
| knownPackages.put(name, TheNotFoundPackage); // saves asking the oracle next time |
| return null; |
| } |
| /* Answer the type corresponding to the compoundName. |
| * Ask the name environment for the type if its not in the cache. |
| * Answer null if the type cannot be found... likely a fatal error. |
| */ |
| |
| public ReferenceBinding getType(char[][] compoundName) { |
| ReferenceBinding referenceBinding; |
| |
| if (compoundName.length == 1) { |
| if (defaultPackage == null) |
| return null; |
| |
| if ((referenceBinding = defaultPackage.getType0(compoundName[0])) == null) { |
| PackageBinding packageBinding = getPackage0(compoundName[0]); |
| if (packageBinding != null && packageBinding != TheNotFoundPackage) |
| return null; // collides with a known package... should not call this method in such a case |
| referenceBinding = askForType(defaultPackage, compoundName[0]); |
| } |
| } else { |
| PackageBinding packageBinding = getPackage0(compoundName[0]); |
| if (packageBinding == TheNotFoundPackage) |
| return null; |
| |
| if (packageBinding != null) { |
| for (int i = 1, packageLength = compoundName.length - 1; i < packageLength; i++) { |
| if ((packageBinding = packageBinding.getPackage0(compoundName[i])) == null) |
| break; |
| if (packageBinding == TheNotFoundPackage) |
| return null; |
| } |
| } |
| |
| if (packageBinding == null) |
| referenceBinding = askForType(compoundName); |
| else if ((referenceBinding = packageBinding.getType0(compoundName[compoundName.length - 1])) == null) |
| referenceBinding = askForType(packageBinding, compoundName[compoundName.length - 1]); |
| } |
| |
| if (referenceBinding == null || referenceBinding == TheNotFoundType) |
| return null; |
| referenceBinding = BinaryTypeBinding.resolveType(referenceBinding, this, false); // no raw conversion for now |
| |
| // compoundName refers to a nested type incorrectly (for example, package1.A$B) |
| if (referenceBinding.isNestedType()) |
| return new ProblemReferenceBinding(compoundName, InternalNameProvided); |
| return referenceBinding; |
| } |
| private TypeBinding[] getTypeArgumentsFromSignature(SignatureWrapper wrapper, TypeVariableBinding[] staticVariables, ReferenceBinding enclosingType, ReferenceBinding genericType) { |
| java.util.ArrayList args = new java.util.ArrayList(2); |
| int rank = 0; |
| do { |
| args.add(getTypeFromVariantTypeSignature(wrapper, staticVariables, enclosingType, genericType, rank++)); |
| } while (wrapper.signature[wrapper.start] != '>'); |
| wrapper.start++; // skip '>' |
| TypeBinding[] typeArguments = new TypeBinding[args.size()]; |
| args.toArray(typeArguments); |
| return typeArguments; |
| } |
| /* Answer the type corresponding to the name from the binary file. |
| * Does not ask the oracle for the type if its not found in the cache... instead an |
| * unresolved type is returned which must be resolved before used. |
| * |
| * NOTE: Does NOT answer base types nor array types! |
| * |
| * NOTE: Aborts compilation if the class file cannot be found. |
| */ |
| |
| ReferenceBinding getTypeFromConstantPoolName(char[] signature, int start, int end, boolean isParameterized) { |
| if (end == -1) |
| end = signature.length; |
| |
| char[][] compoundName = CharOperation.splitOn('/', signature, start, end); |
| ReferenceBinding binding = getCachedType(compoundName); |
| if (binding == null) { |
| PackageBinding packageBinding = computePackageFrom(compoundName); |
| binding = new UnresolvedReferenceBinding(compoundName, packageBinding); |
| packageBinding.addType(binding); |
| } else if (binding == TheNotFoundType) { |
| problemReporter.isClassPathCorrect(compoundName, null); |
| return null; // will not get here since the above error aborts the compilation |
| } else if (!isParameterized && binding.isGenericType()) { |
| // check raw type, only for resolved types |
| binding = createRawType(binding, binding.enclosingType()); |
| } |
| return binding; |
| } |
| /* Answer the type corresponding to the signature from the binary file. |
| * Does not ask the oracle for the type if its not found in the cache... instead an |
| * unresolved type is returned which must be resolved before used. |
| * |
| * NOTE: Does answer base types & array types. |
| * |
| * NOTE: Aborts compilation if the class file cannot be found. |
| */ |
| |
| TypeBinding getTypeFromSignature(char[] signature, int start, int end, boolean isParameterized, TypeBinding enclosingType) { |
| int dimension = 0; |
| while (signature[start] == '[') { |
| start++; |
| dimension++; |
| } |
| if (end == -1) |
| end = signature.length - 1; |
| |
| // Just switch on signature[start] - the L case is the else |
| TypeBinding binding = null; |
| if (start == end) { |
| switch (signature[start]) { |
| case 'I' : |
| binding = IntBinding; |
| break; |
| case 'Z' : |
| binding = BooleanBinding; |
| break; |
| case 'V' : |
| binding = VoidBinding; |
| break; |
| case 'C' : |
| binding = CharBinding; |
| break; |
| case 'D' : |
| binding = DoubleBinding; |
| break; |
| case 'B' : |
| binding = ByteBinding; |
| break; |
| case 'F' : |
| binding = FloatBinding; |
| break; |
| case 'J' : |
| binding = LongBinding; |
| break; |
| case 'S' : |
| binding = ShortBinding; |
| break; |
| default : |
| problemReporter.corruptedSignature(enclosingType, signature, start); |
| // will never reach here, since error will cause abort |
| } |
| } else { |
| binding = getTypeFromConstantPoolName(signature, start + 1, end, isParameterized); // skip leading 'L' or 'T' |
| } |
| |
| if (dimension == 0) |
| return binding; |
| return createArrayType(binding, dimension); |
| } |
| TypeBinding getTypeFromTypeSignature(SignatureWrapper wrapper, TypeVariableBinding[] staticVariables, ReferenceBinding enclosingType) { |
| // TypeVariableSignature = 'T' Identifier ';' |
| // ArrayTypeSignature = '[' TypeSignature |
| // ClassTypeSignature = 'L' Identifier TypeArgs(optional) ';' |
| // or ClassTypeSignature '.' 'L' Identifier TypeArgs(optional) ';' |
| // TypeArgs = '<' VariantTypeSignature VariantTypeSignatures '>' |
| int dimension = 0; |
| while (wrapper.signature[wrapper.start] == '[') { |
| wrapper.start++; |
| dimension++; |
| } |
| |
| if (wrapper.signature[wrapper.start] == 'T') { |
| int varStart = wrapper.start + 1; |
| int varEnd = wrapper.computeEnd(); |
| for (int i = staticVariables.length; --i >= 0;) |
| if (CharOperation.equals(staticVariables[i].sourceName, wrapper.signature, varStart, varEnd)) |
| return dimension == 0 ? (TypeBinding) staticVariables[i] : createArrayType(staticVariables[i], dimension); |
| ReferenceBinding initialType = enclosingType; |
| do { |
| if (enclosingType instanceof BinaryTypeBinding) { // per construction can only be binary type binding |
| TypeVariableBinding[] enclosingVariables = ((BinaryTypeBinding)enclosingType).typeVariables; // do not trigger resolution of variables |
| for (int i = enclosingVariables.length; --i >= 0;) |
| if (CharOperation.equals(enclosingVariables[i].sourceName, wrapper.signature, varStart, varEnd)) |
| return dimension == 0 ? (TypeBinding) enclosingVariables[i] : createArrayType(enclosingVariables[i], dimension); |
| } |
| } while ((enclosingType = enclosingType.enclosingType()) != null); |
| problemReporter.undefinedTypeVariableSignature(CharOperation.subarray(wrapper.signature, varStart, varEnd), initialType); |
| return null; // cannot reach this, since previous problem will abort compilation |
| } |
| boolean isParameterized; |
| TypeBinding type = getTypeFromSignature(wrapper.signature, wrapper.start, wrapper.computeEnd(), isParameterized = (wrapper.end == wrapper.bracket), enclosingType); |
| if (!isParameterized) |
| return dimension == 0 ? type : createArrayType(type, dimension); |
| |
| // type must be a ReferenceBinding at this point, cannot be a BaseTypeBinding or ArrayTypeBinding |
| ReferenceBinding actualType = (ReferenceBinding) type; |
| TypeBinding[] typeArguments = getTypeArgumentsFromSignature(wrapper, staticVariables, enclosingType, actualType); |
| ParameterizedTypeBinding parameterizedType = createParameterizedType(actualType, typeArguments, null); |
| |
| while (wrapper.signature[wrapper.start] == '.') { |
| wrapper.start++; // skip '.' |
| char[] memberName = wrapper.nextWord(); |
| BinaryTypeBinding.resolveType(parameterizedType, this, false); |
| ReferenceBinding memberType = parameterizedType.type.getMemberType(memberName); |
| if (wrapper.signature[wrapper.start] == '<') { |
| wrapper.start++; // skip '<' |
| typeArguments = getTypeArgumentsFromSignature(wrapper, staticVariables, enclosingType, memberType); |
| } else { |
| typeArguments = null; |
| } |
| parameterizedType = createParameterizedType(memberType, typeArguments, parameterizedType); |
| } |
| wrapper.start++; // skip ';' |
| return dimension == 0 ? (TypeBinding) parameterizedType : createArrayType(parameterizedType, dimension); |
| } |
| TypeBinding getTypeFromVariantTypeSignature( |
| SignatureWrapper wrapper, |
| TypeVariableBinding[] staticVariables, |
| ReferenceBinding enclosingType, |
| ReferenceBinding genericType, |
| int rank) { |
| // VariantTypeSignature = '-' TypeSignature |
| // or '+' TypeSignature |
| // or TypeSignature |
| // or '*' |
| switch (wrapper.signature[wrapper.start]) { |
| case '-' : |
| // ? super aType |
| wrapper.start++; |
| TypeBinding bound = getTypeFromTypeSignature(wrapper, staticVariables, enclosingType); |
| return createWildcard(genericType, rank, bound, null /*no extra bound*/, Wildcard.SUPER); |
| case '+' : |
| // ? extends aType |
| wrapper.start++; |
| bound = getTypeFromTypeSignature(wrapper, staticVariables, enclosingType); |
| return createWildcard(genericType, rank, bound, null /*no extra bound*/, Wildcard.EXTENDS); |
| case '*' : |
| // ? |
| wrapper.start++; |
| return createWildcard(genericType, rank, null, null /*no extra bound*/, Wildcard.UNBOUND); |
| default : |
| return getTypeFromTypeSignature(wrapper, staticVariables, enclosingType); |
| } |
| } |
| public boolean isBoxingCompatibleWith(TypeBinding left, TypeBinding right) { |
| if (options.sourceLevel < ClassFileConstants.JDK1_5 || left.isBaseType() == right.isBaseType()) |
| return false; |
| |
| TypeBinding convertedType = computeBoxingType(left); |
| return convertedType == right || convertedType.isCompatibleWith(right); |
| } |
| /* Ask the oracle if a package exists named name in the package named compoundName. |
| */ |
| boolean isPackage(char[][] compoundName, char[] name) { |
| if (compoundName == null || compoundName.length == 0) |
| return nameEnvironment.isPackage(null, name); |
| return nameEnvironment.isPackage(compoundName, name); |
| } |
| // The method verifier is lazily initialized to guarantee the receiver, the compiler & the oracle are ready. |
| |
| public MethodVerifier methodVerifier() { |
| if (verifier == null) |
| verifier = this.options.complianceLevel < ClassFileConstants.JDK1_5 |
| ? new MethodVerifier(this) |
| : new MethodVerifier15(this); // check for covariance even if sourceLevel is < 1.5 |
| return verifier; |
| } |
| public void reset() { |
| this.defaultPackage = new PackageBinding(this); // assume the default package always exists |
| this.defaultImports = null; |
| this.knownPackages = new HashtableOfPackage(); |
| this.accessRestrictions = new HashMap(3); |
| |
| this.verifier = null; |
| for (int i = this.uniqueArrayBindings.length; --i >= 0;) { |
| ArrayBinding[] arrayBindings = this.uniqueArrayBindings[i]; |
| if (arrayBindings != null) |
| for (int j = arrayBindings.length; --j >= 0;) |
| arrayBindings[j] = null; |
| } |
| this.uniqueParameterizedTypeBindings = new SimpleLookupTable(3); |
| this.uniqueRawTypeBindings = new SimpleLookupTable(3); |
| this.uniqueWildcardBindings = new SimpleLookupTable(3); |
| |
| for (int i = this.units.length; --i >= 0;) |
| this.units[i] = null; |
| this.lastUnitIndex = -1; |
| this.lastCompletedUnitIndex = -1; |
| this.unitBeingCompleted = null; // in case AbortException occurred |
| |
| // name environment has a longer life cycle, and must be reset in |
| // the code which created it. |
| } |
| /** |
| * Associate a given type with some access restriction |
| * (did not store the restriction directly into binding, since sparse information) |
| */ |
| public void setAccessRestriction(ReferenceBinding type, AccessRestriction accessRestriction) { |
| if (accessRestriction == null) return; |
| type.modifiers |= CompilerModifiers.AccRestrictedAccess; |
| this.accessRestrictions.put(type, accessRestriction); |
| } |
| |
| void updateCaches(UnresolvedReferenceBinding unresolvedType, ReferenceBinding resolvedType) { |
| // walk all the unique collections & replace the unresolvedType with the resolvedType |
| // must prevent 2 entries so == still works (1 containing the unresolvedType and the other containing the resolvedType) |
| if (this.uniqueParameterizedTypeBindings.get(unresolvedType) != null) { // update the key |
| Object[] keys = this.uniqueParameterizedTypeBindings.keyTable; |
| for (int i = 0, l = keys.length; i < l; i++) { |
| if (keys[i] == unresolvedType) { |
| keys[i] = resolvedType; // hashCode is based on compoundName so this works - cannot be raw since type of parameterized type |
| break; |
| } |
| } |
| } |
| |
| if (this.uniqueWildcardBindings.get(unresolvedType) != null) { // update the key |
| Object[] keys = this.uniqueWildcardBindings.keyTable; |
| for (int i = 0, l = keys.length; i < l; i++) { |
| if (keys[i] == unresolvedType) { |
| keys[i] = resolvedType; // hashCode is based on compoundName so this works |
| break; |
| } |
| } |
| } |
| } |
| } |