JDK-8165750 : Constant variable (type string) still referenced in code, violating JLS 13.1
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 8u101
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • Submitted: 2016-09-08
  • Updated: 2016-09-12
  • Resolved: 2016-09-12
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_101"
Java(TM) SE Runtime Environment (build 1.8.0_101-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Linux paloalto 4.4.0-31-generic #50-Ubuntu SMP Wed Jul 13 00:07:12 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

A DESCRIPTION OF THE PROBLEM :
In rare cases, constant variables are not resolved at compile time (as mandated by JLS 13.1 bullet point 3) and a reference to the field is still present in the code.

The behavior seems to be triggered by a ternary operator and could not be observed in other cases.


REGRESSION.  Last worked in version 7u80

ADDITIONAL REGRESSION INFORMATION: 
I did not perform full regression testing, only confirmed that the problem does not occur with a Java 7 compiler (1.7.0_80).

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile the attached source code with javac.
Use javap to inspect the resulting byte code.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The fields ENABLED and DISABLED should not be referenced in the code using getstatic instructions, but their constant value should have been resolved at compile time and used in code via ldc instructions.

ACTUAL -
Result of javap. You can see that the fields are still referenced with getstatic instructions, although they have a ConstantValue attribute.

javap -p -v -cp . MyTest
Classfile xxx/MyTest.class
  Last modified Sep 8, 2016; size 1142 bytes
  MD5 checksum 2b2abc16281f5f368a40eb0c6e7d550f
  Compiled from "MyTest.java"
public class MyTest
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #16.#40        // java/lang/Object."<init>":()V
   #2 = String             #41            // A
   #3 = Methodref          #42.#43        // java/lang/String.equals:(Ljava/lang/Object;)Z
   #4 = Class              #44            // java/lang/StringBuilder
   #5 = Methodref          #4.#40         // java/lang/StringBuilder."<init>":()V
   #6 = Methodref          #4.#45         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #7 = Methodref          #4.#46         // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #8 = String             #47            // F
   #9 = Methodref          #15.#48        // MyTest.isEnabled:(Ljava/lang/String;)Z
  #10 = Fieldref           #15.#49        // MyTest.ENABLED:Ljava/lang/String;
  #11 = Fieldref           #15.#50        // MyTest.DISABLED:Ljava/lang/String;
  #12 = Methodref          #15.#51        // MyTest.getString:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
  #13 = Fieldref           #52.#53        // java/lang/System.out:Ljava/io/PrintStream;
  #14 = Methodref          #54.#55        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #15 = Class              #56            // MyTest
  #16 = Class              #57            // java/lang/Object
  #17 = Utf8               ENABLED
  #18 = Utf8               Ljava/lang/String;
  #19 = Utf8               ConstantValue
  #20 = String             #58            // Y
  #21 = Utf8               DISABLED
  #22 = String             #59            // N
  #23 = Utf8               <init>
  #24 = Utf8               ()V
  #25 = Utf8               Code
  #26 = Utf8               LineNumberTable
  #27 = Utf8               isEnabled
  #28 = Utf8               (Ljava/lang/String;)Z
  #29 = Utf8               StackMapTable
  #30 = Utf8               getString
  #31 = Utf8               (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
  #32 = Utf8               main
  #33 = Utf8               ([Ljava/lang/String;)V
  #34 = Class              #60            // java/lang/String
  #35 = Class              #61            // "[Ljava/lang/String;"
  #36 = Utf8               Exceptions
  #37 = Class              #62            // java/lang/Exception
  #38 = Utf8               SourceFile
  #39 = Utf8               MyTest.java
  #40 = NameAndType        #23:#24        // "<init>":()V
  #41 = Utf8               A
  #42 = Class              #60            // java/lang/String
  #43 = NameAndType        #63:#64        // equals:(Ljava/lang/Object;)Z
  #44 = Utf8               java/lang/StringBuilder
  #45 = NameAndType        #65:#66        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #46 = NameAndType        #67:#68        // toString:()Ljava/lang/String;
  #47 = Utf8               F
  #48 = NameAndType        #27:#28        // isEnabled:(Ljava/lang/String;)Z
  #49 = NameAndType        #17:#18        // ENABLED:Ljava/lang/String;
  #50 = NameAndType        #21:#18        // DISABLED:Ljava/lang/String;
  #51 = NameAndType        #30:#31        // getString:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
  #52 = Class              #69            // java/lang/System
  #53 = NameAndType        #70:#71        // out:Ljava/io/PrintStream;
  #54 = Class              #72            // java/io/PrintStream
  #55 = NameAndType        #73:#74        // println:(Ljava/lang/String;)V
  #56 = Utf8               MyTest
  #57 = Utf8               java/lang/Object
  #58 = Utf8               Y
  #59 = Utf8               N
  #60 = Utf8               java/lang/String
  #61 = Utf8               [Ljava/lang/String;
  #62 = Utf8               java/lang/Exception
  #63 = Utf8               equals
  #64 = Utf8               (Ljava/lang/Object;)Z
  #65 = Utf8               append
  #66 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #67 = Utf8               toString
  #68 = Utf8               ()Ljava/lang/String;
  #69 = Utf8               java/lang/System
  #70 = Utf8               out
  #71 = Utf8               Ljava/io/PrintStream;
  #72 = Utf8               java/io/PrintStream
  #73 = Utf8               println
  #74 = Utf8               (Ljava/lang/String;)V
{
  private static final java.lang.String ENABLED;
    descriptor: Ljava/lang/String;
    flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL
    ConstantValue: String Y

  private static final java.lang.String DISABLED;
    descriptor: Ljava/lang/String;
    flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL
    ConstantValue: String N

  public MyTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 2: 0

  private static boolean isEnabled(java.lang.String);
    descriptor: (Ljava/lang/String;)Z
    flags: ACC_PRIVATE, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: ldc           #2                  // String A
         3: invokevirtual #3                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
         6: ifeq          11
         9: iconst_1
        10: ireturn
        11: iconst_0
        12: ireturn
      LineNumberTable:
        line 9: 0
        line 10: 11
      StackMapTable: number_of_entries = 1
        frame_type = 11 /* same */

  private static java.lang.String getString(java.lang.String, java.lang.String);
    descriptor: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
    flags: ACC_PRIVATE, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=2
         0: new           #4                  // class java/lang/StringBuilder
         3: dup
         4: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
         7: aload_0
         8: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        11: aload_1
        12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        15: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        18: areturn
      LineNumberTable:
        line 15: 0

  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=2, 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: return
      LineNumberTable:
        line 20: 0
        line 21: 23
        line 25: 30
      StackMapTable: number_of_entries = 2
        frame_type = 80 /* same_locals_1_stack_item */
          stack = [ class java/lang/String ]
        frame_type = 255 /* full_frame */
          offset_delta = 2
          locals = [ class "[Ljava/lang/String;" ]
          stack = [ class java/lang/String, class java/lang/String ]
    Exceptions:
      throws java.lang.Exception
}
SourceFile: "MyTest.java"


REPRODUCIBILITY :
This bug can be reproduced rarely.

---------- BEGIN SOURCE ----------
public class MyTest
{
    private static final String ENABLED = "Y";
    private static final String DISABLED = "N";

    private static boolean isEnabled(String key)
    {
        if (key.equals("A")) return true;
        else return false;
    }

    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);
    }

}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Workaround seems to be to store the result of the ternary operator into a temporary variable.


Comments
After discussing this with Maurizio, we determined that this was fixed by the fix to JDK-8066871. This bug is present in 8u101 but was fixed in 8u102 by a backport, so this explains why the submitter observed it and why it isn't reproducible in 8u102. Reopening and closing as a duplicate.
12-09-2016

This issue is not reproducible on 8u102 8u112 ea build, below is the result 7u80 - Pass 8u91 - Fail 8u101 - Fail 8u102 - Pass 8u112 - Pass 9 ea b-131- Pass Got fixed in 8u102 and not reproducible on current release.Hence closing as cannot reproduce
09-09-2016