Finn Voichick Finn Voichick - 10 months ago 39
Java Question

How does one create an ASM LdcInsnNode that statically adds the current class to the stack?

I'm using the ASM library to modify bytecode created by others. For an arbitrary method in an arbitrary class, I'd like to create an

that adds the current class to the stack.

For example, let's say I'm transforming a class called
. I'd like to create bytecode that is equivalent to

This seems like a relatively simple task. When I use the Eclipse Bytecode Outline plugin, it says that the following bytecode is equivalent:

GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC Lcom/example/ExampleClass;.class
INVOKEVIRTUAL java/lang/Class.getName ()Ljava/lang/String;
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V

I tried the following code:

private InsnList printClass() {
InsnList result = new InsnList();
result.add(new FieldInsnNode(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"));
result.add(new LdcInsnNode("L" + name + ";.class"));
result.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false));
result.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false));
return result;

This is being run in an extension of
, so
refers to the
field. The
returned by this method is being inserted before an existing
InsnList.insertBefore(AbstractInsnNode, printClass())
. When this point is reached in the bytecode, I get an error with the following reason:

Type 'java/lang/String' (current frame, stack[1]) is not assignable to 'java/lang/Class'

This is clearly because the LDC instruction is adding the String
instead of the actual class

Is there any workaround for this? It seems impossible to directly add the
object to an
because the class doesn't exist yet. But is there a way to add an instruction that loads the

In my particular case, calling the
method is not an option because it needs to work from a static context.

Answer Source

You don’t need to refer to a Class object, in fact, it isn’t even supported (directly). If you want to push a Class to the operand stack via ASM, you have to refer to it as Type instance.


new LdcInsnNode(Type.getObjectType(name))

using the Type.getObjectType(…) factory method, which expects name to represent the internal name form, e.g. com/example/ExampleClass without L … ; around it. It’s equivalent to Type.getType('L'+name+';') for non-array types. The choice of the factory method is important as a Type object may represent primitive types or method types as well. Since both, ldc and Type.getObjectType, only support reference types, this is the natural combination.

It seems the LdcInsnNode constructor documentation is a bit outdated, as it only mentions number types and String (Class support with ldc exists since Java 5, but the documentation contains links to 1.4.2). You may refer to MethodVisitor.visitLdcInsn(…) instead, to which the object is eventually passed when producing the byte code.