| /******************************************************************************* |
| * Copyright (c) 2000, 2005 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.compiler.lookup; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.ast.*; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.env.AccessRestriction; |
| import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; |
| import org.eclipse.jdt.internal.compiler.util.*; |
| |
| public class CompilationUnitScope extends Scope { |
| |
| public LookupEnvironment environment; |
| public CompilationUnitDeclaration referenceContext; |
| public char[][] currentPackageName; |
| public PackageBinding fPackage; |
| public ImportBinding[] imports; |
| public HashtableOfObject resolvedSingeTypeImports; |
| |
| public SourceTypeBinding[] topLevelTypes; |
| |
| private CompoundNameVector qualifiedReferences; |
| private SimpleNameVector simpleNameReferences; |
| private ObjectVector referencedTypes; |
| private ObjectVector referencedSuperTypes; |
| |
| HashtableOfType constantPoolNameUsage; |
| |
| public CompilationUnitScope(CompilationUnitDeclaration unit, LookupEnvironment environment) { |
| super(COMPILATION_UNIT_SCOPE, null); |
| this.environment = environment; |
| this.referenceContext = unit; |
| unit.scope = this; |
| this.currentPackageName = unit.currentPackage == null ? CharOperation.NO_CHAR_CHAR : unit.currentPackage.tokens; |
| |
| if (environment.options.produceReferenceInfo) { |
| this.qualifiedReferences = new CompoundNameVector(); |
| this.simpleNameReferences = new SimpleNameVector(); |
| this.referencedTypes = new ObjectVector(); |
| this.referencedSuperTypes = new ObjectVector(); |
| } else { |
| this.qualifiedReferences = null; // used to test if dependencies should be recorded |
| this.simpleNameReferences = null; |
| this.referencedTypes = null; |
| this.referencedSuperTypes = null; |
| } |
| } |
| void buildFieldsAndMethods() { |
| for (int i = 0, length = topLevelTypes.length; i < length; i++) |
| topLevelTypes[i].scope.buildFieldsAndMethods(); |
| } |
| void buildTypeBindings(AccessRestriction accessRestriction) { |
| topLevelTypes = new SourceTypeBinding[0]; // want it initialized if the package cannot be resolved |
| if (referenceContext.compilationResult.compilationUnit != null) { |
| char[][] expectedPackageName = referenceContext.compilationResult.compilationUnit.getPackageName(); |
| if (expectedPackageName != null |
| && !CharOperation.equals(currentPackageName, expectedPackageName)) { |
| |
| // only report if the unit isn't structurally empty |
| if (referenceContext.currentPackage != null |
| || referenceContext.types != null |
| || referenceContext.imports != null) { |
| problemReporter().packageIsNotExpectedPackage(referenceContext); |
| } |
| currentPackageName = expectedPackageName.length == 0 ? CharOperation.NO_CHAR_CHAR : expectedPackageName; |
| } |
| } |
| if (currentPackageName == CharOperation.NO_CHAR_CHAR) { |
| if ((fPackage = environment.defaultPackage) == null) { |
| problemReporter().mustSpecifyPackage(referenceContext); |
| return; |
| } |
| } else { |
| if ((fPackage = environment.createPackage(currentPackageName)) == null) { |
| problemReporter().packageCollidesWithType(referenceContext); |
| return; |
| } |
| recordQualifiedReference(currentPackageName); // always dependent on your own package |
| } |
| |
| // Skip typeDeclarations which know of previously reported errors |
| TypeDeclaration[] types = referenceContext.types; |
| int typeLength = (types == null) ? 0 : types.length; |
| topLevelTypes = new SourceTypeBinding[typeLength]; |
| int count = 0; |
| nextType: for (int i = 0; i < typeLength; i++) { |
| TypeDeclaration typeDecl = types[i]; |
| ReferenceBinding typeBinding = fPackage.getType0(typeDecl.name); |
| recordSimpleReference(typeDecl.name); // needed to detect collision cases |
| if (typeBinding != null && !(typeBinding instanceof UnresolvedReferenceBinding)) { |
| // if a type exists, it must be a valid type - cannot be a NotFound problem type |
| // unless its an unresolved type which is now being defined |
| problemReporter().duplicateTypes(referenceContext, typeDecl); |
| continue nextType; |
| } |
| if (fPackage != environment.defaultPackage && fPackage.getPackage(typeDecl.name) != null) { |
| // if a package exists, it must be a valid package - cannot be a NotFound problem package |
| problemReporter().typeCollidesWithPackage(referenceContext, typeDecl); |
| continue nextType; |
| } |
| |
| if ((typeDecl.modifiers & AccPublic) != 0) { |
| char[] mainTypeName; |
| if ((mainTypeName = referenceContext.getMainTypeName()) != null // mainTypeName == null means that implementor of ICompilationUnit decided to return null |
| && !CharOperation.equals(mainTypeName, typeDecl.name)) { |
| problemReporter().publicClassMustMatchFileName(referenceContext, typeDecl); |
| // tolerate faulty main type name (91091), allow to proceed into type construction |
| } |
| } |
| |
| ClassScope child = new ClassScope(this, typeDecl); |
| SourceTypeBinding type = child.buildType(null, fPackage, accessRestriction); |
| if(type != null) { |
| topLevelTypes[count++] = type; |
| } |
| } |
| |
| // shrink topLevelTypes... only happens if an error was reported |
| if (count != topLevelTypes.length) |
| System.arraycopy(topLevelTypes, 0, topLevelTypes = new SourceTypeBinding[count], 0, count); |
| } |
| void checkAndSetImports() { |
| if (referenceContext.imports == null) { |
| imports = getDefaultImports(); |
| return; |
| } |
| |
| // allocate the import array, add java.lang.* by default |
| int numberOfStatements = referenceContext.imports.length; |
| int numberOfImports = numberOfStatements + 1; |
| for (int i = 0; i < numberOfStatements; i++) { |
| ImportReference importReference = referenceContext.imports[i]; |
| if (importReference.onDemand && CharOperation.equals(JAVA_LANG, importReference.tokens) && !importReference.isStatic()) { |
| numberOfImports--; |
| break; |
| } |
| } |
| ImportBinding[] resolvedImports = new ImportBinding[numberOfImports]; |
| resolvedImports[0] = getDefaultImports()[0]; |
| int index = 1; |
| |
| nextImport : for (int i = 0; i < numberOfStatements; i++) { |
| ImportReference importReference = referenceContext.imports[i]; |
| char[][] compoundName = importReference.tokens; |
| |
| // skip duplicates or imports of the current package |
| for (int j = 0; j < index; j++) { |
| ImportBinding resolved = resolvedImports[j]; |
| if (resolved.onDemand == importReference.onDemand && resolved.isStatic() == importReference.isStatic()) |
| if (CharOperation.equals(compoundName, resolvedImports[j].compoundName)) |
| continue nextImport; |
| } |
| |
| if (importReference.onDemand) { |
| if (CharOperation.equals(compoundName, currentPackageName)) |
| continue nextImport; |
| |
| Binding importBinding = findImport(compoundName, compoundName.length); |
| if (!importBinding.isValidBinding() || (importReference.isStatic() && importBinding instanceof PackageBinding)) |
| continue nextImport; // we report all problems in faultInImports() |
| resolvedImports[index++] = new ImportBinding(compoundName, true, importBinding, importReference); |
| } else { |
| // resolve single imports only when the last name matches |
| resolvedImports[index++] = new ImportBinding(compoundName, false, null, importReference); |
| } |
| } |
| |
| // shrink resolvedImports... only happens if an error was reported |
| if (resolvedImports.length > index) |
| System.arraycopy(resolvedImports, 0, resolvedImports = new ImportBinding[index], 0, index); |
| imports = resolvedImports; |
| } |
| /* |
| * INTERNAL USE-ONLY |
| * Innerclasses get their name computed as they are generated, since some may not |
| * be actually outputed if sitting inside unreachable code. |
| */ |
| public char[] computeConstantPoolName(LocalTypeBinding localType) { |
| if (localType.constantPoolName() != null) { |
| return localType.constantPoolName(); |
| } |
| // delegates to the outermost enclosing classfile, since it is the only one with a global vision of its innertypes. |
| |
| if (constantPoolNameUsage == null) |
| constantPoolNameUsage = new HashtableOfType(); |
| |
| ReferenceBinding outerMostEnclosingType = localType.scope.outerMostClassScope().enclosingSourceType(); |
| |
| // ensure there is not already such a local type name defined by the user |
| int index = 0; |
| char[] candidateName; |
| while(true) { |
| if (localType.isMemberType()){ |
| if (index == 0){ |
| candidateName = CharOperation.concat( |
| localType.enclosingType().constantPoolName(), |
| localType.sourceName, |
| '$'); |
| } else { |
| // in case of collision, then member name gets extra $1 inserted |
| // e.g. class X { { class L{} new X(){ class L{} } } } |
| candidateName = CharOperation.concat( |
| localType.enclosingType().constantPoolName(), |
| '$', |
| String.valueOf(index).toCharArray(), |
| '$', |
| localType.sourceName); |
| } |
| } else if (localType.isAnonymousType()){ |
| candidateName = CharOperation.concat( |
| outerMostEnclosingType.constantPoolName(), |
| String.valueOf(index+1).toCharArray(), |
| '$'); |
| } else { |
| candidateName = CharOperation.concat( |
| outerMostEnclosingType.constantPoolName(), |
| '$', |
| String.valueOf(index+1).toCharArray(), |
| '$', |
| localType.sourceName); |
| } |
| if (constantPoolNameUsage.get(candidateName) != null) { |
| index ++; |
| } else { |
| constantPoolNameUsage.put(candidateName, localType); |
| break; |
| } |
| } |
| return candidateName; |
| } |
| |
| void connectTypeHierarchy() { |
| for (int i = 0, length = topLevelTypes.length; i < length; i++) |
| topLevelTypes[i].scope.connectTypeHierarchy(); |
| } |
| void faultInImports() { |
| if (referenceContext.imports == null) |
| return; |
| |
| // collect the top level type names if a single type import exists |
| int numberOfStatements = referenceContext.imports.length; |
| HashtableOfType typesBySimpleNames = null; |
| for (int i = 0; i < numberOfStatements; i++) { |
| if (!referenceContext.imports[i].onDemand) { |
| typesBySimpleNames = new HashtableOfType(topLevelTypes.length + numberOfStatements); |
| for (int j = 0, length = topLevelTypes.length; j < length; j++) |
| typesBySimpleNames.put(topLevelTypes[j].sourceName, topLevelTypes[j]); |
| break; |
| } |
| } |
| |
| // allocate the import array, add java.lang.* by default |
| int numberOfImports = numberOfStatements + 1; |
| for (int i = 0; i < numberOfStatements; i++) { |
| ImportReference importReference = referenceContext.imports[i]; |
| if (importReference.onDemand && CharOperation.equals(JAVA_LANG, importReference.tokens) && !importReference.isStatic()) { |
| numberOfImports--; |
| break; |
| } |
| } |
| ImportBinding[] resolvedImports = new ImportBinding[numberOfImports]; |
| resolvedImports[0] = getDefaultImports()[0]; |
| int index = 1; |
| |
| // keep static imports with normal imports until there is a reason to split them up |
| // on demand imports continue to be packages & types. need to check on demand type imports for fields/methods |
| // single imports change from being just types to types or fields |
| nextImport : for (int i = 0; i < numberOfStatements; i++) { |
| ImportReference importReference = referenceContext.imports[i]; |
| char[][] compoundName = importReference.tokens; |
| |
| // skip duplicates or imports of the current package |
| for (int j = 0; j < index; j++) { |
| ImportBinding resolved = resolvedImports[j]; |
| if (resolved.onDemand == importReference.onDemand && resolved.isStatic() == importReference.isStatic()) { |
| if (CharOperation.equals(compoundName, resolved.compoundName)) { |
| problemReporter().unusedImport(importReference); // since skipped, must be reported now |
| continue nextImport; |
| } |
| } |
| } |
| if (importReference.onDemand) { |
| if (CharOperation.equals(compoundName, currentPackageName)) { |
| problemReporter().unusedImport(importReference); // since skipped, must be reported now |
| continue nextImport; |
| } |
| |
| Binding importBinding = findImport(compoundName, compoundName.length); |
| if (!importBinding.isValidBinding()) { |
| problemReporter().importProblem(importReference, importBinding); |
| continue nextImport; |
| } |
| if (importReference.isStatic() && importBinding instanceof PackageBinding) { |
| problemReporter().cannotImportPackage(importReference); |
| continue nextImport; |
| } |
| resolvedImports[index++] = new ImportBinding(compoundName, true, importBinding, importReference); |
| } else { |
| Binding importBinding = findSingleImport(compoundName, importReference.isStatic()); |
| if (!importBinding.isValidBinding()) { |
| problemReporter().importProblem(importReference, importBinding); |
| continue nextImport; |
| } |
| if (importBinding instanceof PackageBinding) { |
| problemReporter().cannotImportPackage(importReference); |
| continue nextImport; |
| } |
| // collisions between an imported static field & a type should be checked according to spec... but currently not by javac |
| if (importBinding instanceof ReferenceBinding) { |
| ReferenceBinding referenceBinding = (ReferenceBinding) importBinding; |
| if (importReference.isTypeUseDeprecated(referenceBinding, this)) |
| problemReporter().deprecatedType(referenceBinding, importReference); |
| |
| ReferenceBinding existingType = typesBySimpleNames.get(compoundName[compoundName.length - 1]); |
| if (existingType != null) { |
| // duplicate test above should have caught this case, but make sure |
| if (existingType == referenceBinding) |
| continue nextImport; |
| // either the type collides with a top level type or another imported type |
| for (int j = 0, length = topLevelTypes.length; j < length; j++) { |
| if (CharOperation.equals(topLevelTypes[j].sourceName, existingType.sourceName)) { |
| problemReporter().conflictingImport(importReference); |
| continue nextImport; |
| } |
| } |
| problemReporter().duplicateImport(importReference); |
| continue nextImport; |
| } |
| typesBySimpleNames.put(compoundName[compoundName.length - 1], referenceBinding); |
| } else if (importBinding instanceof FieldBinding) { |
| for (int j = 0; j < index; j++) { |
| ImportBinding resolved = resolvedImports[j]; |
| // find other static fields with the same name |
| if (resolved.isStatic() && resolved.resolvedImport instanceof FieldBinding && importBinding != resolved.resolvedImport) { |
| if (CharOperation.equals(compoundName[compoundName.length - 1], resolved.compoundName[resolved.compoundName.length - 1])) { |
| problemReporter().duplicateImport(importReference); |
| continue nextImport; |
| } |
| } |
| } |
| } |
| resolvedImports[index++] = new ImportBinding(compoundName, false, importBinding, importReference); |
| } |
| } |
| |
| // shrink resolvedImports... only happens if an error was reported |
| if (resolvedImports.length > index) |
| System.arraycopy(resolvedImports, 0, resolvedImports = new ImportBinding[index], 0, index); |
| imports = resolvedImports; |
| |
| int length = imports.length; |
| resolvedSingeTypeImports = new HashtableOfObject(length); |
| for (int i = 0; i < length; i++) { |
| ImportBinding binding = imports[i]; |
| if (!binding.onDemand && binding.resolvedImport instanceof ReferenceBinding) |
| resolvedSingeTypeImports.put(binding.compoundName[binding.compoundName.length - 1], binding); |
| } |
| } |
| public void faultInTypes() { |
| faultInImports(); |
| |
| for (int i = 0, length = topLevelTypes.length; i < length; i++) |
| topLevelTypes[i].faultInTypesForFieldsAndMethods(); |
| } |
| private Binding findImport(char[][] compoundName, int length) { |
| recordQualifiedReference(compoundName); |
| |
| Binding binding = environment.getTopLevelPackage(compoundName[0]); |
| int i = 1; |
| foundNothingOrType: if (binding != null) { |
| PackageBinding packageBinding = (PackageBinding) binding; |
| while (i < length) { |
| binding = packageBinding.getTypeOrPackage(compoundName[i++]); |
| if (binding == null || !binding.isValidBinding()) { |
| binding = null; |
| break foundNothingOrType; |
| } |
| if (!(binding instanceof PackageBinding)) |
| break foundNothingOrType; |
| |
| packageBinding = (PackageBinding) binding; |
| } |
| return packageBinding; |
| } |
| |
| ReferenceBinding type; |
| if (binding == null) { |
| if (environment.defaultPackage == null || environment.options.complianceLevel >= ClassFileConstants.JDK1_4) |
| return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, i), NotFound); |
| type = findType(compoundName[0], environment.defaultPackage, environment.defaultPackage); |
| if (type == null || !type.isValidBinding()) |
| return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, i), NotFound); |
| i = 1; // reset to look for member types inside the default package type |
| } else { |
| type = (ReferenceBinding) binding; |
| } |
| |
| while (i < length) { |
| if (!type.canBeSeenBy(fPackage)) |
| return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, i), type, NotVisible); |
| |
| char[] name = compoundName[i++]; |
| // does not look for inherited member types on purpose, only immediate members |
| type = type.getMemberType(name); |
| if (type == null) |
| return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, i), NotFound); |
| } |
| if (!type.canBeSeenBy(fPackage)) |
| return new ProblemReferenceBinding(compoundName, type, NotVisible); |
| return type; |
| } |
| private Binding findSingleImport(char[][] compoundName, boolean findStaticImports) { |
| if (compoundName.length == 1) { |
| // findType records the reference |
| // the name cannot be a package |
| if (environment.defaultPackage == null || environment.options.complianceLevel >= ClassFileConstants.JDK1_4) |
| return new ProblemReferenceBinding(compoundName, NotFound); |
| ReferenceBinding typeBinding = findType(compoundName[0], environment.defaultPackage, fPackage); |
| if (typeBinding == null) |
| return new ProblemReferenceBinding(compoundName, NotFound); |
| return typeBinding; |
| } |
| |
| if (findStaticImports) |
| return findSingleStaticImport(compoundName); |
| return findImport(compoundName, compoundName.length); |
| } |
| private Binding findSingleStaticImport(char[][] compoundName) { |
| Binding binding = findImport(compoundName, compoundName.length - 1); |
| if (!binding.isValidBinding()) return binding; |
| |
| char[] name = compoundName[compoundName.length - 1]; |
| if (binding instanceof PackageBinding) { |
| Binding temp = ((PackageBinding) binding).getTypeOrPackage(name); |
| if (temp != null && temp instanceof ReferenceBinding) // must resolve to a member type or field, not a top level type |
| return new ProblemReferenceBinding(compoundName, InvalidTypeForStaticImport); |
| return binding; // cannot be a package, error is caught in sender |
| } |
| |
| // look to see if its a static field first |
| ReferenceBinding type = (ReferenceBinding) binding; |
| FieldBinding field = findField(type, name, null, true); |
| if (field != null && field.isValidBinding() && field.isStatic() && field.canBeSeenBy(fPackage)) |
| return field; |
| |
| // look to see if there is a static method with the same selector |
| MethodBinding method = findStaticMethod(type, name); |
| if (method != null) return method; |
| |
| type = findMemberType(name, type); |
| if (type == null || !type.isStatic()) |
| return new ProblemReferenceBinding(compoundName, type, NotFound); |
| if (!type.canBeSeenBy(fPackage)) |
| return new ProblemReferenceBinding(compoundName, type, NotVisible); |
| return type; |
| } |
| MethodBinding findStaticMethod(ReferenceBinding currentType, char[] selector) { |
| if (!currentType.canBeSeenBy(this)) |
| return null; |
| |
| do { |
| MethodBinding[] methods = currentType.getMethods(selector); |
| if (methods != NoMethods) { |
| for (int i = methods.length; --i >= 0;) { |
| MethodBinding method = methods[i]; |
| if (method.isStatic() && method.canBeSeenBy(fPackage)) |
| return method; |
| } |
| } |
| if (currentType.superInterfaces() == null) // needed for statically imported types which don't know their hierarchy yet |
| ((SourceTypeBinding) currentType).scope.connectTypeHierarchy(); |
| } while ((currentType = currentType.superclass()) != null); |
| return null; |
| } |
| ImportBinding[] getDefaultImports() { |
| // initialize the default imports if necessary... share the default java.lang.* import |
| if (environment.defaultImports != null) return environment.defaultImports; |
| |
| Binding importBinding = environment.getTopLevelPackage(JAVA); |
| if (importBinding != null) |
| importBinding = ((PackageBinding) importBinding).getTypeOrPackage(JAVA_LANG[1]); |
| |
| // abort if java.lang cannot be found... |
| if (importBinding == null || !importBinding.isValidBinding()) |
| problemReporter().isClassPathCorrect(JAVA_LANG_OBJECT, referenceCompilationUnit()); |
| |
| return environment.defaultImports = new ImportBinding[] {new ImportBinding(JAVA_LANG, true, importBinding, null)}; |
| } |
| // NOT Public API |
| public final Binding getImport(char[][] compoundName, boolean onDemand, boolean isStaticImport) { |
| if (onDemand) |
| return findImport(compoundName, compoundName.length); |
| return findSingleImport(compoundName, isStaticImport); |
| } |
| /* Answer the problem reporter to use for raising new problems. |
| * |
| * Note that as a side-effect, this updates the current reference context |
| * (unit, type or method) in case the problem handler decides it is necessary |
| * to abort. |
| */ |
| |
| public ProblemReporter problemReporter() { |
| ProblemReporter problemReporter = referenceContext.problemReporter; |
| problemReporter.referenceContext = referenceContext; |
| return problemReporter; |
| } |
| |
| /* |
| What do we hold onto: |
| |
| 1. when we resolve 'a.b.c', say we keep only 'a.b.c' |
| & when we fail to resolve 'c' in 'a.b', lets keep 'a.b.c' |
| THEN when we come across a new/changed/removed item named 'a.b.c', |
| we would find all references to 'a.b.c' |
| -> This approach fails because every type is resolved in every onDemand import to |
| detect collision cases... so the references could be 10 times bigger than necessary. |
| |
| 2. when we resolve 'a.b.c', lets keep 'a.b' & 'c' |
| & when we fail to resolve 'c' in 'a.b', lets keep 'a.b' & 'c' |
| THEN when we come across a new/changed/removed item named 'a.b.c', |
| we would find all references to 'a.b' & 'c' |
| -> This approach does not have a space problem but fails to handle collision cases. |
| What happens if a type is added named 'a.b'? We would search for 'a' & 'b' but |
| would not find a match. |
| |
| 3. when we resolve 'a.b.c', lets keep 'a', 'a.b' & 'a', 'b', 'c' |
| & when we fail to resolve 'c' in 'a.b', lets keep 'a', 'a.b' & 'a', 'b', 'c' |
| THEN when we come across a new/changed/removed item named 'a.b.c', |
| we would find all references to 'a.b' & 'c' |
| OR 'a.b' -> 'a' & 'b' |
| OR 'a' -> '' & 'a' |
| -> As long as each single char[] is interned, we should not have a space problem |
| and can handle collision cases. |
| |
| 4. when we resolve 'a.b.c', lets keep 'a.b' & 'a', 'b', 'c' |
| & when we fail to resolve 'c' in 'a.b', lets keep 'a.b' & 'a', 'b', 'c' |
| THEN when we come across a new/changed/removed item named 'a.b.c', |
| we would find all references to 'a.b' & 'c' |
| OR 'a.b' -> 'a' & 'b' in the simple name collection |
| OR 'a' -> 'a' in the simple name collection |
| -> As long as each single char[] is interned, we should not have a space problem |
| and can handle collision cases. |
| */ |
| void recordQualifiedReference(char[][] qualifiedName) { |
| if (qualifiedReferences == null) return; // not recording dependencies |
| |
| int length = qualifiedName.length; |
| if (length > 1) { |
| while (!qualifiedReferences.contains(qualifiedName)) { |
| qualifiedReferences.add(qualifiedName); |
| if (length == 2) { |
| recordSimpleReference(qualifiedName[0]); |
| recordSimpleReference(qualifiedName[1]); |
| return; |
| } |
| length--; |
| recordSimpleReference(qualifiedName[length]); |
| System.arraycopy(qualifiedName, 0, qualifiedName = new char[length][], 0, length); |
| } |
| } else if (length == 1) { |
| recordSimpleReference(qualifiedName[0]); |
| } |
| } |
| void recordReference(char[][] qualifiedEnclosingName, char[] simpleName) { |
| recordQualifiedReference(qualifiedEnclosingName); |
| recordSimpleReference(simpleName); |
| } |
| void recordReference(ReferenceBinding type, char[] simpleName) { |
| ReferenceBinding actualType = typeToRecord(type); |
| if (actualType != null) |
| recordReference(actualType.compoundName, simpleName); |
| } |
| void recordSimpleReference(char[] simpleName) { |
| if (simpleNameReferences == null) return; // not recording dependencies |
| |
| if (!simpleNameReferences.contains(simpleName)) |
| simpleNameReferences.add(simpleName); |
| } |
| void recordSuperTypeReference(TypeBinding type) { |
| if (referencedSuperTypes == null) return; // not recording dependencies |
| |
| ReferenceBinding actualType = typeToRecord(type); |
| if (actualType != null && !referencedSuperTypes.containsIdentical(actualType)) |
| referencedSuperTypes.add(actualType); |
| } |
| public void recordTypeConversion(TypeBinding superType, TypeBinding subType) { |
| recordSuperTypeReference(subType); // must record the hierarchy of the subType that is converted to the superType |
| } |
| void recordTypeReference(TypeBinding type) { |
| if (referencedTypes == null) return; // not recording dependencies |
| |
| ReferenceBinding actualType = typeToRecord(type); |
| if (actualType != null && !referencedTypes.containsIdentical(actualType)) |
| referencedTypes.add(actualType); |
| } |
| void recordTypeReferences(TypeBinding[] types) { |
| if (referencedTypes == null) return; // not recording dependencies |
| if (types == null || types.length == 0) return; |
| |
| for (int i = 0, max = types.length; i < max; i++) { |
| // No need to record supertypes of method arguments & thrown exceptions, just the compoundName |
| // If a field/method is retrieved from such a type then a separate call does the job |
| ReferenceBinding actualType = typeToRecord(types[i]); |
| if (actualType != null && !referencedTypes.containsIdentical(actualType)) |
| referencedTypes.add(actualType); |
| } |
| } |
| Binding resolveSingleImport(ImportBinding importBinding) { |
| if (importBinding.resolvedImport == null) { |
| importBinding.resolvedImport = findSingleImport(importBinding.compoundName, importBinding.isStatic()); |
| if (!importBinding.resolvedImport.isValidBinding() || importBinding.resolvedImport instanceof PackageBinding) { |
| if (this.imports != null) { |
| ImportBinding[] newImports = new ImportBinding[imports.length - 1]; |
| for (int i = 0, n = 0, max = this.imports.length; i < max; i++) |
| if (this.imports[i] != importBinding) |
| newImports[n++] = this.imports[i]; |
| this.imports = newImports; |
| } |
| return null; |
| } |
| } |
| return importBinding.resolvedImport; |
| } |
| public void storeDependencyInfo() { |
| // add the type hierarchy of each referenced supertype |
| // cannot do early since the hierarchy may not be fully resolved |
| for (int i = 0; i < referencedSuperTypes.size; i++) { // grows as more types are added |
| ReferenceBinding type = (ReferenceBinding) referencedSuperTypes.elementAt(i); |
| if (!referencedTypes.containsIdentical(type)) |
| referencedTypes.add(type); |
| |
| if (!type.isLocalType()) { |
| ReferenceBinding enclosing = type.enclosingType(); |
| if (enclosing != null) |
| recordSuperTypeReference(enclosing); |
| } |
| ReferenceBinding superclass = type.superclass(); |
| if (superclass != null) |
| recordSuperTypeReference(superclass); |
| ReferenceBinding[] interfaces = type.superInterfaces(); |
| if (interfaces != null) |
| for (int j = 0, length = interfaces.length; j < length; j++) |
| recordSuperTypeReference(interfaces[j]); |
| } |
| |
| for (int i = 0, l = referencedTypes.size; i < l; i++) { |
| ReferenceBinding type = (ReferenceBinding) referencedTypes.elementAt(i); |
| if (!type.isLocalType()) |
| recordQualifiedReference(type.isMemberType() |
| ? CharOperation.splitOn('.', type.readableName()) |
| : type.compoundName); |
| } |
| |
| int size = qualifiedReferences.size; |
| char[][][] qualifiedRefs = new char[size][][]; |
| for (int i = 0; i < size; i++) |
| qualifiedRefs[i] = qualifiedReferences.elementAt(i); |
| referenceContext.compilationResult.qualifiedReferences = qualifiedRefs; |
| |
| size = simpleNameReferences.size; |
| char[][] simpleRefs = new char[size][]; |
| for (int i = 0; i < size; i++) |
| simpleRefs[i] = simpleNameReferences.elementAt(i); |
| referenceContext.compilationResult.simpleNameReferences = simpleRefs; |
| } |
| public String toString() { |
| return "--- CompilationUnit Scope : " + new String(referenceContext.getFileName()); //$NON-NLS-1$ |
| } |
| private ReferenceBinding typeToRecord(TypeBinding type) { |
| if (type.isArrayType()) |
| type = ((ArrayBinding) type).leafComponentType; |
| |
| switch (type.kind()) { |
| case Binding.BASE_TYPE : |
| case Binding.TYPE_PARAMETER : |
| case Binding.WILDCARD_TYPE : |
| return null; |
| case Binding.PARAMETERIZED_TYPE : |
| case Binding.RAW_TYPE : |
| type = type.erasure(); |
| } |
| ReferenceBinding refType = (ReferenceBinding) type; |
| if (refType.isLocalType()) return null; |
| return refType; |
| } |
| public void verifyMethods(MethodVerifier verifier) { |
| for (int i = 0, length = topLevelTypes.length; i < length; i++) |
| topLevelTypes[i].verifyMethods(verifier); |
| } |
| } |