在字节码确定哪里是超()方法调用所有构造函数必须在JVM上做(Determining in the

2019-08-16 16:46发布

我在想,如果有分析构造函数的字节码的时候,以确定其中的一个明显和快速的方式super()代码结束。

更具体地说,并形成鲜明对比的Java,其中在构造函数中的任何一个调用super()构造方法是可选的(或者说,当不存在-隐含的),在字节码的世界里总是需要。

对于黑魔法的目的,我需要知道的只是字节码分析和可用的最简单的方法,有什么的INVOKESPECIAL对应于Java世界的召唤super()调用。

在这里我要离开你用硬例如:

public static class A {
    public A(Object o, Object b) {
    }
}

public static class B extends A {
    public B() {
        //the below super is in bold just to signal that's the one
        //I'm looking for
        SUPER(new A(new Object(), new Integer(2)), new Integer(1));
        System.out.println(new A(new Object(), new Integer(2)));
    }
}

与对应的字节代码:

Answer 1:

实际上,对于字节码构造的规则比Java的规则更为宽松。

唯一的规则是,只有一个构造函数必须正常返回,如果一个构造函数调用抛出一个异常,那么你必须抛出一个异常过任何路径上调用。

别的不说,这意味着一个构造函数可以包含对其他构造函数或根本多个呼叫。

不管怎样,唯一可以保证的方法来确定是否一个给定的invokespecial调用初始化当前对象是做数据流分析,因为它可以初始化同一类,它会混淆天真探测器的其他对象。

编辑:这是一个完全有效的类的实例(使用喀拉喀托汇编语法),显示出一些你可能会遇到的问题。 除其他事项外,它在同一类调用其他构造函数,构造函数的递归调用,并构建在构造函数中同一类的其他对象。

.class public ctors
.super java/lang/Object

; A normal constructor
.method public <init> : ()V
    .limit locals 1
    .limit stack 1

    aload_0
    invokespecial java/lang/Object <init> ()V
    return
.end method

; A weird constructor
.method public <init> : (I)V
    .limit locals 2
    .limit stack 5

    iload_1
    ifne LREST
        aload_0
        invokespecial ctors <init> ()V
        return

LREST:
    aload_0
    new ctors
    iinc 1 -1
    iload_1
LFAKE_START:
    invokespecial ctors <init> (I)V
LFAKE_END:
    iconst_0
    invokespecial ctors <init> (I)V
    return

.catch [0] from LFAKE_START to LFAKE_END using LCATCH
LCATCH:
    aload_0
    invokespecial java/lang/Object <init> ()V
    return
.end method

.method public static main : ([Ljava/lang/String;)V
    .limit locals 1
    .limit stack 2

    new ctors
    iconst_5
    invokespecial ctors <init> (I)V
    return
.end method


Answer 2:

一个简单的解决方法是计数数量new A的对象和数量A.<init>当有更多initnew已被称为超级构造函数。 你必须做相同的检查new BB.<init>的情况下, this(...)被调用。



Answer 3:

你必须找出在该调用操作码操作数栈包含this引用将被用来作为第一个参数。 对于这一点,你只需要知道的是,不同的操作码有操作数栈上的影响。 在您的例子中,你开始aload_0 (这是this参考),然后做了相当多的神奇高于基准(更新操作数栈所有的时间)。 过了一会儿,你正在寻找的invoke操作码是存在的,这消耗了this参考(和对参数一些参考)。 然后,这是super调用。



Answer 4:

这个问题的答案超()的调用是线上没有。 31。

我发现很容易通过Eclipse的类文件编辑器。 看看下面附卡。

这里有一点要记住的是,前缀“A”指操作码操作对象的引用。 前缀的“i”表示操作码操作的整数。

因此,由线解释的路线如下,

12  new java.lang.Integer //Create a new java.lang.Integer 
15  dup //Make a extra reference to the same Integer
16  iconst_2 // this means opcode is manipulating Integer as Integer(2)
17  invokespecial java.lang.Integer(int) //Integer(2) is invoked
20  invokespecial A(java.lang.Object, java.lang.Object) //new A(new Object(), new Integer(2) is invoked
23  new java.lang.Integer //Create a new java.lang.Integer
26  dup //Make a extra reference to the same Integer
27  iconst_1 // this means opcode is manipulating Integer as Integer(1)
28  invokespecial java.lang.Integer(int) //Integer(1) is invoked
31  invokespecial A(java.lang.Object, java.lang.Object) **//super(new A(new Object(), new Integer(2)), new Integer(1)) is invoked**

我希望以后很容易理解。 :)

56 - this invoke is for the sysout related A(object,object) invocation.



文章来源: Determining in the bytecode where is the super() method call all constructors must do on the JVM
标签: java bytecode