Made it possible to supply a custom Global to the core scala runners.

The absence of "Global pluggability", combined with the fact that most
of the functionality in Global is unnecessarily rigid due to the phases
being implemented as objects, means that it has been close to impossible
to do interesting compiler development in a way which doesn't require
modifying the scalac source tree. This then leaves you continually
subject to punishment by code drift as the various places you were
forced to modify change out from under you.

This is somewhat less true now, thanks to new option:  -Yglobal-class

The primary wielders of Global (fsc/scala/scalac) now instantiate
the compiler via a (Settings, Reporter) => Global factory method in
the Global companion. If -Yglobal-class was given, that class (which
must have a (Settings, Reporter) constructor) will be instantiated if
possible, falling back on the standard one.  See

  test/files/pos/CustomGlobal.scala

for a working example. (It's not in run because I would have to be able
to give partest a different set of flags for successive compiles in the
same test.) Review by odersky.

git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@25600 5e8d7ff9-d8ef-0310-90f0-a4852d11357a
This commit is contained in:
extempore 2011-09-01 02:31:55 +00:00
parent 47d321c78e
commit c8354bb691
7 changed files with 92 additions and 8 deletions

View File

@ -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 ---------------------------------------

View File

@ -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,11 +377,18 @@ 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
// phaseName = "superaccessors"
object superAccessors extends {
val global: Global.this.type = Global.this
@ -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)
}
}

View File

@ -73,8 +73,8 @@ 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)
resident(compiler)

View File

@ -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.

View File

@ -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. */

View File

@ -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" +

View File

@ -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)
}
}
}
}