Skip to content

Commit 4133eb8

Browse files
committed
refactors ToolBoxGlobal wrappers
Centralizes wrappers in a single location and makes it impossible to use the compiler without going through that location. Also fixes the option string tokenization bug that was there for ages. This time I figured out that there's already an implementation of the tokenizer in our compiler, so I just used it :)
1 parent 7122560 commit 4133eb8

File tree

1 file changed

+74
-64
lines changed

1 file changed

+74
-64
lines changed

src/compiler/scala/tools/reflect/ToolBoxFactory.scala

Lines changed: 74 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ package scala
22
package tools
33
package reflect
44

5+
import scala.tools.cmd.CommandLineParser
6+
import scala.tools.nsc.Global
57
import scala.tools.nsc.reporters._
68
import scala.tools.nsc.CompilerCommand
7-
import scala.tools.nsc.io.VirtualDirectory
9+
import scala.tools.nsc.io.{AbstractFile, VirtualDirectory}
810
import scala.tools.nsc.util.AbstractFileClassLoader
911
import scala.reflect.internal.Flags._
1012
import scala.reflect.internal.util.{BatchSourceFile, NoSourceFile, NoFile}
@@ -29,6 +31,13 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
2931
lazy val classLoader = new AbstractFileClassLoader(virtualDirectory, factorySelf.mirror.classLoader)
3032
lazy val mirror: u.Mirror = u.runtimeMirror(classLoader)
3133

34+
lazy val arguments = CommandLineParser.tokenize(options)
35+
lazy val virtualDirectory =
36+
arguments.iterator.sliding(2).collectFirst{ case Seq("-d", dir) => dir } match {
37+
case Some(outDir) => AbstractFile.getDirectory(outDir)
38+
case None => new VirtualDirectory("(memory)", None)
39+
}
40+
3241
class ToolBoxGlobal(settings: scala.tools.nsc.Settings, reporter0: Reporter)
3342
extends ReflectGlobal(settings, reporter0, toolBoxSelf.classLoader) {
3443
import definitions._
@@ -47,7 +56,6 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
4756
}
4857

4958
// should be called after every use of ToolBoxGlobal in order to prevent leaks
50-
// there's the `withCleanupCaches` method defined below, which provides a convenient interface for that
5159
def cleanupCaches(): Unit = {
5260
perRunCaches.clearAll()
5361
undoLog.clear()
@@ -56,14 +64,6 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
5664
lastSeenContext = null
5765
}
5866

59-
def withCleanupCaches[T](body: => T): T =
60-
try body
61-
finally cleanupCaches()
62-
63-
def wrappingFatalErrors[T](body: => T): T =
64-
try body
65-
catch { case ex: FatalError => throw ToolBoxError(s"fatal compiler error", ex) }
66-
6767
def verify(expr: Tree): Unit = {
6868
// Previously toolboxes used to typecheck their inputs before compiling.
6969
// Actually, the initial demo by Martin first typechecked the reified tree,
@@ -307,50 +307,56 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
307307
}
308308
}
309309

310-
// todo. is not going to work with quoted arguments with embedded whitespaces
311-
lazy val arguments = options.split(" ")
310+
trait CompilerApi {
311+
val compiler: ToolBoxGlobal
312+
val importer: compiler.Importer { val from: u.type }
313+
val exporter: u.Importer { val from: compiler.type }
314+
}
312315

313-
lazy val virtualDirectory =
314-
(arguments zip arguments.tail).collect{ case ("-d", dir) => dir }.lastOption match {
315-
case Some(outDir) => scala.tools.nsc.io.AbstractFile.getDirectory(outDir)
316-
case None => new VirtualDirectory("(memory)", None)
316+
object withCompilerApi {
317+
private object api extends CompilerApi {
318+
lazy val compiler: ToolBoxGlobal = {
319+
try {
320+
val errorFn: String => Unit = msg => frontEnd.log(scala.reflect.internal.util.NoPosition, msg, frontEnd.ERROR)
321+
val command = new CompilerCommand(arguments.toList, errorFn)
322+
command.settings.outputDirs setSingleOutput virtualDirectory
323+
val instance = new ToolBoxGlobal(command.settings, frontEndToReporter(frontEnd, command.settings))
324+
if (frontEnd.hasErrors) {
325+
throw ToolBoxError(
326+
"reflective compilation has failed: cannot initialize the compiler:" + EOL + EOL +
327+
(frontEnd.infos map (_.msg) mkString EOL)
328+
)
329+
}
330+
instance
331+
} catch {
332+
case ex: Throwable =>
333+
throw ToolBoxError(s"reflective compilation has failed: cannot initialize the compiler due to $ex", ex)
334+
}
335+
}
336+
337+
lazy val importer = compiler.mkImporter(u)
338+
lazy val exporter = importer.reverse
317339
}
318340

319-
lazy val compiler: ToolBoxGlobal = {
320-
try {
321-
val errorFn: String => Unit = msg => frontEnd.log(scala.reflect.internal.util.NoPosition, msg, frontEnd.ERROR)
322-
val command = new CompilerCommand(arguments.toList, errorFn)
323-
command.settings.outputDirs setSingleOutput virtualDirectory
324-
val instance = new ToolBoxGlobal(command.settings, frontEndToReporter(frontEnd, command.settings))
325-
if (frontEnd.hasErrors) {
326-
throw ToolBoxError(
327-
"reflective compilation has failed: cannot initialize the compiler:" + EOL + EOL +
328-
(frontEnd.infos map (_.msg) mkString EOL)
329-
)
330-
}
331-
instance
332-
} catch {
333-
case ex: Throwable =>
334-
throw ToolBoxError(s"reflective compilation has failed: cannot initialize the compiler due to $ex", ex)
341+
def apply[T](f: CompilerApi => T): T = {
342+
try f(api)
343+
catch { case ex: FatalError => throw ToolBoxError(s"fatal compiler error", ex) }
344+
finally api.compiler.cleanupCaches()
335345
}
336346
}
337347

338-
lazy val importer = compiler.mkImporter(u)
339-
lazy val exporter = importer.reverse
348+
def typeCheck(tree: u.Tree, expectedType: u.Type, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): u.Tree = withCompilerApi { compilerApi =>
349+
import compilerApi._
340350

341-
def typeCheck(tree: u.Tree, expectedType: u.Type, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): u.Tree =
342-
compiler.wrappingFatalErrors {
343-
compiler.withCleanupCaches {
344-
if (compiler.settings.verbose) println("importing "+tree+", expectedType = "+expectedType)
345-
val ctree: compiler.Tree = importer.importTree(tree)
346-
val cexpectedType: compiler.Type = importer.importType(expectedType)
351+
if (compiler.settings.verbose) println("importing "+tree+", expectedType = "+expectedType)
352+
val ctree: compiler.Tree = importer.importTree(tree)
353+
val cexpectedType: compiler.Type = importer.importType(expectedType)
347354

348-
if (compiler.settings.verbose) println("typing "+ctree+", expectedType = "+expectedType)
349-
val ttree: compiler.Tree = compiler.typeCheck(ctree, cexpectedType, silent = silent, withImplicitViewsDisabled = withImplicitViewsDisabled, withMacrosDisabled = withMacrosDisabled)
350-
val uttree = exporter.importTree(ttree)
351-
uttree
352-
}
353-
}
355+
if (compiler.settings.verbose) println("typing "+ctree+", expectedType = "+expectedType)
356+
val ttree: compiler.Tree = compiler.typeCheck(ctree, cexpectedType, silent = silent, withImplicitViewsDisabled = withImplicitViewsDisabled, withMacrosDisabled = withMacrosDisabled)
357+
val uttree = exporter.importTree(ttree)
358+
uttree
359+
}
354360

355361
def inferImplicitValue(pt: u.Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, pos: u.Position = u.NoPosition): u.Tree = {
356362
inferImplicit(u.EmptyTree, pt, isView = false, silent = silent, withMacrosDisabled = withMacrosDisabled, pos = pos)
@@ -362,43 +368,47 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
362368
inferImplicit(tree, viewTpe, isView = true, silent = silent, withMacrosDisabled = withMacrosDisabled, pos = pos)
363369
}
364370

365-
private def inferImplicit(tree: u.Tree, pt: u.Type, isView: Boolean, silent: Boolean, withMacrosDisabled: Boolean, pos: u.Position): u.Tree =
366-
compiler.wrappingFatalErrors {
367-
compiler.withCleanupCaches {
368-
if (compiler.settings.verbose) println(s"importing pt=$pt, tree=$tree, pos=$pos")
369-
val ctree: compiler.Tree = importer.importTree(tree)
370-
val cpt: compiler.Type = importer.importType(pt)
371-
val cpos: compiler.Position = importer.importPosition(pos)
372-
373-
if (compiler.settings.verbose) println("inferring implicit %s of type %s, macros = %s".format(if (isView) "view" else "value", pt, !withMacrosDisabled))
374-
val itree: compiler.Tree = compiler.inferImplicit(ctree, cpt, isView = isView, silent = silent, withMacrosDisabled = withMacrosDisabled, pos = cpos)
375-
val uitree = exporter.importTree(itree)
376-
uitree
377-
}
378-
}
371+
private def inferImplicit(tree: u.Tree, pt: u.Type, isView: Boolean, silent: Boolean, withMacrosDisabled: Boolean, pos: u.Position): u.Tree = withCompilerApi { compilerApi =>
372+
import compilerApi._
379373

380-
def resetAllAttrs(tree: u.Tree): u.Tree = compiler.wrappingFatalErrors {
374+
if (compiler.settings.verbose) println(s"importing pt=$pt, tree=$tree, pos=$pos")
375+
val ctree: compiler.Tree = importer.importTree(tree)
376+
val cpt: compiler.Type = importer.importType(pt)
377+
val cpos: compiler.Position = importer.importPosition(pos)
378+
379+
if (compiler.settings.verbose) println("inferring implicit %s of type %s, macros = %s".format(if (isView) "view" else "value", pt, !withMacrosDisabled))
380+
val itree: compiler.Tree = compiler.inferImplicit(ctree, cpt, isView = isView, silent = silent, withMacrosDisabled = withMacrosDisabled, pos = cpos)
381+
val uitree = exporter.importTree(itree)
382+
uitree
383+
}
384+
385+
def resetAllAttrs(tree: u.Tree): u.Tree = withCompilerApi { compilerApi =>
386+
import compilerApi._
381387
val ctree: compiler.Tree = importer.importTree(tree)
382388
val ttree: compiler.Tree = compiler.resetAllAttrs(ctree)
383389
val uttree = exporter.importTree(ttree)
384390
uttree
385391
}
386392

387-
def resetLocalAttrs(tree: u.Tree): u.Tree = compiler.wrappingFatalErrors {
393+
def resetLocalAttrs(tree: u.Tree): u.Tree = withCompilerApi { compilerApi =>
394+
import compilerApi._
388395
val ctree: compiler.Tree = importer.importTree(tree)
389396
val ttree: compiler.Tree = compiler.resetLocalAttrs(ctree)
390397
val uttree = exporter.importTree(ttree)
391398
uttree
392399
}
393400

394-
def parse(code: String): u.Tree = compiler.wrappingFatalErrors {
401+
def parse(code: String): u.Tree = withCompilerApi { compilerApi =>
402+
import compilerApi._
395403
if (compiler.settings.verbose) println("parsing "+code)
396404
val ctree: compiler.Tree = compiler.parse(code)
397405
val utree = exporter.importTree(ctree)
398406
utree
399407
}
400408

401-
def compile(tree: u.Tree): () => Any = compiler.wrappingFatalErrors {
409+
def compile(tree: u.Tree): () => Any = withCompilerApi { compilerApi =>
410+
import compilerApi._
411+
402412
if (compiler.settings.verbose) println("importing "+tree)
403413
val ctree: compiler.Tree = importer.importTree(tree)
404414

0 commit comments

Comments
 (0)