Mit Listing 4.3 wurde ein kurzes Java-Programm
vorgestellt, mit dem der Konstantenpool einer http://jakarta.apache.org/bcel/ bezogen werden. BCEL ist aber weit mehr als ein Analyse-API für Klassendateien.
Mit Hilfe der Bibliothek kann eine Klassendatei von Grund auf zusammengesetzt werden.
So können z.B. mit Hilfe eines Kostruktoraufrufs der Klasse ClassGen cg = new ClassGen("BCELSynthesizeExample", "java.lang.Object", "BCELSynthesizeExample.java", ACC_PUBLIC | ACC_SUPER, null); Die folgende Abbildung beinhaltet ein Klassendiagramm wichtiger BCEL-Klassen, die zum Analaysieren und Synthetisieren von Klassendateien genutzt werden können. Abbildung 4.6. Klassendiagramm wichtiger BCEL-Klassen, die beim Analysieren (linker Teil der Klassenhierarchie) und Synthetisieren von Klassendateien (rechter Teil) zur Anwendung kommen. Nachdem BCEL heruntergeladen wurde, wird das Archiv lib/ext [innerhalb der JRE] jre/lib/ext [innerhalb des JDK] In Abschnitt 4.2 wurde die Unterteilung einer Klassendatei in zusammenhängende Byte-Blöcke geschildert. Die folgenden Unterabschnitte bauen auf diesen Erläuterungen auf. Versionen (BCEL): Die Homepage von BCEL war bis zur Version 4.x http://bcel.sourceforge.net . Ab der Version 5.0 wird BCEL im Rahmen eines Apache-Jakarta-Projekts weiterentwickelt. Unter Verwendung der BCEL soll ein kurzes Java-Programm erstellt werden,
das die einzelnen Bereiche einer Klassendatei analysiert und das Ergebnis auf
dem Bildschirm ausgibt. Dazu muss zunächst eine
Einlesen von Bytecode Die Klasse org.apache.bcel.classfile Class ClassParser EXT API: BCEL 5.2 Konstruktor: ClassParser(String file_name) ClassParser(String zip_file, String file_name) Methode: JavaClass parse() Die Methode try { ClassParser parser = new ClassParser("Test.class"); JavaClass clazz = parser.parse(); } catch (Exception e) { System.out.println(e.toString()); }
Auslesen einzelner Teilbereiche des Bytecodes Nachdem nun die Informationen der Klassendatei auf ein Objekt vom Typ
org.apache.bcel.classfile Class JavaClass EXT API: BCEL 5.2 Methode: byte[] getBytes() String getFileName() int getMinor() int getMajor() ConstantPool getConstantPool() int getClassNameIndex() int getSuperclassNameIndex() int[] getInterfaceIndices() Field[] getFields() Method[] getMethods() Attribute[] getAttributes() Auch ein Hexdump der zu untersuchenden Klassendatei kann mit der Methode
org.apache.bcel.classfile Class AccessFlags EXT API: BCEL 5.2 Methode: int getAccessFlags()
Einzelne Konstanten aus dem Konstantenpool auslesen Durch Aufruf von der Methode org.apache.bcel.classfile Class ConstantPool EXT API: BCEL 5.2 Methode: int getLength() Constant getConstant(int index) Die Klasse ConstantPool cp = clazz.getConstantPool(); int cpl = cp.getLength() - 1; System.out.println("Anzahl der Konstanten im Konstantenpool: " + cpl); for (int i = 1; i <= cpl; i++) { Constant c = cp.getConstant(i); System.out.println(i + ". Konstante: " + c.toString()); }
Auslesen von Feld- und Methodeninformationen Die abstrakte Klasse org.apache.bcel.classfile Class FieldOrMethod EXT API: BCEL 5.2 Methode: int getNameIndex() String getName() int getSignatureIndex() String getSignature() Attribute[] getAttributes() Mit Field[] f = clazz.getFields(); System.out.println("Anzahl der Felder der Klasse: " + f.length); for (int i = 0; i < f.length; i++) { System.out.println((i+1) + ". Feld:"); System.out.println(" Zugangscode: " + f[i].getAccessFlags()); System.out.println(" Name und Index im Konstantenpool: " + f[i].getName() + ", " + f[i].getNameIndex()); System.out.println(" Deskriptor und Index im Konstantenpool: " + f[i].getSignature() + ", " + f[i].getSignatureIndex()); Attribute[] fieldAttr = f[i].getAttributes(); System.out.println(" Anzahl der Feld-Attribute: " + fieldAttr.length); } Method[] m = clazz.getMethods(); System.out.println("Anzahl der Methoden der Klasse: " + m.length); for (int i = 0; i < m.length; i++) { System.out.println(i+1) + ". Methode:"); System.out.println(" Zugangscode: " + m[i].getAccessFlags()); System.out.println(" Name und Index im Konstantenpool: " + m[i].getName() + ", " + m[i].getNameIndex()); System.out.println(" Deskriptor und Index im Konstantenpool: " + m[i].getSignature() + ", " + m[i].getSignatureIndex()); Attribute[] methodAttr = m[i].getAttributes(); System.out.println(" Anzahl der Methoden-Attribute: " + methodAttr.length); }
Auslesen von Attributinformationen Für Attribute, wie sie in der Java Virtual Machine Specification definiert
werden, wird innerhalb der BCEL eine eigene Klasse definiert. Für Attribute,
die nicht standardmäßig durch die Spezifikation definiert sind, wird die Klasse
Die abstrakte Superklasse org.apache.bcel.classfile Class Attribute EXT API: BCEL 5.2 Methode: int getNameIndex() byte getTag() String toString() Durch Aufruf von
Diese Tags sind innerhalb des Interfaces org.apache.bcel Interface Constants EXT API: BCEL 5.2 Feld: static byte ATTR_SOURCE_FILE static byte ATTR_CONSTANT_VALUE static byte ATTR_CODE static byte ATTR_EXCEPTIONS static byte ATTR_LINE_NUMBER_TABLE static byte ATTR_LOCAL_VARIABLE_TABLE static byte ATTR_INNER_CLASSES static byte ATTR_SYNTHETIC static byte ATTR_DEPRECATED static byte ATTR_SIGNATURE static byte ATTR_UNKNOWN Attribute können in verschiedenen Bereichen einer Klassendatei auftreten:
Feldbereich, Methodenbereich oder Klassenbereich (Attributabschnitt direkt im
Anschluss an den Methodenbereich). Attribute können auch innerhalb von Attributen
selbst auftreten, so ist z.B. ein LineNumberTable-Attribut innerhalb eines
Code-Attributs zulässig. BCEL definiert für jeden dieser Bereich eine Klasse, in
der jeweils eine Methode Attribute[] classAttr = clazz.getAttributes(); Field[] f = clazz.getFields(); Attribute[] fieldAttr = f[0].getAttributes(); Attribute[] fieldAttr = f[1].getAttributes(); ... Method[] m = clazz.getMethods(); Attribute[] methodAttr = m[0].getAttributes(); Attribute[] methodAttr = m[1].getAttributes(); ... Code codeAttr = m[0].getCode(); Attribute[] codeAttrAttr = codeAttr.getAttributes(); Code codeAttr = m[1].getCode(); Attribute[] codeAttrAttr = codeAttr.getAttributes(); ... Die Elemente (Objekte) des Method[] m = clazz.getMethods(); Attribute[] methodAttr = m[0].getAttributes(); ... if (methodAttr[0] instanceof Code) { ... } if (methodAttr[0] instanceof Exceptions { ... } ... byte tag = methodAttr[0].getTag(); if (tag == Constants.ATTR_CODE) { ... } if (tag == Constants.ATTR_EXCEPTIONS) { ... } ... Der Listing 4.4. /* BCELAnalyzeExample.java */ import org.apache.bcel.*; import org.apache.bcel.classfile.*; public class BCELAnalyzeExample { public static void main(String[] args) { JavaClass clazz = null; try { ClassParser parser = new ClassParser("BytecodeExample.class"); clazz = parser.parse(); } catch (Exception e) { System.out.println(e.toString()); } Method[] m = clazz.getMethods(); for (int i = 0; i < m.length; i++) { Attribute[] methodAttr = m[i].getAttributes(); for (int j = 0; j < methodAttr.length; j++) { byte tag = methodAttr[j].getTag(); if (tag == Constants.ATTR_CODE) { Code c = (Code)methodAttr[j]; byte[] ca = c.getCode(); System.out.print((i+1) + ". Methode, Code-Bytes: "); for (int k = 0; k < ca.length; k++) { System.out.print(ca[k] + " "); } System.out.println(); } } } } } Das Beispiel erzeugt die Ausgabe: 1. Methode, Code-Bytes: 42 -73 0 1 -79 2. Methode, Code-Bytes: 3 59 3 60 27 16 10 -94 0 13 26 5 96 59 -124 1 1 -89 -1 -13 -78 0 2 26 -74 0 3 -79 3. Methode, Code-Bytes: -72 0 4 -79 Die ausgegebenen Zahlenwerte liegen im Bereich von -128 bis 127. In hexadezimaler Schreibweise lauten sie (siehe dazu auch Abschnitt 4.2.2): 2a b7 00 01 b1 03 3b 03 3c 1b 10 0a a2 00 0d 1a 05 60 3b 84 01 01 a7 ff f3 b2 00 02 1a b6 00 03 b1 b8 00 04 b1 Innerhalb des Listings wird die Methode org.apache.bcel.classfile Class Code EXT API: BCEL 5.2 Methode: Attribute[] getAttributes() byte[] getCode() CodeException[] getExceptionTable() LineNumberTable getLineNumberTable() LocalVariableTable getLocalVariableTable() int getMaxLocals() int getMaxStack() String toString() Der Aufruf von Code c = (Code)methodAttr[j]; System.out.println(c.toString()); // neu vorgenommen, kann die folgende Ausgabe erzeugt werden: Code(max_stack = 2, max_locals = 2, code_length = 28) 0: iconst_0 1: istore_0 2: iconst_0 3: istore_1 4: iload_1 5: bipush 10 7: if_icmpge 20 10: iload_0 11: iconst_2 12: iadd 13: istore_0 14: iinc 1, 1 17: goto 4 20: getstatic java.lang.System.out Ljava/io/PrintStream; (2) 23: iload_0 24: invokevirtual java.io.PrintStream.println (I)V (3) 27: return Attribute(s) = LineNumber(0, 6), LineNumber(2, 7), LineNumber(10, 8), LineNumber(14, 7), LineNumber(20, 10), LineNumber(27, 11) Eine komplette Beschreibung der Methoden, die zum Auslesen von Attributinformationen zur Verfügung stehen, kann innerhalb der API-Dokumentation zu BCEL nachgelesen werden. Die Dokumentation ist ebenfalls auf den BCEL-Seiten im Internet vorhanden.
Beispielprogramm zur Analyse einer Klassendatei Das kurze Beispielprgramm aus Listing 4.4 nutzt
bereits die Bytecode Engineering Library, um die Bytecode-Repräsentation von
JVM-Befehlen aus einer Klassendatei auszulesen. Das folgenden
Listing 4.5 ist ein Analyse-Tool, mit dem eine vorliegende
java ClassFileAnalyzer [options] [jarfile] class options -hex Die Ausgabe enthält einen Hexdump der Klassendatei. -ai Die Ausgabe enthält Attribut-Informationen. -f Die Ausgabe erfolgt nicht auf dem Bildschirm, sondern wird in eine Textdatei geschrieben (bei längeren Analyseausgaben). jarfile Ein JAR-Archiv, in dem sich die zu analysierende Klassendatei class befindet. class Die zu analysierende Klassendatei. Das Analyse-Tool Listing 4.5. /* ClassFileAnalyzer.java */ import org.apache.bcel.*; import org.apache.bcel.classfile.*; import java.util.*; import java.io.*; public class ClassFileAnalyzer { JavaClass clazz; ConstantPool cp; StringBuffer out = new StringBuffer(); public void getClazz(String s1, String s2) { ClassParser parser; try { if (s2 == null) { parser = new ClassParser(s1); } else { parser = new ClassParser(s1, s2); } clazz = parser.parse(); out.append("Analyze " + clazz.getFileName() + "\n\n"); } catch (Exception e) { System.out.println(e.toString()); System.out.println("Die angegebene Datei kann nicht eingelesen werden!"); System.exit(2); } } public void hexDump(String str) { int bi; byte[] b = clazz.getBytes(); out.append("Hexdump (" + b.length + " Bytes):\n"); for (int i = 0; i < b.length; i++) { bi = (new Byte(b[i])).intValue(); bi = bi & 0x000000ff; String s = Integer.toHexString(bi); if (s.length() == 1) { s = "0" + s; } out.append(s + " "); if (((i+1) % 16) == 0) { out.append(" " + (i-15) + "\n"); } } out.append("\n\n"); } public void getMinorMajor() { int minor = clazz.getMinor(); int major = clazz.getMajor(); out.append("Version des Klassendatei-Formats: " + major + "." + minor + "\n\n"); } public void getConstants() { cp = clazz.getConstantPool(); int cpl = cp.getLength() - 1; out.append("Anzahl der Konstanten im Konstantenpool: " + cpl + "\n"); for (int i = 1; i <= cpl; i++) { Constant c = cp.getConstant(i); if (c != null) { // Long- und Double-Konstanten beachten! out.append(i + ". Konstante: " + c.toString() + "\n"); } } out.append("\n"); } public void getAccessThisSuper() { int caf = clazz.getAccessFlags(); out.append("Klassen-Zugangscode: " + caf + "\n"); int cni = clazz.getClassNameIndex(); out.append("Klasse (Index im Konstantenpool): " + cni + "\n"); int sni = clazz.getSuperclassNameIndex(); out.append("Superklasse (Index im Konstantenpool): " + sni + "\n\n"); } public void getInterfaces() { int[] ii = clazz.getInterfaceIndices(); out.append("Anzahl der implementierten Interfaces: " + ii.length + "\n"); if (ii.length > 0) { out.append("Implementierte Interfaces (Index im Konstantenpool): "); for (int i = 0; i < ii.length; i++) { out.append(ii[i] + " "); } out.append("\n\n"); } } public void getFields(boolean b) { Field[] f = clazz.getFields(); out.append("Anzahl der Felder der Klasse: " + f.length + "\n"); for (int i = 0; i < f.length; i++) { out.append((i+1) + ". Feld:\n"); out.append(" Zugangscode: " + f[i].getAccessFlags() + "\n"); out.append(" Name und Index im Konstantenpool: " + f[i].getName() + ", " + f[i].getNameIndex() + "\n"); out.append(" Deskriptor und Index im Konstantenpool: " + f[i].getSignature() + ", " + f[i].getSignatureIndex() + "\n"); Attribute[] fieldAttr = f[i].getAttributes(); out.append(" Anzahl der Feld-Attribute: " + fieldAttr.length + "\n"); for (int j = 0; j < fieldAttr.length; j++) { getAttributeInfo(fieldAttr[j], j, " ", b); } } out.append("\n"); } public void getMethods(boolean b) { Method[] m = clazz.getMethods(); out.append("Anzahl der Methoden der Klasse: " + m.length + "\n"); for (int i = 0; i < m.length; i++) { out.append((i+1) + ". Methode:\n"); out.append(" Zugangscode: " + m[i].getAccessFlags() + "\n"); out.append(" Name und Index im Konstantenpool: " + m[i].getName() + ", " + m[i].getNameIndex() + "\n"); out.append(" Deskriptor und Index im Konstantenpool: " + m[i].getSignature() + ", " + m[i].getSignatureIndex() + "\n"); Attribute[] methodAttr = m[i].getAttributes(); out.append(" Anzahl der Methoden-Attribute: " + methodAttr.length + "\n"); for (int j = 0; j < methodAttr.length; j ++) { getAttributeInfo(methodAttr[j], j, " ", b); } } out.append("\n"); } public void getClassAttributes(boolean b) { Attribute[] classAttr = clazz.getAttributes(); out.append("Anzahl der Attribute der Klasse: " + classAttr.length + "\n"); for (int j = 0; j < classAttr.length; j++) { getAttributeInfo(classAttr[j], j, "", b); } } public void getAttributeInfo(Attribute attr, int index, String spaces, boolean info) { int fni = attr.getNameIndex(); out.append(spaces + (index+1) + ". Attribut:\n"); ConstantUtf8 cutf8 = (ConstantUtf8)cp.getConstant(fni, Constants.CONSTANT_Utf8); out.append(spaces + " Typ und Index im Konstantenpool: " + cutf8.getBytes() + ", " + fni + "\n"); if (info) { byte tag = attr.getTag(); String attrStr = attr.toString(); // Texteinrueckung String[] asa = attrStr.split("\n"); out.append(spaces + " Attribut-Beschreibung (tag=" + tag + "):\n"); for (int i = 0; i < asa.length; i++) { asa[i] = asa[i].trim(); if (!asa[i].equals("")) out.append(spaces + " " + asa[i] + "\n"); } } } public void output(boolean b, String s) { if (b) { int i = s.lastIndexOf("/"); s = s.substring(i+1); String str = s + ".txt"; try { FileWriter fw = new FileWriter(str); fw.write(out.toString()); fw.flush(); fw.close(); } catch (Exception e) { e.printStackTrace(); } System.out.println("Datei " + str + " im aktuellen Verzeichnis erstellt."); } else { System.out.println(out.toString()); } } public static void main(String[] args) { boolean fileOutput = false; boolean attrInfo = false; boolean hexDump = false; boolean option = true; String fileName = ""; Vector<String> options = new Vector<String>(); Vector<String> sources = new Vector<String>(); ClassFileAnalyzer analyzer = new ClassFileAnalyzer(); for (int i = 0; i < args.length; i++) { if ((args[i].startsWith("-")) && (option == true)) { options.add(args[i]); } else { option = false; sources.add(args[i]); } } for (int j = 0; j < options.size(); j++) { String str = options.get(j); if (str.equals("-f")) fileOutput = true; if (str.equals("-ai")) attrInfo = true; if (str.equals("-hex")) hexDump = true; } if (sources.size() == 1) { analyzer.getClazz(sources.get(0), null); fileName = sources.get(0); } else if (sources.size() == 2) { analyzer.getClazz(sources.get(0), sources.get(1)); fileName = sources.get(1); } else { System.out.println("usage: java ClassFileAnalyzer " + "[options] [jarfile] class"); System.exit(1); } if (hexDump) { analyzer.hexDump(fileName); } analyzer.getMinorMajor(); analyzer.getConstants(); analyzer.getAccessThisSuper(); analyzer.getInterfaces(); analyzer.getFields(attrInfo); analyzer.getMethods(attrInfo); analyzer.getClassAttributes(attrInfo); analyzer.output(fileOutput, fileName); } } Mit dem Beispielaufruf > java ClassFileAnalyzer -f -ai -hex c:/jdk/jre/lib/rt.jar java/lang/String.class kann die Klassendatei Analyze java/lang/String.class Hexdump (14783 Bytes): ca fe ba be 00 00 00 31 01 a4 03 00 00 ff ff 03 0 00 01 00 00 03 00 10 ff ff 08 00 42 08 00 45 08 16 00 5b 08 00 87 08 00 8b 08 00 a4 08 00 a6 01 00 32 ... Version des Klassendatei-Formats: 49.0 Anzahl der Konstanten im Konstantenpool: 419 1. Konstante: CONSTANT_Integer[3](bytes = 65535) 2. Konstante: CONSTANT_Integer[3](bytes = 65536) ... Klassen-Zugangscode: 49 Klasse (Index im Konstantenpool): 188 Superklasse (Index im Konstantenpool): 187 Anzahl der implementierten Interfaces: 3 Implementierte Interfaces (Index im Konstantenpool): 172 177 175 Anzahl der Felder der Klasse: 7 1. Feld: Zugangscode: 18 Name und Index im Konstantenpool: value, 167 Deskriptor und Index im Konstantenpool: [C, 63 Anzahl der Feld-Attribute: 0 ... Anzahl der Methoden der Klasse: 83 1. Methode: Zugangscode: 1 Name und Index im Konstantenpool: <init>, 50 Deskriptor und Index im Konstantenpool: ()V, 13 Anzahl der Methoden-Attribute: 1 1. Attribut: Typ und Index im Konstantenpool: Code, 52 Attribut-Beschreibung (tag=2): Code(max_stack = 2, max_locals = 1, code_length = 22) 0: aload_0 1: invokespecial java.lang.Object.<init> ()V (362) 4: aload_0 ... Anzahl der Attribute der Klasse: 3 1. Attribut: Typ und Index im Konstantenpool: SourceFile, 62 Attribut-Beschreibung (tag=0): SourceFile(String.java) ...
Die Utility-Klasse Mit Listing 4.5 kann eine Klassendatei analysiert
werden, wobei die Ausgabe auf dem Bildschirm oder in eine Textdatei erfolgen kann.
BCEL enthält innerhalb des Pakets > java org.apache.bcel.util.Class2HTML BytecodeExample.class Durch die Anweisung werden automatisch die folgenden fünf html-Datein ins aktuellen Verzeichnis geschrieben: BytecodeExample.html BytecodeExample_attributes.html BytecodeExample_code.html BytecodeExample_cp.html BytecodeExample_methods.html Versionen (JDK 6):
Es kann zu einer Fehlermeldung bei der Erzeugung der html-Dateien kommen, falls
Klassendateien mit Die html-Datei Abbildung 4.8. Browseranzeige von Die im "Gen-Teil" der BCEL API bereitgestellten Klassen wie public class BCELSynthesizeExample0 { public static void main(String[] args) { System.out.println("Hallo Apache BCEL!"); } } Die Klasse org.apache.bcel.generic Class ClassGen EXT API: BCEL 5.2 Konstruktor: ClassGen(String class_name, String super_class_name, String file_name, int access_flags, String[] interfaces) Methode: ConstantPoolGen getConstantPool() void addEmptyConstructor(int access_flags) void addMethod(Method m) JavaClass getJavaClass() Die zu erstellende Klasse soll den Namen org.apache.bcel Interface Constants EXT API: BCEL 5.2 Feld: static short ACC_PUBLIC static short ACC_SUPER static short GETSTATIC static short INVOKEVIRTUAL Im Beispiel aus Listing 4.6 wird
dieses Interface, das nur Konstanten enthält, durch die Beispielklasse implementiert
und erbt damit alle Konstanten des Interfaces. Das ist sinnvoll, da viele Konstanten
im Interface zusammengefasst sind. Jeder Klasse, die dieses Interface implementiert,
stehen damit benötigte Konstanten zur Verfügung. Die Konstante ClassGen cg = new ClassGen("BCELSynthesizeExample", "java.lang.Object", "BCELSynthesizeExample.java", ACC_PUBLIC | ACC_SUPER, null); Mit getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; ldc #3; //String Hallo Apache BCEL! invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V return wie die Disassemblierung der Klassendatei org.apache.bcel.generic Class InstructionFactory EXT API: BCEL 5.2 Konstruktor: InstructionFactory(ConstantPoolGen cp) Methode: FieldInstruction createFieldAccess(String class_name, String name, Type type, short kind) InvokeInstruction createInvoke(String class_name, String name, Type ret_type, Type[] arg_types, short kind) static ReturnInstruction createReturn(Type type) Mit dem angegebenen Konstruktor kann ein Objekt der Factory erhalten werden.
Dazu wird ein Objekte vom Typ ConstantPoolGen cp = cg.getConstantPool(); InstructionFactory ifactory = new InstructionFactory(cp); // getstatic FieldInstruction inst1 = ifactory.createFieldAccess( "java.lang.System", "out", new ObjectType("java.io.PrintStream"), GETSTATIC); // invokevirtual InvokeInstruction inst3 = ifactory.createInvoke( "java.io.PrintStream", "println", Type.VOID, new Type[] { Type.STRING }, INVOKEVIRTUAL); // return ReturnInstruction inst4 = InstructionFactory.createReturn(Type.VOID); Die ersten beiden Argumente, die den Methoden System.out.println(new ObjectType("java.io.PrintStream").getSignature()); ausgebenen werden und lautet: Die BCEL API stellt auch Wrapper-Klassen für virtuelle Instruktionen zur
Verfügung (Compound Instruction). So kann z.B. die Wrapper-Klasse org.apache.bcel.generic Class PUSH EXT API: BCEL 5.2 Konstruktor: PUSH(ConstantPoolGen cp, String value) PUSH(ConstantPoolGen cp, int value) Methode: Instruction getInstruction() Die folgenden Zeilen zeigen die flexible Anwendung der Wrapper-Klasse
PUSH p1 = new PUSH(cp, "Hallo!"); PUSH p2 = new PUSH(cp, 127); PUSH p3 = new PUSH(cp, 3); Instruction i1 = p1.getInstruction(); Instruction i2 = p2.getInstruction(); Instruction i3 = p3.getInstruction(); System.out.println(i1.toString()); System.out.println(i2.toString()); System.out.println(i3.toString()); Ausgabe (ohne Operandenbytes): ldc[18](2) bipush[16](2) iconst_3[6](1) Die Ausgabe enthält in eckigen Klammern den Operationscode des JVM-Befehls
und anchließend in runden Klammern die Länge des gesamten JVM-Befehls, wobei die
Längenangabe mögliche Operandenbytes berücksichtigt. Zusammen mit der Klasse
CompoundInstruction inst2 = new PUSH(cp, "Hallo Apache BCEL!"); Die im Laufe der Beschreibung erzeugten Objekte org.apache.bcel.generic Class InstructionList EXT API: BCEL 5.2 Konstruktor: InstructionList() Methode: InstructionHandle append(Instruction i) InstructionHandle append(CompoundInstruction c) void dispose() Ein Objekt der Instruktions-Liste org.apache.bcel.generic Class MethodGen EXT API: BCEL 5.2 Konstruktor: MethodGen(int access_flags, Type return_type, Type[] arg_types, String[] arg_names, String method_name, String class_name, InstructionList il, ConstantPoolGen cp) Methode: void setMaxStack() void setMaxLocals() Method getMethod() Im Beispiel könnte die Erzeugung eines Objekts für die MethodGen mainMethod = new MethodGen(ACC_PUBLIC | ACC_STATIC, Type.VOID, new Type[] { new ArrayType(Type.STRING, 1) }, new String[] { "args" }, "main", "BCELSynthesizeExample", il, cp); Das zweite Argument, des verwendeten Konstruktors der Klasse Listing 4.6. /* BCELSynthesizeExampleGenerator.java */ import org.apache.bcel.*; import org.apache.bcel.generic.*; public class BCELSynthesizeExampleGenerator implements Constants { public static void main(String[] args) { ClassGen cg = new ClassGen("BCELSynthesizeExample", "java.lang.Object", "BCELSynthesizeExample.java", ACC_PUBLIC | ACC_SUPER, null); ConstantPoolGen cp = cg.getConstantPool(); // leeren Konstruktor hinzufuegen cg.addEmptyConstructor(ACC_PUBLIC); // main-Methode erstellen InstructionFactory ifactory = new InstructionFactory(cp); FieldInstruction inst1 = ifactory.createFieldAccess( "java.lang.System", "out", new ObjectType("java.io.PrintStream"), GETSTATIC); CompoundInstruction inst2 = new PUSH(cp, "Hallo Apache BCEL!"); InvokeInstruction inst3 = ifactory.createInvoke( "java.io.PrintStream", "println", Type.VOID, new Type[] { Type.STRING }, INVOKEVIRTUAL); ReturnInstruction inst4 = InstructionFactory.createReturn(Type.VOID); InstructionList il = new InstructionList(); il.append(inst1); il.append(inst2); il.append(inst3); il.append(inst4); MethodGen mainMethod = new MethodGen(ACC_PUBLIC | ACC_STATIC, Type.VOID, new Type[] { new ArrayType(Type.STRING, 1) }, new String[] { "args" }, "main", "BCELSynthesizeExample", il, cp); mainMethod.setMaxStack(); mainMethod.setMaxLocals(); cg.addMethod(mainMethod.getMethod()); il.dispose(); // erstellten Bytecode in Klassendatei abspeichern try { cg.getJavaClass().dump("BCELSynthesizeExample.class"); } catch (IOException e) { System.out.println(e.toString()); } System.out.println("Datei BCELSynthesizeExample.class erstellt."); } } Die mit Hilfe der BCEL-API erstellte Datei > java BCELSynthesizeExample aufgerufen werden. Die Ausgabe auf die Konsole sollte dann wie gewünscht lauten: Hallo Apache BCEL! Innerhalb des Listings wurde u.a. InstructionFactory ifactory = new InstructionFactory(cp); InstructionList il = ifactory.createPrintln("Hallo Apache BCEL!"); Instruction[] i = il.getInstructions(); for (int j = 0; j < i.length; j++) { System.out.println(i[j].toString()); } Ausgabe (ohne Operandenbytes): getstatic[178](3) ldc[18](2) invokevirtual[182](3) Das Paket
Die Utility-Klasse Mit der Klasse > java org.apache.bcel.util.BCELifier BCELSynthesizeExample Die Anweisung hat die folgende Konsolenausgabe zur Folge: /* BCELSynthesizeExampleCreator.java */ import org.apache.bcel.generic.*; import org.apache.bcel.classfile.*; import org.apache.bcel.*; import java.io.*; public class BCELSynthesizeExampleCreator implements Constants { private InstructionFactory _factory; private ConstantPoolGen _cp; private ClassGen _cg; public BCELSynthesizeExampleCreator() { _cg = new ClassGen("BCELSynthesizeExample", "java.lang.Object", "BCELSynthesizeExample.java", ACC_PUBLIC | ACC_SUPER, new String[] { }); _cp = _cg.getConstantPool(); _factory = new InstructionFactory(_cg, _cp); } public void create(OutputStream out) throws IOException { createMethod_0(); createMethod_1(); _cg.getJavaClass().dump(out); } private void createMethod_0() { InstructionList il = new InstructionList(); MethodGen method = new MethodGen(ACC_PUBLIC, Type.VOID, Type.NO_ARGS, new String[] { }, "<init>", "BCELSynthesizeExample", il, _cp); InstructionHandle ih_0 = il.append(_factory.createLoad(Type.OBJECT, 0)); il.append(_factory.createInvoke("java.lang.Object", "<init>", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); InstructionHandle ih_4 = il.append(_factory.createReturn(Type.VOID)); method.setMaxStack(); method.setMaxLocals(); _cg.addMethod(method.getMethod()); il.dispose(); } private void createMethod_1() { InstructionList il = new InstructionList(); MethodGen method = new MethodGen( ACC_PUBLIC | ACC_STATIC, Type.VOID, new Type[] { new ArrayType(Type.STRING, 1) }, new String[] { "arg0" }, "main", "BCELSynthesizeExample", il, _cp); InstructionHandle ih_0 = il.append(_factory.createFieldAccess( "java.lang.System", "out", new ObjectType("java.io.PrintStream"), Constants.GETSTATIC)); il.append(new PUSH(_cp, "Hallo Apache BCEL!")); il.append(_factory.createInvoke("java.io.PrintStream", "println", Type.VOID, new Type[] { Type.STRING }, Constants.INVOKEVIRTUAL)); InstructionHandle ih_8 = il.append(_factory.createReturn(Type.VOID)); method.setMaxStack(); method.setMaxLocals(); _cg.addMethod(method.getMethod()); il.dispose(); } public static void main(String[] args) throws Exception { BCELSynthesizeExampleCreator creator = new BCELSynthesizeExampleCreator(); creator.create(new FileOutputStream("BCELSynthesizeExample.class")); } } Der entstandene Quelltext könnte dann wiederum compiliert und ausgeführt werden, was zu einer Abspeicherung einer Klassendatei in das aktuelle Verzeichnis führt. BCELSynthesizeExample.class (1) --> BCELSynthesizeExampleCreator.java ----+ | BCELSynthesizeExample.class (2) <-------------------------------------------+ Die neu abgespeicherte Klassendatei Bevor eine Klassendatei ausgeführt wird, überprüft die virtuelle Java-Maschine
(JVM), ob es sich bei der Datei auch wirklich um eine Klassendatei nach der
JVM-Spezifikation handelt. BCEL enthält einen eigenen Verfifier mit dem Namen
JustIce, der eine
Damit der Verifier JustIce eine Klassendatei überprüfen kann, muss zunächst
ein Objekt der Klasse org.apache.bcel.verifier Class VerifierFactory EXT API: BCEL 5.2 Methode: static Verifier getVerifier(String fully_qualified_classname) Nachdem das Objekt beschafft wurde, können nun die einzelnen
Verifikationgsschritte mit den Methoden org.apache.bcel.verifier Class Verifier EXT API: BCEL 5.2 Methode: VerificationResult doPass1() VerificationResult doPass2() VerificationResult doPass3a(int method_no) VerificationResult doPass3b(int method_no) String[] getMessages() Mit org.apache.bcel.verifier Class VerificationResult EXT API: BCEL 5.2 Feld: static int VERIFIED_OK static int VERIFIED_REJECTED static int VERIFIED_NOTYET Methode: int getStatus() String toString() Die Methode Listing 4.7. /* BCELVerifyExample.java */ import org.apache.bcel.classfile.*; import org.apache.bcel.verifier.*; public class BCELVerifyExample { public static void main(String[] args) { try { System.out.println("Klassendatei BCELSynthesizeExample.class " + "wird verifiziert:\n"); Verifier v = VerifierFactory.getVerifier("BCELSynthesizeExample"); VerificationResult vr; // Pass 1 vr = v.doPass1(); System.out.println("Pass 1:\n" + vr.toString()); // Pass 2 vr = v.doPass2(); System.out.println("Pass 2:\n" + vr.toString()); // Pass 3 if (vr.getStatus() == VerificationResult.VERIFIED_OK) { ClassParser parser = new ClassParser("BCELSynthesizeExample.class"); JavaClass clazz = parser.parse(); Method[] m = clazz.getMethods(); for (int i = 0; i < m.length; i++) { vr = v.doPass3a(i); System.out.println("Pass 3a, method " + i + " (" + m[i].toString() + "):\n" + vr.toString()); vr = v.doPass3b(i); System.out.println("Pass 3b, method " + i + " (" + m[i].toString() + "):\n" + vr.toString()); } } String[] w = v.getMessages(); if (w.length != 0) { System.out.println("Warnings:"); for (int j = 0; j < w.length; j++) { System.out.println(w[j]); } } v.flush(); } catch (Exception e) { System.out.println(e.toString()); } } } Die Ausgabe der Überprüfung lautet: Klassendatei BCELSynthesizeExample.class wird verifiziert: Pass 1: VERIFIED_OK Passed verification. Pass 2: VERIFIED_OK Passed verification. Pass 3a, method 0 (public void <init>()): VERIFIED_OK Passed verification. Pass 3b, method 0 (public void <init>()): VERIFIED_OK Passed verification. Pass 3a, method 1 (public static void main(String[] args)): VERIFIED_OK Passed verification. Pass 3b, method 1 (public static void main(String[] args)): VERIFIED_OK Passed verification. Warnings: Pass 2: Attribute '<LocalVariableTable: LocalVariable(start_pc = 0, length = 5, index = 0:BCELSynthesizeExample this)>' as an attribute of Code attribute '<CODE>' (method 'public void <init>()') will effectively be ignored and is only useful for debuggers and such. Pass 2: Attribute '<LocalVariableTable: LocalVariable(start_pc = 0, length = 9, index = 0:String[] args)>' as an attribute of Code attribute '<CODE>' (method 'public static void main(String[] args)') will effectively be ignored and is only useful for debuggers and such. Die Überprüfung des Bytecodes innerhalb einer Klassendatei auf Gültigkeit kann auch mit einer einzigen Kommandozeilenanweisung geschehen: > java org.apache.bcel.verifier.Verifier BCELSynthesizeExample.class
Fallbeispiele zu Pass 1, Pass 2 und Pass 3 Pass 1: Wird das vierte Byte innerhalb
einer gültigen Klassendatei von z.B. ca fe ba be --> ca fe ba ba Pass 1: VERIFIED_REJECTED BCELSynthesizeExample is not a Java .class file Pass 2: Der gültige Methodendeskriptor
([Ljava/lang/String;)V --> ([Ljava/lang/String;)X Pass 2: VERIFIED_REJECTED Illegal descriptor (==signature) '([Ljava/lang/String;)X' used by Method '<<Method>>'. Pass 3: Auch der Name eines Feldes wird
durch eine Utf8-Konstante gespeichert. Ist z.B. der Name des Feldes
out --> ous Pass 3a, method 1 (public static void main(String[] args)): VERIFIED_REJECTED Instruction getstatic[178](3) 20 constraint violated: Referenced field 'ous' does not exist in class 'java.lang.System'. Pass 3b, method 1 (public static void main(String[] args)): VERIFIED_NOTYET Not yet verified. |
||||||||||||||||||||||||
|
||||||||||||||||||||||||