/** Code generation for a array allocation expression */ public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { int pc = codeStream.position; if (initializer != null) { initializer.generateCode(currentScope, codeStream, valueRequired); return; } int nonNullDimensionsLength = 0; for (int i = 0, max = dimensions.length; i < max; i++) if (dimensions[i] != null) { dimensions[i].generateCode(currentScope, codeStream, true); nonNullDimensionsLength++; } // Generate a sequence of bytecodes corresponding to an array allocation if (this.resolvedType.dimensions() == 1) { // Mono-dimensional array codeStream.newArray((ArrayBinding) this.resolvedType); } else { // Multi-dimensional array codeStream.multianewarray(this.resolvedType, nonNullDimensionsLength); } if (valueRequired) { codeStream.generateImplicitConversion(implicitConversion); } else { codeStream.pop(); } codeStream.recordPositionsFrom(pc, this.sourceStart); }
/** * Code generation for instanceOfExpression * * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream * @param valueRequired boolean */ public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { int pc = codeStream.position; this.expression.generateCode(currentScope, codeStream, true); codeStream.instance_of(this.type.resolvedType); if (valueRequired) { codeStream.generateImplicitConversion(this.implicitConversion); } else { codeStream.pop(); } codeStream.recordPositionsFrom(pc, this.sourceStart); }
/** * MessageSendDotClass code generation * * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream * @param valueRequired boolean */ public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { // {ObjectTeams: role class literal? if (this.roleClassLiteralAccess != null) { this.roleClassLiteralAccess.generateCode(currentScope, codeStream, valueRequired); return; } // SH} int pc = codeStream.position; // in interface case, no caching occurs, since cannot make a cache field for interface if (valueRequired) { codeStream.generateClassLiteralAccessForType(this.type.resolvedType, this.syntheticField); codeStream.generateImplicitConversion(this.implicitConversion); } codeStream.recordPositionsFrom(pc, this.sourceStart); }
public void generateCode(BlockScope currentScope, CodeStream codeStream) { if ((this.bits & IsReachable) == 0) { return; } int pc = codeStream.position; if (this.assertionSyntheticFieldBinding != null) { BranchLabel assertionActivationLabel = new BranchLabel(codeStream); codeStream.fieldAccess( Opcodes.OPC_getstatic, this.assertionSyntheticFieldBinding, null /* default declaringClass */); codeStream.ifne(assertionActivationLabel); BranchLabel falseLabel; this.assertExpression.generateOptimizedBoolean( currentScope, codeStream, (falseLabel = new BranchLabel(codeStream)), null, true); codeStream.newJavaLangAssertionError(); codeStream.dup(); if (this.exceptionArgument != null) { this.exceptionArgument.generateCode(currentScope, codeStream, true); codeStream.invokeJavaLangAssertionErrorConstructor( this.exceptionArgument.implicitConversion & 0xF); } else { codeStream.invokeJavaLangAssertionErrorDefaultConstructor(); } codeStream.athrow(); // May loose some local variable initializations : affecting the local variable attributes if (this.preAssertInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.preAssertInitStateIndex); } falseLabel.place(); assertionActivationLabel.place(); } else { // May loose some local variable initializations : affecting the local variable attributes if (this.preAssertInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.preAssertInitStateIndex); } } codeStream.recordPositionsFrom(pc, this.sourceStart); }
/** * 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); }
/** Code generation for a binary operation */ public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { int pc = codeStream.position; if (this.constant != Constant.NotAConstant) { // inlined value if (valueRequired) codeStream.generateConstant(this.constant, this.implicitConversion); codeStream.recordPositionsFrom(pc, this.sourceStart); return; } Constant cst = this.right.constant; if (cst != Constant.NotAConstant) { // <expr> || true --> true if (cst.booleanValue() == true) { this.left.generateCode(currentScope, codeStream, false); if (valueRequired) codeStream.iconst_1(); } else { // <expr>|| false --> <expr> this.left.generateCode(currentScope, codeStream, valueRequired); } if (this.mergedInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex); } codeStream.generateImplicitConversion(this.implicitConversion); codeStream.recordPositionsFrom(pc, this.sourceStart); return; } BranchLabel trueLabel = new BranchLabel(codeStream), endLabel; cst = this.left.optimizedBooleanConstant(); boolean leftIsConst = cst != Constant.NotAConstant; boolean leftIsTrue = leftIsConst && cst.booleanValue() == true; cst = this.right.optimizedBooleanConstant(); boolean rightIsConst = cst != Constant.NotAConstant; boolean rightIsTrue = rightIsConst && cst.booleanValue() == true; generateOperands: { if (leftIsConst) { this.left.generateCode(currentScope, codeStream, false); if (leftIsTrue) { break generateOperands; // no need to generate right operand } } else { this.left.generateOptimizedBoolean(currentScope, codeStream, trueLabel, null, true); // need value, e.g. if (a == 1 || ((b = 2) > 0)) {} -> shouldn't initialize 'b' if a==1 } if (this.rightInitStateIndex != -1) { codeStream.addDefinitelyAssignedVariables(currentScope, this.rightInitStateIndex); } if (rightIsConst) { this.right.generateCode(currentScope, codeStream, false); } else { this.right.generateOptimizedBoolean( currentScope, codeStream, trueLabel, null, valueRequired); } } if (this.mergedInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex); } /* * improving code gen for such a case: boolean b = i < 0 || true since * the label has never been used, we have the inlined value on the * stack. */ if (valueRequired) { if (leftIsConst && leftIsTrue) { codeStream.iconst_1(); codeStream.recordPositionsFrom(codeStream.position, this.left.sourceEnd); } else { if (rightIsConst && rightIsTrue) { codeStream.iconst_1(); codeStream.recordPositionsFrom(codeStream.position, this.left.sourceEnd); } else { codeStream.iconst_0(); } if (trueLabel.forwardReferenceCount() > 0) { if ((this.bits & IsReturnedValue) != 0) { codeStream.generateImplicitConversion(this.implicitConversion); codeStream.generateReturnBytecode(this); trueLabel.place(); codeStream.iconst_1(); } else { codeStream.goto_(endLabel = new BranchLabel(codeStream)); codeStream.decrStackSize(1); trueLabel.place(); codeStream.iconst_1(); endLabel.place(); } } else { trueLabel.place(); } } codeStream.generateImplicitConversion(this.implicitConversion); codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd); } else { trueLabel.place(); } }
/** Boolean operator code generation Optimized operations are: || */ public void generateOptimizedBoolean( BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel, BranchLabel falseLabel, boolean valueRequired) { if (this.constant != Constant.NotAConstant) { super.generateOptimizedBoolean( currentScope, codeStream, trueLabel, falseLabel, valueRequired); return; } // <expr> || false --> <expr> Constant cst = this.right.constant; if (cst != Constant.NotAConstant && cst.booleanValue() == false) { int pc = codeStream.position; this.left.generateOptimizedBoolean( currentScope, codeStream, trueLabel, falseLabel, valueRequired); if (this.mergedInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex); } codeStream.recordPositionsFrom(pc, this.sourceStart); return; } cst = this.left.optimizedBooleanConstant(); boolean leftIsConst = cst != Constant.NotAConstant; boolean leftIsTrue = leftIsConst && cst.booleanValue() == true; cst = this.right.optimizedBooleanConstant(); boolean rightIsConst = cst != Constant.NotAConstant; boolean rightIsTrue = rightIsConst && cst.booleanValue() == true; // default case generateOperands: { if (falseLabel == null) { if (trueLabel != null) { // implicit falling through the FALSE case this.left.generateOptimizedBoolean( currentScope, codeStream, trueLabel, null, !leftIsConst); // need value, e.g. if (a == 1 || ((b = 2) > 0)) {} -> shouldn't initialize 'b' if a==1 if (leftIsTrue) { if (valueRequired) codeStream.goto_(trueLabel); codeStream.recordPositionsFrom(codeStream.position, this.left.sourceEnd); break generateOperands; // no need to generate right operand } if (this.rightInitStateIndex != -1) { codeStream.addDefinitelyAssignedVariables(currentScope, this.rightInitStateIndex); } this.right.generateOptimizedBoolean( currentScope, codeStream, trueLabel, null, valueRequired && !rightIsConst); if (valueRequired && rightIsTrue) { codeStream.goto_(trueLabel); codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd); } } } else { // implicit falling through the TRUE case if (trueLabel == null) { BranchLabel internalTrueLabel = new BranchLabel(codeStream); this.left.generateOptimizedBoolean( currentScope, codeStream, internalTrueLabel, null, !leftIsConst); // need value, e.g. if (a == 1 || ((b = 2) > 0)) {} -> shouldn't initialize 'b' if a==1 if (leftIsTrue) { internalTrueLabel.place(); break generateOperands; // no need to generate right operand } if (this.rightInitStateIndex != -1) { codeStream.addDefinitelyAssignedVariables(currentScope, this.rightInitStateIndex); } this.right.generateOptimizedBoolean( currentScope, codeStream, null, falseLabel, valueRequired && !rightIsConst); int pc = codeStream.position; if (valueRequired && rightIsConst && !rightIsTrue) { codeStream.goto_(falseLabel); codeStream.recordPositionsFrom(pc, this.sourceEnd); } internalTrueLabel.place(); } else { // no implicit fall through TRUE/FALSE --> should never occur } } } if (this.mergedInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex); } }
public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { if (!valueRequired) currentScope.problemReporter().unusedObjectAllocation(this); int pc = codeStream.position; MethodBinding codegenBinding = this.binding.original(); ReferenceBinding allocatedType = codegenBinding.declaringClass; codeStream.new_(allocatedType); boolean isUnboxing = (this.implicitConversion & TypeIds.UNBOXING) != 0; if (valueRequired || isUnboxing) { codeStream.dup(); } // better highlight for allocation: display the type individually if (this.type != null) { // null for enum constant body codeStream.recordPositionsFrom(pc, this.type.sourceStart); } else { // push enum constant name and ordinal codeStream.ldc(String.valueOf(this.enumConstant.name)); codeStream.generateInlinedValue(this.enumConstant.binding.id); } // handling innerclass instance allocation - enclosing instance arguments if (allocatedType.isNestedType()) { codeStream.generateSyntheticEnclosingInstanceValues( currentScope, allocatedType, enclosingInstance(), this); } // generate the arguments for constructor generateArguments(this.binding, this.arguments, currentScope, codeStream); // handling innerclass instance allocation - outer local arguments if (allocatedType.isNestedType()) { codeStream.generateSyntheticOuterArgumentValues(currentScope, allocatedType, this); } // invoke constructor if (this.syntheticAccessor == null) { codeStream.invoke( Opcodes.OPC_invokespecial, codegenBinding, null /* default declaringClass */); } else { // synthetic accessor got some extra arguments appended to its signature, which need values for (int i = 0, max = this.syntheticAccessor.parameters.length - codegenBinding.parameters.length; i < max; i++) { codeStream.aconst_null(); } codeStream.invoke( Opcodes.OPC_invokespecial, this.syntheticAccessor, null /* default declaringClass */); } if (valueRequired) { codeStream.generateImplicitConversion(this.implicitConversion); } else if (isUnboxing) { // conversion only generated if unboxing codeStream.generateImplicitConversion(this.implicitConversion); switch (postConversionType(currentScope).id) { case T_long: case T_double: codeStream.pop2(); break; default: codeStream.pop(); } } codeStream.recordPositionsFrom(pc, this.sourceStart); }
/** Code generation for a array initializer */ public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { // Flatten the values and compute the dimensions, by iterating in depth into nested array // initializers int pc = codeStream.position; int expressionLength = (this.expressions == null) ? 0 : this.expressions.length; codeStream.generateInlinedValue(expressionLength); codeStream.newArray(this.binding); if (this.expressions != null) { // binding is an ArrayType, so I can just deal with the dimension int elementsTypeID = this.binding.dimensions > 1 ? -1 : this.binding.leafComponentType.id; for (int i = 0; i < expressionLength; i++) { Expression expr; if ((expr = this.expressions[i]).constant != Constant.NotAConstant) { switch (elementsTypeID) { // filter out initializations to default values case T_int: case T_short: case T_byte: case T_char: case T_long: if (expr.constant.longValue() != 0) { codeStream.dup(); codeStream.generateInlinedValue(i); expr.generateCode(currentScope, codeStream, true); codeStream.arrayAtPut(elementsTypeID, false); } break; case T_float: case T_double: double constantValue = expr.constant.doubleValue(); if (constantValue == -0.0 || constantValue != 0) { codeStream.dup(); codeStream.generateInlinedValue(i); expr.generateCode(currentScope, codeStream, true); codeStream.arrayAtPut(elementsTypeID, false); } break; case T_boolean: if (expr.constant.booleanValue() != false) { codeStream.dup(); codeStream.generateInlinedValue(i); expr.generateCode(currentScope, codeStream, true); codeStream.arrayAtPut(elementsTypeID, false); } break; default: if (!(expr instanceof NullLiteral)) { codeStream.dup(); codeStream.generateInlinedValue(i); expr.generateCode(currentScope, codeStream, true); codeStream.arrayAtPut(elementsTypeID, false); } } } else if (!(expr instanceof NullLiteral)) { codeStream.dup(); codeStream.generateInlinedValue(i); expr.generateCode(currentScope, codeStream, true); codeStream.arrayAtPut(elementsTypeID, false); } } } if (valueRequired) { codeStream.generateImplicitConversion(this.implicitConversion); } else { codeStream.pop(); } codeStream.recordPositionsFrom(pc, this.sourceStart); }
/** * 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); }