public void generateCode(ClassFile classFile) { classFile.generateMethodInfoHeader(this.binding); int methodAttributeOffset = classFile.contentsOffset; int attributeNumber = classFile.generateMethodInfoAttributes(this.binding); if ((!this.binding.isNative()) && (!this.binding.isAbstract())) { int codeAttributeOffset = classFile.contentsOffset; classFile.generateCodeAttributeHeader(); CodeStream codeStream = classFile.codeStream; codeStream.reset(this, classFile); // initialize local positions this.scope.computeLocalVariablePositions(this.binding.isStatic() ? 0 : 1, codeStream); // arguments initialization for local variable debug attributes if (this.arguments != null) { for (int i = 0, max = this.arguments.length; i < max; i++) { LocalVariableBinding argBinding; codeStream.addVisibleLocalVariable(argBinding = this.arguments[i].binding); argBinding.recordInitializationStartPC(0); } } if (this.statements != null) { for (int i = 0, max = this.statements.length; i < max; i++) this.statements[i].generateCode(this.scope, codeStream); } // if a problem got reported during code gen, then trigger problem method creation if (this.ignoreFurtherInvestigation) { throw new AbortMethod(this.scope.referenceCompilationUnit().compilationResult, null); } if ((this.bits & ASTNode.NeedFreeReturn) != 0) { codeStream.return_(); } // local variable attributes codeStream.exitUserScope(this.scope); codeStream.recordPositionsFrom(0, this.declarationSourceEnd); try { classFile.completeCodeAttribute(codeAttributeOffset); } catch (NegativeArraySizeException e) { throw new AbortMethod(this.scope.referenceCompilationUnit().compilationResult, null); } attributeNumber++; } else { checkArgumentsSize(); } classFile.completeMethodInfo(this.binding, methodAttributeOffset, attributeNumber); }
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; } }
/** * Try statement code generation with or without jsr bytecode use post 1.5 target level, cannot * use jsr bytecode, must instead inline finally block returnAddress is only allocated if jsr is * allowed */ public void generateCode(BlockScope currentScope, CodeStream codeStream) { if ((bits & IsReachableMASK) == 0) { return; } // in case the labels needs to be reinitialized // when the code generation is restarted in wide mode if (this.anyExceptionLabelsCount > 0) { this.anyExceptionLabels = NO_EXCEPTION_HANDLER; this.anyExceptionLabelsCount = 0; } int pc = codeStream.position; final int NO_FINALLY = 0; // no finally block final int FINALLY_SUBROUTINE = 1; // finally is generated as a subroutine (using jsr/ret bytecodes) final int FINALLY_DOES_NOT_COMPLETE = 2; // non returning finally is optimized with only one instance of finally block final int FINALLY_MUST_BE_INLINED = 3; // finally block must be inlined since cannot use jsr/ret bytecodes >1.5 int finallyMode; if (subRoutineStartLabel == null) { finallyMode = NO_FINALLY; } else { if (this.isSubRoutineEscaping) { finallyMode = FINALLY_DOES_NOT_COMPLETE; } else if (scope.compilerOptions().inlineJsrBytecode) { finallyMode = FINALLY_MUST_BE_INLINED; } else { finallyMode = FINALLY_SUBROUTINE; } } boolean requiresNaturalExit = false; // preparing exception labels int maxCatches; ExceptionLabel[] exceptionLabels = new ExceptionLabel[maxCatches = catchArguments == null ? 0 : catchArguments.length]; for (int i = 0; i < maxCatches; i++) { exceptionLabels[i] = new ExceptionLabel(codeStream, catchArguments[i].binding.type); } if (subRoutineStartLabel != null) { subRoutineStartLabel.initialize(codeStream); this.enterAnyExceptionHandler(codeStream); } // generate the try block tryBlock.generateCode(scope, codeStream); boolean tryBlockHasSomeCode = codeStream.position != pc; // flag telling if some bytecodes were issued inside the try block // place end positions of user-defined exception labels if (tryBlockHasSomeCode) { // natural exit may require subroutine invocation (if finally != null) Label naturalExitLabel = new Label(codeStream); if (!tryBlockExit) { int position = codeStream.position; switch (finallyMode) { case FINALLY_SUBROUTINE: case FINALLY_MUST_BE_INLINED: requiresNaturalExit = true; // fall through case NO_FINALLY: codeStream.goto_(naturalExitLabel); break; case FINALLY_DOES_NOT_COMPLETE: codeStream.goto_(subRoutineStartLabel); break; } codeStream.updateLastRecordedEndPC(tryBlock.scope, position); // goto is tagged as part of the try block } for (int i = 0; i < maxCatches; i++) { exceptionLabels[i].placeEnd(); } /* generate sequence of handler, all starting by storing the TOS (exception thrown) into their own catch variables, the one specified in the source that must denote the handled exception. */ if (catchArguments != null) { for (int i = 0; i < maxCatches; i++) { // May loose some local variable initializations : affecting the local variable attributes if (preTryInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables(currentScope, preTryInitStateIndex); } exceptionLabels[i].place(); codeStream.incrStackSize(1); // optimizing the case where the exception variable is not actually used LocalVariableBinding catchVar; int varPC = codeStream.position; if ((catchVar = catchArguments[i].binding).resolvedPosition != -1) { codeStream.store(catchVar, false); catchVar.recordInitializationStartPC(codeStream.position); codeStream.addVisibleLocalVariable(catchVar); } else { codeStream.pop(); } codeStream.recordPositionsFrom(varPC, catchArguments[i].sourceStart); // Keep track of the pcs at diverging point for computing the local attribute // since not passing the catchScope, the block generation will exitUserScope(catchScope) catchBlocks[i].generateCode(scope, codeStream); if (!catchExits[i]) { switch (finallyMode) { case FINALLY_SUBROUTINE: case FINALLY_MUST_BE_INLINED: requiresNaturalExit = true; // fall through case NO_FINALLY: codeStream.goto_(naturalExitLabel); break; case FINALLY_DOES_NOT_COMPLETE: codeStream.goto_(subRoutineStartLabel); break; } } } } this.exitAnyExceptionHandler(); // extra handler for trailing natural exit (will be fixed up later on when natural exit is // generated below) ExceptionLabel naturalExitExceptionHandler = finallyMode == FINALLY_SUBROUTINE && requiresNaturalExit ? new ExceptionLabel(codeStream, null) : null; // addition of a special handler so as to ensure that any uncaught exception (or exception // thrown // inside catch blocks) will run the finally block int finallySequenceStartPC = codeStream.position; if (subRoutineStartLabel != null) { this.placeAllAnyExceptionHandlers(); if (naturalExitExceptionHandler != null) naturalExitExceptionHandler.place(); if (preTryInitStateIndex != -1) { // reset initialization state, as for a normal catch block codeStream.removeNotDefinitelyAssignedVariables(currentScope, preTryInitStateIndex); } codeStream.incrStackSize(1); switch (finallyMode) { case FINALLY_SUBROUTINE: codeStream.store(anyExceptionVariable, false); codeStream.jsr(subRoutineStartLabel); codeStream.recordPositionsFrom(finallySequenceStartPC, finallyBlock.sourceStart); int position = codeStream.position; codeStream.load(anyExceptionVariable); codeStream.athrow(); codeStream.recordPositionsFrom(position, finallyBlock.sourceEnd); subRoutineStartLabel.place(); codeStream.incrStackSize(1); position = codeStream.position; codeStream.store(returnAddressVariable, false); codeStream.recordPositionsFrom(position, finallyBlock.sourceStart); finallyBlock.generateCode(scope, codeStream); position = codeStream.position; codeStream.ret(returnAddressVariable.resolvedPosition); // codeStream.updateLastRecordedEndPC(position); codeStream.recordPositionsFrom(position, finallyBlock.sourceEnd); // the ret bytecode is part of the subroutine break; case FINALLY_MUST_BE_INLINED: codeStream.store(anyExceptionVariable, false); codeStream.recordPositionsFrom(finallySequenceStartPC, finallyBlock.sourceStart); this.finallyBlock.generateCode(currentScope, codeStream); position = codeStream.position; codeStream.load(anyExceptionVariable); codeStream.athrow(); subRoutineStartLabel.place(); codeStream.recordPositionsFrom(position, finallyBlock.sourceEnd); break; case FINALLY_DOES_NOT_COMPLETE: codeStream.pop(); subRoutineStartLabel.place(); codeStream.recordPositionsFrom(finallySequenceStartPC, finallyBlock.sourceStart); finallyBlock.generateCode(scope, codeStream); break; } // will naturally fall into subsequent code after subroutine invocation naturalExitLabel.place(); if (requiresNaturalExit) { switch (finallyMode) { case FINALLY_SUBROUTINE: int position = codeStream.position; // fix up natural exit handler naturalExitExceptionHandler.placeStart(); codeStream.jsr(subRoutineStartLabel); naturalExitExceptionHandler.placeEnd(); codeStream.recordPositionsFrom(position, finallyBlock.sourceEnd); break; case FINALLY_MUST_BE_INLINED: // May loose some local variable initializations : affecting the local variable // attributes // needed since any exception handler got inlined subroutine if (preTryInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables(currentScope, preTryInitStateIndex); } // entire sequence for finally is associated to finally block finallyBlock.generateCode(scope, codeStream); break; case FINALLY_DOES_NOT_COMPLETE: break; } } } else { // no subroutine, simply position end label (natural exit == end) naturalExitLabel.place(); } } else { // try block had no effect, only generate the body of the finally block if any if (subRoutineStartLabel != null) { finallyBlock.generateCode(scope, codeStream); } } // May loose some local variable initializations : affecting the local variable attributes if (mergedInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables(currentScope, mergedInitStateIndex); codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex); } codeStream.recordPositionsFrom(pc, this.sourceStart); }
/** Figures if @Deprecated annotation is specified, do not resolve entire annotations. */ public static void resolveDeprecatedAnnotations( BlockScope scope, Annotation[] annotations, Binding recipient) { if (recipient != null) { int kind = recipient.kind(); if (annotations != null) { int length; if ((length = annotations.length) >= 0) { switch (kind) { case Binding.PACKAGE: PackageBinding packageBinding = (PackageBinding) recipient; if ((packageBinding.tagBits & TagBits.DeprecatedAnnotationResolved) != 0) return; break; case Binding.TYPE: case Binding.GENERIC_TYPE: ReferenceBinding type = (ReferenceBinding) recipient; if ((type.tagBits & TagBits.DeprecatedAnnotationResolved) != 0) return; break; case Binding.METHOD: MethodBinding method = (MethodBinding) recipient; if ((method.tagBits & TagBits.DeprecatedAnnotationResolved) != 0) return; break; case Binding.FIELD: FieldBinding field = (FieldBinding) recipient; if ((field.tagBits & TagBits.DeprecatedAnnotationResolved) != 0) return; break; case Binding.LOCAL: LocalVariableBinding local = (LocalVariableBinding) recipient; if ((local.tagBits & TagBits.DeprecatedAnnotationResolved) != 0) return; break; default: return; } for (int i = 0; i < length; i++) { TypeReference annotationTypeRef = annotations[i].type; // only resolve type name if 'Deprecated' last token if (!CharOperation.equals( TypeConstants.JAVA_LANG_DEPRECATED[2], annotationTypeRef.getLastToken())) return; TypeBinding annotationType = annotations[i].type.resolveType(scope); if (annotationType != null && annotationType.isValidBinding() && annotationType.id == TypeIds.T_JavaLangDeprecated) { switch (kind) { case Binding.PACKAGE: PackageBinding packageBinding = (PackageBinding) recipient; packageBinding.tagBits |= (TagBits.AnnotationDeprecated | TagBits.DeprecatedAnnotationResolved); return; case Binding.TYPE: case Binding.GENERIC_TYPE: case Binding.TYPE_PARAMETER: ReferenceBinding type = (ReferenceBinding) recipient; type.tagBits |= (TagBits.AnnotationDeprecated | TagBits.DeprecatedAnnotationResolved); return; case Binding.METHOD: MethodBinding method = (MethodBinding) recipient; method.tagBits |= (TagBits.AnnotationDeprecated | TagBits.DeprecatedAnnotationResolved); return; case Binding.FIELD: FieldBinding field = (FieldBinding) recipient; field.tagBits |= (TagBits.AnnotationDeprecated | TagBits.DeprecatedAnnotationResolved); return; case Binding.LOCAL: LocalVariableBinding local = (LocalVariableBinding) recipient; local.tagBits |= (TagBits.AnnotationDeprecated | TagBits.DeprecatedAnnotationResolved); return; default: return; } } } } } switch (kind) { case Binding.PACKAGE: PackageBinding packageBinding = (PackageBinding) recipient; packageBinding.tagBits |= TagBits.DeprecatedAnnotationResolved; return; case Binding.TYPE: case Binding.GENERIC_TYPE: case Binding.TYPE_PARAMETER: ReferenceBinding type = (ReferenceBinding) recipient; type.tagBits |= TagBits.DeprecatedAnnotationResolved; return; case Binding.METHOD: MethodBinding method = (MethodBinding) recipient; method.tagBits |= TagBits.DeprecatedAnnotationResolved; return; case Binding.FIELD: FieldBinding field = (FieldBinding) recipient; field.tagBits |= TagBits.DeprecatedAnnotationResolved; return; case Binding.LOCAL: LocalVariableBinding local = (LocalVariableBinding) recipient; local.tagBits |= TagBits.DeprecatedAnnotationResolved; return; default: return; } } }
/** * Resolve annotations, and check duplicates, answers combined tagBits for recognized standard * annotations */ public static void resolveAnnotations( BlockScope scope, Annotation[] sourceAnnotations, Binding recipient) { AnnotationBinding[] annotations = null; int length = sourceAnnotations == null ? 0 : sourceAnnotations.length; if (recipient != null) { switch (recipient.kind()) { case Binding.PACKAGE: PackageBinding packageBinding = (PackageBinding) recipient; if ((packageBinding.tagBits & TagBits.AnnotationResolved) != 0) return; packageBinding.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved); break; case Binding.TYPE: case Binding.GENERIC_TYPE: ReferenceBinding type = (ReferenceBinding) recipient; if ((type.tagBits & TagBits.AnnotationResolved) != 0) return; type.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved); if (length > 0) { annotations = new AnnotationBinding[length]; type.setAnnotations(annotations); } break; case Binding.METHOD: MethodBinding method = (MethodBinding) recipient; if ((method.tagBits & TagBits.AnnotationResolved) != 0) return; method.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved); if (length > 0) { annotations = new AnnotationBinding[length]; method.setAnnotations(annotations); } break; case Binding.FIELD: FieldBinding field = (FieldBinding) recipient; if ((field.tagBits & TagBits.AnnotationResolved) != 0) return; field.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved); if (length > 0) { annotations = new AnnotationBinding[length]; field.setAnnotations(annotations); } break; case Binding.LOCAL: LocalVariableBinding local = (LocalVariableBinding) recipient; if ((local.tagBits & TagBits.AnnotationResolved) != 0) return; local.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved); if (length > 0) { annotations = new AnnotationBinding[length]; local.setAnnotations(annotations); } break; default: return; } } if (sourceAnnotations == null) return; for (int i = 0; i < length; i++) { Annotation annotation = sourceAnnotations[i]; final Binding annotationRecipient = annotation.recipient; if (annotationRecipient != null && recipient != null) { // only local and field can share annnotations switch (recipient.kind()) { case Binding.FIELD: FieldBinding field = (FieldBinding) recipient; field.tagBits = ((FieldBinding) annotationRecipient).tagBits; break; case Binding.LOCAL: LocalVariableBinding local = (LocalVariableBinding) recipient; local.tagBits = ((LocalVariableBinding) annotationRecipient).tagBits; break; } if (annotations != null) { // need to fill the instances array annotations[0] = annotation.getCompilerAnnotation(); for (int j = 1; j < length; j++) { Annotation annot = sourceAnnotations[j]; annotations[j] = annot.getCompilerAnnotation(); } } return; } else { annotation.recipient = recipient; annotation.resolveType(scope); // null if receiver is a package binding if (annotations != null) { annotations[i] = annotation.getCompilerAnnotation(); } } } // check duplicate annotations if (annotations != null) { AnnotationBinding[] distinctAnnotations = annotations; // only copy after 1st duplicate is detected for (int i = 0; i < length; i++) { AnnotationBinding annotation = distinctAnnotations[i]; if (annotation == null) continue; TypeBinding annotationType = annotation.getAnnotationType(); boolean foundDuplicate = false; for (int j = i + 1; j < length; j++) { AnnotationBinding otherAnnotation = distinctAnnotations[j]; if (otherAnnotation == null) continue; if (otherAnnotation.getAnnotationType() == annotationType) { foundDuplicate = true; if (distinctAnnotations == annotations) { System.arraycopy( distinctAnnotations, 0, distinctAnnotations = new AnnotationBinding[length], 0, length); } distinctAnnotations[j] = null; // report it only once scope.problemReporter().duplicateAnnotation(sourceAnnotations[j]); } } if (foundDuplicate) { scope.problemReporter().duplicateAnnotation(sourceAnnotations[i]); } } } }
/** * Try statement code generation with or without jsr bytecode use post 1.5 target level, cannot * use jsr bytecode, must instead inline finally block returnAddress is only allocated if jsr is * allowed */ public void generateCode(BlockScope currentScope, CodeStream codeStream) { if ((this.bits & ASTNode.IsReachable) == 0) { return; } boolean isStackMapFrameCodeStream = codeStream instanceof StackMapFrameCodeStream; // in case the labels needs to be reinitialized // when the code generation is restarted in wide mode this.anyExceptionLabel = null; this.reusableJSRTargets = null; this.reusableJSRSequenceStartLabels = null; this.reusableJSRTargetsCount = 0; int pc = codeStream.position; int finallyMode = finallyMode(); boolean requiresNaturalExit = false; // preparing exception labels int maxCatches = this.catchArguments == null ? 0 : this.catchArguments.length; ExceptionLabel[] exceptionLabels; if (maxCatches > 0) { exceptionLabels = new ExceptionLabel[maxCatches]; for (int i = 0; i < maxCatches; i++) { ExceptionLabel exceptionLabel = new ExceptionLabel(codeStream, this.catchArguments[i].binding.type); exceptionLabel.placeStart(); exceptionLabels[i] = exceptionLabel; } } else { exceptionLabels = null; } if (this.subRoutineStartLabel != null) { this.subRoutineStartLabel.initialize(codeStream); enterAnyExceptionHandler(codeStream); } // generate the try block try { this.declaredExceptionLabels = exceptionLabels; this.tryBlock.generateCode(this.scope, codeStream); } finally { this.declaredExceptionLabels = null; } boolean tryBlockHasSomeCode = codeStream.position != pc; // flag telling if some bytecodes were issued inside the try block // place end positions of user-defined exception labels if (tryBlockHasSomeCode) { // natural exit may require subroutine invocation (if finally != null) BranchLabel naturalExitLabel = new BranchLabel(codeStream); BranchLabel postCatchesFinallyLabel = null; for (int i = 0; i < maxCatches; i++) { exceptionLabels[i].placeEnd(); } if ((this.bits & ASTNode.IsTryBlockExiting) == 0) { int position = codeStream.position; switch (finallyMode) { case FINALLY_SUBROUTINE: case FINALLY_INLINE: requiresNaturalExit = true; if (this.naturalExitMergeInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables( currentScope, this.naturalExitMergeInitStateIndex); codeStream.addDefinitelyAssignedVariables( currentScope, this.naturalExitMergeInitStateIndex); } codeStream.goto_(naturalExitLabel); break; case NO_FINALLY: if (this.naturalExitMergeInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables( currentScope, this.naturalExitMergeInitStateIndex); codeStream.addDefinitelyAssignedVariables( currentScope, this.naturalExitMergeInitStateIndex); } codeStream.goto_(naturalExitLabel); break; case FINALLY_DOES_NOT_COMPLETE: codeStream.goto_(this.subRoutineStartLabel); break; } codeStream.updateLastRecordedEndPC(this.tryBlock.scope, position); // goto is tagged as part of the try block } /* generate sequence of handler, all starting by storing the TOS (exception thrown) into their own catch variables, the one specified in the source that must denote the handled exception. */ exitAnyExceptionHandler(); if (this.catchArguments != null) { postCatchesFinallyLabel = new BranchLabel(codeStream); for (int i = 0; i < maxCatches; i++) { /* * This should not happen. For consistency purpose, if the exception label is never used * we also don't generate the corresponding catch block, otherwise we have some * unreachable bytecodes */ if (exceptionLabels[i].count == 0) continue; enterAnyExceptionHandler(codeStream); // May loose some local variable initializations : affecting the local variable attributes if (this.preTryInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables( currentScope, this.preTryInitStateIndex); codeStream.addDefinitelyAssignedVariables(currentScope, this.preTryInitStateIndex); } codeStream.pushExceptionOnStack(exceptionLabels[i].exceptionType); exceptionLabels[i].place(); // optimizing the case where the exception variable is not actually used LocalVariableBinding catchVar; int varPC = codeStream.position; if ((catchVar = this.catchArguments[i].binding).resolvedPosition != -1) { codeStream.store(catchVar, false); catchVar.recordInitializationStartPC(codeStream.position); codeStream.addVisibleLocalVariable(catchVar); } else { codeStream.pop(); } codeStream.recordPositionsFrom(varPC, this.catchArguments[i].sourceStart); // Keep track of the pcs at diverging point for computing the local attribute // since not passing the catchScope, the block generation will exitUserScope(catchScope) this.catchBlocks[i].generateCode(this.scope, codeStream); exitAnyExceptionHandler(); if (!this.catchExits[i]) { switch (finallyMode) { case FINALLY_INLINE: // inlined finally here can see all merged variables if (isStackMapFrameCodeStream) { ((StackMapFrameCodeStream) codeStream) .pushStateIndex(this.naturalExitMergeInitStateIndex); } if (this.catchExitInitStateIndexes[i] != -1) { codeStream.removeNotDefinitelyAssignedVariables( currentScope, this.catchExitInitStateIndexes[i]); codeStream.addDefinitelyAssignedVariables( currentScope, this.catchExitInitStateIndexes[i]); } // entire sequence for finally is associated to finally block this.finallyBlock.generateCode(this.scope, codeStream); codeStream.goto_(postCatchesFinallyLabel); if (isStackMapFrameCodeStream) { ((StackMapFrameCodeStream) codeStream).popStateIndex(); } break; case FINALLY_SUBROUTINE: requiresNaturalExit = true; // $FALL-THROUGH$ case NO_FINALLY: if (this.naturalExitMergeInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables( currentScope, this.naturalExitMergeInitStateIndex); codeStream.addDefinitelyAssignedVariables( currentScope, this.naturalExitMergeInitStateIndex); } codeStream.goto_(naturalExitLabel); break; case FINALLY_DOES_NOT_COMPLETE: codeStream.goto_(this.subRoutineStartLabel); break; } } } } // extra handler for trailing natural exit (will be fixed up later on when natural exit is // generated below) ExceptionLabel naturalExitExceptionHandler = requiresNaturalExit && (finallyMode == FINALLY_SUBROUTINE) ? new ExceptionLabel(codeStream, null) : null; // addition of a special handler so as to ensure that any uncaught exception (or exception // thrown // inside catch blocks) will run the finally block int finallySequenceStartPC = codeStream.position; if (this.subRoutineStartLabel != null && this.anyExceptionLabel.count != 0) { codeStream.pushExceptionOnStack(this.scope.getJavaLangThrowable()); if (this.preTryInitStateIndex != -1) { // reset initialization state, as for a normal catch block codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.preTryInitStateIndex); codeStream.addDefinitelyAssignedVariables(currentScope, this.preTryInitStateIndex); } placeAllAnyExceptionHandler(); if (naturalExitExceptionHandler != null) naturalExitExceptionHandler.place(); switch (finallyMode) { case FINALLY_SUBROUTINE: // any exception handler codeStream.store(this.anyExceptionVariable, false); codeStream.jsr(this.subRoutineStartLabel); codeStream.recordPositionsFrom(finallySequenceStartPC, this.finallyBlock.sourceStart); int position = codeStream.position; codeStream.throwAnyException(this.anyExceptionVariable); codeStream.recordPositionsFrom(position, this.finallyBlock.sourceEnd); // subroutine this.subRoutineStartLabel.place(); codeStream.pushExceptionOnStack(this.scope.getJavaLangThrowable()); position = codeStream.position; codeStream.store(this.returnAddressVariable, false); codeStream.recordPositionsFrom(position, this.finallyBlock.sourceStart); this.finallyBlock.generateCode(this.scope, codeStream); position = codeStream.position; codeStream.ret(this.returnAddressVariable.resolvedPosition); codeStream.recordPositionsFrom(position, this.finallyBlock.sourceEnd); // the ret bytecode is part of the subroutine break; case FINALLY_INLINE: // any exception handler codeStream.store(this.anyExceptionVariable, false); codeStream.addVariable(this.anyExceptionVariable); codeStream.recordPositionsFrom(finallySequenceStartPC, this.finallyBlock.sourceStart); // subroutine this.finallyBlock.generateCode(currentScope, codeStream); position = codeStream.position; codeStream.throwAnyException(this.anyExceptionVariable); codeStream.removeVariable(this.anyExceptionVariable); if (this.preTryInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables( currentScope, this.preTryInitStateIndex); } this.subRoutineStartLabel.place(); codeStream.recordPositionsFrom(position, this.finallyBlock.sourceEnd); break; case FINALLY_DOES_NOT_COMPLETE: // any exception handler codeStream.pop(); this.subRoutineStartLabel.place(); codeStream.recordPositionsFrom(finallySequenceStartPC, this.finallyBlock.sourceStart); // subroutine this.finallyBlock.generateCode(this.scope, codeStream); break; } // will naturally fall into subsequent code after subroutine invocation if (requiresNaturalExit) { switch (finallyMode) { case FINALLY_SUBROUTINE: naturalExitLabel.place(); int position = codeStream.position; naturalExitExceptionHandler.placeStart(); codeStream.jsr(this.subRoutineStartLabel); naturalExitExceptionHandler.placeEnd(); codeStream.recordPositionsFrom(position, this.finallyBlock.sourceEnd); break; case FINALLY_INLINE: // inlined finally here can see all merged variables if (isStackMapFrameCodeStream) { ((StackMapFrameCodeStream) codeStream) .pushStateIndex(this.naturalExitMergeInitStateIndex); } if (this.naturalExitMergeInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables( currentScope, this.naturalExitMergeInitStateIndex); codeStream.addDefinitelyAssignedVariables( currentScope, this.naturalExitMergeInitStateIndex); } naturalExitLabel.place(); // entire sequence for finally is associated to finally block this.finallyBlock.generateCode(this.scope, codeStream); if (postCatchesFinallyLabel != null) { position = codeStream.position; // entire sequence for finally is associated to finally block codeStream.goto_(postCatchesFinallyLabel); codeStream.recordPositionsFrom(position, this.finallyBlock.sourceEnd); } if (isStackMapFrameCodeStream) { ((StackMapFrameCodeStream) codeStream).popStateIndex(); } break; case FINALLY_DOES_NOT_COMPLETE: break; default: naturalExitLabel.place(); break; } } if (postCatchesFinallyLabel != null) { postCatchesFinallyLabel.place(); } } else { // no subroutine, simply position end label (natural exit == end) naturalExitLabel.place(); } } else { // try block had no effect, only generate the body of the finally block if any if (this.subRoutineStartLabel != null) { this.finallyBlock.generateCode(this.scope, codeStream); } } // May loose some local variable initializations : affecting the local variable attributes if (this.mergedInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex); codeStream.addDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex); } codeStream.recordPositionsFrom(pc, this.sourceStart); }