1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 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 org.eclipse.jdt.internal.compiler.lookup;
13 import org.eclipse.jdt.core.compiler.CharOperation;
14 import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
15 import org.eclipse.jdt.internal.compiler.ast.ImportReference;
16 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
17 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
18 import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
19 import org.eclipse.jdt.internal.compiler.util.CompoundNameVector;
20 import org.eclipse.jdt.internal.compiler.util.HashtableOfObject;
21 import org.eclipse.jdt.internal.compiler.util.HashtableOfType;
22 import org.eclipse.jdt.internal.compiler.util.ObjectVector;
23 import org.eclipse.jdt.internal.compiler.util.SimpleNameVector;
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;
32 public HashtableOfObject resolvedSingeTypeImports;
34 public SourceTypeBinding[] topLevelTypes;
36 private CompoundNameVector qualifiedReferences;
37 private SimpleNameVector simpleNameReferences;
38 private ObjectVector referencedTypes;
40 HashtableOfType constantPoolNameUsage;
42 public CompilationUnitScope(CompilationUnitDeclaration unit, LookupEnvironment environment) {
43 super(COMPILATION_UNIT_SCOPE, null);
44 this.environment = environment;
45 this.referenceContext = unit;
47 this.currentPackageName = unit.currentPackage == null ? CharOperation.NO_CHAR_CHAR : unit.currentPackage.tokens;
49 if (environment.options.produceReferenceInfo) {
50 this.qualifiedReferences = new CompoundNameVector();
51 this.simpleNameReferences = new SimpleNameVector();
52 this.referencedTypes = new ObjectVector();
54 this.qualifiedReferences = null; // used to test if dependencies should be recorded
55 this.simpleNameReferences = null;
56 this.referencedTypes = null;
59 void buildFieldsAndMethods() {
60 for (int i = 0, length = topLevelTypes.length; i < length; i++)
61 topLevelTypes[i].scope.buildFieldsAndMethods();
63 void buildTypeBindings() {
64 topLevelTypes = new SourceTypeBinding[0]; // want it initialized if the package cannot be resolved
65 if (referenceContext.compilationResult.compilationUnit != null) {
66 char[][] expectedPackageName = referenceContext.compilationResult.compilationUnit.getPackageName();
67 if (expectedPackageName != null
68 && !CharOperation.equals(currentPackageName, expectedPackageName)) {
70 // only report if the unit isn't structurally empty
71 if (referenceContext.currentPackage != null
72 || referenceContext.types != null
73 || referenceContext.imports != null) {
74 problemReporter().packageIsNotExpectedPackage(referenceContext);
76 currentPackageName = expectedPackageName.length == 0 ? CharOperation.NO_CHAR_CHAR : expectedPackageName;
79 if (currentPackageName == CharOperation.NO_CHAR_CHAR) {
80 if ((fPackage = environment.defaultPackage) == null) {
81 problemReporter().mustSpecifyPackage(referenceContext);
85 if ((fPackage = environment.createPackage(currentPackageName)) == null) {
86 problemReporter().packageCollidesWithType(referenceContext);
89 recordQualifiedReference(currentPackageName); // always dependent on your own package
92 // Skip typeDeclarations which know of previously reported errors
93 TypeDeclaration[] types = referenceContext.types;
94 int typeLength = (types == null) ? 0 : types.length;
95 topLevelTypes = new SourceTypeBinding[typeLength];
97 nextType: for (int i = 0; i < typeLength; i++) {
98 TypeDeclaration typeDecl = types[i];
99 ReferenceBinding typeBinding = fPackage.getType0(typeDecl.name);
100 recordSimpleReference(typeDecl.name); // needed to detect collision cases
101 if (typeBinding != null && !(typeBinding instanceof UnresolvedReferenceBinding)) {
102 // if a type exists, it must be a valid type - cannot be a NotFound problem type
103 // unless its an unresolved type which is now being defined
104 problemReporter().duplicateTypes(referenceContext, typeDecl);
107 if (fPackage != environment.defaultPackage && fPackage.getPackage(typeDecl.name) != null) {
108 // if a package exists, it must be a valid package - cannot be a NotFound problem package
109 problemReporter().typeCollidesWithPackage(referenceContext, typeDecl);
113 if ((typeDecl.modifiers & AccPublic) != 0) {
115 if ((mainTypeName = referenceContext.getMainTypeName()) != null // mainTypeName == null means that implementor of ICompilationUnit decided to return null
116 && !CharOperation.equals(mainTypeName, typeDecl.name)) {
117 problemReporter().publicClassMustMatchFileName(referenceContext, typeDecl);
122 ClassScope child = new ClassScope(this, typeDecl);
123 SourceTypeBinding type = child.buildType(null, fPackage);
125 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 if (referenceContext.imports == null) {
135 imports = getDefaultImports();
139 // allocate the import array, add java.lang.* by default
140 int numberOfStatements = referenceContext.imports.length;
141 int numberOfImports = numberOfStatements + 1;
142 for (int i = 0; i < numberOfStatements; i++) {
143 ImportReference importReference = referenceContext.imports[i];
144 if (importReference.onDemand && CharOperation.equals(JAVA_LANG, importReference.tokens)) {
149 ImportBinding[] resolvedImports = new ImportBinding[numberOfImports];
150 resolvedImports[0] = getDefaultImports()[0];
153 nextImport : for (int i = 0; i < numberOfStatements; i++) {
154 ImportReference importReference = referenceContext.imports[i];
155 char[][] compoundName = importReference.tokens;
157 // skip duplicates or imports of the current package
158 for (int j = 0; j < index; j++)
159 if (resolvedImports[j].onDemand == importReference.onDemand)
160 if (CharOperation.equals(compoundName, resolvedImports[j].compoundName))
162 if (importReference.onDemand == true)
163 if (CharOperation.equals(compoundName, currentPackageName))
166 if (importReference.onDemand) {
167 Binding importBinding = findOnDemandImport(compoundName);
168 if (!importBinding.isValidBinding())
169 continue nextImport; // we report all problems in faultInImports()
170 resolvedImports[index++] = new ImportBinding(compoundName, true, importBinding, importReference);
172 resolvedImports[index++] = new ImportBinding(compoundName, false, null, importReference);
176 // shrink resolvedImports... only happens if an error was reported
177 if (resolvedImports.length > index)
178 System.arraycopy(resolvedImports, 0, resolvedImports = new ImportBinding[index], 0, index);
179 imports = resolvedImports;
183 * Innerclasses get their name computed as they are generated, since some may not
184 * be actually outputed if sitting inside unreachable code.
186 public char[] computeConstantPoolName(LocalTypeBinding localType) {
187 if (localType.constantPoolName() != null) {
188 return localType.constantPoolName();
190 // delegates to the outermost enclosing classfile, since it is the only one with a global vision of its innertypes.
192 if (constantPoolNameUsage == null)
193 constantPoolNameUsage = new HashtableOfType();
195 ReferenceBinding outerMostEnclosingType = localType.scope.outerMostClassScope().enclosingSourceType();
197 // ensure there is not already such a local type name defined by the user
199 char[] candidateName;
201 if (localType.isMemberType()){
203 candidateName = CharOperation.concat(
204 localType.enclosingType().constantPoolName(),
205 localType.sourceName,
208 // in case of collision, then member name gets extra $1 inserted
209 // e.g. class X { { class L{} new X(){ class L{} } } }
210 candidateName = CharOperation.concat(
211 localType.enclosingType().constantPoolName(),
213 String.valueOf(index).toCharArray(),
215 localType.sourceName);
217 } else if (localType.isAnonymousType()){
218 candidateName = CharOperation.concat(
219 outerMostEnclosingType.constantPoolName(),
220 String.valueOf(index+1).toCharArray(),
223 candidateName = CharOperation.concat(
224 outerMostEnclosingType.constantPoolName(),
226 String.valueOf(index+1).toCharArray(),
228 localType.sourceName);
230 if (constantPoolNameUsage.get(candidateName) != null) {
233 constantPoolNameUsage.put(candidateName, localType);
237 return candidateName;
240 void connectTypeHierarchy() {
241 for (int i = 0, length = topLevelTypes.length; i < length; i++)
242 topLevelTypes[i].scope.connectTypeHierarchy();
244 void faultInImports() {
245 if (referenceContext.imports == null)
248 // collect the top level type names if a single type import exists
249 int numberOfStatements = referenceContext.imports.length;
250 HashtableOfType typesBySimpleNames = null;
251 for (int i = 0; i < numberOfStatements; i++) {
252 if (!referenceContext.imports[i].onDemand) {
253 typesBySimpleNames = new HashtableOfType(topLevelTypes.length + numberOfStatements);
254 for (int j = 0, length = topLevelTypes.length; j < length; j++)
255 typesBySimpleNames.put(topLevelTypes[j].sourceName, topLevelTypes[j]);
260 // allocate the import array, add java.lang.* by default
261 int numberOfImports = numberOfStatements + 1;
262 for (int i = 0; i < numberOfStatements; i++) {
263 ImportReference importReference = referenceContext.imports[i];
264 if (importReference.onDemand && CharOperation.equals(JAVA_LANG, importReference.tokens)) {
269 ImportBinding[] resolvedImports = new ImportBinding[numberOfImports];
270 resolvedImports[0] = getDefaultImports()[0];
273 nextImport : for (int i = 0; i < numberOfStatements; i++) {
274 ImportReference importReference = referenceContext.imports[i];
275 char[][] compoundName = importReference.tokens;
277 // skip duplicates or imports of the current package
278 for (int j = 0; j < index; j++)
279 if (resolvedImports[j].onDemand == importReference.onDemand)
280 if (CharOperation.equals(compoundName, resolvedImports[j].compoundName)) {
281 problemReporter().unusedImport(importReference); // since skipped, must be reported now
284 if (importReference.onDemand == true)
285 if (CharOperation.equals(compoundName, currentPackageName)) {
286 problemReporter().unusedImport(importReference); // since skipped, must be reported now
289 if (importReference.onDemand) {
290 Binding importBinding = findOnDemandImport(compoundName);
291 if (!importBinding.isValidBinding()) {
292 problemReporter().importProblem(importReference, importBinding);
295 resolvedImports[index++] = new ImportBinding(compoundName, true, importBinding, importReference);
297 Binding typeBinding = findSingleTypeImport(compoundName);
298 if (!typeBinding.isValidBinding()) {
299 problemReporter().importProblem(importReference, typeBinding);
302 if (typeBinding instanceof PackageBinding) {
303 problemReporter().cannotImportPackage(importReference);
306 if (typeBinding instanceof ReferenceBinding) {
307 ReferenceBinding referenceBinding = (ReferenceBinding) typeBinding;
308 if (importReference.isTypeUseDeprecated(referenceBinding, this)) {
309 problemReporter().deprecatedType((TypeBinding) typeBinding, importReference);
312 ReferenceBinding existingType = typesBySimpleNames.get(compoundName[compoundName.length - 1]);
313 if (existingType != null) {
314 // duplicate test above should have caught this case, but make sure
315 if (existingType == typeBinding) {
318 // either the type collides with a top level type or another imported type
319 for (int j = 0, length = topLevelTypes.length; j < length; j++) {
320 if (CharOperation.equals(topLevelTypes[j].sourceName, existingType.sourceName)) {
321 problemReporter().conflictingImport(importReference);
325 problemReporter().duplicateImport(importReference);
328 resolvedImports[index++] = new ImportBinding(compoundName, false, typeBinding, importReference);
329 typesBySimpleNames.put(compoundName[compoundName.length - 1], (ReferenceBinding) typeBinding);
333 // shrink resolvedImports... only happens if an error was reported
334 if (resolvedImports.length > index)
335 System.arraycopy(resolvedImports, 0, resolvedImports = new ImportBinding[index], 0, index);
336 imports = resolvedImports;
338 int length = imports.length;
339 resolvedSingeTypeImports = new HashtableOfObject(length);
340 for (int i = 0; i < length; i++) {
341 ImportBinding binding = imports[i];
342 if (!binding.onDemand)
343 resolvedSingeTypeImports.put(binding.compoundName[binding.compoundName.length - 1], binding);
346 public void faultInTypes() {
349 for (int i = 0, length = topLevelTypes.length; i < length; i++)
350 topLevelTypes[i].faultInTypesForFieldsAndMethods();
352 private Binding findOnDemandImport(char[][] compoundName) {
353 recordQualifiedReference(compoundName);
355 Binding binding = environment.getTopLevelPackage(compoundName[0]);
357 int length = compoundName.length;
358 foundNothingOrType: if (binding != null) {
359 PackageBinding packageBinding = (PackageBinding) binding;
361 binding = packageBinding.getTypeOrPackage(compoundName[i++]);
362 if (binding == null || !binding.isValidBinding()) {
364 break foundNothingOrType;
366 if (!(binding instanceof PackageBinding))
367 break foundNothingOrType;
369 packageBinding = (PackageBinding) binding;
371 return packageBinding;
374 ReferenceBinding type;
375 if (binding == null) {
376 if (environment.defaultPackage == null
377 || environment.options.complianceLevel >= ClassFileConstants.JDK1_4){
378 return new ProblemReferenceBinding(
379 CharOperation.subarray(compoundName, 0, i),
382 type = findType(compoundName[0], environment.defaultPackage, environment.defaultPackage);
383 if (type == null || !type.isValidBinding())
384 return new ProblemReferenceBinding(
385 CharOperation.subarray(compoundName, 0, i),
387 i = 1; // reset to look for member types inside the default package type
389 type = (ReferenceBinding) binding;
392 for (; i < length; i++) {
393 if (!type.canBeSeenBy(fPackage)) {
394 return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, i), type, NotVisible);
396 // does not look for inherited member types on purpose
397 if ((type = type.getMemberType(compoundName[i])) == null) {
398 return new ProblemReferenceBinding(
399 CharOperation.subarray(compoundName, 0, i + 1),
403 if (!type.canBeSeenBy(fPackage))
404 return new ProblemReferenceBinding(compoundName, type, NotVisible);
407 private Binding findSingleTypeImport(char[][] compoundName) {
408 if (compoundName.length == 1) {
409 // findType records the reference
410 // the name cannot be a package
411 if (environment.defaultPackage == null
412 || environment.options.complianceLevel >= ClassFileConstants.JDK1_4)
413 return new ProblemReferenceBinding(compoundName, NotFound);
414 ReferenceBinding typeBinding = findType(compoundName[0], environment.defaultPackage, fPackage);
415 if (typeBinding == null)
416 return new ProblemReferenceBinding(compoundName, NotFound);
419 return findOnDemandImport(compoundName);
421 ImportBinding[] getDefaultImports() {
422 // initialize the default imports if necessary... share the default java.lang.* import
423 if (environment.defaultImports != null) return environment.defaultImports;
425 Binding importBinding = environment.getTopLevelPackage(JAVA);
426 if (importBinding != null)
427 importBinding = ((PackageBinding) importBinding).getTypeOrPackage(JAVA_LANG[1]);
429 // abort if java.lang cannot be found...
430 if (importBinding == null || !importBinding.isValidBinding())
431 problemReporter().isClassPathCorrect(JAVA_LANG_OBJECT, referenceCompilationUnit());
433 return environment.defaultImports = new ImportBinding[] {new ImportBinding(JAVA_LANG, true, importBinding, null)};
435 /* Answer the problem reporter to use for raising new problems.
437 * Note that as a side-effect, this updates the current reference context
438 * (unit, type or method) in case the problem handler decides it is necessary
442 public ProblemReporter problemReporter() {
443 ProblemReporter problemReporter = referenceContext.problemReporter;
444 problemReporter.referenceContext = referenceContext;
445 return problemReporter;
449 What do we hold onto:
451 1. when we resolve 'a.b.c', say we keep only 'a.b.c'
452 & when we fail to resolve 'c' in 'a.b', lets keep 'a.b.c'
453 THEN when we come across a new/changed/removed item named 'a.b.c',
454 we would find all references to 'a.b.c'
455 -> This approach fails because every type is resolved in every onDemand import to
456 detect collision cases... so the references could be 10 times bigger than necessary.
458 2. when we resolve 'a.b.c', lets keep 'a.b' & 'c'
459 & when we fail to resolve 'c' in 'a.b', lets keep 'a.b' & 'c'
460 THEN when we come across a new/changed/removed item named 'a.b.c',
461 we would find all references to 'a.b' & 'c'
462 -> This approach does not have a space problem but fails to handle collision cases.
463 What happens if a type is added named 'a.b'? We would search for 'a' & 'b' but
464 would not find a match.
466 3. when we resolve 'a.b.c', lets keep 'a', 'a.b' & 'a', 'b', 'c'
467 & when we fail to resolve 'c' in 'a.b', lets keep 'a', 'a.b' & 'a', 'b', 'c'
468 THEN when we come across a new/changed/removed item named 'a.b.c',
469 we would find all references to 'a.b' & 'c'
470 OR 'a.b' -> 'a' & 'b'
472 -> As long as each single char[] is interned, we should not have a space problem
473 and can handle collision cases.
475 4. when we resolve 'a.b.c', lets keep 'a.b' & 'a', 'b', 'c'
476 & when we fail to resolve 'c' in 'a.b', lets keep 'a.b' & 'a', 'b', 'c'
477 THEN when we come across a new/changed/removed item named 'a.b.c',
478 we would find all references to 'a.b' & 'c'
479 OR 'a.b' -> 'a' & 'b' in the simple name collection
480 OR 'a' -> 'a' in the simple name collection
481 -> As long as each single char[] is interned, we should not have a space problem
482 and can handle collision cases.
484 void recordQualifiedReference(char[][] qualifiedName) {
485 if (qualifiedReferences == null) return; // not recording dependencies
487 int length = qualifiedName.length;
489 while (!qualifiedReferences.contains(qualifiedName)) {
490 qualifiedReferences.add(qualifiedName);
492 recordSimpleReference(qualifiedName[0]);
493 recordSimpleReference(qualifiedName[1]);
497 recordSimpleReference(qualifiedName[length]);
498 System.arraycopy(qualifiedName, 0, qualifiedName = new char[length][], 0, length);
500 } else if (length == 1) {
501 recordSimpleReference(qualifiedName[0]);
504 void recordReference(char[][] qualifiedEnclosingName, char[] simpleName) {
505 recordQualifiedReference(qualifiedEnclosingName);
506 recordSimpleReference(simpleName);
508 void recordSimpleReference(char[] simpleName) {
509 if (simpleNameReferences == null) return; // not recording dependencies
511 if (!simpleNameReferences.contains(simpleName))
512 simpleNameReferences.add(simpleName);
514 void recordTypeReference(TypeBinding type) {
515 if (referencedTypes == null) return; // not recording dependencies
517 if (type.isArrayType())
518 type = ((ArrayBinding) type).leafComponentType;
520 if (type.isBaseType()) return;
521 if (referencedTypes.containsIdentical(type)) return;
522 if (((ReferenceBinding) type).isLocalType()) return;
524 referencedTypes.add(type);
526 void recordTypeReferences(TypeBinding[] types) {
527 if (qualifiedReferences == null) return; // not recording dependencies
528 if (types == null || types.length == 0) return;
530 for (int i = 0, max = types.length; i < max; i++) {
531 // No need to record supertypes of method arguments & thrown exceptions, just the compoundName
532 // If a field/method is retrieved from such a type then a separate call does the job
533 TypeBinding type = types[i];
534 if (type.isArrayType())
535 type = ((ArrayBinding) type).leafComponentType;
536 if (!type.isBaseType()) {
537 ReferenceBinding actualType = (ReferenceBinding) type;
538 if (!actualType.isLocalType())
539 recordQualifiedReference(actualType.isMemberType()
540 ? CharOperation.splitOn('.', actualType.readableName())
541 : actualType.compoundName);
545 Binding resolveSingleTypeImport(ImportBinding importBinding) {
546 if (importBinding.resolvedImport == null) {
547 importBinding.resolvedImport = findSingleTypeImport(importBinding.compoundName);
548 if (!importBinding.resolvedImport.isValidBinding() || importBinding.resolvedImport instanceof PackageBinding) {
549 if (this.imports != null){
550 ImportBinding[] newImports = new ImportBinding[imports.length - 1];
551 for (int i = 0, n = 0, max = this.imports.length; i < max; i++)
552 if (this.imports[i] != importBinding){
553 newImports[n++] = this.imports[i];
555 this.imports = newImports;
560 return importBinding.resolvedImport;
562 public void storeDependencyInfo() {
563 // add the type hierarchy of each referenced type
564 // cannot do early since the hierarchy may not be fully resolved
565 for (int i = 0; i < referencedTypes.size; i++) { // grows as more types are added
566 ReferenceBinding type = (ReferenceBinding) referencedTypes.elementAt(i);
567 if (!type.isLocalType()) {
568 recordQualifiedReference(type.isMemberType()
569 ? CharOperation.splitOn('.', type.readableName())
570 : type.compoundName);
571 ReferenceBinding enclosing = type.enclosingType();
572 if (enclosing != null && !referencedTypes.containsIdentical(enclosing))
573 referencedTypes.add(enclosing); // to record its supertypes
575 ReferenceBinding superclass = type.superclass();
576 if (superclass != null && !referencedTypes.containsIdentical(superclass))
577 referencedTypes.add(superclass); // to record its supertypes
578 ReferenceBinding[] interfaces = type.superInterfaces();
579 if (interfaces != null && interfaces.length > 0)
580 for (int j = 0, length = interfaces.length; j < length; j++)
581 if (!referencedTypes.containsIdentical(interfaces[j]))
582 referencedTypes.add(interfaces[j]); // to record its supertypes
585 int size = qualifiedReferences.size;
586 char[][][] qualifiedRefs = new char[size][][];
587 for (int i = 0; i < size; i++)
588 qualifiedRefs[i] = qualifiedReferences.elementAt(i);
589 referenceContext.compilationResult.qualifiedReferences = qualifiedRefs;
591 size = simpleNameReferences.size;
592 char[][] simpleRefs = new char[size][];
593 for (int i = 0; i < size; i++)
594 simpleRefs[i] = simpleNameReferences.elementAt(i);
595 referenceContext.compilationResult.simpleNameReferences = simpleRefs;
597 public String toString() {
598 return "--- CompilationUnit Scope : " + new String(referenceContext.getFileName()); //$NON-NLS-1$
600 public void verifyMethods(MethodVerifier verifier) {
601 for (int i = 0, length = topLevelTypes.length; i < length; i++)
602 topLevelTypes[i].verifyMethods(verifier);