From f77ac08102c6a36e2476745b1a8f42ccf0097cbd Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 6 Apr 2018 10:03:46 +0200 Subject: [PATCH 1/9] Test compilation of decompiled code --- .../decompiler/DecompilationPrinter.scala | 14 ++++------- .../dotty/tools/dotc/decompiler/Main.scala | 5 ++-- .../dotc/printing/DecompilerPrinter.scala | 6 ++++- .../tools/dotc/printing/RefinedPrinter.scala | 3 ++- .../dotty/tools/dotc/FromTastyTests.scala | 24 ++++++++++++------- .../dotty/tools/vulpix/ParallelTesting.scala | 24 ++++++++++++------- tests/pos/lambda.decompiled | 7 ++---- tests/pos/methodTypes.decompiled | 7 ++---- tests/pos/simpleCaseObject.decompiled | 9 +++---- tests/pos/simpleClass-2.decompiled | 12 +++------- tests/pos/simpleClass.decompiled | 12 +++------- tests/pos/simpleDoWhile.decompiled | 7 ++---- tests/pos/simpleWhile.decompiled | 7 ++---- tests/run/puzzle.decompiled | 9 +++---- 14 files changed, 65 insertions(+), 81 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/decompiler/DecompilationPrinter.scala b/compiler/src/dotty/tools/dotc/decompiler/DecompilationPrinter.scala index 51533640a3bb..d3a855bd8949 100644 --- a/compiler/src/dotty/tools/dotc/decompiler/DecompilationPrinter.scala +++ b/compiler/src/dotty/tools/dotc/decompiler/DecompilationPrinter.scala @@ -24,7 +24,7 @@ class DecompilationPrinter extends Phase { var os: OutputStream = null var ps: PrintStream = null try { - os = File(outputDir + ".decompiled").outputStream(append = true) + os = File(outputDir + "/decompiled.scala").outputStream(append = true) ps = new PrintStream(os) printToOutput(ps) } finally { @@ -38,21 +38,15 @@ class DecompilationPrinter extends Phase { val unit = ctx.compilationUnit val pageWidth = ctx.settings.pageWidth.value val printLines = ctx.settings.printLines.value - val doubleLine = "=" * pageWidth - val line = "-" * pageWidth - - out.println(doubleLine) - out.println(unit.source) - out.println(line) + out.println(s"/** Decompiled from $unit */") val printer = new DecompilerPrinter(ctx) - out.println(printer.toText(unit.tpdTree).mkString(pageWidth, printLines)) - out.println(line) if (ctx.settings.printTasty.value) { + out.println("/*") new TastyPrinter(unit.pickled.head._2).printContents() - out.println(line) + out.println("*/") } } } diff --git a/compiler/src/dotty/tools/dotc/decompiler/Main.scala b/compiler/src/dotty/tools/dotc/decompiler/Main.scala index c91afc45fb34..3d4ebb214c14 100644 --- a/compiler/src/dotty/tools/dotc/decompiler/Main.scala +++ b/compiler/src/dotty/tools/dotc/decompiler/Main.scala @@ -14,13 +14,14 @@ object Main extends dotc.Driver { assert(ctx.settings.fromTasty.value) val outputDir = ctx.settings.outputDir.value if (outputDir != ".") - Files.deleteIfExists(Paths.get(outputDir + ".decompiled")) + Files.deleteIfExists(Paths.get(outputDir + "/decompiled.scala")) new TASTYDecompiler } override def setup(args0: Array[String], rootCtx: Context): (List[String], Context) = { var args = args0.filter(a => a != "-decompile") - args = if (args.contains("-from-tasty")) args else "-from-tasty" +: args + if (!args.contains("-from-tasty")) args = "-from-tasty" +: args + if (args.contains("-d")) args = "-color:never" +: args super.setup(args, rootCtx) } } diff --git a/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala b/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala index 288d07c30ff3..e33e9468341a 100644 --- a/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala @@ -55,7 +55,11 @@ class DecompilerPrinter(_ctx: Context) extends RefinedPrinter(_ctx) { } override protected def toTextTemplate(impl: Template, ofNew: Boolean = false): Text = { - val impl1 = impl.copy(parents = impl.parents.filterNot(_.symbol.maybeOwner == defn.ObjectClass)) + def filter(sym: Symbol): Boolean = { + sym.maybeOwner == defn.ObjectClass || + (sym == defn.ProductClass && impl.symbol.owner.is(Case)) + } + val impl1 = impl.copy(parents = impl.parents.filterNot(p => filter(p.symbol))) super.toTextTemplate(impl1, ofNew) } diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 6500693da068..7d8da3d0a2cc 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -640,7 +640,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val tparamsTxt = withEnclosingDef(constr) { tparamsText(tparams) } val primaryConstrs = if (constr.rhs.isEmpty) Nil else constr :: Nil val prefix: Text = - if (vparamss.isEmpty || primaryConstrs.nonEmpty) tparamsTxt + if (constr.symbol.owner.is(Module)) " " + else if (vparamss.isEmpty || primaryConstrs.nonEmpty) tparamsTxt else { var modsText = modText(constr.mods, "") if (!modsText.isEmpty) modsText = " " ~ modsText diff --git a/compiler/test/dotty/tools/dotc/FromTastyTests.scala b/compiler/test/dotty/tools/dotc/FromTastyTests.scala index 750245d464a2..b89d58ef7332 100644 --- a/compiler/test/dotty/tools/dotc/FromTastyTests.scala +++ b/compiler/test/dotty/tools/dotc/FromTastyTests.scala @@ -26,7 +26,7 @@ class FromTastyTests extends ParallelTesting { // > dotc -Ythrough-tasty -Ycheck:all implicit val testGroup: TestGroup = TestGroup("posTestFromTasty") - val (step1, step2, step3) = compileTastyInDir("tests/pos", defaultOptions, + val (step1, step2, step3, step4) = compileTastyInDir("tests/pos", defaultOptions, blacklist = Set( // Wrong number of arguments (only on bootstrapped) "i3130b.scala", @@ -36,12 +36,16 @@ class FromTastyTests extends ParallelTesting { // MatchError in SymDenotation.sourceModule on a ThisType "t3612.scala", + ), + recompileBlacklist = Set( + "simpleCaseObject" ) ) step1.checkCompile() // Compile all files to generate the class files with tasty step2.checkCompile() // Compile from tasty step3.checkCompile() // Decompile from tasty - (step1 + step2 + step3).delete() + step4.checkCompile() // Recompile decompiled code + (step1 + step2 + step3 + step4).delete() } @Test def runTestFromTasty: Unit = { @@ -51,16 +55,20 @@ class FromTastyTests extends ParallelTesting { // > dotr Test implicit val testGroup: TestGroup = TestGroup("runTestFromTasty") - val (step1, step2, step3) = compileTastyInDir("tests/run", defaultOptions, - blacklist = Set( - // Closure type miss match - "eff-dependent.scala", - ) + val (step1, step2, step3, step4) = compileTastyInDir("tests/run", defaultOptions, + blacklist = Set( + // Closure type miss match + "eff-dependent.scala", + ), + recompileBlacklist = Set( + "puzzle" + ) ) step1.checkCompile() // Compile all files to generate the class files with tasty step2.checkRuns() // Compile from tasty and run the result step3.checkCompile() // Decompile from tasty - (step1 + step2 + step3).delete() + step4.checkCompile() // Recompile decompiled code + (step1 + step2 + step3 + step4).delete() } private implicit class tastyCompilationTuples(tup: (CompilationTest, CompilationTest)) { diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index 27a40306ecd5..5ce232b95f41 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -425,16 +425,17 @@ trait ParallelTesting extends RunnerOrchestration { self => reporter } - protected def decompile(flags0: TestFlags, suppressErrors: Boolean, targetDir: JFile): TestReporter = { - val decompilationOutput = new JFile(targetDir.getPath) - decompilationOutput.mkdir() + protected def decompile(flags0: TestFlags, suppressErrors: Boolean, targetDir0: JFile): TestReporter = { + val targetDir = new JFile(targetDir0.getParent + "_decompiled") + val decompilationOutput = new JFile(targetDir + "/" + targetDir0.getName) + decompilationOutput.mkdirs() val flags = flags0 and ("-d", decompilationOutput.getAbsolutePath) and "-decompile" and "-pagewidth" and "80" def hasTastyFileToClassName(f: JFile): String = - targetDir.toPath.relativize(f.toPath).toString.dropRight(".hasTasty".length).replace('/', '.') - val classes = flattenFiles(targetDir).filter(isHasTastyFile).map(hasTastyFileToClassName).sorted + targetDir0.toPath.relativize(f.toPath).toString.dropRight(".hasTasty".length).replace('/', '.') + val classes = flattenFiles(targetDir0).filter(isHasTastyFile).map(hasTastyFileToClassName).sorted val reporter = TestReporter.reporter(realStdout, logLevel = @@ -522,7 +523,7 @@ trait ParallelTesting extends RunnerOrchestration { self => checkFileOpt match { case Some(checkFile) => val stripTrailingWhitespaces = "(.*\\S|)\\s+".r - val output = Source.fromFile(outDir + ".decompiled").getLines().map {line => + val output = Source.fromFile(outDir.getParent + "_decompiled/" + outDir.getName + "/decompiled.scala").getLines().map {line => stripTrailingWhitespaces.unapplySeq(line).map(_.head).getOrElse(line) }.mkString("\n") @@ -1257,8 +1258,8 @@ trait ParallelTesting extends RunnerOrchestration { self => * Tests in the first part of the tuple must be executed before the second. * Both testsRequires explicit delete(). */ - def compileTastyInDir(f: String, flags0: TestFlags, blacklist: Set[String] = Set.empty)( - implicit testGroup: TestGroup): (CompilationTest, CompilationTest, CompilationTest) = { + def compileTastyInDir(f: String, flags0: TestFlags, blacklist: Set[String], recompileBlacklist: Set[String])( + implicit testGroup: TestGroup): (CompilationTest, CompilationTest, CompilationTest, CompilationTest) = { val outDir = defaultOutputDir + testGroup + "/" val flags = flags0 and "-Yretain-trees" val sourceDir = new JFile(f) @@ -1284,10 +1285,15 @@ trait ParallelTesting extends RunnerOrchestration { self => // Create a CompilationTest and let the user decide whether to execute a pos or a neg test val generateClassFiles = compileFilesInDir(f, flags0, blacklist) + val decompilationDir = outDir + sourceDir.getName + "_decompiled" + new JFile(decompilationDir).mkdirs() + val recompileDecompiled = compileFilesInDir(decompilationDir, flags0, recompileBlacklist) + ( generateClassFiles.keepOutput, new CompilationTest(targets).keepOutput, - new CompilationTest(targets2).keepOutput + new CompilationTest(targets2).keepOutput, + recompileDecompiled.keepOutput ) } diff --git a/tests/pos/lambda.decompiled b/tests/pos/lambda.decompiled index f610dc4481a4..d8f61efaa99a 100644 --- a/tests/pos/lambda.decompiled +++ b/tests/pos/lambda.decompiled @@ -1,6 +1,4 @@ -================================================================================ -out/posTestFromTasty/pos/lambda/foo/Foo.class --------------------------------------------------------------------------------- +/** Decompiled from out/posTestFromTasty/pos/lambda/foo/Foo.class */ package foo { class Foo() { { @@ -11,5 +9,4 @@ package foo { } val a: Int => Int = (x: Int) => x.*(x) } -} --------------------------------------------------------------------------------- \ No newline at end of file +} \ No newline at end of file diff --git a/tests/pos/methodTypes.decompiled b/tests/pos/methodTypes.decompiled index 40cc677c5c03..da19e193f895 100644 --- a/tests/pos/methodTypes.decompiled +++ b/tests/pos/methodTypes.decompiled @@ -1,9 +1,6 @@ -================================================================================ -out/posTestFromTasty/pos/methodTypes/Foo.class --------------------------------------------------------------------------------- +/** Decompiled from out/posTestFromTasty/pos/methodTypes/Foo.class */ class Foo() { val x: Int = 1 def y: Int = 2 def z(): Int = 3 -} --------------------------------------------------------------------------------- \ No newline at end of file +} \ No newline at end of file diff --git a/tests/pos/simpleCaseObject.decompiled b/tests/pos/simpleCaseObject.decompiled index abf361f74dda..58f307b86936 100644 --- a/tests/pos/simpleCaseObject.decompiled +++ b/tests/pos/simpleCaseObject.decompiled @@ -1,8 +1,6 @@ -================================================================================ -out/posTestFromTasty/pos/simpleCaseObject/foo/Foo.class --------------------------------------------------------------------------------- +/** Decompiled from out/posTestFromTasty/pos/simpleCaseObject/foo/Foo.class */ package foo { - case object Foo() extends _root_.scala.Product { this: foo.Foo.type => + case object Foo { this: foo.Foo.type => override def hashCode(): Int = 1045991777 override def toString(): String = "Foo" override def canEqual(that: Any): Boolean = @@ -15,5 +13,4 @@ package foo { case _ => throw new IndexOutOfBoundsException(n.toString()) } } -} --------------------------------------------------------------------------------- +} \ No newline at end of file diff --git a/tests/pos/simpleClass-2.decompiled b/tests/pos/simpleClass-2.decompiled index 23e04b8e8701..b8f785f36508 100644 --- a/tests/pos/simpleClass-2.decompiled +++ b/tests/pos/simpleClass-2.decompiled @@ -1,14 +1,8 @@ -================================================================================ -out/posTestFromTasty/pos/simpleClass-2/foo/A.class --------------------------------------------------------------------------------- +/** Decompiled from out/posTestFromTasty/pos/simpleClass-2/foo/A.class */ package foo { class A() extends foo.B() {} } --------------------------------------------------------------------------------- -================================================================================ -out/posTestFromTasty/pos/simpleClass-2/foo/B.class --------------------------------------------------------------------------------- +/** Decompiled from out/posTestFromTasty/pos/simpleClass-2/foo/B.class */ package foo { class B() {} -} --------------------------------------------------------------------------------- \ No newline at end of file +} \ No newline at end of file diff --git a/tests/pos/simpleClass.decompiled b/tests/pos/simpleClass.decompiled index 814df8bceddc..794c0baff401 100644 --- a/tests/pos/simpleClass.decompiled +++ b/tests/pos/simpleClass.decompiled @@ -1,14 +1,8 @@ -================================================================================ -out/posTestFromTasty/pos/simpleClass/foo/A.class --------------------------------------------------------------------------------- +/** Decompiled from out/posTestFromTasty/pos/simpleClass/foo/A.class */ package foo { class A() {} } --------------------------------------------------------------------------------- -================================================================================ -out/posTestFromTasty/pos/simpleClass/foo/B.class --------------------------------------------------------------------------------- +/** Decompiled from out/posTestFromTasty/pos/simpleClass/foo/B.class */ package foo { class B() extends foo.A() {} -} --------------------------------------------------------------------------------- \ No newline at end of file +} \ No newline at end of file diff --git a/tests/pos/simpleDoWhile.decompiled b/tests/pos/simpleDoWhile.decompiled index 811801631451..e8f316aec6db 100644 --- a/tests/pos/simpleDoWhile.decompiled +++ b/tests/pos/simpleDoWhile.decompiled @@ -1,6 +1,4 @@ -================================================================================ -out/posTestFromTasty/pos/simpleDoWhile/Foo.class --------------------------------------------------------------------------------- +/** Decompiled from out/posTestFromTasty/pos/simpleDoWhile/Foo.class */ class Foo() { def foo: Unit = { @@ -11,5 +9,4 @@ class Foo() { } while (i.!=(0)) } -} --------------------------------------------------------------------------------- \ No newline at end of file +} \ No newline at end of file diff --git a/tests/pos/simpleWhile.decompiled b/tests/pos/simpleWhile.decompiled index 8ae185525386..1fec54f3fd45 100644 --- a/tests/pos/simpleWhile.decompiled +++ b/tests/pos/simpleWhile.decompiled @@ -1,6 +1,4 @@ -================================================================================ -out/posTestFromTasty/pos/simpleWhile/Foo.class --------------------------------------------------------------------------------- +/** Decompiled from out/posTestFromTasty/pos/simpleWhile/Foo.class */ class Foo() { def foo: Unit = { @@ -10,5 +8,4 @@ class Foo() { i = 0 } } -} --------------------------------------------------------------------------------- \ No newline at end of file +} \ No newline at end of file diff --git a/tests/run/puzzle.decompiled b/tests/run/puzzle.decompiled index 870c215b4a1d..5a3236b7c1fc 100644 --- a/tests/run/puzzle.decompiled +++ b/tests/run/puzzle.decompiled @@ -1,7 +1,5 @@ -================================================================================ -out/runTestFromTasty/run/puzzle/Test.class --------------------------------------------------------------------------------- -object Test() { this: Test.type => +/** Decompiled from out/runTestFromTasty/run/puzzle/Test.class */ +object Test { this: Test.type => def main(args: Array[String]): Unit = { println(if false then 5.0 else 53.0) @@ -11,5 +9,4 @@ object Test() { this: Test.type => val y: Float = Long.long2float(z) () } -} --------------------------------------------------------------------------------- \ No newline at end of file +} \ No newline at end of file From 1160d2c221b4b73f3758c01f5291a8cda9151459 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sun, 8 Apr 2018 14:56:05 +0200 Subject: [PATCH 2/9] Do not print self types of module objects --- compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala | 2 +- compiler/test/dotty/tools/dotc/FromTastyTests.scala | 1 - tests/pos/simpleCaseObject.decompiled | 2 +- tests/run/puzzle.decompiled | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 7d8da3d0a2cc..b3c993b2c844 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -652,7 +652,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val selfText = { val selfName = if (self.name == nme.WILDCARD) keywordStr("this") else self.name.toString (selfName ~ optText(self.tpt)(": " ~ _) ~ " =>").close - } provided !self.isEmpty + } provided (!self.isEmpty && !constr.symbol.owner.is(Module)) val body = if (ctx.settings.YtestPickler.value) { // Pickling/unpickling reorders the body members, so we need to homogenize diff --git a/compiler/test/dotty/tools/dotc/FromTastyTests.scala b/compiler/test/dotty/tools/dotc/FromTastyTests.scala index b89d58ef7332..fcd368d4cb0a 100644 --- a/compiler/test/dotty/tools/dotc/FromTastyTests.scala +++ b/compiler/test/dotty/tools/dotc/FromTastyTests.scala @@ -61,7 +61,6 @@ class FromTastyTests extends ParallelTesting { "eff-dependent.scala", ), recompileBlacklist = Set( - "puzzle" ) ) step1.checkCompile() // Compile all files to generate the class files with tasty diff --git a/tests/pos/simpleCaseObject.decompiled b/tests/pos/simpleCaseObject.decompiled index 58f307b86936..cffa8ea17e3d 100644 --- a/tests/pos/simpleCaseObject.decompiled +++ b/tests/pos/simpleCaseObject.decompiled @@ -1,6 +1,6 @@ /** Decompiled from out/posTestFromTasty/pos/simpleCaseObject/foo/Foo.class */ package foo { - case object Foo { this: foo.Foo.type => + case object Foo { override def hashCode(): Int = 1045991777 override def toString(): String = "Foo" override def canEqual(that: Any): Boolean = diff --git a/tests/run/puzzle.decompiled b/tests/run/puzzle.decompiled index 5a3236b7c1fc..220e4f4a0264 100644 --- a/tests/run/puzzle.decompiled +++ b/tests/run/puzzle.decompiled @@ -1,5 +1,5 @@ /** Decompiled from out/runTestFromTasty/run/puzzle/Test.class */ -object Test { this: Test.type => +object Test { def main(args: Array[String]): Unit = { println(if false then 5.0 else 53.0) From cd663ec800940aab01539ad06bc14edd0925853c Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sun, 8 Apr 2018 15:06:47 +0200 Subject: [PATCH 3/9] Enable runtime tests on recompiled code --- compiler/test/dotty/tools/dotc/FromTastyTests.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/test/dotty/tools/dotc/FromTastyTests.scala b/compiler/test/dotty/tools/dotc/FromTastyTests.scala index fcd368d4cb0a..b674c1aa0ee1 100644 --- a/compiler/test/dotty/tools/dotc/FromTastyTests.scala +++ b/compiler/test/dotty/tools/dotc/FromTastyTests.scala @@ -66,7 +66,7 @@ class FromTastyTests extends ParallelTesting { step1.checkCompile() // Compile all files to generate the class files with tasty step2.checkRuns() // Compile from tasty and run the result step3.checkCompile() // Decompile from tasty - step4.checkCompile() // Recompile decompiled code + step4.checkRuns() // Recompile decompiled code (step1 + step2 + step3 + step4).delete() } From c1d1c212e4b7242ee023838cfaf0cf135c47ad90 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 9 Apr 2018 11:05:44 +0200 Subject: [PATCH 4/9] Rename filter to isSyntheticParent --- .../src/dotty/tools/dotc/printing/DecompilerPrinter.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala b/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala index e33e9468341a..d308014670cd 100644 --- a/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala @@ -55,11 +55,11 @@ class DecompilerPrinter(_ctx: Context) extends RefinedPrinter(_ctx) { } override protected def toTextTemplate(impl: Template, ofNew: Boolean = false): Text = { - def filter(sym: Symbol): Boolean = { + def isSyntheticParent(sym: Symbol): Boolean = { sym.maybeOwner == defn.ObjectClass || (sym == defn.ProductClass && impl.symbol.owner.is(Case)) } - val impl1 = impl.copy(parents = impl.parents.filterNot(p => filter(p.symbol))) + val impl1 = impl.copy(parents = impl.parents.filterNot(p => isSyntheticParent(p.symbol))) super.toTextTemplate(impl1, ofNew) } From 11fe1b9a855db83b1d1e47bbc752c6a94f89703b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 9 Apr 2018 11:06:18 +0200 Subject: [PATCH 5/9] Override printing of self in DecompilerPrinter --- .../tools/dotc/printing/DecompilerPrinter.scala | 3 +++ .../dotty/tools/dotc/printing/RefinedPrinter.scala | 12 +++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala b/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala index d308014670cd..5f2cfb254804 100644 --- a/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala @@ -54,6 +54,9 @@ class DecompilerPrinter(_ctx: Context) extends RefinedPrinter(_ctx) { decl ~~ typeText(nameIdText(tree)) ~ withEnclosingDef(tree) { toTextTemplate(impl) } ~ "" } + override protected def selfToText(impl: Template): Text = + super.selfToText(impl).provided(!impl.constr.symbol.owner.is(Module)) + override protected def toTextTemplate(impl: Template, ofNew: Boolean = false): Text = { def isSyntheticParent(sym: Symbol): Boolean = { sym.maybeOwner == defn.ObjectClass || diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index b3c993b2c844..73a077bbe803 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -649,11 +649,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { withEnclosingDef(constr) { addVparamssText(tparamsTxt ~~ modsText, vparamss) } } val parentsText = Text(parents map constrText, keywordStr(" with ")) - val selfText = { - val selfName = if (self.name == nme.WILDCARD) keywordStr("this") else self.name.toString - (selfName ~ optText(self.tpt)(": " ~ _) ~ " =>").close - } provided (!self.isEmpty && !constr.symbol.owner.is(Module)) - + val selfText = selfToText(impl) val body = if (ctx.settings.YtestPickler.value) { // Pickling/unpickling reorders the body members, so we need to homogenize val (params, rest) = impl.body partition { @@ -670,6 +666,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { prefix ~ (keywordText(" extends") provided (!ofNew && parents.nonEmpty)) ~~ parentsText ~~ bodyText } + protected def selfToText(impl: Template): Text = { + val self = impl.self + val selfName = if (self.name == nme.WILDCARD) keywordStr("this") else self.name.toString + (selfName ~ optText(self.tpt)(": " ~ _) ~ " =>").close provided (!self.isEmpty) + } + protected def templateText(tree: TypeDef, impl: Template): Text = { val decl = modText(tree.mods, keywordStr(if ((tree).mods is Trait) "trait" else "class")) decl ~~ typeText(nameIdText(tree)) ~ withEnclosingDef(tree) { toTextTemplate(impl) } ~ From 8bfc4ec53a454a1e69d9b7c96a0d16598ea2f091 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 9 Apr 2018 11:27:15 +0200 Subject: [PATCH 6/9] Fix step4 not able to see availavble tests They only exist after step3 has been executed --- .../dotty/tools/dotc/FromTastyTests.scala | 21 ++++++++++++------- .../dotty/tools/vulpix/ParallelTesting.scala | 8 +++---- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/compiler/test/dotty/tools/dotc/FromTastyTests.scala b/compiler/test/dotty/tools/dotc/FromTastyTests.scala index b674c1aa0ee1..432a62097f47 100644 --- a/compiler/test/dotty/tools/dotc/FromTastyTests.scala +++ b/compiler/test/dotty/tools/dotc/FromTastyTests.scala @@ -26,7 +26,7 @@ class FromTastyTests extends ParallelTesting { // > dotc -Ythrough-tasty -Ycheck:all implicit val testGroup: TestGroup = TestGroup("posTestFromTasty") - val (step1, step2, step3, step4) = compileTastyInDir("tests/pos", defaultOptions, + val (step1, step2, step3, decompilationDir) = compileTastyInDir("tests/pos", defaultOptions, blacklist = Set( // Wrong number of arguments (only on bootstrapped) "i3130b.scala", @@ -36,15 +36,19 @@ class FromTastyTests extends ParallelTesting { // MatchError in SymDenotation.sourceModule on a ThisType "t3612.scala", - ), - recompileBlacklist = Set( - "simpleCaseObject" ) ) step1.checkCompile() // Compile all files to generate the class files with tasty step2.checkCompile() // Compile from tasty step3.checkCompile() // Decompile from tasty + + val step4 = compileFilesInDir(decompilationDir, defaultOptions, + blacklist = Set( + "simpleCaseObject" + ) + ).keepOutput step4.checkCompile() // Recompile decompiled code + (step1 + step2 + step3 + step4).delete() } @@ -55,18 +59,19 @@ class FromTastyTests extends ParallelTesting { // > dotr Test implicit val testGroup: TestGroup = TestGroup("runTestFromTasty") - val (step1, step2, step3, step4) = compileTastyInDir("tests/run", defaultOptions, + val (step1, step2, step3, decompilationDir) = compileTastyInDir("tests/run", defaultOptions, blacklist = Set( // Closure type miss match "eff-dependent.scala", - ), - recompileBlacklist = Set( ) ) step1.checkCompile() // Compile all files to generate the class files with tasty step2.checkRuns() // Compile from tasty and run the result step3.checkCompile() // Decompile from tasty - step4.checkRuns() // Recompile decompiled code + + val step4 = compileFilesInDir(decompilationDir, defaultOptions, blacklist = Set()).keepOutput + step4.checkRuns() // Recompile and run decompiled code + (step1 + step2 + step3 + step4).delete() } diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index 5ce232b95f41..debb4bdc5186 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -1258,8 +1258,8 @@ trait ParallelTesting extends RunnerOrchestration { self => * Tests in the first part of the tuple must be executed before the second. * Both testsRequires explicit delete(). */ - def compileTastyInDir(f: String, flags0: TestFlags, blacklist: Set[String], recompileBlacklist: Set[String])( - implicit testGroup: TestGroup): (CompilationTest, CompilationTest, CompilationTest, CompilationTest) = { + def compileTastyInDir(f: String, flags0: TestFlags, blacklist: Set[String])( + implicit testGroup: TestGroup): (CompilationTest, CompilationTest, CompilationTest, String) = { val outDir = defaultOutputDir + testGroup + "/" val flags = flags0 and "-Yretain-trees" val sourceDir = new JFile(f) @@ -1286,14 +1286,12 @@ trait ParallelTesting extends RunnerOrchestration { self => val generateClassFiles = compileFilesInDir(f, flags0, blacklist) val decompilationDir = outDir + sourceDir.getName + "_decompiled" - new JFile(decompilationDir).mkdirs() - val recompileDecompiled = compileFilesInDir(decompilationDir, flags0, recompileBlacklist) ( generateClassFiles.keepOutput, new CompilationTest(targets).keepOutput, new CompilationTest(targets2).keepOutput, - recompileDecompiled.keepOutput + decompilationDir ) } From bcfc2b78ca00bbc4d09b7ae3a943128628ef2eb3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 9 Apr 2018 11:40:28 +0200 Subject: [PATCH 7/9] Create TastyCompilationTest --- .../dotty/tools/dotc/FromTastyTests.scala | 37 +++--------- .../dotty/tools/vulpix/ParallelTesting.scala | 56 +++++++++++++++---- 2 files changed, 54 insertions(+), 39 deletions(-) diff --git a/compiler/test/dotty/tools/dotc/FromTastyTests.scala b/compiler/test/dotty/tools/dotc/FromTastyTests.scala index 432a62097f47..243d6f2ded00 100644 --- a/compiler/test/dotty/tools/dotc/FromTastyTests.scala +++ b/compiler/test/dotty/tools/dotc/FromTastyTests.scala @@ -26,7 +26,7 @@ class FromTastyTests extends ParallelTesting { // > dotc -Ythrough-tasty -Ycheck:all implicit val testGroup: TestGroup = TestGroup("posTestFromTasty") - val (step1, step2, step3, decompilationDir) = compileTastyInDir("tests/pos", defaultOptions, + compileTastyInDir("tests/pos", defaultOptions, blacklist = Set( // Wrong number of arguments (only on bootstrapped) "i3130b.scala", @@ -36,20 +36,11 @@ class FromTastyTests extends ParallelTesting { // MatchError in SymDenotation.sourceModule on a ThisType "t3612.scala", - ) - ) - step1.checkCompile() // Compile all files to generate the class files with tasty - step2.checkCompile() // Compile from tasty - step3.checkCompile() // Decompile from tasty - - val step4 = compileFilesInDir(decompilationDir, defaultOptions, - blacklist = Set( + ), + recompilationBlacklist = Set( "simpleCaseObject" ) - ).keepOutput - step4.checkCompile() // Recompile decompiled code - - (step1 + step2 + step3 + step4).delete() + ).checkCompile() } @Test def runTestFromTasty: Unit = { @@ -59,25 +50,13 @@ class FromTastyTests extends ParallelTesting { // > dotr Test implicit val testGroup: TestGroup = TestGroup("runTestFromTasty") - val (step1, step2, step3, decompilationDir) = compileTastyInDir("tests/run", defaultOptions, + compileTastyInDir("tests/run", defaultOptions, blacklist = Set( // Closure type miss match "eff-dependent.scala", - ) - ) - step1.checkCompile() // Compile all files to generate the class files with tasty - step2.checkRuns() // Compile from tasty and run the result - step3.checkCompile() // Decompile from tasty - - val step4 = compileFilesInDir(decompilationDir, defaultOptions, blacklist = Set()).keepOutput - step4.checkRuns() // Recompile and run decompiled code - - (step1 + step2 + step3 + step4).delete() - } - - private implicit class tastyCompilationTuples(tup: (CompilationTest, CompilationTest)) { - def +(that: (CompilationTest, CompilationTest)): (CompilationTest, CompilationTest) = - (tup._1 + that._1, tup._2 + that._2) + ), + recompilationBlacklist = Set() + ).checkRuns() } } diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index debb4bdc5186..497d68138f50 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -2,12 +2,12 @@ package dotty package tools package vulpix -import java.io.{ File => JFile } +import java.io.{File => JFile} import java.text.SimpleDateFormat import java.util.HashMap import java.nio.file.StandardCopyOption.REPLACE_EXISTING -import java.nio.file.{ Files, Path, Paths, NoSuchFileException } -import java.util.concurrent.{ Executors => JExecutors, TimeUnit, TimeoutException } +import java.nio.file.{Files, NoSuchFileException, Path, Paths} +import java.util.concurrent.{TimeUnit, TimeoutException, Executors => JExecutors} import scala.io.Source import scala.util.control.NonFatal @@ -15,14 +15,14 @@ import scala.util.Try import scala.collection.mutable import scala.util.matching.Regex import scala.util.Random - import dotc.core.Contexts._ -import dotc.reporting.{ Reporter, TestReporter } +import dotc.reporting.{Reporter, TestReporter} import dotc.reporting.diagnostic.MessageContainer import dotc.interfaces.Diagnostic.ERROR import dotc.util.DiffUtil -import dotc.{ Driver, Compiler } +import dotc.{Compiler, Driver} import dotc.decompiler +import dotty.tools.vulpix.TestConfiguration.defaultOptions /** A parallel testing suite whose goal is to integrate nicely with JUnit * @@ -1258,8 +1258,8 @@ trait ParallelTesting extends RunnerOrchestration { self => * Tests in the first part of the tuple must be executed before the second. * Both testsRequires explicit delete(). */ - def compileTastyInDir(f: String, flags0: TestFlags, blacklist: Set[String])( - implicit testGroup: TestGroup): (CompilationTest, CompilationTest, CompilationTest, String) = { + def compileTastyInDir(f: String, flags0: TestFlags, blacklist: Set[String], recompilationBlacklist: Set[String])( + implicit testGroup: TestGroup): TastyCompilationTest = { val outDir = defaultOutputDir + testGroup + "/" val flags = flags0 and "-Yretain-trees" val sourceDir = new JFile(f) @@ -1287,14 +1287,50 @@ trait ParallelTesting extends RunnerOrchestration { self => val decompilationDir = outDir + sourceDir.getName + "_decompiled" - ( + new TastyCompilationTest( generateClassFiles.keepOutput, new CompilationTest(targets).keepOutput, new CompilationTest(targets2).keepOutput, - decompilationDir + recompilationBlacklist, + decompilationDir, + shouldDelete = true ) } + class TastyCompilationTest(step1: CompilationTest, step2: CompilationTest, step3: CompilationTest, + recompilationBlacklist: Set[String], decompilationDir: String, shouldDelete: Boolean)(implicit testGroup: TestGroup) { + + def keepOutput: TastyCompilationTest = + new TastyCompilationTest(step1, step2, step3, recompilationBlacklist, decompilationDir, shouldDelete) + + def checkCompile()(implicit summaryReport: SummaryReporting): this.type = { + step1.checkCompile() // Compile all files to generate the class files with tasty + step2.checkCompile() // Compile from tasty + step3.checkCompile() // Decompile from tasty + + val step4 = compileFilesInDir(decompilationDir, defaultOptions, recompilationBlacklist).keepOutput + step4.checkCompile() // Recompile decompiled code + + if (shouldDelete) + (step1 + step2 + step3 + step4).delete() + + this + } + + def checkRuns()(implicit summaryReport: SummaryReporting): this.type = { + step1.checkCompile() // Compile all files to generate the class files with tasty + step2.checkRuns() // Compile from tasty + step3.checkCompile() // Decompile from tasty + + val step4 = compileFilesInDir(decompilationDir, defaultOptions, recompilationBlacklist).keepOutput + step4.checkRuns() // Recompile decompiled code + + if (shouldDelete) + (step1 + step2 + step3 + step4).delete() + + this + } + } /** This function behaves similar to `compileFilesInDir` but it ignores * sub-directories and as such, does **not** perform separate compilation From 6971c0db945c065d0d6ca849f377c6d51641ea35 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 7 May 2018 08:15:34 +0200 Subject: [PATCH 8/9] Fix double space in module diefinition printing --- compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala | 6 ++++-- tests/pos/simpleCaseObject.decompiled | 2 +- tests/run/puzzle.decompiled | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 73a077bbe803..983cdab535e3 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -640,7 +640,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val tparamsTxt = withEnclosingDef(constr) { tparamsText(tparams) } val primaryConstrs = if (constr.rhs.isEmpty) Nil else constr :: Nil val prefix: Text = - if (constr.symbol.owner.is(Module)) " " + if (constr.symbol.owner.is(Module)) "" else if (vparamss.isEmpty || primaryConstrs.nonEmpty) tparamsTxt else { var modsText = modText(constr.mods, "") @@ -661,7 +661,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { params ::: rest } else impl.body - val bodyText = "{" ~~ selfText ~~ toTextGlobal(primaryConstrs ::: body, "\n") ~ "}" + val bodyText = + (if (constr.symbol.owner.is(Module)) " {" else "{") ~~ + selfText ~~ toTextGlobal(primaryConstrs ::: body, "\n") ~ "}" prefix ~ (keywordText(" extends") provided (!ofNew && parents.nonEmpty)) ~~ parentsText ~~ bodyText } diff --git a/tests/pos/simpleCaseObject.decompiled b/tests/pos/simpleCaseObject.decompiled index cffa8ea17e3d..66dac789c7b2 100644 --- a/tests/pos/simpleCaseObject.decompiled +++ b/tests/pos/simpleCaseObject.decompiled @@ -1,6 +1,6 @@ /** Decompiled from out/posTestFromTasty/pos/simpleCaseObject/foo/Foo.class */ package foo { - case object Foo { + case object Foo { override def hashCode(): Int = 1045991777 override def toString(): String = "Foo" override def canEqual(that: Any): Boolean = diff --git a/tests/run/puzzle.decompiled b/tests/run/puzzle.decompiled index 220e4f4a0264..2f8c2d50577f 100644 --- a/tests/run/puzzle.decompiled +++ b/tests/run/puzzle.decompiled @@ -1,5 +1,5 @@ /** Decompiled from out/runTestFromTasty/run/puzzle/Test.class */ -object Test { +object Test { def main(args: Array[String]): Unit = { println(if false then 5.0 else 53.0) From 3301cf4a925b4da4efa98b68bbf7dca8b446f31c Mon Sep 17 00:00:00 2001 From: Allan Renucci Date: Tue, 8 May 2018 17:43:38 +0200 Subject: [PATCH 9/9] Move more logic from RefinedPrinter to DecompilerPrinter RefinedPrinter cannot make use of symbols in order to be able to print after all phases (e.g. Parser). --- .../dotc/printing/DecompilerPrinter.scala | 20 ++++++++++++------- .../tools/dotc/printing/RefinedPrinter.scala | 20 +++++++------------ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala b/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala index 5f2cfb254804..969276e732bc 100644 --- a/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala @@ -1,7 +1,7 @@ package dotty.tools.dotc.printing import dotty.tools.dotc.ast.Trees._ -import dotty.tools.dotc.ast.untpd.{PackageDef, Template, TypeDef} +import dotty.tools.dotc.ast.untpd.{Tree, PackageDef, Template, TypeDef} import dotty.tools.dotc.ast.{Trees, untpd} import dotty.tools.dotc.printing.Texts._ import dotty.tools.dotc.core.Contexts._ @@ -54,16 +54,22 @@ class DecompilerPrinter(_ctx: Context) extends RefinedPrinter(_ctx) { decl ~~ typeText(nameIdText(tree)) ~ withEnclosingDef(tree) { toTextTemplate(impl) } ~ "" } - override protected def selfToText(impl: Template): Text = - super.selfToText(impl).provided(!impl.constr.symbol.owner.is(Module)) - override protected def toTextTemplate(impl: Template, ofNew: Boolean = false): Text = { - def isSyntheticParent(sym: Symbol): Boolean = { + def isSynthetic(parent: Tree): Boolean = { + val sym = parent.symbol sym.maybeOwner == defn.ObjectClass || (sym == defn.ProductClass && impl.symbol.owner.is(Case)) } - val impl1 = impl.copy(parents = impl.parents.filterNot(p => isSyntheticParent(p.symbol))) - super.toTextTemplate(impl1, ofNew) + val parents = impl.parents.filterNot(isSynthetic) + + // We don't print self type and constructor for objects + val isObject = impl.constr.symbol.owner.is(Module) + if (isObject) { + val parentsText = keywordText(" extends") ~~ Text(parents.map(constrText), keywordStr(" with ")) + val bodyText = " {" ~~ toTextGlobal(impl.body, "\n") ~ "}" + parentsText.provided(parents.nonEmpty) ~ bodyText + } + else super.toTextTemplate(impl.copy(parents = parents), ofNew) } override protected def typeApplyText[T >: Untyped](tree: TypeApply[T]): Text = { diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 983cdab535e3..ff945431e5c6 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -640,8 +640,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val tparamsTxt = withEnclosingDef(constr) { tparamsText(tparams) } val primaryConstrs = if (constr.rhs.isEmpty) Nil else constr :: Nil val prefix: Text = - if (constr.symbol.owner.is(Module)) "" - else if (vparamss.isEmpty || primaryConstrs.nonEmpty) tparamsTxt + if (vparamss.isEmpty || primaryConstrs.nonEmpty) tparamsTxt else { var modsText = modText(constr.mods, "") if (!modsText.isEmpty) modsText = " " ~ modsText @@ -649,7 +648,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { withEnclosingDef(constr) { addVparamssText(tparamsTxt ~~ modsText, vparamss) } } val parentsText = Text(parents map constrText, keywordStr(" with ")) - val selfText = selfToText(impl) + val selfText = { + val selfName = if (self.name == nme.WILDCARD) keywordStr("this") else self.name.toString + (selfName ~ optText(self.tpt)(": " ~ _) ~ " =>").close + }.provided(!self.isEmpty) val body = if (ctx.settings.YtestPickler.value) { // Pickling/unpickling reorders the body members, so we need to homogenize val (params, rest) = impl.body partition { @@ -661,17 +663,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { params ::: rest } else impl.body - val bodyText = - (if (constr.symbol.owner.is(Module)) " {" else "{") ~~ - selfText ~~ toTextGlobal(primaryConstrs ::: body, "\n") ~ "}" - - prefix ~ (keywordText(" extends") provided (!ofNew && parents.nonEmpty)) ~~ parentsText ~~ bodyText - } + val bodyText = " {" ~~ selfText ~~ toTextGlobal(primaryConstrs ::: body, "\n") ~ "}" - protected def selfToText(impl: Template): Text = { - val self = impl.self - val selfName = if (self.name == nme.WILDCARD) keywordStr("this") else self.name.toString - (selfName ~ optText(self.tpt)(": " ~ _) ~ " =>").close provided (!self.isEmpty) + prefix ~ keywordText(" extends").provided(!ofNew && parents.nonEmpty) ~~ parentsText ~ bodyText } protected def templateText(tree: TypeDef, impl: Template): Text = {