From b2d3b05194b378f394523be57575c468dba05e43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Fri, 8 Sep 2023 17:45:40 +0200 Subject: [PATCH] Introduce a more restrictive `ReaderContext` inside `reader.*`. It statically makes sure that we never access anything that requires reading another file. This change surfaced a couple places where there could potentially be external reading, and which are now a bit more complicated to fit the static restriction. The most prominent example is `TreeUnpickler.readWithin`, which also suggested that we decouple reading the `privateWithin` from the flags (to only do it after `createSymbols()` is over). --- build.sbt | 25 ++-- .../main/scala/tastyquery/Definitions.scala | 2 +- .../src/main/scala/tastyquery/Symbols.scala | 51 ++++++-- .../src/main/scala/tastyquery/Types.scala | 12 +- .../scala/tastyquery/reader/Loaders.scala | 18 ++- .../tastyquery/reader/ReaderContext.scala | 77 ++++++++++++ .../reader/classfiles/ClassfileParser.scala | 50 ++++---- .../reader/classfiles/ClassfileReader.scala | 10 +- .../reader/classfiles/Descriptors.scala | 33 +++--- .../reader/classfiles/JavaSignatures.scala | 39 +++--- .../reader/pickles/PickleReader.scala | 82 +++++++------ .../tastyquery/reader/pickles/Unpickler.scala | 5 +- .../reader/tasties/PositionUnpickler.scala | 7 +- .../reader/tasties/TastyUnpickler.scala | 10 +- .../reader/tasties/TreeUnpickler.scala | 111 +++++++++++------- 15 files changed, 338 insertions(+), 194 deletions(-) create mode 100644 tasty-query/shared/src/main/scala/tastyquery/reader/ReaderContext.scala diff --git a/build.sbt b/build.sbt index 63ab759f..5e4d1ccf 100644 --- a/build.sbt +++ b/build.sbt @@ -112,23 +112,14 @@ lazy val tastyQuery = mimaBinaryIssueFilters ++= { import com.typesafe.tools.mima.core.* Seq( - // private, so this is fine - ProblemFilters.exclude[DirectMissingMethodProblem]("tastyquery.reader.tasties.TreeUnpickler#Caches.refinedTypeTreeCache"), - ProblemFilters.exclude[MissingClassProblem]("tastyquery.reader.tasties.TreeUnpickler$LocalContext"), - - // private[reader], so this is fine - ProblemFilters.exclude[Problem]("tastyquery.reader.tasties.TastyUnpickler#*"), - - // private[tastyquery], so this is fine - ProblemFilters.exclude[MissingClassProblem]("tastyquery.Types$TypeParamInfo"), - - /* We removed TypeParamInfo from the parents of ClassTypeParam. - * Since TypeParamInfo was `private[tastyquery]`, there is little chance it leaked. - */ - ProblemFilters.exclude[MissingTypesProblem]("tastyquery.Symbols$ClassTypeParamSymbol"), - - // new abstract method in fully sealed trait, so this is fine - ProblemFilters.exclude[ReversedMissingMethodProblem]("tastyquery.Types#TermLambdaType.paramTypes"), + // Everything in tastyquery.reader is private[tastyquery] at most + ProblemFilters.exclude[Problem]("tastyquery.reader.*"), + + // private[tastyquery], not an issue + ProblemFilters.exclude[IncompatibleMethTypeProblem]("tastyquery.Symbols#ClassSymbol.createRefinedClassSymbol"), + ProblemFilters.exclude[DirectMissingMethodProblem]("tastyquery.Types#PolyType.fromParamsSymbols"), + ProblemFilters.exclude[DirectMissingMethodProblem]("tastyquery.Types#TypeLambda.fromParamsSymbols"), + ProblemFilters.exclude[DirectMissingMethodProblem]("tastyquery.Types#TypeLambdaTypeCompanion.fromParamsSymbols"), ) }, ) diff --git a/tasty-query/shared/src/main/scala/tastyquery/Definitions.scala b/tasty-query/shared/src/main/scala/tastyquery/Definitions.scala index 1addb145..d1138c64 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/Definitions.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/Definitions.scala @@ -411,7 +411,7 @@ final class Definitions private[tastyquery] (ctx: Context, rootPackage: PackageS def isTupleNClass(sym: ClassSymbol): Boolean = sym.owner == scalaPackage && TupleNClasses.contains(sym) - lazy val hasGenericTuples = scalaPackage.getDecl(tpnme.TupleCons).isDefined + lazy val hasGenericTuples = ctx.classloader.hasGenericTuples lazy val uninitializedMethod: Option[TermSymbol] = scalaCompiletimePackage.getDecl(moduleClassName("package$package")).flatMap { packageObjectClass => diff --git a/tasty-query/shared/src/main/scala/tastyquery/Symbols.scala b/tasty-query/shared/src/main/scala/tastyquery/Symbols.scala index 4aa19757..262f9074 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/Symbols.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/Symbols.scala @@ -75,7 +75,7 @@ object Symbols { private var isFlagsInitialized = false private var myFlags: FlagSet = Flags.EmptyFlagSet private var myTree: Option[DefiningTreeType] = None - private var myPrivateWithin: Option[DeclaringSymbol] = None + private var myPrivateWithin: Option[DeclaringSymbol] | Null = null private var myAnnotations: List[Annotation] | Null = null /** Checks that this `Symbol` has been completely initialized. @@ -96,6 +96,7 @@ object Symbols { */ protected def doCheckCompleted(): Unit = if !isFlagsInitialized then throw failNotCompleted("flags were not initialized") + if myPrivateWithin == null then throw failNotCompleted("privateWithin was not initialized") if myAnnotations == null then throw failNotCompleted("annotations were not initialized") private[tastyquery] def withTree(t: DefiningTreeType): this.type = @@ -107,12 +108,24 @@ object Symbols { myTree private[tastyquery] final def withFlags(flags: FlagSet, privateWithin: Option[DeclaringSymbol]): this.type = - if isFlagsInitialized then throw IllegalStateException(s"reassignment of flags to $this") + setFlags(flags) + setPrivateWithin(privateWithin) + + private[tastyquery] final def setFlags(flags: FlagSet): this.type = + if isFlagsInitialized || myPrivateWithin != null then + throw IllegalStateException(s"reassignment of flags to $this") else isFlagsInitialized = true myFlags = flags + this + end setFlags + + private[tastyquery] final def setPrivateWithin(privateWithin: Option[DeclaringSymbol]): this.type = + if myPrivateWithin != null then throw IllegalStateException(s"reassignment of privateWithin to $this") + else myPrivateWithin = privateWithin this + end setPrivateWithin private[tastyquery] final def setAnnotations(annots: List[Annotation]): this.type = if myAnnotations != null then throw IllegalStateException(s"reassignment of annotations to $this") @@ -126,8 +139,9 @@ object Symbols { else throw IllegalStateException(s"annotations of $this have not been initialized") protected final def privateWithin: Option[DeclaringSymbol] = - if isFlagsInitialized then myPrivateWithin - else throw IllegalStateException(s"flags of $this have not been initialized") + val local = myPrivateWithin + if local != null then local + else throw IllegalStateException(s"privateWithin of $this has not been initialized") protected final def flags: FlagSet = if isFlagsInitialized then myFlags @@ -1187,13 +1201,16 @@ object Symbols { end distinguishOverloaded final def getDecl(name: TypeName)(using Context): Option[TypeSymbol] = + getDeclImpl(name) + + private[tastyquery] final def getDeclImpl(name: TypeName): Option[TypeSymbol] = myDeclarations.get(name) match case Some(decls) => assert(decls.sizeIs == 1, decls) Some(decls.head.asType) case None => None - end getDecl + end getDeclImpl final def getDecl(name: TermName)(using Context): Option[TermSymbol] = getDecl(name: Name).map(_.asTerm) @@ -1273,6 +1290,9 @@ object Symbols { end findNonOverloadedDecl final def declarations(using Context): List[TermOrTypeSymbol] = + declarationsOfClass + + private[tastyquery] final def declarationsOfClass: List[TermOrTypeSymbol] = myDeclarations.values.toList.flatten // Member lookup, including inherited members @@ -1537,9 +1557,7 @@ object Symbols { * * This is only used by the Scala 2 unpickler. */ - private[tastyquery] def setScala2SealedChildren(children: List[Symbol | Scala2ExternalSymRef])( - using Context - ): Unit = + private[tastyquery] def setScala2SealedChildren(children: List[Symbol | Scala2ExternalSymRef]): Unit = if !flags.is(Scala2Defined) then throw IllegalArgumentException(s"Illegal $this.setScala2SealedChildren($children) for non-Scala 2 class") if myScala2SealedChildren.isDefined then @@ -1617,17 +1635,20 @@ object Symbols { private[tastyquery] def createNotDeclaration(name: TypeName, owner: Symbol): ClassSymbol = ClassSymbol(name, owner) - private[tastyquery] def createRefinedClassSymbol(owner: Symbol, flags: FlagSet, pos: SourcePosition)( - using Context + private[tastyquery] def createRefinedClassSymbol( + owner: Symbol, + objectType: TypeRef, + flags: FlagSet, + pos: SourcePosition ): ClassSymbol = // TODO Store the `pos` - createRefinedClassSymbol(owner, flags) + createRefinedClassSymbol(owner, objectType, flags) - private[tastyquery] def createRefinedClassSymbol(owner: Symbol, flags: FlagSet)(using Context): ClassSymbol = + private[tastyquery] def createRefinedClassSymbol(owner: Symbol, objectType: TypeRef, flags: FlagSet): ClassSymbol = val cls = ClassSymbol(tpnme.RefinedClassMagic, owner) // by-pass `owner.addDeclIfDeclaringSym` cls .withTypeParams(Nil) - .withParentsDirect(defn.ObjectType :: Nil) + .withParentsDirect(objectType :: Nil) .withGivenSelfType(None) .withFlags(flags, None) .setAnnotations(Nil) @@ -1665,6 +1686,10 @@ object Symbols { /** Is this the root package? */ final def isRootPackage: Boolean = owner == null + /** Is this the scala package? */ + private[tastyquery] def isScalaPackage: Boolean = + name == nme.scalaPackageName && owner != null && owner.isRootPackage + /** Gets the subpackage with the specified `name`, if it exists. * * If this package contains a subpackage with the name `name`, returns diff --git a/tasty-query/shared/src/main/scala/tastyquery/Types.scala b/tasty-query/shared/src/main/scala/tastyquery/Types.scala index 335f6ceb..e08c9487 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/Types.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/Types.scala @@ -693,10 +693,10 @@ object Types { None // TODO /** Is this type exactly Nothing (no vars, aliases, refinements etc allowed)? */ - private[tastyquery] final def isExactlyNothing(using Context): Boolean = this match + private[tastyquery] final def isExactlyNothing: Boolean = this match case tp: TypeRef if tp.name == tpnme.Nothing => tp.prefix.match - case prefix: PackageRef => prefix.symbol == defn.scalaPackage + case prefix: PackageRef => prefix.symbol.isScalaPackage case _ => false case _ => false @@ -894,6 +894,10 @@ object Types { private[tastyquery] final def isLocalRef(sym: Symbol): Boolean = prefix == NoPrefix && (designator eq sym) + private[tastyquery] final def localSymbol: ThisSymbolType = + require(prefix == NoPrefix, prefix) + designator.asInstanceOf[ThisSymbolType] + private[tastyquery] final def isSomeClassTypeParamRef: Boolean = designator.isInstanceOf[ClassTypeParamSymbol] @@ -1585,9 +1589,7 @@ object Types { sealed abstract class TypeLambdaTypeCompanion[RT <: TypeOrMethodic, LT <: TypeLambdaType] extends LambdaTypeCompanion[TypeName, TypeBounds, RT, LT] { @targetName("fromParamsSymbols") - private[tastyquery] final def fromParams(params: List[LocalTypeParamSymbol], resultType: RT)( - using Context - ): LT | RT = + private[tastyquery] final def fromParams(params: List[LocalTypeParamSymbol], resultType: RT): LT | RT = if params.isEmpty then resultType else val paramNames = params.map(_.name) diff --git a/tasty-query/shared/src/main/scala/tastyquery/reader/Loaders.scala b/tasty-query/shared/src/main/scala/tastyquery/reader/Loaders.scala index 3fd55f42..bd8f24a0 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/reader/Loaders.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/reader/Loaders.scala @@ -11,6 +11,7 @@ import tastyquery.Names.* import tastyquery.Symbols.* import tastyquery.Trees.* +import tastyquery.reader.ReaderContext.rctx import tastyquery.reader.classfiles.ClassfileParser import tastyquery.reader.classfiles.ClassfileParser.{ClassKind, InnerClassDecl, Resolver} import tastyquery.reader.tasties.TastyUnpickler @@ -26,7 +27,7 @@ private[tastyquery] object Loaders { pkg.fullName.select(rootName) end Loader - class Loader(val classpath: Classpath) { loader => + class Loader(val classpath: Classpath) { given Resolver = Resolver() @@ -39,6 +40,7 @@ private[tastyquery] object Loaders { private var searched = false private var packages: Map[PackageSymbol, IArray[PackageData]] = compiletime.uninitialized + private var _hasGenericTuples: Boolean = compiletime.uninitialized private var byEntry: ByEntryMap | Null = null private val roots: mutable.Map[PackageSymbol, mutable.Map[SimpleName, Entry]] = mutable.HashMap.empty private var topLevelTastys: Map[Loader.Root, List[Tree]] = Map.empty @@ -65,11 +67,13 @@ private[tastyquery] object Loaders { * In any case, no new declarations can ever be found for the given root * after this method. */ - private def completeRoot(root: Loader.Root, entry: Entry)(using Context): Unit = + private def completeRoot(root: Loader.Root, entry: Entry)(using ctx: Context): Unit = + doCompleteRoot(root, entry)(using ReaderContext(ctx)) + private def doCompleteRoot(root: Loader.Root, entry: Entry)(using ReaderContext): Unit = def innerClassLookup(nested: IArray[ClassData]): Map[SimpleName, ClassData] = val mkBinaryName: String => SimpleName = - if root.pkg == defn.EmptyPackage then termName(_) + if root.pkg == rctx.EmptyPackage then termName(_) else val pre = root.pkg.fullName.path.mkString("/") bin => termName(s"$pre/$bin") @@ -140,7 +144,7 @@ private[tastyquery] object Loaders { case entry: Entry.TastyOnly => // Tested in `SymbolSuite`, `ReadTreeSuite`, these do not need to see class files. enterTasty(root, entry.tastyData) - end completeRoot + end doCompleteRoot /** Loads all the roots of the given `pkg`. */ private[tastyquery] def loadAllRoots(pkg: PackageSymbol)(using Context): Unit = @@ -272,9 +276,13 @@ private[tastyquery] object Loaders { if !searched then searched = true - loader.packages = loadPackages().groupMap((pkg, _) => pkg)((_, data) => data) + packages = loadPackages().groupMap((pkg, _) => pkg)((_, data) => data) + _hasGenericTuples = + packages.get(defn.scalaPackage).exists(_.exists(_.tastys.exists(_.binaryName == "$times$colon"))) end initPackages + def hasGenericTuples: Boolean = _hasGenericTuples + private def computeByEntry()(using Context): ByEntryMap = require(searched) diff --git a/tasty-query/shared/src/main/scala/tastyquery/reader/ReaderContext.scala b/tasty-query/shared/src/main/scala/tastyquery/reader/ReaderContext.scala new file mode 100644 index 00000000..d235ddab --- /dev/null +++ b/tasty-query/shared/src/main/scala/tastyquery/reader/ReaderContext.scala @@ -0,0 +1,77 @@ +package tastyquery.reader + +import tastyquery.Contexts.* +import tastyquery.Names.* +import tastyquery.SourceFile +import tastyquery.Symbols.* +import tastyquery.Types.* + +/** A restricted Context that is safe to use from the readers. + * + * It does not give access to anything that might require reading other files. + */ +private[reader] final class ReaderContext(underlying: Context): + def RootPackage: PackageSymbol = underlying.defn.RootPackage + def EmptyPackage: PackageSymbol = underlying.defn.EmptyPackage + def javaLangPackage: PackageSymbol = underlying.defn.javaLangPackage + def scalaPackage: PackageSymbol = underlying.defn.scalaPackage + + def NothingType: NothingType = underlying.defn.NothingType + def AnyType: TypeRef = underlying.defn.AnyType + def MatchableType: TypeRef = underlying.defn.MatchableType + def ObjectType: TypeRef = underlying.defn.ObjectType + def FromJavaObjectType: TypeRef = underlying.defn.FromJavaObjectType + + def IntType: TypeRef = underlying.defn.IntType + def LongType: TypeRef = underlying.defn.LongType + def FloatType: TypeRef = underlying.defn.FloatType + def DoubleType: TypeRef = underlying.defn.DoubleType + def BooleanType: TypeRef = underlying.defn.BooleanType + def ByteType: TypeRef = underlying.defn.ByteType + def ShortType: TypeRef = underlying.defn.ShortType + def CharType: TypeRef = underlying.defn.CharType + def UnitType: TypeRef = underlying.defn.UnitType + + def ArrayTypeOf(tpe: TypeOrWildcard): AppliedType = underlying.defn.ArrayTypeOf(tpe) + def RepeatedTypeOf(tpe: TypeOrWildcard): AppliedType = underlying.defn.RepeatedTypeOf(tpe) + + def GenericTupleTypeOf(elementTypes: List[TypeOrWildcard]): Type = underlying.defn.GenericTupleTypeOf(elementTypes) + + def NothingAnyBounds: RealTypeBounds = underlying.defn.NothingAnyBounds + + def uninitializedMethodTermRef: TermRef = underlying.defn.uninitializedMethodTermRef + + def findPackageFromRootOrCreate(fullyQualifiedName: FullyQualifiedName): PackageSymbol = + underlying.findPackageFromRootOrCreate(fullyQualifiedName) + + /** Reads a package reference, with a fallback on faked term references. + * + * In a full, correct classpath, `createPackageSelection()` will always + * return a `PackageRef`. However, in an incomplete or incorrect classpath, + * this method may return a `TermRef` if the target package does not exist. + * + * An alternative would be to create missing packages on the fly, but that + * would not be consistent with `Trees.Select.tpe` and + * `Trees.TermRefTypeTree.toType`. + */ + def createPackageSelection(path: List[TermName]): TermReferenceType = + path.foldLeft[TermReferenceType](RootPackage.packageRef) { (prefix, name) => + NamedType.possibleSelFromPackage(prefix, name) + } + end createPackageSelection + + def getSourceFile(path: String): SourceFile = + underlying.getSourceFile(path) + + def hasGenericTuples: Boolean = underlying.classloader.hasGenericTuples + + def createObjectMagicMethods(cls: ClassSymbol): Unit = + underlying.defn.createObjectMagicMethods(cls) + + def createStringMagicMethods(cls: ClassSymbol): Unit = + underlying.defn.createStringMagicMethods(cls) +end ReaderContext + +private[reader] object ReaderContext: + def rctx(using context: ReaderContext): context.type = context +end ReaderContext diff --git a/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/ClassfileParser.scala b/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/ClassfileParser.scala index 8721481a..312fd4a5 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/ClassfileParser.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/ClassfileParser.scala @@ -13,6 +13,8 @@ import tastyquery.SourceLanguage import tastyquery.Symbols.* import tastyquery.Types.* +import tastyquery.reader.ReaderContext +import tastyquery.reader.ReaderContext.rctx import tastyquery.reader.pickles.{Unpickler, PickleReader} import ClassfileReader.* @@ -38,29 +40,29 @@ private[reader] object ClassfileParser { private val refs = mutable.HashMap.empty[SimpleName, TypeRef] private val staticrefs = mutable.HashMap.empty[SimpleName, TermRef] - private def computeRef(binaryName: SimpleName, isStatic: Boolean)(using Context, InnerClasses): NamedType = + private def computeRef(binaryName: SimpleName, isStatic: Boolean)(using ReaderContext, InnerClasses): NamedType = innerClasses.get(binaryName) match case Some(InnerClassRef(name, outer, isStaticInner)) => val pre = lookup(outer, isStaticInner) - pre.select(if isStatic then name else name.toTypeName) + NamedType(pre, if isStatic then name else name.toTypeName) case None if !isStatic && binaryName == javaLangObjectBinaryName => - defn.FromJavaObjectType + rctx.FromJavaObjectType case None => val (pkgRef, cls) = binaryName.name.lastIndexOf('/') match - case -1 => (defn.RootPackage.packageRef, binaryName) + case -1 => (rctx.RootPackage.packageRef, binaryName) case i => (computePkg(binaryName.name.take(i)), termName(binaryName.name.drop(i + 1))) - pkgRef.select(if isStatic then cls else cls.toTypeName) + NamedType(pkgRef, if isStatic then cls else cls.toTypeName) end computeRef - private def computePkg(packageName: String)(using Context): PackageRef = + private def computePkg(packageName: String)(using ReaderContext): TermReferenceType = val parts = packageName.split('/').map(termName).toList - ctx.findPackageFromRoot(FullyQualifiedName(parts)).packageRef + rctx.createPackageSelection(parts) - private def lookup(binaryName: SimpleName, isStatic: Boolean)(using Context, InnerClasses): NamedType = + private def lookup(binaryName: SimpleName, isStatic: Boolean)(using ReaderContext, InnerClasses): NamedType = if isStatic then staticrefs.getOrElseUpdate(binaryName, computeRef(binaryName, isStatic = true).asTermRef) else refs.getOrElseUpdate(binaryName, computeRef(binaryName, isStatic = false).asTypeRef) - def resolve(binaryName: SimpleName)(using Context, InnerClasses): TypeRef = + def resolve(binaryName: SimpleName)(using ReaderContext, InnerClasses): TypeRef = lookup(binaryName, isStatic = false).asTypeRef end Resolver @@ -81,7 +83,7 @@ private[reader] object ClassfileParser { structure: Structure, lookup: Map[SimpleName, ClassData], innerClasses: Forked[DataStream] - )(using Context): InnerClasses = + ): InnerClasses = import structure.reader def missingClass(binaryName: SimpleName) = @@ -116,7 +118,7 @@ private[reader] object ClassfileParser { val attributes: Forked[DataStream] )(using val pool: reader.ConstantPool) - def loadScala2Class(structure: Structure, runtimeAnnotStart: Forked[DataStream])(using Context): Unit = { + def loadScala2Class(structure: Structure, runtimeAnnotStart: Forked[DataStream])(using ReaderContext): Unit = { import structure.{reader, given} val Some(Annotation(tpe, args)) = runtimeAnnotStart.use { @@ -143,7 +145,7 @@ private[reader] object ClassfileParser { classSig: SigOrSupers, innerLookup: Map[SimpleName, ClassData], optInnerClasses: Option[Forked[DataStream]] - )(using Context, Resolver): List[InnerClassDecl] = { + )(using ReaderContext, Resolver): List[InnerClassDecl] = { import structure.{reader, given} val allRegisteredSymbols = mutable.ListBuffer.empty[Symbol] @@ -165,7 +167,7 @@ private[reader] object ClassfileParser { .withTypeParams(Nil) .withFlags(clsFlags | Flags.ModuleClassCreationFlags, clsPrivateWithin) .setAnnotations(Nil) - .withParentsDirect(defn.ObjectType :: Nil) + .withParentsDirect(rctx.ObjectType :: Nil) .withGivenSelfType(None) allRegisteredSymbols += moduleClass @@ -221,7 +223,7 @@ private[reader] object ClassfileParser { } end parents val parents1 = - if parents.head eq defn.FromJavaObjectType then defn.ObjectType :: parents.tail + if parents.head eq rctx.FromJavaObjectType then rctx.ObjectType :: parents.tail else parents cls.withParentsDirect(parents1) end initParents @@ -232,9 +234,9 @@ private[reader] object ClassfileParser { initParents() // Intercept java.lang.Object and java.lang.String to create their magic methods - if cls.owner == defn.javaLangPackage then - if cls.name == tpnme.Object then defn.createObjectMagicMethods(cls) - else if cls.name == tpnme.String then defn.createStringMagicMethods(cls) + if cls.owner == rctx.javaLangPackage then + if cls.name == tpnme.Object then rctx.createObjectMagicMethods(cls) + else if cls.name == tpnme.String then rctx.createStringMagicMethods(cls) for (sym, javaFlags, sigOrDesc) <- loadMembers() do val parsedType = sigOrDesc match @@ -255,12 +257,12 @@ private[reader] object ClassfileParser { innerClasses.declarations } - private def patchForVarargs(sym: TermSymbol, tpe: TypeOrMethodic)(using Context): MethodicType = + private def patchForVarargs(sym: TermSymbol, tpe: TypeOrMethodic)(using ReaderContext): MethodicType = tpe match case tpe: MethodType if tpe.paramNames.sizeIs >= 1 => val patchedLast = tpe.paramTypes.last match case ArrayTypeExtractor(lastElemType) => - defn.RepeatedTypeOf(lastElemType) + rctx.RepeatedTypeOf(lastElemType) case _ => throw ClassfileFormatException(s"Found ACC_VARARGS on $sym but its last param type was not an array: $tpe") tpe.derivedLambdaType(tpe.paramNames, tpe.paramTypes.init :+ patchedLast, tpe.resultType) @@ -276,11 +278,11 @@ private[reader] object ClassfileParser { * is not otherwise guaranteed to work in all situations. */ private object ArrayTypeExtractor: - def unapply(tpe: AppliedType)(using Context): Option[TypeOrWildcard] = + def unapply(tpe: AppliedType)(using ReaderContext): Option[TypeOrWildcard] = tpe.tycon match case tycon: TypeRef if tycon.name == tpnme.Array && tpe.args.sizeIs == 1 => tycon.prefix match - case prefix: PackageRef if prefix.symbol == defn.scalaPackage => + case prefix: PackageRef if prefix.symbol == rctx.scalaPackage => Some(tpe.args.head) case _ => None @@ -288,7 +290,7 @@ private[reader] object ClassfileParser { None end ArrayTypeExtractor - private def parse(classRoot: ClassData, structure: Structure)(using Context): ClassKind = { + private def parse(classRoot: ClassData, structure: Structure): ClassKind = { import structure.{reader, given} def process(attributesStream: Forked[DataStream]): ClassKind = @@ -359,7 +361,7 @@ private[reader] object ClassfileParser { ) } - private def toplevel(classOwner: DeclaringSymbol, classRoot: ClassData)(using Context): Structure = { + private def toplevel(classOwner: DeclaringSymbol, classRoot: ClassData): Structure = { def headerAndStructure(reader: ClassfileReader)(using DataStream) = { reader.acceptHeader(classOwner, classRoot) structure(reader)(using reader.readConstantPool()) @@ -368,7 +370,7 @@ private[reader] object ClassfileParser { ClassfileReader.unpickle(classRoot)(headerAndStructure) } - def readKind(classOwner: DeclaringSymbol, classRoot: ClassData)(using Context): ClassKind = + def readKind(classOwner: DeclaringSymbol, classRoot: ClassData): ClassKind = parse(classRoot, toplevel(classOwner, classRoot)) } diff --git a/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/ClassfileReader.scala b/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/ClassfileReader.scala index 9729cbae..7c500453 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/ClassfileReader.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/ClassfileReader.scala @@ -189,20 +189,16 @@ private[classfiles] final class ClassfileReader private () { reader } - def readFields(op: (SimpleName, SigOrDesc, AccessFlags) => Unit)(using DataStream, ConstantPool)( - using Context - ): Unit = + def readFields(op: (SimpleName, SigOrDesc, AccessFlags) => Unit)(using DataStream, ConstantPool): Unit = readMembers(isMethod = false, op) - def readMethods(op: (SimpleName, SigOrDesc, AccessFlags) => Unit)(using DataStream, ConstantPool)( - using Context - ): Unit = + def readMethods(op: (SimpleName, SigOrDesc, AccessFlags) => Unit)(using DataStream, ConstantPool): Unit = readMembers(isMethod = true, op) private def readMembers( isMethod: Boolean, op: (SimpleName, SigOrDesc, AccessFlags) => Unit - )(using DataStream, ConstantPool)(using Context): Unit = { + )(using DataStream, ConstantPool): Unit = { val count = data.readU2() loop(count) { val accessFlags = readAccessFlags() diff --git a/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/Descriptors.scala b/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/Descriptors.scala index 1c4b02f4..be5acdaa 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/Descriptors.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/Descriptors.scala @@ -9,27 +9,30 @@ import tastyquery.Types.* import tastyquery.Symbols.* import tastyquery.Flags +import tastyquery.reader.ReaderContext +import tastyquery.reader.ReaderContext.rctx + import ClassfileParser.* private[classfiles] object Descriptors: def parseSupers(cls: ClassSymbol, superClass: Option[SimpleName], interfaces: IArray[SimpleName])( - using Context, + using ReaderContext, InnerClasses, Resolver ): List[Type] = cls.withTypeParams(Nil) // !!! Cannot access `defn.ObjectClass` here, because that's a cycle when initializing defn.ObjectClass itself - if cls.owner == defn.javaLangPackage && cls.name == tpnme.Object then defn.AnyType :: defn.MatchableType :: Nil + if cls.owner == rctx.javaLangPackage && cls.name == tpnme.Object then rctx.AnyType :: rctx.MatchableType :: Nil else - val superRef = superClass.map(classRef).getOrElse(defn.ObjectType) + val superRef = superClass.map(classRef).getOrElse(rctx.ObjectType) superRef :: interfaces.map(classRef).toList - def classRef(binaryName: SimpleName)(using Context, InnerClasses, Resolver): TypeRef = + def classRef(binaryName: SimpleName)(using ReaderContext, InnerClasses, Resolver): TypeRef = resolver.resolve(binaryName) @throws[ClassfileFormatException] - def parseDescriptor(member: Symbol, desc: String)(using Context, InnerClasses, Resolver): TypeOrMethodic = + def parseDescriptor(member: Symbol, desc: String)(using ReaderContext, InnerClasses, Resolver): TypeOrMethodic = // TODO: once we support inner classes, decide if we merge with parseSignature var offset = 0 var end = desc.length @@ -61,14 +64,14 @@ private[classfiles] object Descriptors: def baseType: Option[Type] = if available >= 1 then (peek: @switch) match - case 'B' => commitSimple(1, Some(defn.ByteType)) - case 'C' => commitSimple(1, Some(defn.CharType)) - case 'D' => commitSimple(1, Some(defn.DoubleType)) - case 'F' => commitSimple(1, Some(defn.FloatType)) - case 'I' => commitSimple(1, Some(defn.IntType)) - case 'J' => commitSimple(1, Some(defn.LongType)) - case 'S' => commitSimple(1, Some(defn.ShortType)) - case 'Z' => commitSimple(1, Some(defn.BooleanType)) + case 'B' => commitSimple(1, Some(rctx.ByteType)) + case 'C' => commitSimple(1, Some(rctx.CharType)) + case 'D' => commitSimple(1, Some(rctx.DoubleType)) + case 'F' => commitSimple(1, Some(rctx.FloatType)) + case 'I' => commitSimple(1, Some(rctx.IntType)) + case 'J' => commitSimple(1, Some(rctx.LongType)) + case 'S' => commitSimple(1, Some(rctx.ShortType)) + case 'Z' => commitSimple(1, Some(rctx.BooleanType)) case _ => None else None @@ -81,14 +84,14 @@ private[classfiles] object Descriptors: def arrayType: Option[Type] = if consume('[') then val tpe = fieldDescriptor - Some(defn.ArrayTypeOf(tpe)) + Some(rctx.ArrayTypeOf(tpe)) else None def fieldDescriptor: Type = baseType.orElse(objectType).orElse(arrayType).getOrElse(abort) def returnDescriptor: Type = - if consume('V') then defn.UnitType + if consume('V') then rctx.UnitType else fieldDescriptor def methodDescriptor: MethodType = diff --git a/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/JavaSignatures.scala b/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/JavaSignatures.scala index 163c926d..d3ff7409 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/JavaSignatures.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/JavaSignatures.scala @@ -12,6 +12,9 @@ import tastyquery.Names.* import tastyquery.Types.* import tastyquery.Symbols.* +import tastyquery.reader.ReaderContext +import tastyquery.reader.ReaderContext.rctx + import ClassfileParser.{InnerClasses, Resolver} private[classfiles] object JavaSignatures: @@ -20,7 +23,7 @@ private[classfiles] object JavaSignatures: @throws[ClassfileFormatException] def parseSignature(member: Symbol { val owner: Symbol }, signature: String, allRegisteredSymbols: Growable[Symbol])( - using Context, + using ReaderContext, InnerClasses, Resolver ): TypeOrMethodic = @@ -29,8 +32,8 @@ private[classfiles] object JavaSignatures: val isClass = member.isClass val isMethod = member.isTerm && member.asTerm.isMethod - lazy val someEmptyType = Some(defn.AnyType) - lazy val emptyTypeBounds = defn.NothingAnyBounds + lazy val someEmptyType = Some(rctx.AnyType) + lazy val emptyTypeBounds = rctx.NothingAnyBounds extension (env: JavaSignature) @@ -130,14 +133,14 @@ private[classfiles] object JavaSignatures: def baseType: Option[Type] = if available >= 1 then (peek: @switch) match - case 'B' => commitSimple(1, Some(defn.ByteType)) - case 'C' => commitSimple(1, Some(defn.CharType)) - case 'D' => commitSimple(1, Some(defn.DoubleType)) - case 'F' => commitSimple(1, Some(defn.FloatType)) - case 'I' => commitSimple(1, Some(defn.IntType)) - case 'J' => commitSimple(1, Some(defn.LongType)) - case 'S' => commitSimple(1, Some(defn.ShortType)) - case 'Z' => commitSimple(1, Some(defn.BooleanType)) + case 'B' => commitSimple(1, Some(rctx.ByteType)) + case 'C' => commitSimple(1, Some(rctx.CharType)) + case 'D' => commitSimple(1, Some(rctx.DoubleType)) + case 'F' => commitSimple(1, Some(rctx.FloatType)) + case 'I' => commitSimple(1, Some(rctx.IntType)) + case 'J' => commitSimple(1, Some(rctx.LongType)) + case 'S' => commitSimple(1, Some(rctx.ShortType)) + case 'Z' => commitSimple(1, Some(rctx.BooleanType)) case _ => None else None @@ -175,16 +178,16 @@ private[classfiles] object JavaSignatures: if available >= 1 then (peek: @switch) match case '*' => - commitSimple(1, WildcardTypeArg(defn.NothingAnyBounds)) + commitSimple(1, WildcardTypeArg(rctx.NothingAnyBounds)) case '+' => commit(1) { val upper = referenceType(env) - WildcardTypeArg(RealTypeBounds(defn.NothingType, upper)) + WildcardTypeArg(RealTypeBounds(rctx.NothingType, upper)) } case '-' => commit(1) { val lower = referenceType(env) - WildcardTypeArg(RealTypeBounds(lower, defn.FromJavaObjectType)) + WildcardTypeArg(RealTypeBounds(lower, rctx.FromJavaObjectType)) } case _ => referenceType(env) @@ -199,10 +202,10 @@ private[classfiles] object JavaSignatures: expect(':') referenceTypeSignature(env) match case Some(tpe) => tpe - case _ => defn.FromJavaObjectType + case _ => rctx.FromJavaObjectType val interfaceBounds = readWhile(':', referenceType(env)) if env.withAddedParam(tname) then emptyTypeBounds // shortcut as we will throw away the bounds - else RealTypeBounds(defn.NothingType, interfaceBounds.foldLeft(classBound)(AndType(_, _))) + else RealTypeBounds(rctx.NothingType, interfaceBounds.foldLeft(classBound)(AndType(_, _))) def typeParamsRest(env: JavaSignature): List[TypeBounds] = readUntil('>', typeParameter(env)) @@ -215,11 +218,11 @@ private[classfiles] object JavaSignatures: def arrayType(env: JavaSignature): Option[Type] = if consume('[') then val tpe = javaTypeSignature(env) - Some(defn.ArrayTypeOf(tpe)) + Some(rctx.ArrayTypeOf(tpe)) else None def result(env: JavaSignature): Type = - if consume('V') then defn.UnitType + if consume('V') then rctx.UnitType else javaTypeSignature(env) def referenceType(env: JavaSignature): Type = diff --git a/tasty-query/shared/src/main/scala/tastyquery/reader/pickles/PickleReader.scala b/tasty-query/shared/src/main/scala/tastyquery/reader/pickles/PickleReader.scala index d0bf0309..fe365021 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/reader/pickles/PickleReader.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/reader/pickles/PickleReader.scala @@ -13,7 +13,8 @@ import tastyquery.Substituters import tastyquery.Symbols.* import tastyquery.Types.* -import tastyquery.reader.UTF8Utils +import tastyquery.reader.{ReaderContext, UTF8Utils} +import tastyquery.reader.ReaderContext.rctx import PickleReader.* import PickleFormat.* @@ -92,22 +93,22 @@ private[pickles] class PickleReader { } } - def readLocalSymbolRef()(using Context, PklStream, Entries, Index): Symbol = + def readLocalSymbolRef()(using ReaderContext, PklStream, Entries, Index): Symbol = readMaybeExternalSymbolRef() match case sym: Symbol => sym case external => errorBadSignature(s"expected local symbol reference but found $external") - def readLocalSymbolAt(i: Int)(using Context, PklStream, Entries, Index): Symbol = + def readLocalSymbolAt(i: Int)(using ReaderContext, PklStream, Entries, Index): Symbol = readMaybeExternalSymbolAt(i) match case sym: Symbol => sym case external => errorBadSignature(s"expected local symbol reference but found $external") - def readMaybeExternalSymbolRef()(using Context, PklStream, Entries, Index): MaybeExternalSymbol = + def readMaybeExternalSymbolRef()(using ReaderContext, PklStream, Entries, Index): MaybeExternalSymbol = readMaybeExternalSymbolAt(pkl.readNat()) - def ensureReadAllLocalDeclsOfRefinement(ownerIndex: Int)(using Context, PklStream, Entries, Index): Unit = + def ensureReadAllLocalDeclsOfRefinement(ownerIndex: Int)(using ReaderContext, PklStream, Entries, Index): Unit = index.loopWithIndices { (offset, i) => val idx = index(i) val tag = pkl.bytes(idx).toInt @@ -123,7 +124,7 @@ private[pickles] class PickleReader { } end ensureReadAllLocalDeclsOfRefinement - def readMaybeExternalSymbolAt(i: Int)(using Context, PklStream, Entries, Index): MaybeExternalSymbol = + def readMaybeExternalSymbolAt(i: Int)(using ReaderContext, PklStream, Entries, Index): MaybeExternalSymbol = // Similar to at(), but sometimes readMaybeExternalSymbol stores the result itself in entries val tOpt = entries(i).asInstanceOf[MaybeExternalSymbol | Null] if tOpt == null then { @@ -136,7 +137,9 @@ private[pickles] class PickleReader { res } else tOpt - def readMaybeExternalSymbol(storeInEntriesAt: Int)(using Context, PklStream, Entries, Index): MaybeExternalSymbol = { + def readMaybeExternalSymbol( + storeInEntriesAt: Int + )(using ReaderContext, PklStream, Entries, Index): MaybeExternalSymbol = { // val start = indexCoord(readIndex) // no need yet to record the position of symbols val tag = pkl.readByte() val end = pkl.readEnd() @@ -148,10 +151,10 @@ private[pickles] class PickleReader { def readExtSymbol(): MaybeExternalSymbol = val name = readNameRef().decode - val owner = if (atEnd) ctx.defn.RootPackage else readMaybeExternalSymbolRef() + val owner = if (atEnd) rctx.RootPackage else readMaybeExternalSymbolRef() name match case nme.RootName | nme.RootPackageName => - ctx.defn.RootPackage + rctx.RootPackage case _ => def defaultRef = ExternalSymbolRef(owner, name) owner match @@ -202,7 +205,7 @@ private[pickles] class PickleReader { case external => errorBadSignature(s"expected local symbol reference but found $external") - if name1 == nme.m_getClass && owner.owner == defn.scalaPackage && HasProblematicGetClass.contains(owner.name) then + if name1 == nme.m_getClass && owner.owner == rctx.scalaPackage && HasProblematicGetClass.contains(owner.name) then return NoExternalSymbolRef.instance /* In some situations, notably involving EXISTENTIALtpe, reading the @@ -272,7 +275,8 @@ private[pickles] class PickleReader { case CLASSsym => val tname = name.toTypeName val cls = - if tname == tpnme.RefinedClassMagic then ClassSymbol.createRefinedClassSymbol(owner, Scala2Defined) + if tname == tpnme.RefinedClassMagic then + ClassSymbol.createRefinedClassSymbol(owner, rctx.ObjectType, Scala2Defined) else if tname == tpnme.scala2LocalChild then ClassSymbol.createNotDeclaration(tname, owner) else ClassSymbol.create(tname, owner) storeResultInEntries(cls) @@ -288,12 +292,12 @@ private[pickles] class PickleReader { case tpe => throw Scala2PickleFormatException(s"unexpected type $tpe for $cls, owner is $owner") val parentTypes = - if cls.owner == defn.scalaPackage && tname == tpnme.AnyVal then + if cls.owner == rctx.scalaPackage && tname == tpnme.AnyVal then // Patch the superclasses of AnyVal to contain Matchable - scala2ParentTypes :+ defn.MatchableType - else if cls.owner == defn.scalaPackage && isTupleClassName(tname) && defn.hasGenericTuples then + scala2ParentTypes :+ rctx.MatchableType + else if cls.owner == rctx.scalaPackage && isTupleClassName(tname) && rctx.hasGenericTuples then // Patch the superclass of TupleN classes to inherit from *: - defn.GenericTupleTypeOf(typeParams.map(_.localRef)) :: scala2ParentTypes.tail + rctx.GenericTupleTypeOf(typeParams.map(_.localRef)) :: scala2ParentTypes.tail else scala2ParentTypes val givenSelfType = if atEnd then None else Some(readTrueTypeRef()) cls.withParentsDirect(parentTypes) @@ -346,7 +350,7 @@ private[pickles] class PickleReader { sym } - private def patchConstructorType(cls: ClassSymbol, tpe: TypeOrMethodic)(using Context): TypeOrMethodic = + private def patchConstructorType(cls: ClassSymbol, tpe: TypeOrMethodic)(using ReaderContext): TypeOrMethodic = def resultToUnit(tpe: TypeOrMethodic): TypeOrMethodic = tpe match case tpe: PolyType => @@ -354,7 +358,7 @@ private[pickles] class PickleReader { case tpe: MethodType => tpe.derivedLambdaType(tpe.paramNames, tpe.paramTypes, resultToUnit(tpe.resultType)) case _: Type => - defn.UnitType + rctx.UnitType val tpe1 = resultToUnit(tpe) @@ -372,7 +376,7 @@ private[pickles] class PickleReader { ) end patchConstructorType - def readChildren()(using Context, PklStream, Entries, Index): Unit = + def readChildren()(using ReaderContext, PklStream, Entries, Index): Unit = val tag = pkl.readByte() assert(tag == CHILDREN) val end = pkl.readEnd() @@ -473,7 +477,7 @@ private[pickles] class PickleReader { val tag = pkl.bytes(index(i)) tag == CHILDREN - protected def isRefinementClass(sym: Symbol)(using Context): Boolean = + protected def isRefinementClass(sym: Symbol): Boolean = sym.name == tpnme.RefinedClassMagic def isSymbolRef(i: Int)(using PklStream, Index): Boolean = { @@ -481,36 +485,36 @@ private[pickles] class PickleReader { (firstSymTag <= tag && tag <= lastExtSymTag) } - private def readPrefixRef()(using Context, PklStream, Entries, Index): Prefix = + private def readPrefixRef()(using ReaderContext, PklStream, Entries, Index): Prefix = readTypeMappableRef() match case tpe: Prefix => tpe case tpe => errorBadSignature(s"expected a prefix but got $tpe") - private def readTrueTypeRef()(using Context, PklStream, Entries, Index): Type = + private def readTrueTypeRef()(using ReaderContext, PklStream, Entries, Index): Type = readTypeMappableRef() match case tpe: Type => tpe case tpe => errorBadSignature(s"expected a type but got $tpe") /** Read a type */ - private def readTrueType()(using Context, PklStream, Entries, Index): Type = + private def readTrueType()(using ReaderContext, PklStream, Entries, Index): Type = readTypeMappable() match case tpe: Type => tpe case tpe => errorBadSignature(s"expected a type but got $tpe") - private def readTypeOrWildcardRef()(using Context, PklStream, Entries, Index): TypeOrWildcard = + private def readTypeOrWildcardRef()(using ReaderContext, PklStream, Entries, Index): TypeOrWildcard = readTypeMappableRef() match case tpe: TypeOrWildcard => tpe case tpe => errorBadSignature(s"expected a type argument but got $tpe") - private def readTypeOrMethodicRef()(using Context, PklStream, Entries, Index): TypeOrMethodic = + private def readTypeOrMethodicRef()(using ReaderContext, PklStream, Entries, Index): TypeOrMethodic = readTypeMappableRef() match case tpe: TypeOrMethodic => tpe case tpe => errorBadSignature(s"expected a type or methodic type but got $tpe") - private def readTypeMappableRef()(using Context, PklStream, Entries, Index): TypeMappable = + private def readTypeMappableRef()(using ReaderContext, PklStream, Entries, Index): TypeMappable = at(pkl.readNat())(readTypeMappable()) - private def readTypeMappable()(using Context, PklStream, Entries, Index): TypeMappable = { + private def readTypeMappable()(using ReaderContext, PklStream, Entries, Index): TypeMappable = { def select(pre: Prefix, sym: TermOrTypeSymbol): Type = // structural members need to be selected by name, their symbols are only // valid in the synthetic refinement class that defines them. @@ -529,7 +533,7 @@ private[pickles] class PickleReader { * while still being able to detect these erroneous types if we really * want to. */ - TypeRef(defn.scalaPackage.packageRef, typeName("")) + TypeRef(rctx.scalaPackage.packageRef, typeName("")) case NOPREFIXtpe => NoPrefix case THIStpe => @@ -611,7 +615,7 @@ private[pickles] class PickleReader { def isByNameParamClass(tpe: Type): Boolean = tpe match case tpe: TypeRef if tpe.name == tpnme.scala2ByName => tpe.prefix match - case prefix: PackageRef => prefix.symbol == defn.scalaPackage + case prefix: PackageRef => prefix.symbol == rctx.scalaPackage case _ => false case _ => false @@ -632,7 +636,7 @@ private[pickles] class PickleReader { val parents = pkl.until(end, () => readTrueTypeRef()) val parent = parents.reduceLeft(AndType(_, _)) ensureReadAllLocalDeclsOfRefinement(clazzIndex) - val decls = clazz.declarations + val decls = clazz.declarationsOfClass if decls.isEmpty then parent else val refined = decls.toList.foldLeft(parent) { (parent, sym) => @@ -685,7 +689,7 @@ private[pickles] class PickleReader { } } - private def readTypeParams()(using Context, PklStream, Entries, Index): List[ClassTypeParamSymbol] = { + private def readTypeParams()(using ReaderContext, PklStream, Entries, Index): List[ClassTypeParamSymbol] = { val tag = pkl.readByte() val end = pkl.readEnd() if (tag == POLYtpe) { @@ -695,14 +699,14 @@ private[pickles] class PickleReader { } /** Convert temp poly type to TypeLambda and leave other types alone. */ - private def translateTempPolyForTypeArg(tp: TypeOrWildcard)(using Context): TypeOrWildcard = + private def translateTempPolyForTypeArg(tp: TypeOrWildcard)(using ReaderContext): TypeOrWildcard = translateTempPolyForTypeMember(tp) match case TypeAlias(alias) => alias case bounds: RealTypeBounds => WildcardTypeArg(bounds) end translateTempPolyForTypeArg /** Convert temp poly type to TypeLambda and leave other types alone. */ - private def translateTempPolyForTypeMember(tp: TypeOrWildcard)(using Context): TypeBounds = tp match + private def translateTempPolyForTypeMember(tp: TypeOrWildcard)(using ReaderContext): TypeBounds = tp match case tp: WildcardTypeArg => def rec(bound: Type): Type = translateTempPolyForTypeMember(bound).asInstanceOf[TypeAlias].alias @@ -728,7 +732,7 @@ private[pickles] class PickleReader { end translateTempPolyForTypeMember /** Convert temp poly type to PolyType and leave other types alone. */ - private def translateTempPolyForMethod(tp: TypeOrMethodic)(using Context): TypeOrMethodic = tp match + private def translateTempPolyForMethod(tp: TypeOrMethodic)(using ReaderContext): TypeOrMethodic = tp match case TempPolyType(tparams, restpe) => val localTParams = tparams.asInstanceOf[List[LocalTypeParamSymbol]] // no class type params in methods restpe match @@ -744,11 +748,11 @@ private[pickles] class PickleReader { private def noSuchTypeTag(tag: Int, end: Int): Nothing = errorBadSignature("bad type tag: " + tag) - private def readConstantRef()(using Context, PklStream, Entries, Index): Constant | TermRef = + private def readConstantRef()(using ReaderContext, PklStream, Entries, Index): Constant | TermRef = at(pkl.readNat())(readConstant()) /** Read a constant */ - private def readConstant()(using Context, PklStream, Entries, Index): Constant | TermRef = { + private def readConstant()(using ReaderContext, PklStream, Entries, Index): Constant | TermRef = { import java.lang.Float.intBitsToFloat import java.lang.Double.longBitsToDouble @@ -789,7 +793,7 @@ private[pickles] class PickleReader { * to * tp { name: T } */ - private def elimExistentials(boundSyms: List[TypeMemberSymbol], tp: Type)(using Context): Type = + private def elimExistentials(boundSyms: List[TypeMemberSymbol], tp: Type)(using ReaderContext): Type = // Need to be careful not to run into cyclic references here (observed when // compiling t247.scala). That's why we avoid taking `symbol` of a TypeRef // unless names match up. @@ -901,13 +905,13 @@ private[reader] object PickleReader { } final class ExternalSymbolRef(owner: MaybeExternalSymbol, name: Name) { - def toTypeRef(pre: Prefix)(using Context): TypeRef = + def toTypeRef(pre: Prefix)(using ReaderContext): TypeRef = toNamedType(pre).asInstanceOf[TypeRef] - def toTermRef(pre: Prefix)(using Context): TermRef = + def toTermRef(pre: Prefix)(using ReaderContext): TermRef = toNamedType(pre).asInstanceOf[TermRef] - def toNamedType(pre: Prefix)(using Context): NamedType = + def toNamedType(pre: Prefix)(using ReaderContext): NamedType = NamedType(pre, toScala2ExternalSymRef) def toScala2ExternalSymRef: Scala2ExternalSymRef = diff --git a/tasty-query/shared/src/main/scala/tastyquery/reader/pickles/Unpickler.scala b/tasty-query/shared/src/main/scala/tastyquery/reader/pickles/Unpickler.scala index 769eb8ae..8e40fda9 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/reader/pickles/Unpickler.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/reader/pickles/Unpickler.scala @@ -2,13 +2,14 @@ package tastyquery.reader.pickles import PickleReader.{PklStream, index, pkl} -import tastyquery.Contexts.Context import tastyquery.Exceptions.* import tastyquery.Flags.* import tastyquery.SourceLanguage +import tastyquery.reader.ReaderContext + private[reader] object Unpickler { - def loadInfo(sigBytes: IArray[Byte])(using Context): Unit = { + def loadInfo(sigBytes: IArray[Byte])(using ReaderContext): Unit = { def run(reader: PickleReader, structure: reader.Structure)(using PklStream): Unit = { import structure.given diff --git a/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/PositionUnpickler.scala b/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/PositionUnpickler.scala index ef7eebf2..20bbf783 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/PositionUnpickler.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/PositionUnpickler.scala @@ -15,8 +15,11 @@ import tastyquery.SourceFile import tastyquery.SourcePosition import tastyquery.Spans.* +import tastyquery.reader.ReaderContext +import tastyquery.reader.ReaderContext.rctx + /** Unpickler for tree positions */ -private[reader] class PositionUnpickler(reader: TastyReader, nameAtRef: TastyUnpickler.NameTable)(using Context) { +private[reader] class PositionUnpickler(reader: TastyReader, nameAtRef: TastyUnpickler.NameTable)(using ReaderContext) { import reader.* private val mySourcePositions = mutable.HashMap.empty[Addr, SourcePosition] @@ -67,7 +70,7 @@ private[reader] class PositionUnpickler(reader: TastyReader, nameAtRef: TastyUnp header = readInt() if header == SOURCE then val path = nameAtRef.simple(readNameRef()).toString() - curSource = ctx.getSourceFile(path) + curSource = rctx.getSourceFile(path) if noSourceSeenYet then curSource.setLineSizes(myLineSizes) noSourceSeenYet = false diff --git a/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/TastyUnpickler.scala b/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/TastyUnpickler.scala index 272e8678..eef4a31d 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/TastyUnpickler.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/TastyUnpickler.scala @@ -11,21 +11,21 @@ import tastyquery.Exceptions.* import tastyquery.Names.* import tastyquery.Signatures.* -import tastyquery.reader.UTF8Utils +import tastyquery.reader.{ReaderContext, UTF8Utils} private[reader] object TastyUnpickler { abstract class SectionUnpickler[R](val name: String) { - def unpickle(filename: String, reader: TastyReader, nameAtRef: NameTable)(using Context): R + def unpickle(filename: String, reader: TastyReader, nameAtRef: NameTable)(using ReaderContext): R } class TreeSectionUnpickler(posUnpickler: Option[PositionUnpickler]) extends SectionUnpickler[TreeUnpickler]("ASTs") { - def unpickle(filename: String, reader: TastyReader, nameAtRef: NameTable)(using Context): TreeUnpickler = + def unpickle(filename: String, reader: TastyReader, nameAtRef: NameTable)(using ReaderContext): TreeUnpickler = new TreeUnpickler(filename, reader, nameAtRef, posUnpickler) } class PositionSectionUnpickler extends SectionUnpickler[PositionUnpickler]("Positions") { - def unpickle(filename: String, reader: TastyReader, nameAtRef: NameTable)(using Context): PositionUnpickler = + def unpickle(filename: String, reader: TastyReader, nameAtRef: NameTable)(using ReaderContext): PositionUnpickler = new PositionUnpickler(reader, nameAtRef) } @@ -146,7 +146,7 @@ private[reader] class TastyUnpickler(reader: TastyReader) { } } - def unpickle[R](filename: String, sec: SectionUnpickler[R])(using Context): Option[R] = + def unpickle[R](filename: String, sec: SectionUnpickler[R])(using ReaderContext): Option[R] = for (reader <- sectionReader.get(sec.name)) yield sec.unpickle(filename, reader, nameAtRef) def bytes: Array[Byte] = reader.bytes diff --git a/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/TreeUnpickler.scala b/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/TreeUnpickler.scala index fd98db2a..3ea77d4e 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/TreeUnpickler.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/TreeUnpickler.scala @@ -23,6 +23,9 @@ import tastyquery.Symbols.* import tastyquery.Trees.* import tastyquery.Types.* +import tastyquery.reader.ReaderContext +import tastyquery.reader.ReaderContext.rctx + import TastyUnpickler.NameTable private[tasties] sealed trait AbstractCaseDefFactory[CaseDefType] @@ -35,11 +38,11 @@ private[tasties] class TreeUnpickler private ( nameAtRef: NameTable, posUnpicklerOpt: Option[PositionUnpickler], caches: TreeUnpickler.Caches -)(using Context) { +)(using ReaderContext) { import TreeUnpickler.* def this(filename: String, reader: TastyReader, nameAtRef: NameTable, posUnpicklerOpt: Option[PositionUnpickler])( - using Context + using ReaderContext ) = this(filename, reader, nameAtRef, posUnpicklerOpt, new TreeUnpickler.Caches) def unpickle(): List[Tree] = @@ -58,7 +61,7 @@ private[tasties] class TreeUnpickler private ( end unpickle private def enterSymbols(): Unit = - while !reader.isAtEnd do createSymbols(defn.RootPackage) + while !reader.isAtEnd do createSymbols(rctx.RootPackage) /* This method walks a TASTy file and creates all symbols in it. * @@ -77,7 +80,7 @@ private[tasties] class TreeUnpickler private ( val end = reader.readEnd() val sym = readPotentiallyShared({ assert(reader.readByte() == TERMREFpkg, posErrorMsg) - ctx.findPackageFromRootOrCreate(readFullyQualifiedName) + rctx.findPackageFromRootOrCreate(readFullyQualifiedName) }) reader.until(end)(createSymbols(owner = sym)) case TYPEDEF => @@ -85,7 +88,12 @@ private[tasties] class TreeUnpickler private ( val name = readName.toTypeName val tag = reader.nextByte val sym = - if tag == TEMPLATE then ClassSymbol.create(name, owner) + if tag == TEMPLATE then + val cls = ClassSymbol.create(name, owner) + owner match + case owner: PackageSymbol => caches.declaredTopLevelClasses += (owner, name) -> cls + case _ => () + cls else TypeMemberSymbol.create(name, owner) caches.registerSym(start, sym) readSymbolModifiers(sym, tag, end) @@ -113,12 +121,12 @@ private[tasties] class TreeUnpickler private ( if tagFollowShared == TYPEBOUNDS then LocalTypeParamSymbol.create(name.toTypeName, owner) else TermSymbol.create(name, owner) caches.registerSym(start, sym) - sym.withFlags(Case, None) + sym.setFlags(Case) // bind is never an owner reader.until(end)(createSymbols(owner)) case REFINEDtpt => val spn = spanAt(start) - val sym = ClassSymbol.createRefinedClassSymbol(owner, EmptyFlagSet, spn) + val sym = ClassSymbol.createRefinedClassSymbol(owner, rctx.ObjectType, EmptyFlagSet, spn) caches.registerSym(start, sym) val end = reader.readEnd() reader.until(end)(createSymbols(owner = sym)) @@ -247,14 +255,13 @@ private[tasties] class TreeUnpickler private ( modsReader.skipTree() // tpt val rhsIsEmpty = modsReader.nothingButMods(end) if !rhsIsEmpty then modsReader.skipTree() - val (flags, privateWithin) = modsReader.readModifiers(end) + val flags = modsReader.readModifiers(sym, end) val normalizedFlags = normalizeFlags(sym, tag, flags, rhsIsEmpty) - sym.withFlags(normalizedFlags, privateWithin) + sym.setFlags(normalizedFlags) /** Read modifiers into a set of flags and a privateWithin boundary symbol. */ - private def readModifiers(end: Addr): (FlagSet, Option[DeclaringSymbol]) = + private def readModifiers(sym: Symbol, end: Addr): FlagSet = var flags: FlagSet = EmptyFlagSet - var privateWithin: Option[DeclaringSymbol] = None def addFlag(flag: FlagSet): Unit = flags |= flag reader.readByte() @@ -308,40 +315,67 @@ private[tasties] class TreeUnpickler private ( case INFIX => addFlag(Infix) case PRIVATEqualified => ignoreFlag() - privateWithin = Some(readWithin) + skipTree() case PROTECTEDqualified => addFlag(Protected) - privateWithin = Some(readWithin) + skipTree() case ANNOTATION => ignoreFlag() ignoreAnnot() case tag => assert(false, s"illegal modifier tag $tag at ${reader.currentAddr}, end = $end") end while - (flags, privateWithin) + flags end readModifiers - private def readWithin: DeclaringSymbol = - readTypeMappable() match - case TypeRef.OfClass(cls) => cls - case pkgRef: PackageRef => pkgRef.symbol - case tpe => throw TastyFormatException(s"unexpected type for readWithin: $tpe") + private def readWithin(sym: Symbol): DeclaringSymbol = + /* Must be a combination of NoPrefix, PackageRef, TypeRef and ThisType that + * all point to elements of the owner chain of `sym` or their companion classes. + */ + val orig = readTypeMappable() + + def throwInvalid(): Nothing = + throw TastyFormatException(s"unexpected type in readWithin for $sym: $orig") + + def resolveLocalClass(tpe: TypeRef): ClassSymbol = tpe.prefix match + case NoPrefix => + tpe.localSymbol match + case cls: ClassSymbol => cls + case _ => throwInvalid() + + case prefix: PackageRef => + caches.declaredTopLevelClasses.getOrElse((prefix.symbol, tpe.name), throwInvalid()) + + case prefix: ThisType => + resolveLocalClass(prefix.tref).getDeclImpl(tpe.name) match + case Some(cls: ClassSymbol) => cls + case _ => throwInvalid() + + case _ => + throwInvalid() + end resolveLocalClass + + orig match + case pkgRef: PackageRef => pkgRef.symbol + case tpe: TypeRef => resolveLocalClass(tpe) + case tpe => throwInvalid() end readWithin private def readAnnotationsInModifiers(sym: Symbol, end: Addr): Unit = + var privateWithin: Option[DeclaringSymbol] = None var annots: List[Annotation] = Nil while reader.currentAddr != end && isModifierTag(reader.nextByte) do reader.readByte() match case PRIVATEqualified | PROTECTEDqualified => - skipTree() + privateWithin = Some(readWithin(sym)) case ANNOTATION => annots ::= readAnnotation() case _ => () end while - sym.setAnnotations(annots) + sym.setPrivateWithin(privateWithin).setAnnotations(annots) end readAnnotationsInModifiers private def readAnnotation(): Annotation = @@ -386,7 +420,7 @@ private[tasties] class TreeUnpickler private ( val packageEnd = reader.readEnd() val pid = readPotentiallyShared({ assert(reader.readByte() == TERMREFpkg, posErrorMsg) - ctx.findPackageFromRootOrCreate(readFullyQualifiedName) + rctx.findPackageFromRootOrCreate(readFullyQualifiedName) }) PackageDef(pid, reader.until(packageEnd)(readTopLevelStat))(spn) case _ => readStat @@ -514,7 +548,7 @@ private[tasties] class TreeUnpickler private ( case _ => val alias = readTypeTree if forOpaque then - OpaqueTypeAliasDefinitionTree(InferredTypeBoundsTree(defn.NothingAnyBounds)(NoPosition), alias)(alias.pos) + OpaqueTypeAliasDefinitionTree(InferredTypeBoundsTree(rctx.NothingAnyBounds)(NoPosition), alias)(alias.pos) else TypeAliasDefinitionTree(alias)(alias.pos) } end readTypeDefinition @@ -789,7 +823,7 @@ private[tasties] class TreeUnpickler private ( val name = readName val typ = readTermType() val typ1 = - if name == nme.Wildcard then defn.uninitializedMethodTermRef + if name == nme.Wildcard then rctx.uninitializedMethodTermRef else typ makeIdent(name, typ1, spn) case _ => @@ -807,7 +841,7 @@ private[tasties] class TreeUnpickler private ( /* This is a bit of a hack for default arguments to Java annotations with default values. * See simple_trees.Annotations.javaAnnotWithDefaultImplicit(). */ - defn.uninitializedMethodTermRef + rctx.uninitializedMethodTermRef else typ makeIdent(name, typ1, spn) case APPLY => @@ -1068,22 +1102,11 @@ private[tasties] class TreeUnpickler private ( caches.getSymbol[Symbol](symAddr) } - /** Reads a package reference, with a fallback on faked term references. - * - * In a full, correct classpath, `readPackageRef()` will always return a - * `PackageRef`. However, in an incomplete or incorrect classpath, this - * method may return a `TermRef` if the target package does not exist. - * - * An alternative would be to create missing packages on the fly, but that - * would not be consistent with `Trees.Select.tpe` and - * `Trees.TermRefTypeTree.toType`. - */ + /** Reads a package reference, with a fallback on faked term references. */ private def readPackageRef(): TermReferenceType = - val fullyQualifiedName = readFullyQualifiedName - fullyQualifiedName.path.foldLeft[TermReferenceType](defn.RootPackage.packageRef) { (prefix, name) => - val termName = name.asInstanceOf[TermName] // readFullyQualifiedName only reads TermName's in paths - NamedType.possibleSelFromPackage(prefix, termName) - } + // readFullyQualifiedName only reads TermName's in paths, so the cast is OK + val path = readFullyQualifiedName.path.asInstanceOf[List[TermName]] + rctx.createPackageSelection(path) end readPackageRef private def readTypeRef(): TypeRef = @@ -1471,7 +1494,7 @@ private[tasties] class TreeUnpickler private ( val end = reader.readEnd() val selOrBound = readTypeTree val (bound, selector) = - if tagFollowShared == CASEDEF then (TypeWrapper(defn.AnyType)(spn), selOrBound) + if tagFollowShared == CASEDEF then (TypeWrapper(rctx.AnyType)(spn), selOrBound) else (selOrBound, readTypeTree) MatchTypeTree(bound, selector, readCases[TypeCaseDef](TypeCaseDefFactory, end))(spn) // TODO: why in TYPEAPPLY? @@ -1615,6 +1638,12 @@ private[tasties] object TreeUnpickler { val registeredBinders = mutable.Map.empty[Addr, Binders] + /** The top-level classes declared in this .tasty file. + * + * This is used in `readWithin` to resolve top-level class references without a Context. + */ + val declaredTopLevelClasses = mutable.AnyRefMap.empty[(PackageSymbol, TypeName), ClassSymbol] + def hasSymbolAt(addr: Addr): Boolean = localSymbols.contains(addr) /** Registers a symbol at @addr with @name. */