Java中的invokedynamic

来源:互联网 发布:大数据的商品化 编辑:程序博客网 时间:2024/05/21 15:01

invokedynamic字节码不能直接通过Java的编译器来直接生成的,它只有在碰到lambda的时候,会生成。

那么我们如果来测试invokedynamic字节码呢。可以通过asm-all-4.0.jar来自己生成。其实这个命名主要是为了动态语言而生的,

一般只有在写动态语言的解释器来需要用到。


可以看看下面一个例子,来了解一下invokedynamic是如何生成的使用的。

首先写一个生成invokedynamic的抽象类:

package invokedynamic;import java.lang.invoke.CallSite;import java.lang.invoke.MethodHandles;import java.lang.invoke.MethodType;import org.objectweb.asm.AnnotationVisitor;import org.objectweb.asm.ClassWriter;import org.objectweb.asm.FieldVisitor;import org.objectweb.asm.Handle;import org.objectweb.asm.MethodVisitor;import org.objectweb.asm.Opcodes;@SuppressWarnings("unused")public abstract class AbstractDynamicInvokerGenerator implements Opcodes {public byte[] dump(String dynamicInvokerClassName, String dynamicLinkageClassName, String bootstrapMethodName, String targetMethodDescriptor)throws Exception {ClassWriter cw = new ClassWriter(0);FieldVisitor fv;MethodVisitor mv;AnnotationVisitor av0;cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, dynamicInvokerClassName, null, "java/lang/Object", null);{mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);mv.visitCode();mv.visitVarInsn(ALOAD, 0);mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");mv.visitInsn(RETURN);mv.visitMaxs(1, 1);mv.visitEnd();}{mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);mv.visitCode();MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,MethodType.class);Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, dynamicLinkageClassName, bootstrapMethodName,mt.toMethodDescriptorString());int maxStackSize = addMethodParameters(mv);mv.visitInvokeDynamicInsn("runCalculation", targetMethodDescriptor, bootstrap); //注意这里mv.visitInsn(RETURN);mv.visitMaxs(maxStackSize, 1);mv.visitEnd();}cw.visitEnd();return cw.toByteArray();}protected abstract int addMethodParameters(MethodVisitor mv);}

asm通过
visitInvokeDynamicInsn
命令来生成invokedynamic指令。


然后再实现一个通过invokedynamic来调用SimpleDynamicLinkageExample类bootstrapDynamic方法;

package invokedynamic.generator;import invokedynamic.AbstractDynamicInvokerGenerator;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import org.objectweb.asm.MethodVisitor;public class SimpleDynamicInvokerGenerator extendsAbstractDynamicInvokerGenerator {@Overrideprotected int addMethodParameters(MethodVisitor mv) {return 0;}public static void main(String[] args) throws IOException, Exception {String dynamicInvokerClassName = "invokedynamic/generator/SimpleDynamicInvoker";FileOutputStream fos = new FileOutputStream(new File("bin/"+ dynamicInvokerClassName + ".class"));fos.write(new SimpleDynamicInvokerGenerator().dump(dynamicInvokerClassName,"invokedynamic/linkageclasses/SimpleDynamicLinkageExample","bootstrapDynamic", "()V"));}}

然后实现SimpleDynamicLinkageExample类:

package invokedynamic.linkageclasses;import java.lang.invoke.CallSite;import java.lang.invoke.ConstantCallSite;import java.lang.invoke.MethodHandle;import java.lang.invoke.MethodHandles;import java.lang.invoke.MethodType;@SuppressWarnings({ "unused", "rawtypes" })public class SimpleDynamicLinkageExample {private static MethodHandle sayHello;private static void sayHello() {System.out.println("There we go!");}public static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {MethodHandles.Lookup lookup = MethodHandles.lookup();Class thisClass = lookup.lookupClass(); // (who am I?)sayHello = lookup.findStatic(thisClass, "sayHello", MethodType.methodType(void.class));return new ConstantCallSite(sayHello.asType(type));}}

这是需要的类就写完了,需要注意的是,你需要先在Eclipse中运行SimpleDynamicInvokerGenerator,让它生成invokedynamic/generator/SimpleDynamicInvoker.class文件;

这个类是我们的入口类,还需要注意的是,我是在Eclipse中编译这些文件的,如果你的bin目录指定到别的地方,你需要修改SimpleDynamicInvokerGenerator类中的路径;

因为生成的SimpleDynamicInvoker类对Eclipse不可见,所以我们需要在命令行中来执行它;

先cd到你的工程目录下,执行

C:\Users\tmp\workspace_java\SimpleDyn\bin>java -classpath "C:\Users\tmp\workspace_java\SimpleDyn\bin;" invokedynamic.generator.SimpleDynamicInvoker
There we go!

那么只是怎么完成的呢:

看看SimpleDynamicInvokerGenerator的内容就知道了:

//  (version 1.7 : 51.0, super bit)public class invokedynamic.generator.SimpleDynamicInvoker {    // Method descriptor #6 ()V  // Stack: 1, Locals: 1  public SimpleDynamicInvoker();    0  aload_0 [this]    1  invokespecial java.lang.Object() [8]    4  return    // Method descriptor #10 ([Ljava/lang/String;)V  // Stack: 0, Locals: 1  public static void main(java.lang.String[] arg0);    0  invokedynamic 0 runCalculation() : void [20]    5  returnBootstrap methods:  0 : # 17 invokestatic invokedynamic/linkageclasses/SimpleDynamicLinkageExample.bootstrapDynamic:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;Method arguments:}


在class中JVM通过invokedynamic来调用runCalculation方法,这个方法是我们通过asm来写入的,可以是任意的名字;

然后我们用通过Bootstrap方法将runCalculation和

SimpleDynamicLinkageExample
bootstrapDynamic
方法关联起来;

SimpleDynamicLinkageExample
中我们有通过MethodHandle找到其中的sayHello方法,然后调用它,这样就完成了invokedynamic命令和实际的函数关联了起来。

http://niklasschlimm.blogspot.com/2012/02/java-7-complete-invokedynamic-example.html

0 0
原创粉丝点击