/******************************************************************************* | |
* Copyright (c) 2000, 2004 IBM Corporation and others. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Common Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/cpl-v10.html | |
* | |
* Contributors: | |
* IBM Corporation - initial API and implementation | |
*******************************************************************************/ | |
package org.eclipse.wst.jsdt.internal.compiler.lookup; | |
import org.eclipse.wst.jsdt.internal.compiler.ast.*; | |
import org.eclipse.wst.jsdt.internal.compiler.ast.AbstractMethodDeclaration; | |
import org.eclipse.wst.jsdt.internal.compiler.ast.ConstructorDeclaration; | |
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.TypeDeclaration; | |
import org.eclipse.wst.jsdt.internal.compiler.classfmt.ClassFileConstants; | |
import org.eclipse.wst.jsdt.internal.compiler.codegen.CodeStream; | |
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.CompilerOptions; | |
import org.eclipse.wst.jsdt.internal.compiler.impl.ReferenceContext; | |
import org.eclipse.wst.jsdt.internal.compiler.problem.ProblemReporter; | |
/** | |
* 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 | |
public boolean isPropagatingInnerClassEmulation; | |
// for local variables table attributes | |
public int lastIndex = 0; | |
public long[] definiteInits = new long[4]; | |
public long[][] extraDefiniteInits = new long[4][]; | |
// inner-emulation | |
public SyntheticArgumentBinding[] extraSyntheticArguments; | |
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; | |
} | |
/* 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; | |
} | |
/* Compute variable positions in scopes given an initial position offset | |
* ignoring unused local variables. | |
* | |
* Deal with arguments here, locals and subscopes are processed in BlockScope method | |
*/ | |
public void computeLocalVariablePositions(int initOffset, CodeStream codeStream) { | |
boolean isReportingUnusedArgument = false; | |
if (referenceContext instanceof AbstractMethodDeclaration) { | |
AbstractMethodDeclaration methodDecl = (AbstractMethodDeclaration)referenceContext; | |
MethodBinding method = methodDecl.binding; | |
CompilerOptions options = compilationUnitScope().environment.options; | |
if (!(method.isAbstract() | |
|| (method.isImplementing() && !options.reportUnusedParameterWhenImplementingAbstract) | |
|| (method.isOverriding() && !method.isImplementing() && !options.reportUnusedParameterWhenOverridingConcrete) | |
|| method.isMain())) { | |
isReportingUnusedArgument = true; | |
} | |
} | |
this.offset = initOffset; | |
this.maxOffset = initOffset; | |
// manage arguments | |
int ilocal = 0, maxLocals = this.localIndex; | |
while (ilocal < maxLocals) { | |
LocalVariableBinding local = locals[ilocal]; | |
if (local == null || !local.isArgument) break; // done with arguments | |
// do not report fake used variable | |
if (isReportingUnusedArgument | |
&& local.useFlag == LocalVariableBinding.UNUSED | |
&& ((local.declaration.bits & ASTNode.IsLocalDeclarationReachableMASK) != 0)) { // declaration is reachable | |
this.problemReporter().unusedArgument(local.declaration); | |
} | |
// record user-defined argument for attribute generation | |
codeStream.record(local); | |
// assign variable position | |
local.resolvedPosition = this.offset; | |
if ((local.type == LongBinding) || (local.type == DoubleBinding)) { | |
this.offset += 2; | |
} else { | |
this.offset++; | |
} | |
// check for too many arguments/local variables | |
if (this.offset > 0xFF) { // no more than 255 words of arguments | |
this.problemReporter().noMoreAvailableSpaceForArgument(local, local.declaration); | |
} | |
ilocal++; | |
} | |
// sneak in extra argument before other local variables | |
if (extraSyntheticArguments != null) { | |
for (int iarg = 0, maxArguments = extraSyntheticArguments.length; iarg < maxArguments; iarg++){ | |
SyntheticArgumentBinding argument = extraSyntheticArguments[iarg]; | |
argument.resolvedPosition = this.offset; | |
if ((argument.type == LongBinding) || (argument.type == DoubleBinding)){ | |
this.offset += 2; | |
} else { | |
this.offset++; | |
} | |
if (this.offset > 0xFF) { // no more than 255 words of arguments | |
this.problemReporter().noMoreAvailableSpaceForArgument(argument, (ASTNode)this.referenceContext); | |
} | |
} | |
} | |
this.computeLocalVariablePositions(ilocal, this.offset, codeStream); | |
} | |
/* 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()) { | |
if (method.isDefaultConstructor()) { | |
modifiers |= AccIsDefaultConstructor; | |
} | |
method.binding = new MethodBinding(modifiers, null, null, declaringClass); | |
checkAndSetModifiersForConstructor(method.binding); | |
} else { | |
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(); | |
TypeParameter[] typeParameters = method.typeParameters(); | |
// do not construct type variables if source < 1.5 | |
if (typeParameters == null || environment().options.sourceLevel < ClassFileConstants.JDK1_5) { | |
method.binding.typeVariables = NoTypeVariables; | |
} else { | |
method.binding.typeVariables = createTypeVariables(typeParameters, method.binding); | |
method.binding.modifiers |= AccGenericSignature; | |
} | |
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, | |
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, | |
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, | |
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.isReachable()) 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 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() { | |
return ((ClassScope) parent).referenceContext; | |
} | |
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; | |
} | |
} |