-
Bug
-
Resolution: Withdrawn
-
P3
-
repo-valhalla
In LW1, it was possible for a given value type Foo to be referenced in two different ways: LFoo; and QFoo;.
In LW3, LFoo; and QFoo; now denote two different types.
It is critical that symbolic references in the constant pool denote unambiguously the correct type. Type descriptors, used for fields and method signatures, already always use an envelope around the class name to specify which type is expected. However, CONSTANT_Class_info entries still use simple binary names in many places.
The Expert Group has defined a new semantic for CONSTANT_Class_info entries:
- a symbolic reference to an inline type must always put a Q-envelope around the class name
- a symbolic reference to a non inline type can either use a L-envelope or a simple binary name (for backward compatibility)
Example:
The following source code:
public inline class CCIEntry {
int i = 0;
int getI() {
return i;
}
public static void main(String[] args) {
CCIEntry c = new CCIEntry();
int i = c.i;
i = c.getI();
}
static boolean isCCIEntry(Object o) {
return o instanceof CCIEntry;
}
static CCIEntry getCCIEntryOrThrow(Object o) {
return (CCIEntry)o;
}
}
is currently compiled into this class file:
Classfile CCIEntry.class
Last modified May 13, 2020; size 1270 bytes
SHA-256 checksum 0c6fec11effe415da2294aeadf0021c3a3aea3af07599d483eba8dd0bd1cc020
Compiled from "CCIEntry.java"
public final value class CCIEntry extends CCIEntry$ref implements java.lang.InlineObject
minor version: 0
major version: 59
flags: (0x0131) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_VALUE
this_class: #1 // CCIEntry
super_class: #29 // CCIEntry$ref
interfaces: 1, fields: 1, methods: 8, attributes: 3
Constant pool:
#1 = Class #2 // CCIEntry
#2 = Utf8 CCIEntry
#3 = Fieldref #1.#4 // CCIEntry.i:I
#4 = NameAndType #5:#6 // i:I
#5 = Utf8 i
#6 = Utf8 I
#7 = Methodref #1.#8 // CCIEntry."<init>":()QCCIEntry;
#8 = NameAndType #9:#10 // "<init>":()QCCIEntry;
#9 = Utf8 <init>
#10 = Utf8 ()QCCIEntry;
#11 = Methodref #1.#12 // CCIEntry.getI:()I
#12 = NameAndType #13:#14 // getI:()I
#13 = Utf8 getI
#14 = Utf8 ()I
#15 = Class #16 // "QCCIEntry;"
#16 = Utf8 QCCIEntry;
#17 = InvokeDynamic #0:#18 // #0:hashCode:(QCCIEntry;)I
#18 = NameAndType #19:#20 // hashCode:(QCCIEntry;)I
#19 = Utf8 hashCode
#20 = Utf8 (QCCIEntry;)I
#21 = InvokeDynamic #0:#22 // #0:equals:(QCCIEntry;Ljava/lang/Object;)Z
#22 = NameAndType #23:#24 // equals:(QCCIEntry;Ljava/lang/Object;)Z
#23 = Utf8 equals
#24 = Utf8 (QCCIEntry;Ljava/lang/Object;)Z
#25 = InvokeDynamic #0:#26 // #0:toString:(QCCIEntry;)Ljava/lang/String;
#26 = NameAndType #27:#28 // toString:(QCCIEntry;)Ljava/lang/String;
#27 = Utf8 toString
#28 = Utf8 (QCCIEntry;)Ljava/lang/String;
#29 = Class #30 // CCIEntry$ref
#30 = Utf8 CCIEntry$ref
#31 = Class #32 // java/lang/InlineObject
#32 = Utf8 java/lang/InlineObject
#33 = Utf8 Code
#34 = Utf8 LineNumberTable
#35 = Utf8 main
#36 = Utf8 ([Ljava/lang/String;)V
#37 = Utf8 isCCIEntry
#38 = Utf8 (Ljava/lang/Object;)Z
#39 = Utf8 getCCIEntryOrThrow
#40 = Utf8 (Ljava/lang/Object;)QCCIEntry;
#41 = Utf8 ()Ljava/lang/String;
#42 = Utf8 SourceFile
#43 = Utf8 CCIEntry.java
#44 = Utf8 BootstrapMethods
#45 = MethodHandle 6:#46 // REF_invokeStatic java/lang/invoke/ValueBootstrapMethods.makeBootstrapMethod:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#46 = Methodref #47.#48 // java/lang/invoke/ValueBootstrapMethods.makeBootstrapMethod:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#47 = Class #49 // java/lang/invoke/ValueBootstrapMethods
#48 = NameAndType #50:#51 // makeBootstrapMethod:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#49 = Utf8 java/lang/invoke/ValueBootstrapMethods
#50 = Utf8 makeBootstrapMethod
#51 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#52 = Utf8 InnerClasses
#53 = Class #54 // java/lang/invoke/MethodHandles$Lookup
#54 = Utf8 java/lang/invoke/MethodHandles$Lookup
#55 = Class #56 // java/lang/invoke/MethodHandles
#56 = Utf8 java/lang/invoke/MethodHandles
#57 = Utf8 Lookup
{
final int i;
descriptor: I
flags: (0x0010) ACC_FINAL
int getI();
descriptor: ()I
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #3 // Field i:I
4: ireturn
LineNumberTable:
line 5: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=3, args_size=1
0: invokestatic #7 // Method "<init>":()QCCIEntry;
3: astore_1
4: aload_1
5: getfield #3 // Field i:I
8: istore_2
9: aload_1
10: invokevirtual #11 // Method getI:()I
13: istore_2
14: return
LineNumberTable:
line 9: 0
line 10: 4
line 11: 9
line 12: 14
static boolean isCCIEntry(java.lang.Object);
descriptor: (Ljava/lang/Object;)Z
flags: (0x0008) ACC_STATIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: instanceof #1 // class CCIEntry
4: ireturn
LineNumberTable:
line 15: 0
static CCIEntry getCCIEntryOrThrow(java.lang.Object);
descriptor: (Ljava/lang/Object;)QCCIEntry;
flags: (0x0008) ACC_STATIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: checkcast #15 // class "QCCIEntry;"
4: areturn
LineNumberTable:
line 19: 0
public final int hashCode();
descriptor: ()I
flags: (0x0011) ACC_PUBLIC, ACC_FINAL
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokedynamic #17, 0 // InvokeDynamic #0:hashCode:(QCCIEntry;)I
6: ireturn
LineNumberTable:
line 1: 0
public final boolean equals(java.lang.Object);
descriptor: (Ljava/lang/Object;)Z
flags: (0x0011) ACC_PUBLIC, ACC_FINAL
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: invokedynamic #21, 0 // InvokeDynamic #0:equals:(QCCIEntry;Ljava/lang/Object;)Z
7: ireturn
LineNumberTable:
line 1: 0
public final java.lang.String toString();
descriptor: ()Ljava/lang/String;
flags: (0x0011) ACC_PUBLIC, ACC_FINAL
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokedynamic #25, 0 // InvokeDynamic #0:toString:(QCCIEntry;)Ljava/lang/String;
6: areturn
LineNumberTable:
line 1: 0
public static CCIEntry CCIEntry();
descriptor: ()QCCIEntry;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=0
0: defaultvalue #1 // class CCIEntry
3: astore_0
4: iconst_0
5: aload_0
6: swap
7: withfield #3 // Field i:I
10: astore_0
11: aload_0
12: areturn
LineNumberTable:
line 1: 0
line 2: 4
}
SourceFile: "CCIEntry.java"
BootstrapMethods:
0: #45 REF_invokeStatic java/lang/invoke/ValueBootstrapMethods.makeBootstrapMethod:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
InnerClasses:
public static final #57= #53 of #55; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
The entries that need to be fixed are:
The class name itself, CCIEntry is an inline type, so the CONSTANT_Class_info entry at index 1 should use a Q-envelope, which means the Utf8 at index 2 should be "QCCIEntry;".
The field reference at index 3 points to a field of an inline type, so its symbolic reference to a class should use a Q-envelope too. Same fix as above.
The method references at index 7 and 11 points to methods of an inline type, so their symbolic reference to a class should use a Q-envelope too. Same fix as above.
In method isCCIEntry(java.lang.Object), the instanceof bytecode should point to a CONSTANT_Class_info entry referencing type "QCCIEntry;" (Fixing entry 1 would also fix this one).
In method getCCIEntryOrThrow(java.lang.Object), the bytecode checkcast already correctly points to a CONSTANT_Class_info entry referencing type ""QCCIEntry;" (but the entry can now be shared will all the other above).
In LW3, LFoo; and QFoo; now denote two different types.
It is critical that symbolic references in the constant pool denote unambiguously the correct type. Type descriptors, used for fields and method signatures, already always use an envelope around the class name to specify which type is expected. However, CONSTANT_Class_info entries still use simple binary names in many places.
The Expert Group has defined a new semantic for CONSTANT_Class_info entries:
- a symbolic reference to an inline type must always put a Q-envelope around the class name
- a symbolic reference to a non inline type can either use a L-envelope or a simple binary name (for backward compatibility)
Example:
The following source code:
public inline class CCIEntry {
int i = 0;
int getI() {
return i;
}
public static void main(String[] args) {
CCIEntry c = new CCIEntry();
int i = c.i;
i = c.getI();
}
static boolean isCCIEntry(Object o) {
return o instanceof CCIEntry;
}
static CCIEntry getCCIEntryOrThrow(Object o) {
return (CCIEntry)o;
}
}
is currently compiled into this class file:
Classfile CCIEntry.class
Last modified May 13, 2020; size 1270 bytes
SHA-256 checksum 0c6fec11effe415da2294aeadf0021c3a3aea3af07599d483eba8dd0bd1cc020
Compiled from "CCIEntry.java"
public final value class CCIEntry extends CCIEntry$ref implements java.lang.InlineObject
minor version: 0
major version: 59
flags: (0x0131) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_VALUE
this_class: #1 // CCIEntry
super_class: #29 // CCIEntry$ref
interfaces: 1, fields: 1, methods: 8, attributes: 3
Constant pool:
#1 = Class #2 // CCIEntry
#2 = Utf8 CCIEntry
#3 = Fieldref #1.#4 // CCIEntry.i:I
#4 = NameAndType #5:#6 // i:I
#5 = Utf8 i
#6 = Utf8 I
#7 = Methodref #1.#8 // CCIEntry."<init>":()QCCIEntry;
#8 = NameAndType #9:#10 // "<init>":()QCCIEntry;
#9 = Utf8 <init>
#10 = Utf8 ()QCCIEntry;
#11 = Methodref #1.#12 // CCIEntry.getI:()I
#12 = NameAndType #13:#14 // getI:()I
#13 = Utf8 getI
#14 = Utf8 ()I
#15 = Class #16 // "QCCIEntry;"
#16 = Utf8 QCCIEntry;
#17 = InvokeDynamic #0:#18 // #0:hashCode:(QCCIEntry;)I
#18 = NameAndType #19:#20 // hashCode:(QCCIEntry;)I
#19 = Utf8 hashCode
#20 = Utf8 (QCCIEntry;)I
#21 = InvokeDynamic #0:#22 // #0:equals:(QCCIEntry;Ljava/lang/Object;)Z
#22 = NameAndType #23:#24 // equals:(QCCIEntry;Ljava/lang/Object;)Z
#23 = Utf8 equals
#24 = Utf8 (QCCIEntry;Ljava/lang/Object;)Z
#25 = InvokeDynamic #0:#26 // #0:toString:(QCCIEntry;)Ljava/lang/String;
#26 = NameAndType #27:#28 // toString:(QCCIEntry;)Ljava/lang/String;
#27 = Utf8 toString
#28 = Utf8 (QCCIEntry;)Ljava/lang/String;
#29 = Class #30 // CCIEntry$ref
#30 = Utf8 CCIEntry$ref
#31 = Class #32 // java/lang/InlineObject
#32 = Utf8 java/lang/InlineObject
#33 = Utf8 Code
#34 = Utf8 LineNumberTable
#35 = Utf8 main
#36 = Utf8 ([Ljava/lang/String;)V
#37 = Utf8 isCCIEntry
#38 = Utf8 (Ljava/lang/Object;)Z
#39 = Utf8 getCCIEntryOrThrow
#40 = Utf8 (Ljava/lang/Object;)QCCIEntry;
#41 = Utf8 ()Ljava/lang/String;
#42 = Utf8 SourceFile
#43 = Utf8 CCIEntry.java
#44 = Utf8 BootstrapMethods
#45 = MethodHandle 6:#46 // REF_invokeStatic java/lang/invoke/ValueBootstrapMethods.makeBootstrapMethod:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#46 = Methodref #47.#48 // java/lang/invoke/ValueBootstrapMethods.makeBootstrapMethod:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#47 = Class #49 // java/lang/invoke/ValueBootstrapMethods
#48 = NameAndType #50:#51 // makeBootstrapMethod:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#49 = Utf8 java/lang/invoke/ValueBootstrapMethods
#50 = Utf8 makeBootstrapMethod
#51 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#52 = Utf8 InnerClasses
#53 = Class #54 // java/lang/invoke/MethodHandles$Lookup
#54 = Utf8 java/lang/invoke/MethodHandles$Lookup
#55 = Class #56 // java/lang/invoke/MethodHandles
#56 = Utf8 java/lang/invoke/MethodHandles
#57 = Utf8 Lookup
{
final int i;
descriptor: I
flags: (0x0010) ACC_FINAL
int getI();
descriptor: ()I
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #3 // Field i:I
4: ireturn
LineNumberTable:
line 5: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=3, args_size=1
0: invokestatic #7 // Method "<init>":()QCCIEntry;
3: astore_1
4: aload_1
5: getfield #3 // Field i:I
8: istore_2
9: aload_1
10: invokevirtual #11 // Method getI:()I
13: istore_2
14: return
LineNumberTable:
line 9: 0
line 10: 4
line 11: 9
line 12: 14
static boolean isCCIEntry(java.lang.Object);
descriptor: (Ljava/lang/Object;)Z
flags: (0x0008) ACC_STATIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: instanceof #1 // class CCIEntry
4: ireturn
LineNumberTable:
line 15: 0
static CCIEntry getCCIEntryOrThrow(java.lang.Object);
descriptor: (Ljava/lang/Object;)QCCIEntry;
flags: (0x0008) ACC_STATIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: checkcast #15 // class "QCCIEntry;"
4: areturn
LineNumberTable:
line 19: 0
public final int hashCode();
descriptor: ()I
flags: (0x0011) ACC_PUBLIC, ACC_FINAL
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokedynamic #17, 0 // InvokeDynamic #0:hashCode:(QCCIEntry;)I
6: ireturn
LineNumberTable:
line 1: 0
public final boolean equals(java.lang.Object);
descriptor: (Ljava/lang/Object;)Z
flags: (0x0011) ACC_PUBLIC, ACC_FINAL
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: invokedynamic #21, 0 // InvokeDynamic #0:equals:(QCCIEntry;Ljava/lang/Object;)Z
7: ireturn
LineNumberTable:
line 1: 0
public final java.lang.String toString();
descriptor: ()Ljava/lang/String;
flags: (0x0011) ACC_PUBLIC, ACC_FINAL
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokedynamic #25, 0 // InvokeDynamic #0:toString:(QCCIEntry;)Ljava/lang/String;
6: areturn
LineNumberTable:
line 1: 0
public static CCIEntry CCIEntry();
descriptor: ()QCCIEntry;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=0
0: defaultvalue #1 // class CCIEntry
3: astore_0
4: iconst_0
5: aload_0
6: swap
7: withfield #3 // Field i:I
10: astore_0
11: aload_0
12: areturn
LineNumberTable:
line 1: 0
line 2: 4
}
SourceFile: "CCIEntry.java"
BootstrapMethods:
0: #45 REF_invokeStatic java/lang/invoke/ValueBootstrapMethods.makeBootstrapMethod:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
InnerClasses:
public static final #57= #53 of #55; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
The entries that need to be fixed are:
The class name itself, CCIEntry is an inline type, so the CONSTANT_Class_info entry at index 1 should use a Q-envelope, which means the Utf8 at index 2 should be "QCCIEntry;".
The field reference at index 3 points to a field of an inline type, so its symbolic reference to a class should use a Q-envelope too. Same fix as above.
The method references at index 7 and 11 points to methods of an inline type, so their symbolic reference to a class should use a Q-envelope too. Same fix as above.
In method isCCIEntry(java.lang.Object), the instanceof bytecode should point to a CONSTANT_Class_info entry referencing type "QCCIEntry;" (Fixing entry 1 would also fix this one).
In method getCCIEntryOrThrow(java.lang.Object), the bytecode checkcast already correctly points to a CONSTANT_Class_info entry referencing type ""QCCIEntry;" (but the entry can now be shared will all the other above).