处理Java字节码中的Try / Catch异常? (“堆栈高度不一致”)

时间:2011-12-12 22:36:51

标签: java exception try-catch bytecode jasmin

我正在尝试在java字节码中进行一些错误处理。我首先尝试实现一些类似catch的子例程,在那里我将检查错误情况,并跳转到相应的子例程,有点像:

  iconst_1
  iconst_0
  dup
  ifeq calldiverr
  goto enddivtest
calldiverr:
  jsr divError
enddivtest:
  idiv

...More instructions...

divError:
  getstatic java/lang/System/out Ljava/io/PrintStream;
  ldc "Oh dear you divided by 0!"
  invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V

上面的问题是当我有多个跳转到这个子程序的指令时,运行字节码时会收到一条错误信息,说堆栈高度不一致。

也许使用例外是解决这个问题的最好方法吗?

通过一些谷歌搜索我发现你可以创建Exception类的实例并用以下内容初始化它们:

new java/lang/Exception
dup
ldc "exception message!"
invokespecial java/lang/Exception/<init>(Ljava/lang/String;)V

我还发现你可以用athrow抛出它们,这似乎没问题。

然而令我感到困惑的是如何抓住异常。似乎有一个神奇的“异常表”将异常的抛出和捕获粘在一起,但我不知道如何从头开始编写字节码(以及使用Jasmin进行汇编)时定义其中一个。有人可以告诉我创建异常表的秘诀吗?并且可能给我一个与jasmin汇编的异常处理示例?

3 个答案:

答案 0 :(得分:2)

最后,我提出了一个比jsr更好的解决方案 - 在Jasmin中使用.method定义一个方法。一旦检测到错误,我就用invokestatic来调用我的错误处理程序。

对于那些寻找实际异常处理的人 - 我认为在Jasmin中定义异常表可能是使用.catch完成的,但我没有调查它,因为方法定义解决了我的问题。

编辑:

我最后必须查看.catch,发现它非常易于使用。它记录在案here

答案 1 :(得分:0)

首先,它值得指出版本51.0中的类文件可能不包含jsr指令。重复代码或使用方法。

在字节码的每一点,必须知道帧中每个元素的静态类型。每个帧都不是调用堆栈。

一般来说,你不想玩巨大的筹码。将临时存储在局部变量中以保持简单。

如果抛出异常,那么显然该帧可能已经从异常可能抛出的任何地方获取了内容。因此,内容将被丢弃并替换为异常。无论如何,您将无法返回并继续使用框架内容。

答案 2 :(得分:0)

验证jsr的规则非常复杂,正如Tom所说,操作码已被弃用。所以最好避免。

jsr上的记忆有点模糊,但是......

(更新)Java字节码验证中有一条规则,即只要两个控制流连接在一起,堆栈深度必须在两个分支上相同。 jsr子例程从这个规则中被豁免到一个点 - 具有不同堆栈深度的多个异常点可以“到达”相同的jsr例程,但是堆栈深度的净变化来自jsr例程后续ret的条目必须为零(或实际上减1,因为异常原因总是在进入例程时被推送)。

此外,虽然jsr例程可以“转义”并分支回常规控制流,但如果这样做,则jsr不会从连接点的堆栈深度规则中免除。这严重限制了您可以执行此操作的情况,因为可能会以不同的堆栈深度输入jsr例程。

(我毫无疑问仍然有一些错误,但这是我能做的最好的。)

(我不太明白你是如何计划“解决”jsr例外的问题。)

(另外,Sun使字节码写入更复杂,有4或5(不记得哪个),这使得手动编码字节码几乎不可能。他们这样做是因为他们不知道如何快速进行验证足以击败IBM的验证者,但这是另一回事。)