Skip to content

Commit ce79e35

Browse files
committed
Implement run/show for quoted expressions
1 parent e4b718d commit ce79e35

File tree

25 files changed

+373
-13
lines changed

25 files changed

+373
-13
lines changed

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,13 @@ class CompilationUnit(val source: SourceFile) {
3131
object CompilationUnit {
3232

3333
/** Make a compilation unit for top class `clsd` with the contends of the `unpickled` */
34-
def mkCompilationUnit(clsd: ClassDenotation, unpickled: Tree, forceTrees: Boolean)(implicit ctx: Context): CompilationUnit = {
34+
def mkCompilationUnit(clsd: ClassDenotation, unpickled: Tree, forceTrees: Boolean)(implicit ctx: Context): CompilationUnit =
35+
mkCompilationUnit(new SourceFile(clsd.symbol.associatedFile, Seq()), unpickled, forceTrees)
36+
37+
/** Make a compilation unit the given unpickled tree */
38+
def mkCompilationUnit(source: SourceFile, unpickled: Tree, forceTrees: Boolean)(implicit ctx: Context): CompilationUnit = {
3539
assert(!unpickled.isEmpty, unpickled)
36-
val unit1 = new CompilationUnit(new SourceFile(clsd.symbol.associatedFile, Seq()))
40+
val unit1 = new CompilationUnit(source)
3741
unit1.tpdTree = unpickled
3842
if (forceTrees)
3943
force.traverse(unit1.tpdTree)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package dotty.tools.dotc.quoted
2+
3+
import dotty.tools.dotc.CompilationUnit
4+
import dotty.tools.dotc.util.NoSource
5+
6+
import scala.quoted.Expr
7+
8+
class ExprCompilationUnit(val expr: Expr[_]) extends CompilationUnit(NoSource) {
9+
override def toString = s"Expr($expr)"
10+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package dotty.tools.dotc
2+
package quoted
3+
4+
import dotty.tools.backend.jvm.GenBCode
5+
import dotty.tools.dotc.core.Contexts.Context
6+
import dotty.tools.dotc.core.{Mode, Phases}
7+
import dotty.tools.dotc.core.Phases.Phase
8+
import dotty.tools.dotc.transform.Pickler
9+
import dotty.tools.io.VirtualDirectory
10+
11+
class ExprCompiler(directory: VirtualDirectory) extends Compiler {
12+
13+
/** A GenBCode phase that outputs to a virtual directory */
14+
private class ExprGenBCode extends GenBCode {
15+
override def phaseName = "genBCode"
16+
override def outputDir(implicit ctx: Context) = directory
17+
}
18+
19+
// TODO abstract over frontend, pickling, transforms and backend phase groups
20+
override def phases: List[List[Phase]] = {
21+
val backendPhases = super.phases.dropWhile {
22+
case List(_: Pickler) => false
23+
case _ => true
24+
}.tail
25+
26+
List(new ExprFrontend(putInClass = true)) ::
27+
Phases.replace(classOf[GenBCode], _ => new ExprGenBCode :: Nil, backendPhases)
28+
}
29+
30+
override def newRun(implicit ctx: Context): ExprRun = {
31+
reset()
32+
new ExprRun(this, ctx.addMode(Mode.ReadPositions))
33+
}
34+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package dotty.tools.dotc.quoted
2+
3+
import java.io.PrintStream
4+
5+
import dotty.tools.dotc.core.Phases.Phase
6+
7+
class ExprDecompiler(out: PrintStream) extends ExprCompiler(null) {
8+
override def phases: List[List[Phase]] = List(
9+
List(new ExprFrontend(putInClass = false)), // Create class from Expr
10+
List(new QuotePrinter(out)) // Print all loaded classes
11+
)
12+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package dotty.tools.dotc.quoted
2+
3+
import dotty.tools.dotc.CompilationUnit
4+
import dotty.tools.dotc.ast.tpd
5+
import dotty.tools.dotc.core.Contexts.Context
6+
import dotty.tools.dotc.core.Flags._
7+
import dotty.tools.dotc.core.Scopes._
8+
import dotty.tools.dotc.core.StdNames._
9+
import dotty.tools.dotc.core.Symbols._
10+
import dotty.tools.dotc.core.Types._
11+
import dotty.tools.dotc.typer.FrontEnd
12+
import dotty.tools.dotc.util.Positions._
13+
import dotty.tools.dotc.util.SourceFile
14+
import dotty.tools.io._
15+
16+
import scala.quoted.Expr
17+
18+
/** Frontend that receives scala.quoted.Expr as input */
19+
class ExprFrontend(putInClass: Boolean) extends FrontEnd {
20+
import tpd._
21+
import PickledQuotes.quotedToTree
22+
23+
override def isTyper = false
24+
25+
override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = {
26+
units.map {
27+
case exprUnit: ExprCompilationUnit =>
28+
val tree =
29+
if (putInClass) inClass(exprUnit.expr)
30+
else quotedToTree(exprUnit.expr)
31+
val source = new SourceFile("", Seq())
32+
CompilationUnit.mkCompilationUnit(source, tree, forceTrees = true)
33+
}
34+
}
35+
36+
private def inClass(expr: Expr[_])(implicit ctx: Context): Tree = {
37+
val pos = Position(0)
38+
val assocFile = new PlainFile(Path("<quote>"))
39+
40+
val cls = ctx.newCompleteClassSymbol(defn.RootClass, nme.QUOTE.toTypeName, EmptyFlags,
41+
defn.ObjectType :: Nil, newScope, coord = pos, assocFile = assocFile).entered.asClass
42+
cls.enter(ctx.newDefaultConstructor(cls), EmptyScope)
43+
val meth = ctx.newSymbol(cls, nme.apply, Method, ExprType(defn.AnyType), coord = pos).entered
44+
45+
val quoted = quotedToTree(expr)(ctx.withOwner(meth))
46+
47+
val run = DefDef(meth, quoted)
48+
val classTree = ClassDef(cls, DefDef(cls.primaryConstructor.asTerm), run :: Nil)
49+
PackageDef(ref(defn.RootPackage).asInstanceOf[Ident], classTree :: Nil).withPos(pos)
50+
}
51+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package dotty.tools.dotc
2+
package quoted
3+
4+
import dotty.tools.dotc.core.Contexts._
5+
6+
import scala.quoted._
7+
8+
class ExprRun(comp: Compiler, ictx: Context) extends Run(comp, ictx) {
9+
def compileExpr(expr: Expr[_]): Unit = {
10+
val units = new ExprCompilationUnit(expr) :: Nil
11+
compileUnits(units)
12+
}
13+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package dotty.tools.dotc.quoted
2+
3+
import dotty.tools.dotc.Driver
4+
import dotty.tools.dotc.core.Contexts.Context
5+
import dotty.tools.dotc.core.StdNames._
6+
import dotty.tools.io.VirtualDirectory
7+
8+
import dotty.tools.repl.AbstractFileClassLoader
9+
10+
import scala.quoted.Expr
11+
12+
import java.io.ByteArrayOutputStream
13+
import java.io.PrintStream
14+
import java.nio.charset.StandardCharsets
15+
16+
class QuoteDriver extends Driver {
17+
18+
def run[T](expr: Expr[T]): T = {
19+
val ctx: Context = initCtx.fresh
20+
// TODO enable optimisation?
21+
// ctx.settings.optimise.update(true)(ctx)
22+
23+
val outDir = new VirtualDirectory("(memory)", None)
24+
25+
new ExprCompiler(outDir).newRun(ctx).compileExpr(expr)
26+
27+
val classLoader = new AbstractFileClassLoader(outDir, this.getClass.getClassLoader)
28+
29+
val clazz = classLoader.loadClass(nme.QUOTE.toString)
30+
val method = clazz.getMethod("apply")
31+
val instance = clazz.newInstance()
32+
33+
method.invoke(instance).asInstanceOf[T]
34+
}
35+
36+
def show(expr: Expr[_]): String = {
37+
val ctx: Context = initCtx.fresh
38+
ctx.settings.color.update("never")(ctx) // TODO support colored show
39+
val baos = new ByteArrayOutputStream
40+
var ps: PrintStream = null
41+
try {
42+
ps = new PrintStream(baos, true, "utf-8")
43+
44+
new ExprDecompiler(ps).newRun(ctx).compileExpr(expr)
45+
46+
new String(baos.toByteArray, StandardCharsets.UTF_8)
47+
}
48+
finally if (ps != null) ps.close()
49+
}
50+
51+
override def initCtx: Context = {
52+
val ictx = super.initCtx.fresh
53+
val compilerClasspath = System.getProperty("dotty.tools.dotc.classpath")
54+
assert(compilerClasspath ne null, "System property `dotty.tools.dotc.classpath` is not set.")
55+
val classpath = System.getProperty("java.class.path")
56+
val scalaLib = classpath.split(":").filter(_.contains("scala-library")).mkString(":")
57+
ictx.settings.classpath.update(compilerClasspath + ":" + scalaLib)(ictx)
58+
ictx
59+
}
60+
61+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package dotty.tools.dotc.quoted
2+
3+
import java.io.PrintStream
4+
5+
import dotty.tools.dotc.core.Contexts._
6+
import dotty.tools.dotc.core.Phases.Phase
7+
8+
class QuotePrinter(out: PrintStream) extends Phase {
9+
10+
override def phaseName: String = "quotePrinter"
11+
12+
override def run(implicit ctx: Context): Unit = {
13+
val unit = ctx.compilationUnit
14+
out.print(unit.tpdTree.show)
15+
}
16+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package dotty.tools.dotc.quoted
2+
3+
import scala.quoted.Expr
4+
import scala.runtime.quoted._
5+
6+
object Runners {
7+
implicit def runner[T]: Runner[T] = (expr: Expr[T]) => new QuoteDriver().run(expr)
8+
implicit def show[T]: Show[T] = (expr: Expr[T]) => new QuoteDriver().show(expr)
9+
}

compiler/test/dotty/Jars.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ object Jars {
1414
val dottyInterfaces: String = sys.env.get("DOTTY_INTERFACE")
1515
.getOrElse(Properties.dottyInterfaces)
1616

17+
/** Scala asm Jar */
18+
lazy val scalaAsm: String =
19+
findJarFromRuntime("scala-asm-6.0.0-scala-1")
20+
1721
/** Dotty extras classpath from env or properties */
1822
val dottyExtras: List[String] = sys.env.get("DOTTY_EXTRAS")
1923
.map(_.split(":").toList).getOrElse(Properties.dottyExtras)
@@ -25,6 +29,10 @@ object Jars {
2529
val dottyTestDeps: List[String] =
2630
dottyLib :: dottyCompiler :: dottyInterfaces :: dottyExtras
2731

32+
/** Dotty runtime with compiler dependencies, used for quoted.Expr.run */
33+
val dottyRunWithCompiler: List[String] =
34+
dottyLib :: dottyCompiler :: dottyInterfaces :: scalaAsm :: Nil
35+
2836
def scalaLibrary: String = sys.env.get("DOTTY_SCALA_LIBRARY")
2937
.getOrElse(findJarFromRuntime("scala-library-2."))
3038

0 commit comments

Comments
 (0)