/******************************************************************************* * 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.ast.AbstractMethodDeclaration; import net.sourceforge.phpdt.internal.compiler.ast.ConstructorDeclaration; import net.sourceforge.phpdt.internal.compiler.ast.QualifiedNameReference; import net.sourceforge.phpdt.internal.compiler.ast.SingleNameReference; import net.sourceforge.phpdt.internal.compiler.ast.TypeDeclaration; 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; /** * 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 FieldBinding initializedField; // the field being initialized 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 + "initializedField = " + initializedField; //$NON-NLS-1$ s += newLine + "fieldDeclarationIndex = " + fieldDeclarationIndex; //$NON-NLS-1$ s += newLine + "referenceContext = " + referenceContext; //$NON-NLS-1$ return s; } }