2016-12-12 9 views
0

Wenn ich meine Klassendatei zu zerlegen, bekomme ich LineNumberTables die FormJava LineNumberTable: Eintrag Erklärung

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 

Ich weiß, dass diese Tabellen Debug-Informationen enthalten, und dass der erste Eintrag ist eine Art von Position in der Klasse -Datei, während der zweite eine Position im Quellcode ist. Ich möchte wissen:

  1. Sind die Zeilennummern des Quellcodes relativ oder absolut? Wenn ich sie absolut interpretiere, zeigen einige auf die Mitte von mehrzeiligen Kommentaren, die seltsam erscheinen.

  2. Zwei verschiedene Compilations desselben Quellcodes unterscheiden sich nur in einem Byte: Der Eintrag "Zeile 216: 90" wird durch "Zeile 215: 90" ersetzt. Ich versuche herauszufinden, was die Ursache dafür sein könnte. Irgendeine Idee?

+0

Siehe [this link] (https://docs.oracle.com/javase/specs/jvms/se6/html/ClassFile.doc.html#22856) - Teil der ersten Seite von googling "Java Line Number Table ". – TripeHound

+0

Danke - Ich habe diese Seite schon gelesen. –

+0

Entschuldigung (obwohl Sie erwähnt haben, dass Sie eine solche Seite gelesen haben (könnte) sarky Kommentare wie meine :-) Die Seite sagt die erste Nummer ist der Offset in den Byte-Code und die zweite ist die "Zeilennummer in der ursprünglichen Quelldatei ". Es besagt auch, dass Einträge in den (möglicherweise mehreren) Tabellen nicht eins zu eins mit den Quellzeilen sein müssen ... obwohl das nicht so klingt, als sollten die Zeilennummern in der Mitte eines mehrzeiligen Kommentars landen. Wenn Sie eine Vermutung riskieren, könnten Sie die Inkonsistenzen sehen, weil _andere_ Klassen zwischenzeitlich neu kompiliert wurden (siehe 2. Absatz in Abschnitt 4.9 auf dieser Seite)? – TripeHound

Antwort

2

Wenden Sie gesunden Menschenverstand auf, was Sie gerade lesen. Wenn Sie die Spezifikation korrekt lesen, d. H. Die erste Zahl, die im Array LineNumberTable gespeichert ist, ist ein Bytecode-Offset, und die zweite Nummer ist eine Zeilennummer. Dies impliziert nicht, dass der verwendete Disassembler diese auch in dieser Reihenfolge ausgibt.

Es gibt zwei Indikatoren, die die Reihenfolge

  1. Es ist das Wort „Linie“ gedruckt, bevor die erste Zahl, was darauf hindeutet, dass diese erste Zahl ist eine Zeilennummer
  2. Die erste Zahl Bereiche ausgelagert wurde von 204 bis 253, was für Quelltext-Zeilen einer Methode irgendwo in einer Klassendeklaration sinnvoll ist, während die zweite Zahl von 0 bis reicht, was für Byte-Code-Offsets innerhalb einer Methode sinnvoll ist, die mit Null beginnen.

    Im Gegensatz dazu ist es unwahrscheinlich, dass Zeilennummern bei einer Methode mit Null beginnen, da der Quellcode normalerweise mit package und import Deklarationen beginnt. Und es wäre auch ungewöhnlich, wenn die ersten 203 Bytes eines Methodencodes keine Quellcodezeilen hätten (obwohl das nicht unmöglich wäre).

Beide Indikatoren zusammen sind ziemlich stark. Dann ist die beobachtete Änderung ziemlich plausibel. Offensichtlich hat sich der generierte Code nicht geändert. Da es jedoch keinen Standard gibt, wie die Zeilennummern und der generierte Code verknüpft sind, können je nach Compiler-Version, z. wenn ein Ausdruck mehrere Zeilen umfasst, aber nur eine Anweisung generiert oder wenn der Compiler versucht, zu große Zeilennummerentabellen zu vermeiden.

z. der Code

foo(
); 

erzeugt nur eine Anweisung (wenn foo()static ist) und es ist nicht festgelegt, welche der beiden Leitungen mit dieser Instruktion zu assoziieren. Wenn es sich um eine Instanzmethode handelt, besteht sie aus zwei Anweisungen, die jedoch in verschiedenen Zeilennummern dargestellt werden könnten, wäre fraglich, da Zwischenschritte während des Debuggens nicht sehr hilfreich wären. Aber es ist die Entscheidung des Compilers.Auch mit

foo(
    null, 
    1, 
    true 
); 

jeden der konstanten Argumenten in den Stapel erfordert einen Byte in der Befehlssequenz drückt, während eine bestimmte Leitungsnummer zu jedem der Befehlen Assoziieren wäre ein weiteres vier Bytes pro Befehl erfordern. Da das Drücken dieser Konstanten wahrscheinlich nicht fehlschlägt, würde die Verfolgung dieser Konstanten wenig Sinn ergeben, da Compiler entscheiden könnten, die gesamte Sequenz der einzigen Zeilennummer der Aufrufanweisung zuzuordnen. Da diese Entscheidung vom tatsächlichen Compiler und möglicherweise sogar von seiner aktuellen Konfiguration abhängt, kann das Neukompilieren die Zuordnung ändern.

Ein weiterer Unterschied ist, wie Compiler mit synthetischen Methoden umgehen, wie Bridge-Methoden und innere Klasse Accessoren. Ich habe bisher gesehen, dass sie nur mit Null assoziiert sind, mit dem Beginn der umgebenden Klassendeklaration und mit dem Beginn des eigentlichen Zielmitglieds, an das sie delegieren.