Subscribed unsubscribe Subscribe Subscribe

Kengo's blog

Technical articles about original projects, JVM, Static Analysis and JavaScript.

ObjectWeb ASM 3.3 Tips

ObjectWeb ASM java

# 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

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.