blob: 9c9f9df2791392e323cae75aba34d0e476385c25 [file] [log] [blame]
package org.eclipse.jdt.internal.compiler.lookup;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import org.eclipse.jdt.internal.compiler.impl.*;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.flow.*;
import org.eclipse.jdt.internal.compiler.problem.*;
import org.eclipse.jdt.internal.compiler.util.*;
/**
* 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 needToCompactLocalVariables;
public boolean isStatic; // method modifier or initializer one
//fields used in the TC process (no real meaning)
public static final int NotInFieldDecl = -1 ; //must be a negative value
public boolean isConstructorCall = false ; //modified on the fly by the TC
public int fieldDeclarationIndex = NotInFieldDecl ; //modified on the fly by the TC
public int analysisIndex; // for setting flow-analysis id
public boolean isPropagatingInnerClassEmulation;
// for local variables table attributes
public int lastIndex = 0;
public long[] definiteInits = new long[4];
public long[][] extraDefiniteInits = new long[4][];
public MethodScope(ClassScope parent, ReferenceContext context, boolean isStatic) {
super(METHOD_SCOPE, parent);
locals = new LocalVariableBinding[5];
this.referenceContext = context;
this.isStatic = isStatic;
this.startIndex = 0;
}
String basicToString(int tab) {
String newLine = "\n"/*nonNLS*/;
for (int i = tab; --i >= 0;)
newLine += "\t"/*nonNLS*/;
String s = newLine + "--- Method Scope ---"/*nonNLS*/;
newLine += "\t"/*nonNLS*/;
s += newLine + "locals:"/*nonNLS*/;
for (int i = 0; i < localIndex; i++)
s += newLine + "\t"/*nonNLS*/ + locals[i].toString();
s += newLine + "startIndex = "/*nonNLS*/ + startIndex;
s += newLine + "isConstructorCall = "/*nonNLS*/ + isConstructorCall;
s += newLine + "fieldDeclarationIndex = "/*nonNLS*/ + fieldDeclarationIndex;
return s;
}
/* Spec : 8.4.3 & 9.4
*/
private void checkAndSetModifiersForConstructor(MethodBinding methodBinding) {
int modifiers = methodBinding.modifiers;
if ((modifiers & AccAlternateModifierProblem) != 0)
problemReporter().duplicateModifierForMethod(methodBinding.declaringClass, (AbstractMethodDeclaration) referenceContext);
if (((ConstructorDeclaration) referenceContext).isDefaultConstructor) {
if (methodBinding.declaringClass.isPublic())
modifiers |= AccPublic;
else if (methodBinding.declaringClass.isProtected())
modifiers |= AccProtected;
}
// after this point, tests on the 16 bits reserved.
int realModifiers = modifiers & AccJustFlag;
// check for abnormal modifiers
int unexpectedModifiers = ~(AccPublic | AccPrivate | AccProtected | AccStrictfp);
if ((realModifiers & unexpectedModifiers) != 0)
problemReporter().illegalModifierForMethod(methodBinding.declaringClass, (AbstractMethodDeclaration) referenceContext);
else if ((((AbstractMethodDeclaration) referenceContext).modifiers & AccStrictfp) != 0) // must check the parse node explicitly
problemReporter().illegalModifierForMethod(methodBinding.declaringClass, (AbstractMethodDeclaration) referenceContext);
// check for incompatible modifiers in the visibility bits, isolate the visibility bits
int accessorBits = realModifiers & (AccPublic | AccProtected | AccPrivate);
if ((accessorBits & (accessorBits - 1)) != 0) {
problemReporter().illegalVisibilityModifierCombinationForMethod(methodBinding.declaringClass, (AbstractMethodDeclaration) referenceContext);
// need to keep the less restrictive
if ((accessorBits & AccPublic) != 0) {
if ((accessorBits & AccProtected) != 0)
modifiers ^= AccProtected;
if ((accessorBits & AccPrivate) != 0)
modifiers ^= AccPrivate;
}
if ((accessorBits & AccProtected) != 0)
if ((accessorBits & AccPrivate) != 0)
modifiers ^= 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 (methodBinding.declaringClass.isPrivate())
if ((modifiers & AccPrivate) != 0)
modifiers ^= AccPrivate;
methodBinding.modifiers = modifiers;
}
/* Spec : 8.4.3 & 9.4
*/
private void checkAndSetModifiersForMethod(MethodBinding methodBinding) {
int modifiers = methodBinding.modifiers;
if ((modifiers & AccAlternateModifierProblem) != 0)
problemReporter().duplicateModifierForMethod(methodBinding.declaringClass, (AbstractMethodDeclaration) referenceContext);
// after this point, tests on the 16 bits reserved.
int realModifiers = modifiers & AccJustFlag;
// set the requested modifiers for a method in an interface
if (methodBinding.declaringClass.isInterface()) {
if ((realModifiers & ~(AccPublic | AccAbstract)) != 0)
problemReporter().illegalModifierForInterfaceMethod(methodBinding.declaringClass, (AbstractMethodDeclaration) referenceContext);
return;
}
// check for abnormal modifiers
int unexpectedModifiers = ~(AccPublic | AccPrivate | AccProtected | AccAbstract | AccStatic | AccFinal | AccSynchronized | AccNative | AccStrictfp);
if ((realModifiers & unexpectedModifiers) != 0)
problemReporter().illegalModifierForMethod(methodBinding.declaringClass, (AbstractMethodDeclaration) referenceContext);
// check for incompatible modifiers in the visibility bits, isolate the visibility bits
int accessorBits = realModifiers & (AccPublic | AccProtected | AccPrivate);
if ((accessorBits & (accessorBits - 1)) != 0) {
problemReporter().illegalVisibilityModifierCombinationForMethod(methodBinding.declaringClass, (AbstractMethodDeclaration) referenceContext);
// need to keep the less restrictive
if ((accessorBits & AccPublic) != 0) {
if ((accessorBits & AccProtected) != 0)
modifiers ^= AccProtected;
if ((accessorBits & AccPrivate) != 0)
modifiers ^= AccPrivate;
}
if ((accessorBits & AccProtected) != 0)
if ((accessorBits & AccPrivate) != 0)
modifiers ^= AccPrivate;
}
// check for modifiers incompatible with abstract modifier
if ((modifiers & AccAbstract) != 0) {
int incompatibleWithAbstract = AccPrivate | AccStatic | AccFinal | AccSynchronized | AccNative | AccStrictfp;
if ((modifiers & incompatibleWithAbstract) != 0)
problemReporter().illegalAbstractModifierCombinationForMethod(methodBinding.declaringClass, (AbstractMethodDeclaration) referenceContext);
if (!methodBinding.declaringClass.isAbstract())
problemReporter().abstractMethodInAbstractClass((SourceTypeBinding) methodBinding.declaringClass, (AbstractMethodDeclaration) referenceContext);
}
/* 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;
*/
// native methods cannot also be tagged as strictfp
if ((modifiers & AccNative) != 0 && (modifiers & AccStrictfp) != 0)
problemReporter().nativeMethodsCannotBeStrictfp(methodBinding.declaringClass, (AbstractMethodDeclaration) referenceContext);
// static members are only authorized in a static member or top level type
if (((realModifiers & AccStatic) != 0) && methodBinding.declaringClass.isNestedType() && !methodBinding.declaringClass.isStatic())
problemReporter().unexpectedStaticModifierForMethod(methodBinding.declaringClass, (AbstractMethodDeclaration) referenceContext);
methodBinding.modifiers = modifiers;
}
/* Error management:
* keep null for all the errors that prevent the method to be created
* otherwise return a correct method binding (but without the element
* that caused the problem) : ie : Incorrect thrown exception
*/
MethodBinding createMethod(AbstractMethodDeclaration method) {
// is necessary to ensure error reporting
this.referenceContext = method;
method.scope = this;
SourceTypeBinding declaringClass = referenceType().binding;
int modifiers = method.modifiers | AccUnresolved;
if (method.isConstructor()) {
method.binding = new MethodBinding(modifiers, null, null, declaringClass);
checkAndSetModifiersForConstructor(method.binding);
} else {
if (((MethodDeclaration) method).returnType == null) {
problemReporter().missingReturnType(method);
return null;
}
if (declaringClass.isInterface())
modifiers |= AccPublic | AccAbstract;
method.binding = new MethodBinding(modifiers, method.selector, null, null, null, declaringClass);
checkAndSetModifiersForMethod(method.binding);
}
this.isStatic = method.binding.isStatic();
return method.binding;
}
/* Overridden to detect the error case inside an explicit constructor call:
class X {
int i;
X myX;
X(X x) {
this(i, myX.i, x.i); // same for super calls... only the first 2 field accesses are errors
}
}
*/
public FieldBinding findField(TypeBinding receiverType, char[] fieldName, InvocationSite invocationSite) {
FieldBinding field = super.findField(receiverType, fieldName, invocationSite);
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(fieldName, 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(fieldName, NonStaticReferenceInConstructorInvocation);
}
return field;
}
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;
} else {
return outerMethodScope.problemReporter();
}
}
public final int recordInitializationStates(FlowInfo flowInfo) {
if ((flowInfo == FlowInfo.DeadEnd) || (flowInfo.isFakeReachable())) {
return -1;
}
UnconditionalFlowInfo unconditionalFlowInfo = flowInfo.unconditionalInits();
long[] extraInits = unconditionalFlowInfo.extraDefiniteInits;
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 type of this scope.
*
* i.e. the nearest enclosing type of this scope.
*/
public TypeDeclaration referenceType() {
return (TypeDeclaration) ((ClassScope) parent).referenceContext;
}
}