Java LineNumberTable:条目说明 [英] Java LineNumberTable: Entry explanation

查看:82
本文介绍了Java LineNumberTable:条目说明的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我反汇编我的类文件,则会得到如下形式的LineNumberTables

If I disassemble my class file, I get LineNumberTables of the form

  LineNumberTable:
    line 204: 0
    line 205: 9
    line 208: 57
    line 209: 63
    line 210: 72
    line 211: 75
    line 212: 78
    line 213: 87
    line 216: 90
    line 218: 118
    line 221: 126
    line 222: 131
    line 223: 138
    line 224: 143
    line 227: 150
    line 230: 157
    line 231: 160
    line 232: 170
    line 235: 194
    line 237: 228
    line 240: 249
    line 241: 259
    line 243: 266
    line 245: 269
    line 246: 292
    line 248: 295
    line 249: 301
    line 250: 308
    line 251: 315
    line 252: 322
    line 253: 329

我知道这些表包含调试信息,并且第一个条目是类文件中的某种位置,而第二个条目是源代码中的某个位置.我想知道:

I know that these tables contain debug information, and that the first entry is some kind of position in the class-file, while the second one is a position in the source code. I would like to know:

  1. 源代码行号是相对的还是绝对的?如果我绝对地解释它们,则有些地方指向多行注释的中间,这似乎很奇怪.

  1. Are the source code line numbers relative or absolute? If I interpret them absolutely, some point to the middle of multi-line comments which seems to be strange.

相同源代码的两个不同编译仅在一个字节中有所不同:条目第216:90行"被第215:90行"代替.我试图找出造成这种情况的原因.有什么主意吗?

Two different compilations of the same source code differ only in one byte: The entry "line 216: 90" is replaced by "line 215: 90". I try to find out what could be the cause for this. Any idea?

推荐答案

将常识应用于正在阅读的内容.正确阅读规范后,即存储在 LineNumberTable 属性数组中的第一个数字是字节码偏移量,第二个是行号,这并不意味着您正在使用的反汇编程序将还要按顺序打印它们.

Apply common sense to what you are reading. While you read the spec correctly, i.e. the first number stored in the LineNumberTable attribute’s array is a byte code offset and the second one is a line number, that doesn’t imply that the Disassembler you are using will also print them in that order.

有两个表明订单已被交换的指示器

There are two indicators that the order has been swapped

  1. 第一个数字之前印有行"一词,表明该第一个数字是行号
  2. 第一个数字的范围是204到253,这对于类声明中某个地方的方法的源代码行是合理的,而第二个数字的范围是 0 329,这对于方法中以零开头的字节码偏移量是合理的.

  1. There is the word "Line" printed before the first number, suggesting that this first number is a line number
  2. The first number ranges from 204 to 253, which is reasonable for source code lines of a method somewhere in a class declaration, whereas the second number ranges from 0 to 329, which is reasonable for byte code offsets within a method, which start by zero.

相比之下,方法的行号不太可能以零开头,因为源代码通常以 package import 声明开头.而且,如果方法代码的前203个字节没有关联的源代码行(尽管那并非不可能),这也将是不寻常的.

In contrast, it’s unlikely to see line numbers starting with zero for a method, as source code usually starts with package and import declarations. And it would also be unusual, if the first 203 bytes of of a method’s code had no associated source code lines (though that wouldn’t be impossible).

两个指标在一起都非常强.然后,观察到的变化是很合理的.显然,生成的代码没有改变.但是由于没有关于行号和生成的代码如何关联的标准,因此可能存在细微的差异,具体取决于编译器的版本,例如当表达式跨越多行但仅生成一条指令时,或者当编译器试图避免行号表过多时.

Both indicators together are quite strong. Then, the observed change is quite plausible. Obviously, the generated code didn’t change. But since there is no standard about how the line numbers and the generated code are associated, there might be subtle differences, depending on the compiler version, e.g. when an expression spans multiple lines, but generates only one instruction or when the compiler tries to avoid too excessive line number tables.

例如代码

foo(
);

仅生成一条指令(如果 foo() static ),并且未定义,则与该指令关联的两行中的哪一行.当它是一个实例方法时,它将由两条指令组成,但是将它们表示为不同的行号将是值得商,的,因为在调试过程中在它们之间进行切换不会有太大帮助.但这是编译器的决定.也可以

generates only one instruction (if foo() is static) and it’s not defined, which of the two lines to associate with that instruction. When it is an instance method, it will consist of two instructions, but representing them as being in different line numbers would be debatable, as stepping in-between during debugging would not be much helpful. But it’s the compiler’s decision. Also with

foo(
    null,
    1,
    true
);

将每个常量参数推入堆栈需要在指令序列中使用一个字节,而将不同的行号与每个指令相关联将需要在每个指令中使用另外四个字节.由于推入这些常量不太可能失败,因此,对其进行跟踪几乎没有意义,因此编译器可能会决定将整个序列与调用指令的唯一行号相关联.由于此决定取决于实际的编译器,甚至可能取决于其当前配置,因此重新编译可能会更改关联.

pushing each of the constant arguments to the stack requires one byte in the instruction sequence, whereas associating a distinct line number to each of the instructions would require another four bytes per instruction. Since pushing these constants is unlikely to fail, thus, tracing them would make little sense, compilers may decide to associate the entire sequence with the sole line number of the invocation instruction. Since this decision depends on the actual compiler and perhaps even on its current configuration, recompiling may change the association.

另一个差异是编译器如何处理综合方法,例如桥接方法和内部类访问器.到目前为止,我已经看到,它们仅与零关联,与周围的类声明的开头以及与它们所委托的实际目标成员的开头相关.

Another difference is how compilers handle synthetic methods, like bridge methods and inner class accessors. I’ve seen so far, them being associated with just zero, with the beginning of the surrounding class declaration, and with the beginning of the actual target member to which they delegate.

这篇关于Java LineNumberTable:条目说明的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆