Skip to content

Commit 41c0fd3

Browse files
committed
Eagerly read attributes
1 parent 57ecd84 commit 41c0fd3

File tree

1 file changed

+65
-41
lines changed

1 file changed

+65
-41
lines changed

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

Lines changed: 65 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ class ClassfileParser(
114114
}
115115

116116
/** Return the class symbol of the given name. */
117-
def classNameToSymbol(name: Name)(using Context, DataReader): Symbol = innerClasses.get(name) match {
117+
def classNameToSymbol(name: Name)(using Context): Symbol = innerClasses.get(name.toString) match {
118118
case Some(entry) => innerClasses.classSymbol(entry)
119119
case None => requiredClass(name)
120120
}
@@ -191,7 +191,7 @@ class ClassfileParser(
191191

192192
setClassInfo(moduleRoot, staticInfo, fromScala2 = false)
193193

194-
classInfo = parseAttributes(classRoot.symbol, classInfo)
194+
classInfo = parseAttributes(classRoot.symbol).complete(classInfo)
195195
if (isAnnotation)
196196
// classInfo must be a TempClassInfoType and not a TempPolyType,
197197
// because Java annotations cannot have type parameters.
@@ -227,32 +227,37 @@ class ClassfileParser(
227227
val sflags =
228228
if (method) Flags.Method | methodTranslation.flags(jflags)
229229
else fieldTranslation.flags(jflags)
230-
val name = pool.getName(in.nextChar)
231-
if (!sflags.isOneOf(Flags.PrivateOrArtifact) || name.name == nme.CONSTRUCTOR) {
230+
val preName = pool.getName(in.nextChar)
231+
if (!sflags.isOneOf(Flags.PrivateOrArtifact) || preName.name == nme.CONSTRUCTOR) {
232+
val sig = pool.getExternalName(in.nextChar).value
233+
val completer = MemberCompleter(preName.name, jflags, sig)
232234
val member = newSymbol(
233-
getOwner(jflags), name.name, sflags, memberCompleter,
235+
getOwner(jflags), preName.name, sflags, completer,
234236
getPrivateWithin(jflags), coord = start)
235237
getScope(jflags).enter(member)
238+
239+
val pt = if sig.size == 1 then constantTagToType(sig(0)) else WildcardType
240+
val isVarargs = sflags.is(Flags.Method) && (jflags & JAVA_ACC_VARARGS) != 0
241+
completer.attrCompleter = parseAttributes(member, pt, isVarargs)
242+
}
243+
else {
244+
in.nextChar // info
245+
skipAttributes()
236246
}
237-
// skip rest of member for now
238-
in.nextChar // info
239-
skipAttributes()
240247
}
241248

242-
val memberCompleter: LazyType = new LazyType {
249+
class MemberCompleter(name: SimpleName, jflags: Int, sig: String) extends LazyType {
250+
var attrCompleter: AttributeCompleter = null
243251

244-
def complete(denot: SymDenotation)(using Context): Unit = withReader(classfile) { (using in) =>
245-
in.bp = denot.symbol.coord.toIndex
252+
def complete(denot: SymDenotation)(using Context): Unit = {
246253
val sym = denot.symbol
247-
val jflags = in.nextChar
248254
val isEnum = (jflags & JAVA_ACC_ENUM) != 0
249-
val name = pool.getName(in.nextChar).name
250255
val isConstructor = name eq nme.CONSTRUCTOR
251256

252257
/** Strip leading outer param from constructor and trailing access tag for
253258
* private inner constructors.
254259
*/
255-
def normalizeConstructorParams() = innerClasses.get(currentClassName) match {
260+
def normalizeConstructorParams() = innerClasses.get(currentClassName.toString) match {
256261
case Some(entry) if !isStatic(entry.jflags) =>
257262
val mt @ MethodTpe(paramNames, paramTypes, resultType) = denot.info
258263
var normalizedParamNames = paramNames.tail
@@ -283,9 +288,9 @@ class ClassfileParser(
283288
}
284289

285290
val isVarargs = denot.is(Flags.Method) && (jflags & JAVA_ACC_VARARGS) != 0
286-
denot.info = pool.getType(in.nextChar, isVarargs)
291+
denot.info = sigToType(sig, isVarargs = isVarargs)
287292
if (isConstructor) normalizeConstructorParams()
288-
denot.info = translateTempPoly(parseAttributes(sym, denot.info, isVarargs))
293+
denot.info = translateTempPoly(attrCompleter.complete(denot.info))
289294
if (isConstructor) normalizeConstructorInfo()
290295

291296
if (ctx.explicitNulls) denot.info = JavaNullInterop.nullifyMember(denot.symbol, denot.info, isEnum)
@@ -317,7 +322,7 @@ class ClassfileParser(
317322
case BOOL_TAG => defn.BooleanType
318323
}
319324

320-
private def sigToType(sig: String, owner: Symbol = null, isVarargs: Boolean = false)(using Context, DataReader): Type = {
325+
private def sigToType(sig: String, owner: Symbol = null, isVarargs: Boolean = false)(using Context): Type = {
321326
var index = 0
322327
val end = sig.length
323328
def accept(ch: Char): Unit = {
@@ -604,8 +609,20 @@ class ClassfileParser(
604609
None // ignore malformed annotations
605610
}
606611

607-
def parseAttributes(sym: Symbol, symtype: Type, isVarargs: Boolean = false)(using ctx: Context, in: DataReader): Type = {
608-
var newType = symtype
612+
abstract class AttributeCompleter {
613+
def complete(tp: Type)(using Context): Type
614+
}
615+
616+
// invariant: `in` and `ctx` should not be captured inside the result function
617+
def parseAttributes(sym: Symbol, pt: Type = WildcardType, isVarargs: Boolean = false)(using ctx: Context, in: DataReader): AttributeCompleter = {
618+
var typeUpdate: Option[Context ?=> Type] = None
619+
620+
val res = new AttributeCompleter {
621+
def complete(tp: Type)(using Context): Type = {
622+
val newType = if (typeUpdate.isEmpty) tp else typeUpdate.get
623+
cook.apply(newType)
624+
}
625+
}
609626

610627
def parseAttribute(): Unit = {
611628
val attrName = pool.getName(in.nextChar).name.toTypeName
@@ -614,9 +631,12 @@ class ClassfileParser(
614631
attrName match {
615632
case tpnme.SignatureATTR =>
616633
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)
634+
typeUpdate = Some {
635+
val newType = sigToType(sig.value, sym, isVarargs)
636+
if (ctx.debug && ctx.verbose)
637+
println("" + sym + "; signature = " + sig + " type = " + newType)
638+
newType
639+
}
620640

621641
case tpnme.SyntheticATTR =>
622642
sym.setFlag(Flags.SyntheticArtifact)
@@ -630,8 +650,8 @@ class ClassfileParser(
630650
sym.addAnnotation(Annotation(defn.DeprecatedAnnot, msg, since))
631651

632652
case tpnme.ConstantValueATTR =>
633-
val c = pool.getConstant(in.nextChar, symtype)
634-
if (c ne null) newType = ConstantType(c)
653+
val c = pool.getConstant(in.nextChar, pt)
654+
if (c ne null) typeUpdate = Some { ConstantType(c) }
635655
else report.warning(s"Invalid constant in attribute of ${sym.showLocated} while parsing ${classfile}")
636656

637657
case tpnme.AnnotationDefaultATTR =>
@@ -652,12 +672,12 @@ class ClassfileParser(
652672
parseExceptions(attrLen)
653673

654674
case tpnme.CodeATTR =>
675+
in.skip(attrLen)
655676
if (sym.owner.isAllOf(Flags.JavaInterface)) {
656677
sym.resetFlag(Flags.Deferred)
657678
sym.owner.resetFlag(Flags.PureInterface)
658679
report.log(s"$sym in ${sym.owner} is a java8+ default method.")
659680
}
660-
in.skip(attrLen)
661681

662682
case _ =>
663683
}
@@ -677,6 +697,7 @@ class ClassfileParser(
677697
}
678698
}
679699

700+
680701
/** Parse a sequence of annotations and attaches them to the
681702
* current symbol sym, except for the ScalaSignature annotation that it returns, if it is available. */
682703
def parseAnnotations(len: Int): Unit = {
@@ -693,7 +714,7 @@ class ClassfileParser(
693714
for (i <- 0 until in.nextChar)
694715
parseAttribute()
695716

696-
cook.apply(newType)
717+
res
697718
}
698719

699720
/** Annotations in Scala are assumed to get all their arguments as constructor
@@ -730,7 +751,7 @@ class ClassfileParser(
730751

731752
for entry <- innerClasses.valuesIterator do
732753
// create a new class member for immediate inner classes
733-
if entry.outerName == currentClassName then
754+
if entry.outer.name == currentClassName then
734755
val file = ctx.platform.classPath.findClassFile(entry.externalName.toString) getOrElse {
735756
throw new AssertionError(entry.externalName)
736757
}
@@ -911,8 +932,11 @@ class ClassfileParser(
911932
val nameIndex = in.nextChar
912933
val jflags = in.nextChar
913934
if (innerIndex != 0 && outerIndex != 0 && nameIndex != 0) {
914-
val entry = InnerClassEntry(innerIndex, outerIndex, nameIndex, jflags)
915-
innerClasses(pool.getClassName(innerIndex).name) = entry
935+
val inner = pool.getClassName(innerIndex)
936+
val outer = pool.getClassName(outerIndex)
937+
val name = pool.getClassName(nameIndex)
938+
val entry = InnerClassEntry(inner, outer, name, jflags)
939+
innerClasses(pool.getClassName(innerIndex).value) = entry
916940
}
917941
}
918942
}
@@ -922,20 +946,20 @@ class ClassfileParser(
922946
}
923947

924948
/** 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
949+
case class InnerClassEntry(external: NameOrString, outer: NameOrString, name: NameOrString, jflags: Int) {
950+
def externalName = external.value
951+
def outerName = outer.value
952+
def originalName = name.name
929953

930-
def show(using DataReader): String =
931-
s"$originalName in $outerName($externalName)"
954+
// The name of the outer class, without its trailing $ if it has one.
955+
def strippedOuter = outer.name.stripModuleClassSuffix
932956
}
933957

934-
private object innerClasses extends util.HashMap[Name, InnerClassEntry] {
958+
private object innerClasses extends util.HashMap[String, InnerClassEntry] {
935959
/** Return the Symbol of the top level class enclosing `name`,
936960
* or 'name's symbol if no entry found for `name`.
937961
*/
938-
def topLevelClass(name: Name)(using Context, DataReader): Symbol = {
962+
def topLevelClass(name: String)(using Context): Symbol = {
939963
val tlName = if (contains(name)) {
940964
var entry = this(name)
941965
while (contains(entry.outerName))
@@ -944,13 +968,13 @@ class ClassfileParser(
944968
}
945969
else
946970
name
947-
classNameToSymbol(tlName)
971+
classNameToSymbol(tlName.toTypeName)
948972
}
949973

950974
/** Return the class symbol for `entry`. It looks it up in its outer class.
951975
* This might force outer class symbols.
952976
*/
953-
def classSymbol(entry: InnerClassEntry)(using Context, DataReader): Symbol = {
977+
def classSymbol(entry: InnerClassEntry)(using Context): Symbol = {
954978
def getMember(sym: Symbol, name: Name)(using Context): Symbol =
955979
if (isStatic(entry.jflags))
956980
if (sym == classRoot.symbol)
@@ -966,7 +990,7 @@ class ClassfileParser(
966990
else
967991
sym.info.member(name).symbol
968992

969-
val outerName = entry.outerName.stripModuleClassSuffix
993+
val outerName = entry.strippedOuter
970994
val innerName = entry.originalName
971995
val owner = classNameToSymbol(outerName)
972996
val result = atPhase(typerPhase)(getMember(owner, innerName.toTypeName))
@@ -1025,7 +1049,7 @@ class ClassfileParser(
10251049
}
10261050
}
10271051

1028-
def getClassSymbol(name: SimpleName)(using Context, DataReader): Symbol =
1052+
def getClassSymbol(name: SimpleName)(using Context): Symbol =
10291053
if (name.endsWith("$") && (name ne nme.nothingRuntimeClass) && (name ne nme.nullRuntimeClass))
10301054
// Null$ and Nothing$ ARE classes
10311055
requiredModule(name.dropRight(1))

0 commit comments

Comments
 (0)