处理 Java 字节码中的尝试/捕获异常?( "stack height inconsistent" )



我正在尝试在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

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

也许使用异常是解决这个问题的最好方法?

从一些谷歌我发现你可以创建例外类的实例,并初始化他们的东西,如:

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

我也发现你可以把它们和athrow一起扔,这似乎没问题。

让我困惑的是异常是如何被捕获的。似乎有一个神奇的"异常表",它将异常的抛出和捕获粘合在一起,但我不知道如何在从头编写字节码(并使用Jasmin进行组装)时定义其中一个。有人能告诉我创建异常表的秘密吗?可能给我一个异常处理的例子,将与jasmin组装?

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

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

编辑:

我确实不得不看看.catch在最后,发现它真的很容易使用。

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

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

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

如果抛出异常,则很明显该框架可能具有异常可能抛出的任何位置的内容。因此,内容将被丢弃并由异常替换。无论如何,您都无法返回并继续使用框架内容。

验证jsr的规则非常复杂,正如Tom指出的那样,不赞成使用操作码。所以最好避免。

我对jsr的记忆有点模糊,但是…

(已更新)在Java字节码验证中有一个规则,无论两个控制流在哪里连接在一起,堆栈深度必须沿着两个分支相同。jsr子程序在一定程度上不受此规则的约束——具有不同堆栈深度的多个异常点可以"到达"同一个jsr例程,但是从jsr例程进入到随后的ret例程的堆栈深度净变化必须为零(或者实际上是负1,因为异常原因总是在进入例程时被推入)。

此外,虽然jsr例程可以"逃逸"并分支回常规控制流,但如果它这样做了,jsr就不能免除连接点的堆栈深度规则。这严重限制了您可以这样做的情况,因为jsr例程可能会以不同的堆栈深度进入。

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

(我不太明白你是如何计划"绕过"你的jsr异常问题的。)

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

相关内容

  • 没有找到相关文章

最新更新