What is Java Synthetic Class (ACC_SYNTHETIC)? with the example of BoundMethodHandle$1.class in Java package java.lang.invoke

When a .class file marked with the ACC_SYNTHETIC flag, it indicates the .class file was generated by compiler and does not appear in the corresponding .java source code.

Binary Code is always the best document, so let's check an example: the BoundMethodHandle$1.class class in java.base.jmod in JDK.

The class file BoundMethodHandle$1.class has been marked as synthetic flag, as shown bellow:

Basic Information of BoundMethodHandle$1.class 
UML diagram of BoundMethodHandle$1.class

And this class is used by the container class BoundMethodHandle:

Dependency Network diagram of BoundMethodHandle$1.class

This class does not exist in the original source code BoundMethodHandle.java, but generated by the java compiler, from the following two (2) methods in the Java class BoundMethodHandle:

  1. BoundMethodHandle.bindSingle(), at byte code offset 0
  2. BoundMethodHandle.arg(), at byte code offset 17
Byte code of method BoundMethodHandle.bindSingle()

Byte code of method BoundMethodHandle.arg()

Highlight: Source code of BoundMethodHandle.bindSingle():

62  static BoundMethodHandle bindSingle(MethodType type, LambdaForm form, BasicType xtype, Object x) {
63      // for some type signatures, there exist pre-defined concrete BMH classes
64      try {
65          switch (xtype) {
66          case L_TYPE:
67              return bindSingle(type, form, x);  // Use known fast path.
68          case I_TYPE:
69              return (BoundMethodHandle) SPECIALIZER.topSpecies().extendWith(I_TYPE_NUM).factory().invokeBasic(type, form, ValueConversions.widenSubword(x));
70          case J_TYPE:
71              return (BoundMethodHandle) SPECIALIZER.topSpecies().extendWith(J_TYPE_NUM).factory().invokeBasic(type, form, (long) x);
72          case F_TYPE:
73              return (BoundMethodHandle) SPECIALIZER.topSpecies().extendWith(F_TYPE_NUM).factory().invokeBasic(type, form, (float) x);
74          case D_TYPE:
75              return (BoundMethodHandle) SPECIALIZER.topSpecies().extendWith(D_TYPE_NUM).factory().invokeBasic(type, form, (double) x);
76          default : throw newInternalError("unexpected xtype: " + xtype);
77          }
78      } catch (Throwable t) {
79          throw uncaughtException(t);
80      }
81  }

Highlight: Source code of BoundMethodHandle.arg():

178  /*non-public*/ final Object arg(int i) {
179      try {
180          Class fieldType = speciesData().fieldTypes().get(i);
181          switch (BasicType.basicType(fieldType)) {
182              case L_TYPE: return          speciesData().getter(i).invokeBasic(this);
183              case I_TYPE: return (int)    speciesData().getter(i).invokeBasic(this);
184              case J_TYPE: return (long)   speciesData().getter(i).invokeBasic(this);
185              case F_TYPE: return (float)  speciesData().getter(i).invokeBasic(this);
186              case D_TYPE: return (double) speciesData().getter(i).invokeBasic(this);
187          }
188      } catch (Throwable ex) {
189          throw uncaughtException(ex);
190      }
191      throw new InternalError("unexpected type: " + speciesData().key()+"."+i);
192  }

So we can found that, the key reason for the compiler to generate the BoundMethodHandle$1.class class, and its $SwitchMap$java$lang$invoke$LambdaForm$BasicType field is, there is an switch statment on the LambdaForm.BasicType value.

Here is the highlighted source code of the LambdaForm.BasicType:

139  enum BasicType {
140      L_TYPE('L', Object.class, Wrapper.OBJECT),  // all reference types
141      I_TYPE('I', int.class,    Wrapper.INT),
142      J_TYPE('J', long.class,   Wrapper.LONG),
143      F_TYPE('F', float.class,  Wrapper.FLOAT),
144      D_TYPE('D', double.class, Wrapper.DOUBLE),  // all primitive types
145      V_TYPE('V', void.class,   Wrapper.VOID);    // not valid in all contexts
155      static final byte
156              L_TYPE_NUM = (byte) L_TYPE.ordinal(),
157              I_TYPE_NUM = (byte) I_TYPE.ordinal(),
158              J_TYPE_NUM = (byte) J_TYPE.ordinal(),
159              F_TYPE_NUM = (byte) F_TYPE.ordinal(),
160              D_TYPE_NUM = (byte) D_TYPE.ordinal(),
161              V_TYPE_NUM = (byte) V_TYPE.ordinal();

And we can easily double confirm it is indeed the source code for the generated class contructor logic of BoundMethodHandle$1.class.

Call graph of BoundMethodHandle$1.class class constructor


Popular posts from this blog

What is Java Bridge Method (ACC_BRIDGE)?

Evolution of Java Dependency Diagrams, from Java 7 to Java 13