Skip to content

Commit 6c74c56

Browse files
committed
Refactor constant parsing
It is better to handle the conversion in the context where conversion is expected.
1 parent 3c91893 commit 6c74c56

File tree

1 file changed

+36
-23
lines changed

1 file changed

+36
-23
lines changed

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

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,8 @@ class ClassfileParser(
235235
getOwner(jflags), preName.name, sflags, completer,
236236
getPrivateWithin(jflags), coord = start)
237237

238-
val pt = if sig.size == 1 then constantTagToType(sig(0)) else WildcardType
239238
val isVarargs = sflags.is(Flags.Method) && (jflags & JAVA_ACC_VARARGS) != 0
240-
completer.attrCompleter = parseAttributes(member, pt, isVarargs)
239+
completer.attrCompleter = parseAttributes(member, isVarargs)
241240

242241
getScope(jflags).enter(member)
243242

@@ -324,6 +323,21 @@ class ClassfileParser(
324323
case BOOL_TAG => defn.BooleanType
325324
}
326325

326+
/** As specified in https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.16.1,
327+
* an annotation argument of type boolean, byte, char or short will
328+
* be represented as a CONSTANT_INTEGER, so we need to convert it to
329+
* produce a correctly-typed tree. We need to do this each time the
330+
* constant is accessed instead of storing the result of the
331+
* conversion in the `values` cache, because the same constant might
332+
* be used for annotation arguments of different type.
333+
*/
334+
def convertTo(ct: Constant, pt: Type)(using Context): Constant = {
335+
if (pt eq defn.BooleanType) && ct.tag == IntTag then
336+
Constant(ct.value != 0)
337+
else
338+
ct.convertTo(pt)
339+
}
340+
327341
private def sigToType(sig: String, owner: Symbol = null, isVarargs: Boolean = false)(using Context): Type = {
328342
var index = 0
329343
val end = sig.length
@@ -525,7 +539,10 @@ class ClassfileParser(
525539
case STRING_TAG =>
526540
if (skip) None else Some(lit(Constant(pool.getName(index).value)))
527541
case BOOL_TAG | BYTE_TAG | CHAR_TAG | SHORT_TAG =>
528-
if (skip) None else Some(lit(pool.getConstant(index, constantTagToType(tag))))
542+
if (skip) None else {
543+
val constant = convertTo(pool.getConstant(index), constantTagToType(tag))
544+
Some(lit(constant))
545+
}
529546
case INT_TAG | LONG_TAG | FLOAT_TAG | DOUBLE_TAG =>
530547
if (skip) None else Some(lit(pool.getConstant(index)))
531548
case CLASS_TAG =>
@@ -612,17 +629,27 @@ class ClassfileParser(
612629
}
613630

614631
abstract class AttributeCompleter {
632+
var constant: Constant = null
615633
var exceptions: List[NameOrString] = Nil
616634
def complete(tp: Type)(using Context): Type
617635
}
618636

619637
// invariant: `in` and `ctx` should not be captured inside the result function
620-
def parseAttributes(sym: Symbol, pt: Type = WildcardType, isVarargs: Boolean = false)(using ctx: Context, in: DataReader): AttributeCompleter = {
638+
def parseAttributes(sym: Symbol, isVarargs: Boolean = false)(using ctx: Context, in: DataReader): AttributeCompleter = {
621639
var typeUpdate: Option[Context ?=> Type] = None
622640
var delayedWork: List[Context ?=> Unit] = Nil
623641
val res = new AttributeCompleter {
624642
def complete(tp: Type)(using Context): Type = {
625-
val newType = if (typeUpdate.isEmpty) tp else typeUpdate.get
643+
val updatedType =
644+
if typeUpdate.isEmpty then tp
645+
else typeUpdate.get
646+
647+
val newType =
648+
if this.constant != null then
649+
val ct = convertTo(this.constant, updatedType)
650+
if ct != null then ConstantType(ct) else updatedType
651+
else updatedType
652+
626653
exceptions.foreach { ex =>
627654
val cls = getClassSymbol(ex.name)
628655
sym.addAnnotation(ThrowsAnnotation(cls.asClass))
@@ -658,8 +685,8 @@ class ClassfileParser(
658685
sym.addAnnotation(Annotation(defn.DeprecatedAnnot, msg, since))
659686

660687
case tpnme.ConstantValueATTR =>
661-
val c = pool.getConstant(in.nextChar, pt)
662-
if (c ne null) typeUpdate = Some { ConstantType(c) }
688+
val c = pool.getConstant(in.nextChar)
689+
if (c ne null) res.constant = c
663690
else report.warning(s"Invalid constant in attribute of ${sym.showLocated} while parsing ${classfile}")
664691

665692
case tpnme.AnnotationDefaultATTR =>
@@ -1183,7 +1210,7 @@ class ClassfileParser(
11831210
getClassSymbol(index)
11841211
}
11851212

1186-
def getConstant(index: Int, pt: Type = WildcardType)(using ctx: Context, in: DataReader): Constant = {
1213+
def getConstant(index: Int)(using ctx: Context, in: DataReader): Constant = {
11871214
if (index <= 0 || len <= index) errorBadIndex(index)
11881215
var value = values(index)
11891216
if (value eq null) {
@@ -1207,21 +1234,7 @@ class ClassfileParser(
12071234
values(index) = value
12081235
}
12091236
value match {
1210-
case ct: Constant =>
1211-
if pt ne WildcardType then
1212-
// As specified in https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.16.1,
1213-
// an annotation argument of type boolean, byte, char or short will
1214-
// be represented as a CONSTANT_INTEGER, so we need to convert it to
1215-
// produce a correctly-typed tree. We need to do this each time the
1216-
// constant is accessed instead of storing the result of the
1217-
// conversion in the `values` cache, because the same constant might
1218-
// be used for annotation arguments of different type.
1219-
if (pt eq defn.BooleanType) && ct.tag == IntTag then
1220-
Constant(ct.value != 0)
1221-
else
1222-
ct.convertTo(pt)
1223-
else
1224-
ct
1237+
case ct: Constant => ct
12251238
case cls: Symbol => Constant(cls.typeRef)
12261239
case arr: Type => Constant(arr)
12271240
}

0 commit comments

Comments
 (0)