JVM字节码访问修饰符标志何时为0x1000(十六进制)为“合成”?组? [英] When is the JVM bytecode access modifier flag 0x1000 (hex) "synthetic" set?
问题描述
对于某些Java字节代码解析器项目,我阅读了JVM规范并发现Java虚拟机类文件格式访问修饰符字段的位掩码值为
ACC_PUBLIC = 0x0001
ACC_FINAL = 0x0010
ACC_SUPER = 0x0020#旧的调用特殊指令语义(Java 1.0x?)
ACC_INTERFACE = 0x0200
ACC_ABSTRACT = 0x0400
ACC_SYNTHETIC = 0x1000
ACC_ANNOTATION = 0x2000
ACC_ENUM = 0x4000
我不知道 0x1000
是干什么的。我曾经在一个内部类中看到过它,但是从那以后我检查的所有内部类都从未设置过此标志。您现在是否知道此标志的含义以及它的设置位置/时间?
合成元素是指存在于已编译的类文件中,但不存在于其源代码中。通过检查元素是否为合成元素,您可以将这些元素与反射性处理代码的工具区分开。当然,这首先与使用反射的库相关,但也与其他工具(如IDE)相关,这些工具不允许您调用合成方法或使用合成类。最后,对于Java编译器来说,在编译过程中验证代码不要直接使用合成元素也很重要。合成元素仅用于使Java运行时感到高兴,该Java运行时仅在与其他元素相同地对待合成元素的情况下简单地处理(并验证)所交付的代码。
内部类作为示例,其中Java编译器插入了合成元素,因此让我们看一下这样的类:
class Foo {
private String foo;
class Bar {
private Bar(){}
字符串bar(){
return foo;
}
}
Bar bar(){
return new Bar();
}
}
这可以很好地编译,但是没有合成元素,它将不了解内部类的JVM会拒绝它。 Java编译器将上述类
class Foo {
$ p如前所述,JVM不了解内部类,但会强制成员的私有访问,即内部类将无法访问其封闭类的私有属性。因此,Java编译器需要向被访问的类中添加所谓的访问器,以显示其不可见的属性:
private String foo;
字符串访问$ 100(){//合成方法
return foo;
}
Foo $ Bar bar(){
返回新的Foo $ Bar(this,(Foo $ 1)null);
}
Foo(){} //非合成,但隐式!
}
class Foo $ Bar {
private final Foo $ this; //综合字段
private Foo $ Bar(Foo $ this){//综合参数
this。$ this = $ this;
}
Foo $ Bar(Foo $ this,Foo $ 1未使用){//综合构造函数
this($ this);
}
字符串bar(){
return $ this.access $ 100();
}
}
类Foo $ 1 {/ *空,没有构造函数* /} //合成类
foo
字段是私有字段,因此只能从Foo
内部访问。access $ 100
方法将此字段暴露给始终在其中查找内部类的包。此方法是由编译器添加的合成方法。
Bar
构造函数是私有的,可以因此只能从其自己的类中调用。为了实例化Bar
的实例,另一个(合成的)构造函数需要公开实例的构造。但是,构造函数具有固定的名称(在内部,它们全称为< init>
),因此我们无法将技术应用于仅将其命名为<$ c的方法访问器$ c> access $ xxx 。相反,我们通过创建综合类型Foo $ 1
来使构造函数访问器唯一。
在其外部实例中,内部类需要存储对此实例的引用,该引用存储在合成字段
$ this
中。此引用需要通过构造函数中的综合参数传递给内部实例。
其他有关综合元素的示例是表示lambda表达式的类,在使用类型不同的签名覆盖方法时桥接方法,创建
Proxy
类或由其他工具(例如Maven构建或运行时)创建的类代码生成器,例如字节好友(无耻的插件)。For some Java byte code parser project I read the JVM spec and figured out that the bit mask values of the Java virtual machine class file format access modifier fields are
ACC_PUBLIC = 0x0001 ACC_FINAL = 0x0010 ACC_SUPER = 0x0020 # old invokespecial instruction semantics (Java 1.0x?) ACC_INTERFACE = 0x0200 ACC_ABSTRACT = 0x0400 ACC_SYNTHETIC = 0x1000 ACC_ANNOTATION = 0x2000 ACC_ENUM = 0x4000
Somehow I have no idea what
0x1000
is for. I saw it once in an inner class, but for all inner classes I checked since then, this flag was never set. Do you now what the meaning of this flag is and where/when it is set?解决方案A synthetic element is any element that is present in a compiled class file but not in the source code it is compiled from. By checking an element for it being synthetic, you allow a distinction of such elements for tools that process code reflectively. This is of course first of all relevant to libraries that use reflection but it is also relevant for other tools like IDEs that do not allow you to call synthetic methods or to work with synthetic classes. Finally, it is also important for the Java compiler to verify code during its compilation to never directly use synthetic elements. Synthetic elements are only used to make the Java runtime happy which simply processes (and verifies) the delivered code where it treats synthetic elements identically to any other element.
You already mentioned inner classes as an example where synthetic elements are inserted by the Java compiler, so let us look at such a class:
class Foo { private String foo; class Bar { private Bar() { } String bar() { return foo; } } Bar bar() { return new Bar(); } }
This compiles perfectly fine but without synthetic elements, it would be refused by a JVM that does not know a thing about inner classes. The Java compiler desugares the above class to something like the following:
class Foo { private String foo; String access$100() { // synthetic method return foo; } Foo$Bar bar() { return new Foo$Bar(this, (Foo$1)null); } Foo() { } // NON-synthetic, but implicit! } class Foo$Bar { private final Foo $this; // synthetic field private Foo$Bar(Foo $this) { // synthetic parameter this.$this = $this; } Foo$Bar(Foo $this, Foo$1 unused) { // synthetic constructor this($this); } String bar() { return $this.access$100(); } } class Foo$1 { /*empty, no constructor */ } // synthetic class
As said, the JVM does not know about inner classes but enforces private access of members, i.e. an inner class would not be able to access its enclosing classes' private properties. Thus, the Java compiler needs to add so-called accessors to an accessed class in order to expose its non-visible properties:
The
foo
field is private and can therefore only be accessed from withinFoo
. Theaccess$100
method exposes this field to its package in which an inner class is always to be found. This method is synthetic as it is added by the compiler.The
Bar
constructor is private and can therefore only be called from within its own class. In order to instantiate an instance ofBar
, another (synthetic) constructor needs to expose the construction of an instance. However, constructors have a fixed name (internally, they are all called<init>
), thus we cannot apply the technique for method accessors where we simply named themaccess$xxx
. Instead, we make constructor accessors unique by creating a synthetic typeFoo$1
.In order to access its outer instance, an inner class needs to store a reference to this instance which is stored in a synthetic field
$this
. This reference needs to be handed to the inner instance by a synthetic parameter in the constructor.Other examples for synthetic elements are classes that represent lambda expressions, bridge methods when overriding methods with a type-divergent signatures, the creation of
Proxy
classes or classes that are created by other tools like Maven builds or runtime code generators such as Byte Buddy (shameless plug).这篇关于JVM字节码访问修饰符标志何时为0x1000(十六进制)为“合成”?组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!