ASM Try/Catch Block with an output value

北城余情 提交于 2019-12-06 06:13:41

Obviously, ASM can calculate stackmap frames for correct code only as no stackmap can fix broken code. We can learn what went wrong when we analyze the exception.

java.lang.VerifyError: Inconsistent stackmap frames at branch target 17

there is a branch targeting byte code position 17.

Exception Details:
  Location:
    dyvil/test/Main.main([Ljava/lang/String;)V @14: goto

the source of the branch is a goto instruction at position 14

  Reason:
    Current frame's stack size doesn't match stackmap.

quite self explaining. The only thing you have to consider that non-matching frames don’t necessarily indicate a wrong stackmap calculation; it might be that the bytecode itself is violates the constraints and the calculated stackmap just reflects that.

  Current Frame:
    bci: @14
    flags: { }
    locals: { '[Ljava/lang/String;' }
    stack: { integer }

at 14, the source of the branch (the location of the goto instruction), the stack contains one int value.

  Stackmap Frame:
    bci: @17
    flags: { }
    locals: { '[Ljava/lang/String;' }
    stack: { top, integer }

at 17, the target of the branch, are two values on the stack.

  Bytecode:
    0000000: b200 1412 16b8 001c a700 0957 100a a700
    0000010: 03b6 0024 b1                           

well, the bytecode isn’t disassembled here, but you can’t say the exception message was too brief up to this point. Manual disassembling the bytecode yields:

 0: getstatic     0x0014
 3: ldc           0x16
 5: invokestatic  0x001c
 8: goto          +9 (=>17)
11: pop
12: bipush        #10
14: goto          +3 (=>17)
17: invokevirtual 0x0024
20: return

 

  Exception Handler Table:
    bci [3, 11] => handler: 11

What we can see here is that there are two ways of reaching location 17, one is the ordinary execution of getstatic, ldc, invokestatic the other is the exception handler, starting at 11, performing pop bipush. We can deduce for the latter that it has indeed one int value on the stack as it pops the exception and pushes one int constant.

For the former, there is not enough information here, i.e. I don’t know the signature of the invoked method, however, since the verifier didn’t reject the goto from 8 to 17, it’s safe to assume that the stack indeed holds two values before the branch. Since getstatic, ldc produces two values, the static method must have either a void () or a value (value) signature. This implies that the value of the very first getstatic instruction is not used before the branch.

→After reading your comment, the error becomes apparent: that first getstatic instruction reads System.out which you want to use at the end of the method to invoke println, however, when an exception occurred, the stack is flushed and no PrintWriter is on the stack but the exception handler tries to recover and join the code path at the place where the PrintWriter is required for invoking println. It is important to understand that exception handlers always start with an operand stack consisting of a single element, the exception. None of the values you might have pushed before the exception occurred will persist. So if you want to prefetch a field value (like System.out) before the guarded code and use it regardless of whether an exception occurred, you have to store it in a local variable and retrieve afterwards.

It seems that ASM derived the stackmap frame for location @17 from the state before the first branch and when joining it with the frame of the state before the second branch, it only cared for the types but not the different depth, which is a pity as it’s an error that is easy to spot. But it’s only a missing feature (as COMPUTE_FRAMES is not specified to do error checking), not a bug.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!