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 java.util.ArrayList;
15 import net.sourceforge.phpdt.core.compiler.CharOperation;
16 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
17 import net.sourceforge.phpdt.internal.compiler.util.CompoundNameVector;
18 import net.sourceforge.phpdt.internal.compiler.util.HashtableOfType;
19 import net.sourceforge.phpdt.internal.compiler.util.ObjectVector;
20 import net.sourceforge.phpdt.internal.compiler.util.SimpleNameVector;
21 import net.sourceforge.phpeclipse.internal.compiler.ast.CompilationUnitDeclaration;
22 import net.sourceforge.phpeclipse.internal.compiler.ast.ImportReference;
23 import net.sourceforge.phpeclipse.internal.compiler.ast.TypeDeclaration;
25 public class CompilationUnitScope extends Scope {
27 public LookupEnvironment environment;
28 public CompilationUnitDeclaration referenceContext;
29 public char[][] currentPackageName;
30 public PackageBinding fPackage;
31 public ImportBinding[] imports;
33 public SourceTypeBinding[] topLevelTypes;
35 private CompoundNameVector qualifiedReferences;
36 private SimpleNameVector simpleNameReferences;
37 private ObjectVector referencedTypes;
39 HashtableOfType constantPoolNameUsage;
41 public CompilationUnitScope(CompilationUnitDeclaration unit, LookupEnvironment environment) {
42 super(COMPILATION_UNIT_SCOPE, null);
43 this.environment = environment;
44 this.referenceContext = unit;
46 this.currentPackageName = unit.currentPackage == null ? CharOperation.NO_CHAR_CHAR : unit.currentPackage.tokens;
48 // if (environment.options.produceReferenceInfo) {
49 // this.qualifiedReferences = new CompoundNameVector();
50 // this.simpleNameReferences = new SimpleNameVector();
51 // this.referencedTypes = new ObjectVector();
53 this.qualifiedReferences = null; // used to test if dependencies should be recorded
54 this.simpleNameReferences = null;
55 this.referencedTypes = null;
58 void buildFieldsAndMethods() {
59 for (int i = 0, length = topLevelTypes.length; i < length; i++)
60 topLevelTypes[i].scope.buildFieldsAndMethods();
62 void buildTypeBindings() {
63 if (referenceContext.compilationResult.compilationUnit != null) {
64 char[][] expectedPackageName = referenceContext.compilationResult.compilationUnit.getPackageName();
65 if (expectedPackageName != null && !CharOperation.equals(currentPackageName, expectedPackageName)) {
67 // only report if the unit isn't structurally empty
68 // if (referenceContext.currentPackage != null
69 // || referenceContext.types != null
70 // || referenceContext.imports != null) {
71 // problemReporter().packageIsNotExpectedPackage(referenceContext);
73 currentPackageName = expectedPackageName.length == 0 ? CharOperation.NO_CHAR_CHAR : expectedPackageName;
76 if (currentPackageName == CharOperation.NO_CHAR_CHAR) {
77 if ((fPackage = environment.defaultPackage) == null) {
78 problemReporter().mustSpecifyPackage(referenceContext);
82 if ((fPackage = environment.createPackage(currentPackageName)) == null) {
83 // problemReporter().packageCollidesWithType(referenceContext);
86 recordQualifiedReference(currentPackageName); // always dependent on your own package
89 // Skip typeDeclarations which know of previously reported errors
90 ArrayList types = referenceContext.types;
91 int typeLength = (types == null) ? 0 : types.size();
92 topLevelTypes = new SourceTypeBinding[typeLength];
94 nextType : for (int i = 0; i < typeLength; i++) {
95 if (types.get(i) instanceof TypeDeclaration) {
96 TypeDeclaration typeDecl = (TypeDeclaration) types.get(i);
97 ReferenceBinding typeBinding = fPackage.getType0(typeDecl.name);
98 recordSimpleReference(typeDecl.name); // needed to detect collision cases
99 if (typeBinding != null && !(typeBinding instanceof UnresolvedReferenceBinding)) {
100 // if a type exists, it must be a valid type - cannot be a NotFound problem type
101 // unless its an unresolved type which is now being defined
102 problemReporter().duplicateTypes(referenceContext, typeDecl);
105 if (fPackage != environment.defaultPackage && fPackage.getPackage(typeDecl.name) != null) {
106 // if a package exists, it must be a valid package - cannot be a NotFound problem package
107 problemReporter().typeCollidesWithPackage(referenceContext, typeDecl);
111 if ((typeDecl.modifiers & AccPublic) != 0) {
113 if ((mainTypeName = referenceContext.getMainTypeName()) != null
114 // mainTypeName == null means that implementor of ICompilationUnit decided to return null
115 && !CharOperation.equals(mainTypeName, typeDecl.name)) {
116 problemReporter().publicClassMustMatchFileName(referenceContext, typeDecl);
121 ClassScope child = new ClassScope(this, typeDecl);
122 SourceTypeBinding type = child.buildType(null, fPackage);
124 topLevelTypes[count++] = type;
129 // shrink topLevelTypes... only happens if an error was reported
130 if (count != topLevelTypes.length)
131 System.arraycopy(topLevelTypes, 0, topLevelTypes = new SourceTypeBinding[count], 0, count);
133 void checkAndSetImports() {
134 // initialize the default imports if necessary... share the default java.lang.* import
135 if (environment.defaultImports == null) {
136 Binding importBinding = environment.getTopLevelPackage(JAVA);
137 if (importBinding != null)
138 importBinding = ((PackageBinding) importBinding).getTypeOrPackage(JAVA_LANG[1]);
140 // abort if java.lang cannot be found...
141 if (importBinding == null || !importBinding.isValidBinding())
142 problemReporter().isClassPathCorrect(JAVA_LANG_OBJECT, referenceCompilationUnit());
144 environment.defaultImports = new ImportBinding[] { new ImportBinding(JAVA_LANG, true, importBinding, null)};
146 if (referenceContext.imports == null) {
147 imports = environment.defaultImports;
151 // allocate the import array, add java.lang.* by default
152 int numberOfStatements = referenceContext.imports.length;
153 int numberOfImports = numberOfStatements + 1;
154 for (int i = 0; i < numberOfStatements; i++) {
155 ImportReference importReference = referenceContext.imports[i];
156 if (importReference.onDemand && CharOperation.equals(JAVA_LANG, importReference.tokens)) {
161 ImportBinding[] resolvedImports = new ImportBinding[numberOfImports];
162 resolvedImports[0] = environment.defaultImports[0];
165 nextImport : for (int i = 0; i < numberOfStatements; i++) {
166 ImportReference importReference = referenceContext.imports[i];
167 char[][] compoundName = importReference.tokens;
169 // skip duplicates or imports of the current package
170 for (int j = 0; j < index; j++)
171 if (resolvedImports[j].onDemand == importReference.onDemand)
172 if (CharOperation.equals(compoundName, resolvedImports[j].compoundName))
174 if (importReference.onDemand == true)
175 if (CharOperation.equals(compoundName, currentPackageName))
178 if (importReference.onDemand) {
179 Binding importBinding = findOnDemandImport(compoundName);
180 if (!importBinding.isValidBinding())
181 continue nextImport; // we report all problems in faultInImports()
182 resolvedImports[index++] = new ImportBinding(compoundName, true, importBinding, importReference);
184 resolvedImports[index++] = new ImportBinding(compoundName, false, null, importReference);
188 // shrink resolvedImports... only happens if an error was reported
189 if (resolvedImports.length > index)
190 System.arraycopy(resolvedImports, 0, resolvedImports = new ImportBinding[index], 0, index);
191 imports = resolvedImports;
195 * Innerclasses get their name computed as they are generated, since some may not
196 * be actually outputed if sitting inside unreachable code.
198 public char[] computeConstantPoolName(LocalTypeBinding localType) {
199 if (localType.constantPoolName() != null) {
200 return localType.constantPoolName();
202 // delegates to the outermost enclosing classfile, since it is the only one with a global vision of its innertypes.
204 if (constantPoolNameUsage == null)
205 constantPoolNameUsage = new HashtableOfType();
207 ReferenceBinding outerMostEnclosingType = localType.scope.outerMostClassScope().enclosingSourceType();
209 // ensure there is not already such a local type name defined by the user
211 char[] candidateName;
213 if (localType.isMemberType()) {
215 candidateName = CharOperation.concat(localType.enclosingType().constantPoolName(), localType.sourceName, '$');
217 // in case of collision, then member name gets extra $1 inserted
218 // e.g. class X { { class L{} new X(){ class L{} } } }
220 CharOperation.concat(localType.enclosingType().constantPoolName(), '$', String.valueOf(index).toCharArray(), '$', localType.sourceName);
222 } else if (localType.isAnonymousType()) {
223 candidateName = CharOperation.concat(outerMostEnclosingType.constantPoolName(), String.valueOf(index + 1).toCharArray(), '$');
226 CharOperation.concat(outerMostEnclosingType.constantPoolName(), '$', String.valueOf(index + 1).toCharArray(), '$', localType.sourceName);
228 if (constantPoolNameUsage.get(candidateName) != null) {
231 constantPoolNameUsage.put(candidateName, localType);
235 return candidateName;
238 void connectTypeHierarchy() {
239 for (int i = 0, length = topLevelTypes.length; i < length; i++)
240 topLevelTypes[i].scope.connectTypeHierarchy();
242 void faultInImports() {
243 if (referenceContext.imports == null)
246 // collect the top level type names if a single type import exists
247 int numberOfStatements = referenceContext.imports.length;
248 HashtableOfType typesBySimpleNames = null;
249 for (int i = 0; i < numberOfStatements; i++) {
250 if (!referenceContext.imports[i].onDemand) {
251 typesBySimpleNames = new HashtableOfType(topLevelTypes.length + numberOfStatements);
252 for (int j = 0, length = topLevelTypes.length; j < length; j++)
253 typesBySimpleNames.put(topLevelTypes[j].sourceName, topLevelTypes[j]);
258 // allocate the import array, add java.lang.* by default
259 int numberOfImports = numberOfStatements + 1;
260 for (int i = 0; i < numberOfStatements; i++) {
261 ImportReference importReference = referenceContext.imports[i];
262 if (importReference.onDemand && CharOperation.equals(JAVA_LANG, importReference.tokens)) {
267 ImportBinding[] resolvedImports = new ImportBinding[numberOfImports];
268 resolvedImports[0] = environment.defaultImports[0];
271 nextImport : for (int i = 0; i < numberOfStatements; i++) {
272 ImportReference importReference = referenceContext.imports[i];
273 char[][] compoundName = importReference.tokens;
275 // skip duplicates or imports of the current package
276 for (int j = 0; j < index; j++)
277 if (resolvedImports[j].onDemand == importReference.onDemand)
278 if (CharOperation.equals(compoundName, resolvedImports[j].compoundName)) {
279 problemReporter().unusedImport(importReference); // since skipped, must be reported now
282 if (importReference.onDemand == true)
283 if (CharOperation.equals(compoundName, currentPackageName)) {
284 problemReporter().unusedImport(importReference); // since skipped, must be reported now
287 if (importReference.onDemand) {
288 Binding importBinding = findOnDemandImport(compoundName);
289 if (!importBinding.isValidBinding()) {
290 problemReporter().importProblem(importReference, importBinding);
293 resolvedImports[index++] = new ImportBinding(compoundName, true, importBinding, importReference);
295 Binding typeBinding = findSingleTypeImport(compoundName);
296 if (!typeBinding.isValidBinding()) {
297 problemReporter().importProblem(importReference, typeBinding);
300 if (typeBinding instanceof PackageBinding) {
301 problemReporter().cannotImportPackage(importReference);
304 if (typeBinding instanceof ReferenceBinding) {
305 ReferenceBinding referenceBinding = (ReferenceBinding) typeBinding;
306 if (importReference.isTypeUseDeprecated(referenceBinding, this)) {
307 problemReporter().deprecatedType((TypeBinding) typeBinding, importReference);
310 ReferenceBinding existingType = typesBySimpleNames.get(compoundName[compoundName.length - 1]);
311 if (existingType != null) {
312 // duplicate test above should have caught this case, but make sure
313 if (existingType == typeBinding) {
316 // either the type collides with a top level type or another imported type
317 for (int j = 0, length = topLevelTypes.length; j < length; j++) {
318 if (CharOperation.equals(topLevelTypes[j].sourceName, existingType.sourceName)) {
319 problemReporter().conflictingImport(importReference);
323 problemReporter().duplicateImport(importReference);
326 resolvedImports[index++] = new ImportBinding(compoundName, false, typeBinding, importReference);
327 typesBySimpleNames.put(compoundName[compoundName.length - 1], (ReferenceBinding) typeBinding);
331 // shrink resolvedImports... only happens if an error was reported
332 if (resolvedImports.length > index)
333 System.arraycopy(resolvedImports, 0, resolvedImports = new ImportBinding[index], 0, index);
334 imports = resolvedImports;
336 public void faultInTypes() {
338 if (topLevelTypes==null) {
339 topLevelTypes = new SourceTypeBinding[0];
341 for (int i = 0, length = topLevelTypes.length; i < length; i++)
342 topLevelTypes[i].faultInTypesForFieldsAndMethods();
344 private Binding findOnDemandImport(char[][] compoundName) {
345 recordQualifiedReference(compoundName);
347 Binding binding = environment.getTopLevelPackage(compoundName[0]);
349 int length = compoundName.length;
350 foundNothingOrType : if (binding != null) {
351 PackageBinding packageBinding = (PackageBinding) binding;
353 binding = packageBinding.getTypeOrPackage(compoundName[i++]);
354 if (binding == null || !binding.isValidBinding()) {
356 break foundNothingOrType;
358 if (!(binding instanceof PackageBinding))
359 break foundNothingOrType;
361 packageBinding = (PackageBinding) binding;
363 return packageBinding;
366 ReferenceBinding type;
367 if (binding == null) {
368 // if (environment.defaultPackage == null
369 // || environment.options.complianceLevel >= CompilerOptions.JDK1_4){
370 // return new ProblemReferenceBinding(
371 // CharOperation.subarray(compoundName, 0, i),
374 type = findType(compoundName[0], environment.defaultPackage, environment.defaultPackage);
375 if (type == null || !type.isValidBinding())
376 return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, i), NotFound);
377 i = 1; // reset to look for member types inside the default package type
379 type = (ReferenceBinding) binding;
382 for (; i < length; i++) {
383 if (!type.canBeSeenBy(fPackage)) {
384 return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, i), type, NotVisible);
386 // does not look for inherited member types on purpose
387 if ((type = type.getMemberType(compoundName[i])) == null) {
388 return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, i + 1), NotFound);
391 if (!type.canBeSeenBy(fPackage))
392 return new ProblemReferenceBinding(compoundName, type, NotVisible);
395 private Binding findSingleTypeImport(char[][] compoundName) {
396 if (compoundName.length == 1) {
397 // findType records the reference
398 // the name cannot be a package
399 // if (environment.defaultPackage == null
400 // || environment.options.complianceLevel >= CompilerOptions.JDK1_4)
401 // return new ProblemReferenceBinding(compoundName, NotFound);
402 ReferenceBinding typeBinding = findType(compoundName[0], environment.defaultPackage, fPackage);
403 if (typeBinding == null)
404 return new ProblemReferenceBinding(compoundName, NotFound);
408 return findOnDemandImport(compoundName);
410 /* Answer the problem reporter to use for raising new problems.
412 * Note that as a side-effect, this updates the current reference context
413 * (unit, type or method) in case the problem handler decides it is necessary
417 public ProblemReporter problemReporter() {
418 ProblemReporter problemReporter = referenceContext.problemReporter;
419 problemReporter.referenceContext = referenceContext;
420 return problemReporter;
424 What do we hold onto:
426 1. when we resolve 'a.b.c', say we keep only 'a.b.c'
427 & when we fail to resolve 'c' in 'a.b', lets keep 'a.b.c'
428 THEN when we come across a new/changed/removed item named 'a.b.c',
429 we would find all references to 'a.b.c'
430 -> This approach fails because every type is resolved in every onDemand import to
431 detect collision cases... so the references could be 10 times bigger than necessary.
433 2. when we resolve 'a.b.c', lets keep 'a.b' & 'c'
434 & when we fail to resolve 'c' in 'a.b', lets keep 'a.b' & 'c'
435 THEN when we come across a new/changed/removed item named 'a.b.c',
436 we would find all references to 'a.b' & 'c'
437 -> This approach does not have a space problem but fails to handle collision cases.
438 What happens if a type is added named 'a.b'? We would search for 'a' & 'b' but
439 would not find a match.
441 3. when we resolve 'a.b.c', lets keep 'a', 'a.b' & 'a', 'b', 'c'
442 & when we fail to resolve 'c' in 'a.b', lets keep 'a', 'a.b' & 'a', 'b', 'c'
443 THEN when we come across a new/changed/removed item named 'a.b.c',
444 we would find all references to 'a.b' & 'c'
445 OR 'a.b' -> 'a' & 'b'
447 -> As long as each single char[] is interned, we should not have a space problem
448 and can handle collision cases.
450 4. when we resolve 'a.b.c', lets keep 'a.b' & 'a', 'b', 'c'
451 & when we fail to resolve 'c' in 'a.b', lets keep 'a.b' & 'a', 'b', 'c'
452 THEN when we come across a new/changed/removed item named 'a.b.c',
453 we would find all references to 'a.b' & 'c'
454 OR 'a.b' -> 'a' & 'b' in the simple name collection
455 OR 'a' -> 'a' in the simple name collection
456 -> As long as each single char[] is interned, we should not have a space problem
457 and can handle collision cases.
459 void recordQualifiedReference(char[][] qualifiedName) {
460 if (qualifiedReferences == null)
461 return; // not recording dependencies
463 int length = qualifiedName.length;
465 while (!qualifiedReferences.contains(qualifiedName)) {
466 qualifiedReferences.add(qualifiedName);
468 recordSimpleReference(qualifiedName[0]);
469 recordSimpleReference(qualifiedName[1]);
473 recordSimpleReference(qualifiedName[length]);
474 System.arraycopy(qualifiedName, 0, qualifiedName = new char[length][], 0, length);
476 } else if (length == 1) {
477 recordSimpleReference(qualifiedName[0]);
480 void recordReference(char[][] qualifiedEnclosingName, char[] simpleName) {
481 recordQualifiedReference(qualifiedEnclosingName);
482 recordSimpleReference(simpleName);
484 void recordSimpleReference(char[] simpleName) {
485 if (simpleNameReferences == null)
486 return; // not recording dependencies
488 if (!simpleNameReferences.contains(simpleName))
489 simpleNameReferences.add(simpleName);
491 void recordTypeReference(TypeBinding type) {
492 if (referencedTypes == null)
493 return; // not recording dependencies
495 if (type.isArrayType())
496 type = ((ArrayBinding) type).leafComponentType;
497 if (!type.isBaseType() && !referencedTypes.containsIdentical(type))
498 referencedTypes.add(type);
500 void recordTypeReferences(TypeBinding[] types) {
501 if (qualifiedReferences == null)
502 return; // not recording dependencies
503 if (types == null || types.length == 0)
506 for (int i = 0, max = types.length; i < max; i++) {
507 // No need to record supertypes of method arguments & thrown exceptions, just the compoundName
508 // If a field/method is retrieved from such a type then a separate call does the job
509 TypeBinding type = types[i];
510 if (type.isArrayType())
511 type = ((ArrayBinding) type).leafComponentType;
512 if (!type.isBaseType()) {
513 ReferenceBinding actualType = (ReferenceBinding) type;
514 if (!actualType.isLocalType())
515 recordQualifiedReference(actualType.isMemberType() ? CharOperation.splitOn('.', actualType.readableName()) : actualType.compoundName);
519 Binding resolveSingleTypeImport(ImportBinding importBinding) {
520 if (importBinding.resolvedImport == null) {
521 importBinding.resolvedImport = findSingleTypeImport(importBinding.compoundName);
522 if (!importBinding.resolvedImport.isValidBinding() || importBinding.resolvedImport instanceof PackageBinding) {
523 if (this.imports != null) {
524 ImportBinding[] newImports = new ImportBinding[imports.length - 1];
525 for (int i = 0, n = 0, max = this.imports.length; i < max; i++)
526 if (this.imports[i] != importBinding) {
527 newImports[n++] = this.imports[i];
529 this.imports = newImports;
534 return importBinding.resolvedImport;
536 public void storeDependencyInfo() {
537 // add the type hierarchy of each referenced type
538 // cannot do early since the hierarchy may not be fully resolved
539 for (int i = 0; i < referencedTypes.size; i++) { // grows as more types are added
540 ReferenceBinding type = (ReferenceBinding) referencedTypes.elementAt(i);
541 if (!type.isLocalType()) {
542 recordQualifiedReference(type.isMemberType() ? CharOperation.splitOn('.', type.readableName()) : type.compoundName);
543 ReferenceBinding enclosing = type.enclosingType();
544 if (enclosing != null && !referencedTypes.containsIdentical(enclosing))
545 referencedTypes.add(enclosing); // to record its supertypes
547 ReferenceBinding superclass = type.superclass();
548 if (superclass != null && !referencedTypes.containsIdentical(superclass))
549 referencedTypes.add(superclass); // to record its supertypes
550 ReferenceBinding[] interfaces = type.superInterfaces();
551 if (interfaces != null && interfaces.length > 0)
552 for (int j = 0, length = interfaces.length; j < length; j++)
553 if (!referencedTypes.containsIdentical(interfaces[j]))
554 referencedTypes.add(interfaces[j]); // to record its supertypes
557 int size = qualifiedReferences.size;
558 char[][][] qualifiedRefs = new char[size][][];
559 for (int i = 0; i < size; i++)
560 qualifiedRefs[i] = qualifiedReferences.elementAt(i);
561 referenceContext.compilationResult.qualifiedReferences = qualifiedRefs;
563 size = simpleNameReferences.size;
564 char[][] simpleRefs = new char[size][];
565 for (int i = 0; i < size; i++)
566 simpleRefs[i] = simpleNameReferences.elementAt(i);
567 referenceContext.compilationResult.simpleNameReferences = simpleRefs;
569 public String toString() {
570 return "--- CompilationUnit Scope : " + new String(referenceContext.getFileName()); //$NON-NLS-1$
572 public void verifyMethods(MethodVerifier verifier) {
573 for (int i = 0, length = topLevelTypes.length; i < length; i++)
574 topLevelTypes[i].verifyMethods(verifier);