diff options
-rw-r--r-- | src/compiler/scala/tools/reflect/ToolBox.scala | 10 | ||||
-rw-r--r-- | src/compiler/scala/tools/reflect/ToolBoxFactory.scala | 47 | ||||
-rw-r--r-- | test/files/run/t7570a.check | 1 | ||||
-rw-r--r-- | test/files/run/t7570a.scala | 11 | ||||
-rw-r--r-- | test/files/run/t7570b.check | 1 | ||||
-rw-r--r-- | test/files/run/t7570b.scala | 17 | ||||
-rw-r--r-- | test/files/run/t7570c.check | 2 | ||||
-rw-r--r-- | test/files/run/t7570c.scala | 13 |
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 |