| /******************************************************************************* |
| * * Copyright (c) 2000, 2020 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * IBM Corporation - initial API and implementation |
| * Fraunhofer FIRST - extended API and implementation |
| * Technical University Berlin - extended API and implementation |
| * Stephan Herrmann - Contributions for |
| * bug 349326 - [1.7] new warning for missing try-with-resources |
| * bug 374605 - Unreasonable warning for enum-based switch statements |
| * bug 382353 - [1.8][compiler] Implementation property modifiers should be accepted on default methods. |
| * bug 382354 - [1.8][compiler] Compiler silent on conflicting modifier |
| * bug 401030 - [1.8][null] Null analysis support for lambda methods. |
| * Bug 416176 - [1.8][compiler][null] null type annotations cause grief on type variables |
| * Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault |
| * Jesper S Moller - Contributions for |
| * bug 382701 - [1.8][compiler] Implement semantic analysis of Lambda expressions & Reference expression |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.compiler.lookup; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.ASTVisitor; |
| import org.eclipse.jdt.internal.compiler.ast.*; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.codegen.CodeStream; |
| import org.eclipse.jdt.internal.compiler.codegen.ConstantPool; |
| import org.eclipse.jdt.internal.compiler.flow.FlowInfo; |
| import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo; |
| import org.eclipse.jdt.internal.compiler.impl.IrritantSet; |
| import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; |
| import org.eclipse.jdt.internal.compiler.problem.IProblemRechecker; |
| import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.ast.AbstractMethodMappingDeclaration; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.ast.CallinMappingDeclaration; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.WordValueAttribute; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.CallinCalloutScope; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.model.MethodModel; |
| |
| /** |
| * OTDT changes: |
| * |
| * What: Different rules for modifiers of role methods |
| * Why: Callout bindings open up new cases: |
| * + method is declared abstract but implemented by a callout binding, |
| * => must defere check for abstract method in concrete class until after mapping-transformation. |
| * + static methods are allowed and can even be abstract. |
| * + also "private static" is OK. |
| * New modifier AccCallin. |
| * |
| * What: Pass the MethodModel from AST to Binding |
| * Where:createMethod(). |
| * |
| * What: New query isCallinWrapper() |
| * |
| * =============== |
| * Specific 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 |
| /* By specifying {@code -Djdt.flow.test.extra=true} tests can push all flow analysis into the extra bits of UnconditionalFlowInfo. */ |
| private static int baseAnalysisIndex = 0; |
| public int analysisIndex = baseAnalysisIndex; // 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; |
| |
| // remember suppressed warning re missing 'default:' to give hints on possibly related flow problems |
| public boolean hasMissingSwitchDefault; // TODO(stephan): combine flags to a bitset? |
| |
| public boolean isCompactConstructorScope = false; |
| |
| static { |
| if (Boolean.getBoolean("jdt.flow.test.extra")) { //$NON-NLS-1$ |
| baseAnalysisIndex = 64; |
| System.out.println("JDT/Core testing with -Djdt.flow.test.extra=true"); //$NON-NLS-1$ |
| } |
| } |
| |
| public MethodScope(Scope parent, ReferenceContext context, boolean isStatic) { |
| super(METHOD_SCOPE, parent); |
| this.locals = new LocalVariableBinding[5]; |
| this.referenceContext = context; |
| this.isStatic = isStatic; |
| this.startIndex = 0; |
| } |
| |
| public MethodScope(Scope parent, ReferenceContext context, boolean isStatic, int lastVisibleFieldID) { |
| this(parent, context, isStatic); |
| this.lastVisibleFieldID = lastVisibleFieldID; |
| } |
| |
| @Override |
| 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 < this.localIndex; i++) |
| s += newLine + "\t" + this.locals[i].toString(); //$NON-NLS-1$ |
| s += newLine + "startIndex = " + this.startIndex; //$NON-NLS-1$ |
| s += newLine + "isConstructorCall = " + this.isConstructorCall; //$NON-NLS-1$ |
| s += newLine + "initializedField = " + this.initializedField; //$NON-NLS-1$ |
| s += newLine + "lastVisibleFieldID = " + this.lastVisibleFieldID; //$NON-NLS-1$ |
| s += newLine + "referenceContext = " + this.referenceContext; //$NON-NLS-1$ |
| return s; |
| } |
| |
| /** |
| * Spec : 8.4.3 & 9.4 |
| * TODO: Add the spec section number for private interface methods from jls 9 |
| */ |
| private void checkAndSetModifiersForConstructor(MethodBinding methodBinding) { |
| int modifiers = methodBinding.modifiers; |
| final ReferenceBinding declaringClass = methodBinding.declaringClass; |
| if ((modifiers & ExtraCompilerModifiers.AccAlternateModifierProblem) != 0) |
| problemReporter().duplicateModifierForMethod(declaringClass, (AbstractMethodDeclaration) this.referenceContext); |
| |
| int astNodeBits = ((ConstructorDeclaration) this.referenceContext).bits; |
| if ((astNodeBits & ASTNode.IsDefaultConstructor) != 0 |
| ||((astNodeBits & ASTNode.IsImplicit) != 0 && (astNodeBits & ASTNode.IsCanonicalConstructor) != 0)) { |
| // certain flags are propagated from declaring class onto constructor |
| final int DECLARING_FLAGS = ClassFileConstants.AccEnum|ClassFileConstants.AccPublic|ClassFileConstants.AccProtected; |
| final int VISIBILITY_FLAGS = ClassFileConstants.AccPrivate|ClassFileConstants.AccPublic|ClassFileConstants.AccProtected; |
| int flags; |
| if ((flags = declaringClass.modifiers & DECLARING_FLAGS) != 0) { |
| if ((flags & ClassFileConstants.AccEnum) != 0) { |
| modifiers &= ~VISIBILITY_FLAGS; |
| modifiers |= ClassFileConstants.AccPrivate; // default constructor is implicitly private in enum |
| } else { |
| modifiers &= ~VISIBILITY_FLAGS; |
| modifiers |= flags; // propagate public/protected |
| } |
| } |
| } |
| |
| // after this point, tests on the 16 bits reserved. |
| int realModifiers = modifiers & ExtraCompilerModifiers.AccJustFlag; |
| |
| // check for abnormal modifiers |
| final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccPrivate | ClassFileConstants.AccProtected | ClassFileConstants.AccStrictfp); |
| if (declaringClass.isEnum() && (((ConstructorDeclaration) this.referenceContext).bits & ASTNode.IsDefaultConstructor) == 0) { |
| final int UNEXPECTED_ENUM_CONSTR_MODIFIERS = ~(ClassFileConstants.AccPrivate | ClassFileConstants.AccStrictfp); |
| if ((realModifiers & UNEXPECTED_ENUM_CONSTR_MODIFIERS) != 0) { |
| problemReporter().illegalModifierForEnumConstructor((AbstractMethodDeclaration) this.referenceContext); |
| modifiers &= ~ExtraCompilerModifiers.AccJustFlag | ~UNEXPECTED_ENUM_CONSTR_MODIFIERS; |
| } else if ((((AbstractMethodDeclaration) this.referenceContext).modifiers & ClassFileConstants.AccStrictfp) != 0) { |
| // must check the parse node explicitly |
| problemReporter().illegalModifierForMethod((AbstractMethodDeclaration) this.referenceContext); |
| } |
| modifiers |= ClassFileConstants.AccPrivate; // enum constructor is implicitly private |
| } else if ((realModifiers & UNEXPECTED_MODIFIERS) != 0) { |
| problemReporter().illegalModifierForMethod((AbstractMethodDeclaration) this.referenceContext); |
| modifiers &= ~ExtraCompilerModifiers.AccJustFlag | ~UNEXPECTED_MODIFIERS; |
| } else if ((((AbstractMethodDeclaration) this.referenceContext).modifiers & ClassFileConstants.AccStrictfp) != 0) { |
| // must check the parse node explicitly |
| problemReporter().illegalModifierForMethod((AbstractMethodDeclaration) this.referenceContext); |
| } |
| |
| // 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) { |
| problemReporter().illegalVisibilityModifierCombinationForMethod(declaringClass, (AbstractMethodDeclaration) this.referenceContext); |
| |
| // 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 |
| */ |
| //{ObjectTeams: make arg final to ease the use in an anonymous class: |
| /* orig: |
| private void checkAndSetModifiersForMethod(MethodBinding methodBinding) { |
| :giro */ |
| private void checkAndSetModifiersForMethod(final MethodBinding methodBinding) { |
| // SH} |
| int modifiers = methodBinding.modifiers; |
| final ReferenceBinding declaringClass = methodBinding.declaringClass; |
| if ((modifiers & ExtraCompilerModifiers.AccAlternateModifierProblem) != 0) |
| problemReporter().duplicateModifierForMethod(declaringClass, (AbstractMethodDeclaration) this.referenceContext); |
| |
| // after this point, tests on the 16 bits reserved. |
| int realModifiers = modifiers & ExtraCompilerModifiers.AccJustFlag; |
| long sourceLevel = compilerOptions().sourceLevel; |
| //{ObjectTeams: push the callin flag to a CallinFlags attribute: |
| if ((modifiers & ExtraCompilerModifiers.AccCallin) != 0) |
| MethodModel.getModel(methodBinding.sourceMethod()) |
| .addAttribute(WordValueAttribute.callinFlagsAttribute(0)); |
| //SH} |
| |
| // set the requested modifiers for a method in an interface/annotation |
| //{ObjectTeams: synthetic role interfaces may have any access modifier. |
| /* orig: |
| if (declaringClass.isInterface()) { |
| :giro */ |
| if (declaringClass.isRegularInterface()) { |
| // SH} |
| int expectedModifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract; |
| boolean isDefaultMethod = (modifiers & ExtraCompilerModifiers.AccDefaultMethod) != 0; // no need to check validity, is done by the parser |
| boolean reportIllegalModifierCombination = false; |
| if (sourceLevel >= ClassFileConstants.JDK1_8 && !declaringClass.isAnnotationType()) { |
| expectedModifiers |= ClassFileConstants.AccStrictfp |
| | ExtraCompilerModifiers.AccDefaultMethod | ClassFileConstants.AccStatic; |
| expectedModifiers |= sourceLevel >= ClassFileConstants.JDK9 ? ClassFileConstants.AccPrivate : 0; |
| if (!methodBinding.isAbstract()) { |
| reportIllegalModifierCombination = isDefaultMethod && methodBinding.isStatic(); |
| } else { |
| reportIllegalModifierCombination = isDefaultMethod || methodBinding.isStatic(); |
| if (methodBinding.isStrictfp()) { |
| problemReporter().illegalAbstractModifierCombinationForMethod((AbstractMethodDeclaration) this.referenceContext); |
| } |
| } |
| if (reportIllegalModifierCombination) { |
| problemReporter().illegalModifierCombinationForInterfaceMethod((AbstractMethodDeclaration) this.referenceContext); |
| } |
| if (sourceLevel >= ClassFileConstants.JDK9 && (methodBinding.modifiers & ClassFileConstants.AccPrivate) != 0) { |
| int remaining = realModifiers & ~expectedModifiers; |
| if (remaining == 0) { // check for the combination of allowed modifiers with private |
| remaining = realModifiers & ~(ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic | ClassFileConstants.AccStrictfp); |
| if (isDefaultMethod || remaining != 0) |
| problemReporter().illegalModifierCombinationForPrivateInterfaceMethod((AbstractMethodDeclaration) this.referenceContext); |
| } |
| } |
| // Kludge - The AccDefaultMethod bit is outside the lower 16 bits and got removed earlier. Putting it back. |
| if (isDefaultMethod) { |
| realModifiers |= ExtraCompilerModifiers.AccDefaultMethod; |
| } |
| } |
| if ((realModifiers & ~expectedModifiers) != 0) { |
| if ((declaringClass.modifiers & ClassFileConstants.AccAnnotation) != 0) |
| problemReporter().illegalModifierForAnnotationMember((AbstractMethodDeclaration) this.referenceContext); |
| else |
| problemReporter().illegalModifierForInterfaceMethod((AbstractMethodDeclaration) this.referenceContext, sourceLevel); |
| methodBinding.modifiers &= (expectedModifiers | ~ExtraCompilerModifiers.AccJustFlag); |
| } |
| return; |
| } else if (declaringClass.isAnonymousType() && sourceLevel >= ClassFileConstants.JDK9) { |
| // If the class instance creation expression elides the supertype's type arguments using '<>', |
| // then for all non-private methods declared in the class body, it is as if the method declaration |
| // is annotated with @Override - https://bugs.openjdk.java.net/browse/JDK-8073593 |
| LocalTypeBinding local = (LocalTypeBinding) declaringClass; |
| TypeReference ref = local.scope.referenceContext.allocation.type; |
| if (ref != null && (ref.bits & ASTNode.IsDiamond) != 0) { |
| // |
| if ((realModifiers & (ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic )) == 0) { |
| methodBinding.tagBits |= TagBits.AnnotationOverride; |
| } |
| } |
| } |
| |
| // check for abnormal modifiers |
| final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccPrivate | ClassFileConstants.AccProtected |
| //{ObjectTeams: allow callin methods: |
| | ExtraCompilerModifiers.AccCallin |
| // SH} |
| | ClassFileConstants.AccAbstract | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal | ClassFileConstants.AccSynchronized | ClassFileConstants.AccNative | ClassFileConstants.AccStrictfp); |
| if ((realModifiers & UNEXPECTED_MODIFIERS) != 0) { |
| problemReporter().illegalModifierForMethod((AbstractMethodDeclaration) this.referenceContext); |
| modifiers &= ~ExtraCompilerModifiers.AccJustFlag | ~UNEXPECTED_MODIFIERS; |
| } |
| |
| // 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) { |
| problemReporter().illegalVisibilityModifierCombinationForMethod(declaringClass, (AbstractMethodDeclaration) this.referenceContext); |
| |
| // 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; |
| } |
| } |
| |
| // check for modifiers incompatible with abstract modifier |
| if ((modifiers & ClassFileConstants.AccAbstract) != 0) { |
| int incompatibleWithAbstract = ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal | ClassFileConstants.AccSynchronized | ClassFileConstants.AccNative | ClassFileConstants.AccStrictfp; |
| //{ObjectTeams: allow "abstract static" "private abstract" for callout |
| if (declaringClass.isDirectRole()) |
| incompatibleWithAbstract ^= (ClassFileConstants.AccStatic|ClassFileConstants.AccPrivate); |
| // SH} |
| if ((modifiers & incompatibleWithAbstract) != 0) |
| problemReporter().illegalAbstractModifierCombinationForMethod(declaringClass, (AbstractMethodDeclaration) this.referenceContext); |
| if (!methodBinding.declaringClass.isAbstract()) |
| //{ObjectTeams: set a re-checker since method abstractness might go during callout transformation. |
| /* orig: |
| problemReporter().abstractMethodInAbstractClass((SourceTypeBinding) declaringClass, (AbstractMethodDeclaration) this.referenceContext); |
| :giro */ |
| problemReporter().setRechecker(new IProblemRechecker() { @Override |
| public boolean shouldBeReported(IrritantSet[] foundIrritants) { |
| return methodBinding.isAbstract(); |
| }}) |
| .abstractMethodInAbstractClass((SourceTypeBinding) declaringClass, (AbstractMethodDeclaration) this.referenceContext); |
| //JH & MW & SH} |
| } |
| |
| /* 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 & ClassFileConstants.AccNative) != 0 && (modifiers & ClassFileConstants.AccStrictfp) != 0) |
| problemReporter().nativeMethodsCannotBeStrictfp(declaringClass, (AbstractMethodDeclaration) this.referenceContext); |
| |
| // static members are only authorized in a static member or top level type |
| if (((realModifiers & ClassFileConstants.AccStatic) != 0) && declaringClass.isNestedType() && !declaringClass.isStatic()) |
| //{ObjectTeams: allow static in roles |
| if (!methodBinding.declaringClass.isDirectRole()) |
| // SH} |
| problemReporter().unexpectedStaticModifierForMethod(declaringClass, (AbstractMethodDeclaration) this.referenceContext); |
| |
| //{ObjectTeams: callin modifier: |
| if ( methodBinding.isCallin() |
| && !referenceMethod().isCopied) |
| { |
| MethodDeclaration referenceMethod = (MethodDeclaration) this.referenceContext; |
| // only allowed in direct role: |
| if (!methodBinding.declaringClass.isDirectRole()) { |
| problemReporter().callinInNonRole(methodBinding.declaringClass, referenceMethod); |
| // remove callin flag (note that method arguments will not be enhanced (during STB.resolveTypesFor())): |
| referenceMethod.modifiers &= ~ExtraCompilerModifiers.AccCallin; |
| modifiers &= ~ExtraCompilerModifiers.AccCallin; |
| } else { |
| // must not be combined with visibility modifiers: |
| if (accessorBits != 0) |
| problemReporter().callinWithVisibility(referenceMethod, accessorBits); |
| } |
| } |
| // SH} |
| methodBinding.modifiers = modifiers; |
| } |
| |
| public void checkUnusedParameters(MethodBinding method) { |
| if (method.isAbstract() |
| || (method.isImplementing() && !compilerOptions().reportUnusedParameterWhenImplementingAbstract) |
| || (method.isOverriding() && !method.isImplementing() && !compilerOptions().reportUnusedParameterWhenOverridingConcrete) |
| || method.isMain()) { |
| // do not want to check |
| return; |
| } |
| for (int i = 0, maxLocals = this.localIndex; i < maxLocals; i++) { |
| LocalVariableBinding local = this.locals[i]; |
| if (local == null || ((local.tagBits & TagBits.IsArgument) == 0)) { |
| break; // done with arguments |
| } |
| if (local.useFlag == LocalVariableBinding.UNUSED && |
| //{ObjectTeams: don't report generated argument: |
| !local.declaration.isGenerated && |
| // SH} |
| // do not report fake used variable |
| ((local.declaration.bits & ASTNode.IsLocalDeclarationReachable) != 0)) { // declaration is reachable |
| problemReporter().unusedArgument(local.declaration); |
| } |
| } |
| } |
| |
| /** |
| * 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) { |
| this.offset = initOffset; |
| this.maxOffset = initOffset; |
| |
| // manage arguments |
| int ilocal = 0, maxLocals = this.localIndex; |
| while (ilocal < maxLocals) { |
| LocalVariableBinding local = this.locals[ilocal]; |
| if (local == null || ((local.tagBits & TagBits.IsArgument) == 0)) break; // done with arguments |
| |
| // record user-defined argument for attribute generation |
| codeStream.record(local); |
| |
| // assign variable position |
| local.resolvedPosition = this.offset; |
| |
| if ((TypeBinding.equalsEquals(local.type, TypeBinding.LONG)) || (TypeBinding.equalsEquals(local.type, TypeBinding.DOUBLE))) { |
| this.offset += 2; |
| } else { |
| this.offset++; |
| } |
| // check for too many arguments/local variables |
| if (this.offset > 0xFF) { // no more than 255 words of arguments |
| problemReporter().noMoreAvailableSpaceForArgument(local, local.declaration); |
| } |
| ilocal++; |
| } |
| |
| // sneak in extra argument before other local variables |
| if (this.extraSyntheticArguments != null) { |
| for (int iarg = 0, maxArguments = this.extraSyntheticArguments.length; iarg < maxArguments; iarg++){ |
| SyntheticArgumentBinding argument = this.extraSyntheticArguments[iarg]; |
| argument.resolvedPosition = this.offset; |
| if ((TypeBinding.equalsEquals(argument.type, TypeBinding.LONG)) || (TypeBinding.equalsEquals(argument.type, TypeBinding.DOUBLE))){ |
| this.offset += 2; |
| } else { |
| this.offset++; |
| } |
| if (this.offset > 0xFF) { // no more than 255 words of arguments |
| 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) : i.e. Incorrect thrown exception |
| */ |
| MethodBinding createMethod(AbstractMethodDeclaration method) { |
| // is necessary to ensure error reporting |
| this.referenceContext = method; |
| method.scope = this; |
| long sourceLevel = compilerOptions().sourceLevel; |
| SourceTypeBinding declaringClass = referenceType().binding; |
| int modifiers = method.modifiers | ExtraCompilerModifiers.AccUnresolved; |
| if (method.isConstructor()) { |
| if (method.isDefaultConstructor()) |
| modifiers |= ExtraCompilerModifiers.AccIsDefaultConstructor; |
| method.binding = new MethodBinding(modifiers, null, null, declaringClass); |
| checkAndSetModifiersForConstructor(method.binding); |
| } else { |
| //{ObjectTeams: changed to isRegularInterface: role interfaces don't force public |
| /*orig: |
| if (declaringClass.isInterface()) {// interface or annotation type |
| :giro */ |
| if (declaringClass.isRegularInterface()) { |
| // SH} |
| if (sourceLevel >= ClassFileConstants.JDK9 && ((method.modifiers & ClassFileConstants.AccPrivate) != 0)) { // private method |
| // do nothing |
| } else if (method.isDefaultMethod() || method.isStatic()) { |
| modifiers |= ClassFileConstants.AccPublic; // default method is not abstract |
| } else { |
| modifiers |= ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract; |
| } |
| } |
| method.binding = |
| new MethodBinding(modifiers, method.selector, null, null, null, declaringClass); |
| checkAndSetModifiersForMethod(method.binding); |
| } |
| //{ObjectTeams: share the model if set |
| if (method.model != null) { |
| method.model.linkBinding(method.binding); |
| // need to transfer the clearPrivate flag, since RoleSplitter works before binding creation |
| if (method.model._clearPrivateModifier) |
| method.binding.tagBits |= TagBits.ClearPrivateModifier; |
| } |
| // SH} |
| this.isStatic = method.binding.isStatic(); |
| |
| Argument[] argTypes = method.arguments; |
| int argLength = argTypes == null ? 0 : argTypes.length; |
| if (argLength > 0) { |
| Argument argument = argTypes[argLength - 1]; |
| method.binding.parameterNames = new char[argLength][]; |
| method.binding.parameterNames[--argLength] = argument.name; |
| if (argument.isVarArgs() && sourceLevel >= ClassFileConstants.JDK1_5) |
| method.binding.modifiers |= ClassFileConstants.AccVarargs; |
| if (CharOperation.equals(argument.name, ConstantPool.This)) { |
| problemReporter().illegalThisDeclaration(argument); |
| } |
| while (--argLength >= 0) { |
| argument = argTypes[argLength]; |
| method.binding.parameterNames[argLength] = argument.name; |
| if (argument.isVarArgs() && sourceLevel >= ClassFileConstants.JDK1_5) |
| problemReporter().illegalVararg(argument, method); |
| if (CharOperation.equals(argument.name, ConstantPool.This)) { |
| problemReporter().illegalThisDeclaration(argument); |
| } |
| } |
| } |
| if (method.receiver != null) { |
| if (sourceLevel <= ClassFileConstants.JDK1_7) { |
| problemReporter().illegalSourceLevelForThis(method.receiver); |
| } |
| if (method.receiver.annotations != null) { |
| method.bits |= ASTNode.HasTypeAnnotations; |
| } |
| } |
| |
| TypeParameter[] typeParameters = method.typeParameters(); |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=324850, If they exist at all, process type parameters irrespective of source level. |
| if (typeParameters == null || typeParameters.length == 0) { |
| method.binding.typeVariables = Binding.NO_TYPE_VARIABLES; |
| } else { |
| method.binding.typeVariables = createTypeVariables(typeParameters, method.binding); |
| method.binding.modifiers |= ExtraCompilerModifiers.AccGenericSignature; |
| } |
| checkAndSetRecordCanonicalConsAndMethods(method); |
| return method.binding; |
| } |
| private void checkAndSetRecordCanonicalConsAndMethods(AbstractMethodDeclaration am) { |
| if (am.binding != null && (am.bits & ASTNode.IsImplicit) != 0) { |
| am.binding.tagBits |= TagBits.isImplicit; |
| am.binding.tagBits |= (am.bits & ASTNode.IsCanonicalConstructor) != 0 ? TagBits.IsCanonicalConstructor : 0; |
| } |
| } |
| |
| /** |
| * 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 |
| } |
| } |
| */ |
| @Override |
| 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 (receiverType.isInterface() && invocationSite.isQualifiedSuper()) |
| return new ProblemFieldBinding( |
| field, // closest match |
| field.declaringClass, |
| fieldName, |
| ProblemReasons.NoProperEnclosingInstance); |
| |
| if (field.isStatic()) |
| return field; // static fields are always accessible |
| |
| if (!this.isConstructorCall || TypeBinding.notEquals(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 (this.referenceContext instanceof ConstructorDeclaration); |
| } |
| |
| public boolean isInsideInitializer() { |
| return (this.referenceContext instanceof TypeDeclaration); |
| } |
| |
| @Override |
| public boolean isLambdaScope() { |
| return this.referenceContext instanceof LambdaExpression; |
| } |
| |
| public boolean isInsideInitializerOrConstructor() { |
| return (this.referenceContext instanceof TypeDeclaration) |
| || (this.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. |
| */ |
| @Override |
| public ProblemReporter problemReporter() { |
| ProblemReporter problemReporter = referenceCompilationUnit().problemReporter; |
| problemReporter.referenceContext = this.referenceContext; |
| return problemReporter; |
| } |
| |
| public final int recordInitializationStates(FlowInfo flowInfo) { |
| if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0) return -1; |
| UnconditionalFlowInfo unconditionalFlowInfo = flowInfo.unconditionalInitsWithoutSideEffect(); |
| long[] extraInits = unconditionalFlowInfo.extra == null ? |
| null : unconditionalFlowInfo.extra[0]; |
| long inits = unconditionalFlowInfo.definiteInits; |
| checkNextEntry : for (int i = this.lastIndex; --i >= 0;) { |
| if (this.definiteInits[i] == inits) { |
| long[] otherInits = this.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 (this.definiteInits.length == this.lastIndex) { |
| // need a resize |
| System.arraycopy( |
| this.definiteInits, |
| 0, |
| (this.definiteInits = new long[this.lastIndex + 20]), |
| 0, |
| this.lastIndex); |
| System.arraycopy( |
| this.extraDefiniteInits, |
| 0, |
| (this.extraDefiniteInits = new long[this.lastIndex + 20][]), |
| 0, |
| this.lastIndex); |
| } |
| this.definiteInits[this.lastIndex] = inits; |
| if (extraInits != null) { |
| this.extraDefiniteInits[this.lastIndex] = new long[extraInits.length]; |
| System.arraycopy( |
| extraInits, |
| 0, |
| this.extraDefiniteInits[this.lastIndex], |
| 0, |
| extraInits.length); |
| } |
| return this.lastIndex++; |
| } |
| |
| /** |
| * Answer the reference method of this scope, or null if initialization scope or lambda scope. |
| */ |
| public AbstractMethodDeclaration referenceMethod() { |
| if (this.referenceContext instanceof AbstractMethodDeclaration) return (AbstractMethodDeclaration) this.referenceContext; |
| return null; |
| } |
| |
| /** |
| * Answers the binding of the reference method or reference lambda expression. |
| */ |
| public MethodBinding referenceMethodBinding() { |
| if (this.referenceContext instanceof LambdaExpression) |
| return ((LambdaExpression)this.referenceContext).binding; |
| if (this.referenceContext instanceof AbstractMethodDeclaration) |
| return ((AbstractMethodDeclaration)this.referenceContext).binding; |
| return null; |
| } |
| |
| /** |
| * Answer the reference type of this scope. |
| * It is the nearest enclosing type of this scope. |
| */ |
| @Override |
| public TypeDeclaration referenceType() { |
| ClassScope scope = enclosingClassScope(); |
| return scope == null ? null : scope.referenceContext; |
| } |
| |
| @Override |
| void resolveTypeParameter(TypeParameter typeParameter) { |
| typeParameter.resolve(this); |
| } |
| @Override |
| public boolean hasDefaultNullnessFor(int location, int sourceStart) { |
| int nonNullByDefaultValue = localNonNullByDefaultValue(sourceStart); |
| if(nonNullByDefaultValue != 0) { |
| return (nonNullByDefaultValue & location) != 0; |
| } |
| AbstractMethodDeclaration referenceMethod = referenceMethod(); |
| if (referenceMethod != null) { |
| MethodBinding binding = referenceMethod.binding; |
| if (binding != null && binding.defaultNullness != 0) { |
| return (binding.defaultNullness & location) != 0; |
| } |
| } |
| return this.parent.hasDefaultNullnessFor(location, sourceStart); |
| } |
| @Override |
| public Binding checkRedundantDefaultNullness(int nullBits, int sourceStart) { |
| Binding target = localCheckRedundantDefaultNullness(nullBits, sourceStart); |
| if (target != null) { |
| return target; |
| } |
| AbstractMethodDeclaration referenceMethod = referenceMethod(); |
| if (referenceMethod != null) { |
| MethodBinding binding = referenceMethod.binding; |
| if (binding != null && binding.defaultNullness != 0) { |
| return (binding.defaultNullness == nullBits) ? binding : null; |
| } |
| } |
| return this.parent.checkRedundantDefaultNullness(nullBits, sourceStart); |
| } |
| //{ObjectTeams: new queries: |
| /** |
| * Does this scope represent a callin wrapper |
| * (either the mapping declaration or the generated wrapper method)? |
| */ |
| public boolean isCallinWrapper() { |
| if (this.referenceContext instanceof CallinMappingDeclaration) |
| return true; |
| if (!(this.referenceContext instanceof AbstractMethodDeclaration)) |
| return false; |
| AbstractMethodDeclaration method = (AbstractMethodDeclaration)this.referenceContext; |
| return method.isMappingWrapper._callin(); |
| } |
| |
| public CallinCalloutScope getDeclaringMappingScope() { |
| AbstractMethodDeclaration method = referenceMethod(); |
| TypeDeclaration type = referenceType(); |
| if (method == null || type == null) |
| return null; |
| if (type.callinCallouts != null) { |
| for (AbstractMethodMappingDeclaration mapping : type.callinCallouts) |
| if (mapping.isCallout() && mapping.roleMethodSpec.resolvedMethod == method.binding) |
| return mapping.scope; |
| } |
| if (type.memberTypes != null) { // search for callin bindings in member roles |
| for (TypeDeclaration member : type.memberTypes) |
| if (member.callinCallouts != null) |
| for (AbstractMethodMappingDeclaration mapping : member.callinCallouts) |
| if (mapping.isCallin()) { |
| CallinMappingDeclaration callinMapping = (CallinMappingDeclaration) mapping; |
| if (callinMapping.wrappers != null) |
| for (AbstractMethodDeclaration wrapper : callinMapping.wrappers) |
| if (wrapper == method) |
| return mapping.scope; |
| } |
| } |
| return null; |
| } |
| // SH} |
| public boolean shouldCheckAPILeaks(ReferenceBinding declaringClass, boolean memberIsPublic) { |
| if (environment().useModuleSystem) |
| return memberIsPublic && declaringClass.isPublic() && declaringClass.fPackage.isExported(); |
| return false; |
| } |
| public void detectAPILeaks(ASTNode typeNode, TypeBinding type) { |
| if (environment().useModuleSystem) { |
| // NB: using an ASTVisitor yields more precise locations than a TypeBindingVisitor would |
| ASTVisitor visitor = new ASTVisitor() { |
| @Override |
| public boolean visit(SingleTypeReference typeReference, BlockScope scope) { |
| if (typeReference.resolvedType instanceof ReferenceBinding) |
| checkType((ReferenceBinding) typeReference.resolvedType, typeReference.sourceStart, typeReference.sourceEnd); |
| return true; |
| } |
| @Override |
| public boolean visit(QualifiedTypeReference typeReference, BlockScope scope) { |
| if (typeReference.resolvedType instanceof ReferenceBinding) |
| checkType((ReferenceBinding) typeReference.resolvedType, typeReference.sourceStart, typeReference.sourceEnd); |
| return true; |
| } |
| @Override |
| public boolean visit(ArrayTypeReference typeReference, BlockScope scope) { |
| TypeBinding leafComponentType = typeReference.resolvedType.leafComponentType(); |
| if (leafComponentType instanceof ReferenceBinding) |
| checkType((ReferenceBinding) leafComponentType, typeReference.sourceStart, typeReference.originalSourceEnd); |
| return true; |
| } |
| private void checkType(ReferenceBinding referenceBinding, int sourceStart, int sourceEnd) { |
| if (!referenceBinding.isValidBinding()) |
| return; |
| ModuleBinding otherModule = referenceBinding.module(); |
| if (otherModule == otherModule.environment.javaBaseModule()) |
| return; // always accessible |
| if (!isFullyPublic(referenceBinding)) { |
| problemReporter().nonPublicTypeInAPI(referenceBinding, sourceStart, sourceEnd); |
| } else if (!referenceBinding.fPackage.isExported()) { |
| problemReporter().notExportedTypeInAPI(referenceBinding, sourceStart, sourceEnd); |
| } else if (isUnrelatedModule(referenceBinding.fPackage)) { |
| problemReporter().missingRequiresTransitiveForTypeInAPI(referenceBinding, sourceStart, sourceEnd); |
| } |
| } |
| private boolean isFullyPublic(ReferenceBinding referenceBinding) { |
| if (!referenceBinding.isPublic()) |
| return false; |
| if (referenceBinding instanceof NestedTypeBinding) |
| return isFullyPublic(((NestedTypeBinding) referenceBinding).enclosingType); |
| return true; |
| } |
| private boolean isUnrelatedModule(PackageBinding fPackage) { |
| ModuleBinding otherModule = fPackage.enclosingModule; |
| ModuleBinding thisModule = module(); |
| if (thisModule != otherModule) { |
| return !thisModule.isTransitivelyRequired(otherModule); |
| } |
| return false; |
| } |
| }; |
| typeNode.traverse(visitor, this); |
| } |
| } |
| } |