Copyright (c) 2000, 2019 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 Contributors: IBM Corporation - initial API and implementation Stephan Herrmann - Contributions for bug 349326 - [1.7] new warning for missing try-with-resources bug 359334 - Analysis for resource leak warnings does not consider exceptions as method exit points bug 358903 - Filter practically unimportant resource leak warnings bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK bug 370639 - [compiler][resource] restore the default for resource leak warnings bug 388996 - [compiler][resource] Incorrect 'potential resource leak' bug 379784 - [compiler] "Method can be static" is not getting reported bug 394768 - [compiler][resource] Incorrect resource leak warning when creating stream in conditional bug 404649 - [1.8][compiler] detect illegal reference to indirect or redundant super Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault Bug 371614 - [compiler][resource] Wrong "resource leak" problem on return/throw inside while loop Bug 421035 - [resource] False alarm of resource leak warning when casting a closeable in its assignment Bug 444964 - [1.7+][resource] False resource leak warning (try-with-resources for ByteArrayOutputStream - return inside for loop) Bug 396575 - [compiler][resources] Incorrect Errors/Warnings check for potential resource leak when surrounding with try-catch Jesper S Moller - Contributions for bug 378674 - "The method can be declared as static" is wrong Keigo Imai - Contribution for bug 388903 - Cannot extend inner class as an anonymous class when it extends the outer class
/******************************************************************************* * Copyright (c) 2000, 2019 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 * * Contributors: * IBM Corporation - initial API and implementation * Stephan Herrmann - Contributions for * bug 349326 - [1.7] new warning for missing try-with-resources * bug 359334 - Analysis for resource leak warnings does not consider exceptions as method exit points * bug 358903 - Filter practically unimportant resource leak warnings * bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK * bug 370639 - [compiler][resource] restore the default for resource leak warnings * bug 388996 - [compiler][resource] Incorrect 'potential resource leak' * bug 379784 - [compiler] "Method can be static" is not getting reported * bug 394768 - [compiler][resource] Incorrect resource leak warning when creating stream in conditional * bug 404649 - [1.8][compiler] detect illegal reference to indirect or redundant super * Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault * Bug 371614 - [compiler][resource] Wrong "resource leak" problem on return/throw inside while loop * Bug 421035 - [resource] False alarm of resource leak warning when casting a closeable in its assignment * Bug 444964 - [1.7+][resource] False resource leak warning (try-with-resources for ByteArrayOutputStream - return inside for loop) * Bug 396575 - [compiler][resources] Incorrect Errors/Warnings check for potential resource leak when surrounding with try-catch * Jesper S Moller <jesper@selskabet.org> - Contributions for * bug 378674 - "The method can be declared as static" is wrong * Keigo Imai - Contribution for bug 388903 - Cannot extend inner class as an anonymous class when it extends the outer class *******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.jdt.core.compiler.CharOperation; 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.flow.FlowContext; import org.eclipse.jdt.internal.compiler.flow.FlowInfo; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; @SuppressWarnings({"rawtypes", "unchecked"}) public class BlockScope extends Scope { // Local variable management public LocalVariableBinding[] locals; public int localIndex; // position for next variable public int startIndex; // start position in this scope - for ordering scopes vs. variables public int offset; // for variable allocation throughout scopes public int maxOffset; // for variable allocation throughout scopes // finally scopes must be shifted behind respective try&catch scope(s) so as to avoid // collisions of secret variables (return address, save value). public BlockScope[] shiftScopes; public Scope[] subscopes = new Scope[1]; // need access from code assist public int subscopeCount = 0; // need access from code assist // record the current case statement being processed (for entire switch case block). public CaseStatement enclosingCase; // from 1.4 on, local types should not be accessed across switch case blocks (52221) public final static VariableBinding[] EmulationPathToImplicitThis = {}; public final static VariableBinding[] NoEnclosingInstanceInConstructorCall = {}; public final static VariableBinding[] NoEnclosingInstanceInStaticContext = {}; // annotation support public boolean insideTypeAnnotation = false; public Statement blockStatement; public BlockScope(BlockScope parent) { this(parent, true); } public BlockScope(BlockScope parent, boolean addToParentScope) { this(Scope.BLOCK_SCOPE, parent); this.locals = new LocalVariableBinding[5]; if (addToParentScope) parent.addSubscope(this); this.startIndex = parent.localIndex; } public BlockScope(BlockScope parent, int variableCount) { this(Scope.BLOCK_SCOPE, parent); this.locals = new LocalVariableBinding[variableCount]; parent.addSubscope(this); this.startIndex = parent.localIndex; } protected BlockScope(int kind, Scope parent) { super(kind, parent); } /* Create the class scope & binding for the anonymous type. */ public final void addAnonymousType(TypeDeclaration anonymousType, ReferenceBinding superBinding) { ClassScope anonymousClassScope = new ClassScope(this, anonymousType); anonymousClassScope.buildAnonymousTypeBinding( enclosingSourceType(), superBinding); /* Tag any enclosing lambdas as instance capturing. Strictly speaking they need not be, unless the local/anonymous type references enclosing instance state. but the types themselves track enclosing types regardless of whether the state is accessed or not. This creates a mismatch in expectations in code generation time, if we choose to make the lambda method static. To keep things simple and avoid a messy rollback, we force the lambda to be an instance method under this situation. However if per source, the lambda occurs in a static context, we would generate a static synthetic method. */ MethodScope methodScope = methodScope(); while (methodScope != null && methodScope.referenceContext instanceof LambdaExpression) { LambdaExpression lambda = (LambdaExpression) methodScope.referenceContext; if (!lambda.scope.isStatic && !lambda.scope.isConstructorCall) { lambda.shouldCaptureInstance = true; } methodScope = methodScope.enclosingMethodScope(); } } /* Create the class scope & binding for the local type. */ public final void addLocalType(TypeDeclaration localType) { ClassScope localTypeScope = new ClassScope(this, localType); addSubscope(localTypeScope); localTypeScope.buildLocalTypeBinding(enclosingSourceType()); // See comment in addAnonymousType. MethodScope methodScope = methodScope(); while (methodScope != null && methodScope.referenceContext instanceof LambdaExpression) { LambdaExpression lambda = (LambdaExpression) methodScope.referenceContext; if (!lambda.scope.isStatic && !lambda.scope.isConstructorCall) { lambda.shouldCaptureInstance = true; } methodScope = methodScope.enclosingMethodScope(); } } /* Insert a local variable into a given scope, updating its position * and checking there are not too many locals or arguments allocated. */ public final void addLocalVariable(LocalVariableBinding binding) { checkAndSetModifiersForVariable(binding); // insert local in scope if (this.localIndex == this.locals.length) System.arraycopy( this.locals, 0, (this.locals = new LocalVariableBinding[this.localIndex * 2]), 0, this.localIndex); this.locals[this.localIndex++] = binding; // update local variable binding binding.declaringScope = this; binding.id = outerMostMethodScope().analysisIndex++; // share the outermost method scope analysisIndex } public void addSubscope(Scope childScope) { if (this.subscopeCount == this.subscopes.length) System.arraycopy( this.subscopes, 0, (this.subscopes = new Scope[this.subscopeCount * 2]), 0, this.subscopeCount); this.subscopes[this.subscopeCount++] = childScope; }
Answer true if the receiver is suitable for assigning final blank fields. in other words, it is inside an initializer, a constructor or a clinit
/** * Answer true if the receiver is suitable for assigning final blank fields. * in other words, it is inside an initializer, a constructor or a clinit */
public final boolean allowBlankFinalFieldAssignment(FieldBinding binding) { if (TypeBinding.notEquals(enclosingReceiverType(), binding.declaringClass)) return false; MethodScope methodScope = methodScope(); if (methodScope.isStatic != binding.isStatic()) return false; if (methodScope.isLambdaScope()) return false; return methodScope.isInsideInitializer() // inside initializer || ((AbstractMethodDeclaration) methodScope.referenceContext).isInitializationMethod(); // inside constructor or clinit } String basicToString(int tab) { String newLine = "\n"; //$NON-NLS-1$ for (int i = tab; --i >= 0;) newLine += "\t"; //$NON-NLS-1$ String s = newLine + "--- Block 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$ return s; } private void checkAndSetModifiersForVariable(LocalVariableBinding varBinding) { int modifiers = varBinding.modifiers; if ((modifiers & ExtraCompilerModifiers.AccAlternateModifierProblem) != 0 && varBinding.declaration != null){ problemReporter().duplicateModifierForVariable(varBinding.declaration, this instanceof MethodScope); } int realModifiers = modifiers & ExtraCompilerModifiers.AccJustFlag; int unexpectedModifiers = ~ClassFileConstants.AccFinal; if ((realModifiers & unexpectedModifiers) != 0 && varBinding.declaration != null){ problemReporter().illegalModifierForVariable(varBinding.declaration, this instanceof MethodScope); } varBinding.modifiers = modifiers; } /* Compute variable positions in scopes given an initial position offset * ignoring unused local variables. * * No argument is expected here (ilocal is the first non-argument local of the outermost scope) * Arguments are managed by the MethodScope method */ void computeLocalVariablePositions(int ilocal, int initOffset, CodeStream codeStream) { this.offset = initOffset; this.maxOffset = initOffset; // local variable init int maxLocals = this.localIndex; boolean hasMoreVariables = ilocal < maxLocals; // scope init int iscope = 0, maxScopes = this.subscopeCount; boolean hasMoreScopes = maxScopes > 0; // iterate scopes and variables in parallel while (hasMoreVariables || hasMoreScopes) { if (hasMoreScopes && (!hasMoreVariables || (this.subscopes[iscope].startIndex() <= ilocal))) { // consider subscope first if (this.subscopes[iscope] instanceof BlockScope) { BlockScope subscope = (BlockScope) this.subscopes[iscope]; int subOffset = subscope.shiftScopes == null ? this.offset : subscope.maxShiftedOffset(); subscope.computeLocalVariablePositions(0, subOffset, codeStream); if (subscope.maxOffset > this.maxOffset) this.maxOffset = subscope.maxOffset; } hasMoreScopes = ++iscope < maxScopes; } else { // consider variable first LocalVariableBinding local = this.locals[ilocal]; // if no local at all, will be locals[ilocal]==null // check if variable is actually used, and may force it to be preserved boolean generateCurrentLocalVar = (local.useFlag > LocalVariableBinding.UNUSED && local.constant() == Constant.NotAConstant); // do not report fake used variable if (local.useFlag == LocalVariableBinding.UNUSED && (local.declaration != null) // unused (and non secret) local && ((local.declaration.bits & ASTNode.IsLocalDeclarationReachable) != 0)) { // declaration is reachable if (local.isCatchParameter()) { problemReporter().unusedExceptionParameter(local.declaration); // report unused catch arguments } else { problemReporter().unusedLocalVariable(local.declaration); } } // could be optimized out, but does need to preserve unread variables ? if (!generateCurrentLocalVar) { if (local.declaration != null && compilerOptions().preserveAllLocalVariables) { generateCurrentLocalVar = true; // force it to be preserved in the generated code if (local.useFlag == LocalVariableBinding.UNUSED) local.useFlag = LocalVariableBinding.USED; } } // allocate variable if (generateCurrentLocalVar) { if (local.declaration != null) { codeStream.record(local); // record user-defined local variables for attribute generation } // 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++; } if (this.offset > 0xFFFF) { // no more than 65535 words of locals problemReporter().noMoreAvailableSpaceForLocal( local, local.declaration == null ? (ASTNode)methodScope().referenceContext : local.declaration); } } else { local.resolvedPosition = -1; // not generated } hasMoreVariables = ++ilocal < maxLocals; } } if (this.offset > this.maxOffset) this.maxOffset = this.offset; } /* * Record the suitable binding denoting a synthetic field or constructor argument, * mapping to the actual outer local variable in the scope context. * Note that this may not need any effect, in case the outer local variable does not * need to be emulated and can directly be used as is (using its back pointer to its * declaring scope). */ public void emulateOuterAccess(LocalVariableBinding outerLocalVariable) { BlockScope outerVariableScope = outerLocalVariable.declaringScope; if (outerVariableScope == null) return; // no need to further emulate as already inserted (val$this$0) int depth = 0; Scope scope = this; while (outerVariableScope != scope) { switch(scope.kind) { case CLASS_SCOPE: depth++; break; case METHOD_SCOPE: if (scope.isLambdaScope()) { LambdaExpression lambdaExpression = (LambdaExpression) scope.referenceContext(); lambdaExpression.addSyntheticArgument(outerLocalVariable); } break; } scope = scope.parent; } if (depth == 0) return; MethodScope currentMethodScope = methodScope(); if (outerVariableScope.methodScope() != currentMethodScope) { NestedTypeBinding currentType = (NestedTypeBinding) enclosingSourceType(); //do nothing for member types, pre emulation was performed already if (!currentType.isLocalType()) { return; } // must also add a synthetic field if we're not inside a constructor if (!currentMethodScope.isInsideInitializerOrConstructor()) { currentType.addSyntheticArgumentAndField(outerLocalVariable); } else { currentType.addSyntheticArgument(outerLocalVariable); } } } /* Note that it must never produce a direct access to the targetEnclosingType, * but instead a field sequence (this$2.this$1.this$0) so as to handle such a test case: * * class XX { * void foo() { * class A { * class B { * class C { * boolean foo() { * return (Object) A.this == (Object) B.this; * } * } * } * } * new A().new B().new C(); * } * } * where we only want to deal with ONE enclosing instance for C (could not figure out an A for C) */ public final ReferenceBinding findLocalType(char[] name) { long compliance = compilerOptions().complianceLevel; for (int i = this.subscopeCount-1; i >= 0; i--) { if (this.subscopes[i] instanceof ClassScope) { LocalTypeBinding sourceType = (LocalTypeBinding)((ClassScope) this.subscopes[i]).referenceContext.binding; // from 1.4 on, local types should not be accessed across switch case blocks (52221) if (compliance >= ClassFileConstants.JDK1_4 && sourceType.enclosingCase != null) { if (!isInsideCase(sourceType.enclosingCase)) { continue; } } if (CharOperation.equals(sourceType.sourceName(), name)) return sourceType; } } return null; }
Returns all declarations of most specific locals containing a given position in their source range. This code does not recurse in nested types. Returned array may have null values at trailing indexes.
/** * Returns all declarations of most specific locals containing a given position in their source range. * This code does not recurse in nested types. * Returned array may have null values at trailing indexes. */
public LocalDeclaration[] findLocalVariableDeclarations(int position) { // local variable init int ilocal = 0, maxLocals = this.localIndex; boolean hasMoreVariables = maxLocals > 0; LocalDeclaration[] localDeclarations = null; int declPtr = 0; // scope init int iscope = 0, maxScopes = this.subscopeCount; boolean hasMoreScopes = maxScopes > 0; // iterate scopes and variables in parallel while (hasMoreVariables || hasMoreScopes) { if (hasMoreScopes && (!hasMoreVariables || (this.subscopes[iscope].startIndex() <= ilocal))) { // consider subscope first Scope subscope = this.subscopes[iscope]; if (subscope.kind == Scope.BLOCK_SCOPE) { // do not dive in nested types localDeclarations = ((BlockScope)subscope).findLocalVariableDeclarations(position); if (localDeclarations != null) { return localDeclarations; } } hasMoreScopes = ++iscope < maxScopes; } else { // consider variable first LocalVariableBinding local = this.locals[ilocal]; // if no local at all, will be locals[ilocal]==null if (local != null) { LocalDeclaration localDecl = local.declaration; if (localDecl != null) { if (localDecl.declarationSourceStart <= position) { if (position <= localDecl.declarationSourceEnd) { if (localDeclarations == null) { localDeclarations = new LocalDeclaration[maxLocals]; } localDeclarations[declPtr++] = localDecl; } } else { return localDeclarations; } } } hasMoreVariables = ++ilocal < maxLocals; if (!hasMoreVariables && localDeclarations != null) { return localDeclarations; } } } return null; } @Override public LocalVariableBinding findVariable(char[] variableName) { int varLength = variableName.length; for (int i = this.localIndex-1; i >= 0; i--) { // lookup backward to reach latest additions first LocalVariableBinding local; char[] localName; if ((localName = (local = this.locals[i]).name).length == varLength && CharOperation.equals(localName, variableName)) return local; } return null; } /* API * flag is a mask of the following values VARIABLE (= FIELD or LOCAL), TYPE. * Only bindings corresponding to the mask will be answered. * * if the VARIABLE mask is set then * If the first name provided is a field (or local) then the field (or local) is answered * Otherwise, package names and type names are consumed until a field is found. * In this case, the field is answered. * * if the TYPE mask is set, * package names and type names are consumed until the end of the input. * Only if all of the input is consumed is the type answered * * All other conditions are errors, and a problem binding is returned. * * NOTE: If a problem binding is returned, senders should extract the compound name * from the binding & not assume the problem applies to the entire compoundName. * * The VARIABLE mask has precedence over the TYPE mask. * * InvocationSite implements * isSuperAccess(); this is used to determine if the discovered field is visible. * setFieldIndex(int); this is used to record the number of names that were consumed. * * For example, getBinding({"foo","y","q", VARIABLE, site) will answer * the binding for the field or local named "foo" (or an error binding if none exists). * In addition, setFieldIndex(1) will be sent to the invocation site. * If a type named "foo" exists, it will not be detected (and an error binding will be answered) * * IMPORTANT NOTE: This method is written under the assumption that compoundName is longer than length 1. */ public Binding getBinding(char[][] compoundName, int mask, InvocationSite invocationSite, boolean needResolve) { Binding binding = getBinding(compoundName[0], mask | Binding.TYPE | Binding.PACKAGE, invocationSite, needResolve); invocationSite.setFieldIndex(1); if (binding instanceof VariableBinding) return binding; CompilationUnitScope unitScope = compilationUnitScope(); // in the problem case, we want to ensure we record the qualified dependency in case a type is added // and we do not know that its package was also added (can happen with CompilationParticipants) unitScope.recordQualifiedReference(compoundName); if (!binding.isValidBinding()) return binding; int length = compoundName.length; int currentIndex = 1; foundType : if (binding instanceof PackageBinding) { PackageBinding packageBinding = (PackageBinding) binding; while (currentIndex < length) { unitScope.recordReference(packageBinding.compoundName, compoundName[currentIndex]); binding = packageBinding.getTypeOrPackage(compoundName[currentIndex++], module(), currentIndex<length); invocationSite.setFieldIndex(currentIndex); if (binding == null) { if (currentIndex == length) { // must be a type if its the last name, otherwise we have no idea if its a package or type return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), null, ProblemReasons.NotFound); } return new ProblemBinding( CharOperation.subarray(compoundName, 0, currentIndex), ProblemReasons.NotFound); } if (binding instanceof ReferenceBinding) { if (!binding.isValidBinding()) return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), (ReferenceBinding)((ReferenceBinding)binding).closestMatch(), binding.problemId()); if (!((ReferenceBinding) binding).canBeSeenBy(this)) return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), (ReferenceBinding) binding, ProblemReasons.NotVisible); break foundType; } packageBinding = (PackageBinding) binding; } // It is illegal to request a PACKAGE from this method. return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), null, ProblemReasons.NotFound); } // know binding is now a ReferenceBinding ReferenceBinding referenceBinding = (ReferenceBinding) binding; binding = environment().convertToRawType(referenceBinding, false /*do not force conversion of enclosing types*/); if (invocationSite instanceof ASTNode) { ASTNode invocationNode = (ASTNode) invocationSite; if (invocationNode.isTypeUseDeprecated(referenceBinding, this)) { problemReporter().deprecatedType(referenceBinding, invocationNode); } } Binding problemFieldBinding = null; while (currentIndex < length) { referenceBinding = (ReferenceBinding) binding; char[] nextName = compoundName[currentIndex++]; invocationSite.setFieldIndex(currentIndex); invocationSite.setActualReceiverType(referenceBinding); if ((mask & Binding.FIELD) != 0 && (binding = findField(referenceBinding, nextName, invocationSite, true /*resolve*/)) != null) { if (binding.isValidBinding()) { break; // binding is now a field } problemFieldBinding = new ProblemFieldBinding( ((ProblemFieldBinding)binding).closestMatch, ((ProblemFieldBinding)binding).declaringClass, CharOperation.concatWith(CharOperation.subarray(compoundName, 0, currentIndex), '.'), binding.problemId()); // https://bugs.eclipse.org/bugs/show_bug.cgi?id=317858 : If field is inaccessible, // don't give up yet, continue to look for a visible member type if (binding.problemId() != ProblemReasons.NotVisible) { return problemFieldBinding; } } if ((binding = findMemberType(nextName, referenceBinding)) == null) { if (problemFieldBinding != null) { return problemFieldBinding; } if ((mask & Binding.FIELD) != 0) { return new ProblemFieldBinding( null, referenceBinding, nextName, ProblemReasons.NotFound); } else if ((mask & Binding.VARIABLE) != 0) { return new ProblemBinding( CharOperation.subarray(compoundName, 0, currentIndex), referenceBinding, ProblemReasons.NotFound); } return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), referenceBinding, ProblemReasons.NotFound); } // binding is a ReferenceBinding if (!binding.isValidBinding()) { if (problemFieldBinding != null) { return problemFieldBinding; } return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), (ReferenceBinding)((ReferenceBinding)binding).closestMatch(), binding.problemId()); } if (invocationSite instanceof ASTNode) { referenceBinding = (ReferenceBinding) binding; ASTNode invocationNode = (ASTNode) invocationSite; if (invocationNode.isTypeUseDeprecated(referenceBinding, this)) { problemReporter().deprecatedType(referenceBinding, invocationNode); } } } if ((mask & Binding.FIELD) != 0 && (binding instanceof FieldBinding)) { // was looking for a field and found a field FieldBinding field = (FieldBinding) binding; if (!field.isStatic()) return new ProblemFieldBinding( field, field.declaringClass, CharOperation.concatWith(CharOperation.subarray(compoundName, 0, currentIndex), '.'), ProblemReasons.NonStaticReferenceInStaticContext); // Since a qualified reference must be for a static member, it won't affect static-ness of the enclosing method, // so we don't have to call resetEnclosingMethodStaticFlag() in this case return binding; } if ((mask & Binding.TYPE) != 0 && (binding instanceof ReferenceBinding)) { // was looking for a type and found a type return binding; } // handle the case when a field or type was asked for but we resolved the compoundName to a type or field return new ProblemBinding( CharOperation.subarray(compoundName, 0, currentIndex), ProblemReasons.NotFound); } // Added for code assist... NOT Public API public final Binding getBinding(char[][] compoundName, InvocationSite invocationSite) { int currentIndex = 0; int length = compoundName.length; Binding binding = getBinding( compoundName[currentIndex++], Binding.VARIABLE | Binding.TYPE | Binding.PACKAGE, invocationSite, true /*resolve*/); if (!binding.isValidBinding()) return binding; foundType : if (binding instanceof PackageBinding) { while (currentIndex < length) { PackageBinding packageBinding = (PackageBinding) binding; binding = packageBinding.getTypeOrPackage(compoundName[currentIndex++], module(), currentIndex<length); if (binding == null) { if (currentIndex == length) { // must be a type if its the last name, otherwise we have no idea if its a package or type return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), null, ProblemReasons.NotFound); } return new ProblemBinding( CharOperation.subarray(compoundName, 0, currentIndex), ProblemReasons.NotFound); } if (binding instanceof ReferenceBinding) { if (!binding.isValidBinding()) return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), (ReferenceBinding)((ReferenceBinding)binding).closestMatch(), binding.problemId()); if (!((ReferenceBinding) binding).canBeSeenBy(this)) return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), (ReferenceBinding) binding, ProblemReasons.NotVisible); break foundType; } } return binding; } foundField : if (binding instanceof ReferenceBinding) { while (currentIndex < length) { ReferenceBinding typeBinding = (ReferenceBinding) binding; char[] nextName = compoundName[currentIndex++]; TypeBinding receiverType = typeBinding.capture(this, invocationSite.sourceStart(), invocationSite.sourceEnd()); if ((binding = findField(receiverType, nextName, invocationSite, true /*resolve*/)) != null) { if (!binding.isValidBinding()) { return new ProblemFieldBinding( (FieldBinding) binding, ((FieldBinding) binding).declaringClass, CharOperation.concatWith(CharOperation.subarray(compoundName, 0, currentIndex), '.'), binding.problemId()); } if (!((FieldBinding) binding).isStatic()) return new ProblemFieldBinding( (FieldBinding) binding, ((FieldBinding) binding).declaringClass, CharOperation.concatWith(CharOperation.subarray(compoundName, 0, currentIndex), '.'), ProblemReasons.NonStaticReferenceInStaticContext); break foundField; // binding is now a field } if ((binding = findMemberType(nextName, typeBinding)) == null) { return new ProblemBinding( CharOperation.subarray(compoundName, 0, currentIndex), typeBinding, ProblemReasons.NotFound); } if (!binding.isValidBinding()) { return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), (ReferenceBinding)((ReferenceBinding)binding).closestMatch(), binding.problemId()); } } return binding; } VariableBinding variableBinding = (VariableBinding) binding; while (currentIndex < length) { TypeBinding typeBinding = variableBinding.type; if (typeBinding == null) { return new ProblemFieldBinding( null, null, CharOperation.concatWith(CharOperation.subarray(compoundName, 0, currentIndex), '.'), ProblemReasons.NotFound); } TypeBinding receiverType = typeBinding.capture(this, invocationSite.sourceStart(), invocationSite.sourceEnd()); variableBinding = findField(receiverType, compoundName[currentIndex++], invocationSite, true /*resolve*/); if (variableBinding == null) { return new ProblemFieldBinding( null, receiverType instanceof ReferenceBinding ? (ReferenceBinding) receiverType : null, CharOperation.concatWith(CharOperation.subarray(compoundName, 0, currentIndex), '.'), ProblemReasons.NotFound); } if (!variableBinding.isValidBinding()) return variableBinding; } return variableBinding; } /* * This retrieves the argument that maps to an enclosing instance of the suitable type, * if not found then answers nil -- do not create one * * #implicitThis : the implicit this will be ok * #((arg) this$n) : available as a constructor arg * #((arg) this$n ... this$p) : available as as a constructor arg + a sequence of fields * #((fieldDescr) this$n ... this$p) : available as a sequence of fields * nil : not found * * Note that this algorithm should answer the shortest possible sequence when * shortcuts are available: * this$0 . this$0 . this$0 * instead of * this$2 . this$1 . this$0 . this$1 . this$0 * thus the code generation will be more compact and runtime faster */ public VariableBinding[] getEmulationPath(LocalVariableBinding outerLocalVariable) { MethodScope currentMethodScope = methodScope(); SourceTypeBinding sourceType = currentMethodScope.enclosingSourceType(); // identity check BlockScope variableScope = outerLocalVariable.declaringScope; if (variableScope == null /*val$this$0*/ || currentMethodScope == variableScope.methodScope()) { return new VariableBinding[] { outerLocalVariable }; // implicit this is good enough } if (currentMethodScope.isLambdaScope()) { LambdaExpression lambda = (LambdaExpression) currentMethodScope.referenceContext; SyntheticArgumentBinding syntheticArgument; if ((syntheticArgument = lambda.getSyntheticArgument(outerLocalVariable)) != null) { return new VariableBinding[] { syntheticArgument }; } } // use synthetic constructor arguments if possible if (currentMethodScope.isInsideInitializerOrConstructor() && (sourceType.isNestedType())) { SyntheticArgumentBinding syntheticArg; if ((syntheticArg = ((NestedTypeBinding) sourceType).getSyntheticArgument(outerLocalVariable)) != null) { return new VariableBinding[] { syntheticArg }; } } // use a synthetic field then if (!currentMethodScope.isStatic) { FieldBinding syntheticField; if ((syntheticField = sourceType.getSyntheticField(outerLocalVariable)) != null) { return new VariableBinding[] { syntheticField }; } } return null; } /* * This retrieves the argument that maps to an enclosing instance of the suitable type, * if not found then answers nil -- do not create one * * #implicitThis : the implicit this will be ok * #((arg) this$n) : available as a constructor arg * #((arg) this$n access$m... access$p) : available as as a constructor arg + a sequence of synthetic accessors to synthetic fields * #((fieldDescr) this$n access#m... access$p) : available as a first synthetic field + a sequence of synthetic accessors to synthetic fields * null : not found * jls 15.9.2 + http://www.ergnosis.com/java-spec-report/java-language/jls-8.8.5.1-d.html */ public Object[] getEmulationPath(ReferenceBinding targetEnclosingType, boolean onlyExactMatch, boolean denyEnclosingArgInConstructorCall) { MethodScope currentMethodScope = methodScope(); SourceTypeBinding sourceType = currentMethodScope.enclosingSourceType(); // use 'this' if possible if (!currentMethodScope.isStatic && !currentMethodScope.isConstructorCall) { if (TypeBinding.equalsEquals(sourceType, targetEnclosingType) || (!onlyExactMatch && sourceType.findSuperTypeOriginatingFrom(targetEnclosingType) != null)) { return BlockScope.EmulationPathToImplicitThis; // implicit this is good enough } } if (!sourceType.isNestedType() || sourceType.isStatic()) { // no emulation from within non-inner types if (currentMethodScope.isConstructorCall) { return BlockScope.NoEnclosingInstanceInConstructorCall; } else if (currentMethodScope.isStatic){ return BlockScope.NoEnclosingInstanceInStaticContext; } return null; } boolean insideConstructor = currentMethodScope.isInsideInitializerOrConstructor(); // use synthetic constructor arguments if possible if (insideConstructor) { SyntheticArgumentBinding syntheticArg; if ((syntheticArg = ((NestedTypeBinding) sourceType).getSyntheticArgument(targetEnclosingType, onlyExactMatch, currentMethodScope.isConstructorCall)) != null) { boolean isAnonymousAndHasEnclosing = sourceType.isAnonymousType() && sourceType.scope.referenceContext.allocation.enclosingInstance != null; // reject allocation and super constructor call if (denyEnclosingArgInConstructorCall && !isAnonymousAndHasEnclosing && (TypeBinding.equalsEquals(sourceType, targetEnclosingType) || (!onlyExactMatch && sourceType.findSuperTypeOriginatingFrom(targetEnclosingType) != null))) { return BlockScope.NoEnclosingInstanceInConstructorCall; } return new Object[] { syntheticArg }; } } // use a direct synthetic field then if (currentMethodScope.isStatic) { return BlockScope.NoEnclosingInstanceInStaticContext; } if (sourceType.isAnonymousType()) { ReferenceBinding enclosingType = sourceType.enclosingType(); if (enclosingType.isNestedType()) { NestedTypeBinding nestedEnclosingType = (NestedTypeBinding) enclosingType; SyntheticArgumentBinding enclosingArgument = nestedEnclosingType.getSyntheticArgument(nestedEnclosingType.enclosingType(), onlyExactMatch, currentMethodScope.isConstructorCall); if (enclosingArgument != null) { FieldBinding syntheticField = sourceType.getSyntheticField(enclosingArgument); if (syntheticField != null) { if (TypeBinding.equalsEquals(syntheticField.type, targetEnclosingType) || (!onlyExactMatch && ((ReferenceBinding)syntheticField.type).findSuperTypeOriginatingFrom(targetEnclosingType) != null)) return new Object[] { syntheticField }; } } } } FieldBinding syntheticField = sourceType.getSyntheticField(targetEnclosingType, onlyExactMatch); if (syntheticField != null) { if (currentMethodScope.isConstructorCall){ return BlockScope.NoEnclosingInstanceInConstructorCall; } return new Object[] { syntheticField }; } // could be reached through a sequence of enclosing instance link (nested members) Object[] path = new Object[2]; // probably at least 2 of them ReferenceBinding currentType = sourceType.enclosingType(); if (insideConstructor) { path[0] = ((NestedTypeBinding) sourceType).getSyntheticArgument(currentType, onlyExactMatch, currentMethodScope.isConstructorCall); } else { if (currentMethodScope.isConstructorCall){ return BlockScope.NoEnclosingInstanceInConstructorCall; } path[0] = sourceType.getSyntheticField(currentType, onlyExactMatch); } if (path[0] != null) { // keep accumulating int count = 1; ReferenceBinding currentEnclosingType; while ((currentEnclosingType = currentType.enclosingType()) != null) { //done? if (TypeBinding.equalsEquals(currentType, targetEnclosingType) || (!onlyExactMatch && currentType.findSuperTypeOriginatingFrom(targetEnclosingType) != null)) break; if (currentMethodScope != null) { currentMethodScope = currentMethodScope.enclosingMethodScope(); if (currentMethodScope != null && currentMethodScope.isConstructorCall){ return BlockScope.NoEnclosingInstanceInConstructorCall; } if (currentMethodScope != null && currentMethodScope.isStatic){ return BlockScope.NoEnclosingInstanceInStaticContext; } } syntheticField = ((NestedTypeBinding) currentType).getSyntheticField(currentEnclosingType, onlyExactMatch); if (syntheticField == null) break; // append inside the path if (count == path.length) { System.arraycopy(path, 0, (path = new Object[count + 1]), 0, count); } // private access emulation is necessary since synthetic field is private path[count++] = ((SourceTypeBinding) syntheticField.declaringClass).addSyntheticMethod(syntheticField, true/*read*/, false /*not super access*/); currentType = currentEnclosingType; } if (TypeBinding.equalsEquals(currentType, targetEnclosingType) || (!onlyExactMatch && currentType.findSuperTypeOriginatingFrom(targetEnclosingType) != null)) { return path; } } return null; } /* Answer true if the variable name already exists within the receiver's scope. */ public final boolean isDuplicateLocalVariable(char[] name) { BlockScope current = this; while (true) { for (int i = 0; i < this.localIndex; i++) { if (CharOperation.equals(name, current.locals[i].name)) return true; } if (current.kind != Scope.BLOCK_SCOPE) return false; current = (BlockScope)current.parent; } } public int maxShiftedOffset() { int max = -1; if (this.shiftScopes != null){ for (int i = 0, length = this.shiftScopes.length; i < length; i++){ if (this.shiftScopes[i] != null) { int subMaxOffset = this.shiftScopes[i].maxOffset; if (subMaxOffset > max) max = subMaxOffset; } } } return max; }
Returns true if the context requires to check initialization of final blank fields. in other words, it is inside an initializer, a constructor or a clinit
/** * Returns true if the context requires to check initialization of final blank fields. * in other words, it is inside an initializer, a constructor or a clinit */
public final boolean needBlankFinalFieldInitializationCheck(FieldBinding binding) { boolean isStatic = binding.isStatic(); ReferenceBinding fieldDeclaringClass = binding.declaringClass; // loop in enclosing context, until reaching the field declaring context MethodScope methodScope = namedMethodScope(); while (methodScope != null) { if (methodScope.isStatic != isStatic) return false; if (!methodScope.isInsideInitializer() // inside initializer && !((AbstractMethodDeclaration) methodScope.referenceContext).isInitializationMethod()) { // inside constructor or clinit return false; // found some non-initializer context } ReferenceBinding enclosingType = methodScope.enclosingReceiverType(); if (TypeBinding.equalsEquals(enclosingType, fieldDeclaringClass)) { return true; // found the field context, no need to check any further } if (!enclosingType.erasure().isAnonymousType()) { return false; // only check inside anonymous type } methodScope = methodScope.enclosingMethodScope().namedMethodScope(); } return false; } /* 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() { return methodScope().problemReporter(); } /* * Code responsible to request some more emulation work inside the invocation type, so as to supply * correct synthetic arguments to any allocation of the target type. */ public void propagateInnerEmulation(ReferenceBinding targetType, boolean isEnclosingInstanceSupplied) { // no need to propagate enclosing instances, they got eagerly allocated already. SyntheticArgumentBinding[] syntheticArguments; if ((syntheticArguments = targetType.syntheticOuterLocalVariables()) != null) { for (int i = 0, max = syntheticArguments.length; i < max; i++) { SyntheticArgumentBinding syntheticArg = syntheticArguments[i]; // need to filter out the one that could match a supplied enclosing instance if (!(isEnclosingInstanceSupplied && (TypeBinding.equalsEquals(syntheticArg.type, targetType.enclosingType())))) { emulateOuterAccess(syntheticArg.actualOuterLocalVariable); } } } } /* Answer the reference type of this scope. * * It is the nearest enclosing type of this scope. */ public TypeDeclaration referenceType() { return methodScope().referenceType(); } /* * Answer the index of this scope relatively to its parent. * For method scope, answers -1 (not a classScope relative position) */ public int scopeIndex() { if (this instanceof MethodScope) return -1; BlockScope parentScope = (BlockScope)this.parent; Scope[] parentSubscopes = parentScope.subscopes; for (int i = 0, max = parentScope.subscopeCount; i < max; i++) { if (parentSubscopes[i] == this) return i; } return -1; } // start position in this scope - for ordering scopes vs. variables @Override int startIndex() { return this.startIndex; } @Override public String toString() { return toString(0); } public String toString(int tab) { String s = basicToString(tab); for (int i = 0; i < this.subscopeCount; i++) if (this.subscopes[i] instanceof BlockScope) s += ((BlockScope) this.subscopes[i]).toString(tab + 1) + "\n"; //$NON-NLS-1$ return s; } private List trackingVariables; // can be null if no resources are tracked
Used only during analyseCode and only for checking if a resource was closed in a finallyBlock.
/** Used only during analyseCode and only for checking if a resource was closed in a finallyBlock. */
public FlowInfo finallyInfo;
Register a tracking variable and compute its id.
/** * Register a tracking variable and compute its id. */
public int registerTrackingVariable(FakedTrackingVariable fakedTrackingVariable) { if (this.trackingVariables == null) this.trackingVariables = new ArrayList(3); this.trackingVariables.add(fakedTrackingVariable); MethodScope outerMethodScope = outerMostMethodScope(); return outerMethodScope.analysisIndex++; }
When are no longer interested in this tracking variable - remove it.
/** When are no longer interested in this tracking variable - remove it. */
public void removeTrackingVar(FakedTrackingVariable trackingVariable) { if (trackingVariable.innerTracker != null) { trackingVariable.innerTracker.withdraw(); trackingVariable.innerTracker = null; } if (this.trackingVariables != null) if (this.trackingVariables.remove(trackingVariable)) return; if (this.parent instanceof BlockScope) ((BlockScope)this.parent).removeTrackingVar(trackingVariable); }
Unregister a wrapper resource without affecting its inner.
/** Unregister a wrapper resource without affecting its inner. */
public void pruneWrapperTrackingVar(FakedTrackingVariable trackingVariable) { this.trackingVariables.remove(trackingVariable); }
At the end of a block check the closing-status of all tracked closeables that are declared in this block. Also invoked when entering unreachable code.
/** * At the end of a block check the closing-status of all tracked closeables that are declared in this block. * Also invoked when entering unreachable code. */
public void checkUnclosedCloseables(FlowInfo flowInfo, FlowContext flowContext, ASTNode location, BlockScope locationScope) { if (!compilerOptions().analyseResourceLeaks) return; if (this.trackingVariables == null) { // at a method return we also consider enclosing scopes if (location != null && this.parent instanceof BlockScope && !isLambdaScope()) ((BlockScope) this.parent).checkUnclosedCloseables(flowInfo, flowContext, location, locationScope); return; } if (location != null && flowInfo.reachMode() != 0) return; FakedTrackingVariable returnVar = (location instanceof ReturnStatement) ? FakedTrackingVariable.getCloseTrackingVariable(((ReturnStatement)location).expression, flowInfo, flowContext) : null; // iterate variables according to the priorities defined in FakedTrackingVariable.IteratorForReporting.Stage Iterator<FakedTrackingVariable> iterator = new FakedTrackingVariable.IteratorForReporting(this.trackingVariables, this, location != null); while (iterator.hasNext()) { FakedTrackingVariable trackingVar = iterator.next(); if (returnVar != null && trackingVar.isResourceBeingReturned(returnVar)) { continue; } if (location != null && trackingVar.hasDefinitelyNoResource(flowInfo)) { continue; // reporting against a specific location, there is no resource at this flow, don't complain } if (location != null && flowContext != null && flowContext.recordExitAgainstResource(this, flowInfo, trackingVar, location)) { continue; // handled by the flow context } // compute the most specific null status for this resource, int status = trackingVar.findMostSpecificStatus(flowInfo, this, locationScope); if (status == FlowInfo.NULL) { // definitely unclosed: highest priority reportResourceLeak(trackingVar, location, status); continue; } if (location == null) // at end of block and not definitely unclosed { // problems at specific locations: medium priority if (trackingVar.reportRecordedErrors(this, status, flowInfo.reachMode() != FlowInfo.REACHABLE)) // ... report previously recorded errors continue; } if (status == FlowInfo.POTENTIALLY_NULL) { // potentially unclosed: lower priority reportResourceLeak(trackingVar, location, status); } else if (status == FlowInfo.NON_NULL) { // properly closed but not managed by t-w-r: lowest priority if (environment().globalOptions.complianceLevel >= ClassFileConstants.JDK1_7) trackingVar.reportExplicitClosing(problemReporter()); } } if (location == null) { // when leaving this block dispose off all tracking variables: for (int i=0; i<this.localIndex; i++) this.locals[i].closeTracker = null; this.trackingVariables = null; } } private void reportResourceLeak(FakedTrackingVariable trackingVar, ASTNode location, int nullStatus) { if (location != null) trackingVar.recordErrorLocation(location, nullStatus); else trackingVar.reportError(problemReporter(), null, nullStatus); }
If one branch of an if-else closes any AutoCloseable resource, and if the same resource is known to be null on the other branch mark it as closed, too, so that merging both branches indicates that the resource is always closed. Example: FileReader fr1 = null; try {\n" + fr1 = new FileReader(someFile);" + fr1.read(buf);\n" + } finally {\n" + if (fr1 != null)\n" + try {\n" + fr1.close();\n" + } catch (IOException e) { // do nothing } // after this if statement fr1 is definitely not leaked }
/** * If one branch of an if-else closes any AutoCloseable resource, and if the same * resource is known to be null on the other branch mark it as closed, too, * so that merging both branches indicates that the resource is always closed. * Example: * FileReader fr1 = null; * try {\n" + * fr1 = new FileReader(someFile);" + * fr1.read(buf);\n" + * } finally {\n" + * if (fr1 != null)\n" + * try {\n" + * fr1.close();\n" + * } catch (IOException e) { * // do nothing * } * // after this if statement fr1 is definitely not leaked * } */
public void correlateTrackingVarsIfElse(FlowInfo thenFlowInfo, FlowInfo elseFlowInfo) { if (this.trackingVariables != null) { int trackVarCount = this.trackingVariables.size(); for (int i=0; i<trackVarCount; i++) { FakedTrackingVariable trackingVar = (FakedTrackingVariable) this.trackingVariables.get(i); if (trackingVar.originalBinding == null) continue; if ( thenFlowInfo.isDefinitelyNonNull(trackingVar.binding) // closed in then branch && elseFlowInfo.isDefinitelyNull(trackingVar.originalBinding)) // null in else branch { elseFlowInfo.markAsDefinitelyNonNull(trackingVar.binding); // -> always closed } else if ( elseFlowInfo.isDefinitelyNonNull(trackingVar.binding) // closed in else branch && thenFlowInfo.isDefinitelyNull(trackingVar.originalBinding)) // null in then branch { thenFlowInfo.markAsDefinitelyNonNull(trackingVar.binding); // -> always closed } else { if (thenFlowInfo == FlowInfo.DEAD_END || elseFlowInfo == FlowInfo.DEAD_END) continue; // short cut for (int j=i+1; j<trackVarCount; j++) { FakedTrackingVariable var2 = ((FakedTrackingVariable) this.trackingVariables.get(j)); if (trackingVar.originalBinding == var2.originalBinding) { // two tracking variables for the same original, merge info from both branches now: boolean var1SeenInThen = thenFlowInfo.hasNullInfoFor(trackingVar.binding); boolean var1SeenInElse = elseFlowInfo.hasNullInfoFor(trackingVar.binding); boolean var2SeenInThen = thenFlowInfo.hasNullInfoFor(var2.binding); boolean var2SeenInElse = elseFlowInfo.hasNullInfoFor(var2.binding); int newStatus; if (!var1SeenInThen && var1SeenInElse && var2SeenInThen && !var2SeenInElse) { newStatus = FlowInfo.mergeNullStatus(thenFlowInfo.nullStatus(var2.binding), elseFlowInfo.nullStatus(trackingVar.binding)); } else if (var1SeenInThen && !var1SeenInElse && !var2SeenInThen && var2SeenInElse) { newStatus = FlowInfo.mergeNullStatus(thenFlowInfo.nullStatus(trackingVar.binding), elseFlowInfo.nullStatus(var2.binding)); } else { continue; } thenFlowInfo.markNullStatus(trackingVar.binding, newStatus); elseFlowInfo.markNullStatus(trackingVar.binding, newStatus); trackingVar.originalBinding.closeTracker = trackingVar; // avoid further use of var2 thenFlowInfo.markNullStatus(var2.binding, FlowInfo.NON_NULL); elseFlowInfo.markNullStatus(var2.binding, FlowInfo.NON_NULL); } } } } } if (this.parent instanceof BlockScope) ((BlockScope) this.parent).correlateTrackingVarsIfElse(thenFlowInfo, elseFlowInfo); }
15.12.3 (Java 8) "Compile-Time Step 3: Is the Chosen Method Appropriate?"
/** 15.12.3 (Java 8) "Compile-Time Step 3: Is the Chosen Method Appropriate?" */
public void checkAppropriateMethodAgainstSupers(char[] selector, MethodBinding compileTimeMethod, TypeBinding[] parameters, InvocationSite site) { ReferenceBinding enclosingType = enclosingReceiverType(); MethodBinding otherMethod = getMethod(enclosingType.superclass(), selector, parameters, site); if (checkAppropriate(compileTimeMethod, otherMethod, site)) { ReferenceBinding[] superInterfaces = enclosingType.superInterfaces(); if (superInterfaces != null) { for (int i = 0; i < superInterfaces.length; i++) { otherMethod = getMethod(superInterfaces[i], selector, parameters, site); if (!checkAppropriate(compileTimeMethod, otherMethod, site)) break; } } } } private boolean checkAppropriate(MethodBinding compileTimeDeclaration, MethodBinding otherMethod, InvocationSite location) { if (otherMethod == null || !otherMethod.isValidBinding() || otherMethod.original() == compileTimeDeclaration.original()) return true; if (MethodVerifier.doesMethodOverride(otherMethod, compileTimeDeclaration, this.environment())) { problemReporter().illegalSuperCallBypassingOverride(location, compileTimeDeclaration, otherMethod.declaringClass); return false; } return true; } }