Skip to content

Commit 4790d75

Browse files
committed
Create dotc decompiler
* `dotc -decompile <options|classes>*`
1 parent cc43c34 commit 4790d75

File tree

8 files changed

+101
-11
lines changed

8 files changed

+101
-11
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ object CompilationUnit {
2828
/** Make a compilation unit for top class `clsd` with the contends of the `unpickled` */
2929
def mkCompilationUnit(clsd: ClassDenotation, unpickled: Tree, forceTrees: Boolean)(implicit ctx: Context): CompilationUnit = {
3030
assert(!unpickled.isEmpty, unpickled)
31-
val unit1 = new CompilationUnit(new SourceFile(clsd.symbol.sourceFile, Seq()))
31+
val unit1 = new CompilationUnit(new SourceFile(clsd.symbol.associatedFile, Seq()))
3232
unit1.tpdTree = unpickled
3333
if (forceTrees)
3434
force.traverse(unit1.tpdTree)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package dotty.tools.dotc
2+
package decompiler
3+
4+
import dotty.tools.dotc.core.Contexts._
5+
import dotty.tools.dotc.core.Phases.Phase
6+
7+
/** Phase that prints the trees in all loaded compilation units.
8+
*
9+
* @author Nicolas Stucki
10+
*/
11+
class DecompilationPrinter extends Phase {
12+
13+
override def phaseName: String = "decompilationPrinter"
14+
15+
override def run(implicit ctx: Context): Unit = {
16+
val unit = ctx.compilationUnit
17+
18+
val pageWidth = ctx.settings.pageWidth.value
19+
20+
val doubleLine = "=" * pageWidth
21+
val line = "-" * pageWidth
22+
23+
println(doubleLine)
24+
println(unit.source)
25+
println(line)
26+
27+
val code = unit.tpdTree.show
28+
println(if (ctx.useColors) printing.SyntaxHighlighting(code) else code)
29+
println(line)
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package dotty.tools.dotc.decompiler
2+
3+
import dotty.tools.dotc
4+
import dotty.tools.dotc.core.Contexts._
5+
6+
/** Main class of the `dotc -decompiler` decompiler.
7+
*
8+
* @author Nicolas Stucki
9+
*/
10+
object Main extends dotc.Driver {
11+
override protected def newCompiler(implicit ctx: Context): dotc.Compiler = {
12+
assert(ctx.settings.fromTasty.value)
13+
new TASTYDecompiler
14+
}
15+
16+
override def setup(args0: Array[String], rootCtx: Context): (List[String], Context) = {
17+
var args = args0.filter(a => a != "-decompile")
18+
args = if (args.contains("-from-tasty")) args else "-from-tasty" +: args
19+
super.setup(args, rootCtx)
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package dotty.tools.dotc.decompiler
2+
3+
import dotty.tools.dotc.fromtasty._
4+
import dotty.tools.dotc.core.Phases.Phase
5+
6+
/** Compiler from tasty to user readable high text representation
7+
* of the compiled scala code.
8+
*
9+
* @author Nicolas Stucki
10+
*/
11+
class TASTYDecompiler extends TASTYCompiler {
12+
override def phases: List[List[Phase]] = List(
13+
List(new ReadTastyTreesFromClasses), // Load classes from tasty
14+
List(new DecompilationPrinter) // Print all loaded classes
15+
)
16+
}

dist/bin/dotc

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ default_java_opts="-Xmx768m -Xms768m"
3131
bootcp=true
3232

3333
CompilerMain=dotty.tools.dotc.Main
34+
DecompilerMain=dotty.tools.dotc.decompiler.Main
3435
ReplMain=dotty.tools.repl.Main
3536

3637
PROG_NAME=$CompilerMain
@@ -82,6 +83,7 @@ case "$1" in
8283
-Oshort) addJava "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" && shift ;;
8384
-repl) PROG_NAME="$ReplMain" && shift ;;
8485
-compile) PROG_NAME="$CompilerMain" && shift ;;
86+
-decompile) PROG_NAME="$DecompilerMain" && shift ;;
8587
-run) PROG_NAME="$ReplMain" && shift ;;
8688
-bootcp) bootcp=true && shift ;;
8789
-nobootcp) unset bootcp && shift ;;

project/Build.scala

+12-5
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ object Build {
8787
lazy val dotr =
8888
inputKey[Unit]("run compiled binary using the correct classpath, or the user supplied classpath")
8989

90+
9091
// Compiles the documentation and static site
9192
lazy val genDocs = taskKey[Unit]("run dottydoc to generate static documentation site")
9293

@@ -523,8 +524,8 @@ object Build {
523524
}
524525
},
525526
run := dotc.evaluated,
526-
dotc := runCompilerMain(false).evaluated,
527-
repl := runCompilerMain(true).evaluated,
527+
dotc := runCompilerMain().evaluated,
528+
repl := runCompilerMain(repl = true).evaluated,
528529

529530
// enable verbose exception messages for JUnit
530531
testOptions in Test += Tests.Argument(
@@ -618,16 +619,22 @@ object Build {
618619
}
619620
)
620621

621-
def runCompilerMain(repl: Boolean) = Def.inputTaskDyn {
622+
def runCompilerMain(repl: Boolean = false) = Def.inputTaskDyn {
622623
val dottyLib = packageAll.value("dotty-library")
623624
val args0: List[String] = spaceDelimited("<arg>").parsed.toList
624-
val args = args0.filter(arg => arg != "-repl")
625+
val decompile = args0.contains("-decompile")
626+
val args = args0.filter(arg => arg != "-repl" || arg != "-decompile")
625627

626628
val main =
627629
if (repl) "dotty.tools.repl.Main"
630+
else if (decompile) "dotty.tools.dotc.decompiler.Main"
628631
else "dotty.tools.dotc.Main"
629632

630-
val fullArgs = main :: insertClasspathInArgs(args, dottyLib)
633+
val extraClasspath =
634+
if (decompile && !args.contains("-classpath")) dottyLib + ":."
635+
else dottyLib
636+
637+
val fullArgs = main :: insertClasspathInArgs(args, extraClasspath)
631638

632639
(runMain in Compile).toTask(fullArgs.mkString(" ", " ", ""))
633640
}

project/scripts/sbtBootstrappedTests

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
./project/scripts/sbt dist-bootstrapped/pack
99

1010
# check that `dotc` compiles and `dotr` runs it
11-
echo "testing sbt dotc and dotr"
11+
echo "testing ./bin/dotc and ./bin/dotr"
1212
mkdir out/scriptedtest0
1313
./bin/dotc tests/pos/sbtDotrTest.scala -d out/scriptedtest0
1414
# FIXME #3477
@@ -21,7 +21,7 @@ mkdir out/scriptedtest0
2121

2222

2323
# check that `dotc` compiles and `dotr` runs it
24-
echo "testing sbt dotc -from-tasty and dotr -classpath"
24+
echo "testing ./bin/dotc -from-tasty and dotr -classpath"
2525
mkdir out/scriptedtest1
2626
mkdir out/scriptedtest2
2727
./bin/dotc tests/pos/sbtDotrTest.scala -d out/scriptedtest1/

project/scripts/sbtTests

+16-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ cat sbtdotr1.out
1111
if grep -e "dotr test ok" sbtdotr1.out; then
1212
echo "output ok"
1313
else
14+
echo "failed output check"
1415
exit -1
1516
fi
1617

@@ -23,13 +24,25 @@ cat sbtdotr2.out
2324
if grep -e "dotr test ok" sbtdotr2.out; then
2425
echo "output ok"
2526
else
27+
echo "failed output check"
28+
exit -1
29+
fi
30+
31+
# check that `sbt dotc -decompile` runs
32+
echo "testing sbt dotc -decompile"
33+
./project/scripts/sbt ";dotc -decompile -code -color:never -classpath out/scriptedtest1 dotrtest.Test" > sbtdotc3.out
34+
cat sbtdotc3.out
35+
if grep -e "def main(args: Array\[String\]): Unit =" sbtdotc3.out; then
36+
echo "output ok"
37+
else
38+
echo "failed output check"
2639
exit -1
2740
fi
2841
echo "testing sbt dotr with no -classpath"
2942

30-
./project/scripts/sbt ";dotc tests/pos/sbtDotrTest.scala; dotr dotrtest.Test" > sbtdotp1.out
31-
cat sbtdotp1.out
32-
if grep -e "dotr test ok" sbtdotp1.out; then
43+
./project/scripts/sbt ";dotc tests/pos/sbtDotrTest.scala; dotr dotrtest.Test" > sbtdotr3.out
44+
cat sbtdotr3.out
45+
if grep -e "dotr test ok" sbtdotr3.out; then
3346
echo "output ok"
3447
else
3548
exit -1

0 commit comments

Comments
 (0)