- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我想看看如何使用与 invokevirtual
相同的调度逻辑进行 invokedynamic
调用。
我问这个问题是因为目前在线的使用 ASM 生成动态方法调用的示例太琐碎而无法概括,我认为这种情况对于任何想要实现自己的调度逻辑的人来说都是一个很好的起点。
显然,我知道仅将 invokevirtual
调用替换为 invokedynamic
调用在实践中是毫无意义的。
明确地说,我想替换它:
methodVisitor.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
myClassName,
methodName,
descriptor,
false);
用这个:
MethodType methodType =
MethodType.methodType(
CallSite.class,
MethodHandles.Lookup.class,
String.class,
MethodType.class);
Handle handle =
new Handle(
Opcodes.H_INVOKESTATIC,
"bytecode/generating/Class",
"bootstrap",
methodType.toMethodDescriptorString(),
false);
methodVisitor.visitInvokeDynamicInsn(
methodName,
descriptor,
handle);
//引导方法
public static CallSite bootstrap(
MethodHandles.Lookup caller,
String name,
MethodType type)
{
// Dispatch logic here.
}
最佳答案
在这种情况下没有什么可做的。您唯一需要关心的是 invokevirtual
有一个隐含的第一个参数,接收器,您必须将其作为显式插入到 invokedynamic
指令的描述符中第一个参数:
public class ConvertToInvokeDynamic extends MethodVisitor {
public static byte[] convertInvokeVirtual(
InputStream in, String linkerClass, String linkerMethod) throws IOException {
ClassReader cr = new ClassReader(in);
ClassWriter cw = new ClassWriter(cr, 0);
cr.accept(new ClassVisitor(Opcodes.ASM5, cw) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
return new ConvertToInvokeDynamic(
super.visitMethod(access, name, desc, signature, exceptions),
linkerClass, linkerMethod);
}
}, 0);
return cw.toByteArray();
}
private final Handle bsm;
public ConvertToInvokeDynamic(
MethodVisitor target, String linkerClass, String linkerMethod) {
super(Opcodes.ASM5, target);
bsm = new Handle(Opcodes.H_INVOKESTATIC, linkerClass, linkerMethod,
"(Ljava/lang/invoke/MethodHandles$Lookup;"
+ "Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;");
}
@Override
public void visitMethodInsn(
int opcode, String owner, String name, String desc, boolean itf) {
if(opcode == Opcodes.INVOKEVIRTUAL) {
desc = '('+(owner.charAt(0)!='['? 'L'+owner+';': owner)+desc.substring(1);
super.visitInvokeDynamicInsn(name, desc, bsm);
}
else super.visitMethodInsn(opcode, owner, name, desc, itf);
}
}
只要这是唯一的变化,堆栈状态将与原始代码保持一致,因此,我们不需要重新计算堆栈帧,也不需要重新计算最大变量/操作数堆栈大小。
代码假定原始类的版本足够高以支持invokedynamic
指令。否则,转换将变得非常重要,因为我们不仅可能需要计算堆栈映射,我们还可能在旧类文件中遇到现在禁止的 jsr
和 ret
指令。
提供一个重新建立原始 invokevirtual
行为的 Bootstrap 方法,也是直截了当的。现在,最大的(不是很大)障碍是我们现在必须提取第一个显式参数类型并将其转换回接收者类型:
public class LinkLikeInvokeVirtual {
public static CallSite bootstrap(MethodHandles.Lookup l, String name, MethodType type){
Class<?> receiver = type.parameterType(0);
type = type.dropParameterTypes(0, 1);
System.out.println("linking to "+name+type+" in "+receiver);
MethodHandle target;
try {
target = l.findVirtual(receiver, name, type);
} catch(NoSuchMethodException|IllegalAccessException ex) {
throw new BootstrapMethodError(ex);
}
return new ConstantCallSite(target);
}
}
现在,我们可以在一个简单的测试用例中组合这两个类:
public class Test {
public static void main(String[] args) throws IOException,ReflectiveOperationException{
byte[] code;
try(InputStream is = Test.class.getResourceAsStream("Test.class")) {
code = ConvertToInvokeDynamic.convertInvokeVirtual(is,
LinkLikeInvokeVirtual.class.getName(), "bootstrap");
}
Class<?> transformed = new ClassLoader() {
Class<?> get() {return defineClass("Test", code, 0, code.length); }
}.get();
transformed.getMethod("example").invoke(null);
}
public static void example() {
System.out.println(Runtime.getRuntime().freeMemory()+" bytes free");
}
}
转换后的 example()
产生
linking to freeMemory()long in class java.lang.Runtime
linking to append(long)StringBuilder in class java.lang.StringBuilder
linking to append(String)StringBuilder in class java.lang.StringBuilder
linking to toString()String in class java.lang.StringBuilder
linking to println(String)void in class java.io.PrintStream
131449472 bytes free
在第一次执行时(因为链接的调用站点保持链接,所以我们不会在下一次调用时看到引导方法的输出)。
StringBuilder
方法是 Java 9 之前编译的字符串连接的产物,因此从 Java 9 开始,它只会打印
linking to freeMemory()long in class java.lang.Runtime
linking to println(String)void in class java.io.PrintStream
131449472 bytes free
(当然,数字会有所不同)
如果你想根据实际的接收者执行替代的动态调度,你可以用这样的东西替换 LinkLikeInvokeVirtual
:
public class LinkWithDynamicDispatch {
static final MethodHandle DISPATCHER;
static {
try {
DISPATCHER = MethodHandles.lookup().findStatic(LinkWithDynamicDispatch.class, "simpleDispatcher",
MethodType.methodType(MethodHandle.class, MethodHandle.class, String.class, Object.class));
} catch(NoSuchMethodException|IllegalAccessException ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static CallSite bootstrap(MethodHandles.Lookup l, String name, MethodType type){
MethodHandle target;
try {
target = l.findVirtual(type.parameterType(0), name, type.dropParameterTypes(0, 1));
} catch(NoSuchMethodException|IllegalAccessException ex) {
throw new BootstrapMethodError(ex);
}
MethodHandle d = MethodHandles.insertArguments(DISPATCHER, 0, target, name);
target = MethodHandles.foldArguments(MethodHandles.exactInvoker(type),
d.asType(d.type().changeParameterType(0, type.parameterType(0))));
return new ConstantCallSite(target);
}
public static MethodHandle simpleDispatcher(
MethodHandle invokeVirtualTarget, String methodName, Object rec) {
System.out.println("simpleDispatcher(): invoke "+methodName+" on "
+ "declared receiver type "+invokeVirtualTarget.type().parameterType(0)+", "
+ "actual receiver "+(rec==null? "null": "("+rec.getClass().getName()+"): "+rec));
return invokeVirtualTarget;
}
}
这会根据静态类型执行类似 invokevirtual
的查找,然后链接到 simpleDispatcher
方法,该方法将额外接收实际的接收器实例到已解析的目标。然后它可能只返回目标句柄或不同的句柄,具体取决于实际的接收者。
关于java - 如何使用 ASM 生成模拟 invokevirtual 的 invokedynamic 调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50778399/
Java 8 引入了对一等函数的支持,允许将函数分配给变量。在这种情况下,变量必须是函数类型,它由函数式接口(interface)(只有一个抽象方法的接口(interface))定义。 因此,考虑一个
最近,我正在阅读有关 Java 7 功能的内容,invokedynamic 是最令人期待的功能之一。大多数博客/文章都从 JVM 实现的角度提供了优点,特别是对于动态类型语言。从应用程序开发人员的角度
为了支持动态类型和方法分派(dispatch),我的编程语言引入了一种称为dynamic 的类型。当在类型为 dynamic 的被调用者上调用方法时,编译器首先将被调用者和所有参数压入堆栈,然后生成一
我正在尝试在 JVM7 中使用 invokedynamic 实现“duck typing”。我创建了两个不同的类,它们都有返回 String 的方法 greet()。我随机选择其中之一,将实例存储在堆
是否可以使用 InvokeDynamic for Grails? 如果是,哪些版本的 Grails、Java 等兼容? 设置过程是怎样的? 如果不可能,什么时候可以将 InvokeDynamic 支持
TL;DR 请提供一段用一些众所周知的动态语言(例如 JavaScript)编写的代码,以及该代码在使用 invokedynamic 的 Java 字节码中的样子,并解释为什么使用 invokedyn
背景 我目前正在用 C# 编写 JVM,纯粹出于学术目的(也许将来会构建混合的 .NET 和 Java/Scala 应用程序)。 上下文 我编写了简单的JAVA类: public class test
我想知道是否可以使用 Java7 的新 invokedynamic 字节码指令来实现 multiple dispatch对于 Java 语言。 java.lang.invoke 下的新 API 是否有
在对 invokeDynamic 进行了大量阅读之后,我仍然有点困惑。一个重复的主题似乎是 Clojure 并不真正需要它,或者至少比 JVM 上的其他动态语言实现(Jruby、JPython)更少需
我渴望在 Java 之上开始使用动态语言。 这成为标准 JDK 的一部分需要多长时间? 最佳答案 Java 7 发布时。目前,最好的猜测是 2010 年底,但可能会发生变化。 Keep an eye
$ scala -Xexperimental Welcome to Scala version 2.9.0.1 (OpenJDK Server VM, Java 1.6.0_22). Type in
想一想动态的、预测的方面语言。可以调用方面(即方法)代替或之前和之后原始方法。这些方面在运行时打开和关闭。甚至可能是,多个方面想要更改相同方法,这将导致这些方面组合成一个方法调用链。 原始方法由加载时
我正在通过 ASM 使用 Java 字节码并试图让一个简单的 invokedynamic 示例正常运行。我觉得好像我从根本上误解了 invokedynamic 应该如何工作。到目前为止,这是我尝试过的
invokedynamic 指令用于帮助虚拟机在运行时确定方法引用,而不是在编译时对其进行硬连接。 这对于动态语言很有用,在动态语言中,直到运行时才知道确切的方法和参数类型。但 Java lambda
是否有估计表明 JSR-292 将对 Groovy 性能产生多大影响? 最佳答案 invokedynamic 确实是一个复杂的故事,因为性能特征在 JDK7 中一直在变化。在将 Groovy 移植到
我有这个类,我编译它。 package org.test; import java.util.function.Supplier; public class Test { static Str
今天下载了studio 3.0 beta 2.0版本,之后尝试在里面打开一个已有的项目,遇到了一些困难,大部分问题我可以借助Google和Stack Overflow解决,但是这个我不行。 Error
我想看看如何使用与 invokevirtual 相同的调度逻辑进行 invokedynamic 调用。 我问这个问题是因为目前在线的使用 ASM 生成动态方法调用的示例太琐碎而无法概括,我认为这种情况
我的 Android 应用程序像这样使用 Java 8 lambda: myView.setOnClickListener(view -> someMethod()); 在构建应用程序或运行单元测试时
我在 Android Studio 3.0 中遇到了这个错误(在 2.3 中出现了另一个错误)。几个小时以来,我一直在尝试修复删除一些依赖项,但没有任何效果。所以现在我不想要我还能尝试什么。
我是一名优秀的程序员,十分优秀!