package org.eclipse.jdt.internal.compiler.lookup; | |
/* | |
* (c) Copyright IBM Corp. 2000, 2001. | |
* All Rights Reserved. | |
*/ | |
import org.eclipse.jdt.internal.compiler.impl.*; | |
import org.eclipse.jdt.internal.compiler.ast.*; | |
import org.eclipse.jdt.internal.compiler.codegen.*; | |
import org.eclipse.jdt.internal.compiler.problem.*; | |
import org.eclipse.jdt.internal.compiler.util.*; | |
public class BlockScope extends Scope { | |
// Local variable management | |
public LocalVariableBinding[] locals; | |
public int localIndex; // position for next variable | |
public int analysisIndex; // for setting flow-analysis id | |
public int startIndex; // start position in this scope - for ordering scopes vs. variables | |
public final static VariableBinding[] EmulationPathToImplicitThis = {}; | |
public Scope[] subscopes = new Scope[1]; // need access from code assist | |
public int scopeIndex = 0; // need access from code assist | |
protected BlockScope(int kind, Scope parent) { | |
super(kind, parent); | |
} | |
public BlockScope(BlockScope parent) { | |
this(BLOCK_SCOPE, parent); | |
locals = new LocalVariableBinding[5]; | |
parent.addSubscope(this); | |
this.startIndex = parent.localIndex; | |
} | |
public BlockScope(BlockScope parent, int variableCount) { | |
this(BLOCK_SCOPE, parent); | |
locals = new LocalVariableBinding[variableCount]; | |
parent.addSubscope(this); | |
this.startIndex = parent.localIndex; | |
} | |
/* Create the class scope & binding for the anonymous type. | |
*/ | |
public final void addAnonymousType(TypeDeclaration anonymousType, ReferenceBinding superBinding) { | |
ClassScope anonymousClassScope = new ClassScope(this, anonymousType); | |
anonymousClassScope.buildAnonymousTypeBinding(enclosingSourceType(), superBinding); | |
/* provide some "default" name? | |
char[][] enclosingName = enclosing.compoundName; | |
System.arraycopy( | |
enclosingName, 0, | |
binding.compoundName = new char[enclosingName.length+1][], 0, | |
enclosingName.length); | |
binding.compoundName[enclosingName.length] = ANONYMOUS_EMPTY_NAME; | |
*/ | |
} | |
/* Create the class scope & binding for the local type. | |
*/ | |
public final void addLocalType(TypeDeclaration localType) { | |
// check that the localType does not conflict with an enclosing type | |
ReferenceBinding type = enclosingSourceType(); | |
do { | |
if (CharOperation.equals(type.sourceName, localType.name)) { | |
problemReporter().hidingEnclosingType(localType); | |
return; | |
} | |
type = type.enclosingType(); | |
} while (type != null); | |
// check that the localType does not conflict with another sibling local type | |
Scope scope = this; | |
do { | |
if (((BlockScope) scope).findLocalType(localType.name) != null) { | |
problemReporter().duplicateNestedType(localType); | |
return; | |
} | |
} while ((scope = scope.parent) instanceof BlockScope); | |
ClassScope localTypeScope = new ClassScope(this, localType); | |
localTypeScope.buildLocalTypeBinding(enclosingSourceType()); | |
addSubscope(localTypeScope); | |
} | |
/* Insert a local variable into a given scope, updating its position | |
* and checking there are not too many locals or arguments allocated. | |
*/ | |
public final void addLocalVariable(LocalVariableBinding binding) { | |
checkAndSetModifiersForVariable(binding); | |
// insert local in scope | |
if (localIndex == locals.length) | |
System.arraycopy(locals, 0, (locals = new LocalVariableBinding[localIndex * 2]), 0, localIndex); | |
locals[localIndex++] = binding; | |
// update local variable binding | |
binding.declaringScope = this; | |
binding.id = this.outerMostMethodScope().analysisIndex++; // share the outermost method scope analysisIndex | |
/* | |
// update localIndex | |
TypeBinding resolvedType; | |
if (((resolvedType = binding.type) == LongBinding) || (resolvedType == DoubleBinding)) { | |
localIndex += 2; | |
} else { | |
localIndex++; | |
}; | |
*/ | |
/* | |
if (binding.isArgument) { | |
// check argument count < 255 words | |
if (localIndex > 255) { | |
problemReporter().noMoreAvailableSpaceForArgument(binding); | |
} | |
} else { | |
// ensure local index < 65535 words | |
if (localIndex > 65535) { | |
problemReporter().noMoreAvailableSpaceForLocalVariable(binding); | |
} | |
} | |
*/ | |
} | |
private void addSubscope(Scope childScope) { | |
if (scopeIndex == subscopes.length) | |
System.arraycopy(subscopes, 0, (subscopes = new Scope[scopeIndex * 2]), 0, scopeIndex); | |
subscopes[scopeIndex++] = childScope; | |
} | |
/* Answer true if the receiver is suitable for assigning final blank fields. | |
* | |
* i.e. is inside an initializer, a constructor or a clinit | |
*/ | |
public final boolean allowBlankFinalFieldAssignment(FieldBinding binding) { | |
if (enclosingSourceType() != binding.declaringClass) | |
return false; | |
MethodScope methodScope = methodScope(); | |
if (methodScope.isStatic != binding.isStatic()) | |
return false; | |
return methodScope.isInsideInitializer() // inside initializer | |
|| ((AbstractMethodDeclaration) methodScope.referenceContext).isInitializationMethod(); // inside constructor or clinit | |
} | |
String basicToString(int tab) { | |
String newLine = "\n"; //$NON-NLS-1$ | |
for (int i = tab; --i >= 0;) | |
newLine += "\t"; //$NON-NLS-1$ | |
String s = newLine + "--- Block Scope ---"; //$NON-NLS-1$ | |
newLine += "\t"; //$NON-NLS-1$ | |
s += newLine + "locals:"; //$NON-NLS-1$ | |
for (int i = 0; i < localIndex; i++) | |
s += newLine + "\t" + locals[i].toString(); //$NON-NLS-1$ | |
s += newLine + "startIndex = " + startIndex; //$NON-NLS-1$ | |
return s; | |
} | |
private void checkAndSetModifiersForVariable(LocalVariableBinding varBinding) { | |
int modifiers = varBinding.modifiers; | |
if ((modifiers & AccAlternateModifierProblem) != 0) | |
problemReporter().duplicateModifierForVariable(varBinding.declaration); | |
int realModifiers = modifiers & AccJustFlag; | |
int unexpectedModifiers = ~AccFinal; | |
if ((realModifiers & unexpectedModifiers) != 0) | |
problemReporter().illegalModifierForVariable(varBinding.declaration); | |
varBinding.modifiers = modifiers; | |
} | |
/* Compute variable positions in scopes given an initial position offset | |
* ignoring unused local variables. | |
*/ | |
public final void computeLocalVariablePositions(int offset, CodeStream codeStream) { | |
// local variable init | |
int ilocal = 0, maxLocals = 0, localsLength = locals.length; | |
while ((maxLocals < localsLength) && (locals[maxLocals] != null)) | |
maxLocals++; | |
boolean hasMoreVariables = maxLocals > 0; | |
// scope init | |
int iscope = 0, maxScopes = 0, subscopesLength = subscopes.length; | |
while ((maxScopes < subscopesLength) && (subscopes[maxScopes] != null)) | |
maxScopes++; | |
boolean hasMoreScopes = maxScopes > 0; | |
// iterate scopes and variables in parallel | |
while (hasMoreVariables || hasMoreScopes) { | |
if (hasMoreScopes && (!hasMoreVariables || (subscopes[iscope].startIndex() <= ilocal))) { | |
// consider subscope first | |
if (subscopes[iscope] instanceof BlockScope) | |
((BlockScope) subscopes[iscope]).computeLocalVariablePositions(offset, codeStream); | |
hasMoreScopes = ++iscope < maxScopes; | |
} else { | |
// consider variable first | |
LocalVariableBinding local = locals[ilocal]; | |
// check if variable is actually used, and may force it to be preserved | |
boolean generatesLocal = (local.used && (local.constant == Constant.NotAConstant)) || local.isArgument; | |
if (!local.used && (local.declaration != null)) { // unused (and non secret) local | |
if (local.isArgument) // method argument | |
this.problemReporter().unusedArgument(local.declaration); | |
else if (!(local.declaration instanceof Argument)) // catch variable | |
this.problemReporter().unusedLocalVariable(local.declaration); | |
} | |
if (!generatesLocal) { | |
if (local.declaration != null && referenceCompilationUnit().problemReporter.options.preserveAllLocalVariables) { | |
generatesLocal = true; // force it to be preserved in the generated code | |
local.used = true; | |
} | |
} | |
if (generatesLocal) { | |
if (local.declaration != null) | |
codeStream.record(local); // record user local variables for attribute generation | |
local.resolvedPosition = offset; | |
// check for too many arguments/local variables | |
if(local.isArgument){ | |
if (offset > 0xFF){ // no more than 255 words of arguments | |
this.problemReporter().noMoreAvailableSpaceForArgument(local.declaration); | |
} | |
} else { | |
if(offset > 0xFFFF){ // no more than 65535 words of locals | |
this.problemReporter().noMoreAvailableSpaceForLocal(local.declaration); | |
} | |
} | |
if ((local.type == LongBinding) || (local.type == DoubleBinding)) | |
offset += 2; | |
else | |
offset++; | |
} else { | |
local.resolvedPosition = -1; // not generated | |
} | |
hasMoreVariables = ++ilocal < maxLocals; | |
} | |
} | |
} | |
/* Answer true if the variable name already exists within the receiver's scope. | |
*/ | |
public final LocalVariableBinding duplicateName(char[] name) { | |
for (int i = 0; i < localIndex; i++) | |
if (CharOperation.equals(name, locals[i].name)) | |
return locals[i]; | |
if (this instanceof MethodScope) | |
return null; | |
else | |
return ((BlockScope) parent).duplicateName(name); | |
} | |
/** | |
* Record the suitable binding denoting a synthetic field or constructor argument, | |
* mapping to the actual outer local variable in the scope context. | |
* Note that this may not need any effect, in case the outer local variable does not | |
* need to be emulated and can directly be used as is (using its back pointer to its | |
* declaring scope). | |
*/ | |
public void emulateOuterAccess(LocalVariableBinding outerLocalVariable) { | |
MethodScope currentMethodScope; | |
if ((currentMethodScope = this.methodScope()) != outerLocalVariable.declaringScope.methodScope()) { | |
NestedTypeBinding currentType = (NestedTypeBinding) this.enclosingSourceType(); | |
//do nothing for member types, pre emulation was performed already | |
if(!currentType.isLocalType()){ | |
return; | |
} | |
// must also add a synthetic field if we're not inside a constructor | |
if (!currentMethodScope.isInsideInitializerOrConstructor()) { | |
currentType.addSyntheticArgumentAndField(outerLocalVariable); | |
} else { | |
currentType.addSyntheticArgument(outerLocalVariable); | |
} | |
} | |
} | |
/** | |
* Record the suitable binding denoting a synthetic field or constructor argument, | |
* mapping to a given actual enclosing instance type in the scope context. | |
* Skip it if the enclosingType is actually the current scope's enclosing type. | |
*/ | |
public void emulateOuterAccess(ReferenceBinding targetEnclosingType, boolean useDirectReference) { | |
ReferenceBinding currentType = enclosingSourceType(); | |
if (currentType.isNestedType() && currentType != targetEnclosingType) { | |
NestedTypeBinding currentNestedType = (NestedTypeBinding) currentType; | |
if (useDirectReference) { | |
// the target enclosing type is not in scope, we directly refer it | |
// must also add a synthetic field if we're not inside a constructor | |
if (methodScope().isInsideInitializerOrConstructor()) | |
currentNestedType.addSyntheticArgument(targetEnclosingType); | |
else | |
currentNestedType.addSyntheticArgumentAndField(targetEnclosingType); | |
} else if (currentNestedType.isLocalType()) { | |
// direct enclosing instance link | |
// must also add a synthetic field if we're not inside a constructor | |
currentType = currentNestedType.enclosingType; | |
if (methodScope().isInsideInitializerOrConstructor()) | |
currentNestedType.addSyntheticArgument(currentType); | |
else | |
currentNestedType.addSyntheticArgumentAndField(currentType); | |
// further indirect cases | |
while (currentType != targetEnclosingType && !targetEnclosingType.isSuperclassOf(currentType)) { | |
currentNestedType = (NestedTypeBinding) currentType; | |
currentType = currentNestedType.enclosingType; | |
currentNestedType.addSyntheticArgumentAndField(currentType); | |
} | |
} | |
} | |
} | |
/* Note that it must never produce a direct access to the targetEnclosingType, | |
but instead a field sequence (this$2.this$1.this$0) so as to handle such a test case: | |
class XX { | |
void foo() { | |
class A { | |
class B { | |
class C { | |
boolean foo() { | |
return (Object) A.this == (Object) B.this; | |
} | |
} | |
} | |
} | |
new A().new B().new C(); | |
} | |
} | |
where we only want to deal with ONE enclosing instance for C (could not figure out an A for C) | |
*/ | |
public final ReferenceBinding findLocalType(char[] name) { | |
for (int i = 0, length = scopeIndex; i < length; i++) { | |
if (subscopes[i] instanceof ClassScope) { | |
SourceTypeBinding sourceType = ((ClassScope) subscopes[i]).referenceContext.binding; | |
if (CharOperation.equals(sourceType.sourceName(), name)) | |
return sourceType; | |
} | |
} | |
return null; | |
} | |
public LocalVariableBinding findVariable(char[] variable) { | |
int variableLength = variable.length; | |
for (int i = 0, length = locals.length; i < length; i++) { | |
LocalVariableBinding local = locals[i]; | |
if (local == null) | |
return null; | |
if (local.name.length == variableLength && CharOperation.prefixEquals(local.name, variable)) | |
return local; | |
} | |
return null; | |
} | |
/* API | |
flag is a mask of the following values VARIABLE (= FIELD or LOCAL), TYPE. | |
Only bindings corresponding to the mask will be answered. | |
if the VARIABLE mask is set then | |
If the first name provided is a field (or local) then the field (or local) is answered | |
Otherwise, package names and type names are consumed until a field is found. | |
In this case, the field is answered. | |
if the TYPE mask is set, | |
package names and type names are consumed until the end of the input. | |
Only if all of the input is consumed is the type answered | |
All other conditions are errors, and a problem binding is returned. | |
NOTE: If a problem binding is returned, senders should extract the compound name | |
from the binding & not assume the problem applies to the entire compoundName. | |
The VARIABLE mask has precedence over the TYPE mask. | |
InvocationSite implements | |
isSuperAccess(); this is used to determine if the discovered field is visible. | |
setFieldIndex(int); this is used to record the number of names that were consumed. | |
For example, getBinding({"foo","y","q", VARIABLE, site) will answer | |
the binding for the field or local named "foo" (or an error binding if none exists). | |
In addition, setFieldIndex(1) will be sent to the invocation site. | |
If a type named "foo" exists, it will not be detected (and an error binding will be answered) | |
IMPORTANT NOTE: This method is written under the assumption that compoundName is longer than length 1. | |
*/ | |
public Binding getBinding(char[][] compoundName, int mask, InvocationSite invocationSite) { | |
Binding binding = getBinding(compoundName[0], mask | TYPE | PACKAGE, invocationSite); | |
invocationSite.setFieldIndex(1); | |
if (!binding.isValidBinding() || binding instanceof VariableBinding) | |
return binding; | |
int length = compoundName.length; | |
int currentIndex = 1; | |
foundType: if (binding instanceof PackageBinding) { | |
PackageBinding packageBinding = (PackageBinding) binding; | |
compilationUnitScope().addNamespaceReference(packageBinding); | |
while (currentIndex < length) { | |
binding = packageBinding.getTypeOrPackage(compoundName[currentIndex++]); | |
invocationSite.setFieldIndex(currentIndex); | |
if (binding == null) { | |
if (currentIndex == length) // must be a type if its the last name, otherwise we have no idea if its a package or type | |
return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, currentIndex), NotFound); | |
else | |
return new ProblemBinding(CharOperation.subarray(compoundName, 0, currentIndex), NotFound); | |
} | |
if (binding instanceof ReferenceBinding) { | |
if (!binding.isValidBinding()) | |
return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, currentIndex), binding.problemId()); | |
compilationUnitScope().addTypeReference((ReferenceBinding) binding); | |
if (!((ReferenceBinding) binding).canBeSeenBy(this)) | |
return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, currentIndex), NotVisible); | |
break foundType; | |
} | |
packageBinding = (PackageBinding) binding; | |
compilationUnitScope().addNamespaceReference(packageBinding); | |
} | |
// It is illegal to request a PACKAGE from this method. | |
return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, currentIndex), NotFound); | |
} | |
// know binding is now a ReferenceBinding | |
while (currentIndex < length) { | |
ReferenceBinding typeBinding = (ReferenceBinding) binding; | |
char[] nextName = compoundName[currentIndex++]; | |
invocationSite.setFieldIndex(currentIndex); | |
if ((binding = findField(typeBinding, nextName, invocationSite)) != null) { | |
if (!binding.isValidBinding()) | |
return new ProblemFieldBinding(CharOperation.subarray(compoundName, 0, currentIndex), binding.problemId()); | |
break; // binding is now a field | |
} | |
if ((binding = findMemberType(nextName, typeBinding)) == null) | |
return new ProblemBinding(CharOperation.subarray(compoundName, 0, currentIndex), typeBinding, NotFound); | |
if (!binding.isValidBinding()) | |
return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, currentIndex), binding.problemId()); | |
} | |
if ((mask & FIELD) != 0 && (binding instanceof FieldBinding)) { // was looking for a field and found a field | |
FieldBinding field = (FieldBinding) binding; | |
if (!field.isStatic()) | |
return new ProblemFieldBinding(CharOperation.subarray(compoundName, 0, currentIndex), NonStaticReferenceInStaticContext); | |
return binding; | |
} | |
if ((mask & TYPE) != 0 && (binding instanceof ReferenceBinding)) { // was looking for a type and found a type | |
return binding; | |
} | |
// handle the case when a field or type was asked for but we resolved the compoundName to a type or field | |
return new ProblemBinding(CharOperation.subarray(compoundName, 0, currentIndex), NotFound); | |
} | |
// Added for code assist... NOT Public API | |
public final Binding getBinding(char[][] compoundName, InvocationSite invocationSite) { | |
int currentIndex = 0; | |
int length = compoundName.length; | |
Binding binding = getBinding(compoundName[currentIndex++], VARIABLE | TYPE | PACKAGE, invocationSite); | |
if (!binding.isValidBinding()) | |
return binding; | |
foundType: if (binding instanceof PackageBinding) { | |
while (currentIndex < length) { | |
PackageBinding packageBinding = (PackageBinding) binding; | |
binding = packageBinding.getTypeOrPackage(compoundName[currentIndex++]); | |
if (binding == null) { | |
if (currentIndex == length) // must be a type if its the last name, otherwise we have no idea if its a package or type | |
return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, currentIndex), NotFound); | |
else | |
return new ProblemBinding(CharOperation.subarray(compoundName, 0, currentIndex), NotFound); | |
} | |
if (binding instanceof ReferenceBinding) { | |
if (!binding.isValidBinding()) | |
return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, currentIndex), binding.problemId()); | |
if (!((ReferenceBinding) binding).canBeSeenBy(this)) | |
return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, currentIndex), NotVisible); | |
break foundType; | |
} | |
} | |
return binding; | |
} | |
foundField: if (binding instanceof ReferenceBinding) { | |
while (currentIndex < length) { | |
ReferenceBinding typeBinding = (ReferenceBinding) binding; | |
char[] nextName = compoundName[currentIndex++]; | |
if ((binding = findField(typeBinding, nextName, invocationSite)) != null) { | |
if (!binding.isValidBinding()) | |
return new ProblemFieldBinding(CharOperation.subarray(compoundName, 0, currentIndex), binding.problemId()); | |
if (!((FieldBinding) binding).isStatic()) | |
return new ProblemFieldBinding(CharOperation.subarray(compoundName, 0, currentIndex), NonStaticReferenceInStaticContext); | |
break foundField; // binding is now a field | |
} | |
if ((binding = findMemberType(nextName, typeBinding)) == null) | |
return new ProblemBinding(CharOperation.subarray(compoundName, 0, currentIndex), typeBinding, NotFound); | |
if (!binding.isValidBinding()) | |
return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, currentIndex), binding.problemId()); | |
} | |
return binding; | |
} | |
VariableBinding variableBinding = (VariableBinding) binding; | |
while (currentIndex < length) { | |
TypeBinding typeBinding = variableBinding.type; | |
if (typeBinding == null) | |
return new ProblemFieldBinding(CharOperation.subarray(compoundName, 0, currentIndex+1), NotFound); | |
variableBinding = findField(typeBinding, compoundName[currentIndex++], invocationSite); | |
if (variableBinding == null) | |
return new ProblemFieldBinding(CharOperation.subarray(compoundName, 0, currentIndex), NotFound); | |
if (!variableBinding.isValidBinding()) | |
return variableBinding; | |
} | |
return variableBinding; | |
} | |
/* API | |
Answer the binding that corresponds to the argument name. | |
flag is a mask of the following values VARIABLE (= FIELD or LOCAL), TYPE, PACKAGE. | |
Only bindings corresponding to the mask can be answered. | |
For example, getBinding("foo", VARIABLE, site) will answer | |
the binding for the field or local named "foo" (or an error binding if none exists). | |
If a type named "foo" exists, it will not be detected (and an error binding will be answered) | |
The VARIABLE mask has precedence over the TYPE mask. | |
If the VARIABLE mask is not set, neither fields nor locals will be looked for. | |
InvocationSite implements: | |
isSuperAccess(); this is used to determine if the discovered field is visible. | |
Limitations: cannot request FIELD independently of LOCAL, or vice versa | |
*/ | |
public Binding getBinding(char[] name, int mask, InvocationSite invocationSite) { | |
Binding binding = null; | |
FieldBinding problemField = null; | |
if ((mask & VARIABLE) != 0) { | |
if (this.kind == BLOCK_SCOPE || this.kind == METHOD_SCOPE) { | |
LocalVariableBinding variableBinding = findVariable(name); // looks in this scope only | |
if (variableBinding != null) return variableBinding; | |
} | |
boolean insideStaticContext = false; | |
boolean insideConstructorCall = false; | |
if (this.kind == METHOD_SCOPE) { | |
MethodScope methodScope = (MethodScope) this; | |
insideStaticContext |= methodScope.isStatic; | |
insideConstructorCall |= methodScope.isConstructorCall; | |
} | |
FieldBinding foundField = null; // can be a problem field which is answered if a valid field is not found | |
ProblemFieldBinding foundInsideProblem = null; // inside Constructor call or inside static context | |
Scope scope = parent; | |
int depth = 0; | |
done : while (true) { // done when a COMPILATION_UNIT_SCOPE is found | |
switch (scope.kind) { | |
case METHOD_SCOPE : | |
MethodScope methodScope = (MethodScope) scope; | |
insideStaticContext |= methodScope.isStatic; | |
insideConstructorCall |= methodScope.isConstructorCall; | |
// Fall through... could duplicate the code below to save a cast - questionable optimization | |
case BLOCK_SCOPE : | |
LocalVariableBinding variableBinding = ((BlockScope) scope).findVariable(name); // looks in this scope only | |
if (variableBinding != null) { | |
if (foundField != null && foundField.isValidBinding()) | |
return new ProblemFieldBinding(name, InheritedNameHidesEnclosingName); | |
if (depth > 0) | |
invocationSite.setDepth(depth); | |
return variableBinding; | |
} | |
break; | |
case CLASS_SCOPE : | |
ClassScope classScope = (ClassScope) scope; | |
SourceTypeBinding enclosingType = classScope.referenceContext.binding; | |
FieldBinding fieldBinding = classScope.findField(enclosingType, name, invocationSite); | |
// Use next line instead if willing to enable protected access accross inner types | |
// FieldBinding fieldBinding = findField(enclosingType, name, invocationSite); | |
if (fieldBinding != null) { // skip it if we did not find anything | |
if (fieldBinding.problemId() == Ambiguous) { | |
if (foundField == null || foundField.problemId() == NotVisible) | |
// supercedes any potential InheritedNameHidesEnclosingName problem | |
return fieldBinding; | |
else | |
// make the user qualify the field, likely wants the first inherited field (javac generates an ambiguous error instead) | |
return new ProblemFieldBinding(name, InheritedNameHidesEnclosingName); | |
} | |
ProblemFieldBinding insideProblem = null; | |
if (fieldBinding.isValidBinding()) { | |
if (!fieldBinding.isStatic()) { | |
if (insideConstructorCall) { | |
insideProblem = new ProblemFieldBinding(name, NonStaticReferenceInConstructorInvocation); | |
} else if (insideStaticContext) { | |
insideProblem = new ProblemFieldBinding(name, NonStaticReferenceInStaticContext); | |
} | |
} | |
if (enclosingType == fieldBinding.declaringClass) { // found a valid field in the 'immediate' scope (ie. not inherited) | |
if (foundField == null) { | |
if (depth > 0) | |
invocationSite.setDepth(depth); | |
// return the fieldBinding if it is not declared in a superclass of the scope's binding (i.e. "inherited") | |
return insideProblem == null ? fieldBinding : insideProblem; | |
} | |
if (foundField.isValidBinding()) // if a valid field was found, complain when another is found in an 'immediate' enclosing type (ie. not inherited) | |
if (foundField.declaringClass != fieldBinding.declaringClass) // ie. have we found the same field - do not trust field identity yet | |
return new ProblemFieldBinding(name, InheritedNameHidesEnclosingName); | |
} | |
} | |
if (foundField == null || (foundField.problemId() == NotVisible && fieldBinding.problemId() != NotVisible)) { | |
// only remember the fieldBinding if its the first one found or the previous one was not visible & fieldBinding is... | |
if (depth > 0) | |
invocationSite.setDepth(depth); | |
foundInsideProblem = insideProblem; | |
foundField = fieldBinding; | |
} | |
} | |
depth++; | |
insideStaticContext |= enclosingType.isStatic(); | |
// 1EX5I8Z - accessing outer fields within a constructor call is permitted | |
// in order to do so, we change the flag as we exit from the type, not the method | |
// itself, because the class scope is used to retrieve the fields. | |
MethodScope enclosingMethodScope = scope.methodScope(); | |
insideConstructorCall = enclosingMethodScope == null ? false : enclosingMethodScope.isConstructorCall; | |
break; | |
case COMPILATION_UNIT_SCOPE : | |
break done; | |
} | |
scope = scope.parent; | |
} | |
if (foundInsideProblem != null) | |
return foundInsideProblem; | |
if (foundField != null) { | |
if (foundField.isValidBinding()) | |
return foundField; | |
problemField = foundField; | |
} | |
} | |
// We did not find a local or instance variable. | |
if ((mask & TYPE) != 0) { | |
if ((binding = getBaseType(name)) != null) | |
return binding; | |
binding = getTypeOrPackage(name, (mask & PACKAGE) == 0 ? TYPE : TYPE | PACKAGE); | |
if (binding.isValidBinding() || mask == TYPE) | |
return binding; // answer the problem type binding if we are only looking for a type | |
} else if ((mask & PACKAGE) != 0) { | |
if ((binding = environment().getTopLevelPackage(name)) != null) | |
return binding; | |
} | |
if (problemField != null) | |
return problemField; | |
else | |
return new ProblemBinding(name, enclosingSourceType(), NotFound); | |
} | |
/** | |
* This retrieves the argument that maps to an enclosing instance of the suitable type, | |
* if not found then answers nil -- do not create one | |
* #implicitThis : the implicit this will be ok | |
* #((arg) this$n) : available as a constructor arg | |
* #((arg) this$n access$m... access$p) : available as as a constructor arg + a sequence of synthetic accessors to synthetic fields | |
* #((fieldDescr) this$n access#m... access$p) : available as a first synthetic field + a sequence of synthetic accessors to synthetic fields | |
* nil : not found | |
* | |
*/ | |
public Object[] getCompatibleEmulationPath(ReferenceBinding targetEnclosingType) { | |
MethodScope currentMethodScope = this.methodScope(); | |
SourceTypeBinding sourceType = currentMethodScope.enclosingSourceType(); | |
// identity check | |
if ((!currentMethodScope.isStatic) && (sourceType == targetEnclosingType || targetEnclosingType.isSuperclassOf(sourceType))) { | |
return EmulationPathToImplicitThis; // implicit this is good enough | |
} | |
if (!sourceType.isNestedType() || sourceType.isStatic()){ // no emulation from within non-inner types | |
return null; | |
} | |
boolean insideConstructor = currentMethodScope.isInsideInitializerOrConstructor(); | |
// use synthetic constructor arguments if possible | |
if (insideConstructor) { | |
SyntheticArgumentBinding syntheticArg; | |
if ((syntheticArg = ((NestedTypeBinding) sourceType).getSyntheticArgument(targetEnclosingType, this, false)) != null) { | |
return new Object[] {syntheticArg}; | |
} | |
} | |
// use a direct synthetic field then | |
if (!currentMethodScope.isStatic) { | |
FieldBinding syntheticField; | |
if ((syntheticField = sourceType.getSyntheticField(targetEnclosingType, this, false)) != null) { | |
return new Object[] {syntheticField}; | |
} | |
// could be reached through a sequence of enclosing instance link (nested members) | |
Object[] path = new Object[2]; // probably at least 2 of them | |
ReferenceBinding currentType = sourceType.enclosingType(); | |
if (insideConstructor) { | |
path[0] = ((NestedTypeBinding) sourceType).getSyntheticArgument((SourceTypeBinding)currentType, this, false); | |
} else { | |
path[0] = sourceType.getSyntheticField((SourceTypeBinding)currentType, this, false); | |
} | |
if (path[0] != null) { // keep accumulating | |
int count = 1; | |
ReferenceBinding currentEnclosingType; | |
while ((currentEnclosingType = currentType.enclosingType()) != null) { | |
//done? | |
if (currentType == targetEnclosingType || targetEnclosingType.isSuperclassOf(currentType)) break; | |
syntheticField = ((NestedTypeBinding)currentType).getSyntheticField((SourceTypeBinding)currentEnclosingType, this, false); | |
if (syntheticField == null) break; | |
// append inside the path | |
if (count == path.length) { | |
System.arraycopy(path, 0, (path = new Object[count + 1]), 0, count); | |
} | |
// private access emulation is necessary since synthetic field is private | |
path[count++] = syntheticField.getSyntheticReadAccess(); | |
currentType = currentEnclosingType; | |
} | |
if (currentType == targetEnclosingType || targetEnclosingType.isSuperclassOf(currentType)) { | |
return path; | |
} | |
} | |
} | |
return null; | |
} | |
/* API | |
Answer the constructor binding that corresponds to receiverType, argumentTypes. | |
InvocationSite implements | |
isSuperAccess(); this is used to determine if the discovered constructor is visible. | |
If no visible constructor is discovered, an error binding is answered. | |
*/ | |
public MethodBinding getConstructor(ReferenceBinding receiverType, TypeBinding[] argumentTypes, InvocationSite invocationSite) { | |
compilationUnitScope().addTypeReferences(argumentTypes); | |
MethodBinding methodBinding = receiverType.getExactConstructor(argumentTypes); | |
if (methodBinding != null) | |
if (methodBinding.canBeSeenBy(invocationSite, this)) | |
return methodBinding; | |
MethodBinding[] methods = receiverType.getMethods(ConstructorDeclaration.ConstantPoolName); | |
if (methods == NoMethods) | |
return new ProblemMethodBinding(ConstructorDeclaration.ConstantPoolName, argumentTypes, NotFound); | |
MethodBinding[] compatible = new MethodBinding[methods.length]; | |
int compatibleIndex = 0; | |
for (int i = 0, length = methods.length; i < length; i++) | |
if (areParametersAssignable(methods[i].parameters, argumentTypes)) | |
compatible[compatibleIndex++] = methods[i]; | |
if (compatibleIndex == 0) | |
return new ProblemMethodBinding(ConstructorDeclaration.ConstantPoolName, argumentTypes, NotFound); // need a more descriptive error... cannot convert from X to Y | |
MethodBinding[] visible = new MethodBinding[compatibleIndex]; | |
int visibleIndex = 0; | |
for (int i = 0; i < compatibleIndex; i++) { | |
MethodBinding method = compatible[i]; | |
if (method.canBeSeenBy(invocationSite, this)) | |
visible[visibleIndex++] = method; | |
} | |
if (visibleIndex == 1) | |
return visible[0]; | |
if (visibleIndex == 0) | |
return new ProblemMethodBinding(ConstructorDeclaration.ConstantPoolName, argumentTypes, NotVisible); | |
return mostSpecificClassMethodBinding(visible, visibleIndex); | |
} | |
/** | |
* This retrieves the argument that maps to an enclosing instance of the suitable type, | |
* if not found then answers nil -- do not create one | |
* #implicitThis : the implicit this will be ok | |
* #((arg) this$n) : available as a constructor arg | |
* #((arg) this$n ... this$p) : available as as a constructor arg + a sequence of fields | |
* #((fieldDescr) this$n ... this$p) : available as a sequence of fields | |
* nil : not found | |
* Note that this algorithm should answer the shortest possible sequence when | |
* shortcuts are available: | |
* this$0 . this$0 . this$0 | |
* instead of | |
* this$2 . this$1 . this$0 . this$1 . this$0 | |
* thus the code generation will be more compact and runtime faster | |
*/ | |
public VariableBinding[] getEmulationPath(LocalVariableBinding outerLocalVariable) { | |
MethodScope currentMethodScope = this.methodScope(); | |
SourceTypeBinding sourceType = currentMethodScope.enclosingSourceType(); | |
// identity check | |
if (currentMethodScope == outerLocalVariable.declaringScope.methodScope()){ | |
return new VariableBinding[]{ outerLocalVariable }; // implicit this is good enough | |
} | |
// use synthetic constructor arguments if possible | |
if (currentMethodScope.isInsideInitializerOrConstructor() && (sourceType.isNestedType())){ | |
SyntheticArgumentBinding syntheticArg; | |
if ((syntheticArg =((NestedTypeBinding)sourceType).getSyntheticArgument(outerLocalVariable)) != null){ | |
return new VariableBinding[]{ syntheticArg }; | |
} | |
} | |
// use a synthetic field then | |
if (!currentMethodScope.isStatic){ | |
FieldBinding syntheticField; | |
if ((syntheticField = sourceType.getSyntheticField(outerLocalVariable)) != null){ | |
return new VariableBinding[]{ syntheticField }; | |
} | |
} | |
return null; | |
} | |
/* | |
| mapping mappingSequence targetFieldKey traversedType investigateArguments staticInvocationSite | |
targetDepth nameEnvironment | | |
targetDepth := expectedEnclosingClass enclosingDepth. | |
targetFieldKey := 'this$' , targetDepth printString. "$NON-NLS$" | |
mappingSequence := OrderedCollection new. | |
investigateArguments := enclosingMethod isConstructor. | |
staticInvocationSite := enclosingMethod isJavaStatic. | |
nameEnvironment := enclosingMethod nameEnvironment. | |
"If possible looks through the available constructor arguments (if we are in context only)" | |
investigateArguments | |
ifTrue: [ | |
(mapping := | |
self generatedConstructorArguments "get back an argument local" | |
at: targetFieldKey | |
ifAbsent: []) | |
notNil | |
ifTrue: [ | |
(mapping resolvedType | |
isCompatibleWith: expectedEnclosingClass | |
in: nameEnvironment) | |
ifTrue: [ | |
^mappingSequence | |
add: mapping; | |
yourself]]]. | |
"Cannot look into synthetic field on static invocation site" | |
staticInvocationSite ifTrue: [^nil]. | |
"We now have to compute the <[argument|field]field*> sequence to retrieve the correct enclosing instance, | |
using the traversedType collection from one of them we will be able to reach the correct | |
target type, but difficult to foresee the correct one since the fields this$0... this$n may | |
be sparsely allocated." | |
traversedType := self. | |
[traversedType isNil] whileFalse: [| info | | |
info := | |
(investigateArguments and: [traversedType == self]) | |
ifTrue: [ "use argument" traversedType generatedConstructorArguments] | |
ifFalse: [ "use field" traversedType generatedAccessFields]. | |
mapping := | |
info "try a direct shortcut" | |
at: targetFieldKey | |
ifAbsent: [ | |
info "one level-up" | |
at: 'this$' , (traversedType enclosingDepth - 1) printString "$NON-NLS$" | |
ifAbsent: [ "cannot proceed" ^nil]]. | |
mappingSequence add: mapping. | |
"If the mapping matches the expected type, then we are done." | |
((mapping isJavaFieldDescriptor ifTrue: [mapping type] ifFalse: [mapping resolvedType]) | |
isCompatibleWith: expectedEnclosingClass | |
in: nameEnvironment) | |
ifTrue: [^mappingSequence]. | |
"Go to the next enclosing type" | |
traversedType := traversedType enclosingType]. | |
"Not suitable enclosing instance available -- should never reach this point since detected above" | |
enclosingMethod errorInterface | |
abortDueToInternalError: (NlsCatJDEV indexedMsg: 439). "$NLS$ Failed to emulate access to enclosing instance" | |
^nil | |
*/ | |
/** | |
* This retrieves the argument that maps to an enclosing instance of the suitable type, | |
* if not found then answers nil -- do not create one | |
* #implicitThis : the implicit this will be ok | |
* #((arg) this$n) : available as a constructor arg | |
* #((arg) this$n access$m... access$p) : available as as a constructor arg + a sequence of synthetic accessors to synthetic fields | |
* #((fieldDescr) this$n access#m... access$p) : available as a first synthetic field + a sequence of synthetic accessors to synthetic fields | |
* nil : not found | |
* | |
* EXACT MATCH VERSION - no type compatibility is performed | |
*/ | |
public Object[] getExactEmulationPath(ReferenceBinding targetEnclosingType) { | |
MethodScope currentMethodScope = this.methodScope(); | |
SourceTypeBinding sourceType = currentMethodScope.enclosingSourceType(); | |
// identity check | |
if ((!currentMethodScope.isStatic) && (sourceType == targetEnclosingType)) { | |
return EmulationPathToImplicitThis; // implicit this is good enough | |
} | |
if (!sourceType.isNestedType() || sourceType.isStatic()){ // no emulation from within non-inner types | |
return null; | |
} | |
boolean insideConstructor = currentMethodScope.isInsideInitializerOrConstructor(); | |
// use synthetic constructor arguments if possible | |
if (insideConstructor) { | |
SyntheticArgumentBinding syntheticArg; | |
if ((syntheticArg = ((NestedTypeBinding) sourceType).getSyntheticArgument(targetEnclosingType, this, true)) != null) { | |
return new Object[] {syntheticArg}; | |
} | |
} | |
// use a direct synthetic field then | |
if (!currentMethodScope.isStatic) { | |
FieldBinding syntheticField; | |
if ((syntheticField = sourceType.getSyntheticField(targetEnclosingType, this, true)) != null) { | |
return new Object[] {syntheticField}; | |
} | |
// could be reached through a sequence of enclosing instance link (nested members) | |
Object[] path = new Object[2]; // probably at least 2 of them | |
ReferenceBinding currentType = sourceType.enclosingType(); | |
if (insideConstructor) { | |
path[0] = ((NestedTypeBinding) sourceType).getSyntheticArgument((SourceTypeBinding)currentType, this, true); | |
} else { | |
path[0] = sourceType.getSyntheticField((SourceTypeBinding)currentType, this, true); | |
} | |
if (path[0] != null) { // keep accumulating | |
int count = 1; | |
ReferenceBinding currentEnclosingType; | |
while ((currentEnclosingType = currentType.enclosingType()) != null) { | |
//done? | |
if (currentType == targetEnclosingType) break; | |
syntheticField = ((NestedTypeBinding)currentType).getSyntheticField((SourceTypeBinding)currentEnclosingType, this, true); | |
if (syntheticField == null) break; | |
// append inside the path | |
if (count == path.length) { | |
System.arraycopy(path, 0, (path = new Object[count + 1]), 0, count); | |
} | |
// private access emulation is necessary since synthetic field is private | |
path[count++] = syntheticField.getSyntheticReadAccess(); | |
currentType = currentEnclosingType; | |
} | |
if (currentType == targetEnclosingType) { | |
return path; | |
} | |
} | |
} | |
return null; | |
} | |
/* API | |
Answer the field binding that corresponds to fieldName. | |
Start the lookup at the receiverType. | |
InvocationSite implements | |
isSuperAccess(); this is used to determine if the discovered field is visible. | |
Only fields defined by the receiverType or its supertypes are answered; | |
a field of an enclosing type will not be found using this API. | |
If no visible field is discovered, an error binding is answered. | |
*/ | |
public FieldBinding getField(TypeBinding receiverType, char[] fieldName, InvocationSite invocationSite) { | |
FieldBinding field = findField(receiverType, fieldName, invocationSite); | |
if (field == null) | |
return new ProblemFieldBinding(fieldName, NotFound); | |
else | |
return field; | |
} | |
/* API | |
Answer the method binding that corresponds to selector, argumentTypes. | |
Start the lookup at the enclosing type of the receiver. | |
InvocationSite implements | |
isSuperAccess(); this is used to determine if the discovered method is visible. | |
setDepth(int); this is used to record the depth of the discovered method | |
relative to the enclosing type of the receiver. (If the method is defined | |
in the enclosing type of the receiver, the depth is 0; in the next enclosing | |
type, the depth is 1; and so on | |
If no visible method is discovered, an error binding is answered. | |
*/ | |
public MethodBinding getImplicitMethod(char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite) { | |
boolean insideStaticContext = false; | |
boolean insideConstructorCall = false; | |
MethodBinding foundMethod = null; | |
ProblemMethodBinding foundFuzzyProblem = null; // the weird method lookup case (matches method name in scope, then arg types, then visibility) | |
ProblemMethodBinding foundInsideProblem = null; // inside Constructor call or inside static context | |
Scope scope = this; | |
int depth = 0; | |
done : while (true) { // done when a COMPILATION_UNIT_SCOPE is found | |
switch(scope.kind) { | |
case METHOD_SCOPE : | |
MethodScope methodScope = (MethodScope) scope; | |
insideStaticContext |= methodScope.isStatic; | |
insideConstructorCall |= methodScope.isConstructorCall; | |
break; | |
case CLASS_SCOPE : | |
ClassScope classScope = (ClassScope) scope; | |
SourceTypeBinding receiverType = classScope.referenceContext.binding; | |
boolean isExactMatch = true; | |
// retrieve an exact visible match (if possible) | |
MethodBinding methodBinding = | |
(foundMethod == null) | |
? classScope.findExactMethod(receiverType, selector, argumentTypes, invocationSite) | |
: classScope.findExactMethod(receiverType, foundMethod.selector, foundMethod.parameters, invocationSite); | |
// ? findExactMethod(receiverType, selector, argumentTypes, invocationSite) | |
// : findExactMethod(receiverType, foundMethod.selector, foundMethod.parameters, invocationSite); | |
if (methodBinding == null && foundMethod == null) { | |
// answers closest approximation, may not check argumentTypes or visibility | |
isExactMatch = false; | |
methodBinding = classScope.findMethod(receiverType, selector, argumentTypes, invocationSite); | |
// methodBinding = findMethod(receiverType, selector, argumentTypes, invocationSite); | |
} | |
if (methodBinding != null) { // skip it if we did not find anything | |
if (methodBinding.problemId() == Ambiguous) { | |
if (foundMethod == null || foundMethod.problemId() == NotVisible) | |
// supercedes any potential InheritedNameHidesEnclosingName problem | |
return methodBinding; | |
else | |
// make the user qualify the method, likely wants the first inherited method (javac generates an ambiguous error instead) | |
return new ProblemMethodBinding(selector, argumentTypes, InheritedNameHidesEnclosingName); | |
} | |
ProblemMethodBinding fuzzyProblem = null; | |
ProblemMethodBinding insideProblem = null; | |
if (methodBinding.isValidBinding()) { | |
if (!isExactMatch) { | |
if (!areParametersAssignable(methodBinding.parameters, argumentTypes)) { | |
fuzzyProblem = new ProblemMethodBinding(methodBinding, selector, argumentTypes, NotFound); | |
} else if (!methodBinding.canBeSeenBy(receiverType, invocationSite, classScope)) { | |
// using <classScope> instead of <this> for visibility check does grant all access to innerclass | |
fuzzyProblem = new ProblemMethodBinding(selector, argumentTypes, methodBinding.declaringClass, NotVisible); | |
} | |
} | |
if (fuzzyProblem == null && !methodBinding.isStatic()) { | |
if (insideConstructorCall) { | |
insideProblem = new ProblemMethodBinding(methodBinding.selector, methodBinding.parameters, NonStaticReferenceInConstructorInvocation); | |
} else if (insideStaticContext) { | |
insideProblem = new ProblemMethodBinding(methodBinding.selector, methodBinding.parameters, NonStaticReferenceInStaticContext); | |
} | |
} | |
if (receiverType == methodBinding.declaringClass || (receiverType.getMethods(selector)) != NoMethods) { | |
// found a valid method in the 'immediate' scope (ie. not inherited) | |
// OR the receiverType implemented a method with the correct name | |
if (foundMethod == null) { | |
if (depth > 0) | |
invocationSite.setDepth(depth); | |
// return the methodBinding if it is not declared in a superclass of the scope's binding (i.e. "inherited") | |
if (fuzzyProblem != null) | |
return fuzzyProblem; | |
if (insideProblem != null) | |
return insideProblem; | |
return methodBinding; | |
} | |
// if a method was found, complain when another is found in an 'immediate' enclosing type (ie. not inherited) | |
// NOTE: Unlike fields, a non visible method hides a visible method | |
if (foundMethod.declaringClass != methodBinding.declaringClass) // ie. have we found the same method - do not trust field identity yet | |
return new ProblemMethodBinding(methodBinding.selector, methodBinding.parameters, InheritedNameHidesEnclosingName); | |
} | |
} | |
if (foundMethod == null || (foundMethod.problemId() == NotVisible && methodBinding.problemId() != NotVisible)) { | |
// only remember the methodBinding if its the first one found or the previous one was not visible & methodBinding is... | |
// remember that private methods are visible if defined directly by an enclosing class | |
if (depth > 0) | |
invocationSite.setDepth(depth); | |
foundFuzzyProblem = fuzzyProblem; | |
foundInsideProblem = insideProblem; | |
if (fuzzyProblem == null) | |
foundMethod = methodBinding; // only keep it if no error was found | |
} | |
} | |
depth++; | |
insideStaticContext |= receiverType.isStatic(); | |
// 1EX5I8Z - accessing outer fields within a constructor call is permitted | |
// in order to do so, we change the flag as we exit from the type, not the method | |
// itself, because the class scope is used to retrieve the fields. | |
MethodScope enclosingMethodScope = scope.methodScope(); | |
insideConstructorCall = enclosingMethodScope == null ? false : enclosingMethodScope.isConstructorCall; | |
break; | |
case COMPILATION_UNIT_SCOPE : | |
break done; | |
} | |
scope = scope.parent; | |
} | |
if (foundFuzzyProblem != null) | |
return foundFuzzyProblem; | |
if (foundInsideProblem != null) | |
return foundInsideProblem; | |
if (foundMethod != null) | |
return foundMethod; | |
return new ProblemMethodBinding(selector, argumentTypes, NotFound); | |
} | |
/* API | |
Answer the method binding that corresponds to selector, argumentTypes. | |
Start the lookup at the receiverType. | |
InvocationSite implements | |
isSuperAccess(); this is used to determine if the discovered method is visible. | |
Only methods defined by the receiverType or its supertypes are answered; | |
use getImplicitMethod() to discover methods of enclosing types. | |
If no visible method is discovered, an error binding is answered. | |
*/ | |
public MethodBinding getMethod(TypeBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite) { | |
if (receiverType.isArrayType()) | |
return findMethodForArray((ArrayBinding) receiverType, selector, argumentTypes, invocationSite); | |
if (receiverType.isBaseType()) | |
return new ProblemMethodBinding(selector, argumentTypes, NotFound); | |
ReferenceBinding currentType = (ReferenceBinding) receiverType; | |
if (!currentType.canBeSeenBy(this)) | |
return new ProblemMethodBinding(selector, argumentTypes, NotVisible); // *** Need a new problem id - TypeNotVisible? | |
// retrieve an exact visible match (if possible) | |
MethodBinding methodBinding = findExactMethod(currentType, selector, argumentTypes, invocationSite); | |
if (methodBinding != null) | |
return methodBinding; | |
// answers closest approximation, may not check argumentTypes or visibility | |
methodBinding = findMethod(currentType, selector, argumentTypes, invocationSite); | |
if (methodBinding == null) | |
return new ProblemMethodBinding(selector, argumentTypes, NotFound); | |
if (methodBinding.isValidBinding()) { | |
if (!areParametersAssignable(methodBinding.parameters, argumentTypes)) | |
return new ProblemMethodBinding(methodBinding, selector, argumentTypes, NotFound); | |
if (!methodBinding.canBeSeenBy(currentType, invocationSite, this)) | |
return new ProblemMethodBinding(selector, argumentTypes, methodBinding.declaringClass, NotVisible); | |
} | |
return methodBinding; | |
} | |
/* 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() { | |
return outerMostMethodScope().problemReporter(); | |
} | |
/** | |
* Code responsible to request some more emulation work inside the invocation type, so as to supply | |
* correct synthetic arguments to any allocation of the target type. | |
*/ | |
public void propagateInnerEmulation(ReferenceBinding targetType, boolean isEnclosingInstanceSupplied, boolean useDirectReference) { | |
// perform some emulation work in case there is some and we are inside a local type only | |
// propage emulation of the enclosing instances | |
ReferenceBinding[] syntheticArgumentTypes; | |
if ((syntheticArgumentTypes = targetType.syntheticEnclosingInstanceTypes()) != null) { | |
for (int i = 0, max = syntheticArgumentTypes.length; i < max; i++) { | |
ReferenceBinding syntheticArgType = syntheticArgumentTypes[i]; | |
// need to filter out the one that could match a supplied enclosing instance | |
if (!(isEnclosingInstanceSupplied && (syntheticArgType == targetType.enclosingType()))) { | |
this.emulateOuterAccess(syntheticArgType, useDirectReference); | |
} | |
} | |
} | |
SyntheticArgumentBinding[] syntheticArguments; | |
if ((syntheticArguments = targetType.syntheticOuterLocalVariables()) != null) { | |
for (int i = 0, max = syntheticArguments.length; i < max; i++) { | |
SyntheticArgumentBinding syntheticArg = syntheticArguments[i]; | |
// need to filter out the one that could match a supplied enclosing instance | |
if (!(isEnclosingInstanceSupplied && (syntheticArg.type == targetType.enclosingType()))) { | |
this.emulateOuterAccess(syntheticArg.actualOuterLocalVariable); | |
} | |
} | |
} | |
} | |
/* Answer the reference type of this scope. | |
* | |
* i.e. the nearest enclosing type of this scope. | |
*/ | |
public TypeDeclaration referenceType() { | |
return methodScope().referenceType(); | |
} | |
// start position in this scope - for ordering scopes vs. variables | |
int startIndex(){ | |
return startIndex; | |
} | |
public String toString() { | |
return toString(0); | |
} | |
public String toString(int tab) { | |
String s = basicToString(tab); | |
for (int i = 0; i < scopeIndex; i++) | |
if (subscopes[i] instanceof BlockScope) | |
s += ((BlockScope) subscopes[i]).toString(tab + 1) + "\n"; //$NON-NLS-1$ | |
return s; | |
} | |
} |