| /******************************************************************************* |
| * 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.jdt.internal.compiler.lookup; |
| |
| import org.eclipse.jdt.internal.compiler.ast.*; |
| import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; |
| import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; |
| import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; |
| import org.eclipse.jdt.internal.compiler.codegen.CodeStream; |
| import org.eclipse.jdt.internal.compiler.flow.FlowInfo; |
| import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo; |
| import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
| import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; |
| import org.eclipse.jdt.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(); |
| 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.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 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; |
| } |
| |
| } |