JVM字节码访问修饰符标志何时为0x1000(十六进制)为“合成”?组? [英] When is the JVM bytecode access modifier flag 0x1000 (hex) "synthetic" set?

查看:311
本文介绍了JVM字节码访问修饰符标志何时为0x1000(十六进制)为“合成”?组?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于某些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 {

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 {/ *空,没有构造函数* /} //合成类

  1. foo 字段是私有字段,因此只能从 Foo 内部访问。 access $ 100 方法将此字段暴露给始终在其中查找内部类的包。此方法是由编译器添加的合成方法。


  2. Bar 构造函数是私有的,可以因此只能从其自己的类中调用。为了实例化 Bar 的实例,另一个(合成的)构造函数需要公开实例的构造。但是,构造函数具有固定的名称(在内部,它们全称为< init> ),因此我们无法将技术应用于仅将其命名为<$ c的方法访问器$ c> access $ xxx 。相反,我们通过创建综合类型 Foo $ 1 来使构造函数访问器唯一。


  3. 在其外部实例中,内部类需要存储对此实例的引用,该引用存储在合成字段 $ 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:

  1. The foo field is private and can therefore only be accessed from within Foo. The access$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.

  2. The Bar constructor is private and can therefore only be called from within its own class. In order to instantiate an instance of Bar, 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 them access$xxx. Instead, we make constructor accessors unique by creating a synthetic type Foo$1.

  3. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆