调用动态指令如何确定其描述?

用户名

我正在使用ASM修改方法引用,所以我可以将其钩住。我的方法是修改引导程序的Handlearg,并使其成为新方法的目标,稍后我将生成它。以下是我针对此目标的代码段。


class MyMethodVisitor extends MethodNode{

    //...

    @Override
    public void visitEnd() {
        super.visitEnd();
        ListIterator<AbstractInsnNode> iterator = this.instructions.iterator();

        while (iterator.hasNext()) {
            AbstractInsnNode node = iterator.next();
            if (node instanceof InvokeDynamicInsnNode) {
                InvokeDynamicInsnNode tmpNode = (InvokeDynamicInsnNode) node;
                String samName = tmpNode.name;

                String middleMethodName = samName + "sa" + counter.incrementAndGet();
                String middleMethodDesc = "";

                Handle handle = (Handle) tmpNode.bsmArgs[1];

                Type type = (Type) tmpNode.bsmArgs[2];
                //handleNew will reference to the middleMethod
                Handle handleNew = new Handle(Opcodes.H_INVOKESTATIC, "cn/curious/asm/lambda/LambdaModel", middleMethodName,
                        type.getDescriptor(), false);
                tmpNode.bsmArgs[1] = handleNew;
                middleMethodDesc = type.getDescriptor();
                String dynamicNewDesc = "()" + Type.getReturnType(tmpNode.desc);

                InvokeDynamicInsnNode newDynamicNode =
                        new InvokeDynamicInsnNode(tmpNode.name,
                                dynamicNewDesc,
                                tmpNode.bsm, tmpNode.bsmArgs[0], handleNew, type);
                //Here, i remote the origin InvokeDynamicInsnNode and add mine
                iterator.remove();
                iterator.add(newDynamicNode);

                MethodNode methodNode = new MethodNode(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC ,
                        middleMethodName,middleMethodDesc,null,null);
                methodNode.visitEnd();
                syntheticMethodList.add(methodNode);
            }
        }
        accept(mv);
    }

   //...
}

我的测试Java源代码如下:

public class LambdaModel {

    public void test1() {
        Consumer<String> consumer = System.out::println;
        consumer.accept("hello world");
    }

    public void test2() {
        Consumer<String> consumer = s -> System.out.println(s);
    }

    public static void main(String[] args) {
        LambdaModel model = new LambdaModel();
        model.test1();
    }
}

并且,生成的类文件如下:

public class LambdaModel {
    public LambdaModel() {
    }

    public void test1() {
        System.out.getClass();
        Consumer<String> consumer = LambdaModel::acceptsa1;
        consumer.accept("hello world");
    }

    public void test2() {
        Consumer<String> consumer = (s) -> {
            System.out.println(s);
        };
    }

    public static void lambda$accept(String str) {
        System.out.println(str);
    }

    public static void main(String[] args) {
        LambdaModel model = new LambdaModel();
        model.test1();
    }
    //This method is generated by using ASM
    public static void acceptsa1(String var0) {
    }
}

您可以看到,该acceptsa1方法没有代码主体,因为我不知道如何分析相应的字节码。

接下来是源代码的字节码片段,我的问题在里面。

public test1()V
   L0
    LINENUMBER 8 L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    DUP
    INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
    POP

    INVOKEDYNAMIC accept(Ljava/io/PrintStream;)Ljava/util/function/Consumer; [
      // handle kind 0x6 : INVOKESTATIC
      java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      // arguments:
      (Ljava/lang/Object;)V, 
      // handle kind 0x5 : INVOKEVIRTUAL
      java/io/PrintStream.println(Ljava/lang/String;)V, 
      (Ljava/lang/String;)V
    ]
    ASTORE 1
   L1

Q1:我无法理解后面的动态调用指令中的以下指令。

    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    DUP
    INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
    POP

Q2:为什么invokedynamic的描述中有一个PrintStream参数?它基于什么规则。

INVOKEDYNAMIC accept(Ljava/io/PrintStream;)Ljava/util/function/Consumer;

对不起,我无法确切描述问题。也许我的最后一个问题是invokedynamic指令如何在字节码中工作。


更新
我发现了一个重要点,即将方法引用更改为正常的lambda表达式。生成中间方法名称时,必须将此方法的访问权限设置为ACC_SYNTHETIC,然后才能得到所需的方法。
例如,
方法参考:

MethodReferenceMain referenceMain = new MethodReferenceMain();
Comparator<User> comparator2 = referenceMain::compareByName;

生成的类:

MethodReferenceMain referenceMain = new MethodReferenceMain();
Comparator<User> comparator2 = (var1, var2) -> {
     return referenceMain.compareByName(var1, var2);
};

注意:肯定还有其他我没有遇到的问题。但这对我来说是重要的一步。

霍尔格

invokedynamic指令不要求任何签名。是代码生成器决定要使用的特定签名和引导方法,它们共同定义了实际的语义。

例如,使用签名而StringConcatFactory.makeConcat(…)不是LambdaMetafactory.metafactory(…)引导方法时,签名及其含义完全不同

这些方法及其包含的类的文档全面描述了该行为。但是,在深入研究字节码细节之前,您应该首先了解源代码功能,即捕获方法引用的功能,如System.out :: println的等效lambda表达式中所述方法参考在实例化时System.out::println捕获PrintStream在静态字段中找到System.out的内容Consumer因此,生成的工厂方法使用aPrintStream并生成a Consumer,从而导致签名(Ljava/io/PrintStream;)Ljava/util/function/Consumer;

因此,在执行invokedynamic指令之前,必须使用GETSTATIC java/lang/System.out指令读取该字段DUP; INVOKEVIRTUAL getClass()序列是内部null检查的一部分,在Java Lambda中讨论了为什么要对捕获的变量调用getClass()

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

如果通过角度控制器动态添加包含指令的元素,则如何调用指令?

来自分类Dev

如果通过角度控制器动态添加包含指令的元素,则如何调用指令?

来自分类Dev

如何动态嵌套指令

来自分类Dev

如何确定屏幕上是否显示了动态组件并在Angular8中访问其属性

来自分类Dev

如何从 html 调用指令

来自分类Dev

如何动态禁用Angular指令?

来自分类Dev

如何动态创建ngInclude指令?

来自分类Dev

如何动态确定libudev版本

来自分类Dev

AngularJS:如何实现输出其HTML的指令?

来自分类Dev

如何控制指令的调用

来自分类Dev

如何从指令调用的函数中调用函数

来自分类Dev

Canvas如何确定其剪辑范围?

来自分类Dev

Set如何确定其值的顺序?

来自分类Dev

如何确定ajax调用的来源

来自分类Dev

如何动态调用Junits

来自分类Dev

如何动态调用函数?

来自分类Dev

如何动态调用范围

来自分类Dev

如何调用动态ID?

来自分类Dev

如何动态调用函数

来自分类Dev

如何自动确定图像文件描述的是照片还是“图形”?

来自分类Dev

如何从Redshift获取描述表并对其进行更改

来自分类Dev

如何动态更新角度指令属性?

来自分类Dev

如何使用指令动态更改模板?

来自分类Dev

如何动态加载Angular JS指令/模板

来自分类Dev

如何在AngularJS中动态嵌套指令

来自分类Dev

如何动态加载和编译指令?

来自分类Dev

如何使用AngularJS加载动态指令内容?

来自分类Dev

动态添加指令时未调用AngularJS $parser

来自分类Dev

如何确定对动态数组的引用数?