Skip to content

Commit 0ccb16d

Browse files
committed
Type constant annotation arguments correctly
An annotation argument of type boolean, byte, char or short will be represented as a CONSTANT_Integer in the classfile, so back in 3b882c2 I made sure that we took this type into account when creating a `Constant`, but this is not enough: we store parsed constants in a `values` cache, and the same constant might be used with different expected types, so the conversion needs to be done on demand.
1 parent 3e888f2 commit 0ccb16d

File tree

4 files changed

+55
-36
lines changed

4 files changed

+55
-36
lines changed

compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,19 @@ class ClassfileParser(
314314
final def objToAny(tp: Type)(implicit ctx: Context): Type =
315315
if (tp.isDirectRef(defn.ObjectClass) && !ctx.phase.erasedTypes) defn.AnyType else tp
316316

317+
def constantTagToType(tag: Int)(using Context): Type =
318+
(tag: @switch) match {
319+
case BYTE_TAG => defn.ByteType
320+
case CHAR_TAG => defn.CharType
321+
case DOUBLE_TAG => defn.DoubleType
322+
case FLOAT_TAG => defn.FloatType
323+
case INT_TAG => defn.IntType
324+
case LONG_TAG => defn.LongType
325+
case SHORT_TAG => defn.ShortType
326+
case VOID_TAG => defn.UnitType
327+
case BOOL_TAG => defn.BooleanType
328+
}
329+
317330
private def sigToType(sig: SimpleName, owner: Symbol = null)(implicit ctx: Context): Type = {
318331
var index = 0
319332
val end = sig.length
@@ -331,15 +344,6 @@ class ClassfileParser(
331344
def sig2type(tparams: immutable.Map[Name, Symbol], skiptvs: Boolean)(implicit ctx: Context): Type = {
332345
val tag = sig(index); index += 1
333346
(tag: @switch) match {
334-
case BYTE_TAG => defn.ByteType
335-
case CHAR_TAG => defn.CharType
336-
case DOUBLE_TAG => defn.DoubleType
337-
case FLOAT_TAG => defn.FloatType
338-
case INT_TAG => defn.IntType
339-
case LONG_TAG => defn.LongType
340-
case SHORT_TAG => defn.ShortType
341-
case VOID_TAG => defn.UnitType
342-
case BOOL_TAG => defn.BooleanType
343347
case 'L' =>
344348
def processInner(tp: Type): Type = tp match {
345349
case tp: TypeRef if !tp.symbol.owner.is(Flags.ModuleClass) =>
@@ -417,6 +421,8 @@ class ClassfileParser(
417421
index += 1
418422
//assert(tparams contains n, s"classTparams = $classTParams, tparams = $tparams, key = $n")
419423
if (skiptvs) defn.AnyType else tparams(n).typeRef
424+
case tag =>
425+
constantTagToType(tag)
420426
}
421427
}
422428
// sig2type(tparams, skiptvs)
@@ -495,12 +501,11 @@ class ClassfileParser(
495501
val tag = in.nextByte.toChar
496502
val index = in.nextChar
497503

498-
499504
tag match {
500505
case STRING_TAG =>
501506
if (skip) None else Some(lit(Constant(pool.getName(index).toString)))
502507
case BOOL_TAG | BYTE_TAG | CHAR_TAG | SHORT_TAG =>
503-
if (skip) None else Some(lit(pool.getConstant(index, tag)))
508+
if (skip) None else Some(lit(pool.getConstant(index, constantTagToType(tag))))
504509
case INT_TAG | LONG_TAG | FLOAT_TAG | DOUBLE_TAG =>
505510
if (skip) None else Some(lit(pool.getConstant(index)))
506511
case CLASS_TAG =>
@@ -571,11 +576,6 @@ class ClassfileParser(
571576
}
572577

573578
def parseAttributes(sym: Symbol, symtype: Type)(implicit ctx: Context): Type = {
574-
def convertTo(c: Constant, pt: Type): Constant =
575-
if (pt == defn.BooleanType && c.tag == IntTag)
576-
Constant(c.value != 0)
577-
else
578-
c convertTo pt
579579
var newType = symtype
580580

581581
def parseAttribute(): Unit = {
@@ -597,10 +597,9 @@ class ClassfileParser(
597597
val since = Literal(Constant(""))
598598
sym.addAnnotation(Annotation(defn.DeprecatedAnnot, msg, since))
599599
case tpnme.ConstantValueATTR =>
600-
val c = pool.getConstant(in.nextChar)
601-
val c1 = convertTo(c, symtype)
602-
if (c1 ne null) newType = ConstantType(c1)
603-
else println("failure to convert " + c + " to " + symtype); //debug
600+
val c = pool.getConstant(in.nextChar, symtype)
601+
if (c ne null) newType = ConstantType(c)
602+
else ctx.warning(s"Invalid constant in attribute of ${sym.showLocated} while parsing ${classfile}")
604603
case tpnme.AnnotationDefaultATTR =>
605604
sym.addAnnotation(Annotation(defn.AnnotationDefaultAnnot, Nil))
606605
// Java annotations on classes / methods / fields with RetentionPolicy.RUNTIME
@@ -1113,28 +1112,14 @@ class ClassfileParser(
11131112
getClassSymbol(index)
11141113
}
11151114

1116-
def getConstant(index: Int, tag: Int = -1)(implicit ctx: Context): Constant = {
1115+
def getConstant(index: Int, pt: Type = WildcardType)(implicit ctx: Context): Constant = {
11171116
if (index <= 0 || len <= index) errorBadIndex(index)
11181117
var value = values(index)
11191118
if (value eq null) {
11201119
val start = starts(index)
11211120
value = (in.buf(start).toInt: @switch) match {
11221121
case CONSTANT_STRING =>
11231122
Constant(getName(in.getChar(start + 1).toInt).toString)
1124-
case CONSTANT_INTEGER if tag != -1 =>
1125-
val value = in.getInt(start + 1)
1126-
(tag: @switch) match {
1127-
case BOOL_TAG =>
1128-
Constant(value != 0)
1129-
case BYTE_TAG =>
1130-
Constant(value.toByte)
1131-
case CHAR_TAG =>
1132-
Constant(value.toChar)
1133-
case SHORT_TAG =>
1134-
Constant(value.toShort)
1135-
case _ =>
1136-
errorBadTag(tag)
1137-
}
11381123
case CONSTANT_INTEGER =>
11391124
Constant(in.getInt(start + 1))
11401125
case CONSTANT_FLOAT =>
@@ -1151,7 +1136,21 @@ class ClassfileParser(
11511136
values(index) = value
11521137
}
11531138
value match {
1154-
case ct: Constant => ct
1139+
case ct: Constant =>
1140+
if pt ne WildcardType then
1141+
// As specified in https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.16.1,
1142+
// an annotation argument of type boolean, byte, char or short will
1143+
// be represented as a CONSTANT_INTEGER, so we need to convert it to
1144+
// produce a correctly-typed tree. We need to do this each time the
1145+
// constant is accessed instead of storing the result of the
1146+
// conversion in the `values` cache, because the same constant might
1147+
// be used for annotation arguments of different type.
1148+
if (pt eq defn.BooleanType) && ct.tag == IntTag then
1149+
Constant(ct.value != 0)
1150+
else
1151+
ct.convertTo(pt)
1152+
else
1153+
ct
11551154
case cls: Symbol => Constant(cls.typeRef)
11561155
case arr: Type => Constant(arr)
11571156
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package pkg;
2+
3+
@interface Annot_1 {
4+
boolean BOOL();
5+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package pkg;
2+
3+
class Constants_1 {
4+
// javac will produce a ConstantValue 1 for the annotation argument
5+
@Annot_1(BOOL=true) static void foo() {};
6+
7+
// ...and reuse it here for the ConstantValue attribute
8+
static final byte BYTE = 1;
9+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package pkg
2+
3+
object U {
4+
println(Constants_1.foo()) // The same constant in the constant pool is first unpickled here as a boolean
5+
println(Constants_1.BYTE) // ... and here as a byte
6+
}

0 commit comments

Comments
 (0)