@@ -50,11 +50,6 @@ object ClassfileParser {
50
50
mapOver(tp)
51
51
}
52
52
}
53
-
54
- def withReader [T ](classfile : AbstractFile )(op : DataReader ?=> T )(using Context ): T =
55
- ctx.base.reusableDataReader.using { reader =>
56
- op(using reader.reset(classfile))
57
- }
58
53
}
59
54
60
55
class ClassfileParser (
@@ -86,11 +81,14 @@ class ClassfileParser(
86
81
private def mismatchError (className : SimpleName ) =
87
82
throw new IOException (s " class file ' ${classfile.canonicalPath}' has location not matching its contents: contains class $className" )
88
83
89
- def run ()(using Context ): Option [Embedded ] = try withReader(classfile) { (using in) =>
84
+ def run ()(using Context ): Option [Embedded ] = try ctx.base.reusableDataReader.withInstance { reader =>
85
+ implicit val reader2 = reader.reset(classfile)
90
86
report.debuglog(" [class] >> " + classRoot.fullName)
91
87
parseHeader()
92
88
this .pool = new ConstantPool
93
- parseClass()
89
+ val res = parseClass()
90
+ this .pool = null
91
+ res
94
92
}
95
93
catch {
96
94
case e : RuntimeException =>
@@ -114,7 +112,7 @@ class ClassfileParser(
114
112
}
115
113
116
114
/** Return the class symbol of the given name. */
117
- def classNameToSymbol (name : Name )(using Context , DataReader ): Symbol = innerClasses.get(name) match {
115
+ def classNameToSymbol (name : Name )(using Context ): Symbol = innerClasses.get(name.toString ) match {
118
116
case Some (entry) => innerClasses.classSymbol(entry)
119
117
case None => requiredClass(name)
120
118
}
@@ -191,7 +189,7 @@ class ClassfileParser(
191
189
192
190
setClassInfo(moduleRoot, staticInfo, fromScala2 = false )
193
191
194
- classInfo = parseAttributes(classRoot.symbol, classInfo)
192
+ classInfo = parseAttributes(classRoot.symbol).complete( classInfo)
195
193
if (isAnnotation)
196
194
// classInfo must be a TempClassInfoType and not a TempPolyType,
197
195
// because Java annotations cannot have type parameters.
@@ -227,32 +225,37 @@ class ClassfileParser(
227
225
val sflags =
228
226
if (method) Flags .Method | methodTranslation.flags(jflags)
229
227
else fieldTranslation.flags(jflags)
230
- val name = pool.getName(in.nextChar)
231
- if (! sflags.isOneOf(Flags .PrivateOrArtifact ) || name.name == nme.CONSTRUCTOR ) {
228
+ val preName = pool.getName(in.nextChar)
229
+ if (! sflags.isOneOf(Flags .PrivateOrArtifact ) || preName.name == nme.CONSTRUCTOR ) {
230
+ val sig = pool.getExternalName(in.nextChar).value
231
+ val completer = MemberCompleter (preName.name, jflags, sig)
232
232
val member = newSymbol(
233
- getOwner(jflags), name .name, sflags, memberCompleter ,
233
+ getOwner(jflags), preName .name, sflags, completer ,
234
234
getPrivateWithin(jflags), coord = start)
235
+
236
+ completer.attrCompleter = parseAttributes(member)
237
+
235
238
getScope(jflags).enter(member)
239
+
240
+ }
241
+ else {
242
+ in.nextChar // info
243
+ skipAttributes()
236
244
}
237
- // skip rest of member for now
238
- in.nextChar // info
239
- skipAttributes()
240
245
}
241
246
242
- val memberCompleter : LazyType = new LazyType {
247
+ class MemberCompleter (name : SimpleName , jflags : Int , sig : String ) extends LazyType {
248
+ var attrCompleter : AttributeCompleter = null
243
249
244
- def complete (denot : SymDenotation )(using Context ): Unit = withReader(classfile) { (using in) =>
245
- in.bp = denot.symbol.coord.toIndex
250
+ def complete (denot : SymDenotation )(using Context ): Unit = {
246
251
val sym = denot.symbol
247
- val jflags = in.nextChar
248
252
val isEnum = (jflags & JAVA_ACC_ENUM ) != 0
249
- val name = pool.getName(in.nextChar).name
250
253
val isConstructor = name eq nme.CONSTRUCTOR
251
254
252
255
/** Strip leading outer param from constructor and trailing access tag for
253
256
* private inner constructors.
254
257
*/
255
- def normalizeConstructorParams () = innerClasses.get(currentClassName) match {
258
+ def normalizeConstructorParams () = innerClasses.get(currentClassName.toString ) match {
256
259
case Some (entry) if ! isStatic(entry.jflags) =>
257
260
val mt @ MethodTpe (paramNames, paramTypes, resultType) = denot.info
258
261
var normalizedParamNames = paramNames.tail
@@ -283,9 +286,9 @@ class ClassfileParser(
283
286
}
284
287
285
288
val isVarargs = denot.is(Flags .Method ) && (jflags & JAVA_ACC_VARARGS ) != 0
286
- denot.info = pool.getType(in.nextChar, isVarargs)
289
+ denot.info = sigToType(sig, isVarargs = isVarargs)
287
290
if (isConstructor) normalizeConstructorParams()
288
- denot.info = translateTempPoly(parseAttributes(sym, denot.info, isVarargs))
291
+ denot.info = translateTempPoly(attrCompleter.complete( denot.info, isVarargs))
289
292
if (isConstructor) normalizeConstructorInfo()
290
293
291
294
if (ctx.explicitNulls) denot.info = JavaNullInterop .nullifyMember(denot.symbol, denot.info, isEnum)
@@ -317,7 +320,22 @@ class ClassfileParser(
317
320
case BOOL_TAG => defn.BooleanType
318
321
}
319
322
320
- private def sigToType (sig : String , owner : Symbol = null , isVarargs : Boolean = false )(using Context , DataReader ): Type = {
323
+ /** As specified in https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.16.1,
324
+ * an annotation argument of type boolean, byte, char or short will
325
+ * be represented as a CONSTANT_INTEGER, so we need to convert it to
326
+ * produce a correctly-typed tree. We need to do this each time the
327
+ * constant is accessed instead of storing the result of the
328
+ * conversion in the `values` cache, because the same constant might
329
+ * be used for annotation arguments of different type.
330
+ */
331
+ def convertTo (ct : Constant , pt : Type )(using Context ): Constant = {
332
+ if (pt eq defn.BooleanType ) && ct.tag == IntTag then
333
+ Constant (ct.value != 0 )
334
+ else
335
+ ct.convertTo(pt)
336
+ }
337
+
338
+ private def sigToType (sig : String , owner : Symbol = null , isVarargs : Boolean = false )(using Context ): Type = {
321
339
var index = 0
322
340
val end = sig.length
323
341
def accept (ch : Char ): Unit = {
@@ -518,7 +536,10 @@ class ClassfileParser(
518
536
case STRING_TAG =>
519
537
if (skip) None else Some (lit(Constant (pool.getName(index).value)))
520
538
case BOOL_TAG | BYTE_TAG | CHAR_TAG | SHORT_TAG =>
521
- if (skip) None else Some (lit(pool.getConstant(index, constantTagToType(tag))))
539
+ if (skip) None else {
540
+ val constant = convertTo(pool.getConstant(index), constantTagToType(tag))
541
+ Some (lit(constant))
542
+ }
522
543
case INT_TAG | LONG_TAG | FLOAT_TAG | DOUBLE_TAG =>
523
544
if (skip) None else Some (lit(pool.getConstant(index)))
524
545
case CLASS_TAG =>
@@ -604,8 +625,48 @@ class ClassfileParser(
604
625
None // ignore malformed annotations
605
626
}
606
627
607
- def parseAttributes (sym : Symbol , symtype : Type , isVarargs : Boolean = false )(using ctx : Context , in : DataReader ): Type = {
608
- var newType = symtype
628
+ /** A completer for attributes
629
+ *
630
+ * Top-level classes complete attributes eagerly, while members complete lazily.
631
+ *
632
+ * @note We cannot simply store the bytes of attributes, as the bytes may
633
+ * contain references to the constant pool, where the constants are loaded
634
+ * lazily.
635
+ */
636
+ class AttributeCompleter (sym : Symbol ) {
637
+ var sig : String = null
638
+ var constant : Constant = null
639
+ var exceptions : List [NameOrString ] = Nil
640
+ var annotations : List [Annotation ] = Nil
641
+ def complete (tp : Type , isVarargs : Boolean = false )(using Context ): Type = {
642
+ val updatedType =
643
+ if sig == null then tp
644
+ else {
645
+ val newType = sigToType(sig, sym, isVarargs)
646
+ if (ctx.debug && ctx.verbose)
647
+ println(" " + sym + " ; signature = " + sig + " type = " + newType)
648
+ newType
649
+ }
650
+
651
+ val newType =
652
+ if this .constant != null then
653
+ val ct = convertTo(this .constant, updatedType)
654
+ if ct != null then ConstantType (ct) else updatedType
655
+ else updatedType
656
+
657
+ annotations.foreach(annot => sym.addAnnotation(annot))
658
+
659
+ exceptions.foreach { ex =>
660
+ val cls = getClassSymbol(ex.name)
661
+ sym.addAnnotation(ThrowsAnnotation (cls.asClass))
662
+ }
663
+
664
+ cook.apply(newType)
665
+ }
666
+ }
667
+
668
+ def parseAttributes (sym : Symbol )(using ctx : Context , in : DataReader ): AttributeCompleter = {
669
+ val res = new AttributeCompleter (sym)
609
670
610
671
def parseAttribute (): Unit = {
611
672
val attrName = pool.getName(in.nextChar).name.toTypeName
@@ -614,9 +675,7 @@ class ClassfileParser(
614
675
attrName match {
615
676
case tpnme.SignatureATTR =>
616
677
val sig = pool.getExternalName(in.nextChar)
617
- newType = sigToType(sig.value, sym, isVarargs)
618
- if (ctx.debug && ctx.verbose)
619
- println(" " + sym + " ; signature = " + sig + " type = " + newType)
678
+ res.sig = sig.value
620
679
621
680
case tpnme.SyntheticATTR =>
622
681
sym.setFlag(Flags .SyntheticArtifact )
@@ -627,11 +686,13 @@ class ClassfileParser(
627
686
case tpnme.DeprecatedATTR =>
628
687
val msg = Literal (Constant (" see corresponding Javadoc for more information." ))
629
688
val since = Literal (Constant (" " ))
630
- sym.addAnnotation(Annotation (defn.DeprecatedAnnot , msg, since))
689
+ res.annotations ::= Annotation .deferredSymAndTree(defn.DeprecatedAnnot ) {
690
+ New (defn.DeprecatedAnnot .typeRef, msg :: since :: Nil )
691
+ }
631
692
632
693
case tpnme.ConstantValueATTR =>
633
- val c = pool.getConstant(in.nextChar, symtype )
634
- if (c ne null ) newType = ConstantType (c)
694
+ val c = pool.getConstant(in.nextChar)
695
+ if (c ne null ) res.constant = c
635
696
else report.warning(s " Invalid constant in attribute of ${sym.showLocated} while parsing ${classfile}" )
636
697
637
698
case tpnme.AnnotationDefaultATTR =>
@@ -652,12 +713,13 @@ class ClassfileParser(
652
713
parseExceptions(attrLen)
653
714
654
715
case tpnme.CodeATTR =>
655
- if (sym.owner.isAllOf(Flags .JavaInterface )) {
716
+ in.skip(attrLen)
717
+ // flag test will trigger completion and cycles, thus have to be lazy
718
+ if (sym.owner.flagsUNSAFE.isAllOf(Flags .JavaInterface )) {
656
719
sym.resetFlag(Flags .Deferred )
657
720
sym.owner.resetFlag(Flags .PureInterface )
658
- report.log(s " $sym in ${sym.owner} is a java8 + default method. " )
721
+ report.log(s " $sym in ${sym.owner} is a java 8 + default method. " )
659
722
}
660
- in.skip(attrLen)
661
723
662
724
case _ =>
663
725
}
@@ -672,11 +734,12 @@ class ClassfileParser(
672
734
val nClasses = in.nextChar
673
735
for (n <- 0 until nClasses) {
674
736
// FIXME: this performs an equivalent of getExceptionTypes instead of getGenericExceptionTypes (SI-7065)
675
- val cls = pool.getClassSymbol (in.nextChar.toInt)
676
- sym.addAnnotation( ThrowsAnnotation ( cls.asClass))
737
+ val cls = pool.getClassName (in.nextChar.toInt)
738
+ res.exceptions ::= cls
677
739
}
678
740
}
679
741
742
+
680
743
/** Parse a sequence of annotations and attaches them to the
681
744
* current symbol sym, except for the ScalaSignature annotation that it returns, if it is available. */
682
745
def parseAnnotations (len : Int ): Unit = {
@@ -693,7 +756,7 @@ class ClassfileParser(
693
756
for (i <- 0 until in.nextChar)
694
757
parseAttribute()
695
758
696
- cook.apply(newType)
759
+ res
697
760
}
698
761
699
762
/** Annotations in Scala are assumed to get all their arguments as constructor
@@ -730,7 +793,7 @@ class ClassfileParser(
730
793
731
794
for entry <- innerClasses.valuesIterator do
732
795
// create a new class member for immediate inner classes
733
- if entry.outerName == currentClassName then
796
+ if entry.outer.name == currentClassName then
734
797
val file = ctx.platform.classPath.findClassFile(entry.externalName.toString) getOrElse {
735
798
throw new AssertionError (entry.externalName)
736
799
}
@@ -911,8 +974,11 @@ class ClassfileParser(
911
974
val nameIndex = in.nextChar
912
975
val jflags = in.nextChar
913
976
if (innerIndex != 0 && outerIndex != 0 && nameIndex != 0 ) {
914
- val entry = InnerClassEntry (innerIndex, outerIndex, nameIndex, jflags)
915
- innerClasses(pool.getClassName(innerIndex).name) = entry
977
+ val inner = pool.getClassName(innerIndex)
978
+ val outer = pool.getClassName(outerIndex)
979
+ val name = pool.getName(nameIndex)
980
+ val entry = InnerClassEntry (inner, outer, name, jflags)
981
+ innerClasses(inner.value) = entry
916
982
}
917
983
}
918
984
}
@@ -922,20 +988,20 @@ class ClassfileParser(
922
988
}
923
989
924
990
/** An entry in the InnerClasses attribute of this class file. */
925
- private case class InnerClassEntry (external : Int , outer : Int , name : Int , jflags : Int ) {
926
- def externalName ( using DataReader ) : SimpleName = pool.getClassName( external).name
927
- def outerName ( using DataReader ) : SimpleName = pool.getClassName( outer).name
928
- def originalName ( using DataReader ) : SimpleName = pool.getName( name) .name
991
+ case class InnerClassEntry (external : NameOrString , outer : NameOrString , name : NameOrString , jflags : Int ) {
992
+ def externalName = external.value
993
+ def outerName = outer.value
994
+ def originalName = name.name
929
995
930
- def show ( using DataReader ) : String =
931
- s " $originalName in $outerName ( $externalName ) "
996
+ // The name of the outer class, without its trailing $ if it has one.
997
+ def strippedOuter = outer.name.stripModuleClassSuffix
932
998
}
933
999
934
- private object innerClasses extends util.HashMap [Name , InnerClassEntry ] {
1000
+ private object innerClasses extends util.HashMap [String , InnerClassEntry ] {
935
1001
/** Return the Symbol of the top level class enclosing `name`,
936
1002
* or 'name's symbol if no entry found for `name`.
937
1003
*/
938
- def topLevelClass (name : Name )(using Context , DataReader ): Symbol = {
1004
+ def topLevelClass (name : String )(using Context ): Symbol = {
939
1005
val tlName = if (contains(name)) {
940
1006
var entry = this (name)
941
1007
while (contains(entry.outerName))
@@ -944,13 +1010,13 @@ class ClassfileParser(
944
1010
}
945
1011
else
946
1012
name
947
- classNameToSymbol(tlName)
1013
+ classNameToSymbol(tlName.toTypeName )
948
1014
}
949
1015
950
1016
/** Return the class symbol for `entry`. It looks it up in its outer class.
951
1017
* This might force outer class symbols.
952
1018
*/
953
- def classSymbol (entry : InnerClassEntry )(using Context , DataReader ): Symbol = {
1019
+ def classSymbol (entry : InnerClassEntry )(using Context ): Symbol = {
954
1020
def getMember (sym : Symbol , name : Name )(using Context ): Symbol =
955
1021
if (isStatic(entry.jflags))
956
1022
if (sym == classRoot.symbol)
@@ -966,7 +1032,7 @@ class ClassfileParser(
966
1032
else
967
1033
sym.info.member(name).symbol
968
1034
969
- val outerName = entry.outerName.stripModuleClassSuffix
1035
+ val outerName = entry.strippedOuter
970
1036
val innerName = entry.originalName
971
1037
val owner = classNameToSymbol(outerName)
972
1038
val result = atPhase(typerPhase)(getMember(owner, innerName.toTypeName))
@@ -1025,7 +1091,7 @@ class ClassfileParser(
1025
1091
}
1026
1092
}
1027
1093
1028
- def getClassSymbol (name : SimpleName )(using Context , DataReader ): Symbol =
1094
+ def getClassSymbol (name : SimpleName )(using Context ): Symbol =
1029
1095
if (name.endsWith(" $" ) && (name ne nme.nothingRuntimeClass) && (name ne nme.nullRuntimeClass))
1030
1096
// Null$ and Nothing$ ARE classes
1031
1097
requiredModule(name.dropRight(1 ))
@@ -1148,7 +1214,7 @@ class ClassfileParser(
1148
1214
getClassSymbol(index)
1149
1215
}
1150
1216
1151
- def getConstant (index : Int , pt : Type = WildcardType )(using ctx : Context , in : DataReader ): Constant = {
1217
+ def getConstant (index : Int )(using ctx : Context , in : DataReader ): Constant = {
1152
1218
if (index <= 0 || len <= index) errorBadIndex(index)
1153
1219
var value = values(index)
1154
1220
if (value eq null ) {
@@ -1172,21 +1238,7 @@ class ClassfileParser(
1172
1238
values(index) = value
1173
1239
}
1174
1240
value match {
1175
- case ct : Constant =>
1176
- if pt ne WildcardType then
1177
- // As specified in https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.16.1,
1178
- // an annotation argument of type boolean, byte, char or short will
1179
- // be represented as a CONSTANT_INTEGER, so we need to convert it to
1180
- // produce a correctly-typed tree. We need to do this each time the
1181
- // constant is accessed instead of storing the result of the
1182
- // conversion in the `values` cache, because the same constant might
1183
- // be used for annotation arguments of different type.
1184
- if (pt eq defn.BooleanType ) && ct.tag == IntTag then
1185
- Constant (ct.value != 0 )
1186
- else
1187
- ct.convertTo(pt)
1188
- else
1189
- ct
1241
+ case ct : Constant => ct
1190
1242
case cls : Symbol => Constant (cls.typeRef)
1191
1243
case arr : Type => Constant (arr)
1192
1244
}
0 commit comments