Reputation: 11
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