/******************************************************************************* * Copyright (c) 2000, 2003 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package net.sourceforge.phpdt.internal.compiler.lookup; import net.sourceforge.phpdt.core.compiler.CharOperation; import net.sourceforge.phpdt.internal.compiler.ast.CompilationUnitDeclaration; import net.sourceforge.phpdt.internal.compiler.env.IBinaryType; import net.sourceforge.phpdt.internal.compiler.env.INameEnvironment; import net.sourceforge.phpdt.internal.compiler.env.NameEnvironmentAnswer; import net.sourceforge.phpdt.internal.compiler.impl.ITypeRequestor; import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter; import net.sourceforge.phpdt.internal.compiler.util.HashtableOfPackage; import net.sourceforge.phpdt.internal.compiler.util.Util; public class LookupEnvironment implements BaseTypes, ProblemReasons, TypeConstants { // public CompilerOptions options; public ProblemReporter problemReporter; public ITypeRequestor typeRequestor; PackageBinding defaultPackage; ImportBinding[] defaultImports; HashtableOfPackage knownPackages; static final ProblemPackageBinding TheNotFoundPackage = new ProblemPackageBinding( CharOperation.NO_CHAR, NotFound); static final ProblemReferenceBinding TheNotFoundType = new ProblemReferenceBinding( CharOperation.NO_CHAR, NotFound); private INameEnvironment nameEnvironment; private MethodVerifier verifier; private ArrayBinding[][] uniqueArrayBindings; private CompilationUnitDeclaration[] units = new CompilationUnitDeclaration[4]; private int lastUnitIndex = -1; private int lastCompletedUnitIndex = -1; // 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; final static int BUILD_TYPE_HIERARCHY = 1; final static int CHECK_AND_SET_IMPORTS = 2; final static int CONNECT_TYPE_HIERARCHY = 3; final static int BUILD_FIELDS_AND_METHODS = 4; public LookupEnvironment(ITypeRequestor typeRequestor, ProblemReporter problemReporter, INameEnvironment nameEnvironment) { // 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 } /* * Ask the oracle 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)); else if (answer.isCompilationUnit()) // the type was found as a .java file, try to build it then search // the cache typeRequestor.accept(answer.getCompilationUnit()); else if (answer.isSourceType()) // the type was found as a source model typeRequestor.accept(answer.getSourceTypes(), computePackageFrom(compoundName)); 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); else if (answer.isCompilationUnit()) // the type was found as a .java file, try to build it then search // the cache typeRequestor.accept(answer.getCompilationUnit()); else if (answer.isSourceType()) // the type was found as a source model typeRequestor.accept(answer.getSourceTypes(), packageBinding); 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) { CompilationUnitScope scope = new CompilationUnitScope(unit, this); scope.buildTypeBindings(); 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) { return cacheBinaryType(binaryType, true); } /* * 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) { 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); 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 = lastCompletedUnitIndex + 1; i <= lastUnitIndex; i++) { // units[i].scope.checkAndSetImports(); // } stepCompleted = CHECK_AND_SET_IMPORTS; // for (int i = lastCompletedUnitIndex + 1; i <= lastUnitIndex; i++) { // units[i].scope.connectTypeHierarchy(); // } stepCompleted = CONNECT_TYPE_HIERARCHY; for (int i = lastCompletedUnitIndex + 1; i <= lastUnitIndex; i++) { // units[i].scope.buildFieldsAndMethods(); units[i] = null; // release unnecessary reference to the parsed // unit } stepCompleted = BUILD_FIELDS_AND_METHODS; lastCompletedUnitIndex = lastUnitIndex; } /* * 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) // parsedUnit.scope.checkAndSetImports(); if (stepCompleted >= CONNECT_TYPE_HIERARCHY) parsedUnit.scope.connectTypeHierarchy(); } } /* * 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 parsedUnit.scope.checkAndSetImports(); parsedUnit.scope.connectTypeHierarchy(); if (buildFieldsAndMethods) parsedUnit.scope.buildFieldsAndMethods(); } 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. */ 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); 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); } public BinaryTypeBinding createBinaryTypeFrom(IBinaryType binaryType, PackageBinding packageBinding) { return createBinaryTypeFrom(binaryType, packageBinding, true); } public BinaryTypeBinding createBinaryTypeFrom(IBinaryType binaryType, PackageBinding packageBinding, boolean needFieldsAndMethods) { 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) { if (cachedType.isBinaryBinding()) // sanity check before the // cast... at this point the // cache should ONLY contain // unresolved types return (BinaryTypeBinding) cachedType; UnresolvedReferenceBinding unresolvedType = (UnresolvedReferenceBinding) cachedType; unresolvedType.resolvedType = binaryBinding; updateArrayCache(unresolvedType, binaryBinding); } packageBinding.addType(binaryBinding); binaryBinding.cachePartsFrom(binaryType, needFieldsAndMethods); return binaryBinding; } /* * Used to create packages from the package statement. */ PackageBinding createPackage(char[][] compoundName) { return null; // 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; } /* * 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; else 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 oracle 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; if (referenceBinding instanceof UnresolvedReferenceBinding) referenceBinding = ((UnresolvedReferenceBinding) referenceBinding) .resolve(this); // compoundName refers to a nested type incorrectly (for example, // package1.A$B) if (referenceBinding.isNestedType()) return new ProblemReferenceBinding(compoundName, InternalNameProvided); else return referenceBinding; } /* * 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) { 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 } 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) { 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: throw new Error( Util .bind( "error.undefinedBaseType", String.valueOf(signature[start]))); //$NON-NLS-1$ } } else { binding = getTypeFromConstantPoolName(signature, start + 1, end); } if (dimension == 0) return binding; else return createArrayType(binding, dimension); } /* * 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); else 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 = new MethodVerifier(this); return verifier; } public void reset() { this.defaultPackage = new PackageBinding(this); // assume the default // package always exists this.defaultImports = null; this.knownPackages = new HashtableOfPackage(); this.verifier = null; for (int i = this.uniqueArrayBindings.length; --i >= 0;) this.uniqueArrayBindings[i] = null; this.uniqueArrayBindings[0] = new ArrayBinding[50]; // start off the // most common 1 // dimension array @ // 50 for (int i = this.units.length; --i >= 0;) this.units[i] = null; this.lastUnitIndex = -1; this.lastCompletedUnitIndex = -1; // name environment has a longer life cycle, and must be reset in // the code which created it. } void updateArrayCache(UnresolvedReferenceBinding unresolvedType, ReferenceBinding resolvedType) { nextDimension: for (int i = 0, length = uniqueArrayBindings.length; i < length; i++) { ArrayBinding[] arrayBindings = uniqueArrayBindings[i]; if (arrayBindings != null) { for (int j = 0, max = arrayBindings.length; j < max; j++) { ArrayBinding currentBinding = arrayBindings[j]; if (currentBinding == null) continue nextDimension; if (currentBinding.leafComponentType == unresolvedType) { currentBinding.leafComponentType = resolvedType; continue nextDimension; } } } } } }