/******************************************************************************* * Copyright (c) 2000, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.wst.jsdt.internal.compiler.lookup; import org.eclipse.wst.jsdt.core.compiler.CharOperation; import org.eclipse.wst.jsdt.core.infer.InferredMethod; import org.eclipse.wst.jsdt.internal.compiler.ast.ASTNode; import org.eclipse.wst.jsdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.wst.jsdt.internal.compiler.ast.Argument; 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.flow.FlowInfo; import org.eclipse.wst.jsdt.internal.compiler.flow.UnconditionalFlowInfo; 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][]; public static final char [] ARGUMENTS_NAME={'a','r','g','u','m','e','n','t','s'}; public LocalVariableBinding argumentsBinding ; public MethodScope(Scope parent, ReferenceContext context, boolean isStatic) { super(METHOD_SCOPE, parent); locals = new LocalVariableBinding[5]; this.referenceContext = context; this.isStatic = isStatic; this.startIndex = 0; argumentsBinding = new LocalVariableBinding(ARGUMENTS_NAME,TypeBinding.UNKNOWN,0,true); argumentsBinding.declaringScope=this; } /* Spec : 8.4.3 & 9.4 */ private void checkAndSetModifiersForConstructor(MethodBinding methodBinding) { int modifiers = methodBinding.modifiers; final ReferenceBinding declaringClass = methodBinding.declaringClass; // if (((ConstructorDeclaration) referenceContext).isDefaultConstructor) { if ((methodBinding.modifiers&ExtraCompilerModifiers.AccIsDefaultConstructor)>0) { // certain flags are propagated from declaring class onto constructor final int DECLARING_FLAGS = ClassFileConstants.AccPublic|ClassFileConstants.AccProtected; final int VISIBILITY_FLAGS = ClassFileConstants.AccPrivate|ClassFileConstants.AccPublic|ClassFileConstants.AccProtected; int flags; if ((flags = declaringClass.modifiers & DECLARING_FLAGS) != 0) { modifiers &= ~VISIBILITY_FLAGS; modifiers |= flags; // propagate public/protected } } // after this point, tests on the 16 bits reserved. int realModifiers = modifiers & ExtraCompilerModifiers.AccJustFlag; // check for incompatible modifiers in the visibility bits, isolate the visibility bits int accessorBits = realModifiers & (ClassFileConstants.AccPublic | ClassFileConstants.AccProtected | ClassFileConstants.AccPrivate); if ((accessorBits & (accessorBits - 1)) != 0) { // need to keep the less restrictive so disable Protected/Private as necessary if ((accessorBits & ClassFileConstants.AccPublic) != 0) { if ((accessorBits & ClassFileConstants.AccProtected) != 0) modifiers &= ~ClassFileConstants.AccProtected; if ((accessorBits & ClassFileConstants.AccPrivate) != 0) modifiers &= ~ClassFileConstants.AccPrivate; } else if ((accessorBits & ClassFileConstants.AccProtected) != 0 && (accessorBits & ClassFileConstants.AccPrivate) != 0) { modifiers &= ~ClassFileConstants.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 (declaringClass.isPrivate() && (modifiers & ClassFileConstants.AccPrivate) != 0) // modifiers &= ~ClassFileConstants.AccPrivate; methodBinding.modifiers = modifiers; } /* Spec : 8.4.3 & 9.4 */ private void checkAndSetModifiersForMethod(MethodBinding methodBinding) { int modifiers = methodBinding.modifiers; final ReferenceBinding declaringClass = methodBinding.declaringClass; // after this point, tests on the 16 bits reserved. int realModifiers = modifiers & ExtraCompilerModifiers.AccJustFlag; // set the requested modifiers for a method in an interface/annotation // if (declaringClass.isInterface()) { // if ((realModifiers & ~(ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract)) != 0) { // if ((declaringClass.modifiers & ClassFileConstants.AccAnnotation) != 0) // problemReporter().illegalModifierForAnnotationMember((AbstractMethodDeclaration) referenceContext); // else // problemReporter().illegalModifierForInterfaceMethod((AbstractMethodDeclaration) referenceContext); // } // return; // } // check for incompatible modifiers in the visibility bits, isolate the visibility bits int accessorBits = realModifiers & (ClassFileConstants.AccPublic | ClassFileConstants.AccProtected | ClassFileConstants.AccPrivate); if ((accessorBits & (accessorBits - 1)) != 0) { // need to keep the less restrictive so disable Protected/Private as necessary if ((accessorBits & ClassFileConstants.AccPublic) != 0) { if ((accessorBits & ClassFileConstants.AccProtected) != 0) modifiers &= ~ClassFileConstants.AccProtected; if ((accessorBits & ClassFileConstants.AccPrivate) != 0) modifiers &= ~ClassFileConstants.AccPrivate; } else if ((accessorBits & ClassFileConstants.AccProtected) != 0 && (accessorBits & ClassFileConstants.AccPrivate) != 0) { modifiers &= ~ClassFileConstants.AccPrivate; } } /* 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; */ // // static members are only authorized in a static member or top level type // if (((realModifiers & ClassFileConstants.AccStatic) != 0) && declaringClass.isNestedType() && !declaringClass.isStatic()) // problemReporter().unexpectedStaticModifierForMethod(declaringClass, (AbstractMethodDeclaration) referenceContext); methodBinding.modifiers = modifiers; } MethodBinding createMethod(InferredMethod inferredMethod,SourceTypeBinding declaringClass) { boolean isConstructor=inferredMethod.isConstructor; if (isConstructor && declaringClass!=inferredMethod.inType.binding) isConstructor=false; MethodBinding binding = createMethod((AbstractMethodDeclaration) inferredMethod.getFunctionDeclaration(),inferredMethod.name,declaringClass, isConstructor,false); if (inferredMethod.isConstructor || declaringClass!=inferredMethod.inType.binding) binding.allocationType=inferredMethod.inType.binding; return binding; } public MethodBinding createMethod(AbstractMethodDeclaration method,char[] name,SourceTypeBinding declaringClass, boolean isConstructor, boolean isLocal) { MethodBinding methodBinding=null; // is necessary to ensure error reporting this.referenceContext = method; method.scope = this; int modifiers = method.modifiers | ExtraCompilerModifiers.AccUnresolved; if ((method.modifiers &(ClassFileConstants.AccPrivate | ClassFileConstants.AccProtected))==0) modifiers|=ClassFileConstants.AccPublic; if (method.inferredMethod!=null && method.inferredMethod.isStatic) modifiers|= ClassFileConstants.AccStatic; if (method.isConstructor() || isConstructor) { if (method.isDefaultConstructor() || isConstructor) { modifiers |= ExtraCompilerModifiers.AccIsDefaultConstructor; } methodBinding = new MethodBinding(modifiers, name, TypeBinding.UNKNOWN, null, declaringClass); methodBinding.tagBits|=TagBits.IsConstructor; checkAndSetModifiersForConstructor(methodBinding); } else { TypeBinding returnType = (method.inferredType!=null)?method.inferredType.resolveType(this,method):TypeBinding.UNKNOWN; if (method.inferredType==null && method.inferredMethod!=null && method.inferredMethod.isConstructor && method.inferredMethod.inType!=null) { returnType=method.inferredMethod.inType.resolveType(this,method); } //return type still null, return type is unknown if (returnType==null) { returnType=TypeBinding.UNKNOWN; } if (isLocal && method.selector!=null) { methodBinding = new LocalFunctionBinding(modifiers, name,returnType, null, declaringClass); } else{// not local method methodBinding = new MethodBinding(modifiers, name,returnType, null, declaringClass); } if (method.inferredMethod!=null) { methodBinding.tagBits |= TagBits.IsInferredType; if ((method.bits&ASTNode.IsInferredJsDocType)!=0) { methodBinding.tagBits |= TagBits.IsInferredJsDocType; } } methodBinding.createFunctionTypeBinding(this); if (method.inferredMethod!=null && method.inferredMethod.isConstructor) { methodBinding.tagBits|=TagBits.IsConstructor; } checkAndSetModifiersForMethod(methodBinding); } this.isStatic =methodBinding.isStatic(); //set arguments Argument[] argTypes = method.arguments; int argLength = argTypes == null ? 0 : argTypes.length; if (argLength > 0 && compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) { if (argTypes[--argLength].isVarArgs()) methodBinding.modifiers |= ClassFileConstants.AccVarargs; } return methodBinding; } 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, ProblemReasons.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, ProblemReasons.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.tagBits & FlowInfo.UNREACHABLE) != 0) return -1; UnconditionalFlowInfo unconditionalFlowInfo = flowInfo.unconditionalInitsWithoutSideEffect(); long[] extraInits = unconditionalFlowInfo.extra == null ? null : unconditionalFlowInfo.extra[0]; 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() { if (parent instanceof ClassScope) return ((ClassScope) parent).referenceContext; return null; } 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; } public LocalVariableBinding findVariable(char[] variableName) { LocalVariableBinding binding = super.findVariable(variableName); if (binding==null && CharOperation.equals(variableName,ARGUMENTS_NAME)) binding=this.argumentsBinding; return binding; } }