summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/reflect/internal/Trees.scala10
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala46
-rw-r--r--src/compiler/scala/tools/nsc/Main.scala2
-rw-r--r--src/compiler/scala/tools/nsc/ScriptRunner.scala2
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/IMain.scala2
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala1
-rw-r--r--test/files/pos/CustomGlobal.scala33
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)
+ }
+ }
+ }
+}