MethodHandle类的描述中显示的示例在使用以下描述WrongMethodTypeException的语句调用中抛出a mh.invokeExact("daddy",'d','n'): (CC)Ljava/lang/String; cannot be called with a different arity as ([Ljava/lang/Object;)Ljava/lang/Object;.
该MethodHandle对象mh具有对应于的符号类型描述符:(CC)Ljava/lang/String.但是当我们调用时mh.invokeExact("daddy",'d','n'),参数:d并n作为Object数组传递,然后它们与该类型的参数不匹配char.
我知道我可以使用invokeWithArguments而不是invokeExcat或者解决上述问题invoke,但是这个例子应该像MethodHandleJava 7 API 的描述中所说的那样工作.除此之外,它invokeWithArguments具有与invoke/ 相关的性能开销invokeExact.
正如我在阅读这篇关于JDK 7中新的invokedynamic字节码指令的帖子所理解的那样,它可以调用对象类中没有静态定义的对象上的方法,并将这些方法调用解析为某些具体的静态方法.其他类通过拦截方法调用目标分辨率(帖子给出一个例子).
这是否意味着Java 7类可以使用Scala之类的隐式方法?如果不是,Scala中的隐式方法解析与invokedynamic方法解析有何不同?
我正在尝试做一些相对简单的事情,我想。以 doSomething(int) 方法的以下 Java 字节码为例:
public java.lang.String doSomething(int i);
0 iload_1 [i]
1 invokestatic MyHelper.doSomething(int) : Java.lang.String
4 areturn
Run Code Online (Sandbox Code Playgroud)
这个字节码几乎只将调用转发给静态助手。
我现在想要做的是使用 Javassist 将 invokestatic 替换为 invokedynamic。我知道如何使用 ASM 做到这一点,因此只是出于纯粹的好奇心假设我想知道它是如何工作的。以下是我的一些问题:
1) 以下是否正确:我不能使用 javassist CtMethod.instrument() 或 CtMethod.insertAt() 方法,因为这些方法需要包含有效 Java 表达式的字符串,而我不能用 Java 语法编写调用动态?
2) invokestatic 的参数就像 invokevirtual 或 invokestatic 的参数一样处理,对吗?我的意思是,您在调用动态之前将参数放入堆栈,就像对 invokevirtual 所做的一样?
3)是否有使用Javassist创建invokedynamic字节码的示例代码(或者你能想出一些)吗?
这就是我目前所知道的:您可以创建一个具有 addInvokedynamic() 方法的 Bytecode 对象。但这需要 BootstrapMethodsAttribute 中 BootstrapMethod 的索引。BootstrapMethod 反过来需要常量池中方法句柄信息的索引,该索引需要方法引用等。所以基本上你必须自己管理整个常量池条目。没关系,但我担心我没有做对,以后会引入奇怪的问题。有没有更简单的方法来做到这一点(辅助方法左右)?我的代码大致看起来像这样(我并没有真正“重写”上面的 invokestatic 但是:
void transform(CtMethod ctmethod, String originalCallDescriptor) {
MethodInfo mInfo = ctmethod.getMethodInfo();
ConstPool pool = ctmethod.getDeclaringClass().getClassFile().getConstPool();
/* add info about the …Run Code Online (Sandbox Code Playgroud) 我试图检查是否可以使用MethodHandle :: invoke或MethodHandle :: invokeExact作为接受MethodHandle并返回通用输出的功能接口的方法引用.
(我知道invoke和invokeExact是签名多态,因此在InvokeExact中调用metafactory.但是,我想知道编译器是否能够忽略我必须做的事情来导出一个合适版本的invoke/invokeExact.)
invoke.InvokeExact0
package invoke;
import java.lang.invoke.MethodHandle;
import static java.lang.System.out;
import static java.lang.invoke.LambdaMetafactory.metafactory;
import static java.lang.invoke.MethodHandles.lookup;
import static java.lang.invoke.MethodType.methodType;
@FunctionalInterface
public interface InvokeExact0<OUTPUT> {
public OUTPUT invokeExact(MethodHandle methodHandle) throws Throwable;
public static <OUTPUT> InvokeExact0<OUTPUT> new_(InvokeExact0<OUTPUT> invokeExact) {
return invokeExact;
}
public static void main(String... arguments) throws Throwable {
out.println(
(InvokeExact0<String>) metafactory(
lookup(),
"invokeExact",
methodType(InvokeExact0.class),
methodType(
Object.class,
MethodHandle.class
),
lookup().findVirtual(
MethodHandle.class,
"invokeExact",
methodType(String.class)
),
methodType(
String.class,
MethodHandle.class
)
)
.getTarget()
.invokeExact()
);
out.println(InvokeExact0.new_(MethodHandle::invokeExact));
}
}
Run Code Online (Sandbox Code Playgroud)
结果
invoke.InvokeExact0$$Lambda$1/1878246837@5ca881b5 …Run Code Online (Sandbox Code Playgroud) 我试图了解什么是 JVM 中的调用站点。引用自https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.6
\n\n\n\n\n调用站点说明符解析的结果是一个元组,其中包含:
\n\n\xe2\x80\xa2 对 的实例的引用
\n\njava.lang.invoke.MethodHandle,\xe2\x80\xa2 对 的实例的引用
\n\njava.lang.invoke.MethodType,\n
Class\xe2\x80\xa2 对、java.lang.invoke.MethodHandle、\njava.lang.invoke.MethodType和实例的引用String。
我们还有所谓的调用站点对象https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokedynamic:
\n\n\n\n\n\n
java.lang.invoke.CallSitebootstrap 方法返回的结果必须是对其类为\n或其子类的\n 对象 的引用java.lang.invoke.CallSite。该对象称为调用站点\n对象
调用站点对象的概念很清晰。这只是一个例子CallSite。但是调用站点说明符又如何呢?那是一个Java对象吗?这是String字面意思吗?
我有两个类A和B,都定义了foo()具有共同签名的方法(不接受任何内容,返回无效)。它们没有声明此方法的公共基类(或接口)。我想调用这个方法,不管是 As 还是 B,只要他们能响应这个调用。这种方法称为Duck Typing。
我知道有一个指令叫做invokedynamic:
调用动态指令的每个实例称为动态调用点。动态调用站点最初处于未链接状态,这意味着没有为调用站点指定要调用的方法。如前所述,动态调用站点通过引导方法链接到方法。动态调用站点的引导方法是编译器为动态类型语言指定的方法,JVM 调用一次以链接站点。从引导方法返回的对象永久确定调用站点的行为。
所以我尝试使用MethodHandles来实现这一点。这是示例:
public static class A {
public void foo() {
}
}
public static class B {
public void foo() {
}
}
public static void main(String[] args) throws Throwable {
final MethodHandle foo = MethodHandles.lookup()
.findVirtual(A.class, "foo", MethodType.methodType(void.class));
foo.invoke(new B());
}
Run Code Online (Sandbox Code Playgroud)
当然,我有:
Exception in thread "main" java.lang.ClassCastException: Cannot cast Main$B to Main$A
at sun.invoke.util.ValueConversions.newClassCastException(ValueConversions.java:461)
at sun.invoke.util.ValueConversions.castReference(ValueConversions.java:456)
at …Run Code Online (Sandbox Code Playgroud) 我有这个课,我编译它.
package org.test;
import java.util.function.Supplier;
public class Test {
static String get() { return "!!"; }
public static void main(String[] args) {
Supplier<String> sup = Test::get;
System.out.println(sup.get());
}
}
Run Code Online (Sandbox Code Playgroud)
然后,尝试查看它的字节码,我得到以下public static void main函数的开头:
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: invokedynamic #3, 0 // InvokeDynamic #0:get:()Ljava/util/function/Supplier;
5: astore_1
6: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
Run Code Online (Sandbox Code Playgroud)
在这里我们可以看到invokedynamic调用,如果我理解正确的话,它会创建一个Supplier接口的匿名实例.传递给invokedynamic有两个参数,一个是#3.第二个参数是0.所以,我的第一个问题是:0代表什么?
恒定池#3代表#3 = InvokeDynamic #0:#27 // #0:get:()Ljava/util/function/Supplier;.在常量池中引用#27,但没有引用#0.我的第二个问题是:#0代表什么?
Java 8引入了对第一类函数的支持,它允许将函数赋值给变量.在这种情况下,变量必须是函数类型,它由函数接口(只有一个抽象方法的接口)定义.
因此,考虑一个接口I和A具有以下定义的类的示例:
interface I{ int foo(); }
class A implements I{
public int foo(){return 7;}
public static int bar(){return 11;}
}
Run Code Online (Sandbox Code Playgroud)
我们可以分配给类型的变量I的实例A或方法参考该方法bar的A.两者都可以存储在类型的变量中I,例如:
I i1 = new A();
I i2 = A::bar;
Run Code Online (Sandbox Code Playgroud)
如果我们分析前面代码编译产生的字节码,我们将得到:
0: new #2 // class A
3: dup
4: invokespecial #3 // Method A."<init>":()V
7: astore_1
8: invokedynamic #4, 0 // InvokeDynamic #0:foo:()LI;
13: astore_2
Run Code Online (Sandbox Code Playgroud)
因为i1 …
我目前正在用 C# 编写 JVM,纯粹出于学术目的(也许将来会构建混合的 .NET 和 Java/Scala 应用程序)。
我写了一个简单的JAVA类:
public class test
{
public static String hello_world(int i)
{
return "Hello " + i + " World!";
}
}
Run Code Online (Sandbox Code Playgroud)
并将其编译成test.class. 当我使用反编译器(我将其编写为 JVM 的一部分)对其进行反编译时,我看到此方法的以下说明:
iload_0
invokedynamic 2
areturn
Run Code Online (Sandbox Code Playgroud)
当在常量池中查找索引处的常量时2,我看到一个包含以下数据的 InvokeDynamic-Constant 条目:
makeConcatWithConstants : (I)Ljava/lang/String;
Run Code Online (Sandbox Code Playgroud)
我想这是有道理的(我更像是 .NET 用户而不是 JAVA 用户)。
hello_world当使用参数执行我的方法时1,在执行之前我有以下堆栈invokedynamic 2:
----TOP---
0x00000001
--BOTTOM--
Run Code Online (Sandbox Code Playgroud)
我的问题是:我该如何使用invokedynamic?
我无法解析该方法makeConcatWithConstants,因为 InvokeDynamic-Constant 没有给我任何makeConcatWithConstants可能位于何处的提示(请参阅文档)。 …
我相信自从Java 9字符串连接是使用StringConcatFactory.
由于这是在 Java 中作为 API 提供的,如何通过直接调用 API 来使用这些方法和方法makeConcat\xe2\x80\x8b?到目前为止,我找不到任何使用它的不同方法的示例。另外,参数、in和参数, in 的含义以及应该传递给它们的内容对于我来说在Java 文档中并不是不言自明的。makeConcatWithConstantsStringConcatFactoryString nameMethodType concatTypemakeConcat\xe2\x80\x8bmakeConcatWithConstantsString recipeObject... constantsmakeConcatWithConstants
invokedynamic ×10
java ×9
jvm ×4
java-8 ×3
methodhandle ×3
java-7 ×2
annotations ×1
bytecode ×1
duck-typing ×1
dynamic ×1
javassist ×1
jvm-bytecode ×1
lambda ×1
methods ×1
scala ×1
string ×1