blob: df71321ce2788278cd32948824bf93069347dca4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2014 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.wst.jsdt.internal.compiler.lookup;
import org.eclipse.wst.jsdt.core.ast.IAbstractVariableDeclaration;
import org.eclipse.wst.jsdt.core.compiler.CharOperation;
import org.eclipse.wst.jsdt.core.infer.InferredMethod;
import org.eclipse.wst.jsdt.internal.compiler.ast.ASTNode;
import org.eclipse.wst.jsdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.wst.jsdt.internal.compiler.ast.Argument;
import org.eclipse.wst.jsdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.wst.jsdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.wst.jsdt.internal.compiler.ast.ProgramElement;
import org.eclipse.wst.jsdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.wst.jsdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.wst.jsdt.internal.compiler.ast.Statement;
import org.eclipse.wst.jsdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.wst.jsdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.wst.jsdt.internal.compiler.flow.FlowInfo;
import org.eclipse.wst.jsdt.internal.compiler.flow.UnconditionalFlowInfo;
import org.eclipse.wst.jsdt.internal.compiler.impl.ReferenceContext;
import org.eclipse.wst.jsdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.wst.jsdt.internal.compiler.util.HashtableOfObject;
/**
* Particular block scope used for methods, constructors or clinits, representing
* its outermost blockscope. Note also that such a scope will be provided to enclose
* field initializers subscopes as well.
*/
public class MethodScope extends BlockScope {
public ReferenceContext referenceContext;
public boolean isStatic; // method modifier or initializer one
//fields used during name resolution
public boolean isConstructorCall = false;
public FieldBinding initializedField; // the field being initialized
public int lastVisibleFieldID = -1; // the ID of the last field which got declared
// note that #initializedField can be null AND lastVisibleFieldID >= 0, when processing instance field initializers.
// flow analysis
public int analysisIndex; // for setting flow-analysis id
// for local variables table attributes
public int lastIndex = 0;
public long[] definiteInits = new long[4];
public long[][] extraDefiniteInits = new long[4][];
public static final char [] ARGUMENTS_NAME={'a','r','g','u','m','e','n','t','s'};
public LocalVariableBinding argumentsBinding;
/**
* <p>
* Map of variable names to statements that have not been resolved that
* define those variables.
* </p>
*/
private HashtableOfObject fUnresolvedLocalVars;
/**
* <p>
* Map of function names to statements that have not been resolved that
* define those variables.
* </p>
*/
private HashtableOfObject fUnresolvedLocalFuncs;
public MethodScope(Scope parent, ReferenceContext context, boolean isStatic) {
super(METHOD_SCOPE, parent);
locals = new LocalVariableBinding[5];
this.referenceContext = context;
this.isStatic = isStatic;
this.startIndex = 0;
argumentsBinding = new LocalVariableBinding(ARGUMENTS_NAME,TypeBinding.UNKNOWN,0,true);
argumentsBinding.declaringScope=this;
}
/* Spec : 8.4.3 & 9.4
*/
private void checkAndSetModifiersForConstructor(MethodBinding methodBinding) {
int modifiers = methodBinding.modifiers;
final ReferenceBinding declaringClass = methodBinding.declaringClass;
// if (((ConstructorDeclaration) referenceContext).isDefaultConstructor) {
if ((methodBinding.modifiers&ExtraCompilerModifiers.AccIsDefaultConstructor)>0) {
// certain flags are propagated from declaring class onto constructor
final int DECLARING_FLAGS = ClassFileConstants.AccPublic|ClassFileConstants.AccProtected;
final int VISIBILITY_FLAGS = ClassFileConstants.AccPrivate|ClassFileConstants.AccPublic|ClassFileConstants.AccProtected;
int flags;
if ((flags = declaringClass.modifiers & DECLARING_FLAGS) != 0) {
modifiers &= ~VISIBILITY_FLAGS;
modifiers |= flags; // propagate public/protected
}
}
// after this point, tests on the 16 bits reserved.
int realModifiers = modifiers & ExtraCompilerModifiers.AccJustFlag;
// check for incompatible modifiers in the visibility bits, isolate the visibility bits
int accessorBits = realModifiers & (ClassFileConstants.AccPublic | ClassFileConstants.AccProtected | ClassFileConstants.AccPrivate);
if ((accessorBits & (accessorBits - 1)) != 0) {
// need to keep the less restrictive so disable Protected/Private as necessary
if ((accessorBits & ClassFileConstants.AccPublic) != 0) {
if ((accessorBits & ClassFileConstants.AccProtected) != 0)
modifiers &= ~ClassFileConstants.AccProtected;
if ((accessorBits & ClassFileConstants.AccPrivate) != 0)
modifiers &= ~ClassFileConstants.AccPrivate;
} else if ((accessorBits & ClassFileConstants.AccProtected) != 0 && (accessorBits & ClassFileConstants.AccPrivate) != 0) {
modifiers &= ~ClassFileConstants.AccPrivate;
}
}
// // if the receiver's declaring class is a private nested type, then make sure the receiver is not private (causes problems for inner type emulation)
// if (declaringClass.isPrivate() && (modifiers & ClassFileConstants.AccPrivate) != 0)
// modifiers &= ~ClassFileConstants.AccPrivate;
methodBinding.modifiers = modifiers;
}
/* Spec : 8.4.3 & 9.4
*/
private void checkAndSetModifiersForMethod(MethodBinding methodBinding) {
int modifiers = methodBinding.modifiers;
final ReferenceBinding declaringClass = methodBinding.declaringClass;
// after this point, tests on the 16 bits reserved.
int realModifiers = modifiers & ExtraCompilerModifiers.AccJustFlag;
// set the requested modifiers for a method in an interface/annotation
// if (declaringClass.isInterface()) {
// if ((realModifiers & ~(ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract)) != 0) {
// if ((declaringClass.modifiers & ClassFileConstants.AccAnnotation) != 0)
// problemReporter().illegalModifierForAnnotationMember((AbstractMethodDeclaration) referenceContext);
// else
// problemReporter().illegalModifierForInterfaceMethod((AbstractMethodDeclaration) referenceContext);
// }
// return;
// }
// check for incompatible modifiers in the visibility bits, isolate the visibility bits
int accessorBits = realModifiers & (ClassFileConstants.AccPublic | ClassFileConstants.AccProtected | ClassFileConstants.AccPrivate);
if ((accessorBits & (accessorBits - 1)) != 0) {
// need to keep the less restrictive so disable Protected/Private as necessary
if ((accessorBits & ClassFileConstants.AccPublic) != 0) {
if ((accessorBits & ClassFileConstants.AccProtected) != 0)
modifiers &= ~ClassFileConstants.AccProtected;
if ((accessorBits & ClassFileConstants.AccPrivate) != 0)
modifiers &= ~ClassFileConstants.AccPrivate;
} else if ((accessorBits & ClassFileConstants.AccProtected) != 0 && (accessorBits & ClassFileConstants.AccPrivate) != 0) {
modifiers &= ~ClassFileConstants.AccPrivate;
}
}
/* DISABLED for backward compatibility with javac (if enabled should also mark private methods as final)
// methods from a final class are final : 8.4.3.3
if (methodBinding.declaringClass.isFinal())
modifiers |= AccFinal;
*/
// // static members are only authorized in a static member or top level type
// if (((realModifiers & ClassFileConstants.AccStatic) != 0) && declaringClass.isNestedType() && !declaringClass.isStatic())
// problemReporter().unexpectedStaticModifierForMethod(declaringClass, (AbstractMethodDeclaration) referenceContext);
methodBinding.modifiers = modifiers;
}
MethodBinding createMethod(InferredMethod inferredMethod,SourceTypeBinding declaringClass) {
boolean isConstructor=inferredMethod.isConstructor;
if (isConstructor && declaringClass!=inferredMethod.inType.binding)
isConstructor=false;
MethodBinding binding = createMethod((AbstractMethodDeclaration) inferredMethod.getFunctionDeclaration(),inferredMethod.name,declaringClass, isConstructor,false);
if (inferredMethod.isConstructor || declaringClass!=inferredMethod.inType.binding)
binding.allocationType=inferredMethod.inType.binding;
return binding;
}
public MethodBinding createMethod(AbstractMethodDeclaration method,char[] name,SourceTypeBinding declaringClass, boolean isConstructor, boolean isLocal) {
MethodBinding methodBinding=null;
// is necessary to ensure error reporting
this.referenceContext = method;
method.setScope(this);
int modifiers = method.modifiers | ExtraCompilerModifiers.AccUnresolved;
if ((method.modifiers &(ClassFileConstants.AccPrivate | ClassFileConstants.AccProtected))==0)
modifiers|=ClassFileConstants.AccPublic;
if (method.inferredMethod!=null && method.inferredMethod.isStatic)
modifiers|= ClassFileConstants.AccStatic;
if (isConstructor) {
if (method.isDefaultConstructor() || isConstructor) {
modifiers |= ExtraCompilerModifiers.AccIsDefaultConstructor;
}
TypeBinding constructorType = null;
if (method.inferredMethod!=null && method.inferredMethod.inType != null) {
constructorType=method.inferredMethod.inType.resolveType(this,method);
}
//return type still null, return type is unknown
if (constructorType==null) {
constructorType=TypeBinding.UNKNOWN;
}
methodBinding = new MethodBinding(modifiers, name, constructorType, null, constructorType instanceof ReferenceBinding ? (ReferenceBinding) constructorType : declaringClass);
methodBinding.tagBits|=TagBits.IsConstructor;
checkAndSetModifiersForConstructor(methodBinding);
} else {
TypeBinding returnType =
(method.inferredType!=null)?method.inferredType.resolveType(this,method):TypeBinding.UNKNOWN;
if (method.inferredType==null && method.inferredMethod!=null && method.inferredMethod.isConstructor
&& method.inferredMethod.inType!=null) {
returnType=method.inferredMethod.inType.resolveType(this,method);
}
//return type still null, return type is unknown
if (returnType==null) {
returnType=TypeBinding.UNKNOWN;
}
if (isLocal && method.getName()!=null) {
methodBinding =
new LocalFunctionBinding(modifiers, name,returnType, null, declaringClass);
} else{// not local method
methodBinding =
new MethodBinding(modifiers, name,returnType, null, declaringClass);
}
if (method.inferredMethod!=null) {
methodBinding.tagBits |= TagBits.IsInferredType;
if ((method.bits&ASTNode.IsInferredJsDocType)!=0) {
methodBinding.tagBits |= TagBits.IsInferredJsDocType;
}
}
//methodBinding.createFunctionTypeBinding(this);
// if (method.inferredMethod!=null && method.inferredMethod.isConstructor) {
// methodBinding.tagBits|=TagBits.IsConstructor;
// }
checkAndSetModifiersForMethod(methodBinding);
}
methodBinding.createFunctionTypeBinding(this);
this.isStatic =methodBinding.isStatic();
//set arguments
Argument[] argTypes = method.arguments;
int argLength = argTypes == null ? 0 : argTypes.length;
if (argLength > 0 && compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) {
if (argTypes[--argLength].isVarArgs())
methodBinding.modifiers |= ClassFileConstants.AccVarargs;
}
return methodBinding;
}
public FieldBinding findField(
TypeBinding receiverType,
char[] fieldName,
InvocationSite invocationSite,
boolean needResolve) {
FieldBinding field = super.findField(receiverType, fieldName, invocationSite, needResolve);
if (field == null)
return null;
if (!field.isValidBinding())
return field; // answer the error field
if (field.isStatic())
return field; // static fields are always accessible
if (!isConstructorCall || receiverType != enclosingSourceType())
return field;
if (invocationSite instanceof SingleNameReference)
return new ProblemFieldBinding(
field, // closest match
field.declaringClass,
fieldName,
ProblemReasons.NonStaticReferenceInConstructorInvocation);
if (invocationSite instanceof QualifiedNameReference) {
// look to see if the field is the first binding
QualifiedNameReference name = (QualifiedNameReference) invocationSite;
if (name.binding == null)
// only true when the field is the fieldbinding at the beginning of name's tokens
return new ProblemFieldBinding(
field, // closest match
field.declaringClass,
fieldName,
ProblemReasons.NonStaticReferenceInConstructorInvocation);
}
return field;
}
public boolean isInsideConstructor() {
return (referenceContext instanceof ConstructorDeclaration);
}
public boolean isInsideInitializer() {
return (referenceContext instanceof TypeDeclaration);
}
public boolean isInsideInitializerOrConstructor() {
return (referenceContext instanceof TypeDeclaration)
|| (referenceContext instanceof ConstructorDeclaration);
}
/* 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() {
MethodScope outerMethodScope;
if ((outerMethodScope = outerMostMethodScope()) == this) {
ProblemReporter problemReporter = referenceCompilationUnit().problemReporter;
problemReporter.referenceContext = referenceContext;
return problemReporter;
}
return outerMethodScope.problemReporter();
}
public final int recordInitializationStates(FlowInfo flowInfo) {
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0) return -1;
UnconditionalFlowInfo unconditionalFlowInfo = flowInfo.unconditionalInitsWithoutSideEffect();
long[] extraInits = unconditionalFlowInfo.extra == null ?
null : unconditionalFlowInfo.extra[0];
long inits = unconditionalFlowInfo.definiteInits;
checkNextEntry : for (int i = lastIndex; --i >= 0;) {
if (definiteInits[i] == inits) {
long[] otherInits = extraDefiniteInits[i];
if ((extraInits != null) && (otherInits != null)) {
if (extraInits.length == otherInits.length) {
int j, max;
for (j = 0, max = extraInits.length; j < max; j++) {
if (extraInits[j] != otherInits[j]) {
continue checkNextEntry;
}
}
return i;
}
} else {
if ((extraInits == null) && (otherInits == null)) {
return i;
}
}
}
}
// add a new entry
if (definiteInits.length == lastIndex) {
// need a resize
System.arraycopy(
definiteInits,
0,
(definiteInits = new long[lastIndex + 20]),
0,
lastIndex);
System.arraycopy(
extraDefiniteInits,
0,
(extraDefiniteInits = new long[lastIndex + 20][]),
0,
lastIndex);
}
definiteInits[lastIndex] = inits;
if (extraInits != null) {
extraDefiniteInits[lastIndex] = new long[extraInits.length];
System.arraycopy(
extraInits,
0,
extraDefiniteInits[lastIndex],
0,
extraInits.length);
}
return lastIndex++;
}
/* Answer the reference method of this scope, or null if initialization scoope.
*/
public AbstractMethodDeclaration referenceMethod() {
if (referenceContext instanceof AbstractMethodDeclaration) return (AbstractMethodDeclaration) referenceContext;
return null;
}
/* Answer the reference type of this scope.
*
* It is the nearest enclosing type of this scope.
*/
public TypeDeclaration referenceType() {
if (parent instanceof ClassScope)
return ((ClassScope) parent).referenceContext;
return null;
}
String basicToString(int tab) {
String newLine = "\n"; //$NON-NLS-1$
for (int i = tab; --i >= 0;)
newLine += "\t"; //$NON-NLS-1$
String s = newLine + "--- Method 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$
s += newLine + "isConstructorCall = " + isConstructorCall; //$NON-NLS-1$
s += newLine + "initializedField = " + initializedField; //$NON-NLS-1$
s += newLine + "lastVisibleFieldID = " + lastVisibleFieldID; //$NON-NLS-1$
s += newLine + "referenceContext = " + referenceContext; //$NON-NLS-1$
return s;
}
/**
* <p>
* This implementation first calls super, if that returns no binding or a
* problem binding then the list of unresolved local variables is checked,
* and if there is a statement that defines a variable with the correct
* name it is resolved, and then the super implementation is called again.
* </p>
*
* <p>
* This allows {@link #addUnresolvedLocalVar(char[], Statement)} to be used in
* conjunction with this method to prevent having to resolve the entire
* {@link AbstractMethodDeclaration} associated with this scope.
* </p>
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.BlockScope#findVariable(char[])
*/
public LocalVariableBinding findVariable(char[] variableName) {
LocalVariableBinding binding = super.findVariable(variableName);
if (binding == null && CharOperation.equals(variableName,ARGUMENTS_NAME)) {
binding = this.argumentsBinding;
}
// if super could not find a good binding then check list of unresolved local variables
if(binding == null && this.fUnresolvedLocalVars != null) {
IAbstractVariableDeclaration statement = (IAbstractVariableDeclaration)this.fUnresolvedLocalVars.removeKey(variableName);
if(statement != null && statement instanceof ProgramElement) {
//resolve and then call super again
if (statement instanceof LocalDeclaration) {
((LocalDeclaration)statement).resolveLocal(this);
} else {
((ProgramElement)statement).resolve(this);
}
binding = super.findVariable(variableName);
}
}
return binding;
}
/**
* <p>
* This implementation first calls super, if that returns no binding or a
* problem binding then the list of unresolved local functions is checked,
* and if there is a statement that defines a function with the correct
* name it is resolved, and then its binding is returned.
* </p>
*
* <p>
* This allows {@link #addUnresolvedLocalFunc(char[], AbstractMethodDeclaration)} to be used in
* conjunction with this method to prevent having to resolve the entire
* {@link AbstractMethodDeclaration} associated with this scope.
* </p>
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.Scope#findMethod(org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding, char[], org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding[], org.eclipse.wst.jsdt.internal.compiler.lookup.InvocationSite)
*/
public MethodBinding findMethod(ReferenceBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite) {
MethodBinding binding = super.findMethod(receiverType, selector, argumentTypes, invocationSite);
// if super could not find a good binding then check list of unresolved local functions
if((binding == null || !binding.isValidBinding()) && this.fUnresolvedLocalFuncs != null) {
AbstractMethodDeclaration statement = (AbstractMethodDeclaration)this.fUnresolvedLocalFuncs.removeKey(selector);
if(statement != null) {
//resolve and then return the statements binding
statement.resolve(this);
binding = statement.getBinding();
}
}
return binding;
}
/**
* <p>
* This implementation first calls super, if that returns no binding or a
* problem binding then the list of unresolved local functions is checked,
* and if there is a statement that defines a function with the correct
* name it is resolved, and then its binding is returned.
* </p>
*
* <p>
* This allows {@link #addUnresolvedLocalFunc(char[], AbstractMethodDeclaration)} to be used in
* conjunction with this method to prevent having to resolve the entire
* {@link AbstractMethodDeclaration} associated with this scope.
* </p>
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.Scope#findMethod(org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding, char[], org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding[], org.eclipse.wst.jsdt.internal.compiler.lookup.InvocationSite)
*/
public MethodBinding findMethod(char[] methodName, TypeBinding[]argumentTypes, boolean checkVars) {
MethodBinding binding = super.findMethod(methodName, argumentTypes, checkVars);
// if super could not find a good binding then check list of unresolved local functions
if((binding == null || !binding.isValidBinding()) && this.fUnresolvedLocalFuncs != null) {
AbstractMethodDeclaration methDecl = (AbstractMethodDeclaration)this.fUnresolvedLocalFuncs.removeKey(methodName);
if(methDecl != null) {
//resolve and then return the statements binding
binding = methDecl.getBinding();
if(binding == null || !binding.isValidBinding()) {
/* if it is anonymous, but has a name (has to to be here) then this is a
* function assignment to a variable not declared in this scope so
* build with compilation unit scope.
*
* else build with this scope */
Scope scope = null;
if(methDecl.isAnonymous()) {
scope = this.compilationUnitScope();
} else {
scope = this;
}
methDecl.resolve(scope);
}
binding = methDecl.getBinding();
}
}
return binding;
}
/**
* <p>
* Adds an unresolved local variable defined in this scope to be resolved
* on an as needed basses.
* </p>
*
* @param name
* the name of the unresolved local variable
* @param expr
* {@link IAbstractVariableDeclaration} that defines the unresolved local variable
* that will be used to resolve it if needed
*/
public void addUnresolvedLocalVar(char[] name, IAbstractVariableDeclaration expr) {
if(name != null && name.length > 0 && expr != null) {
if(this.fUnresolvedLocalVars == null) {
this.fUnresolvedLocalVars = new HashtableOfObject();
}
this.fUnresolvedLocalVars.put(name, expr);
}
}
/**
* <p>
* Adds an unresolved local function defined in this scope to be resolved
* on an as needed basses.
* </p>
*
* @param name
* the name of the unresolved local function
* @param expr
* {@link AbstractMethodDeclaration} that defines the unresolved local function
* that will be used to resolve it if needed
*/
public void addUnresolvedLocalFunc(char[] name, AbstractMethodDeclaration func) {
if(name != null && name.length > 0 && func != null) {
if(this.fUnresolvedLocalFuncs == null) {
this.fUnresolvedLocalFuncs = new HashtableOfObject();
}
this.fUnresolvedLocalFuncs.put(name, func);
}
}
}