blob: db5e23796bb16ee90dd9a629276f37a66dc2a414 [file] [log] [blame]
package org.eclipse.jdt.internal.compiler.lookup;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.problem.*;
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 SourceTypeBinding[] topLevelTypes;
private CompoundNameVector qualifiedReferences;
private SimpleNameVector simpleNameReferences;
private ObjectVector referencedTypes;
private ObjectVector namespaceDependencies;
private ObjectVector typeDependencies;
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 ? NoCharChar : unit.currentPackage.tokens;
if (environment.options.produceReferenceInfo) {
this.qualifiedReferences = new CompoundNameVector();
this.simpleNameReferences = new SimpleNameVector();
this.referencedTypes = new ObjectVector();
this.namespaceDependencies = new ObjectVector();
this.typeDependencies = new ObjectVector();
} else {
this.qualifiedReferences = null; // used to test if dependencies should be recorded
this.simpleNameReferences = null;
this.referencedTypes = null;
this.namespaceDependencies = null; // used to test if dependencies should be recorded
this.typeDependencies = null;
}
}
public void addNamespaceReference(PackageBinding packageBinding) {
if (namespaceDependencies == null) return; // we're not recording dependencies
if (packageBinding.isValidBinding()) {
if (!namespaceDependencies.containsIdentical(packageBinding))
namespaceDependencies.add(packageBinding);
} else {
for (int i = namespaceDependencies.size; --i >= 0;) {
PackageBinding next = (PackageBinding) namespaceDependencies.elementAt(i);
if (!next.isValidBinding() && CharOperation.equals(packageBinding.compoundName, next.compoundName))
return;
}
namespaceDependencies.add(packageBinding);
}
}
public void addTypeReference(TypeBinding type) {
if (typeDependencies == null) return; // we're not recording dependencies
if (type.isArrayType())
type = ((ArrayBinding) type).leafComponentType;
if (!type.isBaseType()) {
ReferenceBinding actualType = (ReferenceBinding) type;
if (!typeDependencies.containsIdentical(actualType))
typeDependencies.add(actualType);
}
}
public void addTypeReferences(TypeBinding[] types) {
if (typeDependencies == null) return; // we're not recording dependencies
if (types == null || types == NoExceptions) return;
for (int i = 0, max = types.length; i < max; i++) addTypeReference(types[i]);
}
void buildFieldsAndMethods() {
for (int i = 0, length = topLevelTypes.length; i < length; i++)
topLevelTypes[i].scope.buildFieldsAndMethods();
}
void buildTypeBindings() {
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)) {
problemReporter().packageIsNotExpectedPackage(referenceContext);
currentPackageName = expectedPackageName.length == 0 ? NoCharChar : expectedPackageName;
}
}
if (currentPackageName == NoCharChar) {
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);
recordReference(currentPackageName, 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;
}
boolean packageExists = currentPackageName == NoCharChar
? environment.getTopLevelPackage(typeDecl.name) != null
: (fPackage.getPackage0(typeDecl.name)) != null || environment.isPackage(currentPackageName, typeDecl.name);
if (packageExists) {
// 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);
continue nextType;
}
}
ClassScope child = new ClassScope(this, typeDecl);
topLevelTypes[count++] = child.buildType(null, fPackage);
}
// 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() {
// initialize the default imports if necessary... share the default java.lang.* import
if (environment.defaultImports == null) {
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());
environment.defaultImports = new ImportBinding[] {new ImportBinding(JAVA_LANG, true, importBinding)};
}
if (referenceContext.imports == null) {
imports = environment.defaultImports;
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)) {
numberOfImports--;
break;
}
}
ImportBinding[] resolvedImports = new ImportBinding[numberOfImports];
resolvedImports[0] = environment.defaultImports[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++)
if (resolvedImports[j].onDemand == importReference.onDemand)
if (CharOperation.equals(compoundName, resolvedImports[j].compoundName))
continue nextImport;
if (importReference.onDemand == true)
if (CharOperation.equals(compoundName, currentPackageName))
continue nextImport;
if (importReference.onDemand) {
Binding importBinding = findOnDemandImport(compoundName);
if (!importBinding.isValidBinding())
continue nextImport; // we report all problems in faultInImports()
resolvedImports[index++] = new ImportBinding(compoundName, true, importBinding);
} else {
resolvedImports[index++] = new ImportBinding(compoundName, false, null);
}
}
// 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;
}
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)) {
numberOfImports--;
break;
}
}
ImportBinding[] resolvedImports = new ImportBinding[numberOfImports];
resolvedImports[0] = environment.defaultImports[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++)
if (resolvedImports[j].onDemand == importReference.onDemand)
if (CharOperation.equals(compoundName, resolvedImports[j].compoundName))
continue nextImport;
if (importReference.onDemand == true)
if (CharOperation.equals(compoundName, currentPackageName))
continue nextImport;
if (importReference.onDemand) {
Binding importBinding = findOnDemandImport(compoundName);
if (!importBinding.isValidBinding()) {
problemReporter().importProblem(importReference, importBinding);
continue nextImport;
}
resolvedImports[index++] = new ImportBinding(compoundName, true, importBinding);
} else {
Binding typeBinding = findSingleTypeImport(compoundName);
if (!typeBinding.isValidBinding()) {
problemReporter().importProblem(importReference, typeBinding);
continue nextImport;
}
if (typeBinding instanceof PackageBinding) {
problemReporter().cannotImportPackage(importReference);
continue nextImport;
}
ReferenceBinding existingType = typesBySimpleNames.get(compoundName[compoundName.length - 1]);
if (existingType != null) {
// duplicate test above should have caught this case, but make sure
if (existingType == typeBinding)
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;
}
resolvedImports[index++] = new ImportBinding(compoundName, false, typeBinding);
typesBySimpleNames.put(compoundName[compoundName.length - 1], (ReferenceBinding) typeBinding);
}
}
// 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;
}
public void faultInTypes() {
faultInImports();
for (int i = 0, length = topLevelTypes.length; i < length; i++)
topLevelTypes[i].faultInTypesForFieldsAndMethods();
}
private Binding findOnDemandImport(char[][] compoundName) {
// replaces the 3 calls to addNamespaceReference & 2 to addTypeReference
recordQualifiedReference(compoundName);
Binding binding = environment.getPackage0(compoundName[0]);
if (binding == null) {
if (environment.isPackage(null, compoundName[0]))
binding = environment.getTopLevelPackage(compoundName[0]);
else // hold onto a problem package since a real one will never be created
addNamespaceReference(new ProblemPackageBinding(compoundName[0], NotFound));
} else {
if (binding == environment.theNotFoundPackage)
binding = null; // forget the NotFound package
}
int i = 1;
int length = compoundName.length;
foundNothingOrType: if (binding != null) {
PackageBinding packageBinding = (PackageBinding) binding;
addNamespaceReference(packageBinding);
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;
addNamespaceReference(packageBinding);
}
return packageBinding;
}
ReferenceBinding type;
if (binding == null) {
if (environment.defaultPackage == null
|| environment.options.complianceLevel >= CompilerOptions.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;
}
for (; i < length; i++) {
addTypeReference(type);
// does not look for inherited member types on purpose
if ((type = type.getMemberType(compoundName[i])) == null)
return new ProblemReferenceBinding(
CharOperation.subarray(compoundName, 0, i + 1),
NotFound);
}
addTypeReference(type);
if (!type.canBeSeenBy(fPackage))
return new ProblemReferenceBinding(compoundName, type, NotVisible);
return type;
}
private Binding findSingleTypeImport(char[][] compoundName) {
if (compoundName.length == 1) {
// findType records the reference
// the name cannot be a package
if (environment.defaultPackage == null
|| environment.options.complianceLevel >= CompilerOptions.JDK1_4)
return new ProblemReferenceBinding(compoundName, NotFound);
ReferenceBinding typeBinding = findType(compoundName[0], environment.defaultPackage, fPackage);
if (typeBinding == null)
return new ProblemReferenceBinding(compoundName, NotFound);
else
return typeBinding;
}
return findOnDemandImport(compoundName);
}
/* 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 recordSimpleReference(char[] simpleName) {
if (simpleNameReferences == null) return; // not recording dependencies
if (!simpleNameReferences.contains(simpleName))
simpleNameReferences.add(simpleName);
}
void recordTypeReference(TypeBinding type) {
if (referencedTypes == null) return; // not recording dependencies
if (type.isArrayType())
type = ((ArrayBinding) type).leafComponentType;
if (!type.isBaseType() && !referencedTypes.containsIdentical(type))
referencedTypes.add(type);
}
void recordTypeReferences(TypeBinding[] types) {
if (qualifiedReferences == 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
TypeBinding type = types[i];
if (type.isArrayType())
type = ((ArrayBinding) type).leafComponentType;
if (!type.isBaseType()) {
ReferenceBinding actualType = (ReferenceBinding) type;
if (!actualType.isLocalType())
recordQualifiedReference(actualType.isMemberType()
? CharOperation.splitOn('.', actualType.readableName())
: actualType.compoundName);
}
}
}
Binding resolveSingleTypeImport(ImportBinding importBinding) {
if (importBinding.resolvedImport == null) {
importBinding.resolvedImport = findSingleTypeImport(importBinding.compoundName);
if (!importBinding.resolvedImport.isValidBinding() || importBinding.resolvedImport instanceof PackageBinding) {
ImportBinding[] newImports = new ImportBinding[imports.length - 1];
for (int i = 0, n = 0, max = imports.length; i < max; i++)
if (imports[i] != importBinding)
newImports[n++] = imports[i];
imports = newImports;
return null;
}
}
return importBinding.resolvedImport;
}
public void storeDependencyInfo() {
// add the type hierarchy of each referenced type
// cannot do early since the hierarchy may not be fully resolved
for (int i = 0; i < referencedTypes.size; i++) { // grows as more types are added
ReferenceBinding type = (ReferenceBinding) referencedTypes.elementAt(i);
if (!type.isLocalType()) {
recordQualifiedReference(type.isMemberType()
? CharOperation.splitOn('.', type.readableName())
: type.compoundName);
ReferenceBinding enclosing = type.enclosingType();
if (enclosing != null && !referencedTypes.containsIdentical(enclosing))
referencedTypes.add(enclosing); // to record its supertypes
}
ReferenceBinding superclass = type.superclass();
if (superclass != null && !referencedTypes.containsIdentical(superclass))
referencedTypes.add(superclass); // to record its supertypes
ReferenceBinding[] interfaces = type.superInterfaces();
if (interfaces != null && interfaces.length > 0)
for (int j = 0, length = interfaces.length; j < length; j++)
if (!referencedTypes.containsIdentical(interfaces[j]))
referencedTypes.add(interfaces[j]); // to record its supertypes
}
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;
// Old code to be removed
for (int i = 0; i < typeDependencies.size; i++) { // grows as more types are added
// add all the supertypes & associated packages
ReferenceBinding type = (ReferenceBinding) typeDependencies.elementAt(i);
addNamespaceReference(type.fPackage); // is this necessary? If so what about a & a.b from a.b.c?
if (type.enclosingType() != null)
addTypeReference(type.enclosingType());
if (type.superclass() != null)
addTypeReference(type.superclass());
ReferenceBinding[] interfaces = type.superInterfaces();
for (int j = 0, length = interfaces.length; j < length; j++)
addTypeReference(interfaces[j]);
}
int length = namespaceDependencies.size;
char[][] namespaceNames = new char[length][];
for (int i = 0; i < length; i++)
namespaceNames[i] = ((PackageBinding) namespaceDependencies.elementAt(i)).readableName();
referenceContext.compilationResult.namespaceDependencies = namespaceNames;
length = typeDependencies.size;
int toplevelTypeCount = 0;
for (int i = 0; i < length; i++)
if (!((ReferenceBinding) typeDependencies.elementAt(i)).isNestedType())
toplevelTypeCount++;
char[][] fileNames = new char[toplevelTypeCount][];
for (int i = 0; i < length; i++)
if (!((ReferenceBinding) typeDependencies.elementAt(i)).isNestedType())
fileNames[--toplevelTypeCount] = ((ReferenceBinding) typeDependencies.elementAt(i)).getFileName();
// eliminate duplicates
int unique = 0;
char[] ownFileName = referenceContext.getFileName();
next : for (int i = 0, l = fileNames.length; i < l; i++) {
char[] fileName = fileNames[i];
if (CharOperation.equals(fileName, ownFileName)) {
fileNames[i] = null;
continue next;
}
for (int j = i + 1; j < l; j++) {
if (CharOperation.equals(fileName, fileNames[j])) {
fileNames[i] = null;
continue next;
}
}
unique++;
}
if (unique < fileNames.length) {
char[][] uniqueFileNames = new char[unique][];
for (int i = fileNames.length; --i >= 0;)
if (fileNames[i] != null)
uniqueFileNames[--unique] = fileNames[i];
fileNames = uniqueFileNames;
}
referenceContext.compilationResult.fileDependencies = fileNames;
}
public String toString() {
return "--- CompilationUnit Scope : " + new String(referenceContext.getFileName()); //$NON-NLS-1$
}
public void verifyMethods(MethodVerifier verifier) {
for (int i = 0, length = topLevelTypes.length; i < length; i++)
topLevelTypes[i].verifyMethods(verifier);
}
}