Last active
November 1, 2023 10:42
-
-
Save bishabosha/dfaf3ff55a1a6c6fb39f57927e1236d0 to your computer and use it in GitHub Desktop.
tasty-query classpath from dotty classpath
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//> using toolkit 0.2.1 | |
//> using scala 3.3.1 | |
//> using dep ch.epfl.scala::tasty-query:0.11.0 | |
//> using dep org.scala-lang::scala3-compiler:3.3.1 | |
//> using dep io.get-coursier:coursier_2.13:2.1.7 | |
package tastyquery.jdk.classpaths | |
import tastyquery.Classpaths.Classpath | |
import tastyquery.Classpaths.ClasspathEntry | |
import tastyquery.Classpaths.PackageData | |
import tastyquery.Classpaths.ClassData | |
import tastyquery.Contexts.Context | |
import tastyquery.Contexts.ctx | |
import dotty.tools.io.ClassPath as DottyClassPath | |
import dotty.tools.dotc.core.Contexts.ContextBase | |
import dotty.tools.dotc.core.Contexts.Context as DottyContext | |
import dotty.tools.dotc.core.Contexts.inContext | |
import dotty.tools.dotc.ScalacCommand | |
import dotty.tools.io.AbstractFile | |
import dotty.tools.dotc.classpath.PackageEntry | |
import dotty.tools.dotc.Compiler | |
import dotty.tools.dotc.classpath.AggregateClassPath | |
import dotty.tools.io.FileZipArchive | |
import coursier.* | |
import dotty.tools.dotc.classpath.ClassFileEntry | |
import LazyClasspathSuite.* | |
object LazyClasspathSuite: | |
val classpathEntries = Fetch() | |
.addDependencies( | |
Dependency( | |
Module(Organization("org.scala-lang"), ModuleName("scala3-compiler_3")), | |
"3.3.1" | |
) | |
) | |
.run() | |
.map(_.toString) | |
enum ForceEvent: | |
case Package(name: String) | |
case Class(name: String) | |
case Tasty(name: String) | |
class DotcEntry(debugName: String, cp: DottyClassPath, callback: ForceEvent => Unit) extends ClasspathEntry: | |
override def toString(): String = debugName | |
lazy val _packages: List[DotcPackageData] = | |
def loadPackage(pkg: PackageEntry): DotcPackageData = | |
val name = pkg.name | |
DotcPackageData(s"$debugName:$name", name, cp, callback) | |
def loadSubPackages(name: String): List[DotcPackageData] = | |
cp.packages(name).toList.flatMap(pkg => loadPackage(pkg) :: loadSubPackages(pkg.name)) | |
loadSubPackages("") | |
override def listAllPackages(): List[DotcPackageData] = _packages | |
class DotcPackageData(val debugName: String, override val dotSeparatedName: String, cp: DottyClassPath, callback: ForceEvent => Unit) extends PackageData: | |
override def toString(): String = debugName | |
def loadClasses(): List[DotcClassData] = | |
cp.classes(dotSeparatedName).toList.map(cls => | |
DotcClassData(s"$debugName:$dotSeparatedName.${cls.name}", cls, callback) | |
) | |
lazy val _classes = | |
try loadClasses() | |
finally callback(ForceEvent.Package(debugName)) | |
lazy val _byName = _classes.map(cls => cls.binaryName -> cls).toMap | |
override def listAllClassDatas(): List[DotcClassData] = _classes | |
override def getClassDataByBinaryName(binaryName: String): Option[DotcClassData] = _byName.get(binaryName) | |
class DotcClassData(val debugName: String, entry: ClassFileEntry, callback: ForceEvent => Unit) extends ClassData { | |
override def toString(): String = debugName | |
override val binaryName: String = entry.name | |
private def sibling(cls: AbstractFile): Option[AbstractFile] = | |
val dir = cls match | |
case cls: FileZipArchive#Entry => cls.parent | |
case cls: AbstractFile => cls.container | |
val name = cls.name.stripSuffix(".class") + ".tasty" | |
Option(dir.lookupName(name, directory = false)) | |
lazy val cls = entry.binary | |
lazy val tsty = cls.flatMap(sibling) | |
override def readClassFileBytes(): IArray[Byte] = | |
try IArray.unsafeFromArray(cls.get.toByteArray) | |
finally callback(ForceEvent.Class(debugName)) | |
override def hasClassFile: Boolean = cls.exists(_.exists) | |
override def readTastyFileBytes(): IArray[Byte] = | |
try IArray.unsafeFromArray(tsty.get.toByteArray) | |
finally callback(ForceEvent.Tasty(debugName)) | |
override def hasTastyFile: Boolean = tsty.exists(_.exists) | |
} | |
class LazyClasspathSuite extends munit.FunSuite: | |
def initialClasspath: DottyClassPath = | |
val ictx = contextFromClasspath(LazyClasspathSuite.classpathEntries) | |
ictx.base.platform.classPath(using ictx) | |
def contextFromClasspath(entries: Seq[String]): DottyContext = | |
val cp = entries.mkString(java.io.File.pathSeparator.nn) | |
val ictx = ContextBase().initialCtx.fresh | |
val summary = ScalacCommand.distill(Array("-classpath", cp), ictx.settings)(ictx.settingsState)(using ictx) | |
ictx.setSettings(summary.sstate) | |
val _ = Compiler().newRun(using ictx) // side effect: initializes the classpath | |
ictx | |
def makeClasspath(callback: ForceEvent => Unit): Classpath = | |
def flattenClasspath(cp: DottyClassPath): List[DottyClassPath] = cp match | |
case ag: AggregateClassPath => ag.aggregates.flatMap(flattenClasspath).toList | |
case _ => List(cp) | |
flattenClasspath(initialClasspath).map(cp => DotcEntry(cp.asClassPathString, cp, callback)) | |
test("load lazy from dotty classpath") { | |
val events = collection.mutable.ListBuffer.empty[ForceEvent] | |
val classpath = makeClasspath: event => | |
println(s"received event: $event") | |
events += event | |
given Context = Context.initialize(classpath) | |
// scala 2 classes | |
val TryClass = ctx.findTopLevelClass("scala.util.Try") | |
val SuccessClass = ctx.findTopLevelClass("scala.util.Success") | |
assert(SuccessClass.isSubClass(TryClass), clue(events.toList)) | |
// scala 3 classes | |
val TupleClass = ctx.findTopLevelClass("scala.Tuple") | |
val ConsClass = ctx.findTopLevelClass("scala.*:") | |
assert(ConsClass.isSubClass(TupleClass), clue(events.toList)) | |
// java classes | |
val ListClass = ctx.findTopLevelClass("java.util.List") | |
val ArrayListClass = ctx.findTopLevelClass("java.util.ArrayList") | |
assert(ArrayListClass.isSubClass(ListClass), clue(events.toList)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment