更新时间:2023-02-24 17:39:39
实际上,字节码构造函数的规则比Java的规则要宽松得多。
Actually, the rules for bytecode constructors are much more lax than Java's rules.
唯一的规则是必须在任何正常返回的路径上调用一个构造函数如果构造函数调用抛出异常,那么你也必须抛出异常。
The only rule is that exactly one constructor must be called on any path that returns normally and if a constructor call throws an exception, then you must throw an exception too.
除此之外,这意味着构造函数可能包含对其他构造函数的多次调用或者没有
Among other things, this means that a constructor may contain multiple calls to other constructors or none at all.
无论如何,确定给定调用特殊
调用是否正在初始化当前对象的唯一保证方法是进行数据流分析,因为可以初始化同一类的其他对象,这会混淆一个天真的探测器。
Anyway, the only guaranteed way to determine whether a given invokespecial
call is initializing the current object is to do a dataflow analysis, since it's possible to initialize other objects of the same class, which would confuse a naive detector.
编辑:这是一个完全有效的类(使用Krakatau汇编语法)的示例,显示了您可能遇到的一些问题。除此之外,它还调用了同一个类中的其他构造函数,构造函数的递归调用,以及在构造函数中构造同一个类的其他对象。
Here is an example of a perfectly valid class (using the Krakatau assembler syntax), showing some of the issues you could run into. Among other things, it has calls to other constructors in the same class, recursive invocation of constructors, and constructing other objects of the same class inside the constructor.
.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