Skip to content

Commit 7eac235

Browse files
Merge pull request #10154 from dotty-staging/test-stdlib-from-tasty-directly-from-jar
Fix #10143 and test stdlib from TASTy directly from Jar and TASTy files
2 parents 8bb8575 + 7fab830 commit 7eac235

File tree

4 files changed

+79
-55
lines changed

4 files changed

+79
-55
lines changed

compiler/src/dotty/tools/dotc/Driver.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ class Driver {
8686
protected def fromTastySetup(fileNames0: List[String], ctx0: Context): (List[String], Context) =
8787
given Context = ctx0
8888
if (ctx0.settings.fromTasty.value) {
89+
val fromTastyIgnoreList = ctx0.settings.YfromTastyIgnoreList.value.toSet
8990
// Resolve classpath and class names of tasty files
9091
val (classPaths, classNames) = fileNames0.flatMap { name =>
9192
val path = Paths.get(name)
@@ -96,7 +97,7 @@ class Driver {
9697
Nil
9798
else if name.endsWith(".jar") then
9899
new dotty.tools.io.Jar(File(name)).toList.collect {
99-
case e if e.getName.endsWith(".tasty") =>
100+
case e if e.getName.endsWith(".tasty") && !fromTastyIgnoreList(e.getName) =>
100101
(name, e.getName.stripSuffix(".tasty").replace("/", "."))
101102
}
102103
else

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class ScalaSettings extends Settings.SettingGroup {
4545
val language: Setting[List[String]] = MultiStringSetting("-language", "feature", "Enable one or more language features.") withAbbreviation "--language"
4646
val rewrite: Setting[Option[Rewrites]] = OptionSetting[Rewrites]("-rewrite", "When used in conjunction with a `...-migration` source version, rewrites sources to migrate to new version.") withAbbreviation "--rewrite"
4747
val silentWarnings: Setting[Boolean] = BooleanSetting("-nowarn", "Silence all warnings.") withAbbreviation "--no-warnings"
48-
val fromTasty: Setting[Boolean] = BooleanSetting("-from-tasty", "Compile classes from tasty in classpath. The arguments are used as class names.") withAbbreviation "--from-tasty"
48+
val fromTasty: Setting[Boolean] = BooleanSetting("-from-tasty", "Compile classes from tasty files. The arguments are .tasty or .jar files.") withAbbreviation "--from-tasty"
4949

5050
val newSyntax: Setting[Boolean] = BooleanSetting("-new-syntax", "Require `then` and `do` in control expressions.")
5151
val oldSyntax: Setting[Boolean] = BooleanSetting("-old-syntax", "Require `(...)` around conditions.")
@@ -158,6 +158,7 @@ class ScalaSettings extends Settings.SettingGroup {
158158
val YretainTrees: Setting[Boolean] = BooleanSetting("-Yretain-trees", "Retain trees for top-level classes, accessible from ClassSymbol#tree")
159159
val Ysemanticdb: Setting[Boolean] = BooleanSetting("-Ysemanticdb", "Store information in SemanticDB.")
160160
val YshowTreeIds: Setting[Boolean] = BooleanSetting("-Yshow-tree-ids", "Uniquely tag all tree nodes in debugging output.")
161+
val YfromTastyIgnoreList: Setting[List[String]] = MultiStringSetting("-Yfrom-tasty-ignore-list", "file", "List of `tasty` files in jar files that will not be loaded when using -from-tasty")
161162

162163
val YprofileEnabled: Setting[Boolean] = BooleanSetting("-Yprofile-enabled", "Enable profiling.")
163164
val YprofileDestination: Setting[String] = StringSetting("-Yprofile-destination", "file", "Where to send profiling output - specify a file, default is to the console.", "")

compiler/src/dotty/tools/dotc/core/tasty/TastyClassName.scala

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,14 @@ class TastyClassName(bytes: Array[Byte]) {
2525
import dotty.tools.tasty.TastyFormat._
2626
def unpickle(reader: TastyReader, tastyName: NameTable): (TermName, TermName) = {
2727
import reader._
28-
def readName() = {
29-
val idx = readNat()
30-
nameAtRef(NameRef(idx))
31-
}
3228
def readNames(packageName: TermName): (TermName, TermName) = {
3329
val tag = readByte()
3430
if (tag >= firstLengthTreeTag) {
3531
val len = readNat()
3632
val end = currentAddr + len
3733
tag match {
3834
case TYPEDEF =>
39-
val className = readName()
35+
val className = reader.readName()
4036
goto(end)
4137
(packageName, className)
4238
case IMPORT | VALDEF =>
@@ -48,13 +44,26 @@ class TastyClassName(bytes: Array[Byte]) {
4844
}
4945
else tag match {
5046
case TERMREFpkg | TYPEREFpkg =>
51-
val subPackageName = readName()
47+
val subPackageName = reader.readName()
48+
readNames(subPackageName)
49+
case SHAREDtype =>
50+
val addr = reader.readAddr()
51+
val reader2 = reader.subReader(addr, reader.endAddr)
52+
val tag2 = reader2.readByte()
53+
assert(tag2 == TERMREFpkg || tag2 == TYPEREFpkg)
54+
val subPackageName = reader2.readName()
5255
readNames(subPackageName)
5356
case _ =>
5457
readNames(packageName)
5558
}
5659
}
5760
readNames(nme.EMPTY_PACKAGE)
5861
}
62+
63+
extension (reader: TastyReader) def readName() = {
64+
val idx = reader.readNat()
65+
nameAtRef(NameRef(idx))
66+
}
5967
}
68+
6069
}

stdlib-bootstrapped-tasty-tests/test/BootstrappedStdLibTASYyTest.scala

Lines changed: 60 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import dotty.tools.dotc.util.ClasspathFromClassloader
1010
import scala.quoted._
1111

1212
import java.io.File.pathSeparator
13+
import java.io.File.separator
1314

1415
class BootstrappedStdLibTASYyTest:
1516

@@ -19,17 +20,14 @@ class BootstrappedStdLibTASYyTest:
1920
@Test def testTastyInspector: Unit =
2021
loadWithTastyInspector(loadBlacklisted)
2122

23+
/** Test that we can load and compile trees from TASTy in a Jar */
24+
@Test def testFromTastyInJar: Unit =
25+
compileFromTastyInJar(loadBlacklisted.union(compileBlacklisted))
26+
2227
/** Test that we can load and compile trees from TASTy */
2328
@Test def testFromTasty: Unit =
2429
compileFromTasty(loadBlacklisted.union(compileBlacklisted))
2530

26-
@Ignore
27-
@Test def testWhiteListFromTasty: Unit =
28-
val whitelist = Set(
29-
"scala.collection.mutable.StringBuilder"
30-
)
31-
compileFromTasty(x => !whitelist(x))
32-
3331
@Test def blacklistNoDuplicates =
3432
def testDup(name: String, list: List[String], set: Set[String]) =
3533
assert(list.size == set.size,
@@ -44,14 +42,14 @@ class BootstrappedStdLibTASYyTest:
4442
"`compileBlacklist` contains names that are already in `loadBlacklist`: \n ", "\n ", "\n\n"))
4543

4644
@Test def blacklistsOnlyContainsClassesThatExist =
47-
val scalaLibJarTastyClassNamesSet = scalaLibJarTastyClassNames.toSet
45+
val scalaLibTastyPathsSet = scalaLibTastyPaths.toSet
4846
val intersection = loadBlacklisted & compileBlacklisted
49-
assert(loadBlacklisted.diff(scalaLibJarTastyClassNamesSet).isEmpty,
50-
loadBlacklisted.diff(scalaLibJarTastyClassNamesSet).mkString(
51-
"`loadBlacklisted` contains names that are not in `scalaLibJarTastyClassNames`: \n ", "\n ", "\n\n"))
52-
assert(compileBlacklisted.diff(scalaLibJarTastyClassNamesSet).isEmpty,
53-
compileBlacklisted.diff(scalaLibJarTastyClassNamesSet).mkString(
54-
"`loadBlacklisted` contains names that are not in `scalaLibJarTastyClassNames`: \n ", "\n ", "\n\n"))
47+
assert(loadBlacklisted.diff(scalaLibTastyPathsSet).isEmpty,
48+
loadBlacklisted.diff(scalaLibTastyPathsSet).mkString(
49+
"`loadBlacklisted` contains names that are not in `scalaLibTastyPaths`: \n ", "\n ", "\n\n"))
50+
assert(compileBlacklisted.diff(scalaLibTastyPathsSet).isEmpty,
51+
compileBlacklisted.diff(scalaLibTastyPathsSet).mkString(
52+
"`loadBlacklisted` contains names that are not in `scalaLibTastyPaths`: \n ", "\n ", "\n\n"))
5553

5654
@Ignore
5755
@Test def testLoadBacklistIsMinimal =
@@ -79,7 +77,7 @@ class BootstrappedStdLibTASYyTest:
7977
val blacklist = blacklist0 - notBlacklisted
8078
println(s"Trying withouth $notBlacklisted in the blacklist (${i+1}/$size)")
8179
try {
82-
compileFromTasty(blacklist)
80+
compileFromTastyInJar(blacklist)
8381
shouldBeWhitelisted = notBlacklisted :: shouldBeWhitelisted
8482
}
8583
catch {
@@ -92,44 +90,59 @@ end BootstrappedStdLibTASYyTest
9290

9391
object BootstrappedStdLibTASYyTest:
9492

95-
val scalaLibJarPath = System.getProperty("dotty.scala.library")
93+
def scalaLibJarPath = System.getProperty("dotty.scala.library")
94+
def scalaLibClassesPath =
95+
java.nio.file.Paths.get(scalaLibJarPath).getParent.resolve("classes").normalize
9696

97-
val scalaLibJarTastyClassNames = {
98-
val scalaLibJar = Jar(new File(java.nio.file.Paths.get(scalaLibJarPath)))
99-
scalaLibJar.toList.map(_.toString).filter(_.endsWith(".tasty"))
100-
.map(_.stripSuffix(".tasty").replace("/", "."))
101-
.sorted
102-
}
97+
val scalaLibTastyPaths =
98+
new Directory(scalaLibClassesPath).deepFiles
99+
.filter(_.`extension` == "tasty")
100+
.map(_.normalize.path.stripPrefix(scalaLibClassesPath.toString + "/"))
101+
.toList
103102

104-
def loadWithTastyInspector(blacklisted: String => Boolean): Unit =
103+
def loadWithTastyInspector(blacklisted: Set[String]): Unit =
105104
val inspector = new scala.tasty.inspector.TastyInspector {
106105
def processCompilationUnit(using QuoteContext)(root: qctx.reflect.Tree): Unit =
107106
root.showExtractors // Check that we can traverse the full tree
108107
()
109108
}
110-
val classNames = scalaLibJarTastyClassNames.filterNot(blacklisted)
111-
val hasErrors = inspector.inspectTastyFilesInJar(scalaLibJarPath)
109+
val tastyFiles = scalaLibTastyPaths.filterNot(blacklisted)
110+
val hasErrors = inspector.inspectTastyFiles(tastyFiles.map(x => scalaLibClassesPath.resolve(x).toString))
112111
assert(!hasErrors, "Errors reported while loading from TASTy")
113112

114-
def compileFromTasty(blacklisted: String => Boolean): Unit = {
113+
def compileFromTastyInJar(blacklisted: Set[String]): Unit = {
115114
val driver = new dotty.tools.dotc.Driver
116-
val currentClasspath = ClasspathFromClassloader(getClass.getClassLoader)
117-
val classNames = scalaLibJarTastyClassNames.filterNot(blacklisted)
115+
val yFromTastyBlacklist =
116+
blacklisted.mkString("-Yfrom-tasty-ignore-list:", ",", "")
118117
val args = Array(
119-
"-classpath", s"$scalaLibJarPath$pathSeparator$currentClasspath",
118+
"-classpath", ClasspathFromClassloader(getClass.getClassLoader),
120119
"-from-tasty",
121-
"-nowarn"
122-
) ++ classNames
120+
"-nowarn",
121+
yFromTastyBlacklist,
122+
scalaLibJarPath,
123+
)
123124
val reporter = driver.process(args)
124125
assert(reporter.errorCount == 0, "Errors while re-compiling")
125126
}
126127

127-
/** List of classes that cannot be loaded from TASTy */
128+
def compileFromTasty(blacklisted: Set[String]): Unit = {
129+
val driver = new dotty.tools.dotc.Driver
130+
val tastyFiles = scalaLibTastyPaths.filterNot(blacklisted)
131+
val args = Array(
132+
"-classpath", ClasspathFromClassloader(getClass.getClassLoader),
133+
"-from-tasty",
134+
"-nowarn",
135+
) ++ tastyFiles.map(x => scalaLibClassesPath.resolve(x).toString)
136+
val reporter = driver.process(args)
137+
assert(reporter.errorCount == 0, "Errors while re-compiling")
138+
}
139+
140+
/** List of tasty files that cannot be loaded from TASTy */
128141
def loadBlacklist = List[String](
129142
// No issues :)
130143
)
131144

132-
/** List of classes that cannot be recompilied from TASTy */
145+
/** List of tasty files that cannot be recompilied from TASTy */
133146
def compileBlacklist = List[String](
134147
// See #10048
135148
// failed: java.lang.AssertionError: assertion failed: class Boolean
@@ -139,22 +152,22 @@ object BootstrappedStdLibTASYyTest:
139152
// at dotty.tools.backend.jvm.BCodeHelpers$BCInnerClassGen.getClassBTypeAndRegisterInnerClass$(BCodeHelpers.scala:210)
140153
// at dotty.tools.backend.jvm.BCodeSkelBuilder$PlainSkelBuilder.getClassBTypeAndRegisterInnerClass(BCodeSkelBuilder.scala:62)
141154
// at dotty.tools.backend.jvm.BCodeHelpers$BCInnerClassGen.internalName(BCodeHelpers.scala:237)
142-
"scala.Array",
143-
"scala.Boolean",
144-
"scala.Byte",
145-
"scala.Char",
146-
"scala.Double",
147-
"scala.Float",
148-
"scala.Int",
149-
"scala.Long",
150-
"scala.Short",
151-
"scala.Unit",
152-
)
153-
154-
/** Set of classes that cannot be loaded from TASTy */
155+
"scala/Array.tasty",
156+
"scala/Boolean.tasty",
157+
"scala/Byte.tasty",
158+
"scala/Char.tasty",
159+
"scala/Double.tasty",
160+
"scala/Float.tasty",
161+
"scala/Int.tasty",
162+
"scala/Long.tasty",
163+
"scala/Short.tasty",
164+
"scala/Unit.tasty",
165+
).map(_.replace("/", separator))
166+
167+
/** Set of tasty files that cannot be loaded from TASTy */
155168
def loadBlacklisted = loadBlacklist.toSet
156169

157-
/** Set of classes that cannot be recompilied from TASTy */
170+
/** Set of tasty files that cannot be recompilied from TASTy */
158171
def compileBlacklisted = compileBlacklist.toSet
159172

160173
end BootstrappedStdLibTASYyTest

0 commit comments

Comments
 (0)