Skip to content

Commit 7e4ac30

Browse files
committed
Add FromTasty compiler tests
Fixes FromTasty when info already loaded
1 parent 8f9c1b1 commit 7e4ac30

File tree

10 files changed

+203
-37
lines changed

10 files changed

+203
-37
lines changed

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package dotty.tools
22
package dotc
33

4-
import dotty.tools.dotc.core.Types.Type
5-
import dotty.tools.dotc.core.tasty.{TastyUnpickler, TastyBuffer, TastyPickler}
64
import util.SourceFile
75
import ast.{tpd, untpd}
86
import dotty.tools.dotc.ast.tpd.{ Tree, TreeTraverser }
@@ -28,6 +26,7 @@ object CompilationUnit {
2826

2927
/** Make a compilation unit for top class `clsd` with the contends of the `unpickled` */
3028
def mkCompilationUnit(clsd: ClassDenotation, unpickled: Tree, forceTrees: Boolean)(implicit ctx: Context): CompilationUnit = {
29+
assert(!unpickled.isEmpty, unpickled)
3130
val unit1 = new CompilationUnit(new SourceFile(clsd.symbol.sourceFile, Seq()))
3231
unit1.tpdTree = unpickled
3332
if (forceTrees)

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

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,28 @@ object FromTasty extends Driver {
6969

7070
def readTASTY(unit: CompilationUnit)(implicit ctx: Context): CompilationUnit = unit match {
7171
case unit: TASTYCompilationUnit =>
72+
assert(ctx.settings.YretainTrees.value)
7273
val className = unit.className.toTypeName
73-
val clsd = ctx.base.staticRef(className)
74-
def cannotUnpickle(reason: String) = {
75-
ctx.error(s"class $className cannot be unpickled because $reason")
76-
unit
77-
}
78-
clsd match {
74+
ctx.base.staticRef(className) match {
7975
case clsd: ClassDenotation =>
80-
clsd.infoOrCompleter match {
76+
def cannotUnpickle(reason: String) =
77+
ctx.error(s"class $className cannot be unpickled because $reason")
78+
def tryToLoad = clsd.infoOrCompleter match {
8179
case info: ClassfileLoader =>
8280
info.load(clsd)
83-
val unpickled = clsd.symbol.asClass.tree
84-
if (unpickled != null) CompilationUnit.mkCompilationUnit(clsd, unpickled, forceTrees = true)
85-
else cannotUnpickle(s"its class file ${info.classfile} does not have a TASTY attribute")
81+
Option(clsd.symbol.asClass.tree).orElse {
82+
cannotUnpickle(s"its class file ${info.classfile} does not have a TASTY attribute")
83+
None
84+
}
85+
8686
case info =>
8787
cannotUnpickle(s"its info of type ${info.getClass} is not a ClassfileLoader")
88+
None
8889
}
90+
Option(clsd.symbol.asClass.tree).orElse(tryToLoad)
91+
.map(unpickled => CompilationUnit.mkCompilationUnit(clsd, unpickled, forceTrees = true))
92+
.getOrElse(unit)
93+
8994
case _ =>
9095
ctx.error(s"class not found: $className")
9196
unit
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package dotty
2+
package tools
3+
package dotc
4+
5+
import org.junit.{AfterClass, Test}
6+
import vulpix._
7+
8+
import scala.concurrent.duration._
9+
10+
class FromTastyTests extends ParallelTesting {
11+
import TestConfiguration._
12+
import FromTastyTests._
13+
14+
// Test suite configuration --------------------------------------------------
15+
16+
def maxDuration = 30.seconds
17+
def numberOfSlaves = 5
18+
def safeMode = Properties.testsSafeMode
19+
def isInteractive = SummaryReport.isInteractive
20+
def testFilter = Properties.testsFilter
21+
22+
23+
@Test def posTestFromTasty: Unit = {
24+
implicit val testGroup: TestGroup = TestGroup("posTestFromTasty")
25+
val (step1, step2) = {
26+
// compileTastyInDir("../tests/pos", defaultOptions) + // FIXME
27+
compileTastyInDir("../tests/pos-from-tasty", defaultOptions)
28+
}
29+
step1.keepOutput.checkCompile() // Compile all files to generate the class files with tasty
30+
step2.keepOutput.checkCompile() // Compile from tasty
31+
(step1 + step2).delete()
32+
}
33+
34+
@Test def runTestFromTasty: Unit = {
35+
implicit val testGroup: TestGroup = TestGroup("runTestFromTasty")
36+
val (step1, step2) = {
37+
// compileTastyInDir("../tests/run", defaultOptions) + // FIXME
38+
compileTastyInDir("../tests/run-from-tasty", defaultOptions)
39+
}
40+
step1.keepOutput.checkCompile() // Compile all files to generate the class files with tasty
41+
step2.keepOutput.checkRuns() // Compile from tasty and run the result
42+
(step1 + step2).delete()
43+
}
44+
45+
private implicit class tastyCompilationTuples(tup: (CompilationTest, CompilationTest)) {
46+
def +(that: (CompilationTest, CompilationTest)): (CompilationTest, CompilationTest) =
47+
(tup._1 + that._1, tup._2 + that._2)
48+
}
49+
}
50+
51+
object FromTastyTests {
52+
implicit val summaryReport: SummaryReporting = new SummaryReport
53+
@AfterClass def cleanup(): Unit = summaryReport.echoSummary()
54+
}

compiler/test/dotty/tools/vulpix/ParallelTesting.scala

Lines changed: 90 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ trait ParallelTesting extends RunnerOrchestration { self =>
132132
name: String,
133133
files: Array[JFile],
134134
flags: TestFlags,
135-
outDir: JFile
135+
outDir: JFile,
136+
fromTasty: Boolean = false
136137
) extends TestSource {
137138
def sourceFiles: Array[JFile] = files.filter(isSourceFile)
138139

@@ -213,7 +214,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
213214
private val filteredSources =
214215
if (!testFilter.isDefined) testSources
215216
else testSources.filter {
216-
case JointCompilationSource(_, files, _, _) =>
217+
case JointCompilationSource(_, files, _, _, _) =>
217218
files.exists(file => file.getAbsolutePath.contains(testFilter.get))
218219
case SeparateCompilationSource(_, dir, _, _) =>
219220
dir.getAbsolutePath.contains(testFilter.get)
@@ -336,15 +337,6 @@ trait ParallelTesting extends RunnerOrchestration { self =>
336337

337338
val files: Array[JFile] = files0.flatMap(flattenFiles)
338339

339-
def addOutDir(xs: Array[String]): Array[String] = {
340-
val (beforeCp, cpAndAfter) = xs.toList.span(_ != "-classpath")
341-
if (cpAndAfter.nonEmpty) {
342-
val (cp :: cpArg :: rest) = cpAndAfter
343-
(beforeCp ++ (cp :: (cpArg + s":${targetDir.getAbsolutePath}") :: rest)).toArray
344-
}
345-
else (beforeCp ++ ("-classpath" :: targetDir.getAbsolutePath :: Nil)).toArray
346-
}
347-
348340
def compileWithJavac(fs: Array[String]) = if (fs.nonEmpty) {
349341
val fullArgs = Array(
350342
"javac",
@@ -379,7 +371,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
379371
}
380372
}
381373

382-
val allArgs = addOutDir(flags.all)
374+
val allArgs = flags.withClasspath(targetDir.getAbsolutePath).all
383375

384376
// Compile with a try to catch any StackTrace generated by the compiler:
385377
try {
@@ -400,6 +392,34 @@ trait ParallelTesting extends RunnerOrchestration { self =>
400392
reporter
401393
}
402394

395+
protected def compileFromTasty(files0: Array[JFile], flags0: TestFlags, suppressErrors: Boolean, targetDir: JFile): TestReporter = {
396+
val tastyOutput = new JFile(targetDir.getPath + "_from-tasty")
397+
tastyOutput.mkdir()
398+
val flags = flags0 and ("-d", tastyOutput.getAbsolutePath)
399+
400+
def hasTastyFileToClassName(f: JFile): String =
401+
targetDir.toPath.relativize(f.toPath).toString.dropRight(".hasTasty".length).replace('/', '.')
402+
val classes = flattenFiles(targetDir).filter(isHasTastyFile).map(hasTastyFileToClassName)
403+
404+
val reporter =
405+
TestReporter.reporter(realStdout, logLevel =
406+
if (suppressErrors || suppressAllOutput) ERROR + 1 else ERROR)
407+
408+
val driver = dotc.FromTasty
409+
410+
val allArgs = flags.withClasspath(files0.map(_.getAbsolutePath).mkString(":")).all
411+
412+
// Compile with a try to catch any StackTrace generated by the compiler:
413+
try {
414+
driver.process(allArgs ++ classes, reporter = reporter)
415+
}
416+
catch {
417+
case NonFatal(ex) => reporter.logStackTrace(ex)
418+
}
419+
420+
reporter
421+
}
422+
403423
private[ParallelTesting] def executeTestSuite(): this.type = {
404424
assert(_testSourcesCompleted == 0, "not allowed to re-use a `CompileRun`")
405425

@@ -440,24 +460,30 @@ trait ParallelTesting extends RunnerOrchestration { self =>
440460

441461
this
442462
}
463+
464+
/** Returns all files in directory or the file if not a directory */
465+
private def flattenFiles(f: JFile): Array[JFile] =
466+
if (f.isDirectory) f.listFiles.flatMap(flattenFiles)
467+
else Array(f)
443468
}
444469

445470
private final class PosTest(testSources: List[TestSource], times: Int, threadLimit: Option[Int], suppressAllOutput: Boolean)(implicit summaryReport: SummaryReporting)
446471
extends Test(testSources, times, threadLimit, suppressAllOutput) {
447472
protected def encapsulatedCompilation(testSource: TestSource) = new LoggedRunnable {
448473
def checkTestSource(): Unit = tryCompile(testSource) {
449474
testSource match {
450-
case testSource @ JointCompilationSource(_, files, flags, outDir) => {
451-
val reporter = compile(testSource.sourceFiles, flags, false, outDir)
475+
case testSource @ JointCompilationSource(_, files, flags, outDir, fromTasty) =>
476+
val reporter =
477+
if (fromTasty) compileFromTasty(testSource.sourceFiles, flags, false, outDir)
478+
else compile(testSource.sourceFiles, flags, false, outDir)
452479
registerCompletion(reporter.errorCount)
453480

454481
if (reporter.compilerCrashed || reporter.errorCount > 0) {
455482
logReporterContents(reporter)
456483
logBuildInstructions(reporter, testSource, reporter.errorCount, reporter.warningCount)
457484
}
458-
}
459485

460-
case testSource @ SeparateCompilationSource(_, dir, flags, outDir) => {
486+
case testSource @ SeparateCompilationSource(_, dir, flags, outDir) =>
461487
val reporters = testSource.compilationGroups.map(files => compile(files, flags, false, outDir))
462488
val compilerCrashed = reporters.exists(_.compilerCrashed)
463489
val errorCount = reporters.foldLeft(0) { (acc, reporter) =>
@@ -475,7 +501,6 @@ trait ParallelTesting extends RunnerOrchestration { self =>
475501
reporters.foreach(logReporterContents)
476502
logBuildInstructions(reporters.head, testSource, errorCount, warningCount)
477503
}
478-
}
479504
}
480505
}
481506
}
@@ -546,7 +571,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
546571
protected def encapsulatedCompilation(testSource: TestSource) = new LoggedRunnable {
547572
def checkTestSource(): Unit = tryCompile(testSource) {
548573
val (compilerCrashed, errorCount, warningCount, verifier: Function0[Unit]) = testSource match {
549-
case testSource @ JointCompilationSource(_, files, flags, outDir) => {
574+
case testSource @ JointCompilationSource(_, files, flags, outDir, fromTasty) =>
550575
val checkFile = files.flatMap { file =>
551576
if (file.isDirectory) Nil
552577
else {
@@ -556,17 +581,18 @@ trait ParallelTesting extends RunnerOrchestration { self =>
556581
else Nil
557582
}
558583
}.headOption
559-
val reporter = compile(testSource.sourceFiles, flags, false, outDir)
584+
val reporter =
585+
if (fromTasty) compileFromTasty(testSource.sourceFiles, flags, false, outDir)
586+
else compile(testSource.sourceFiles, flags, false, outDir)
560587

561588
if (reporter.compilerCrashed || reporter.errorCount > 0) {
562589
logReporterContents(reporter)
563590
logBuildInstructions(reporter, testSource, reporter.errorCount, reporter.warningCount)
564591
}
565592

566593
(reporter.compilerCrashed, reporter.errorCount, reporter.warningCount, () => verifyOutput(checkFile, outDir, testSource, reporter.warningCount))
567-
}
568594

569-
case testSource @ SeparateCompilationSource(_, dir, flags, outDir) => {
595+
case testSource @ SeparateCompilationSource(_, dir, flags, outDir) =>
570596
val checkFile = new JFile(dir.getAbsolutePath.reverse.dropWhile(_ == '/').reverse + ".check")
571597
val reporters = testSource.compilationGroups.map(compile(_, flags, false, outDir))
572598
val compilerCrashed = reporters.exists(_.compilerCrashed)
@@ -584,7 +610,6 @@ trait ParallelTesting extends RunnerOrchestration { self =>
584610
}
585611

586612
(compilerCrashed, errorCount, warningCount, () => verifyOutput(Some(checkFile), outDir, testSource, warningCount))
587-
}
588613
}
589614

590615
if (!compilerCrashed && errorCount == 0) verifier()
@@ -655,7 +680,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
655680
}
656681

657682
val (compilerCrashed, expectedErrors, actualErrors, hasMissingAnnotations, errorMap) = testSource match {
658-
case testSource @ JointCompilationSource(_, files, flags, outDir) => {
683+
case testSource @ JointCompilationSource(_, files, flags, outDir, fromTasty) =>
659684
val sourceFiles = testSource.sourceFiles
660685
val (errorMap, expectedErrors) = getErrorMapAndExpectedCount(sourceFiles)
661686
val reporter = compile(sourceFiles, flags, true, outDir)
@@ -665,7 +690,6 @@ trait ParallelTesting extends RunnerOrchestration { self =>
665690
logReporterContents(reporter)
666691

667692
(reporter.compilerCrashed, expectedErrors, actualErrors, () => getMissingExpectedErrors(errorMap, reporter.errors), errorMap)
668-
}
669693

670694
case testSource @ SeparateCompilationSource(_, dir, flags, outDir) => {
671695
val compilationGroups = testSource.compilationGroups
@@ -941,7 +965,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
941965
*/
942966
def copyToTarget(): CompilationTest = new CompilationTest (
943967
targets.map {
944-
case target @ JointCompilationSource(_, files, _, outDir) =>
968+
case target @ JointCompilationSource(_, files, _, outDir, _) =>
945969
target.copy(files = files.map(copyToDir(outDir,_)))
946970
case target @ SeparateCompilationSource(_, dir, _, outDir) =>
947971
target.copy(dir = copyToDir(outDir, dir))
@@ -1137,6 +1161,44 @@ trait ParallelTesting extends RunnerOrchestration { self =>
11371161
new CompilationTest(targets)
11381162
}
11391163

1164+
/** This function compiles the files and folders contained within directory
1165+
* `f` in a specific way. Once compiled, they are recompiled/run from tasty as sources.
1166+
*
1167+
* - Each file is compiled separately as a single compilation run
1168+
* - Each directory is compiled as a `SeparateCompilationTaret`, in this
1169+
* target all files are grouped according to the file suffix `_X` where `X`
1170+
* is a number. These groups are then ordered in ascending order based on
1171+
* the value of `X` and each group is compiled one after the other.
1172+
*
1173+
* For this function to work as expected, we use the same convention for
1174+
* directory layout as the old partest. That is:
1175+
*
1176+
* - Single files can have an associated check-file with the same name (but
1177+
* with file extension `.check`)
1178+
* - Directories can have an associated check-file, where the check file has
1179+
* the same name as the directory (with the file extension `.check`)
1180+
*/
1181+
def compileTastyInDir(f: String, flags0: TestFlags)(implicit testGroup: TestGroup): (CompilationTest, CompilationTest) = {
1182+
val outDir = defaultOutputDir + testGroup + "/"
1183+
val flags = flags0 and "-Yretain-trees"
1184+
val sourceDir = new JFile(f)
1185+
checkRequirements(f, sourceDir, outDir)
1186+
1187+
val (dirs, files) = compilationTargets(sourceDir)
1188+
1189+
val targets =
1190+
files.map { f =>
1191+
val classpath = createOutputDirsForFile(f, sourceDir, outDir)
1192+
JointCompilationSource(testGroup.name, Array(f), flags.withClasspath(classpath.getPath), classpath, fromTasty = true)
1193+
}
1194+
// TODO add SeparateCompilationSource from tasty?
1195+
1196+
// Create a CompilationTest and let the user decide whether to execute a pos or a neg test
1197+
val generateClassFiles = compileFilesInDir(f, flags0)
1198+
(generateClassFiles, new CompilationTest(targets))
1199+
}
1200+
1201+
11401202
/** This function behaves similar to `compileFilesInDir` but it ignores
11411203
* sub-directories and as such, does **not** perform separate compilation
11421204
* tests.
@@ -1165,4 +1227,7 @@ object ParallelTesting {
11651227
val name = f.getName
11661228
name.endsWith(".scala") || name.endsWith(".java")
11671229
}
1230+
1231+
def isHasTastyFile(f: JFile): Boolean =
1232+
f.getName.endsWith(".hasTasty")
11681233
}

compiler/test/dotty/tools/vulpix/TestFlags.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ final case class TestFlags(
1111
def without(flags: String*): TestFlags =
1212
TestFlags(defaultClassPath, runClassPath, options diff flags)
1313

14+
def withClasspath(classPath: String): TestFlags =
15+
TestFlags(s"$defaultClassPath:$classPath", runClassPath, options)
16+
1417
def all: Array[String] = Array("-classpath", defaultClassPath) ++ options
1518
}
1619

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package foo
2+
3+
class A
4+
5+
class B extends A

tests/run-from-tasty/innerClass.check

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
foo.A
2+
foo.A$B

tests/run-from-tasty/innerClass.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package foo {
2+
3+
class A {
4+
class B
5+
}
6+
7+
}
8+
9+
class Test
10+
object Test {
11+
def main(args: Array[String]): Unit = {
12+
val a = new foo.A
13+
println(a.getClass.getName)
14+
println(new a.B().getClass.getName)
15+
}
16+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
foo.A
2+
foo.B

0 commit comments

Comments
 (0)