United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: 7181320 javac NullPointerException for switch labels with cast to String expressions
7181320 : javac NullPointerException for switch labels with cast to String expressions

Details
Type:
Bug
Submit Date:
2012-07-03
Status:
Closed
Updated Date:
2012-12-18
Project Name:
JDK
Resolved Date:
2012-09-05
Component:
tools
OS:
linux
Sub-Component:
javac
CPU:
x86
Priority:
P4
Resolution:
Fixed
Affected Versions:
7
Fixed Versions:
8

Related Reports
Backport:
Relates:
Relates:
Relates:

Sub Tasks

Description
FULL PRODUCT VERSION :
$ java -version
java version "1.7.0_05"
Java(TM) SE Runtime Environment (build 1.7.0_05-b05)
Java HotSpot(TM) 64-Bit Server VM (build 23.1-b03, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Linux <host> 2.6.18-92.el5 #1 SMP Tue Apr 29 13:16:15 EDT 2008 x86_64 x86_64 x86_64 GNU/Linux


A DESCRIPTION OF THE PROBLEM :
Compiling the source code included with this bug report results in a NullPointerException in the Java 1.7.0_05 compiler, at least on Linux x86_64.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Save the source code included with this bug report as a file named SwitchString.java and compile it with:
  javac SwitchString.java

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The provided source code should compile successfully.
ACTUAL -
The Java compiler aborts the compilation due to a NullPointerException.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
$ javac SwitchString.java
An exception has occurred in the compiler (1.7.0_05). Please file a bug at the Java Developer Connection (http://java.sun.com/webapps/bugreport)  after checking the Bug Parade for duplicates. Include your program and the following diagnostic in your report.  Thank you.
java.lang.NullPointerException
        at com.sun.tools.javac.comp.Lower.visitStringSwitch(Lower.java:3465)
        at com.sun.tools.javac.comp.Lower.visitSwitch(Lower.java:3365)
        at com.sun.tools.javac.tree.JCTree$JCSwitch.accept(JCTree.java:959)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2160)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70)
        at com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:160)
        at com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3319)
        at com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:781)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2160)
        at com.sun.tools.javac.tree.TreeTranslator.visitMethodDef(TreeTranslator.java:144)
        at com.sun.tools.javac.comp.Lower.visitMethodDefInternal(Lower.java:2627)
        at com.sun.tools.javac.comp.Lower.visitMethodDef(Lower.java:2546)
        at com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:669)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2160)
        at com.sun.tools.javac.comp.Lower.visitClassDef(Lower.java:2291)
        at com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:591)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2160)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2180)
        at com.sun.tools.javac.comp.Lower.translateTopLevelClass(Lower.java:3659)
        at com.sun.tools.javac.main.JavaCompiler.desugar(JavaCompiler.java:1395)
        at com.sun.tools.javac.main.JavaCompiler.desugar(JavaCompiler.java:1273)
        at com.sun.tools.javac.main.JavaCompiler.compile2(JavaCompiler.java:870)
        at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:829)
        at com.sun.tools.javac.main.Main.compile(Main.java:439)
        at com.sun.tools.javac.main.Main.compile(Main.java:353)
        at com.sun.tools.javac.main.Main.compile(Main.java:342)
        at com.sun.tools.javac.main.Main.compile(Main.java:333)
        at com.sun.tools.javac.Main.compile(Main.java:76)
        at com.sun.tools.javac.Main.main(Main.java:61)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
/*
 * Compiling this program with the 1.7.0_05 Java compiler, at least on Linux
 *  x86_64, causes the compiler to abort due to a NullPointerException.
 *
 * To reproduce the NPE, compile with:
 *  javac SwitchString.java
 *
 * This test case was constructed to test the use of strings in switch
 *  statements as provided by Java 7 language enhancements.  It was drafted
 *  based on the following clauses from JLS 7 (JSR-000901):
 *
 * 14.11 The switch Statement
 * ...
 * These labels are said to be associated with the switch statement, as are
 * the values of the constant expressions (15.28) or enum constants (8.9.1)
 * in the case labels.
 *
 * 15.28 Constant Expressions
 * ...
 * A compile-time constant expression is an expression denoting a value of
 * primitive type or a String that does not complete abruptly and is composed
 * using only the following:
 * ...
 * - Casts to primitive types and casts to type String (15.16)
 *
 * The cast to String in the case label below has no semantic relevance, but
 *  is permitted according to JLS 7 (JSR-000901)
 */

public class SwitchString {
    public static void main(String [] args) {
        switch (args[0]) {
            case (String)"text":
                break;
        }
    }
}

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

                                    

Comments
5 tests:http://hg.openjdk.java.net/jdk8/tl/langtools/file/tip/test/tools/javac/StringsInSwitch/7181320/
                                     
2012-12-18
EVALUATION

Constant value associated with a type is erased during type erasure.
                                     
2012-08-14
SUGGESTED FIX

When type erasure occurs in TransTypes, constant value associated with a Type (in this case reference Type String) is erased. For primitive types, constant value is preserved.

Hence the following code compiles fine:

public class Test {
   public void func(int i) {
      switch (i) { case (int)33: break }
   }
}

But, the constant value associated with String type is erased and so example in "Description" results in NPE. The following diff fixes the issue by restoring constant value after type erasure. Note that we restore type only when needed (to avoid having to use anon-class instance for type - see Type.constType).


diff -r 1d2db0e5eabc src/share/classes/com/sun/tools/javac/code/Types.java
--- a/src/share/classes/com/sun/tools/javac/code/Types.java	Fri Aug 10 10:14:48 2012 -0700
+++ b/src/share/classes/com/sun/tools/javac/code/Types.java	Tue Aug 14 10:38:35 2012 +0530
@@ -1589,7 +1589,13 @@
      * type parameters in t are deleted.
      */
     public Type erasure(Type t) {
-        return erasure(t, false);
+        Object constVal = t.constValue();
+        Type erasedType = erasure(t, false);
+        // Type erasure should not erase constant value
+        if (constVal != null && erasedType.constValue() == null) {
+            erasedType = erasedType.constType(constVal);
+        }
+        return erasedType;
     }
     //where
     private Type erasure(Type t, boolean recurse) {
                                     
2012-08-14
SUGGESTED FIX

Suspect the extraction of the labelExpr on line 3454 needs some additional code to look through an useless cast to get the real value:

3452                 if (expression != null) { // expression for a "default" case is null
3453                     expression = TreeInfo.skipParens(expression);
3454                     String labelExpr = (String) expression.type.constValue();
3455                     Integer mapping = caseLabelToPosition.put(labelExpr, casePosition);
3456                     Assert.checkNull(mapping);
                                     
2012-07-03



Hardware and Software, Engineered to Work Together