--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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 net.sourceforge.phpdt.internal.compiler.lookup;
+
+import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
+import net.sourceforge.phpdt.internal.compiler.flow.UnconditionalFlowInfo;
+import net.sourceforge.phpdt.internal.compiler.impl.ReferenceContext;
+import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
+import net.sourceforge.phpeclipse.internal.compiler.ast.AbstractMethodDeclaration;
+import net.sourceforge.phpeclipse.internal.compiler.ast.AstNode;
+import net.sourceforge.phpeclipse.internal.compiler.ast.ConstructorDeclaration;
+import net.sourceforge.phpeclipse.internal.compiler.ast.QualifiedNameReference;
+import net.sourceforge.phpeclipse.internal.compiler.ast.SingleNameReference;
+import net.sourceforge.phpeclipse.internal.compiler.ast.TypeDeclaration;
+
+/**
+ * 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 static final int NotInFieldDecl = -1; //must be a negative value
+ public boolean isConstructorCall = false;
+ public int fieldDeclarationIndex = NotInFieldDecl;
+
+ // 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()) {
+ 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();
+ 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(
+ 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.declaringClass,
+ 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.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 type of this scope.
+ *
+ * It is the nearest enclosing type of this scope.
+ */
+ public TypeDeclaration referenceType() {
+
+ return (TypeDeclaration) ((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 + "fieldDeclarationIndex = " + fieldDeclarationIndex; //$NON-NLS-1$
+ s += newLine + "referenceContext = " + referenceContext; //$NON-NLS-1$
+ return s;
+ }
+
+}