X-Git-Url: http://git.phpeclipse.com diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/lookup/CompilationUnitScope.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/lookup/CompilationUnitScope.java new file mode 100644 index 0000000..d50f6f7 --- /dev/null +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/lookup/CompilationUnitScope.java @@ -0,0 +1,576 @@ +/******************************************************************************* + * Copyright (c) 2000, 2003 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package net.sourceforge.phpdt.internal.compiler.lookup; + +import java.util.ArrayList; + +import net.sourceforge.phpdt.core.compiler.CharOperation; +import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter; +import net.sourceforge.phpdt.internal.compiler.util.CompoundNameVector; +import net.sourceforge.phpdt.internal.compiler.util.HashtableOfType; +import net.sourceforge.phpdt.internal.compiler.util.ObjectVector; +import net.sourceforge.phpdt.internal.compiler.util.SimpleNameVector; +import net.sourceforge.phpeclipse.internal.compiler.ast.CompilationUnitDeclaration; +import net.sourceforge.phpeclipse.internal.compiler.ast.ImportReference; +import net.sourceforge.phpeclipse.internal.compiler.ast.TypeDeclaration; + +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; + + 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(); + // } else { + this.qualifiedReferences = null; // used to test if dependencies should be recorded + this.simpleNameReferences = null; + this.referencedTypes = null; + // } + } + void buildFieldsAndMethods() { + for (int i = 0, length = topLevelTypes.length; i < length; i++) + topLevelTypes[i].scope.buildFieldsAndMethods(); + } + void buildTypeBindings() { + 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 + ArrayList types = referenceContext.types; + int typeLength = (types == null) ? 0 : types.size(); + topLevelTypes = new SourceTypeBinding[typeLength]; + int count = 0; + nextType : for (int i = 0; i < typeLength; i++) { + if (types.get(i) instanceof TypeDeclaration) { + TypeDeclaration typeDecl = (TypeDeclaration) types.get(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); + continue nextType; + } + } + + ClassScope child = new ClassScope(this, typeDecl); + SourceTypeBinding type = child.buildType(null, fPackage); + 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() { + // 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, null)}; + } + 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, importReference); + } else { + 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)) { + 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)) { + problemReporter().unusedImport(importReference); // since skipped, must be reported now + continue nextImport; + } + if (importReference.onDemand == true) + if (CharOperation.equals(compoundName, currentPackageName)) { + problemReporter().unusedImport(importReference); // since skipped, must be reported now + 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, importReference); + } else { + Binding typeBinding = findSingleTypeImport(compoundName); + if (!typeBinding.isValidBinding()) { + problemReporter().importProblem(importReference, typeBinding); + continue nextImport; + } + if (typeBinding instanceof PackageBinding) { + problemReporter().cannotImportPackage(importReference); + continue nextImport; + } + if (typeBinding instanceof ReferenceBinding) { + ReferenceBinding referenceBinding = (ReferenceBinding) typeBinding; + if (importReference.isTypeUseDeprecated(referenceBinding, this)) { + problemReporter().deprecatedType((TypeBinding) typeBinding, 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 == 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, importReference); + 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(); + if (topLevelTypes==null) { + topLevelTypes = new SourceTypeBinding[0]; + } + for (int i = 0, length = topLevelTypes.length; i < length; i++) + topLevelTypes[i].faultInTypesForFieldsAndMethods(); + } + private Binding findOnDemandImport(char[][] compoundName) { + recordQualifiedReference(compoundName); + + Binding binding = environment.getTopLevelPackage(compoundName[0]); + int i = 1; + int length = compoundName.length; + 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 >= 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++) { + if (!type.canBeSeenBy(fPackage)) { + return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, i), type, NotVisible); + } + // 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); + } + } + 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) { + 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 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; + } + 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); + } +}