问题
I ran into an issue while manipulating some bytecode, where a certain final
String
constant was not inlined by the java compiler (Java 8), see the example below:
public class MyTest
{
private static final String ENABLED = "Y";
private static final String DISABLED = "N";
private static boolean isEnabled(String key) {
return key.equals("A");
}
private static String getString(String key, String value) {
return key + value;
}
public static void main(String[] args) throws Exception {
String flag = getString("F", isEnabled("A") ? ENABLED : DISABLED);
System.out.println(flag);
String flag2 = getString("F", isEnabled("A") ? ENABLED : DISABLED);
System.out.println(flag2);
}
}
Resulting bytecode with javac (1.8.0_101)
public static void main(java.lang.String[]) throws java.lang.Exception;
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: ldc #8 // String F
2: ldc #2 // String A
4: invokestatic #9 // Method isEnabled:(Ljava/lang/String;)Z
7: ifeq 16
10: getstatic #10 // Field ENABLED:Ljava/lang/String;
13: goto 19
16: getstatic #11 // Field DISABLED:Ljava/lang/String;
19: invokestatic #12 // Method getString:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
22: astore_1
23: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_1
27: invokevirtual #14 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: ldc #8 // String F
32: ldc #2 // String A
34: invokestatic #9 // Method isEnabled:(Ljava/lang/String;)Z
37: ifeq 46
40: getstatic #10 // Field ENABLED:Ljava/lang/String;
43: goto 49
46: getstatic #11 // Field DISABLED:Ljava/lang/String;
49: invokestatic #12 // Method getString:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
52: astore_2
53: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
56: aload_2
57: invokevirtual #14 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
60: return
You can see that the second time the fields ENABLED
and DISABLED
are being accessed, the compiler did not inline their values (using ldc
), but instead used getstatic
to access the field directly. Testing it with other compilers (Java 7, Eclipse) did not trigger the same behavior and the constants were always inlined.
Can this be considered a compiler bug, or is it allowed to not inline string constants all the time according to the JLS?
回答1:
Yes, the “inlining” behavior is mandated by the specification:
13.1. The Form of a Binary
…
A reference to a field that is a constant variable (§4.12.4) must be resolved at compile time to the value
V
denoted by the constant variable's initializer.If such a field is
static
, then no reference to the field should be present in the code in a binary file, including the class or interface which declared the field. Such a field must always appear to have been initialized (§12.4.2); the default initial value for the field (if different thanV
) must never be observed.If such a field is non-
static
, then no reference to the field should be present in the code in a binary file, except in the class containing the field. (It will be a class rather than an interface, since an interface has onlystatic
fields.) The class should have code to set the field's value toV
during instance creation (§12.5).
Note, how this precisely addresses you scenario: “If such a field is static
, then no reference to the field should be present in the code in a binary file, including the class or interface which declared the field”.
In other words, if you encounter a compiler not adhering to this, you found a compiler bug.
As an addendum, the starting point for finding this information was:
4.12.4. final Variables
…
A constant variable is a
final
variable of primitive type or typeString
that is initialized with a constant expression (§15.28). Whether a variable is a constant variable or not may have implications with respect to class initialization (§12.4.1), binary compatibility (§13.1, §13.4.9), and definite assignment (§16 (Definite Assignment)).
来源:https://stackoverflow.com/questions/39386014/does-the-jls-require-inlining-of-final-string-constants