/** 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);
 }
Beispiel #3
0
  /**
   * 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);
  }
  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);
  }
Beispiel #5
0
  /** 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);
  }