# This article is translated from my old blog.
Coding with ASM is little difficult for me, because I cannot find tips at the internet especially Japanese one. This article is memo for me. It was written for ASM 3.3, and it may work well for ASM 4.
If you're searching samples for ASM, you can find it in official download page.
Tiny tips
- Method to get internal name of class is here: Type.getInternalName(Class)
- Method to get descriptor of class is here: Type.getDescriptor(Class)
- Method to get descriptor (arguments and returned value) of method is here: Type.getMethodDescriptor(Type, Type[])
How to push literals onto stack
You can use MethodVisitor#visitLdcInsn(Object) to push.
MethodVisitor mv = // ... // System.err.println("TEST ASM"); mv.visitFieldInsn(GETSTATIC, "java/lang/System", "err", "Ljava/io/PrintStream;"); mv.visitLdcInsn("TEST ASM"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
How to create instance
You should call opcodes in order. The order is: NEW (creating reference) → DUP (duplicate reference) → INVOKESPECIAL (executing constructor).
MethodVisitor mv = // ... // new Object(); mv.visitTypeInsn(NEW, Type.getInternalName(Object.class)); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class), "<init>", "()V");
How to push a reference to 'an array of reference' onto stack
You should push array length onto operand stack before calling ANEWARRAY opcode.
MethodVisitor mv = // ... // new Object[0]; mv.visitInsn(ICONST_0); mv.visitTypeInsn(ANEWARRAY, Type.getInternalName(Object.class));
How to inject bytecodes on entering/exiting method
You can use onMethodEnter() and onMethodExit() to inject bytecodes.
How to define new class dynamically
At first, you have to create byte array (= contents in .class file) with ClassWriter.
private byte[] createClassBinary() { ClassWriter cw = new ClassWriter(0); cw.visit(V1_5, ACC_PUBLIC, "pkg/OwnClass", null, "java/lang/Object", null); { // constructor MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); mv.visitMaxs(2, 1); mv.visitVarInsn(ALOAD, 0); // push `this` to the operand stack mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class), "<init>", "()V"); // call the constructor of super class mv.visitInsn(RETURN); mv.visitEnd(); } { // public Object get() { return new Object(); } MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "get", "()Ljava/lang/Object;", null, null); mv.visitMaxs(2, 1); mv.visitTypeInsn(NEW, Type.getInternalName(Object.class)); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class), "<init>", "()V"); mv.visitInsn(ARETURN); mv.visitEnd(); } cw.visitEnd(); return cw.toByteArray(); }
Then, you can create Class instance from this byte array. You have to create original Class loader and call its #defineClass method.
private static class OwnClassLoader extends ClassLoader { public Class<?> defineClass(String name, byte[] b) { return defineClass(name, b, 0, b.length); } } private Class<?> createClass(byte[] b) { return new OwnClassLoader().defineClass("pkg.OwnClass", b); }
BTW, how to create interface dynamically is documented at official document. Creating interface is easier than class.