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:
parent
47d321c78e
commit
c8354bb691
|
@ -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 ---------------------------------------
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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" +
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue