summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2013-06-19 16:33:19 +0200
committerEugene Burmako <xeno.by@gmail.com>2014-01-24 09:18:04 +0300
commit1d53b2ba45cba536c3369e7b8c300666c603d460 (patch)
treec8d4cc7bb166b776ebc70c7696d82cad9e9f4194
parentf22ddce265e8622e95f5e9cab4d38168bf2c3bf8 (diff)
downloadscala-1d53b2ba45cba536c3369e7b8c300666c603d460.tar.gz
scala-1d53b2ba45cba536c3369e7b8c300666c603d460.tar.bz2
scala-1d53b2ba45cba536c3369e7b8c300666c603d460.zip
SI-7570 top-level codegen for toolboxes
Provides a way to inject top-level classes, traits and modules into toolbox universes. Previously that was impossible, because compile and eval both wrap their arguments into an enclosing method of a synthetic module, which makes it impossible to later on refer to any definitions from the outside.
-rw-r--r--src/compiler/scala/tools/reflect/ToolBox.scala10
-rw-r--r--src/compiler/scala/tools/reflect/ToolBoxFactory.scala47
-rw-r--r--test/files/run/t7570a.check1
-rw-r--r--test/files/run/t7570a.scala11
-rw-r--r--test/files/run/t7570b.check1
-rw-r--r--test/files/run/t7570b.scala17
-rw-r--r--test/files/run/t7570c.check2
-rw-r--r--test/files/run/t7570c.scala13
8 files changed, 91 insertions, 11 deletions
diff --git a/src/compiler/scala/tools/reflect/ToolBox.scala b/src/compiler/scala/tools/reflect/ToolBox.scala
index 4c1bc794bc..8b0dd66ac2 100644
--- a/src/compiler/scala/tools/reflect/ToolBox.scala
+++ b/src/compiler/scala/tools/reflect/ToolBox.scala
@@ -102,6 +102,16 @@ trait ToolBox[U <: scala.reflect.api.Universe] {
*/
def compile(tree: u.Tree): () => Any
+ /** Defines a top-level class, trait or module in this ToolBox,
+ * putting it into a uniquely-named package and returning a symbol that references the defined entity.
+ * For a ClassDef, a ClassSymbol is returned, and for a ModuleDef, a ModuleSymbol is returned (not a module class, but a module itself).
+ *
+ * This method can be used to generate definitions that will later be re-used by subsequent calls to
+ * `compile`, `define` or `eval`. To refer to the generated definition, use the resulting symbol
+ * by wrapping it into an `Ident`.
+ */
+ def define(tree: u.ImplDef): u.Symbol
+
/** Compiles and runs a tree using this ToolBox.
* Is equivalent to `compile(tree)()`.
*/
diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
index 4a8c91bd1b..c54f2d06ba 100644
--- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
+++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
@@ -193,6 +193,19 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
analyzer.inferImplicit(tree, pt, isView, currentTyper.context, silent, withMacrosDisabled, pos, (pos, msg) => throw ToolBoxError(msg))
})
+ private def wrapInPackageAndCompile(packageName: TermName, tree: ImplDef): Symbol = {
+ val pdef = PackageDef(Ident(packageName), List(tree))
+ val unit = new CompilationUnit(NoSourceFile)
+ unit.body = pdef
+
+ val run = new Run
+ reporter.reset()
+ run.compileUnits(List(unit), run.namerPhase)
+ throwIfErrors()
+
+ tree.symbol
+ }
+
def compile(expr0: Tree): () => Any = {
val expr = wrapIntoTerm(expr0)
@@ -200,7 +213,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
val thunks = freeTerms map (fte => () => fte.value) // need to be lazy in order not to distort evaluation order
verify(expr)
- def wrap(expr0: Tree): ModuleDef = {
+ def wrapInModule(expr0: Tree): ModuleDef = {
val (expr, freeTerms) = extractFreeTerms(expr0, wrapFreeTermRefs = true)
val (obj, _) = rootMirror.EmptyPackageClass.newModuleAndClassSymbol(
@@ -241,17 +254,10 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
cleanedUp.asInstanceOf[ModuleDef]
}
- val mdef = wrap(expr)
- val pdef = PackageDef(Ident(mdef.name), List(mdef))
- val unit = new CompilationUnit(NoSourceFile)
- unit.body = pdef
+ val mdef = wrapInModule(expr)
+ val msym = wrapInPackageAndCompile(mdef.name, mdef)
- val run = new Run
- reporter.reset()
- run.compileUnits(List(unit), run.namerPhase)
- throwIfErrors()
-
- val className = mdef.symbol.fullName
+ val className = msym.fullName
if (settings.debug) println("generated: "+className)
def moduleFileName(className: String) = className + "$"
val jclazz = jClass.forName(moduleFileName(className), true, classLoader)
@@ -278,6 +284,13 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
}
}
+ def define(tree: ImplDef): Symbol = {
+ val freeTerms = tree.freeTerms
+ if (freeTerms.nonEmpty) throw ToolBoxError(s"reflective toolbox has failed: cannot have free terms in a top-level definition")
+ verify(tree)
+ wrapInPackageAndCompile(nextWrapperModuleName(), tree)
+ }
+
def parse(code: String): Tree = {
reporter.reset()
val tree = gen.mkTreeOrBlock(newUnitParser(code, "<toolbox>").parseStatsOrPackages())
@@ -421,6 +434,18 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
compiler.compile(ctree)
}
+ def define(tree: u.ImplDef): u.Symbol = withCompilerApi { compilerApi =>
+ import compilerApi._
+
+ if (compiler.settings.verbose) println("importing "+tree)
+ val ctree: compiler.ImplDef = importer.importTree(tree).asInstanceOf[compiler.ImplDef]
+
+ if (compiler.settings.verbose) println("defining "+ctree)
+ val csym: compiler.Symbol = compiler.define(ctree)
+ val usym = exporter.importSymbol(csym)
+ usym
+ }
+
def eval(tree: u.Tree): Any = compile(tree)()
}
}
diff --git a/test/files/run/t7570a.check b/test/files/run/t7570a.check
new file mode 100644
index 0000000000..3cc58df837
--- /dev/null
+++ b/test/files/run/t7570a.check
@@ -0,0 +1 @@
+C
diff --git a/test/files/run/t7570a.scala b/test/files/run/t7570a.scala
new file mode 100644
index 0000000000..b8b4ddeaf2
--- /dev/null
+++ b/test/files/run/t7570a.scala
@@ -0,0 +1,11 @@
+import scala.reflect.runtime.universe._
+import scala.reflect.runtime.{currentMirror => cm}
+import scala.tools.reflect.ToolBox
+import definitions._
+import Flag._
+
+object Test extends App {
+ val tb = cm.mkToolBox()
+ val csym = tb.define(q"""class C { override def toString = "C" }""")
+ println(tb.eval(q"new $csym"))
+} \ No newline at end of file
diff --git a/test/files/run/t7570b.check b/test/files/run/t7570b.check
new file mode 100644
index 0000000000..0c28247025
--- /dev/null
+++ b/test/files/run/t7570b.check
@@ -0,0 +1 @@
+compilation failed: reflective toolbox has failed: cannot have free terms in a top-level definition
diff --git a/test/files/run/t7570b.scala b/test/files/run/t7570b.scala
new file mode 100644
index 0000000000..f1db193186
--- /dev/null
+++ b/test/files/run/t7570b.scala
@@ -0,0 +1,17 @@
+import scala.reflect.runtime.universe._
+import scala.reflect.runtime.{currentMirror => cm}
+import scala.tools.reflect.{ToolBox, ToolBoxError}
+import definitions._
+import Flag._
+
+object Test extends App {
+ val tb = cm.mkToolBox()
+ val msg = build.newFreeTerm("msg", "C")
+ build.setTypeSignature(msg, typeOf[String])
+ try {
+ val csym = tb.define(q"""class C { override def toString = $msg }""")
+ println(tb.eval(q"new $csym"))
+ } catch {
+ case ToolBoxError(message, _) => println(s"compilation failed: $message")
+ }
+} \ No newline at end of file
diff --git a/test/files/run/t7570c.check b/test/files/run/t7570c.check
new file mode 100644
index 0000000000..61e659d9e0
--- /dev/null
+++ b/test/files/run/t7570c.check
@@ -0,0 +1,2 @@
+(class C,true,false,false)
+(object D,false,true,false)
diff --git a/test/files/run/t7570c.scala b/test/files/run/t7570c.scala
new file mode 100644
index 0000000000..a5bdbffe18
--- /dev/null
+++ b/test/files/run/t7570c.scala
@@ -0,0 +1,13 @@
+import scala.reflect.runtime.universe._
+import scala.reflect.runtime.{currentMirror => cm}
+import scala.tools.reflect.{ToolBox, ToolBoxError}
+import definitions._
+import Flag._
+
+object Test extends App {
+ val tb = cm.mkToolBox()
+ val csym = tb.define(q"""class C { override def toString = "C" }""")
+ println((csym, csym.isClass, csym.isModule, csym.isModuleClass))
+ val dsym = tb.define(q"""object D { override def toString = "D" }""".asInstanceOf[ModuleDef])
+ println((dsym, dsym.isClass, dsym.isModule, dsym.isModuleClass))
+} \ No newline at end of file