JDK-8227617 : Inconsistent handling of short between AnnotationValue.toString and Elements.getConstantExpression
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2019-07-12
  • Updated: 2019-08-20
Related Reports
Relates :  
Relates :  
Description
The handling of byte in AnnotationValue.toString [1] and Elements.getConstantExpression [2] is consistent, it is always represented as `String.format("(byte)0x%02x", b)`.

On the other hand, `short` is represented as `String.format("(short)%d", s)` in AnnotationValue.toString, and `String.valueOf(s)` in Elements.getConstantExpression.

This is allowed by the spec, which says getConstantExpression returns text "in a form suitable for representing the value in source code", while AnnotationValue.toString returns text "form suitable for representing this value *in the source code of an annotation*". It's safe to omit the casts for shorts and bytes that appear as constants inside annotations.

Nevertheless, the different handling of byte and short seems slightly inconsistent and surprising. Would it makes to always add the cast to `(short)` for consistency?

[1] https://docs.oracle.com/en/java/javase/11/docs/api/java.compiler/javax/lang/model/element/AnnotationValue.html#toString()
[2] https://docs.oracle.com/en/java/javase/11/docs/api/java.compiler/javax/lang/model/util/Elements.html#getConstantExpression(java.lang.Object)

Repro:

```
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;

@interface A {
  short s();
}

@interface B {
  byte b();
}

@A(s = 1)
class One {}

@B(b = 2)
class Two {}

@SupportedAnnotationTypes("A")
public class P extends AbstractProcessor {

  @Override
  public SourceVersion getSupportedSourceVersion() {
    return SourceVersion.latestSupported();
  }

  @Override
  public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    if (roundEnv.processingOver()) {
      return false;
    }
    Element e = roundEnv.getElementsAnnotatedWith(A.class).iterator().next();
    System.err.println(
        "AnnotationValue short = "
            + e.getAnnotationMirrors().get(0).getElementValues().values().iterator().next());

    e = roundEnv.getElementsAnnotatedWith(B.class).iterator().next();
    System.err.println(
        "AnnotationValue byte = "
            + e.getAnnotationMirrors().get(0).getElementValues().values().iterator().next());

    Elements elements = processingEnv.getElementUtils();
    System.err.println("getConstantExpression short = " + elements.getConstantExpression((short) 1));
    System.err.println("getConstantExpression byte = " + elements.getConstantExpression((byte) 2));

    return false;
  }
}
```

$ javac -processor P -fullversion P.java 
javac full version "14-ea+5-129"
AnnotationValue short = 1
AnnotationValue byte = (byte)0x02
getConstantExpression short = (short)1
getConstantExpression byte = (byte)0x02


The patch to treat short consistently is something like:

```
diff --git a/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java b/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java
--- a/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java
+++ b/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java
@@ -186,6 +186,8 @@ class AnnotationInvocationHandler implem
                 return  toSourceString((long) value);
             else if (type == Byte.class)
                 return  toSourceString((byte) value);
+            else if (type == Short.class)
+                return  toSourceString((short) value);
             else
                 return value.toString();
         } else {
@@ -300,6 +302,10 @@ class AnnotationInvocationHandler implem
         return String.format("(byte)0x%02x", b);
     }
 
+    private static String toSourceString(short s) {
+        return String.format("(short)%d", s);
+    }
+
     private static String toSourceString(long ell) {
         return String.valueOf(ell) + "L";
     }
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Constants.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Constants.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Constants.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Constants.java
@@ -68,6 +68,7 @@ public class Constants {
         case FLOAT:     return formatFloat((Float) value);
         case DOUBLE:    return formatDouble((Double) value);
         case CHAR:      return formatChar((Character) value);
+        case SHORT:     return formatShort((Short) value);
         }
         if (value instanceof String)
             return formatString((String) value);
```
Comments
Attn: [~darcy]
12-07-2019