In den vorangegangenen Abschnitten wurden bereits wichtige Direktiven genannt, um lauffähige Assemblerprogramme erstellen zu können. Darüber hinaus stellt die Assemblersprache Jasmin weitere Direktiven zur Verfügung, die z.B. die Verwendung von Annotationen oder die Aufnahme zusätzlicher Debugging-Informationen ermöglichen.
Debugging-Informationen mit Durch die Verwendung der Direktive .debug "<text>" Für Listing 5.11. ; DebugExample.j .bytecode 50.0 .class public DebugExample .super java/lang/Object .debug "Hello Debugger!" .method public <init>()V .limit stack 1 .limit locals 1 aload_0 invokespecial java/lang/Object/<init>()V return .end method .method public static main([Ljava/lang/String;)V .limit stack 0 .limit locals 1 return .end method Durch ein geeignetes Programm kann aus Hexdump von DebugExample.class: ca fe ba be .. .. .. .. .. .. .. .. .. .. .. .. ................ 0 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ................ 16 ... .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ................ 224 .. .. .. .. .. .. .. 00 0c 00 00 00 0f 48 65 6c .............Hel 240 6c 6f 20 44 65 62 75 67 67 65 72 21 lo Debugger! 256 Ab dem Byte 253 beginnt die ASCII-Codierung von "Hello Debugger!".
Dabei enspricht z.B. der hexadezimale Zahlenwert
Die Beschreibung der .annotation visible <fielddescriptor> <elementname> [<arraytag>]<tag> [<fielddescriptor>] [= <elementvalue>] ... .end annotation .annotation invisible <fielddescriptor> <elementname> [<arraytag>]<tag> [<fielddescriptor>] [= <elementvalue>] ... .end annotation Nach den Schlüsselwörtern .annotation visible Ljava/lang/annotation/Retention; value e Ljava/lang/annotation/RetentionPolicy; = CLASS .end annotation Der Descriptor value [e Ljava/lang/annotation/ElementType; = TYPE METHOD PARAMETER Neben dem bisher verwendeten tag-Zeichen "e" sind die in der folgenden Tabelle enthaltenen tags zulässig. Tabelle 5.2. Mögliche tag-Zeichen innerhalb einer Annotationendefinition und deren Bedeutungen.
Die Zeichen "B", "C", "D", "F", "I", "J", "S" und "Z" stehen jeweils für
einen primitiven Datentyp. Hinzukommen "s" und "c", falls ein Element der
Annotation vom Typ value3 @ LAnnotationJasminExample; = .annotation name s = "Hans Dampf" id I = 1 .end annotation Es wird deutlich, dass in diesem Fall für /* AnnotationJasminExample.java */ import java.lang.annotation.*; @Documented @Retention(value = RetentionPolicy.CLASS) @Target(value = { ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER }) public @interface AnnotationJasminExample { String name(); int id() default 1; } Das Listing .annotation default [<arraytag>]<tag> [<fielddescriptor>] = <elementvalue> .end annotation Der Assemblerquelltext Listing 5.12. ; AnnotationJasminExample.j .bytecode 50.0 .interface public abstract annotation AnnotationJasminExample .super java/lang/Object .implements java/lang/annotation/Annotation .annotation visible Ljava/lang/annotation/Documented; .end annotation .annotation visible Ljava/lang/annotation/Retention; value e Ljava/lang/annotation/RetentionPolicy; = CLASS .end annotation .annotation visible Ljava/lang/annotation/Target; value [e Ljava/lang/annotation/ElementType; = TYPE METHOD PARAMETER .end annotation .method public abstract name()Ljava/lang/String; .end method .method public abstract id()I .annotation default I = 1 .end annotation .end method Annotationen können auch bei Methodenparametern genutzt werden. Dazu
werden die Direktiven .annotation visibleparam <paramnumber> <fielddescriptor> <elementname> [<arraytag>]<tag> [<fielddescriptor>] [= <elementvalue>] ... .end annotation .annotation invisibleparam <paramnumber> <fielddescriptor> <elementname> [<arraytag>]<tag> [<fielddescriptor>] [= <elementvalue>] ... .end annotation Als Beispiel für die Verwendung einer Paramterannotation soll
Listing 5.14 dienen. Im Jasmin-Quelltext wird
der Paramter /* AnnotationJasminExample2.java */ import java.lang.annotation.*; @Target(value = { ElementType.METHOD }) public @interface AnnotationJasminExample2 { enum Direction { NORTH, SOUTH, WEST, EAST }; Direction value1(); Class value2(); AnnotationJasminExample value3(); } Der folgende Jasmin-Quelltext zur Beschreibung der Annotation
Listing 5.13. ; AnnotationJasminExample2.j .bytecode 50.0 .interface public abstract annotation AnnotationJasminExample2 .super java/lang/Object .implements java/lang/annotation/Annotation .annotation visible Ljava/lang/annotation/Target; value [e Ljava/lang/annotation/ElementType; = METHOD .end annotation ; folgende .inner-Direktive in einer einzigen Zeile anordnen .inner class public static final enum Direction inner AnnotationJasminExample2$Direction outer AnnotationJasminExample2 .method public abstract value1()LAnnotationJasminExample2$Direction; .end method .method public abstract value2()Ljava/lang/Class; .end method .method public abstract value3()LAnnotationJasminExample; .end method Die Beispielanwendung Listing 5.14. ; AnnotationJasminExample3.j .bytecode 50.0 .class public AnnotationJasminExample3 .super java/lang/Object ; folgende .inner-Direktive in einer einzigen Zeile anordnen .inner class public static final enum Direction inner AnnotationJasminExample2$Direction outer AnnotationJasminExample2 .method public <init>()V .limit stack 1 .limit locals 1 aload_0 invokespecial java/lang/Object/<init>()V return .end method .method public static main([Ljava/lang/String;)V .limit stack 0 .limit locals 1 return .annotation invisible LAnnotationJasminExample2; value1 e LAnnotationJasminExample2$Direction; = NORTH value2 c = Ljava/lang/Integer; value3 @ LAnnotationJasminExample; = .annotation name s = "Hans Dampf" id I = 1 .end annotation .end annotation .annotation invisibleparam 1 LAnnotationJasminExample; name s = "Hans im Glueck" id I = 2 .end annotation .end method Das Jasmin-Listing /* AnnotationJasminExample3.java */ public class AnnotationJasminExample3 { @AnnotationJasminExample2( value1 = AnnotationJasminExample2.Direction.NORTH, value2 = Integer.class, value3 = @AnnotationJasminExample(name = "Hans Dampf", id = 1) ) public static void main( @AnnotationJasminExample(name = "Hans im Glueck", id = 2) String[] args ) { } }
Programmelemente mit Mit der Direktive .deprecated Die Direktive /* DeprecatedJasminExample.java */ /** * @deprecated DeprecatedJasminExample marked deprecated. */ public class DeprecatedJasminExample { @Deprecated private static final String s = "s marked deprecated"; @Deprecated public static void method1 () { System.out.println("method1 marked deprecated."); } } Mit dem Java-Compiler Ausschnitt des Hexdumps von DeprecatedJasminExample.class: ca fe ba be .. .. .. .. .. .. .. .. .. .. .. .. ................ 0 ... 00 06 00 00 00 01 00 1a 00 07 00 08 00 03 00 09 ................ 464 00 00 00 02 00 0a 00 0b 00 00 00 00 00 0c 00 00 ................ 480 00 06 00 01 00 0d 00 00 00 02 00 01 00 0e 00 0f ................ 496 00 01 00 10 00 00 00 1d 00 01 00 01 00 00 00 05 ................ 512 2a b7 00 01 b1 00 00 00 01 00 11 00 00 00 06 00 *............... 528 01 00 00 00 07 00 09 00 12 00 0f 00 03 00 10 00 ................ 544 00 00 25 00 02 00 00 00 00 00 09 b2 00 02 12 03 ..%............. 560 b6 00 04 b1 00 00 00 01 00 11 00 00 00 0a 00 02 ................ 576 00 00 00 0c 00 08 00 0d 00 0b 00 00 00 00 00 0c ................ 592 00 00 00 06 00 01 00 0d 00 00 00 02 00 13 00 00 ................ 608 00 02 00 14 00 0b 00 00 00 00 .......... 624 Die Klassendatei enthält insgesamt fünf Byteabschnitte, die mit den
Schlüsselwörtern Bytes 486-491 (Deprecated-Attribut): 00 0b 00 00 00 00 Bytes 492-503 (RuntimeVisibleAnnotations-Attribut): 00 0c 00 00 00 06 00 01 00 0d 00 00 Bytes 600-605 (Deprecated-Attribut): 00 0b 00 00 00 00 Bytes 606-617 (RuntimeVisibleAnnotations-Attribut): 00 0c 00 00 00 06 00 01 00 0d 00 00 Bytes 628-633 (Deprecated-Attribut): 00 0b 00 00 00 00 Die ersten beiden Bytes der Byteabschnitte codieren einen Index im
Konstantenpool der Klassenatei. Den hexadezimalen Indizes 11. CONSTANT_Utf8: Deprecated 12. CONSTANT_Utf8: RuntimeVisibleAnnotations 13. CONSTANT_Utf8: Ljava/lang/Deprecated; Die RuntimeVisibleAnnotation-Attribute enthalten weiterhin die Bytes
Listing 5.15. ; DeprecatedJasminExample.j .bytecode 50.0 .class public DeprecatedJasminExample .super java/lang/Object .deprecated .field private static final s Ljava/lang/String; = "s marked deprecated" .deprecated .annotation visible Ljava/lang/Deprecated; .end annotation .end field .method public <init>()V .limit stack 1 .limit locals 1 aload_0 invokespecial java/lang/Object/<init>()V return .end method .method public static method1()V .limit stack 2 .limit locals 0 getstatic java/lang/System/out Ljava/io/PrintStream; ldc "method1 marked deprecated." invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V return .deprecated .annotation visible Ljava/lang/Deprecated; .end annotation .end method
Signaturen werden verwendet bzw. benötigt, um Typinformationen für z.B. generische Typen oder parametrisierte Typen zu codieren (siehe dazu Abschnitt 3). Die Codierung von Signaturen erfolgt dabei durch Zeichenketten. Die Zeichenkette <E:Ljava/lang/Object;>Ljava/lang/Object; ist z.B. eine Klassensignatur zur folgenden Klassendeklaration: public class SignatureExample<E> { } Die Typinformationen in Form einer Signatur können z.B. durch die Reflection-API, von Debuggern oder von einem Java-Compiler verwendet werden. Als Ausgangspunkt zur weiteren Beschreibung soll der nachfolgende Java-Quelltext dienen. /* SignatureExample.java */ public class SignatureExample<E> { private E value; public void set(E v) { value = v; } public E get() { return value; } } Wird .signature "<signature>" Im Beispiel werden insgesamt vier derartige Byteabschnitte eingefügt (Klassensignatur, Typvariablensignatur und zwei Methodensignaturen). Die Signaturen werden durch eine Grammatik spezifiziert (siehe dazu "The Java Virtual Machine Specification, Third Edition"). Die zum Beispiel benötigten vier Signaturen lauten: Klassensignatur: <E:Ljava/lang/Object;>Ljava/lang/Object; Name des (formalen) Typparameters: E Obere Schranke des Typparameters (Bound): Ljava/lang/Object; (angegeben als Klassentypsignatur) Superklassensignatur: Ljava/lang/Object; (Klassentypsignatur) Typvariablensignatur: TE; Methodensignaturen: (TE;)V ()TE; Die Klassensignatur entählt in spitzen Klammern den Namen des Typparameters
und dessen obere Schranke. Im Beispiel wird als obere Schranke und als
Superklassensignatur public class SignatureExample<E extends Number> extends Test { } <E:Ljava/lang/Number;>LTest; Für das Feld Listing 5.16. ; SignatureExample.j .bytecode 50.0 .class public SignatureExample .super java/lang/Object .signature "<E:Ljava/lang/Object;>Ljava/lang/Object;" .field private value Ljava/lang/Object; .signature "TE;" .end field .method public <init>()V .limit stack 1 .limit locals 1 aload_0 invokespecial java/lang/Object/<init>()V return .end method .method public set(Ljava/lang/Object;)V .limit stack 2 .limit locals 2 aload_0 aload_1 putfield SignatureExample/value Ljava/lang/Object; return .signature "(TE;)V" .end method .method public get()Ljava/lang/Object; .limit stack 1 .limit locals 1 aload_0 getfield SignatureExample/value Ljava/lang/Object; areturn .signature "()TE;" .end method
Realisieren von inneren Klassen
mit den Direktive Eine Klasse kann innerhalb einer anderen Klasse definiert werden (innere
Klasse). Im folgenden Java-Quelltext wird die Klasse /* InnerExample.java */ public class InnerExample { int a; public class Inner { int b; } } Die Übersetzung mit .inner class <modifiers> [<name>] inner <innername> [outer <outername>] .inner interface <modifiers> [<name>] inner <innername> [outer <outername>] Um die beiden im Beispiel erzeugten Klassendatein auf Assemblerebene
erzeugen zu können, ist für jede Klassendatei ein eigener Jasmin-Quelltext
notwendig. Die umschließende Klasse Listing 5.17. ; InnerExample.j .bytecode 50.0 .class public InnerExample .super java/lang/Object .inner class public Inner inner InnerExample$Inner outer InnerExample .field a I .method public <init>()V .limit stack 1 .limit locals 1 aload_0 invokespecial java/lang/Object/<init>()V return .end method Der Quelltext einhält eine Listing 5.18. ; InnerExample$Inner.j .bytecode 50.0 .class public InnerExample$Inner .super java/lang/Object .inner class public Inner inner InnerExample$Inner outer InnerExample .field b I .field final synthetic this$0 LInnerExample; .method public <init>(LInnerExample;)V .limit stack 2 .limit locals 2 aload_0 aload_1 putfield InnerExample$Inner/this$0 LInnerExample; aload_0 invokespecial java/lang/Object/<init>()V return .end method Mit /* InnerExample2.java */ public class InnerExample2 { int a; public void method1() { new InnerExample() { int b; }; } } Innerhalb der Methode Listing 5.19. ; InnerExample2.j .bytecode 50.0 .class public InnerExample2 .super java/lang/Object .inner class final inner InnerExample2$1 .field a I .method public <init>()V .limit stack 1 .limit locals 1 aload_0 invokespecial java/lang/Object/<init>()V return .end method .method public method1()V .limit stack 3 .limit locals 1 new InnerExample2$1 dup aload_0 invokespecial InnerExample2$1/<init>(LInnerExample2;)V pop return .end method Der Assemblerquelltext .enclosing method <methodname><methoddescriptor> Für Listing 5.20. ; InnerExample2$1.j .bytecode 50.0 .class final InnerExample2$1 .super InnerExample .enclosing method InnerExample2/method1()V .inner class final inner InnerExample2$1 .field b I .field final synthetic this$0 LInnerExample2; .method <init>(LInnerExample2;)V .limit stack 2 .limit locals 2 aload_0 aload_1 putfield InnerExample2$1/this$0 LInnerExample2; aload_0 invokespecial InnerExample/<init>()V return .end method
Realisierung einer StackMapTable mit der
Direktive Mit der Direktive .stack [offset {<offset> | <label>}] [locals <verificationtype> [<verificationarg>]] ... [stack <verificationtype> [<verificationarg>]] ... .end stack .stack use <integer> locals [offset {<offset> | <label>}] [stack <verificationtype> [<verificationarg>]] ... .end stack Nach dem Schlüsselwort Top Integer Float Long Double Null UninitializedThis Object Uninitialized Als Beispiel für die Definition eines Stack-Map-Frames soll der folgende Ausschnitt aus Listing 5.21 dienen, der sich auf die Bytecodestelle 30 (Index im Code-Array) der zugehörigen Methode bezieht. .stack offset 30 locals Object [Ljava/lang/String; locals Object StackExample locals Object java/lang/Thread stack Object java/lang/InterruptedException .end stack Die Direktive /* StackExample.java */ public class StackExample implements Runnable { public void run() { System.out.println("run..."); } public static void main(String[] args) { StackExample se = new StackExample(); Thread t = new Thread(se); t.start(); try { Thread.sleep(5000); } catch (InterruptedException e) { System.out.println(e.toString()); } } } Im Ausschnitt aus dem Hexdump von ca fe ba be 00 00 00 32 00 3b 0a 00 0f 00 1f 09 .......2.;...... 0 00 20 00 21 08 00 22 0a 00 23 00 24 07 00 25 0a . .!.."..#.$..%. 16 ... 53 74 72 69 6e 67 3b 00 21 00 05 00 0f 00 01 00 String;.!....... 576 10 00 00 00 03 00 01 00 11 00 12 00 01 00 13 00 ................ 592 00 00 1d 00 01 00 01 00 00 00 05 2a b7 00 01 b1 ...........*.... 608 00 00 00 01 00 14 00 00 00 06 00 01 00 00 00 03 ................ 624 00 01 00 15 00 12 00 01 00 13 00 00 00 25 00 02 .............%.. 640 00 01 00 00 00 09 b2 00 02 12 03 b6 00 04 b1 00 ................ 656 00 00 01 00 14 00 00 00 0a 00 02 00 00 00 06 00 ................ 672 08 00 07 00 09 00 16 00 17 00 01 00 13 00 00 00 ................ 688 82 00 03 00 04 00 00 00 2a bb 00 05 59 b7 00 06 ........*...Y... 704 4c bb 00 07 59 2b b7 00 08 4d 2c b6 00 09 14 00 L...Y+...M,..... 720 0a b8 00 0c a7 00 0e 4e b2 00 02 2d b6 00 0e b6 .......N...-.... 736 00 04 b1 00 01 00 15 00 1b 00 1e 00 0d 00 02 00 ................ 752 14 00 00 00 22 00 08 00 00 00 0a 00 08 00 0c 00 ...."........... 768 11 00 0d 00 15 00 0f 00 1b 00 12 00 1e 00 10 00 ................ 784 1f 00 11 00 29 00 13 00 18 00 00 00 16 00 02 ff ....)........... 800 00 1e 00 03 07 00 19 07 00 1a 07 00 1b 00 01 07 ................ 816 00 1c 0a 00 01 00 1d 00 00 00 02 00 1e ............. 832 Das StackMapTable-Attribut beginnt mit den beiden Bytes 1. Frame (Typ: full_frame): ff 00 1e 00 03 07 00 19 07 00 1a 07 00 1b 00 01 07 00 1c 2. Frame (Typ: same_frame): 0a Der zweite Frame besteht lediglich aus dem einzigen Byte Listing 5.21. ; StackExample.j .bytecode 50.0 .class public StackExample .super java/lang/Object .implements java/lang/Runnable .method public <init>()V .limit stack 1 .limit locals 1 0: aload_0 1: invokespecial java/lang/Object/<init>()V 4: return .end method .method public run()V .limit stack 2 .limit locals 1 0: getstatic java/lang/System/out Ljava/io/PrintStream; 3: ldc "run..." 5: invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V 8: return .end method .method public static main([Ljava/lang/String;)V .limit stack 3 .limit locals 4 0: new StackExample 3: dup 4: invokespecial StackExample/<init>()V 7: astore_1 8: new java/lang/Thread 11: dup 12: aload_1 13: invokespecial java/lang/Thread/<init>(Ljava/lang/Runnable;)V 16: astore_2 17: aload_2 18: invokevirtual java/lang/Thread/start()V Label21: 21: ldc2_w 5000 24: invokestatic java/lang/Thread/sleep(J)V Label27: 27: goto Label41 Label30: 30: astore_3 31: getstatic java/lang/System/out Ljava/io/PrintStream; 34: aload_3 35: invokevirtual java/lang/InterruptedException/toString()Ljava/lang/String; 38: invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V Label41: 41: return .catch java/lang/InterruptedException from Label21 to Label27 using Label30 ; full_frame (frameNumber = 0) .stack offset 30 locals Object [Ljava/lang/String; locals Object StackExample locals Object java/lang/Thread stack Object java/lang/InterruptedException .end stack ; same_frame (frameNumber = 1) .stack offset 41 locals Object [Ljava/lang/String; locals Object StackExample locals Object java/lang/Thread .end stack .end method Zur Definition des zweiten Frames vom Typ .stack use 3 locals offset 41 .end stack |
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||