Vikas Yadav
Vikas Yadav

Reputation: 11

Is there a way to generate invokedynamic bytecode using BCEL?

I want to generate invokedynamic bytecode using BCEL library.

Using BCEL I want to generate a class file which calls a bootstrap method using invokedynamic opcode. I can see BCEL has INVOKEDYNAMIC instruction, but I am not sure how to use that with methodhandle.

Bootstrap Class looks like this:

import java.lang.invoke.*;

public class MyBootstrap {
    public static CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
        // Find the target method dynamically
        MethodHandle target = caller.findVirtual(MyDynamicClass.class, name, type);

        // Return a CallSite with the MethodHandle
        return new ConstantCallSite(target);
    }
}

MyDynamicClass :

public class MyDynamicClass {
    public void sayHello(String name) {
        System.out.println("Hello, " + name + "!");
    }
}
public static void main(String[] args) throws Exception {
        // Create a ClassGen to generate the class
        ClassGen classGen = new ClassGen("GeneratedInvoker", "java.lang.Object", "GeneratedInvoker.java", Const.ACC_PUBLIC | Const.ACC_SUPER, null);

        // Create the constructor method
        InstructionList il = new InstructionList();
        MethodGen constructor = new MethodGen(Const.ACC_PUBLIC, Type.VOID, new Type[]{}, new String[]{}, "<init>", "GeneratedInvoker", il, classGen.getConstantPool());

        // Call super() in constructor
        il.append(InstructionFactory.createLoad(Type.OBJECT, 0));
        il.append(new INVOKESPECIAL(classGen.getConstantPool().addMethodref("java.lang.Object", "<init>", "()V")));
        il.append(InstructionFactory.createReturn(Type.VOID));

        // Add the constructor method to the class
        constructor.setMaxStack(1);
        classGen.addMethod(constructor.getMethod());
        il.dispose();

        // Create the main method: public static void main(String[] args)
        il = new InstructionList();
        MethodGen mainMethod = new MethodGen(Const.ACC_PUBLIC | Const.ACC_STATIC, Type.VOID, new Type[]{new ArrayType(Type.STRING, 1)}, new String[]{"args"}, "main", "GeneratedInvoker", il, classGen.getConstantPool());

        // Create an instance of MyDynamicClass
        il.append(new NEW(classGen.getConstantPool().addClass("MyDynamicClass")));
        il.append(InstructionFactory.createDup(1));
        il.append(new INVOKESPECIAL(classGen.getConstantPool().addMethodref("MyDynamicClass", "<init>", "()V")));

        // Push the argument for the sayHello method
        il.append(new PUSH(classGen.getConstantPool(), "world"));
        
// Add the ConstantMethodRef object to the constant pool
        int bootstrapMethodRefIndex = classGen.getConstantPool().addMethodref("MyBootstrap","bootstrap","(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;");
        
        // Add invokedynamic instruction
        ConstantMethodHandle methodHandle = new ConstantMethodHandle(
                Constants.INVOKESTATIC,
                bootstrapMethodRefIndex
        );
        // dont know how to use this methodHandle
        INVOKEDYNAMIC invokedynamic = new INVOKEDYNAMIC(bootstrapMethodRefIndex);

        il.append(invokedynamic);

        // Return from the main method
        il.append(InstructionFactory.createReturn(Type.VOID));

        // Add the main method to the class
        mainMethod.setMaxStack(3);
        classGen.addMethod(mainMethod.getMethod());
        il.dispose();

        // Write the generated class to a file
        try (FileOutputStream fos = new FileOutputStream("GeneratedInvoker.class")) {
            classGen.getJavaClass().dump(fos);
        }

        System.out.println("GeneratedInvoker.class has been created!");
    }

but this didn't work and when I try to run i got following Error:

Unable to initialize main class GeneratedInvoker Caused by: java.lang.ClassFormatError: Illegal local variable table length 13 in method GeneratedInvoker.main(\[Ljava/lang/String;)V

Following is the bytecode generated:

public class GeneratedInvoker
  minor version: 0
  major version: 52
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #4                          // GeneratedInvoker
  super_class: #6                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
   #1 = Utf8               SourceFile
   #2 = Utf8               GeneratedInvoker.java
   #3 = Utf8               GeneratedInvoker
   #4 = Class              #3             // GeneratedInvoker
   #5 = Utf8               java/lang/Object
   #6 = Class              #5             // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = NameAndType        #7:#8          // "<init>":()V
  #10 = Methodref          #6.#9          // java/lang/Object."<init>":()V
  #11 = Utf8               this
  #12 = Utf8               LGeneratedInvoker;
  #13 = Utf8               LocalVariableTable
  #14 = Utf8               Code
  #15 = Utf8               MyDynamicClass
  #16 = Class              #15            // MyDynamicClass
  #17 = Methodref          #16.#9         // MyDynamicClass."<init>":()V
  #18 = Utf8               world
  #19 = String             #18            // world
  #20 = Utf8               bootstrap
  #21 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #22 = NameAndType        #20:#21        // bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #23 = Utf8               MyBootstrap
  #24 = Class              #23            // MyBootstrap
  #25 = Methodref          #24.#22        // MyBootstrap.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #26 = Utf8               main
  #27 = Utf8               ([Ljava/lang/String;)V
  #28 = Utf8               args
  #29 = Utf8               [Ljava/lang/String;
{
  public GeneratedInvoker();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #10                 // Method java/lang/Object."<init>":()V
         4: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LGeneratedInvoker;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=1, args_size=1
         0: new           #16                 // class MyDynamicClass
         3: dup
         4: invokespecial #17                 // Method MyDynamicClass."<init>":()V
         7: ldc           #19                 // String world
         9: invokedynamic #25,  0             // Method MyBootstrap.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
        14: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      13     0  args   [Ljava/lang/String;
}
SourceFile: "GeneratedInvoker.java"

Upvotes: 1

Views: 29

Answers (0)

Related Questions