本文译自:http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html
invokedynamic
操作
调用动态方法
格式
invokedynamic
indexbyte1
indexbyte2
0
0
编码
invokedynamic= 186 (0xba)
操作数栈
..., [arg1, [arg2 ...]] →
描述
每个一个具体的invokedynamic单词的出现称为一个动态调用点。
首先,无符号字节index1和index2用来构造一个指向运行时常量池的索引。索引值为(index<<8)|index2,运行时常量池在该索引的位置为一个调用点说明符的符号引用。第三个和第四个运算元必须为0.
通过对该调用点说明符的解析,可以获取到一个java.lang.invoke.MethodHandle的实例引用,一个java.lang.invoke.MethodType的实例引用,以及一些静态参数的引用。
接下来,继续解析调用点说明符,一个引导方法被调用,就像执行一个invokevirtual指令一样,该invokevirtual指令包含一个指向具有如下属性的方法的符号引用:
- 方法的名字是invoke。
- 方法的描述符具有一个java.lang.invoke.CallSite类型的返回类型。
- 方法的描述符具有如下参数类型,这些参数类型可以从压入操作数栈的条目得出:
前四个参数类型为:java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandles.Lookup,String, 和
java.lang.invoke.MethodType。
如果调用点描述符具有静态参数,则,这些静态参数按照原有参数的顺序压入操作数栈,这些参数可能是:Class,
java.lang.invoke.MethodHandle,java.lang.invoke.MethodType, String, int, long, float, 或 double.
- 方法的所属类的符号引用指向:java.lang.invoke.MethodHandle
。
上述过程就像向操作数栈中按顺序压入了如下条目一样:
针对引导方法的指向java.lang.invoke.MethodHandle的引用。
- 一个指向java.lang.invoke.MethodHandles.Lookup的引用(动态调用点发生的类)。
- 一个指向String的引用(调用点描述中的方法名)。
- 一个指向java.lang.invoke.MethodType对象的引用(针对调用点描述符中的方法描述符)。
- 一些指向调用点说明符中的类,方法类型,方法句柄,以及作为静态参数的字符串以及一些作为参数的数字值,它们按照在调用点说明符中的顺序出现。(也就是说,对于原始类型没有装箱操作)。
引导方法的描述符可以是任意的,只要能被invoke方法正确调用就行。例如,第一个参数类型可以是Object而不是java.lang.invoke.MethodHandles.Lookup
,返回类型可以是
object
而不是
java.lang.invoke.CallSite
.
如果引导方法是变参的,那么,上面说明的操作数栈中的部分或者全部参数可能被收集到一个参数数组中。引导方法的调用发生在试图解析当前动态调用点的调用点描述符的符号引用的线程中。
如果存在多个这样的线程,则引导方法可能会并发执行,因此,针对全局引用数据访问的引导方法需要慎重考虑这种竞争情形。
引导方法的返回值必须是一个java.lang.invoke.CallSite
或者
java.lang.invoke.CallSite
子类对象的引用。这个对象就是所谓的
call site object
。该应用就像调用
Invokevirtual
指令一样从操作数栈中弹出使用。
如果存在多个线程同时对执行一个动态调用点的引导方法,则JVM必须选择一个callsite object作为返回值,并使之对其它线程可见。任何其它线程对该引导方法的执行可以继续直到完成,但返回值会被忽略,被选为callsite object的线程基于选中的callsite object继续执行。
Call site object有一个类型描述符(一个java.lang.invoke.MethodType的实例),该描述符必须在语义上和为调用点描述符中的方法描述符获取到MethodType实例相等。
调用点描述符的成功解析结果是一个call site对象,该对象永久绑定到了当前动态调用点。
被绑定到call site 对象的target的方法句柄被调用。就像执行一个invokevirtual指令一样,该指令包含的运行时常量池索引指向一个具有如下属性方法的符号引用:
- 方法的名称是invokeExact。
- 方法的描述符是调用点说明符中的方法描述符,并且,
- 方法所属类为java.lang.invoke.MethodHandle。
操作数栈将包含当前callsite对象的target引用,后面跟着一组参数,个数,类型和顺序都必须和调用点说明符中的方法描述符中一致。
链接异常
如果解析调用点说明符时抛出了异常E,则invokedynamic指令抛出一个封装了E的错误BootstrapMethodError
否则,调用点说明符解析期间,如果引导方法的调用由于抛出了异常E非正常完成,则Invokedynamic指令抛出对E封装后的错误BootstrapMethodError
。(如果引导方法的参数数量,类型或者返回值出错导致
java.lang.invoke.MethodHandle
.invoke
抛出了
java.lang.invoke.WrongMethodTypeException
异常就会出现这种情形)。
否则,
调用点说明符继续解析期间,如果引导方法的执行结果不是java.lang.invoke.CallSite
类型的实例,则
invokedynamic
指令会抛出
BootstrapMethodError
错误。
否则,
调用点说明符继续解析期间,如果callsiteobject的target的类型描述符语义上和调用点说明符中的方法描述符不相等。则
invokedynamic
指令会抛出
BootstrapMethodError
错误。
运行时异常
如果一个具体的动态调用点已经完成了它的调用点说明符的解析,意味着一个指向java.lang.invoke.CallSite的对象的非null引用已经绑定到了当前动态调用点。因此,存放callsite的target的引用的操作数栈条目不会为null。同样,也意味着调用点说明符中的方法描述符语义上等于将要调用的方法句柄的类型描述符,就像调用Invokevirutal指令一样。
这些不变性表明,一个绑定到callsite对象的invokedynamic指令从来不会抛出NullPointerException
异常或java.lang.invoke.WrongMethodTypeException
异常。