I am new to ASM framework. I have been working around this ASM framework for a week. I saw tutorials in net regarding parsing a class and Generating a .class file from scratch. But am unable to follow how to modify a existing class in ASM.
I am unable to follow the flow of execution between the ClassVisitor
, ClassWriter
and ClassReader
.
Kindly solve my issue by giving me a ASM example for the following code.
public class ClassName {
public void showOne() {
System.out.println("Show One Method");
}
public static void main(String[] args) {
ClassName c = new ClassName();
c.showOne();
}
}
The above class should be modified as:
public class ClassName {
public void showOne() {
System.out.println("Show One Method");
}
public void showTwo() { // <- Newly added method
System.out.println("Show Two Method");
}
public static void main(String[] args) {
ClassName c = new ClassName();
c.showOne();
c.showTwo(); // <- Newly inserted method call
}
}
What should be the ASM code to modify it?
I used the ASMifier tool to generate the code. But I don't know where to apply it.
It can be used to modify existing classes or to dynamically generate classes, directly in binary form. ASM provides some common bytecode transformations and analysis algorithms from which custom complex transformations and code analysis tools can be built.
ASM simply stands for ASMifier tool.
Bytecode manipulation consists in modifying the classes - represented by bytecode - compiled by the Java compiler, at runtime. It is used extensively for instance by frameworks such as Spring (IoC) and Hibernate (ORM) to inject dynamic behaviour to Java objects at runtime.
When you write a Java program and compile it you get a class file. That class file is Java byte code. It is a binary data file that contains instructions for the Java Virtual Machine to execute your program.
Your requirements are a bit underspecified. Below is an example program which uses ASM’s visitor API for transforming a class assumed to have the structure of your question to the resulting class. I added a convenience method taking a byte array and returning a byte array. Such a method can be used in both cases, a static transformation applied to class files on disk as well as in an Instrumentation agent.
When combining a ClassWriter
with a ClassVisitor
passed to a ClassReader
as below, it will automatically replicate every feature of the source class so you have to override only these methods where you want to apply changes.
Here, visitMethod
is overridden to intercept when encountering the main
method to modify it and visitEnd
is overridden to append the entirely new showTwo
method. The MainTransformer
will intercept RETURN
instructions (there should be only one in your example) to insert the call to showTwo
before it.
import org.objectweb.asm.*;
import org.objectweb.asm.commons.GeneratorAdapter;
public class MyTransformer extends ClassVisitor {
public static byte[] transform(byte[] b) {
final ClassReader classReader = new ClassReader(b);
final ClassWriter cw = new ClassWriter(classReader,
ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS);
classReader.accept(new MyTransformer(cw), ClassReader.EXPAND_FRAMES);
return cw.toByteArray();
}
public MyTransformer(ClassVisitor cv) {
super(Opcodes.ASM5, cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
MethodVisitor v=super.visitMethod(access, name, desc, signature, exceptions);
if(name.equals("main") && desc.equals("([Ljava/lang/String;)V"))
v=new MainTransformer(v, access, name, desc, signature, exceptions);
return v;
}
@Override
public void visitEnd() {
appendShowTwo();
super.visitEnd();
}
private void appendShowTwo() {
final MethodVisitor defVisitor = super.visitMethod(
Opcodes.ACC_PUBLIC, "showTwo", "()V", null, null);
defVisitor.visitCode();
defVisitor.visitFieldInsn(Opcodes.GETSTATIC,
"java/lang/System", "out", "Ljava/io/PrintStream;");
defVisitor.visitLdcInsn("Show Two Method");
defVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
defVisitor.visitInsn(Opcodes.RETURN);
defVisitor.visitMaxs(0, 0);
defVisitor.visitEnd();
}
class MainTransformer extends GeneratorAdapter
{
MainTransformer(MethodVisitor delegate, int access, String name, String desc,
String signature, String[] exceptions) {
super(Opcodes.ASM5, delegate, access, name, desc);
}
@Override
public void visitInsn(int opcode) {
if(opcode==Opcodes.RETURN) {
// before return insert c.showTwo();
super.visitVarInsn(Opcodes.ALOAD, 1); // variable c
super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"ClassName", "showTwo", "()V", false);
}
super.visitInsn(opcode);
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With