blob: 59357edf33e1eac6dcb8b4d9072b8306b012d71a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2010 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
* $Id: CompilationUnitScope.java 23405 2010-02-03 17:02:18Z stephan $
*
* Contributors:
* IBM Corporation - initial API and implementation
* Erling Ellingsen - patch for bug 125570
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;
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.impl.IrritantSet;
import org.eclipse.jdt.internal.compiler.parser.Parser;
import org.eclipse.jdt.internal.compiler.problem.IProblemRechecker;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.jdt.internal.compiler.util.*;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.Config;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.Dependencies;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.OTClassScope;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.TeamModel;
/**
* OTDT changes:
*
* Role files and team packages:
* =============================
* What: Don't create PackageBinding for team package
* Why: Although syntactically alike, team package declartations are handled differently:
* Create the binding in SourceTypeBinding.<init> for a team type.
*
* What: Handle, what to Java looks like a clash between package and type.
* Why: If the type is a team, equal names are intended.
* While doing the check also compare names of the current team and the team package.
*
* What: Decide whether to create ClassScopes or OTClassScopes for contained types.
* Why: Teams and role files need OTClassScope for specific lookup.
*
* Other:
* ======
* What: Avoid doubly processing faultInImports()
* Why: faultInTypes is now controlled by Dependencies
*
*/
public class CompilationUnitScope extends Scope {
public LookupEnvironment environment;
public CompilationUnitDeclaration referenceContext;
public char[][] currentPackageName;
public PackageBinding fPackage;
public ImportBinding[] imports;
public HashtableOfObject typeOrPackageCache; // used in Scope.getTypeOrPackage()
public SourceTypeBinding[] topLevelTypes;
private CompoundNameVector qualifiedReferences;
private SimpleNameVector simpleNameReferences;
private SimpleNameVector rootReferences;
private ObjectVector referencedTypes;
private ObjectVector referencedSuperTypes;
HashtableOfType constantPoolNameUsage;
private int captureID = 1;
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 (compilerOptions().produceReferenceInfo) {
this.qualifiedReferences = new CompoundNameVector();
this.simpleNameReferences = new SimpleNameVector();
this.rootReferences = 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.rootReferences = null;
this.referencedTypes = null;
this.referencedSuperTypes = null;
}
}
//{ObjectTeams: accessible from Dependencies:
public
// SH}
void buildFieldsAndMethods() {
for (int i = 0, length = this.topLevelTypes.length; i < length; i++)
this.topLevelTypes[i].scope.buildFieldsAndMethods();
}
void buildTypeBindings(AccessRestriction accessRestriction) {
this.topLevelTypes = new SourceTypeBinding[0]; // want it initialized if the package cannot be resolved
boolean firstIsSynthetic = false;
if (this.referenceContext.compilationResult.compilationUnit != null) {
char[][] expectedPackageName = this.referenceContext.compilationResult.compilationUnit.getPackageName();
if (expectedPackageName != null
&& !CharOperation.equals(this.currentPackageName, expectedPackageName)) {
// only report if the unit isn't structurally empty
if (this.referenceContext.currentPackage != null
|| this.referenceContext.types != null
|| this.referenceContext.imports != null) {
problemReporter().packageIsNotExpectedPackage(this.referenceContext);
}
this.currentPackageName = expectedPackageName.length == 0 ? CharOperation.NO_CHAR_CHAR : expectedPackageName;
}
}
//{ObjectTeams: severe problems have to set ignoreFurtherInvestigation:
boolean hasPackageError = false;
try {
// SH}
if (this.currentPackageName == CharOperation.NO_CHAR_CHAR) {
if ((this.fPackage = this.environment.defaultPackage) == null) {
problemReporter().mustSpecifyPackage(this.referenceContext);
//{ObjectTeams: mark error:
hasPackageError = true;
// SH}
return;
}
} else {
//{ObjectTeams: ROFI consider team packages, different creation time, different conflict rule
if ( this.referenceContext.currentPackage != null
&& this.referenceContext.currentPackage.isTeam())
{
// ROFI: for a role file get the real package from the enclosing team.
if ( this.referenceContext.types != null
&& this.referenceContext.types.length > 0)
{
this.fPackage = this.referenceContext.types[0].getPackageOfTeam(this.referenceContext.currentPackage, this);
if (this.fPackage == null) {
hasPackageError = true;
return; // cannot recover
}
}
}
else {
// ROFI: only create non-team packages here
// team packages are created in SourceTypeBinding.<init>
// orig:
if ((this.fPackage = this.environment.createPackage(this.currentPackageName)) == null) {
// :giro
// ROFI: check whether seeming conflict is due to a missing "team" modifier of the package:
final Parser parser = Config.getParser();
if (parser != null && parser.currentTeam != null) {
if ( this.referenceContext.currentPackage != null
&& CharOperation.equals(
this.referenceContext.currentPackage.tokens,
parser.currentTeam.binding.compoundName))
{
problemReporter().nonTeamPackageForRole(
this.referenceContext.currentPackage.tokens,
this.referenceContext.getFileName(),
this.referenceContext.currentPackage.sourceStart,
this.referenceContext.currentPackage.sourceEnd);
hasPackageError = true;
return;
}
}
// only report if the unit isn't structurally empty
if ( this.referenceContext.currentPackage == null
&& this.referenceContext.types == null
&& this.referenceContext.imports == null)
return;
// orig:
if (this.referenceContext.currentPackage != null)
problemReporter().packageCollidesWithType(this.referenceContext); // only report when the unit has a package statement
/* OT: */ hasPackageError = true;
return;
} else if (this.referenceContext.isPackageInfo()) {
// resolve package annotations now if this is "package-info.java".
if (this.referenceContext.types == null || this.referenceContext.types.length == 0) {
this.referenceContext.types = new TypeDeclaration[1];
this.referenceContext.createPackageInfoType();
firstIsSynthetic = true;
}
// ensure the package annotations are copied over before resolution
if (this.referenceContext.currentPackage != null)
this.referenceContext.types[0].annotations = this.referenceContext.currentPackage.annotations;
}
recordQualifiedReference(this.currentPackageName); // always dependent on your own package
// :giro
}
// SH}
}
// {ObjectTeams: maybe push down severe problem to types:
} finally {
if (hasPackageError)
for (int i = 0; i < this.referenceContext.types.length; i++)
this.referenceContext.types[i].tagAsHavingErrors();
}
// SH}
// Skip typeDeclarations which know of previously reported errors
TypeDeclaration[] types = this.referenceContext.types;
int typeLength = (types == null) ? 0 : types.length;
this.topLevelTypes = new SourceTypeBinding[typeLength];
int count = 0;
nextType: for (int i = 0; i < typeLength; i++) {
TypeDeclaration typeDecl = types[i];
if (this.environment.isProcessingAnnotations && this.environment.isMissingType(typeDecl.name))
throw new SourceTypeCollisionException(); // resolved a type ref before APT generated the type
ReferenceBinding typeBinding = this.fPackage.getType0(typeDecl.name);
recordSimpleReference(typeDecl.name); // needed to detect collision cases
//{ObjectTeams: with role files the assumption below does not hold any more:
/* orig:
if (typeBinding != null && typeBinding.isValidBinding() && !(typeBinding instanceof UnresolvedReferenceBinding)) {
:giro */
if ( typeBinding != null
&& typeBinding.isValidBinding()
&& typeBinding != LookupEnvironment.TheNotFoundType
&& !(typeBinding instanceof UnresolvedReferenceBinding))
{
// SH}
// if its an unresolved binding - its fixed up whenever its needed, see UnresolvedReferenceBinding.resolve()
if (this.environment.isProcessingAnnotations)
throw new SourceTypeCollisionException(); // resolved a type ref before APT generated the type
// if a type exists, check that its a valid type
// it can be a NotFound problem type if its a secondary type referenced before its primary type found in additional units
// and it can be an unresolved type which is now being defined
problemReporter().duplicateTypes(this.referenceContext, typeDecl);
continue nextType;
}
if (this.fPackage != this.environment.defaultPackage && this.fPackage.getPackage(typeDecl.name) != null) {
//{ObjectTeams: handle team-names and team-packages names of roles: is not a clash.
if(!TeamModel.isAnyTeam(typeDecl))
//Markus Witte}
// if a package exists, it must be a valid package - cannot be a NotFound problem package
// this is now a warning since a package does not really 'exist' until it contains a type, see JLS v2, 7.4.3
problemReporter().typeCollidesWithPackage(this.referenceContext, typeDecl);
}
//{ObjectTeams: role files: (a) classes never match the name of the unit: prepended __OT__!
// (b) always check for matching file name (also protected)
boolean isRole = typeDecl.isRole() ;
if ( (typeDecl.modifiers & ClassFileConstants.AccPublic) != 0
|| typeDecl.isRole()) // (b) check independently of publicness
{
char[] mainTypeName;
/* orig:
if ((typeDecl.modifiers & ClassFileConstants.AccPublic) != 0) {
char[] mainTypeName;
if ((mainTypeName = this.referenceContext.getMainTypeName()) != null // mainTypeName == null means that implementor of ICompilationUnit decided to return null
&& !CharOperation.equals(mainTypeName, typeDecl.name)) {
:giro */
if ((mainTypeName = this.referenceContext.getMainTypeName()) != null // mainTypeName == null means that implementor of ICompilationUnit decided to return null
&& !CharOperation.equals(mainTypeName, typeDecl.name)
&& !(isRole && !typeDecl.isInterface())) // (a): ignore role classes
{
if (typeDecl.isRole()) // (b) : more specific message
problemReporter().roleFileMismatchingName(this.referenceContext, typeDecl);
else
// SH}
problemReporter().publicClassMustMatchFileName(this.referenceContext, typeDecl);
// tolerate faulty main type name (91091), allow to proceed into type construction
}
}
//{ObjectTeams: ROFI: new type OTClassScope:
ClassScope child;
if (typeDecl.isRole() || typeDecl.isTeam())
child = OTClassScope.createTopLevelOTClassScope(this, typeDecl);
else
//orig:
child = new ClassScope(this, typeDecl);
// SH}
SourceTypeBinding type = child.buildType(null, this.fPackage, accessRestriction);
if (firstIsSynthetic && i == 0)
type.modifiers |= ClassFileConstants.AccSynthetic;
if (type != null)
this.topLevelTypes[count++] = type;
}
// shrink topLevelTypes... only happens if an error was reported
if (count != this.topLevelTypes.length)
System.arraycopy(this.topLevelTypes, 0, this.topLevelTypes = new SourceTypeBinding[count], 0, count);
}
//{ObjectTeams: open for Dependencies:
public
// SH}
void checkAndSetImports() {
if (this.referenceContext.imports == null) {
this.imports = getDefaultImports();
//{ObjectTeams: may need an empty base import scope:
setBaseImports(new ImportBinding[0], 0, new ImportReference[0]);
// SH}
return;
}
// allocate the import array, add java.lang.* by default
int numberOfStatements = this.referenceContext.imports.length;
int numberOfImports = numberOfStatements + 1;
for (int i = 0; i < numberOfStatements; i++) {
ImportReference importReference = this.referenceContext.imports[i];
if (((importReference.bits & ASTNode.OnDemand) != 0) && CharOperation.equals(TypeConstants.JAVA_LANG, importReference.tokens) && !importReference.isStatic()) {
numberOfImports--;
break;
}
}
ImportBinding[] resolvedImports = new ImportBinding[numberOfImports];
resolvedImports[0] = getDefaultImports()[0];
int index = 1;
//{ObjectTeams: collect base imports seperately:
ImportBinding[] resolvedBaseImports = new ImportBinding[numberOfImports];
ImportReference[] baseImports = new ImportReference[numberOfImports];
int baseCount = 0;
// SH}
nextImport : for (int i = 0; i < numberOfStatements; i++) {
ImportReference importReference = this.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.bits & ASTNode.OnDemand) != 0) && resolved.isStatic() == importReference.isStatic())
if (CharOperation.equals(compoundName, resolvedImports[j].compoundName))
continue nextImport;
}
if ((importReference.bits & ASTNode.OnDemand) != 0) {
if (CharOperation.equals(compoundName, this.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 {
//{ObjectTeams: base imports are collected seperately
if (importReference.isBase()) {
baseImports[baseCount] = importReference;
Binding importBinding = findImport(compoundName, compoundName.length);
if (importBinding.problemId() == ProblemReasons.NotVisible) {
final ReferenceBinding importedType = (ReferenceBinding) ((ProblemReferenceBinding)importBinding).closestMatch();
problemReporter().setRechecker(new IProblemRechecker() {
public boolean shouldBeReported(IrritantSet[] foundIrritants) {
return !CompilationUnitScope.this.isDecapsulationSuppressedAtRole(importedType, foundIrritants);
}
}).decapsulation(importReference);
importBinding = importedType;
}
resolvedBaseImports[baseCount++] = new ImportBinding(compoundName, false, /*isBase:*/true, importBinding, importReference);
} else
// SH}
// 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);
this.imports = resolvedImports;
//{ObjectTeams: base imports:
setBaseImports(resolvedBaseImports, baseCount, baseImports);
// SH}
}
//{ObjectTeams: base imports
// helper: let a role suppress warnings for its base import:
protected boolean isDecapsulationSuppressedAtRole(ReferenceBinding importedType, IrritantSet[] foundIrritants) {
if (this.referenceContext.types != null)
for (TypeDeclaration type : this.referenceContext.types)
if (isDecapsulationSuppressedAtRole(type.binding, importedType, foundIrritants))
return true;
return false;
}
protected boolean isDecapsulationSuppressedAtRole(ReferenceBinding currentType, ReferenceBinding importedType, IrritantSet[] foundIrritants) {
if (currentType == null) return false;
for (ReferenceBinding member : currentType.memberTypes()) {
if (member.baseclass() == importedType) {
TypeDeclaration roleAst = member.roleModel.getAst();
if (roleAst != null)
return this.referenceContext.isWarningSuppressedAt(IProblem.Decapsulation, roleAst.sourceStart, roleAst.sourceEnd, foundIrritants);
}
if (isDecapsulationSuppressedAtRole(member, currentType, foundIrritants))
return true;
}
return false;
}
private void setBaseImports(ImportBinding[] resolvedBaseImports, int baseCount, ImportReference[] refs)
{
if (this.referenceContext == null || this.referenceContext.types == null || this.referenceContext.types.length == 0)
return; // nothing we can do without a type
OTClassScope otScope = null;
TypeDeclaration firstType = this.referenceContext.types[0];
if (this.referenceContext.types != null && this.referenceContext.types.length > 0)
{
if (firstType.scope != null && (firstType.scope instanceof OTClassScope))
otScope = (OTClassScope)firstType.scope;
}
if (otScope != null) {
// shrink resolvedBaseImports...
if (resolvedBaseImports.length > baseCount)
System.arraycopy(resolvedBaseImports, 0, resolvedBaseImports = new ImportBinding[baseCount], 0, baseCount);
otScope.checkAndSetBaseImports(this.environment, refs, resolvedBaseImports);
} else {
for (ImportReference reference : refs) {
if (reference != null)
problemReporter().baseImportInRegularClass(firstType, reference);
}
}
}
// SH}
/**
* Perform deferred check specific to parameterized types: bound checks, supertype collisions
*/
//{ObjectTeams: accessible for Dependencies:
public
// SH}
void checkParameterizedTypes() {
if (compilerOptions().sourceLevel < ClassFileConstants.JDK1_5) return;
for (int i = 0, length = this.topLevelTypes.length; i < length; i++) {
ClassScope scope = this.topLevelTypes[i].scope;
scope.checkParameterizedTypeBounds();
scope.checkParameterizedSuperTypeCollisions();
}
}
/*
* 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 (this.constantPoolNameUsage == null)
this.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;
boolean isCompliant15 = compilerOptions().complianceLevel >= ClassFileConstants.JDK1_5;
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()){
if (isCompliant15) {
// from 1.5 on, use immediately enclosing type name
candidateName = CharOperation.concat(
localType.enclosingType.constantPoolName(),
String.valueOf(index+1).toCharArray(),
'$');
} else {
candidateName = CharOperation.concat(
outerMostEnclosingType.constantPoolName(),
String.valueOf(index+1).toCharArray(),
'$');
}
} else {
// local type
if (isCompliant15) {
candidateName = CharOperation.concat(
CharOperation.concat(
localType.enclosingType().constantPoolName(),
String.valueOf(index+1).toCharArray(),
'$'),
localType.sourceName);
} else {
candidateName = CharOperation.concat(
outerMostEnclosingType.constantPoolName(),
'$',
String.valueOf(index+1).toCharArray(),
'$',
localType.sourceName);
}
}
if (this.constantPoolNameUsage.get(candidateName) != null) {
index ++;
} else {
this.constantPoolNameUsage.put(candidateName, localType);
break;
}
}
return candidateName;
}
//{ObjectTeams: ensure inherited local types may keep their names:
public void registerLocalType(char[] tsuperConstantPoolName, TypeDeclaration localType) {
if (this.constantPoolNameUsage == null)
this.constantPoolNameUsage = new HashtableOfType();
ReferenceBinding outerMostEnclosingType = localType.scope.outerMostClassScope().enclosingSourceType();
// replace outer-class prefix (strengthening): TODO(SH): multi-level?
int dollarPos = CharOperation.indexOf('$', tsuperConstantPoolName);
if (dollarPos > -1)
tsuperConstantPoolName = CharOperation.concat(
outerMostEnclosingType.constantPoolName,
CharOperation.subarray(tsuperConstantPoolName, dollarPos+1, -1),
'$');
ReferenceBinding foundType = this.constantPoolNameUsage.get(tsuperConstantPoolName);
if (foundType != null)
assert foundType == localType.binding;
else
this.constantPoolNameUsage.put(tsuperConstantPoolName, localType.binding);
}
//if its a binary local type reserve its binary name:
public void registerBinaryNested(ReferenceBinding nestedBinding) {
if (nestedBinding.isLocalType()) {
if (this.constantPoolNameUsage == null)
this.constantPoolNameUsage = new HashtableOfType();
assert this.constantPoolNameUsage.get(nestedBinding.constantPoolName) == null;
this.constantPoolNameUsage.put(nestedBinding.constantPoolName, nestedBinding);
}
}
// SH}
//{ObjectTeams: open for Dependencies:
public
// SH}
void connectTypeHierarchy() {
for (int i = 0, length = this.topLevelTypes.length; i < length; i++)
this.topLevelTypes[i].scope.connectTypeHierarchy();
}
//{ObjectTeams: support faultInTypes being controlled by Dependencies
// record when imports have been built:
private boolean faultInImportsDone = false;
// make accessible from Dependencies:
public
// SH}
void faultInImports() {
if (this.typeOrPackageCache != null)
return; // can be called when a field constant is resolved before static imports
if (this.referenceContext.imports == null) {
this.typeOrPackageCache = new HashtableOfObject(1);
return;
}
//{ObjectTeams: don't process twice
if (this.faultInImportsDone)
return;
this.faultInImportsDone = true;
// SH}
// collect the top level type names if a single type import exists
int numberOfStatements = this.referenceContext.imports.length;
HashtableOfType typesBySimpleNames = null;
for (int i = 0; i < numberOfStatements; i++) {
if ((this.referenceContext.imports[i].bits & ASTNode.OnDemand) == 0) {
typesBySimpleNames = new HashtableOfType(this.topLevelTypes.length + numberOfStatements);
for (int j = 0, length = this.topLevelTypes.length; j < length; j++)
typesBySimpleNames.put(this.topLevelTypes[j].sourceName, this.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 = this.referenceContext.imports[i];
if (((importReference.bits & ASTNode.OnDemand) != 0) && CharOperation.equals(TypeConstants.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 = this.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.bits & ASTNode.OnDemand) != 0) && resolved.isStatic() == importReference.isStatic()) {
if (CharOperation.equals(compoundName, resolved.compoundName)) {
problemReporter().unusedImport(importReference); // since skipped, must be reported now
continue nextImport;
}
}
}
if ((importReference.bits & ASTNode.OnDemand) != 0) {
if (CharOperation.equals(compoundName, this.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, Binding.TYPE | Binding.FIELD | Binding.METHOD, importReference.isStatic());
if (!importBinding.isValidBinding()) {
//{ObjectTeams: base imports may decapsulate:
if ( importBinding.problemId() == ProblemReasons.NotVisible
&& importReference.isBase())
{
// decapsulation is already checked in checkAndSetImports, proceed with the closest match
importBinding = ((ProblemReferenceBinding)importBinding).closestMatch();
} else {
// orig:
if (importBinding.problemId() == ProblemReasons.Ambiguous) {
// keep it unless a duplicate can be found below
} else {
problemReporter().importProblem(importReference, importBinding);
continue nextImport;
}
// :giro
}
// SH}
}
//{ObjectTeams: cannot import roles:
if ( importBinding instanceof ReferenceBinding
&& ((ReferenceBinding)importBinding).isRole()) {
problemReporter().cannotImportRole(importReference, importBinding);
continue nextImport;
}
// SH}
if (importBinding instanceof PackageBinding) {
problemReporter().cannotImportPackage(importReference);
continue nextImport;
}
ReferenceBinding conflictingType = null;
if (importBinding instanceof MethodBinding) {
conflictingType = (ReferenceBinding) getType(compoundName, compoundName.length);
if (!conflictingType.isValidBinding() || (importReference.isStatic() && !conflictingType.isStatic()))
conflictingType = null;
}
// collisions between an imported static field & a type should be checked according to spec... but currently not by javac
if (importBinding instanceof ReferenceBinding || conflictingType != null) {
ReferenceBinding referenceBinding = conflictingType == null ? (ReferenceBinding) importBinding : conflictingType;
ReferenceBinding typeToCheck = referenceBinding.problemId() == ProblemReasons.Ambiguous
? ((ProblemReferenceBinding) referenceBinding).closestMatch
: referenceBinding;
if (importReference.isTypeUseDeprecated(typeToCheck, this))
problemReporter().deprecatedType(typeToCheck, 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) {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=302865
// Check all resolved imports to see if this import qualifies as a duplicate
for (int j = 0; j < index; j++) {
ImportBinding resolved = resolvedImports[j];
if (resolved instanceof ImportConflictBinding) {
ImportConflictBinding importConflictBinding = (ImportConflictBinding) resolved;
if (importConflictBinding.conflictingTypeBinding == referenceBinding) {
if (!importReference.isStatic()) {
// resolved is implicitly static
problemReporter().duplicateImport(importReference);
resolvedImports[index++] = new ImportBinding(compoundName, false, importBinding, importReference);
}
}
} else if (resolved.resolvedImport == referenceBinding) {
if (importReference.isStatic() != resolved.isStatic()) {
problemReporter().duplicateImport(importReference);
resolvedImports[index++] = new ImportBinding(compoundName, false, importBinding, importReference);
}
}
}
continue nextImport;
}
// either the type collides with a top level type or another imported type
for (int j = 0, length = this.topLevelTypes.length; j < length; j++) {
if (CharOperation.equals(this.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;
}
}
}
}
//{ObjectTeams: base imports are collected seperately
if (!importReference.isBase())
// SH}
resolvedImports[index++] = conflictingType == null
? new ImportBinding(compoundName, false, importBinding, importReference)
: new ImportConflictBinding(compoundName, importBinding, conflictingType, importReference);
}
}
// shrink resolvedImports... only happens if an error was reported
if (resolvedImports.length > index)
System.arraycopy(resolvedImports, 0, resolvedImports = new ImportBinding[index], 0, index);
this.imports = resolvedImports;
int length = this.imports.length;
this.typeOrPackageCache = new HashtableOfObject(length);
for (int i = 0; i < length; i++) {
ImportBinding binding = this.imports[i];
if (!binding.onDemand && binding.resolvedImport instanceof ReferenceBinding || binding instanceof ImportConflictBinding)
this.typeOrPackageCache.put(binding.compoundName[binding.compoundName.length - 1], binding);
}
}
public void faultInTypes() {
faultInImports();
for (int i = 0, length = this.topLevelTypes.length; i < length; i++)
this.topLevelTypes[i].faultInTypesForFieldsAndMethods();
}
// this API is for code assist purpose
public Binding findImport(char[][] compoundName, boolean findStaticImports, boolean onDemand) {
if(onDemand) {
return findImport(compoundName, compoundName.length);
} else {
return findSingleImport(compoundName, Binding.TYPE | Binding.FIELD | Binding.METHOD, findStaticImports);
}
}
private Binding findImport(char[][] compoundName, int length) {
recordQualifiedReference(compoundName);
Binding binding = this.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 (this.environment.defaultPackage == null || compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4)
return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, i), null, ProblemReasons.NotFound);
type = findType(compoundName[0], this.environment.defaultPackage, this.environment.defaultPackage);
if (type == null || !type.isValidBinding())
return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, i), null, ProblemReasons.NotFound);
i = 1; // reset to look for member types inside the default package type
} else {
type = (ReferenceBinding) binding;
}
while (i < length) {
type = (ReferenceBinding)this.environment.convertToRawType(type, false /*do not force conversion of enclosing types*/); // type imports are necessarily raw for all except last
if (!type.canBeSeenBy(this.fPackage))
return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, i), type, ProblemReasons.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), null, ProblemReasons.NotFound);
}
if (!type.canBeSeenBy(this.fPackage))
return new ProblemReferenceBinding(compoundName, type, ProblemReasons.NotVisible);
return type;
}
private Binding findSingleImport(char[][] compoundName, int mask, boolean findStaticImports) {
if (compoundName.length == 1) {
// findType records the reference
// the name cannot be a package
if (this.environment.defaultPackage == null || compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4)
return new ProblemReferenceBinding(compoundName, null, ProblemReasons.NotFound);
ReferenceBinding typeBinding = findType(compoundName[0], this.environment.defaultPackage, this.fPackage);
if (typeBinding == null)
return new ProblemReferenceBinding(compoundName, null, ProblemReasons.NotFound);
return typeBinding;
}
if (findStaticImports)
return findSingleStaticImport(compoundName, mask);
return findImport(compoundName, compoundName.length);
}
private Binding findSingleStaticImport(char[][] compoundName, int mask) {
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, (ReferenceBinding) temp, ProblemReasons.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 = (mask & Binding.FIELD) != 0 ? findField(type, name, null, true) : null;
if (field != null) {
if (field.problemId() == ProblemReasons.Ambiguous && ((ProblemFieldBinding) field).closestMatch.isStatic())
return field; // keep the ambiguous field instead of a possible method match
if (field.isValidBinding() && field.isStatic() && field.canBeSeenBy(type, null, this))
return field;
}
// look to see if there is a static method with the same selector
MethodBinding method = (mask & Binding.METHOD) != 0 ? findStaticMethod(type, name) : null;
if (method != null) return method;
type = findMemberType(name, type);
if (type == null || !type.isStatic()) {
if (field != null && !field.isValidBinding() && field.problemId() != ProblemReasons.NotFound)
return field;
return new ProblemReferenceBinding(compoundName, type, ProblemReasons.NotFound);
}
if (type.isValidBinding() && !type.canBeSeenBy(this.fPackage))
return new ProblemReferenceBinding(compoundName, type, ProblemReasons.NotVisible);
if (type.problemId() == ProblemReasons.NotVisible) // ensure compoundName is correct
return new ProblemReferenceBinding(compoundName, ((ProblemReferenceBinding) type).closestMatch, ProblemReasons.NotVisible);
return type;
}
// helper method for findSingleStaticImport()
private MethodBinding findStaticMethod(ReferenceBinding currentType, char[] selector) {
if (!currentType.canBeSeenBy(this))
return null;
do {
currentType.initializeForStaticImports();
MethodBinding[] methods = currentType.getMethods(selector);
if (methods != Binding.NO_METHODS) {
for (int i = methods.length; --i >= 0;) {
MethodBinding method = methods[i];
if (method.isStatic() && method.canBeSeenBy(this.fPackage))
return method;
}
}
} while ((currentType = currentType.superclass()) != null);
return null;
}
ImportBinding[] getDefaultImports() {
// initialize the default imports if necessary... share the default java.lang.* import
if (this.environment.defaultImports != null) return this.environment.defaultImports;
Binding importBinding = this.environment.getTopLevelPackage(TypeConstants.JAVA);
if (importBinding != null)
importBinding = ((PackageBinding) importBinding).getTypeOrPackage(TypeConstants.JAVA_LANG[1]);
if (importBinding == null || !importBinding.isValidBinding()) {
// create a proxy for the missing BinaryType
problemReporter().isClassPathCorrect(
TypeConstants.JAVA_LANG_OBJECT,
this.referenceContext,
this.environment.missingClassFileLocation);
BinaryTypeBinding missingObject = this.environment.createMissingType(null, TypeConstants.JAVA_LANG_OBJECT);
importBinding = missingObject.fPackage;
}
return this.environment.defaultImports = new ImportBinding[] {new ImportBinding(TypeConstants.JAVA_LANG, true, importBinding, null)};
}
// NOT Public API
public final Binding getImport(char[][] compoundName, boolean onDemand, boolean isStaticImport) {
//{ObjectTeams: this method may be called in a Config-unaware context:
boolean createdConfig = false;
if (!Config.hasLookupEnvironment()) {
createdConfig = true;
Dependencies.setup(this, null, environment(), false, true);
}
try {
//SH}
if (onDemand)
return findImport(compoundName, compoundName.length);
return findSingleImport(compoundName, Binding.TYPE | Binding.FIELD | Binding.METHOD, isStaticImport);
//{ObjectTeams: cleanup:
} finally {
if (createdConfig)
Dependencies.release(this);
}
//SH}
}
public int nextCaptureID() {
return this.captureID++;
}
/* 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 = this.referenceContext.problemReporter;
problemReporter.referenceContext = this.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 (this.qualifiedReferences == null) return; // not recording dependencies
int length = qualifiedName.length;
if (length > 1) {
recordRootReference(qualifiedName[0]);
while (!this.qualifiedReferences.contains(qualifiedName)) {
this.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) {
recordRootReference(qualifiedName[0]);
recordSimpleReference(qualifiedName[0]);
}
}
void recordReference(char[][] qualifiedEnclosingName, char[] simpleName) {
recordQualifiedReference(qualifiedEnclosingName);
if (qualifiedEnclosingName.length == 0)
recordRootReference(simpleName);
recordSimpleReference(simpleName);
}
void recordReference(ReferenceBinding type, char[] simpleName) {
ReferenceBinding actualType = typeToRecord(type);
if (actualType != null)
recordReference(actualType.compoundName, simpleName);
}
void recordRootReference(char[] simpleName) {
if (this.rootReferences == null) return; // not recording dependencies
if (!this.rootReferences.contains(simpleName))
this.rootReferences.add(simpleName);
}
void recordSimpleReference(char[] simpleName) {
if (this.simpleNameReferences == null) return; // not recording dependencies
if (!this.simpleNameReferences.contains(simpleName))
this.simpleNameReferences.add(simpleName);
}
//{ObjectTeams: public for use by CopyInheritance:
public
// SH}
void recordSuperTypeReference(TypeBinding type) {
if (this.referencedSuperTypes == null) return; // not recording dependencies
ReferenceBinding actualType = typeToRecord(type);
if (actualType != null && !this.referencedSuperTypes.containsIdentical(actualType))
this.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
}
//{ObjectTeams: accessible for classes in org.eclipse.objectteams..:
public
// SH}
void recordTypeReference(TypeBinding type) {
if (this.referencedTypes == null) return; // not recording dependencies
ReferenceBinding actualType = typeToRecord(type);
if (actualType != null && !this.referencedTypes.containsIdentical(actualType))
this.referencedTypes.add(actualType);
}
void recordTypeReferences(TypeBinding[] types) {
if (this.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 && !this.referencedTypes.containsIdentical(actualType))
this.referencedTypes.add(actualType);
}
}
Binding resolveSingleImport(ImportBinding importBinding, int mask) {
if (importBinding.resolvedImport == null) {
importBinding.resolvedImport = findSingleImport(importBinding.compoundName, mask, importBinding.isStatic());
//{ObjectTeams: base imports may decapsulate:
if ( importBinding.resolvedImport.problemId() == ProblemReasons.NotVisible
&& importBinding.isBase)
{
Binding result = ((ProblemReferenceBinding)importBinding.resolvedImport).closestMatch();
importBinding.resolvedImport = null; // treat and report in faultInImports()
return result; // work with the closest match for now.
}
//SH}
if (!importBinding.resolvedImport.isValidBinding() || importBinding.resolvedImport instanceof PackageBinding) {
if (importBinding.resolvedImport.problemId() == ProblemReasons.Ambiguous)
return importBinding.resolvedImport;
if (this.imports != null) {
ImportBinding[] newImports = new ImportBinding[this.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 < this.referencedSuperTypes.size; i++) { // grows as more types are added
ReferenceBinding type = (ReferenceBinding) this.referencedSuperTypes.elementAt(i);
if (!this.referencedTypes.containsIdentical(type))
this.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 = this.referencedTypes.size; i < l; i++) {
ReferenceBinding type = (ReferenceBinding) this.referencedTypes.elementAt(i);
if (!type.isLocalType())
recordQualifiedReference(type.isMemberType()
? CharOperation.splitOn('.', type.readableName())
: type.compoundName);
}
int size = this.qualifiedReferences.size;
char[][][] qualifiedRefs = new char[size][][];
for (int i = 0; i < size; i++)
qualifiedRefs[i] = this.qualifiedReferences.elementAt(i);
this.referenceContext.compilationResult.qualifiedReferences = qualifiedRefs;
size = this.simpleNameReferences.size;
char[][] simpleRefs = new char[size][];
for (int i = 0; i < size; i++)
simpleRefs[i] = this.simpleNameReferences.elementAt(i);
this.referenceContext.compilationResult.simpleNameReferences = simpleRefs;
size = this.rootReferences.size;
char[][] rootRefs = new char[size][];
for (int i = 0; i < size; i++)
rootRefs[i] = this.rootReferences.elementAt(i);
this.referenceContext.compilationResult.rootReferences = rootRefs;
}
public String toString() {
return "--- CompilationUnit Scope : " + new String(this.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 :
case Binding.INTERSECTION_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 = this.topLevelTypes.length; i < length; i++)
this.topLevelTypes[i].verifyMethods(verifier);
}
}