From 1d53b2ba45cba536c3369e7b8c300666c603d460 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Wed, 19 Jun 2013 16:33:19 +0200 Subject: 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. --- src/compiler/scala/tools/reflect/ToolBox.scala | 10 +++++ .../scala/tools/reflect/ToolBoxFactory.scala | 47 +++++++++++++++++----- 2 files changed, 46 insertions(+), 11 deletions(-) (limited to 'src/compiler') 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, "").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)() } } -- cgit v1.2.3 From 604707cbf5418ae2fd903e80da44e75f62ab690d Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Thu, 6 Feb 2014 18:16:20 +0100 Subject: addresses pull request feedback --- src/compiler/scala/tools/reflect/ToolBox.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/reflect/ToolBox.scala b/src/compiler/scala/tools/reflect/ToolBox.scala index 8b0dd66ac2..2a3851ba85 100644 --- a/src/compiler/scala/tools/reflect/ToolBox.scala +++ b/src/compiler/scala/tools/reflect/ToolBox.scala @@ -107,8 +107,7 @@ trait ToolBox[U <: scala.reflect.api.Universe] { * 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`. + * `compile`, `define` or `eval`. To refer to the generated definition in a tree, use q"$sym". */ def define(tree: u.ImplDef): u.Symbol -- cgit v1.2.3