1 /*******************************************************************************
2 * Copyright (c) 2000, 2003 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.lookup;
13 import net.sourceforge.phpdt.core.compiler.CharOperation;
14 import net.sourceforge.phpdt.internal.compiler.env.IBinaryType;
15 import net.sourceforge.phpdt.internal.compiler.env.INameEnvironment;
16 import net.sourceforge.phpdt.internal.compiler.env.NameEnvironmentAnswer;
17 import net.sourceforge.phpdt.internal.compiler.impl.ITypeRequestor;
18 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
19 import net.sourceforge.phpdt.internal.compiler.util.HashtableOfPackage;
20 import net.sourceforge.phpdt.internal.compiler.util.Util;
21 import net.sourceforge.phpeclipse.internal.compiler.ast.CompilationUnitDeclaration;
23 public class LookupEnvironment implements BaseTypes, ProblemReasons, TypeConstants {
24 // public CompilerOptions options;
25 public ProblemReporter problemReporter;
26 public ITypeRequestor typeRequestor;
28 PackageBinding defaultPackage;
29 ImportBinding[] defaultImports;
30 HashtableOfPackage knownPackages;
31 static final ProblemPackageBinding TheNotFoundPackage = new ProblemPackageBinding(CharOperation.NO_CHAR, NotFound);
32 static final ProblemReferenceBinding TheNotFoundType = new ProblemReferenceBinding(CharOperation.NO_CHAR, NotFound);
34 private INameEnvironment nameEnvironment;
35 private MethodVerifier verifier;
36 private ArrayBinding[][] uniqueArrayBindings;
38 private CompilationUnitDeclaration[] units = new CompilationUnitDeclaration[4];
39 private int lastUnitIndex = -1;
40 private int lastCompletedUnitIndex = -1;
42 // indicate in which step on the compilation we are.
43 // step 1 : build the reference binding
44 // step 2 : conect the hierarchy (connect bindings)
45 // step 3 : build fields and method bindings.
46 private int stepCompleted;
47 final static int BUILD_TYPE_HIERARCHY = 1;
48 final static int CHECK_AND_SET_IMPORTS = 2;
49 final static int CONNECT_TYPE_HIERARCHY = 3;
50 final static int BUILD_FIELDS_AND_METHODS = 4;
51 public LookupEnvironment(ITypeRequestor typeRequestor,ProblemReporter problemReporter, INameEnvironment nameEnvironment) {
52 // CompilerOptions options, ProblemReporter problemReporter, INameEnvironment nameEnvironment) {
53 this.typeRequestor = typeRequestor;
54 // this.options = options;
55 this.problemReporter = problemReporter;
56 this.defaultPackage = new PackageBinding(this); // assume the default package always exists
57 this.defaultImports = null;
58 this.nameEnvironment = nameEnvironment;
59 this.knownPackages = new HashtableOfPackage();
60 this.uniqueArrayBindings = new ArrayBinding[5][];
61 this.uniqueArrayBindings[0] = new ArrayBinding[50]; // start off the most common 1 dimension array @ 50
63 /* Ask the oracle for a type which corresponds to the compoundName.
64 * Answer null if the name cannot be found.
67 public ReferenceBinding askForType(char[][] compoundName) {
68 NameEnvironmentAnswer answer = nameEnvironment.findType(compoundName);
72 if (answer.isBinaryType())
73 // the type was found as a .class file
74 typeRequestor.accept(answer.getBinaryType(), computePackageFrom(compoundName));
75 else if (answer.isCompilationUnit())
76 // the type was found as a .java file, try to build it then search the cache
77 typeRequestor.accept(answer.getCompilationUnit());
78 else if (answer.isSourceType())
79 // the type was found as a source model
80 typeRequestor.accept(answer.getSourceTypes(), computePackageFrom(compoundName));
82 return getCachedType(compoundName);
84 /* Ask the oracle for a type named name in the packageBinding.
85 * Answer null if the name cannot be found.
88 ReferenceBinding askForType(PackageBinding packageBinding, char[] name) {
89 if (packageBinding == null) {
90 if (defaultPackage == null)
92 packageBinding = defaultPackage;
94 NameEnvironmentAnswer answer = nameEnvironment.findType(name, packageBinding.compoundName);
98 if (answer.isBinaryType())
99 // the type was found as a .class file
100 typeRequestor.accept(answer.getBinaryType(), packageBinding);
101 else if (answer.isCompilationUnit())
102 // the type was found as a .java file, try to build it then search the cache
103 typeRequestor.accept(answer.getCompilationUnit());
104 else if (answer.isSourceType())
105 // the type was found as a source model
106 typeRequestor.accept(answer.getSourceTypes(), packageBinding);
108 return packageBinding.getType0(name);
110 /* Create the initial type bindings for the compilation unit.
112 * See completeTypeBindings() for a description of the remaining steps
114 * NOTE: This method can be called multiple times as additional source files are needed
117 public void buildTypeBindings(CompilationUnitDeclaration unit) {
118 CompilationUnitScope scope = new CompilationUnitScope(unit, this);
119 scope.buildTypeBindings();
121 int unitsLength = units.length;
122 if (++lastUnitIndex >= unitsLength)
123 System.arraycopy(units, 0, units = new CompilationUnitDeclaration[2 * unitsLength], 0, unitsLength);
124 units[lastUnitIndex] = unit;
126 /* Cache the binary type since we know it is needed during this compile.
128 * Answer the created BinaryTypeBinding or null if the type is already in the cache.
131 public BinaryTypeBinding cacheBinaryType(IBinaryType binaryType) {
132 return cacheBinaryType(binaryType, true);
134 /* Cache the binary type since we know it is needed during this compile.
136 * Answer the created BinaryTypeBinding or null if the type is already in the cache.
139 public BinaryTypeBinding cacheBinaryType(IBinaryType binaryType, boolean needFieldsAndMethods) {
140 char[][] compoundName = CharOperation.splitOn('/', binaryType.getName());
141 ReferenceBinding existingType = getCachedType(compoundName);
143 if (existingType == null || existingType instanceof UnresolvedReferenceBinding)
144 // only add the binary type if its not already in the cache
145 return createBinaryTypeFrom(binaryType, computePackageFrom(compoundName), needFieldsAndMethods);
146 return null; // the type already exists & can be retrieved from the cache
149 * 1. Connect the type hierarchy for the type bindings created for parsedUnits.
150 * 2. Create the field bindings
151 * 3. Create the method bindings
154 /* We know each known compilationUnit is free of errors at this point...
156 * Each step will create additional bindings unless a problem is detected, in which
157 * case either the faulty import/superinterface/field/method will be skipped or a
158 * suitable replacement will be substituted (such as Object for a missing superclass)
161 public void completeTypeBindings() {
162 stepCompleted = BUILD_TYPE_HIERARCHY;
164 // for (int i = lastCompletedUnitIndex + 1; i <= lastUnitIndex; i++) {
165 // units[i].scope.checkAndSetImports();
167 stepCompleted = CHECK_AND_SET_IMPORTS;
169 // for (int i = lastCompletedUnitIndex + 1; i <= lastUnitIndex; i++) {
170 // units[i].scope.connectTypeHierarchy();
172 stepCompleted = CONNECT_TYPE_HIERARCHY;
174 for (int i = lastCompletedUnitIndex + 1; i <= lastUnitIndex; i++) {
175 // units[i].scope.buildFieldsAndMethods();
176 units[i] = null; // release unnecessary reference to the parsed unit
178 stepCompleted = BUILD_FIELDS_AND_METHODS;
179 lastCompletedUnitIndex = lastUnitIndex;
182 * 1. Connect the type hierarchy for the type bindings created for parsedUnits.
183 * 2. Create the field bindings
184 * 3. Create the method bindings
188 * Each step will create additional bindings unless a problem is detected, in which
189 * case either the faulty import/superinterface/field/method will be skipped or a
190 * suitable replacement will be substituted (such as Object for a missing superclass)
193 public void completeTypeBindings(CompilationUnitDeclaration parsedUnit) {
194 if (stepCompleted == BUILD_FIELDS_AND_METHODS) {
195 // This can only happen because the original set of units are completely built and
196 // are now being processed, so we want to treat all the additional units as a group
197 // until they too are completely processed.
198 completeTypeBindings();
200 if (parsedUnit.scope == null) return; // parsing errors were too severe
202 // if (stepCompleted >= CHECK_AND_SET_IMPORTS)
203 // parsedUnit.scope.checkAndSetImports();
205 if (stepCompleted >= CONNECT_TYPE_HIERARCHY)
206 parsedUnit.scope.connectTypeHierarchy();
210 * Used by other compiler tools which do not start by calling completeTypeBindings().
212 * 1. Connect the type hierarchy for the type bindings created for parsedUnits.
213 * 2. Create the field bindings
214 * 3. Create the method bindings
217 public void completeTypeBindings(CompilationUnitDeclaration parsedUnit, boolean buildFieldsAndMethods) {
218 if (parsedUnit.scope == null) return; // parsing errors were too severe
220 // parsedUnit.scope.checkAndSetImports();
221 parsedUnit.scope.connectTypeHierarchy();
223 if (buildFieldsAndMethods)
224 parsedUnit.scope.buildFieldsAndMethods();
226 private PackageBinding computePackageFrom(char[][] constantPoolName) {
227 if (constantPoolName.length == 1)
228 return defaultPackage;
230 PackageBinding packageBinding = getPackage0(constantPoolName[0]);
231 if (packageBinding == null || packageBinding == TheNotFoundPackage) {
232 packageBinding = new PackageBinding(constantPoolName[0], this);
233 knownPackages.put(constantPoolName[0], packageBinding);
236 for (int i = 1, length = constantPoolName.length - 1; i < length; i++) {
237 PackageBinding parent = packageBinding;
238 if ((packageBinding = parent.getPackage0(constantPoolName[i])) == null || packageBinding == TheNotFoundPackage) {
239 packageBinding = new PackageBinding(CharOperation.subarray(constantPoolName, 0, i + 1), parent, this);
240 parent.addPackage(packageBinding);
243 return packageBinding;
245 /* Used to guarantee array type identity.
248 ArrayBinding createArrayType(TypeBinding type, int dimensionCount) {
249 if (type instanceof LocalTypeBinding) // cache local type arrays with the local type itself
250 return ((LocalTypeBinding) type).createArrayType(dimensionCount);
252 // find the array binding cache for this dimension
253 int dimIndex = dimensionCount - 1;
254 int length = uniqueArrayBindings.length;
255 ArrayBinding[] arrayBindings;
256 if (dimIndex < length) {
257 if ((arrayBindings = uniqueArrayBindings[dimIndex]) == null)
258 uniqueArrayBindings[dimIndex] = arrayBindings = new ArrayBinding[10];
261 uniqueArrayBindings, 0,
262 uniqueArrayBindings = new ArrayBinding[dimensionCount][], 0,
264 uniqueArrayBindings[dimIndex] = arrayBindings = new ArrayBinding[10];
267 // find the cached array binding for this leaf component type (if any)
269 length = arrayBindings.length;
270 while (++index < length) {
271 ArrayBinding currentBinding = arrayBindings[index];
272 if (currentBinding == null) // no matching array, but space left
273 return arrayBindings[index] = new ArrayBinding(type, dimensionCount);
274 if (currentBinding.leafComponentType == type)
275 return currentBinding;
278 // no matching array, no space left
281 (arrayBindings = new ArrayBinding[length * 2]), 0,
283 uniqueArrayBindings[dimIndex] = arrayBindings;
284 return arrayBindings[length] = new ArrayBinding(type, dimensionCount);
286 public BinaryTypeBinding createBinaryTypeFrom(IBinaryType binaryType, PackageBinding packageBinding) {
287 return createBinaryTypeFrom(binaryType, packageBinding, true);
289 public BinaryTypeBinding createBinaryTypeFrom(IBinaryType binaryType, PackageBinding packageBinding, boolean needFieldsAndMethods) {
290 BinaryTypeBinding binaryBinding = new BinaryTypeBinding(packageBinding, binaryType, this);
292 // resolve any array bindings which reference the unresolvedType
293 ReferenceBinding cachedType = packageBinding.getType0(binaryBinding.compoundName[binaryBinding.compoundName.length - 1]);
294 if (cachedType != null) {
295 if (cachedType.isBinaryBinding()) // sanity check before the cast... at this point the cache should ONLY contain unresolved types
296 return (BinaryTypeBinding) cachedType;
298 UnresolvedReferenceBinding unresolvedType = (UnresolvedReferenceBinding) cachedType;
299 unresolvedType.resolvedType = binaryBinding;
300 updateArrayCache(unresolvedType, binaryBinding);
303 packageBinding.addType(binaryBinding);
304 binaryBinding.cachePartsFrom(binaryType, needFieldsAndMethods);
305 return binaryBinding;
307 /* Used to create packages from the package statement.
310 PackageBinding createPackage(char[][] compoundName) {
312 // PackageBinding packageBinding = getPackage0(compoundName[0]);
313 // if (packageBinding == null || packageBinding == TheNotFoundPackage) {
314 // packageBinding = new PackageBinding(compoundName[0], this);
315 // knownPackages.put(compoundName[0], packageBinding);
318 // for (int i = 1, length = compoundName.length; i < length; i++) {
319 // // check to see if it collides with a known type...
320 // // this case can only happen if the package does not exist as a directory in the file system
321 // // otherwise when the source type was defined, the correct error would have been reported
322 // // unless its an unresolved type which is referenced from an inconsistent class file
323 // ReferenceBinding type = packageBinding.getType0(compoundName[i]);
324 // if (type != null && type != TheNotFoundType && !(type instanceof UnresolvedReferenceBinding))
327 // PackageBinding parent = packageBinding;
328 // if ((packageBinding = parent.getPackage0(compoundName[i])) == null || packageBinding == TheNotFoundPackage) {
329 // // if the package is unknown, check to see if a type exists which would collide with the new package
330 // // catches the case of a package statement of: package java.lang.Object;
331 // // since the package can be added after a set of source files have already been compiled, we need
332 // // whenever a package statement is encountered
333 // if (nameEnvironment.findType(compoundName[i], parent.compoundName) != null)
336 // packageBinding = new PackageBinding(CharOperation.subarray(compoundName, 0, i + 1), parent, this);
337 // parent.addPackage(packageBinding);
340 // return packageBinding;
342 /* Answer the type for the compoundName if it exists in the cache.
343 * Answer theNotFoundType if it could not be resolved the first time
344 * it was looked up, otherwise answer null.
346 * NOTE: Do not use for nested types... the answer is NOT the same for a.b.C or a.b.C.D.E
347 * assuming C is a type in both cases. In the a.b.C.D.E case, null is the answer.
350 public ReferenceBinding getCachedType(char[][] compoundName) {
351 if (compoundName.length == 1) {
352 if (defaultPackage == null)
354 return defaultPackage.getType0(compoundName[0]);
357 PackageBinding packageBinding = getPackage0(compoundName[0]);
358 if (packageBinding == null || packageBinding == TheNotFoundPackage)
361 for (int i = 1, packageLength = compoundName.length - 1; i < packageLength; i++)
362 if ((packageBinding = packageBinding.getPackage0(compoundName[i])) == null || packageBinding == TheNotFoundPackage)
364 return packageBinding.getType0(compoundName[compoundName.length - 1]);
366 /* Answer the top level package named name if it exists in the cache.
367 * Answer theNotFoundPackage if it could not be resolved the first time
368 * it was looked up, otherwise answer null.
370 * NOTE: Senders must convert theNotFoundPackage into a real problem
371 * package if its to returned.
374 PackageBinding getPackage0(char[] name) {
375 return knownPackages.get(name);
377 /* Answer the top level package named name.
378 * Ask the oracle for the package if its not in the cache.
379 * Answer null if the package cannot be found.
382 PackageBinding getTopLevelPackage(char[] name) {
383 PackageBinding packageBinding = getPackage0(name);
384 if (packageBinding != null) {
385 if (packageBinding == TheNotFoundPackage)
388 return packageBinding;
391 if (nameEnvironment.isPackage(null, name)) {
392 knownPackages.put(name, packageBinding = new PackageBinding(name, this));
393 return packageBinding;
396 knownPackages.put(name, TheNotFoundPackage); // saves asking the oracle next time
399 /* Answer the type corresponding to the compoundName.
400 * Ask the oracle for the type if its not in the cache.
401 * Answer null if the type cannot be found... likely a fatal error.
404 public ReferenceBinding getType(char[][] compoundName) {
405 ReferenceBinding referenceBinding;
407 if (compoundName.length == 1) {
408 if (defaultPackage == null)
411 if ((referenceBinding = defaultPackage.getType0(compoundName[0])) == null) {
412 PackageBinding packageBinding = getPackage0(compoundName[0]);
413 if (packageBinding != null && packageBinding != TheNotFoundPackage)
414 return null; // collides with a known package... should not call this method in such a case
415 referenceBinding = askForType(defaultPackage, compoundName[0]);
418 PackageBinding packageBinding = getPackage0(compoundName[0]);
419 if (packageBinding == TheNotFoundPackage)
422 if (packageBinding != null) {
423 for (int i = 1, packageLength = compoundName.length - 1; i < packageLength; i++) {
424 if ((packageBinding = packageBinding.getPackage0(compoundName[i])) == null)
426 if (packageBinding == TheNotFoundPackage)
431 if (packageBinding == null)
432 referenceBinding = askForType(compoundName);
433 else if ((referenceBinding = packageBinding.getType0(compoundName[compoundName.length - 1])) == null)
434 referenceBinding = askForType(packageBinding, compoundName[compoundName.length - 1]);
437 if (referenceBinding == null || referenceBinding == TheNotFoundType)
439 if (referenceBinding instanceof UnresolvedReferenceBinding)
440 referenceBinding = ((UnresolvedReferenceBinding) referenceBinding).resolve(this);
442 // compoundName refers to a nested type incorrectly (for example, package1.A$B)
443 if (referenceBinding.isNestedType())
444 return new ProblemReferenceBinding(compoundName, InternalNameProvided);
446 return referenceBinding;
448 /* Answer the type corresponding to the name from the binary file.
449 * Does not ask the oracle for the type if its not found in the cache... instead an
450 * unresolved type is returned which must be resolved before used.
452 * NOTE: Does NOT answer base types nor array types!
454 * NOTE: Aborts compilation if the class file cannot be found.
457 ReferenceBinding getTypeFromConstantPoolName(char[] signature, int start, int end) {
459 end = signature.length;
461 char[][] compoundName = CharOperation.splitOn('/', signature, start, end);
462 ReferenceBinding binding = getCachedType(compoundName);
463 if (binding == null) {
464 PackageBinding packageBinding = computePackageFrom(compoundName);
465 binding = new UnresolvedReferenceBinding(compoundName, packageBinding);
466 packageBinding.addType(binding);
467 } else if (binding == TheNotFoundType) {
468 problemReporter.isClassPathCorrect(compoundName, null);
469 return null; // will not get here since the above error aborts the compilation
473 /* Answer the type corresponding to the signature from the binary file.
474 * Does not ask the oracle for the type if its not found in the cache... instead an
475 * unresolved type is returned which must be resolved before used.
477 * NOTE: Does answer base types & array types.
479 * NOTE: Aborts compilation if the class file cannot be found.
482 TypeBinding getTypeFromSignature(char[] signature, int start, int end) {
484 while (signature[start] == '[') {
489 end = signature.length - 1;
491 // Just switch on signature[start] - the L case is the else
492 TypeBinding binding = null;
494 switch (signature[start]) {
496 binding = IntBinding;
499 binding = BooleanBinding;
502 binding = VoidBinding;
505 binding = CharBinding;
508 binding = DoubleBinding;
511 binding = ByteBinding;
514 binding = FloatBinding;
517 binding = LongBinding;
520 binding = ShortBinding;
523 throw new Error(Util.bind("error.undefinedBaseType",String.valueOf(signature[start]))); //$NON-NLS-1$
526 binding = getTypeFromConstantPoolName(signature, start + 1, end);
532 return createArrayType(binding, dimension);
534 /* Ask the oracle if a package exists named name in the package named compoundName.
537 boolean isPackage(char[][] compoundName, char[] name) {
538 if (compoundName == null || compoundName.length == 0)
539 return nameEnvironment.isPackage(null, name);
541 return nameEnvironment.isPackage(compoundName, name);
543 // The method verifier is lazily initialized to guarantee the receiver, the compiler & the oracle are ready.
545 public MethodVerifier methodVerifier() {
546 if (verifier == null)
547 verifier = new MethodVerifier(this);
550 public void reset() {
551 this.defaultPackage = new PackageBinding(this); // assume the default package always exists
552 this.defaultImports = null;
553 this.knownPackages = new HashtableOfPackage();
555 this.verifier = null;
556 for (int i = this.uniqueArrayBindings.length; --i >= 0;)
557 this.uniqueArrayBindings[i] = null;
558 this.uniqueArrayBindings[0] = new ArrayBinding[50]; // start off the most common 1 dimension array @ 50
560 for (int i = this.units.length; --i >= 0;)
561 this.units[i] = null;
562 this.lastUnitIndex = -1;
563 this.lastCompletedUnitIndex = -1;
565 // name environment has a longer life cycle, and must be reset in
566 // the code which created it.
568 void updateArrayCache(UnresolvedReferenceBinding unresolvedType, ReferenceBinding resolvedType) {
569 nextDimension : for (int i = 0, length = uniqueArrayBindings.length; i < length; i++) {
570 ArrayBinding[] arrayBindings = uniqueArrayBindings[i];
571 if (arrayBindings != null) {
572 for (int j = 0, max = arrayBindings.length; j < max; j++) {
573 ArrayBinding currentBinding = arrayBindings[j];
574 if (currentBinding == null)
575 continue nextDimension;
576 if (currentBinding.leafComponentType == unresolvedType) {
577 currentBinding.leafComponentType = resolvedType;
578 continue nextDimension;