Java asm read class file

Class ClassReader

A parser to make a ClassVisitor visit a ClassFile structure, as defined in the Java Virtual Machine Specification (JVMS). This class parses the ClassFile content and calls the appropriate visit methods of a given ClassVisitor for each field, method and bytecode instruction encountered.

Field Summary

A flag to skip the SourceFile, SourceDebugExtension, LocalVariableTable, LocalVariableTypeTable, LineNumberTable and MethodParameters attributes.

Constructor Summary

Method Summary

Makes the given visitor visit the JVMS ClassFile structure passed to the constructor of this ClassReader .

Makes the given visitor visit the JVMS ClassFile structure passed to the constructor of this ClassReader .

Returns a conservative estimate of the maximum length of the strings contained in the class’s constant pool table.

Methods inherited from class java.lang.Object

Field Details

SKIP_CODE

A flag to skip the Code attributes. If this flag is set the Code attributes are neither parsed nor visited.

SKIP_DEBUG

SKIP_FRAMES

A flag to skip the StackMap and StackMapTable attributes. If this flag is set these attributes are neither parsed nor visited (i.e. MethodVisitor.visitFrame(int, int, java.lang.Object[], int, java.lang.Object[]) is not called). This flag is useful when the ClassWriter.COMPUTE_FRAMES option is used: it avoids visiting frames that will be ignored and recomputed from scratch.

EXPAND_FRAMES

A flag to expand the stack map frames. By default stack map frames are visited in their original format (i.e. «expanded» for classes whose version is less than V1_6, and «compressed» for the other classes). If this flag is set, stack map frames are always visited in expanded format (this option adds a decompression/compression step in ClassReader and ClassWriter which degrades performance quite a lot).

b

Constructor Details

ClassReader

ClassReader

ClassReader

ClassReader

Method Details

getAccess

Returns the class’s access flags (see Opcodes ). This value may not reflect Deprecated and Synthetic flags when bytecode is before 1.5 and those flags are represented by attributes.

getClassName

getSuperName

Returns the internal name of the super class (see Type.getInternalName() ). For interfaces, the super class is Object .

getInterfaces

accept

Makes the given visitor visit the JVMS ClassFile structure passed to the constructor of this ClassReader .

accept

Makes the given visitor visit the JVMS ClassFile structure passed to the constructor of this ClassReader .

readBytecodeInstructionOffset

Handles the bytecode offset of the next instruction to be visited in accept(ClassVisitor,int) . This method is called just before the instruction and before its associated label and stack map frame, if any. The default implementation of this method does nothing. Subclasses can override this method to store the argument in a mutable field, for instance, so that MethodVisitor instances can get the bytecode offset of each visited instruction (if so, the usual concurrency issues related to mutable data should be addressed).

Читайте также:  Python факториал без цикла

readLabel

Returns the label corresponding to the given bytecode offset. The default implementation of this method creates a label for the given offset if it has not been already created.

getItemCount

getItem

Returns the start offset in this ClassReader of a JVMS ‘cp_info’ structure (i.e. a constant pool entry), plus one. This method is intended for Attribute sub classes, and is normally not needed by class generators or adapters.

getMaxStringLength

Returns a conservative estimate of the maximum length of the strings contained in the class’s constant pool table.

readByte

Reads a byte value in this ClassReader . This method is intended for Attribute sub classes, and is normally not needed by class generators or adapters.

readUnsignedShort

Reads an unsigned short value in this ClassReader . This method is intended for Attribute sub classes, and is normally not needed by class generators or adapters.

readShort

Reads a signed short value in this ClassReader . This method is intended for Attribute sub classes, and is normally not needed by class generators or adapters.

readInt

Reads a signed int value in this ClassReader . This method is intended for Attribute sub classes, and is normally not needed by class generators or adapters.

readLong

Reads a signed long value in this ClassReader . This method is intended for Attribute sub classes, and is normally not needed by class generators or adapters.

readUTF8

Reads a CONSTANT_Utf8 constant pool entry in this ClassReader . This method is intended for Attribute sub classes, and is normally not needed by class generators or adapters.

readClass

Reads a CONSTANT_Class constant pool entry in this ClassReader . This method is intended for Attribute sub classes, and is normally not needed by class generators or adapters.

readModule

Reads a CONSTANT_Module constant pool entry in this ClassReader . This method is intended for Attribute sub classes, and is normally not needed by class generators or adapters.

readPackage

Reads a CONSTANT_Package constant pool entry in this ClassReader . This method is intended for Attribute sub classes, and is normally not needed by class generators or adapters.

readConst

Reads a numeric or string constant pool entry in this ClassReader . This method is intended for Attribute sub classes, and is normally not needed by class generators or adapters.

Читайте также:  Calendar in java swing

Источник

Manipulating Java Class Files with ASM 4 – Part Two: Tree API

What is ASM tree API: ASM Tree API is the part of ASM that lets you create/modify the class in memory. The class is viewed as a tree of information. Like the whole class is an instance of ClassNode, which contain a list of FieldNode objects, a list of MethodNode objects etc. This article assumes that the reader has already read the first part here.

A simple class through tree API: Let’s use tree API to create our first class. Again I am going to jump right into a code example, because there is nothing better than a code example. The generated class has a main method that prints “Hello World!”.

TreeAPIDemo.java

package com.geekyarticles.asm; import java.io.DataOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.LdcInsnNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; public class TreeAPIDemo < public static void main(String [] args) throws Exception< ClassNode classNode=new ClassNode(4);//4 is just the API version number //These properties of the classNode must be set classNode.version=Opcodes.V1_6;//The generated class will only run on JRE 1.6 or above classNode.access=Opcodes.ACC_PUBLIC; classNode.signature="Lcom/geekyarticles/asm/Generated;"; classNode.name="com/geekyarticles/asm/Generated"; classNode.superName="java/lang/Object"; //Create a method MethodNode mainMethod=new MethodNode(4,Opcodes.ACC_PUBLIC|Opcodes.ACC_STATIC,"main", "([Ljava/lang/String;)V",null, null); mainMethod.instructions.add(new FieldInsnNode(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;")); mainMethod.instructions.add(new LdcInsnNode("Hello World!")); mainMethod.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V")); mainMethod.instructions.add(new InsnNode(Opcodes.RETURN)); //Add the method to the classNode classNode.methods.add(mainMethod); //Write the class ClassWriter cw=new ClassWriter(ClassWriter.COMPUTE_MAXS|ClassWriter.COMPUTE_FRAMES); classNode.accept(cw); //Dump the class in a file File outDir=new File("out/com/geekyarticles/asm"); outDir.mkdirs(); DataOutputStream dout=new DataOutputStream(new FileOutputStream(new File(outDir,"Generated.class"))); dout.write(cw.toByteArray()); dout.flush(); dout.close(); >>

As you can see, the code is very simple. A primary advantage over BCEL is that unlike BCEL, ASM does not require you to add every constant explicitly to the constant pool. Instead, ASM takes care of the constant pool itself.

Reading a class file: A ClassNode is a ClassVisitor. So, reading a class for use in tree API is as simple as creating a ClassReader object and using it to read a class file, while passing the ClassNode object in its accept method as a parameter. Once this is done, the ClassNode passed is fully initalized with all the information present in the class. In the following example, we will print all the methods in the class.

TreeAPIClassReaderDemo.java

package com.geekyarticles.asm; import java.io.FileInputStream; import java.io.InputStream; import org.objectweb.asm.ClassReader; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; public class TreeAPIClassReaderDemo < public static void main(String[] args) throws Exception< InputStream in=new FileInputStream("out/com/geekyarticles/asm/Generated.class"); ClassReader cr=new ClassReader(in); ClassNode classNode=new ClassNode(); //ClassNode is a ClassVisitor cr.accept(classNode, 0); //Let's move through all the methods for(MethodNode methodNodeclassNode.methods)< System.out.println(methodNode.name+" "+methodNode.desc); >> >

Modifying a class file: Modifying a class file is a combination of the above two procedures. We first read the class in the usual way, make necessary changes to the data, and then write it back to a file. The following program implements an automatic injection of some logging code. Currently our Logger class only prints to the standard output. Every method annotated with @Loggable will be logged when they begin and when the return. In this we do not log the throw-exception. However that can also be implemented in the same manner by checking opcode ATHROW.

Читайте также:  Entry points setup python

LoggingInsertion.java

package com.geekyarticles.asm; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.util.Iterator; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.LdcInsnNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; public class LoggingInsertion < public static void main(String[] args) throws Exception< InputStream in=LoggingInsertion.class.getResourceAsStream("/com/geekyarticles/asm/LoggingTest.class"); ClassReader cr=new ClassReader(in); ClassNode classNode=new ClassNode(); cr.accept(classNode, 0); //Let's move through all the methods for(MethodNode methodNodeclassNode.methods)< System.out.println(methodNode.name+" "+methodNode.desc); boolean hasAnnotation=false; if(methodNode.visibleAnnotations!=null)< for(AnnotationNode annotationNodemethodNode.visibleAnnotations)< if(annotationNode.desc.equals("Lcom/geekyarticles/asm/Loggable;"))< hasAnnotation=true; break; >> > if(hasAnnotation) < //Lets insert the begin logger InsnList beginList=new InsnList(); beginList.add(new LdcInsnNode(methodNode.name)); beginList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/geekyarticles/asm/Logger", "logMethodStart", "(Ljava/lang/String;)V")); IteratorinsnNodes=methodNode.instructions.iterator(); while(insnNodes.hasNext()) < System.out.println(insnNodes.next().getOpcode()); >methodNode.instructions.insert(beginList); System.out.println(methodNode.instructions); //A method can have multiple places for return //All of them must be handled. insnNodes=methodNode.instructions.iterator(); while(insnNodes.hasNext()) < AbstractInsnNode insn=insnNodes.next(); System.out.println(insn.getOpcode()); if(insn.getOpcode()==Opcodes.IRETURN ||insn.getOpcode()==Opcodes.RETURN ||insn.getOpcode()==Opcodes.ARETURN ||insn.getOpcode()==Opcodes.LRETURN ||insn.getOpcode()==Opcodes.DRETURN)< InsnList endList=new InsnList(); endList.add(new LdcInsnNode(methodNode.name)); endList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/geekyarticles/asm/Logger", "logMethodReturn", "(Ljava/lang/String;)V")); methodNode.instructions.insertBefore(insn, endList); >> > > //We are done now. so dump the class ClassWriter cw=new ClassWriter(ClassWriter.COMPUTE_MAXS|ClassWriter.COMPUTE_FRAMES); classNode.accept(cw); File outDir=new File("out/com/geekyarticles/asm"); outDir.mkdirs(); DataOutputStream dout=new DataOutputStream(new FileOutputStream(new File(outDir,"LoggingTest.class"))); dout.write(cw.toByteArray()); dout.flush(); dout.close(); > >

LoggingTest.java

package com.geekyarticles.asm; public class LoggingTest < public static void run1()< System.out.println("run 1"); >@Loggable public static void run2() < System.out.println("run 2"); >@Loggable public static void main(String [] args) < run1(); run2(); >>
package com.geekyarticles.asm; public class Logger < public static void logMethodStart(String methodName)< System.out.println("Starting method: "+methodName); >public static void logMethodReturn(String methodName) < System.out.println("Ending method: "+methodName); >>

Loggable.java

package com.geekyarticles.asm; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Loggable

If you run this program, the generated file will have a dependency on the class Logger. Manually copy the Logger class to the correct package in the out directory. If you run the generated class (which is a modified version of LoggingTest class), the following would be the output.

bash-4.1$ java com.geekyarticles.asm.LoggingTest Starting method: main run 1 Starting method: run2 run 2 Ending method: run2 Ending method: main

Note that unlike normal Lists, an InsnList object can be modified while we iterate over it. Any changes are immidiately reflected. So, if some instructions are inserted after the current position, that will also be iterated over.

Reference: Manipulating Java Class Files with ASM 4 – Part Two: Tree API from our JCG partner Debasish Ray Chawdhuri at the Geeky Articles blog.

Источник

Оцените статью