Skip to content

Commit 385a87f

Browse files
committed
Implement run/show for quoted expressions
1 parent db47f14 commit 385a87f

27 files changed

+401
-14
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: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
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+
/* Compilation unit containing the contents of a quoted expression */
9+
class ExprCompilationUnit(val expr: Expr[_]) extends CompilationUnit(NoSource) {
10+
override def toString = s"Expr($expr)"
11+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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+
/** Compiler that takes the contents of a quoted expression `expr` and produces
12+
* a class file with `class ' { def apply: Object = expr }`.
13+
*/
14+
class ExprCompiler(directory: VirtualDirectory) extends Compiler {
15+
16+
/** A GenBCode phase that outputs to a virtual directory */
17+
private class ExprGenBCode extends GenBCode {
18+
override def phaseName = "genBCode"
19+
override def outputDir(implicit ctx: Context) = directory
20+
}
21+
22+
override def phases: List[List[Phase]] = {
23+
val backendPhases = super.phases.dropWhile {
24+
case List(_: Pickler) => false
25+
case _ => true
26+
}.tail
27+
28+
List(new ExprFrontend(putInClass = true)) ::
29+
Phases.replace(classOf[GenBCode], _ => new ExprGenBCode :: Nil, backendPhases)
30+
}
31+
32+
override def newRun(implicit ctx: Context): ExprRun = {
33+
reset()
34+
new ExprRun(this, ctx.addMode(Mode.ReadPositions))
35+
}
36+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package dotty.tools.dotc.quoted
2+
3+
import java.io.PrintStream
4+
5+
import dotty.tools.dotc.core.Phases.Phase
6+
7+
/** Compiler that takes the contents of a quoted expression `expr` and produces outputs
8+
* the pretty printed code.
9+
*/
10+
class ExprDecompiler(out: PrintStream) extends ExprCompiler(null) {
11+
override def phases: List[List[Phase]] = List(
12+
List(new ExprFrontend(putInClass = false)), // Create class from Expr
13+
List(new QuotePrinter(out)) // Print all loaded classes
14+
)
15+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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.core.quoted.PickledQuotes
12+
import dotty.tools.dotc.typer.FrontEnd
13+
import dotty.tools.dotc.util.Positions._
14+
import dotty.tools.dotc.util.SourceFile
15+
import dotty.tools.io._
16+
17+
import scala.quoted.Expr
18+
19+
/** Frontend that receives scala.quoted.Expr as input */
20+
class ExprFrontend(putInClass: Boolean) extends FrontEnd {
21+
import tpd._
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 PickledQuotes.quotedToTree(exprUnit.expr)
31+
val source = new SourceFile("", Seq())
32+
CompilationUnit.mkCompilationUnit(source, tree, forceTrees = true)
33+
}
34+
}
35+
36+
/** Places the contents of expr in a compilable tree for a class
37+
* with the following format.
38+
* `package __root__ { class ' { def apply: Any = <expr> } }`
39+
*/
40+
private def inClass(expr: Expr[_])(implicit ctx: Context): Tree = {
41+
val pos = Position(0)
42+
val assocFile = new PlainFile(Path("<quote>"))
43+
44+
val cls = ctx.newCompleteClassSymbol(defn.RootClass, nme.QUOTE.toTypeName, EmptyFlags,
45+
defn.ObjectType :: Nil, newScope, coord = pos, assocFile = assocFile).entered.asClass
46+
cls.enter(ctx.newDefaultConstructor(cls), EmptyScope)
47+
val meth = ctx.newSymbol(cls, nme.apply, Method, ExprType(defn.AnyType), coord = pos).entered
48+
49+
val quoted = PickledQuotes.quotedToTree(expr)(ctx.withOwner(meth))
50+
51+
val run = DefDef(meth, quoted)
52+
val classTree = ClassDef(cls, DefDef(cls.primaryConstructor.asTerm), run :: Nil)
53+
PackageDef(ref(defn.RootPackage).asInstanceOf[Ident], classTree :: Nil).withPos(pos)
54+
}
55+
}
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: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
/** Pretty prints the compilation unit to an output stream */
9+
class QuotePrinter(out: PrintStream) extends Phase {
10+
11+
override def phaseName: String = "quotePrinter"
12+
13+
override def run(implicit ctx: Context): Unit = {
14+
val unit = ctx.compilationUnit
15+
out.print(unit.tpdTree.show)
16+
}
17+
}
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 scala.quoted.Expr
4+
import scala.runtime.quoted._
5+
6+
/** Default runners for quoted expressions */
7+
object Runners {
8+
implicit def runner[T]: Runner[T] = (expr: Expr[T]) => new QuoteDriver().run(expr)
9+
implicit def show[T]: Show[T] = (expr: Expr[T]) => new QuoteDriver().show(expr)
10+
}

compiler/src/dotty/tools/dotc/transform/Splicer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ object Splicer {
2424
/** Splice the Tree for a Quoted expression which is constructed via a reflective call to the given method */
2525
private def reflectiveSplice(tree: Tree)(implicit ctx: Context): Tree = {
2626
val interpreter = new Interpreter
27-
interpreter.interpretTree[quoted.Expr[_]](tree).map(PickledQuotes.quotedToTree(_)).getOrElse(tree)
27+
interpreter.interpretTree[scala.quoted.Expr[_]](tree).map(PickledQuotes.quotedToTree(_)).getOrElse(tree)
2828
}
2929

3030
}

0 commit comments

Comments
 (0)