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.TypeDeclaration;
24 public class CompilationUnitScope extends Scope {
26 public LookupEnvironment environment;
27 public CompilationUnitDeclaration referenceContext;
28 public char[][] currentPackageName;
29 public PackageBinding fPackage;
30 public ImportBinding[] imports;
32 public SourceTypeBinding[] topLevelTypes;
34 private CompoundNameVector qualifiedReferences;
35 private SimpleNameVector simpleNameReferences;
36 private ObjectVector referencedTypes;
38 HashtableOfType constantPoolNameUsage;
40 public CompilationUnitScope(CompilationUnitDeclaration unit, LookupEnvironment environment) {
41 super(COMPILATION_UNIT_SCOPE, null);
42 this.environment = environment;
43 this.referenceContext = unit;
45 // this.currentPackageName = unit.currentPackage == null ? CharOperation.NO_CHAR_CHAR : unit.currentPackage.tokens;
46 this.currentPackageName = null;
47 // if (environment.options.produceReferenceInfo) {
48 // this.qualifiedReferences = new CompoundNameVector();
49 // this.simpleNameReferences = new SimpleNameVector();
50 // this.referencedTypes = new ObjectVector();
52 this.qualifiedReferences = null; // used to test if dependencies should be recorded
53 this.simpleNameReferences = null;
54 this.referencedTypes = null;
57 void buildFieldsAndMethods() {
58 for (int i = 0, length = topLevelTypes.length; i < length; i++)
59 topLevelTypes[i].scope.buildFieldsAndMethods();
61 void buildTypeBindings() {
62 if (referenceContext.compilationResult.compilationUnit != null) {
63 char[][] expectedPackageName = referenceContext.compilationResult.compilationUnit.getPackageName();
64 if (expectedPackageName != null && !CharOperation.equals(currentPackageName, expectedPackageName)) {
66 // only report if the unit isn't structurally empty
67 // if (referenceContext.currentPackage != null
68 // || referenceContext.types != null
69 // || referenceContext.imports != null) {
70 // problemReporter().packageIsNotExpectedPackage(referenceContext);
72 currentPackageName = expectedPackageName.length == 0 ? CharOperation.NO_CHAR_CHAR : expectedPackageName;
75 if (currentPackageName == CharOperation.NO_CHAR_CHAR) {
76 if ((fPackage = environment.defaultPackage) == null) {
77 problemReporter().mustSpecifyPackage(referenceContext);
81 if ((fPackage = environment.createPackage(currentPackageName)) == null) {
82 // problemReporter().packageCollidesWithType(referenceContext);
85 recordQualifiedReference(currentPackageName); // always dependent on your own package
88 // Skip typeDeclarations which know of previously reported errors
89 ArrayList types = referenceContext.types;
90 int typeLength = (types == null) ? 0 : types.size();
91 topLevelTypes = new SourceTypeBinding[typeLength];
93 nextType : for (int i = 0; i < typeLength; i++) {
94 if (types.get(i) instanceof TypeDeclaration) {
95 TypeDeclaration typeDecl = (TypeDeclaration) types.get(i);
96 ReferenceBinding typeBinding = fPackage.getType0(typeDecl.name);
97 recordSimpleReference(typeDecl.name); // needed to detect collision cases
98 if (typeBinding != null && !(typeBinding instanceof UnresolvedReferenceBinding)) {
99 // if a type exists, it must be a valid type - cannot be a NotFound problem type
100 // unless its an unresolved type which is now being defined
101 problemReporter().duplicateTypes(referenceContext, typeDecl);
104 if (fPackage != environment.defaultPackage && fPackage.getPackage(typeDecl.name) != null) {
105 // if a package exists, it must be a valid package - cannot be a NotFound problem package
106 problemReporter().typeCollidesWithPackage(referenceContext, typeDecl);
110 if ((typeDecl.modifiers & AccPublic) != 0) {
112 if ((mainTypeName = referenceContext.getMainTypeName()) != null
113 // mainTypeName == null means that implementor of ICompilationUnit decided to return null
114 && !CharOperation.equals(mainTypeName, typeDecl.name)) {
115 problemReporter().publicClassMustMatchFileName(referenceContext, typeDecl);
120 ClassScope child = new ClassScope(this, typeDecl);
121 SourceTypeBinding type = child.buildType(null, fPackage);
123 topLevelTypes[count++] = type;
128 // shrink topLevelTypes... only happens if an error was reported
129 if (count != topLevelTypes.length)
130 System.arraycopy(topLevelTypes, 0, topLevelTypes = new SourceTypeBinding[count], 0, count);
132 // void checkAndSetImports() {
133 // // initialize the default imports if necessary... share the default java.lang.* import
134 // if (environment.defaultImports == null) {
135 // Binding importBinding = environment.getTopLevelPackage(JAVA);
136 // if (importBinding != null)
137 // importBinding = ((PackageBinding) importBinding).getTypeOrPackage(JAVA_LANG[1]);
139 // // abort if java.lang cannot be found...
140 // if (importBinding == null || !importBinding.isValidBinding())
141 // problemReporter().isClassPathCorrect(JAVA_LANG_OBJECT, referenceCompilationUnit());
143 // environment.defaultImports = new ImportBinding[] { new ImportBinding(JAVA_LANG, true, importBinding, null)};
145 // if (referenceContext.imports == null) {
146 // imports = environment.defaultImports;
150 // // allocate the import array, add java.lang.* by default
151 // int numberOfStatements = referenceContext.imports.length;
152 // int numberOfImports = numberOfStatements + 1;
153 // for (int i = 0; i < numberOfStatements; i++) {
154 // ImportReference importReference = referenceContext.imports[i];
155 // if (importReference.onDemand && CharOperation.equals(JAVA_LANG, importReference.tokens)) {
156 // numberOfImports--;
160 // ImportBinding[] resolvedImports = new ImportBinding[numberOfImports];
161 // resolvedImports[0] = environment.defaultImports[0];
164 // nextImport : for (int i = 0; i < numberOfStatements; i++) {
165 // ImportReference importReference = referenceContext.imports[i];
166 // char[][] compoundName = importReference.tokens;
168 // // skip duplicates or imports of the current package
169 // for (int j = 0; j < index; j++)
170 // if (resolvedImports[j].onDemand == importReference.onDemand)
171 // if (CharOperation.equals(compoundName, resolvedImports[j].compoundName))
172 // continue nextImport;
173 // if (importReference.onDemand == true)
174 // if (CharOperation.equals(compoundName, currentPackageName))
175 // continue nextImport;
177 // if (importReference.onDemand) {
178 // Binding importBinding = findOnDemandImport(compoundName);
179 // if (!importBinding.isValidBinding())
180 // continue nextImport; // we report all problems in faultInImports()
181 // resolvedImports[index++] = new ImportBinding(compoundName, true, importBinding, importReference);
183 // resolvedImports[index++] = new ImportBinding(compoundName, false, null, importReference);
187 // // shrink resolvedImports... only happens if an error was reported
188 // if (resolvedImports.length > index)
189 // System.arraycopy(resolvedImports, 0, resolvedImports = new ImportBinding[index], 0, index);
190 // imports = resolvedImports;
194 * Innerclasses get their name computed as they are generated, since some may not
195 * be actually outputed if sitting inside unreachable code.
197 public char[] computeConstantPoolName(LocalTypeBinding localType) {
198 if (localType.constantPoolName() != null) {
199 return localType.constantPoolName();
201 // delegates to the outermost enclosing classfile, since it is the only one with a global vision of its innertypes.
203 if (constantPoolNameUsage == null)
204 constantPoolNameUsage = new HashtableOfType();
206 ReferenceBinding outerMostEnclosingType = localType.scope.outerMostClassScope().enclosingSourceType();
208 // ensure there is not already such a local type name defined by the user
210 char[] candidateName;
212 if (localType.isMemberType()) {
214 candidateName = CharOperation.concat(localType.enclosingType().constantPoolName(), localType.sourceName, '$');
216 // in case of collision, then member name gets extra $1 inserted
217 // e.g. class X { { class L{} new X(){ class L{} } } }
219 CharOperation.concat(localType.enclosingType().constantPoolName(), '$', String.valueOf(index).toCharArray(), '$', localType.sourceName);
221 } else if (localType.isAnonymousType()) {
222 candidateName = CharOperation.concat(outerMostEnclosingType.constantPoolName(), String.valueOf(index + 1).toCharArray(), '$');
225 CharOperation.concat(outerMostEnclosingType.constantPoolName(), '$', String.valueOf(index + 1).toCharArray(), '$', localType.sourceName);
227 if (constantPoolNameUsage.get(candidateName) != null) {
230 constantPoolNameUsage.put(candidateName, localType);
234 return candidateName;
237 void connectTypeHierarchy() {
238 for (int i = 0, length = topLevelTypes.length; i < length; i++)
239 topLevelTypes[i].scope.connectTypeHierarchy();
241 // void faultInImports() {
242 // if (referenceContext.imports == null)
245 // // collect the top level type names if a single type import exists
246 // int numberOfStatements = referenceContext.imports.length;
247 // HashtableOfType typesBySimpleNames = null;
248 // for (int i = 0; i < numberOfStatements; i++) {
249 // if (!referenceContext.imports[i].onDemand) {
250 // typesBySimpleNames = new HashtableOfType(topLevelTypes.length + numberOfStatements);
251 // for (int j = 0, length = topLevelTypes.length; j < length; j++)
252 // typesBySimpleNames.put(topLevelTypes[j].sourceName, topLevelTypes[j]);
257 // // allocate the import array, add java.lang.* by default
258 // int numberOfImports = numberOfStatements + 1;
259 // for (int i = 0; i < numberOfStatements; i++) {
260 // ImportReference importReference = referenceContext.imports[i];
261 // if (importReference.onDemand && CharOperation.equals(JAVA_LANG, importReference.tokens)) {
262 // numberOfImports--;
266 // ImportBinding[] resolvedImports = new ImportBinding[numberOfImports];
267 // resolvedImports[0] = environment.defaultImports[0];
270 // nextImport : for (int i = 0; i < numberOfStatements; i++) {
271 // ImportReference importReference = referenceContext.imports[i];
272 // char[][] compoundName = importReference.tokens;
274 // // skip duplicates or imports of the current package
275 // for (int j = 0; j < index; j++)
276 // if (resolvedImports[j].onDemand == importReference.onDemand)
277 // if (CharOperation.equals(compoundName, resolvedImports[j].compoundName)) {
278 // problemReporter().unusedImport(importReference); // since skipped, must be reported now
279 // continue nextImport;
281 // if (importReference.onDemand == true)
282 // if (CharOperation.equals(compoundName, currentPackageName)) {
283 // problemReporter().unusedImport(importReference); // since skipped, must be reported now
284 // continue nextImport;
286 // if (importReference.onDemand) {
287 // Binding importBinding = findOnDemandImport(compoundName);
288 // if (!importBinding.isValidBinding()) {
289 // problemReporter().importProblem(importReference, importBinding);
290 // continue nextImport;
292 // resolvedImports[index++] = new ImportBinding(compoundName, true, importBinding, importReference);
294 // Binding typeBinding = findSingleTypeImport(compoundName);
295 // if (!typeBinding.isValidBinding()) {
296 // problemReporter().importProblem(importReference, typeBinding);
297 // continue nextImport;
299 // if (typeBinding instanceof PackageBinding) {
300 // problemReporter().cannotImportPackage(importReference);
301 // continue nextImport;
303 // if (typeBinding instanceof ReferenceBinding) {
304 // ReferenceBinding referenceBinding = (ReferenceBinding) typeBinding;
305 // if (importReference.isTypeUseDeprecated(referenceBinding, this)) {
306 // problemReporter().deprecatedType((TypeBinding) typeBinding, importReference);
309 // ReferenceBinding existingType = typesBySimpleNames.get(compoundName[compoundName.length - 1]);
310 // if (existingType != null) {
311 // // duplicate test above should have caught this case, but make sure
312 // if (existingType == typeBinding) {
313 // continue nextImport;
315 // // either the type collides with a top level type or another imported type
316 // for (int j = 0, length = topLevelTypes.length; j < length; j++) {
317 // if (CharOperation.equals(topLevelTypes[j].sourceName, existingType.sourceName)) {
318 // problemReporter().conflictingImport(importReference);
319 // continue nextImport;
322 // problemReporter().duplicateImport(importReference);
323 // continue nextImport;
325 // resolvedImports[index++] = new ImportBinding(compoundName, false, typeBinding, importReference);
326 // typesBySimpleNames.put(compoundName[compoundName.length - 1], (ReferenceBinding) typeBinding);
330 // // shrink resolvedImports... only happens if an error was reported
331 // if (resolvedImports.length > index)
332 // System.arraycopy(resolvedImports, 0, resolvedImports = new ImportBinding[index], 0, index);
333 // imports = resolvedImports;
335 public void faultInTypes() {
337 if (topLevelTypes==null) {
338 topLevelTypes = new SourceTypeBinding[0];
340 for (int i = 0, length = topLevelTypes.length; i < length; i++)
341 topLevelTypes[i].faultInTypesForFieldsAndMethods();
343 private Binding findOnDemandImport(char[][] compoundName) {
344 recordQualifiedReference(compoundName);
346 Binding binding = environment.getTopLevelPackage(compoundName[0]);
348 int length = compoundName.length;
349 foundNothingOrType : if (binding != null) {
350 PackageBinding packageBinding = (PackageBinding) binding;
352 binding = packageBinding.getTypeOrPackage(compoundName[i++]);
353 if (binding == null || !binding.isValidBinding()) {
355 break foundNothingOrType;
357 if (!(binding instanceof PackageBinding))
358 break foundNothingOrType;
360 packageBinding = (PackageBinding) binding;
362 return packageBinding;
365 ReferenceBinding type;
366 if (binding == null) {
367 // if (environment.defaultPackage == null
368 // || environment.options.complianceLevel >= CompilerOptions.JDK1_4){
369 // return new ProblemReferenceBinding(
370 // CharOperation.subarray(compoundName, 0, i),
373 type = findType(compoundName[0], environment.defaultPackage, environment.defaultPackage);
374 if (type == null || !type.isValidBinding())
375 return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, i), NotFound);
376 i = 1; // reset to look for member types inside the default package type
378 type = (ReferenceBinding) binding;
381 for (; i < length; i++) {
382 if (!type.canBeSeenBy(fPackage)) {
383 return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, i), type, NotVisible);
385 // does not look for inherited member types on purpose
386 if ((type = type.getMemberType(compoundName[i])) == null) {
387 return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, i + 1), NotFound);
390 if (!type.canBeSeenBy(fPackage))
391 return new ProblemReferenceBinding(compoundName, type, NotVisible);
394 private Binding findSingleTypeImport(char[][] compoundName) {
395 if (compoundName.length == 1) {
396 // findType records the reference
397 // the name cannot be a package
398 // if (environment.defaultPackage == null
399 // || environment.options.complianceLevel >= CompilerOptions.JDK1_4)
400 // return new ProblemReferenceBinding(compoundName, NotFound);
401 ReferenceBinding typeBinding = findType(compoundName[0], environment.defaultPackage, fPackage);
402 if (typeBinding == null)
403 return new ProblemReferenceBinding(compoundName, NotFound);
407 return findOnDemandImport(compoundName);
409 /* Answer the problem reporter to use for raising new problems.
411 * Note that as a side-effect, this updates the current reference context
412 * (unit, type or method) in case the problem handler decides it is necessary
416 public ProblemReporter problemReporter() {
417 ProblemReporter problemReporter = referenceContext.problemReporter;
418 problemReporter.referenceContext = referenceContext;
419 return problemReporter;
423 What do we hold onto:
425 1. when we resolve 'a.b.c', say we keep only 'a.b.c'
426 & when we fail to resolve 'c' in 'a.b', lets keep 'a.b.c'
427 THEN when we come across a new/changed/removed item named 'a.b.c',
428 we would find all references to 'a.b.c'
429 -> This approach fails because every type is resolved in every onDemand import to
430 detect collision cases... so the references could be 10 times bigger than necessary.
432 2. when we resolve 'a.b.c', lets keep 'a.b' & 'c'
433 & when we fail to resolve 'c' in 'a.b', lets keep 'a.b' & 'c'
434 THEN when we come across a new/changed/removed item named 'a.b.c',
435 we would find all references to 'a.b' & 'c'
436 -> This approach does not have a space problem but fails to handle collision cases.
437 What happens if a type is added named 'a.b'? We would search for 'a' & 'b' but
438 would not find a match.
440 3. when we resolve 'a.b.c', lets keep 'a', 'a.b' & 'a', 'b', 'c'
441 & when we fail to resolve 'c' in 'a.b', lets keep 'a', 'a.b' & 'a', 'b', 'c'
442 THEN when we come across a new/changed/removed item named 'a.b.c',
443 we would find all references to 'a.b' & 'c'
444 OR 'a.b' -> 'a' & 'b'
446 -> As long as each single char[] is interned, we should not have a space problem
447 and can handle collision cases.
449 4. when we resolve 'a.b.c', lets keep 'a.b' & 'a', 'b', 'c'
450 & when we fail to resolve 'c' in 'a.b', lets keep 'a.b' & 'a', 'b', 'c'
451 THEN when we come across a new/changed/removed item named 'a.b.c',
452 we would find all references to 'a.b' & 'c'
453 OR 'a.b' -> 'a' & 'b' in the simple name collection
454 OR 'a' -> 'a' in the simple name collection
455 -> As long as each single char[] is interned, we should not have a space problem
456 and can handle collision cases.
458 void recordQualifiedReference(char[][] qualifiedName) {
459 if (qualifiedReferences == null)
460 return; // not recording dependencies
462 int length = qualifiedName.length;
464 while (!qualifiedReferences.contains(qualifiedName)) {
465 qualifiedReferences.add(qualifiedName);
467 recordSimpleReference(qualifiedName[0]);
468 recordSimpleReference(qualifiedName[1]);
472 recordSimpleReference(qualifiedName[length]);
473 System.arraycopy(qualifiedName, 0, qualifiedName = new char[length][], 0, length);
475 } else if (length == 1) {
476 recordSimpleReference(qualifiedName[0]);
479 void recordReference(char[][] qualifiedEnclosingName, char[] simpleName) {
480 recordQualifiedReference(qualifiedEnclosingName);
481 recordSimpleReference(simpleName);
483 void recordSimpleReference(char[] simpleName) {
484 if (simpleNameReferences == null)
485 return; // not recording dependencies
487 if (!simpleNameReferences.contains(simpleName))
488 simpleNameReferences.add(simpleName);
490 void recordTypeReference(TypeBinding type) {
491 if (referencedTypes == null)
492 return; // not recording dependencies
494 if (type.isArrayType())
495 type = ((ArrayBinding) type).leafComponentType;
496 if (!type.isBaseType() && !referencedTypes.containsIdentical(type))
497 referencedTypes.add(type);
499 void recordTypeReferences(TypeBinding[] types) {
500 if (qualifiedReferences == null)
501 return; // not recording dependencies
502 if (types == null || types.length == 0)
505 for (int i = 0, max = types.length; i < max; i++) {
506 // No need to record supertypes of method arguments & thrown exceptions, just the compoundName
507 // If a field/method is retrieved from such a type then a separate call does the job
508 TypeBinding type = types[i];
509 if (type.isArrayType())
510 type = ((ArrayBinding) type).leafComponentType;
511 if (!type.isBaseType()) {
512 ReferenceBinding actualType = (ReferenceBinding) type;
513 if (!actualType.isLocalType())
514 recordQualifiedReference(actualType.isMemberType() ? CharOperation.splitOn('.', actualType.readableName()) : actualType.compoundName);
518 Binding resolveSingleTypeImport(ImportBinding importBinding) {
519 if (importBinding.resolvedImport == null) {
520 importBinding.resolvedImport = findSingleTypeImport(importBinding.compoundName);
521 if (!importBinding.resolvedImport.isValidBinding() || importBinding.resolvedImport instanceof PackageBinding) {
522 if (this.imports != null) {
523 ImportBinding[] newImports = new ImportBinding[imports.length - 1];
524 for (int i = 0, n = 0, max = this.imports.length; i < max; i++)
525 if (this.imports[i] != importBinding) {
526 newImports[n++] = this.imports[i];
528 this.imports = newImports;
533 return importBinding.resolvedImport;
535 public void storeDependencyInfo() {
536 // add the type hierarchy of each referenced type
537 // cannot do early since the hierarchy may not be fully resolved
538 for (int i = 0; i < referencedTypes.size; i++) { // grows as more types are added
539 ReferenceBinding type = (ReferenceBinding) referencedTypes.elementAt(i);
540 if (!type.isLocalType()) {
541 recordQualifiedReference(type.isMemberType() ? CharOperation.splitOn('.', type.readableName()) : type.compoundName);
542 ReferenceBinding enclosing = type.enclosingType();
543 if (enclosing != null && !referencedTypes.containsIdentical(enclosing))
544 referencedTypes.add(enclosing); // to record its supertypes
546 ReferenceBinding superclass = type.superclass();
547 if (superclass != null && !referencedTypes.containsIdentical(superclass))
548 referencedTypes.add(superclass); // to record its supertypes
549 ReferenceBinding[] interfaces = type.superInterfaces();
550 if (interfaces != null && interfaces.length > 0)
551 for (int j = 0, length = interfaces.length; j < length; j++)
552 if (!referencedTypes.containsIdentical(interfaces[j]))
553 referencedTypes.add(interfaces[j]); // to record its supertypes
556 int size = qualifiedReferences.size;
557 char[][][] qualifiedRefs = new char[size][][];
558 for (int i = 0; i < size; i++)
559 qualifiedRefs[i] = qualifiedReferences.elementAt(i);
560 referenceContext.compilationResult.qualifiedReferences = qualifiedRefs;
562 size = simpleNameReferences.size;
563 char[][] simpleRefs = new char[size][];
564 for (int i = 0; i < size; i++)
565 simpleRefs[i] = simpleNameReferences.elementAt(i);
566 referenceContext.compilationResult.simpleNameReferences = simpleRefs;
568 public String toString() {
569 return "--- CompilationUnit Scope : " + new String(referenceContext.getFileName()); //$NON-NLS-1$
571 public void verifyMethods(MethodVerifier verifier) {
572 for (int i = 0, length = topLevelTypes.length; i < length; i++)
573 topLevelTypes[i].verifyMethods(verifier);