Skip to main content

ASM now supports invokedynamic

Posted by forax on June 11, 2009 at 10:42 PM PDT

Great news, ASM 3.2 is released (Extended changelog).
This new version includes the support of the new bytecode instruction : invokedynamic, introduced by JSR292.

 

ASM supports of invokedynamic

From ASM point of view, invokedynamic is a method call instruction emittable/trappable by calling/overriding visitInsnMethod of a MethodVisitor like any other invoke* bytecode instructions.
But because invokedynamic is a function call i.e a call without any receiver, a special fake owner name Opcodes.INVOKE_DYNAMIC_OWNER was introduced to stand as owner of the invokedynamic call instruction.
This design allows ASM to handle invokedynamic without breaking all already existing codes written using ASM 3.x.

Example

This simple code transforms all invokevirtual to invokedynamic, registers a boostrap method and configure invokedynamic call site to perform an invokevirtual.

public class Invokedynamiker extends ClassAdapter {
  boolean isStaticInitPatched;
 
  public Invokedynamiker(ClassVisitor cv) {
    super(cv);
  }
 
  @Override
  public MethodVisitor visitMethod(int access, final String name, final String desc, String signature, String[] exceptions) {
    MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
    return new MethodAdapter(mv) {
      @Override
      public void visitCode() {
        super.visitCode();
        if ("".equals(name)) {
          generateClinitProlog(mv);
          isStaticInitPatched = true;
        }
      }
     
      @Override
      public void visitMethodInsn(int opcode, String owner, String name, String desc) {
        if (opcode == Opcodes.INVOKEVIRTUAL) {
          opcode = Opcodes.INVOKEDYNAMIC;
          desc = "(L"+owner+';'+desc.substring(1);
          owner = Opcodes.INVOKEDYNAMIC_OWNER;
        }
        super.visitMethodInsn(opcode, owner, name, desc);
      }
    };
  }
 
  @Override
  public void visitEnd() {
    if (!isStaticInitPatched) {
      MethodVisitor mv = super.visitMethod(Opcodes.ACC_STATIC, "", "()V", null, null);
      mv.visitCode();
      generateClinitProlog(mv);
      mv.visitMaxs(0, 0);
      mv.visitEnd();
    }
    super.visitEnd();
  }
 
  void generateClinitProlog(MethodVisitor mv) {
    mv.visitLdcInsn(Type.getObjectType("asm/indy/sample/Bootstrap"));
    mv.visitLdcInsn("bootstrapMethod");
    mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/dyn/Linkage",
        "registerBootstrapMethod", "(Ljava/lang/Class;Ljava/lang/String;)V");
    mv.visitInsn(Opcodes.RETURN);
  }
}

The boostrap method creates a call site and links the call site target to a method handles performing an invokevirtual.

public static CallSite bootstrapMethod(Class<?> callerClass, String methodName, MethodType methodType) {
    MethodType argumentType = methodType.dropParameterType(0);
    MethodHandle mh = MethodHandles.lookup().findVirtual(methodType.parameterType(0), methodName, argumentType);
    CallSite site = new CallSite(callerClass, methodName, methodType);
    site.setTarget(mh);
    return site;
  }

The code of the bootstrap method can be changed to, by example, log the arguments before calling the destination method using method handle adapters : convertArguments, spreadArguments, collectArguments and a java.dyn.JavaMethodHandle.
This exercise is left to the reader :)

The whole code is available as a zip : indy-asm-sample.zip.
To run it, download the latest binary of jdk7 (at least b59) and run the ant script (ant run).

cheers,
Rémi (ASM team mate)

Related Topics >>

Comments

What is currently happening in the dynamic world with Java is really great !! And what John Rose, Charles Nutter and yourself are building (among others) is really amazing !!! Thanks !!!

That's a very good example. With a similar transformation of invokespecial (except ) and invokestatic, it could provide a strong stress test for the JSR 292 core.