diff options
-rw-r--r-- | src/compiler/scala/reflect/internal/Trees.scala | 10 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/Global.scala | 46 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/Main.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ScriptRunner.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/interpreter/IMain.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/settings/ScalaSettings.scala | 1 | ||||
-rw-r--r-- | test/files/pos/CustomGlobal.scala | 33 |
7 files changed, 90 insertions, 6 deletions
diff --git a/src/compiler/scala/reflect/internal/Trees.scala b/src/compiler/scala/reflect/internal/Trees.scala index da0ae36ba9..f9ca57fadc 100644 --- a/src/compiler/scala/reflect/internal/Trees.scala +++ b/src/compiler/scala/reflect/internal/Trees.scala @@ -117,6 +117,16 @@ trait Trees extends api.Trees { self: SymbolTable => def shallowDuplicate: Tree = new ShallowDuplicator(tree) transform tree def shortClass: String = tree.getClass.getName split "[.$]" last + /** When you want to know a little more than the class, but a lot + * less than the whole tree. + */ + def summaryString: String = tree match { + case Select(qual, name) => qual.summaryString + "." + name + case Ident(name) => name.longString + case t: DefTree => t.shortClass + " " + t.name + case t: RefTree => t.shortClass + " " + t.name.longString + case t => t.shortClass + } } // ---- values and creators --------------------------------------- diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 275dd3a046..4bc00c5f43 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -9,11 +9,11 @@ import java.io.{ File, FileOutputStream, PrintWriter, IOException, FileNotFoundE import java.nio.charset.{ Charset, CharsetDecoder, IllegalCharsetNameException, UnsupportedCharsetException } import compat.Platform.currentTime -import scala.tools.util.Profiling +import scala.tools.util.{ Profiling, PathResolver } import scala.collection.{ mutable, immutable } import io.{ SourceReader, AbstractFile, Path } import reporters.{ Reporter, ConsoleReporter } -import util.{ Exceptional, ClassPath, SourceFile, Statistics, StatisticsInfo, BatchSourceFile, ScriptSourceFile, ShowPickled, returning } +import util.{ NoPosition, Exceptional, ClassPath, SourceFile, Statistics, StatisticsInfo, BatchSourceFile, ScriptSourceFile, ShowPickled, ScalaClassLoader, returning } import scala.reflect.internal.pickling.{ PickleBuffer, PickleFormat } import settings.{ AestheticSettings } @@ -377,8 +377,15 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb val runsRightAfter = None } with SyntaxAnalyzer + // !!! I think we're overdue for all these phase objects being lazy vals. + // There's no way for a Global subclass to provide a custom typer + // despite the existence of a "def newTyper(context: Context): Typer" + // which is clearly designed for that, because it's defined in + // Analyzer and Global's "object analyzer" allows no override. For now + // I only changed analyzer. + // // factory for phases: namer, packageobjects, typer - object analyzer extends { + lazy val analyzer = new { val global: Global.this.type = Global.this } with Analyzer @@ -1295,3 +1302,36 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb @deprecated("Use forInteractive or forScaladoc, depending on what you're after", "2.9.0") def onlyPresentation = false } + +object Global { + /** If possible, instantiate the global specified via -Yglobal-class. + * This allows the use of a custom Global subclass with the software which + * wraps Globals, such as scalac, fsc, and the repl. + */ + def fromSettings(settings: Settings, reporter: Reporter): Global = { + // !!! The classpath isn't known until the Global is created, which is too + // late, so we have to duplicate it here. Classpath is too tightly coupled, + // it is a construct external to the compiler and should be treated as such. + val loader = ScalaClassLoader.fromURLs(new PathResolver(settings).result.asURLs) + val name = settings.globalClass.value + val clazz = Class.forName(name, true, loader) + val cons = clazz.getConstructor(classOf[Settings], classOf[Reporter]) + + cons.newInstance(settings, reporter).asInstanceOf[Global] + } + + /** A global instantiated this way honors -Yglobal-class setting, and + * falls back on calling the Global constructor directly. + */ + def apply(settings: Settings, reporter: Reporter): Global = { + val g = ( + if (settings.globalClass.isDefault) null + else try fromSettings(settings, reporter) catch { case x => + reporter.warning(NoPosition, "Failed to instantiate " + settings.globalClass.value + ": " + x) + null + } + ) + if (g != null) g + else new Global(settings, reporter) + } +} diff --git a/src/compiler/scala/tools/nsc/Main.scala b/src/compiler/scala/tools/nsc/Main.scala index 38da47aacc..a8dd434690 100644 --- a/src/compiler/scala/tools/nsc/Main.scala +++ b/src/compiler/scala/tools/nsc/Main.scala @@ -73,7 +73,7 @@ object Main extends Driver with EvalLoop { override def newCompiler(): Global = if (settings.Yrangepos.value) new interactive.Global(settings, reporter) - else new Global(settings, reporter) + else Global(settings, reporter) override def doCompile(compiler: Global) { if (settings.resident.value) diff --git a/src/compiler/scala/tools/nsc/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala index 3ec5b2f044..dd9bca4b7b 100644 --- a/src/compiler/scala/tools/nsc/ScriptRunner.scala +++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala @@ -84,7 +84,7 @@ class ScriptRunner extends HasCompileSocket { } protected def newGlobal(settings: Settings, reporter: Reporter) = - new Global(settings, reporter) + Global(settings, reporter) /** Compile a script and then run the specified closure with * a classpath for the compiled script. diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index 920367ce2e..7d0be173e2 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -272,7 +272,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends settings.outputDirs setSingleOutput virtualDirectory settings.exposeEmptyPackage.value = true - new Global(settings, reporter) + Global(settings, reporter) } /** Parent classloader. Overridable. */ diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 5ffc2e1aad..56ac3e16ad 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -145,6 +145,7 @@ trait ScalaSettings extends AbsScalaSettings val refinementMethodDispatch = ChoiceSetting ("-Ystruct-dispatch", "policy", "structural method dispatch policy", List("no-cache", "mono-cache", "poly-cache", "invoke-dynamic"), "poly-cache") + val globalClass = StringSetting ("-Yglobal-class", "class", "subclass of scala.tools.nsc.Global to use for compiler", "") val Yrangepos = BooleanSetting ("-Yrangepos", "Use range positions for syntax trees.") val YrichExes = BooleanSetting ("-Yrich-exceptions", "Fancier exceptions. Set source search path with -D" + diff --git a/test/files/pos/CustomGlobal.scala b/test/files/pos/CustomGlobal.scala new file mode 100644 index 0000000000..30bf227950 --- /dev/null +++ b/test/files/pos/CustomGlobal.scala @@ -0,0 +1,33 @@ +package custom + +import scala.tools.nsc._, reporters._, typechecker._ + +/** Demonstration of a custom Global with a custom Typer, + * decoupled from trunk. Demonstration: + * +{{{ +scalac -d . CustomGlobal.scala && scala -nc -Yglobal-class custom.CustomGlobal \ + -e 'class Bippy(x: Int) ; def f = new Bippy(5)' + +I'm typing a Bippy! It's a ClassDef. +I'm typing a Bippy! It's a Ident. +I'm typing a Bippy! It's a DefDef. +}}} + * + */ +class CustomGlobal(currentSettings: Settings, reporter: Reporter) extends Global(currentSettings, reporter) { + override lazy val analyzer = new { + val global: CustomGlobal.this.type = CustomGlobal.this + } with Analyzer { + override def newTyper(context: Context): Typer = new CustomTyper(context) + + class CustomTyper(context : Context) extends Typer(context) { + override def typed(tree: Tree, mode: Int, pt: Type): Tree = { + if (tree.summaryString contains "Bippy") + println("I'm typing a Bippy! It's a " + tree.shortClass + ".") + + super.typed(tree, mode, pt) + } + } + } +} |