diff options
Diffstat (limited to 'src/compiler/scala/tools/reflect/ToolBoxFactory.scala')
-rw-r--r-- | src/compiler/scala/tools/reflect/ToolBoxFactory.scala | 203 |
1 files changed, 110 insertions, 93 deletions
diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 8803980dac..650598f9db 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -5,9 +5,10 @@ import scala.tools.nsc.reporters._ import scala.tools.nsc.CompilerCommand import scala.tools.nsc.Global import scala.tools.nsc.typechecker.Modes -import scala.tools.nsc.io.VirtualDirectory +import scala.tools.nsc.io.{AbstractFile, VirtualDirectory} import scala.tools.nsc.interpreter.AbstractFileClassLoader import scala.tools.nsc.util.FreshNameCreator +import scala.tools.nsc.util.CommandLineParser import scala.tools.nsc.ast.parser.Tokens.EOF import scala.reflect.internal.Flags._ import scala.reflect.internal.util.{BatchSourceFile, NoSourceFile, NoFile} @@ -17,6 +18,7 @@ import scala.reflect.NameTransformer import scala.reflect.api.JavaUniverse import scala.reflect.io.NoAbstractFile import scala.tools.nsc.interactive.RangePositions +import scala.reflect.internal.FatalError abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => @@ -32,6 +34,13 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => lazy val classLoader = new AbstractFileClassLoader(virtualDirectory, factorySelf.mirror.classLoader) lazy val mirror: u.Mirror = u.runtimeMirror(classLoader) + lazy val arguments = CommandLineParser.tokenize(options) + lazy val virtualDirectory = + arguments.iterator.sliding(2).collectFirst{ case Seq("-d", dir) => dir } match { + case Some(outDir) => AbstractFile.getDirectory(outDir) + case None => new VirtualDirectory("(memory)", None) + } + class ToolBoxGlobal(settings: scala.tools.nsc.Settings, reporter: Reporter) extends ReflectGlobal(settings, reporter, toolBoxSelf.classLoader) { import definitions._ @@ -50,7 +59,6 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => } // should be called after every use of ToolBoxGlobal in order to prevent leaks - // there's the `withCleanupCaches` method defined below, which provides a convenient interface for that def cleanupCaches(): Unit = { perRunCaches.clearAll() undoLog.clear() @@ -59,11 +67,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => lastSeenContext = null } - def withCleanupCaches[T](body: => T): T = - try body - finally cleanupCaches() - - def verify(expr: Tree): Unit = { + def verify(expr: Tree): Tree = { // Previously toolboxes used to typecheck their inputs before compiling. // Actually, the initial demo by Martin first typechecked the reified tree, // then ran it, which typechecked it again, and only then launched the @@ -84,14 +88,8 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => msg += "if you have troubles tracking free type variables, consider using -Xlog-free-types" throw ToolBoxError(msg) } - } - - def wrapIntoTerm(tree: Tree): Tree = - if (!tree.isTerm) Block(List(tree), Literal(Constant(()))) else tree - def unwrapFromTerm(tree: Tree): Tree = tree match { - case Block(List(tree), Literal(Constant(()))) => tree - case tree => tree + expr } def extractFreeTerms(expr0: Tree, wrapFreeTermRefs: Boolean): (Tree, scala.collection.mutable.LinkedHashMap[FreeTermSymbol, TermName]) = { @@ -121,50 +119,51 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => } def transformDuringTyper(expr0: Tree, withImplicitViewsDisabled: Boolean, withMacrosDisabled: Boolean)(transform: (analyzer.Typer, Tree) => Tree): Tree = { - verify(expr0) - - // need to wrap the expr, because otherwise you won't be able to typecheck macros against something that contains free vars - var (expr, freeTerms) = extractFreeTerms(expr0, wrapFreeTermRefs = false) - val dummies = freeTerms.map{ case (freeTerm, name) => ValDef(NoMods, name, TypeTree(freeTerm.info), Select(Ident(PredefModule), newTermName("$qmark$qmark$qmark"))) }.toList - expr = Block(dummies, wrapIntoTerm(expr)) - - // [Eugene] how can we implement that? - // !!! Why is this is in the empty package? If it's only to make - // it inaccessible then please put it somewhere designed for that - // rather than polluting the empty package with synthetics. - val ownerClass = rootMirror.EmptyPackageClass.newClassSymbol(newTypeName("<expression-owner>")) - build.setTypeSignature(ownerClass, ClassInfoType(List(ObjectClass.tpe), newScope, ownerClass)) - val owner = ownerClass.newLocalDummy(expr.pos) - var currentTyper = analyzer.newTyper(analyzer.rootContext(NoCompilationUnit, EmptyTree).make(expr, owner)) - val wrapper1 = if (!withImplicitViewsDisabled) (currentTyper.context.withImplicitsEnabled[Tree] _) else (currentTyper.context.withImplicitsDisabled[Tree] _) - val wrapper2 = if (!withMacrosDisabled) (currentTyper.context.withMacrosEnabled[Tree] _) else (currentTyper.context.withMacrosDisabled[Tree] _) - def wrapper (tree: => Tree) = wrapper1(wrapper2(tree)) - - val run = new Run - run.symSource(ownerClass) = NoAbstractFile // need to set file to something different from null, so that currentRun.defines works - phase = run.typerPhase // need to set a phase to something <= typerPhase, otherwise implicits in typedSelect will be disabled - currentTyper.context.setReportErrors() // need to manually set context mode, otherwise typer.silent will throw exceptions - reporter.reset() - - val expr1 = wrapper(transform(currentTyper, expr)) - var (dummies1, unwrapped) = expr1 match { - case Block(dummies, unwrapped) => (dummies, unwrapped) - case unwrapped => (Nil, unwrapped) - } - var invertedIndex = freeTerms map (_.swap) - // todo. also fixup singleton types - unwrapped = new Transformer { - override def transform(tree: Tree): Tree = - tree match { - case Ident(name) if invertedIndex contains name => - Ident(invertedIndex(name)) setType tree.tpe - case _ => - super.transform(tree) - } - }.transform(unwrapped) - new TreeTypeSubstituter(dummies1 map (_.symbol), dummies1 map (dummy => SingleType(NoPrefix, invertedIndex(dummy.symbol.name)))).traverse(unwrapped) - unwrapped = if (expr0.isTerm) unwrapped else unwrapFromTerm(unwrapped) - unwrapped + wrappingIntoTerm(verify(expr0))(expr1 => { + // need to wrap the expr, because otherwise you won't be able to typecheck macros against something that contains free vars + val exprAndFreeTerms = extractFreeTerms(expr1, wrapFreeTermRefs = false) + var expr2 = exprAndFreeTerms._1 + val freeTerms = exprAndFreeTerms._2 + val dummies = freeTerms.map{ case (freeTerm, name) => ValDef(NoMods, name, TypeTree(freeTerm.info), Select(Ident(PredefModule), newTermName("$qmark$qmark$qmark"))) }.toList + expr2 = Block(dummies, expr2) + + // [Eugene] how can we implement that? + // !!! Why is this is in the empty package? If it's only to make + // it inaccessible then please put it somewhere designed for that + // rather than polluting the empty package with synthetics. + val ownerClass = rootMirror.EmptyPackageClass.newClassSymbol(newTypeName("<expression-owner>")) + build.setTypeSignature(ownerClass, ClassInfoType(List(ObjectClass.tpe), newScope, ownerClass)) + val owner = ownerClass.newLocalDummy(expr2.pos) + var currentTyper = analyzer.newTyper(analyzer.rootContext(NoCompilationUnit, EmptyTree).make(expr2, owner)) + val wrapper1 = if (!withImplicitViewsDisabled) (currentTyper.context.withImplicitsEnabled[Tree] _) else (currentTyper.context.withImplicitsDisabled[Tree] _) + val wrapper2 = if (!withMacrosDisabled) (currentTyper.context.withMacrosEnabled[Tree] _) else (currentTyper.context.withMacrosDisabled[Tree] _) + def wrapper (tree: => Tree) = wrapper1(wrapper2(tree)) + + val run = new Run + run.symSource(ownerClass) = NoAbstractFile // need to set file to something different from null, so that currentRun.defines works + phase = run.typerPhase // need to set a phase to something <= typerPhase, otherwise implicits in typedSelect will be disabled + currentTyper.context.setReportErrors() // need to manually set context mode, otherwise typer.silent will throw exceptions + reporter.reset() + + val expr3 = wrapper(transform(currentTyper, expr2)) + var (dummies1, result) = expr3 match { + case Block(dummies, result) => (dummies, result) + case result => (Nil, result) + } + var invertedIndex = freeTerms map (_.swap) + // todo. also fixup singleton types + result = new Transformer { + override def transform(tree: Tree): Tree = + tree match { + case Ident(name) if invertedIndex contains name => + Ident(invertedIndex(name)) setType tree.tpe + case _ => + super.transform(tree) + } + }.transform(result) + new TreeTypeSubstituter(dummies1 map (_.symbol), dummies1 map (dummy => SingleType(NoPrefix, invertedIndex(dummy.symbol.name)))).traverse(result) + result + }) } def typeCheck(expr: Tree, pt: Type, silent: Boolean, withImplicitViewsDisabled: Boolean, withMacrosDisabled: Boolean): Tree = @@ -317,42 +316,51 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => } } - // todo. is not going to work with quoted arguments with embedded whitespaces - lazy val arguments = options.split(" ") + trait CompilerApi { + val compiler: ToolBoxGlobal + val importer: compiler.Importer { val from: u.type } + val exporter: u.Importer { val from: compiler.type } + } - lazy val virtualDirectory = - (arguments zip arguments.tail).collect{ case ("-d", dir) => dir }.lastOption match { - case Some(outDir) => scala.tools.nsc.io.AbstractFile.getDirectory(outDir) - case None => new VirtualDirectory("(memory)", None) + object withCompilerApi { + private object api extends CompilerApi { + lazy val compiler: ToolBoxGlobal = { + try { + val errorFn: String => Unit = msg => frontEnd.log(scala.reflect.internal.util.NoPosition, msg, frontEnd.ERROR) + val command = new CompilerCommand(arguments.toList, errorFn) + val settings = command.settings + settings.outputDirs setSingleOutput virtualDirectory + val reporter = frontEndToReporter(frontEnd, command.settings) + val instance = + if (settings.Yrangepos.value) new ToolBoxGlobal(settings, reporter) with RangePositions + else new ToolBoxGlobal(settings, reporter) + if (frontEnd.hasErrors) { + var msg = "reflective compilation has failed: cannot initialize the compiler: " + EOL + EOL + msg += frontEnd.infos map (_.msg) mkString EOL + throw ToolBoxError(msg) + } + instance + } catch { + case ex: Throwable => + var msg = "reflective compilation has failed: cannot initialize the compiler due to %s".format(ex.toString) + throw ToolBoxError(msg, ex) + } + } + + lazy val importer = compiler.mkImporter(u) + lazy val exporter = importer.reverse } - lazy val compiler: ToolBoxGlobal = { - try { - val errorFn: String => Unit = msg => frontEnd.log(scala.reflect.internal.util.NoPosition, msg, frontEnd.ERROR) - val command = new CompilerCommand(arguments.toList, errorFn) - val settings = command.settings - settings.outputDirs setSingleOutput virtualDirectory - val reporter = frontEndToReporter(frontEnd, command.settings) - val instance = - if (settings.Yrangepos.value) new ToolBoxGlobal(settings, reporter) with RangePositions - else new ToolBoxGlobal(settings, reporter) - if (frontEnd.hasErrors) { - var msg = "reflective compilation has failed: cannot initialize the compiler: " + EOL + EOL - msg += frontEnd.infos map (_.msg) mkString EOL - throw ToolBoxError(msg) - } - instance - } catch { - case ex: Throwable => - var msg = "reflective compilation has failed: cannot initialize the compiler due to %s".format(ex.toString) - throw ToolBoxError(msg, ex) + def apply[T](f: CompilerApi => T): T = { + object FatalError { def unapply(ex: Throwable) = ex match { case _: FatalError | _: AssertionError => Some(ex); case _ => None } } + try f(api) catch { case FatalError(ex) => throw ToolBoxError(s"fatal compiler error", ex) } + finally api.compiler.cleanupCaches() } } - lazy val importer = compiler.mkImporter(u) - lazy val exporter = importer.reverse + def typeCheck(tree: u.Tree, expectedType: u.Type, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): u.Tree = withCompilerApi { compilerApi => + import compilerApi._ - def typeCheck(tree: u.Tree, expectedType: u.Type, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): u.Tree = compiler.withCleanupCaches { if (compiler.settings.verbose.value) println("importing "+tree+", expectedType = "+expectedType) var ctree: compiler.Tree = importer.importTree(tree) var cexpectedType: compiler.Type = importer.importType(expectedType) @@ -372,7 +380,9 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => inferImplicit(tree, viewTpe, isView = true, silent = silent, withMacrosDisabled = withMacrosDisabled, pos = pos) } - private def inferImplicit(tree: u.Tree, pt: u.Type, isView: Boolean, silent: Boolean, withMacrosDisabled: Boolean, pos: u.Position): u.Tree = compiler.withCleanupCaches { + private def inferImplicit(tree: u.Tree, pt: u.Type, isView: Boolean, silent: Boolean, withMacrosDisabled: Boolean, pos: u.Position): u.Tree = withCompilerApi { compilerApi => + import compilerApi._ + if (compiler.settings.verbose.value) println("importing "+pt, ", tree = "+tree+", pos = "+pos) var ctree: compiler.Tree = importer.importTree(tree) var cpt: compiler.Type = importer.importType(pt) @@ -384,31 +394,38 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => uitree } - def resetAllAttrs(tree: u.Tree): u.Tree = { + def resetAllAttrs(tree: u.Tree): u.Tree = withCompilerApi { compilerApi => + import compilerApi._ val ctree: compiler.Tree = importer.importTree(tree) val ttree: compiler.Tree = compiler.resetAllAttrs(ctree) val uttree = exporter.importTree(ttree) uttree } - def resetLocalAttrs(tree: u.Tree): u.Tree = { + def resetLocalAttrs(tree: u.Tree): u.Tree = withCompilerApi { compilerApi => + import compilerApi._ val ctree: compiler.Tree = importer.importTree(tree) val ttree: compiler.Tree = compiler.resetLocalAttrs(ctree) val uttree = exporter.importTree(ttree) uttree } - def showAttributed(tree: u.Tree, printTypes: Boolean = true, printIds: Boolean = true, printKinds: Boolean = false): String = + def showAttributed(tree: u.Tree, printTypes: Boolean = true, printIds: Boolean = true, printKinds: Boolean = false): String = withCompilerApi { compilerApi => + import compilerApi._ compiler.showAttributed(importer.importTree(tree), printTypes, printIds, printKinds) + } - def parse(code: String): u.Tree = { + def parse(code: String): u.Tree = withCompilerApi { compilerApi => + import compilerApi._ if (compiler.settings.verbose.value) println("parsing "+code) val ctree: compiler.Tree = compiler.parse(code) val utree = exporter.importTree(ctree) utree } - def compile(tree: u.Tree): () => Any = { + def compile(tree: u.Tree): () => Any = withCompilerApi { compilerApi => + import compilerApi._ + if (compiler.settings.verbose.value) println("importing "+tree) val ctree: compiler.Tree = importer.importTree(tree) |