public TypeBinding resolveType(BlockScope scope) { this.constant = Constant.NotAConstant; if (!checkAccess(scope.methodScope())) return null; ReferenceBinding enclosingReceiverType = scope.enclosingReceiverType(); if (enclosingReceiverType.id == T_JavaLangObject) { scope.problemReporter().cannotUseSuperInJavaLangObject(this); return null; } return this.resolvedType = enclosingReceiverType.superclass(); }
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { // Consider the try block and catch block so as to compute the intersection of initializations // and // the minimum exit relative depth amongst all of them. Then consider the subroutine, and append // its // initialization to the try/catch ones, if the subroutine completes normally. If the subroutine // does not // complete, then only keep this result for the rest of the analysis // process the finally block (subroutine) - create a context for the subroutine preTryInitStateIndex = currentScope.methodScope().recordInitializationStates(flowInfo); if (anyExceptionVariable != null) { anyExceptionVariable.useFlag = LocalVariableBinding.USED; } if (returnAddressVariable != null) { // TODO (philippe) if subroutine is escaping, unused returnAddressVariable.useFlag = LocalVariableBinding.USED; } InsideSubRoutineFlowContext insideSubContext; FinallyFlowContext finallyContext; UnconditionalFlowInfo subInfo; if (subRoutineStartLabel == null) { // no finally block insideSubContext = null; finallyContext = null; subInfo = null; } else { // analyse finally block first insideSubContext = new InsideSubRoutineFlowContext(flowContext, this); subInfo = finallyBlock .analyseCode( currentScope, finallyContext = new FinallyFlowContext(flowContext, finallyBlock), flowInfo.copy().unconditionalInits().discardNullRelatedInitializations()) .unconditionalInits(); if (subInfo == FlowInfo.DEAD_END) { isSubRoutineEscaping = true; scope.problemReporter().finallyMustCompleteNormally(finallyBlock); } this.subRoutineInits = subInfo; } // process the try block in a context handling the local exceptions. ExceptionHandlingFlowContext handlingContext = new ExceptionHandlingFlowContext( insideSubContext == null ? flowContext : insideSubContext, tryBlock, caughtExceptionTypes, scope, flowInfo.unconditionalInits()); FlowInfo tryInfo; if (tryBlock.isEmptyBlock()) { tryInfo = flowInfo; tryBlockExit = false; } else { tryInfo = tryBlock.analyseCode(currentScope, handlingContext, flowInfo.copy()); tryBlockExit = !tryInfo.isReachable(); } // check unreachable catch blocks handlingContext.complainIfUnusedExceptionHandlers(scope, this); // process the catch blocks - computing the minimal exit depth amongst try/catch if (catchArguments != null) { int catchCount; catchExits = new boolean[catchCount = catchBlocks.length]; for (int i = 0; i < catchCount; i++) { // keep track of the inits that could potentially have led to this exception handler (for // final assignments diagnosis) FlowInfo catchInfo = flowInfo .copy() .unconditionalInits() .addPotentialInitializationsFrom( handlingContext.initsOnException(caughtExceptionTypes[i]).unconditionalInits()) .addPotentialInitializationsFrom(tryInfo.unconditionalInits()) .addPotentialInitializationsFrom(handlingContext.initsOnReturn); // catch var is always set LocalVariableBinding catchArg = catchArguments[i].binding; FlowContext catchContext = insideSubContext == null ? flowContext : insideSubContext; catchInfo.markAsDefinitelyAssigned(catchArg); catchInfo.markAsDefinitelyNonNull(catchArg); /* "If we are about to consider an unchecked exception handler, potential inits may have occured inside the try block that need to be detected , e.g. try { x = 1; throwSomething();} catch(Exception e){ x = 2} " "(uncheckedExceptionTypes notNil and: [uncheckedExceptionTypes at: index]) ifTrue: [catchInits addPotentialInitializationsFrom: tryInits]." */ if (tryBlock.statements == null) { catchInfo.setReachMode(FlowInfo.UNREACHABLE); } catchInfo = catchBlocks[i].analyseCode(currentScope, catchContext, catchInfo); catchExits[i] = !catchInfo.isReachable(); tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits()); } } if (subRoutineStartLabel == null) { mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(tryInfo); return tryInfo; } // we also need to check potential multiple assignments of final variables inside the finally // block // need to include potential inits from returns inside the try/catch parts - 1GK2AOF finallyContext.complainOnDeferredChecks( tryInfo.isReachable() ? (tryInfo.addPotentialInitializationsFrom(insideSubContext.initsOnReturn)) : insideSubContext.initsOnReturn, currentScope); if (subInfo == FlowInfo.DEAD_END) { mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(subInfo); return subInfo; } else { FlowInfo mergedInfo = tryInfo.addInitializationsFrom(subInfo); mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); return mergedInfo; } }
public void resolve(BlockScope upperScope) { // special scope for secret locals optimization. this.scope = new BlockScope(upperScope); BlockScope tryScope = new BlockScope(scope); BlockScope finallyScope = null; if (finallyBlock != null) { if (finallyBlock.isEmptyBlock()) { if ((finallyBlock.bits & UndocumentedEmptyBlockMASK) != 0) { scope .problemReporter() .undocumentedEmptyBlock(finallyBlock.sourceStart, finallyBlock.sourceEnd); } } else { finallyScope = new BlockScope(scope, false); // don't add it yet to parent scope // provision for returning and forcing the finally block to run MethodScope methodScope = scope.methodScope(); // the type does not matter as long as it is not a base type if (!upperScope.compilerOptions().inlineJsrBytecode) { this.returnAddressVariable = new LocalVariableBinding( SecretReturnName, upperScope.getJavaLangObject(), AccDefault, false); finallyScope.addLocalVariable(returnAddressVariable); this.returnAddressVariable.setConstant(NotAConstant); // not inlinable } this.subRoutineStartLabel = new Label(); this.anyExceptionVariable = new LocalVariableBinding( SecretAnyHandlerName, scope.getJavaLangThrowable(), AccDefault, false); finallyScope.addLocalVariable(this.anyExceptionVariable); this.anyExceptionVariable.setConstant(NotAConstant); // not inlinable if (!methodScope.isInsideInitializer()) { MethodBinding methodBinding = ((AbstractMethodDeclaration) methodScope.referenceContext).binding; if (methodBinding != null) { TypeBinding methodReturnType = methodBinding.returnType; if (methodReturnType.id != T_void) { this.secretReturnValue = new LocalVariableBinding( SecretLocalDeclarationName, methodReturnType, AccDefault, false); finallyScope.addLocalVariable(this.secretReturnValue); this.secretReturnValue.setConstant(NotAConstant); // not inlinable } } } finallyBlock.resolveUsing(finallyScope); // force the finally scope to have variable positions shifted after its try scope and catch // ones finallyScope.shiftScopes = new BlockScope[catchArguments == null ? 1 : catchArguments.length + 1]; finallyScope.shiftScopes[0] = tryScope; } } this.tryBlock.resolveUsing(tryScope); // arguments type are checked against JavaLangThrowable in resolveForCatch(..) if (this.catchBlocks != null) { int length = this.catchArguments.length; TypeBinding[] argumentTypes = new TypeBinding[length]; boolean catchHasError = false; for (int i = 0; i < length; i++) { BlockScope catchScope = new BlockScope(scope); if (finallyScope != null) { finallyScope.shiftScopes[i + 1] = catchScope; } // side effect on catchScope in resolveForCatch(..) if ((argumentTypes[i] = catchArguments[i].resolveForCatch(catchScope)) == null) { catchHasError = true; } catchBlocks[i].resolveUsing(catchScope); } if (catchHasError) { return; } // Verify that the catch clause are ordered in the right way: // more specialized first. this.caughtExceptionTypes = new ReferenceBinding[length]; for (int i = 0; i < length; i++) { caughtExceptionTypes[i] = (ReferenceBinding) argumentTypes[i]; for (int j = 0; j < i; j++) { if (caughtExceptionTypes[i].isCompatibleWith(argumentTypes[j])) { scope .problemReporter() .wrongSequenceOfExceptionTypesError( this, caughtExceptionTypes[i], i, argumentTypes[j]); } } } } else { caughtExceptionTypes = new ReferenceBinding[0]; } if (finallyScope != null) { // add finallyScope as last subscope, so it can be shifted behind try/catch subscopes. // the shifting is necessary to achieve no overlay in between the finally scope and its // sibling in term of local variable positions. this.scope.addSubscope(finallyScope); } }
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { this.breakLabel = new BranchLabel(); this.continueLabel = new BranchLabel(); LoopingFlowContext loopingContext = new LoopingFlowContext( flowContext, flowInfo, this, this.breakLabel, this.continueLabel, currentScope); Constant cst = this.condition.constant; boolean isConditionTrue = cst != Constant.NotAConstant && cst.booleanValue() == true; cst = this.condition.optimizedBooleanConstant(); boolean isConditionOptimizedTrue = cst != Constant.NotAConstant && cst.booleanValue() == true; boolean isConditionOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false; int previousMode = flowInfo.reachMode(); UnconditionalFlowInfo actionInfo = flowInfo.nullInfoLessUnconditionalCopy(); // we need to collect the contribution to nulls of the coming paths through the // loop, be they falling through normally or branched to break, continue labels // or catch blocks if ((this.action != null) && !this.action.isEmptyBlock()) { actionInfo = this.action.analyseCode(currentScope, loopingContext, actionInfo).unconditionalInits(); // code generation can be optimized when no need to continue in the loop if ((actionInfo.tagBits & loopingContext.initsOnContinue.tagBits & FlowInfo.UNREACHABLE) != 0) { this.continueLabel = null; } } /* Reset reach mode, to address following scenario. * final blank; * do { if (true) break; else blank = 0; } while(false); * blank = 1; // may be initialized already */ actionInfo.setReachMode(previousMode); LoopingFlowContext condLoopContext; FlowInfo condInfo = this.condition.analyseCode( currentScope, (condLoopContext = new LoopingFlowContext(flowContext, flowInfo, this, null, null, currentScope)), (this.action == null ? actionInfo : (actionInfo.mergedWith(loopingContext.initsOnContinue))) .copy()); this.preConditionInitStateIndex = currentScope.methodScope().recordInitializationStates(actionInfo); if (!isConditionOptimizedFalse && this.continueLabel != null) { loopingContext.complainOnDeferredFinalChecks(currentScope, condInfo); condLoopContext.complainOnDeferredFinalChecks(currentScope, condInfo); loopingContext.complainOnDeferredNullChecks( currentScope, flowInfo .unconditionalCopy() .addPotentialNullInfoFrom(condInfo.initsWhenTrue().unconditionalInits())); condLoopContext.complainOnDeferredNullChecks( currentScope, actionInfo.addPotentialNullInfoFrom(condInfo.initsWhenTrue().unconditionalInits())); } // end of loop FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches( (loopingContext.initsOnBreak.tagBits & FlowInfo.UNREACHABLE) != 0 ? loopingContext.initsOnBreak : flowInfo.unconditionalCopy().addInitializationsFrom(loopingContext.initsOnBreak), // recover upstream null info isConditionOptimizedTrue, (condInfo.tagBits & FlowInfo.UNREACHABLE) == 0 ? flowInfo.addInitializationsFrom(condInfo.initsWhenFalse()) : condInfo, // recover null inits from before condition analysis false, // never consider opt false case for DO loop, since break can always occur // (47776) !isConditionTrue /*do{}while(true); unreachable(); */); this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); return mergedInfo; }
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { Constant cst = this.left.optimizedBooleanConstant(); boolean isLeftOptimizedTrue = cst != Constant.NotAConstant && cst.booleanValue() == true; boolean isLeftOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false; if (isLeftOptimizedFalse) { // FALSE || anything // need to be careful of scenario: // (x || y) || !z, if passing the left info to the right, it would be swapped by the ! FlowInfo mergedInfo = this.left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits(); flowContext.expireNullCheckedFieldInfo(); mergedInfo = this.right.analyseCode(currentScope, flowContext, mergedInfo); flowContext.expireNullCheckedFieldInfo(); this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); return mergedInfo; } FlowInfo leftInfo = this.left.analyseCode(currentScope, flowContext, flowInfo); flowContext.expireNullCheckedFieldInfo(); // need to be careful of scenario: // (x || y) || !z, if passing the left info to the right, it would be swapped by the ! FlowInfo rightInfo = leftInfo.initsWhenFalse().unconditionalCopy(); this.rightInitStateIndex = currentScope.methodScope().recordInitializationStates(rightInfo); int previousMode = rightInfo.reachMode(); if (isLeftOptimizedTrue) { if ((rightInfo.reachMode() & FlowInfo.UNREACHABLE) == 0) { currentScope.problemReporter().fakeReachable(this.right); rightInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD); } } rightInfo = this.right.analyseCode(currentScope, flowContext, rightInfo); flowContext.expireNullCheckedFieldInfo(); if ((this.left.implicitConversion & TypeIds.UNBOXING) != 0) { this.left.checkNPE(currentScope, flowContext, flowInfo); } if ((this.right.implicitConversion & TypeIds.UNBOXING) != 0) { this.right.checkNPE(currentScope, flowContext, flowInfo); } // The definitely null variables in right info when true should not be missed out while merging // https://bugs.eclipse.org/bugs/show_bug.cgi?id=299900 FlowInfo leftInfoWhenTrueForMerging = leftInfo .initsWhenTrue() .unconditionalCopy() .addPotentialInitializationsFrom(rightInfo.unconditionalInitsWithoutSideEffect()); FlowInfo mergedInfo = FlowInfo.conditional( // merging two true initInfos for such a negative case: if ((t && (b = t)) || f) r = b; // // b may not have been initialized leftInfoWhenTrueForMerging .unconditionalInits() .mergedWith( rightInfo.safeInitsWhenTrue().setReachMode(previousMode).unconditionalInits()), rightInfo.initsWhenFalse()); this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); return mergedInfo; }
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { this.preAssertInitStateIndex = currentScope.methodScope().recordInitializationStates(flowInfo); Constant cst = this.assertExpression.optimizedBooleanConstant(); if ((this.assertExpression.implicitConversion & TypeIds.UNBOXING) != 0) { this.assertExpression.checkNPE(currentScope, flowContext, flowInfo); } boolean isOptimizedTrueAssertion = cst != Constant.NotAConstant && cst.booleanValue() == true; boolean isOptimizedFalseAssertion = cst != Constant.NotAConstant && cst.booleanValue() == false; flowContext.tagBits |= FlowContext.HIDE_NULL_COMPARISON_WARNING; FlowInfo conditionFlowInfo = this.assertExpression.analyseCode(currentScope, flowContext, flowInfo.copy()); flowContext.extendTimeToLiveForNullCheckedField(1); // survive this assert as a Statement flowContext.tagBits &= ~FlowContext.HIDE_NULL_COMPARISON_WARNING; UnconditionalFlowInfo assertWhenTrueInfo = conditionFlowInfo.initsWhenTrue().unconditionalInits(); FlowInfo assertInfo = conditionFlowInfo.initsWhenFalse(); if (isOptimizedTrueAssertion) { assertInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD); } if (this.exceptionArgument != null) { // only gets evaluated when escaping - results are not taken into account FlowInfo exceptionInfo = this.exceptionArgument.analyseCode(currentScope, flowContext, assertInfo.copy()); if (isOptimizedTrueAssertion) { currentScope.problemReporter().fakeReachable(this.exceptionArgument); } else { flowContext.checkExceptionHandlers( currentScope.getJavaLangAssertionError(), this, exceptionInfo, currentScope); } } if (!isOptimizedTrueAssertion) { // add the assert support in the clinit manageSyntheticAccessIfNecessary(currentScope, flowInfo); } // account for potential AssertionError: flowContext.recordAbruptExit(); if (isOptimizedFalseAssertion) { return flowInfo; // if assertions are enabled, the following code will be unreachable // change this if we need to carry null analysis results of the assert // expression downstream } else { CompilerOptions compilerOptions = currentScope.compilerOptions(); if (!compilerOptions.includeNullInfoFromAsserts) { // keep just the initializations info, don't include assert's null info // merge initialization info's and then add back the null info from flowInfo to // make sure that the empty null info of assertInfo doesnt change flowInfo's null info. return ((flowInfo.nullInfoLessUnconditionalCopy()) .mergedWith(assertInfo.nullInfoLessUnconditionalCopy())) .addNullInfoFrom(flowInfo); } return flowInfo .mergedWith(assertInfo.nullInfoLessUnconditionalCopy()) .addInitializationsFrom(assertWhenTrueInfo.discardInitializationInfo()); // keep the merge from the initial code for the definite assignment // analysis, tweak the null part to influence nulls downstream } }
public TypeBinding resolveType(BlockScope scope) { // Answer the signature type of the field. // constants are propaged when the field is final // and initialized with a (compile time) constant // always ignore receiver cast, since may affect constant pool reference boolean receiverCast = false; if (this.receiver instanceof CastExpression) { this.receiver.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on receiverCast = true; } this.actualReceiverType = this.receiver.resolveType(scope); if (this.actualReceiverType == null) { this.constant = Constant.NotAConstant; return null; } if (receiverCast) { // due to change of declaring class with receiver type, only identity cast should be notified if (((CastExpression) this.receiver).expression.resolvedType == this.actualReceiverType) { scope.problemReporter().unnecessaryCast((CastExpression) this.receiver); } } // the case receiverType.isArrayType and token = 'length' is handled by the scope API FieldBinding fieldBinding = this.binding = scope.getField(this.actualReceiverType, this.token, this); if (!fieldBinding.isValidBinding()) { this.constant = Constant.NotAConstant; if (this.receiver.resolvedType instanceof ProblemReferenceBinding) { // problem already got signaled on receiver, do not report secondary problem return null; } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=245007 avoid secondary errors in case of // missing super type for anonymous classes ... ReferenceBinding declaringClass = fieldBinding.declaringClass; boolean avoidSecondary = declaringClass != null && declaringClass.isAnonymousType() && declaringClass.superclass() instanceof MissingTypeBinding; if (!avoidSecondary) { scope.problemReporter().invalidField(this, this.actualReceiverType); } if (fieldBinding instanceof ProblemFieldBinding) { ProblemFieldBinding problemFieldBinding = (ProblemFieldBinding) fieldBinding; FieldBinding closestMatch = problemFieldBinding.closestMatch; switch (problemFieldBinding.problemId()) { case ProblemReasons.InheritedNameHidesEnclosingName: case ProblemReasons.NotVisible: case ProblemReasons.NonStaticReferenceInConstructorInvocation: case ProblemReasons.NonStaticReferenceInStaticContext: if (closestMatch != null) { fieldBinding = closestMatch; } } } if (!fieldBinding.isValidBinding()) { return null; } } // handle indirect inheritance thru variable secondary bound // receiver may receive generic cast, as part of implicit conversion TypeBinding oldReceiverType = this.actualReceiverType; this.actualReceiverType = this.actualReceiverType.getErasureCompatibleType(fieldBinding.declaringClass); this.receiver.computeConversion(scope, this.actualReceiverType, this.actualReceiverType); if (this.actualReceiverType != oldReceiverType && this.receiver.postConversionType(scope) != this .actualReceiverType) { // record need for explicit cast at codegen since receiver // could not handle it this.bits |= NeedReceiverGenericCast; } if (isFieldUseDeprecated(fieldBinding, scope, this.bits)) { scope.problemReporter().deprecatedField(fieldBinding, this); } boolean isImplicitThisRcv = this.receiver.isImplicitThis(); this.constant = isImplicitThisRcv ? fieldBinding.constant() : Constant.NotAConstant; if (fieldBinding.isStatic()) { // static field accessed through receiver? legal but unoptimal (optional warning) if (!(isImplicitThisRcv || (this.receiver instanceof NameReference && (((NameReference) this.receiver).bits & Binding.TYPE) != 0))) { scope.problemReporter().nonStaticAccessToStaticField(this, fieldBinding); } ReferenceBinding declaringClass = this.binding.declaringClass; if (!isImplicitThisRcv && declaringClass != this.actualReceiverType && declaringClass.canBeSeenBy(scope)) { scope.problemReporter().indirectAccessToStaticField(this, fieldBinding); } // check if accessing enum static field in initializer if (declaringClass.isEnum()) { MethodScope methodScope = scope.methodScope(); SourceTypeBinding sourceType = scope.enclosingSourceType(); if (this.constant == Constant.NotAConstant && !methodScope.isStatic && (sourceType == declaringClass || sourceType.superclass == declaringClass) // enum constant body && methodScope.isInsideInitializerOrConstructor()) { scope.problemReporter().enumStaticFieldUsedDuringInitialization(this.binding, this); } } } TypeBinding fieldType = fieldBinding.type; if (fieldType != null) { if ((this.bits & ASTNode.IsStrictlyAssigned) == 0) { fieldType = fieldType.capture(scope, this.sourceEnd); // perform capture conversion if read access } this.resolvedType = fieldType; if ((fieldType.tagBits & TagBits.HasMissingType) != 0) { scope.problemReporter().invalidType(this, fieldType); return null; } } return fieldType; }
public TypeBinding resolveType(BlockScope scope) { this.constant = Constant.NotAConstant; if ((this.targetType = this.type.resolveType(scope, true /* check bounds*/)) == null) return null; /* https://bugs.eclipse.org/bugs/show_bug.cgi?id=320463 https://bugs.eclipse.org/bugs/show_bug.cgi?id=312076 JLS3 15.8.2 forbids the type named in the class literal expression from being a parameterized type. And the grammar in 18.1 disallows (where X and Y are some concrete types) constructs of the form Outer<X>.class, Outer<X>.Inner.class, Outer.Inner<X>.class, Outer<X>.Inner<Y>.class etc. Corollary wise, we should resolve the type of the class literal expression to be a raw type as class literals exist only for the raw underlying type. */ LookupEnvironment environment = scope.environment(); this.targetType = environment.convertToRawType( this.targetType, true /* force conversion of enclosing types*/); if (this.targetType.isArrayType()) { ArrayBinding arrayBinding = (ArrayBinding) this.targetType; TypeBinding leafComponentType = arrayBinding.leafComponentType; if (leafComponentType == TypeBinding.VOID) { scope.problemReporter().cannotAllocateVoidArray(this); return null; } else if (leafComponentType.isTypeVariable()) { scope .problemReporter() .illegalClassLiteralForTypeVariable((TypeVariableBinding) leafComponentType, this); } } else if (this.targetType.isTypeVariable()) { scope .problemReporter() .illegalClassLiteralForTypeVariable((TypeVariableBinding) this.targetType, this); } // {ObjectTeams: do we need a RoleClassLiteralAccess? if (this.targetType instanceof ReferenceBinding) { ReferenceBinding targetRef = (ReferenceBinding) this.targetType; if (targetRef.isRole()) { if (this.verbatim) { this.targetType = RoleTypeCreator.maybeWrapUnqualifiedRoleType(scope, this.targetType, this); } else { SourceTypeBinding site = scope.enclosingSourceType(); if (scope.methodScope().isStatic // role class literal needs team instance && !site.isRole() // static role method are OK. && !RoleTypeBinding.isRoleWithExplicitAnchor(this.targetType)) // t.R.class? { scope.problemReporter().roleClassLiteralLacksTeamInstance(this, targetRef); return null; } ReferenceBinding teamBinding; if (RoleTypeBinding.isRoleWithExplicitAnchor(targetRef)) teamBinding = targetRef.enclosingType(); else teamBinding = TeamModel.findEnclosingTeamContainingRole(site, targetRef); if (teamBinding == null) scope.problemReporter().externalizedRoleClassLiteral(this, targetRef); else { TypeBinding methodType = RoleClassLiteralAccess.ensureGetClassMethod( teamBinding.getTeamModel(), targetRef.roleModel); // not affected by visibility check (for resilience) this.roleClassLiteralAccess = new RoleClassLiteralAccess(this, methodType); this.resolvedType = this.roleClassLiteralAccess.resolveType(scope); } return this.resolvedType; } } } // SH} ReferenceBinding classType = scope.getJavaLangClass(); // https://bugs.eclipse.org/bugs/show_bug.cgi?id=328689 if (scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) { // Integer.class --> Class<Integer>, perform boxing of base types (int.class --> // Class<Integer>) TypeBinding boxedType = null; if (this.targetType.id == T_void) { boxedType = environment.getResolvedType(JAVA_LANG_VOID, scope); } else { boxedType = scope.boxing(this.targetType); } if (environment.usesNullTypeAnnotations()) boxedType = environment.createAnnotatedType( boxedType, new AnnotationBinding[] {environment.getNonNullAnnotation()}); this.resolvedType = environment.createParameterizedType( classType, new TypeBinding[] {boxedType}, null /*not a member*/); } else { this.resolvedType = classType; } return this.resolvedType; }
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { boolean nonStatic = !this.binding.isStatic(); boolean wasInsideAssert = ((flowContext.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) != 0); flowInfo = this.receiver .analyseCode(currentScope, flowContext, flowInfo, nonStatic) .unconditionalInits(); // recording the closing of AutoCloseable resources: boolean analyseResources = currentScope.compilerOptions().analyseResourceLeaks; if (analyseResources && CharOperation.equals(TypeConstants.CLOSE, this.selector)) { FakedTrackingVariable trackingVariable = FakedTrackingVariable.getCloseTrackingVariable(this.receiver); if (trackingVariable != null) { // null happens if receiver is not a local variable or not an AutoCloseable if (trackingVariable.methodScope == currentScope.methodScope()) { trackingVariable.markClose(flowInfo, flowContext); } else { trackingVariable.markClosedInNestedMethod(); } } } if (nonStatic) { this.receiver.checkNPE(currentScope, flowContext, flowInfo); // https://bugs.eclipse.org/bugs/show_bug.cgi?id=318682 if (this.receiver.isThis()) { // accessing non-static method without an object currentScope.resetDeclaringClassMethodStaticFlag(this.binding.declaringClass); } } else if (this.receiver.isThis()) { if ((this.receiver.bits & ASTNode.IsImplicitThis) == 0) { // explicit this receiver, not allowed in static context currentScope.resetEnclosingMethodStaticFlag(); } } FlowInfo conditionFlowInfo; if (this.arguments != null) { int length = this.arguments.length; for (int i = 0; i < length; i++) { Expression argument = this.arguments[i]; if ((argument.implicitConversion & TypeIds.UNBOXING) != 0) { argument.checkNPE(currentScope, flowContext, flowInfo); } if (this.receiver.resolvedType != null && this.receiver.resolvedType.id == TypeIds.T_OrgEclipseCoreRuntimeAssert && argument.resolvedType != null && argument.resolvedType.id == TypeIds.T_boolean) { Constant cst = argument.optimizedBooleanConstant(); boolean isOptimizedTrueAssertion = cst != Constant.NotAConstant && cst.booleanValue() == true; boolean isOptimizedFalseAssertion = cst != Constant.NotAConstant && cst.booleanValue() == false; flowContext.tagBits |= FlowContext.HIDE_NULL_COMPARISON_WARNING; conditionFlowInfo = argument.analyseCode(currentScope, flowContext, flowInfo.copy()); if (!wasInsideAssert) { flowContext.tagBits &= ~FlowContext.HIDE_NULL_COMPARISON_WARNING; } UnconditionalFlowInfo assertWhenTrueInfo = conditionFlowInfo.initsWhenTrue().unconditionalInits(); FlowInfo assertInfo = conditionFlowInfo.initsWhenFalse(); if (isOptimizedTrueAssertion) { assertInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD); } if (!isOptimizedFalseAssertion) { // if assertion is not false for sure, only then it makes sense to carry the flow info // ahead. // if the code does reach ahead, it means the assert didn't cause an exit, and so // the expression inside it shouldn't change the prior flowinfo // viz. org.eclipse.core.runtime.Assert.isLegal(false && o != null) // keep the merge from the initial code for the definite assignment // analysis, tweak the null part to influence nulls downstream flowInfo = flowInfo .mergedWith(assertInfo.nullInfoLessUnconditionalCopy()) .addInitializationsFrom(assertWhenTrueInfo.discardInitializationInfo()); } } else { flowInfo = argument.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits(); } if (analyseResources) { // if argument is an AutoCloseable insert info that it *may* be closed (by the target // method, i.e.) flowInfo = FakedTrackingVariable.markPassedToOutside(currentScope, argument, flowInfo, false); } } analyseArguments(currentScope, flowContext, flowInfo, this.binding, this.arguments); } ReferenceBinding[] thrownExceptions; if ((thrownExceptions = this.binding.thrownExceptions) != Binding.NO_EXCEPTIONS) { if ((this.bits & ASTNode.Unchecked) != 0 && this.genericTypeArguments == null) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=277643, align with javac on JLS 15.12.2.6 thrownExceptions = currentScope.environment().convertToRawTypes(this.binding.thrownExceptions, true, true); } // must verify that exceptions potentially thrown by this expression are caught in the method flowContext.checkExceptionHandlers(thrownExceptions, this, flowInfo.copy(), currentScope); // TODO (maxime) the copy above is needed because of a side effect into // checkExceptionHandlers; consider protecting there instead of here; // NullReferenceTest#test0510 } manageSyntheticAccessIfNecessary(currentScope, flowInfo); return flowInfo; }
public TypeBinding resolveType(BlockScope scope) { // Answer the signature return type // Base type promotion this.constant = Constant.NotAConstant; boolean receiverCast = false, argsContainCast = false; if (this.receiver instanceof CastExpression) { this.receiver.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on receiverCast = true; } this.actualReceiverType = this.receiver.resolveType(scope); boolean receiverIsType = this.receiver instanceof NameReference && (((NameReference) this.receiver).bits & Binding.TYPE) != 0; if (receiverCast && this.actualReceiverType != null) { // due to change of declaring class with receiver type, only identity cast should be notified if (((CastExpression) this.receiver).expression.resolvedType == this.actualReceiverType) { scope.problemReporter().unnecessaryCast((CastExpression) this.receiver); } } // resolve type arguments (for generic constructor call) if (this.typeArguments != null) { int length = this.typeArguments.length; boolean argHasError = scope.compilerOptions().sourceLevel < ClassFileConstants.JDK1_5; // typeChecks all arguments this.genericTypeArguments = new TypeBinding[length]; for (int i = 0; i < length; i++) { TypeReference typeReference = this.typeArguments[i]; if ((this.genericTypeArguments[i] = typeReference.resolveType(scope, true /* check bounds*/)) == null) { argHasError = true; } if (argHasError && typeReference instanceof Wildcard) { scope.problemReporter().illegalUsageOfWildcard(typeReference); } } if (argHasError) { if (this.arguments != null) { // still attempt to resolve arguments for (int i = 0, max = this.arguments.length; i < max; i++) { this.arguments[i].resolveType(scope); } } return null; } } // will check for null after args are resolved TypeBinding[] argumentTypes = Binding.NO_PARAMETERS; if (this.arguments != null) { boolean argHasError = false; // typeChecks all arguments int length = this.arguments.length; argumentTypes = new TypeBinding[length]; for (int i = 0; i < length; i++) { Expression argument = this.arguments[i]; if (argument instanceof CastExpression) { argument.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on argsContainCast = true; } if ((argumentTypes[i] = argument.resolveType(scope)) == null) { argHasError = true; } } if (argHasError) { if (this.actualReceiverType instanceof ReferenceBinding) { // record a best guess, for clients who need hint about possible method match TypeBinding[] pseudoArgs = new TypeBinding[length]; for (int i = length; --i >= 0; ) pseudoArgs[i] = argumentTypes[i] == null ? TypeBinding.NULL : argumentTypes[i]; // replace args with errors with null type this.binding = this.receiver.isImplicitThis() ? scope.getImplicitMethod(this.selector, pseudoArgs, this) : scope.findMethod( (ReferenceBinding) this.actualReceiverType, this.selector, pseudoArgs, this); if (this.binding != null && !this.binding.isValidBinding()) { MethodBinding closestMatch = ((ProblemMethodBinding) this.binding).closestMatch; // record the closest match, for clients who may still need hint about possible method // match if (closestMatch != null) { if (closestMatch.original().typeVariables != Binding.NO_TYPE_VARIABLES) { // generic method // shouldn't return generic method outside its context, rather convert it to raw // method (175409) closestMatch = scope .environment() .createParameterizedGenericMethod( closestMatch.original(), (RawTypeBinding) null); } this.binding = closestMatch; MethodBinding closestMatchOriginal = closestMatch.original(); if (closestMatchOriginal.isOrEnclosedByPrivateType() && !scope.isDefinedInMethod(closestMatchOriginal)) { // ignore cases where method is used from within inside itself (e.g. direct // recursions) closestMatchOriginal.modifiers |= ExtraCompilerModifiers.AccLocallyUsed; } } } } return null; } } if (this.actualReceiverType == null) { return null; } // base type cannot receive any message if (this.actualReceiverType.isBaseType()) { scope.problemReporter().errorNoMethodFor(this, this.actualReceiverType, argumentTypes); return null; } this.binding = this.receiver.isImplicitThis() ? scope.getImplicitMethod(this.selector, argumentTypes, this) : scope.getMethod(this.actualReceiverType, this.selector, argumentTypes, this); if (!this.binding.isValidBinding()) { if (this.binding.declaringClass == null) { if (this.actualReceiverType instanceof ReferenceBinding) { this.binding.declaringClass = (ReferenceBinding) this.actualReceiverType; } else { scope.problemReporter().errorNoMethodFor(this, this.actualReceiverType, argumentTypes); return null; } } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=245007 avoid secondary errors in case of // missing super type for anonymous classes ... ReferenceBinding declaringClass = this.binding.declaringClass; boolean avoidSecondary = declaringClass != null && declaringClass.isAnonymousType() && declaringClass.superclass() instanceof MissingTypeBinding; if (!avoidSecondary) scope.problemReporter().invalidMethod(this, this.binding); MethodBinding closestMatch = ((ProblemMethodBinding) this.binding).closestMatch; switch (this.binding.problemId()) { case ProblemReasons.Ambiguous: break; // no resilience on ambiguous case ProblemReasons.NotVisible: case ProblemReasons.NonStaticReferenceInConstructorInvocation: case ProblemReasons.NonStaticReferenceInStaticContext: case ProblemReasons.ReceiverTypeNotVisible: case ProblemReasons.ParameterBoundMismatch: // only steal returnType in cases listed above if (closestMatch != null) this.resolvedType = closestMatch.returnType; break; } // record the closest match, for clients who may still need hint about possible method match if (closestMatch != null) { this.binding = closestMatch; MethodBinding closestMatchOriginal = closestMatch.original(); if (closestMatchOriginal.isOrEnclosedByPrivateType() && !scope.isDefinedInMethod(closestMatchOriginal)) { // ignore cases where method is used from within inside itself (e.g. direct recursions) closestMatchOriginal.modifiers |= ExtraCompilerModifiers.AccLocallyUsed; } } return (this.resolvedType != null && (this.resolvedType.tagBits & TagBits.HasMissingType) == 0) ? this.resolvedType : null; } final CompilerOptions compilerOptions = scope.compilerOptions(); if (compilerOptions.complianceLevel <= ClassFileConstants.JDK1_6 && this.binding.isPolymorphic()) { scope.problemReporter().polymorphicMethodNotBelow17(this); return null; } if (((this.bits & ASTNode.InsideExpressionStatement) != 0) && this.binding.isPolymorphic()) { // we only set the return type to be void if this method invocation is used inside an // expression statement this.binding = scope .environment() .updatePolymorphicMethodReturnType( (PolymorphicMethodBinding) this.binding, TypeBinding.VOID); } if ((this.binding.tagBits & TagBits.HasMissingType) != 0) { scope.problemReporter().missingTypeInMethod(this, this.binding); } if (!this.binding.isStatic()) { // the "receiver" must not be a type if (receiverIsType) { scope.problemReporter().mustUseAStaticMethod(this, this.binding); if (this.actualReceiverType.isRawType() && (this.receiver.bits & ASTNode.IgnoreRawTypeCheck) == 0 && compilerOptions.getSeverity(CompilerOptions.RawTypeReference) != ProblemSeverities.Ignore) { scope.problemReporter().rawTypeReference(this.receiver, this.actualReceiverType); } } else { // handle indirect inheritance thru variable secondary bound // receiver may receive generic cast, as part of implicit conversion TypeBinding oldReceiverType = this.actualReceiverType; this.actualReceiverType = this.actualReceiverType.getErasureCompatibleType(this.binding.declaringClass); this.receiver.computeConversion(scope, this.actualReceiverType, this.actualReceiverType); if (this.actualReceiverType != oldReceiverType && this.receiver.postConversionType(scope) != this .actualReceiverType) { // record need for explicit cast at codegen since // receiver could not handle it this.bits |= NeedReceiverGenericCast; } } } else { // static message invoked through receiver? legal but unoptimal (optional warning). if (!(this.receiver.isImplicitThis() || this.receiver.isSuper() || receiverIsType)) { scope.problemReporter().nonStaticAccessToStaticMethod(this, this.binding); } if (!this.receiver.isImplicitThis() && this.binding.declaringClass != this.actualReceiverType) { scope.problemReporter().indirectAccessToStaticMethod(this, this.binding); } } if (checkInvocationArguments( scope, this.receiver, this.actualReceiverType, this.binding, this.arguments, argumentTypes, argsContainCast, this)) { this.bits |= ASTNode.Unchecked; } // -------message send that are known to fail at compile time----------- if (this.binding.isAbstract()) { if (this.receiver.isSuper()) { scope.problemReporter().cannotDireclyInvokeAbstractMethod(this, this.binding); } // abstract private methods cannot occur nor abstract static............ } if (isMethodUseDeprecated(this.binding, scope, true)) scope.problemReporter().deprecatedMethod(this.binding, this); // from 1.5 source level on, array#clone() returns the array type (but binding still shows // Object) if (this.binding == scope.environment().arrayClone && compilerOptions.sourceLevel >= ClassFileConstants.JDK1_5) { this.resolvedType = this.actualReceiverType; } else { TypeBinding returnType; if ((this.bits & ASTNode.Unchecked) != 0 && this.genericTypeArguments == null) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=277643, align with javac on JLS 15.12.2.6 returnType = this.binding.returnType; if (returnType != null) { returnType = scope.environment().convertToRawType(returnType.erasure(), true); } } else { returnType = this.binding.returnType; if (returnType != null) { returnType = returnType.capture(scope, this.sourceEnd); } } this.resolvedType = returnType; } if (this.receiver.isSuper() && compilerOptions.getSeverity(CompilerOptions.OverridingMethodWithoutSuperInvocation) != ProblemSeverities.Ignore) { final ReferenceContext referenceContext = scope.methodScope().referenceContext; if (referenceContext instanceof AbstractMethodDeclaration) { final AbstractMethodDeclaration abstractMethodDeclaration = (AbstractMethodDeclaration) referenceContext; MethodBinding enclosingMethodBinding = abstractMethodDeclaration.binding; if (enclosingMethodBinding.isOverriding() && CharOperation.equals(this.binding.selector, enclosingMethodBinding.selector) && this.binding.areParametersEqual(enclosingMethodBinding)) { abstractMethodDeclaration.bits |= ASTNode.OverridingMethodWithSupercall; } } } if (this.typeArguments != null && this.binding.original().typeVariables == Binding.NO_TYPE_VARIABLES) { scope .problemReporter() .unnecessaryTypeArgumentsForMethodInvocation( this.binding, this.genericTypeArguments, this.typeArguments); } return (this.resolvedType.tagBits & TagBits.HasMissingType) == 0 ? this.resolvedType : null; }
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { // Consider the try block and catch block so as to compute the intersection of initializations // and // the minimum exit relative depth amongst all of them. Then consider the subroutine, and append // its // initialization to the try/catch ones, if the subroutine completes normally. If the subroutine // does not // complete, then only keep this result for the rest of the analysis // process the finally block (subroutine) - create a context for the subroutine this.preTryInitStateIndex = currentScope.methodScope().recordInitializationStates(flowInfo); if (this.anyExceptionVariable != null) { this.anyExceptionVariable.useFlag = LocalVariableBinding.USED; } if (this.returnAddressVariable != null) { // TODO (philippe) if subroutine is escaping, unused this.returnAddressVariable.useFlag = LocalVariableBinding.USED; } if (this.subRoutineStartLabel == null) { // no finally block -- this is a simplified copy of the else part // process the try block in a context handling the local exceptions. ExceptionHandlingFlowContext handlingContext = new ExceptionHandlingFlowContext( flowContext, this, this.caughtExceptionTypes, null, this.scope, flowInfo.unconditionalInits()); handlingContext.initsOnFinally = new NullInfoRegistry(flowInfo.unconditionalInits()); // only try blocks initialize that member - may consider creating a // separate class if needed FlowInfo tryInfo; if (this.tryBlock.isEmptyBlock()) { tryInfo = flowInfo; } else { tryInfo = this.tryBlock.analyseCode(currentScope, handlingContext, flowInfo.copy()); if ((tryInfo.tagBits & FlowInfo.UNREACHABLE) != 0) this.bits |= ASTNode.IsTryBlockExiting; } // check unreachable catch blocks handlingContext.complainIfUnusedExceptionHandlers(this.scope, this); // process the catch blocks - computing the minimal exit depth amongst try/catch if (this.catchArguments != null) { int catchCount; this.catchExits = new boolean[catchCount = this.catchBlocks.length]; this.catchExitInitStateIndexes = new int[catchCount]; for (int i = 0; i < catchCount; i++) { // keep track of the inits that could potentially have led to this exception handler (for // final assignments diagnosis) FlowInfo catchInfo; if (this.caughtExceptionTypes[i].isUncheckedException(true)) { catchInfo = handlingContext.initsOnFinally.mitigateNullInfoOf( flowInfo .unconditionalCopy() .addPotentialInitializationsFrom( handlingContext.initsOnException(this.caughtExceptionTypes[i])) .addPotentialInitializationsFrom(tryInfo) .addPotentialInitializationsFrom(handlingContext.initsOnReturn)); } else { FlowInfo initsOnException = handlingContext.initsOnException(this.caughtExceptionTypes[i]); catchInfo = flowInfo .nullInfoLessUnconditionalCopy() .addPotentialInitializationsFrom(initsOnException) .addNullInfoFrom( initsOnException) // null info only from here, this is the only way to enter // the catch block .addPotentialInitializationsFrom(tryInfo.nullInfoLessUnconditionalCopy()) .addPotentialInitializationsFrom( handlingContext.initsOnReturn.nullInfoLessUnconditionalCopy()); } // catch var is always set LocalVariableBinding catchArg = this.catchArguments[i].binding; catchInfo.markAsDefinitelyAssigned(catchArg); catchInfo.markAsDefinitelyNonNull(catchArg); /* "If we are about to consider an unchecked exception handler, potential inits may have occured inside the try block that need to be detected , e.g. try { x = 1; throwSomething();} catch(Exception e){ x = 2} " "(uncheckedExceptionTypes notNil and: [uncheckedExceptionTypes at: index]) ifTrue: [catchInits addPotentialInitializationsFrom: tryInits]." */ if (this.tryBlock.statements == null) { catchInfo.setReachMode(FlowInfo.UNREACHABLE); } catchInfo = this.catchBlocks[i].analyseCode(currentScope, flowContext, catchInfo); this.catchExitInitStateIndexes[i] = currentScope.methodScope().recordInitializationStates(catchInfo); this.catchExits[i] = (catchInfo.tagBits & FlowInfo.UNREACHABLE) != 0; tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits()); } } this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(tryInfo); // chain up null info registry if (flowContext.initsOnFinally != null) { flowContext.initsOnFinally.add(handlingContext.initsOnFinally); } return tryInfo; } else { InsideSubRoutineFlowContext insideSubContext; FinallyFlowContext finallyContext; UnconditionalFlowInfo subInfo; // analyse finally block first insideSubContext = new InsideSubRoutineFlowContext(flowContext, this); subInfo = this.finallyBlock .analyseCode( currentScope, finallyContext = new FinallyFlowContext(flowContext, this.finallyBlock), flowInfo.nullInfoLessUnconditionalCopy()) .unconditionalInits(); if (subInfo == FlowInfo.DEAD_END) { this.bits |= ASTNode.IsSubRoutineEscaping; this.scope.problemReporter().finallyMustCompleteNormally(this.finallyBlock); } this.subRoutineInits = subInfo; // process the try block in a context handling the local exceptions. ExceptionHandlingFlowContext handlingContext = new ExceptionHandlingFlowContext( insideSubContext, this, this.caughtExceptionTypes, null, this.scope, flowInfo.unconditionalInits()); handlingContext.initsOnFinally = new NullInfoRegistry(flowInfo.unconditionalInits()); // only try blocks initialize that member - may consider creating a // separate class if needed FlowInfo tryInfo; if (this.tryBlock.isEmptyBlock()) { tryInfo = flowInfo; } else { tryInfo = this.tryBlock.analyseCode(currentScope, handlingContext, flowInfo.copy()); if ((tryInfo.tagBits & FlowInfo.UNREACHABLE) != 0) this.bits |= ASTNode.IsTryBlockExiting; } // check unreachable catch blocks handlingContext.complainIfUnusedExceptionHandlers(this.scope, this); // process the catch blocks - computing the minimal exit depth amongst try/catch if (this.catchArguments != null) { int catchCount; this.catchExits = new boolean[catchCount = this.catchBlocks.length]; this.catchExitInitStateIndexes = new int[catchCount]; for (int i = 0; i < catchCount; i++) { // keep track of the inits that could potentially have led to this exception handler (for // final assignments diagnosis) FlowInfo catchInfo; if (this.caughtExceptionTypes[i].isUncheckedException(true)) { catchInfo = handlingContext.initsOnFinally.mitigateNullInfoOf( flowInfo .unconditionalCopy() .addPotentialInitializationsFrom( handlingContext.initsOnException(this.caughtExceptionTypes[i])) .addPotentialInitializationsFrom(tryInfo) .addPotentialInitializationsFrom(handlingContext.initsOnReturn)); } else { FlowInfo initsOnException = handlingContext.initsOnException(this.caughtExceptionTypes[i]); catchInfo = flowInfo .nullInfoLessUnconditionalCopy() .addPotentialInitializationsFrom(initsOnException) .addNullInfoFrom( initsOnException) // null info only from here, this is the only way to enter // the catch block .addPotentialInitializationsFrom(tryInfo.nullInfoLessUnconditionalCopy()) .addPotentialInitializationsFrom( handlingContext.initsOnReturn.nullInfoLessUnconditionalCopy()); } // catch var is always set LocalVariableBinding catchArg = this.catchArguments[i].binding; catchInfo.markAsDefinitelyAssigned(catchArg); catchInfo.markAsDefinitelyNonNull(catchArg); /* "If we are about to consider an unchecked exception handler, potential inits may have occured inside the try block that need to be detected , e.g. try { x = 1; throwSomething();} catch(Exception e){ x = 2} " "(uncheckedExceptionTypes notNil and: [uncheckedExceptionTypes at: index]) ifTrue: [catchInits addPotentialInitializationsFrom: tryInits]." */ if (this.tryBlock.statements == null) { catchInfo.setReachMode(FlowInfo.UNREACHABLE); } catchInfo = this.catchBlocks[i].analyseCode(currentScope, insideSubContext, catchInfo); this.catchExitInitStateIndexes[i] = currentScope.methodScope().recordInitializationStates(catchInfo); this.catchExits[i] = (catchInfo.tagBits & FlowInfo.UNREACHABLE) != 0; tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits()); } } // we also need to check potential multiple assignments of final variables inside the finally // block // need to include potential inits from returns inside the try/catch parts - 1GK2AOF finallyContext.complainOnDeferredChecks( handlingContext.initsOnFinally.mitigateNullInfoOf( (tryInfo.tagBits & FlowInfo.UNREACHABLE) == 0 ? flowInfo .unconditionalCopy() .addPotentialInitializationsFrom(tryInfo) . // lighten the influence of the try block, which may have // exited at any point addPotentialInitializationsFrom(insideSubContext.initsOnReturn) : insideSubContext.initsOnReturn), currentScope); // chain up null info registry if (flowContext.initsOnFinally != null) { flowContext.initsOnFinally.add(handlingContext.initsOnFinally); } this.naturalExitMergeInitStateIndex = currentScope.methodScope().recordInitializationStates(tryInfo); if (subInfo == FlowInfo.DEAD_END) { this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(subInfo); return subInfo; } else { FlowInfo mergedInfo = tryInfo.addInitializationsFrom(subInfo); this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); return mergedInfo; } } }