Java中的invokedynamic

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 extends
		AbstractDynamicInvokerGenerator {

	@Override
	protected 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  return

Bootstrap 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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值