summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc')
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala270
-rw-r--r--src/compiler/scala/tools/nsc/PhaseAssembly.scala334
-rw-r--r--src/compiler/scala/tools/nsc/ScalaDoc.scala5
-rw-r--r--src/compiler/scala/tools/nsc/Settings.scala1
-rw-r--r--src/compiler/scala/tools/nsc/SubComponent.scala18
-rw-r--r--src/compiler/scala/tools/nsc/plugins/PluginComponent.scala14
-rw-r--r--src/compiler/scala/tools/nsc/plugins/Plugins.scala58
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Analyzer.scala4
8 files changed, 575 insertions, 129 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index f308651dfd..05a42163d0 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -34,6 +34,7 @@ import backend.icode.analysis._
class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
with CompilationUnits
with Plugins
+ with PhaseAssembly
{
// alternate constructors ------------------------------------------
def this(reporter: Reporter) =
@@ -254,7 +255,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
private val isDevirtualized = prev.name == "devirtualize" || prev.devirtualized
override def devirtualized: Boolean = isDevirtualized // (part of DEVIRTUALIZE)
- /** Is current phase cancelled on this unit? */
+ /** Is current phase cancelled on this unit? */
def cancelled(unit: CompilationUnit) =
reporter.cancelled ||
unit.isJava && this.id > currentRun.namerPhase.id
@@ -271,175 +272,277 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
}
}
- class TerminalPhase(prev: Phase) extends GlobalPhase(prev) {
- def name = "terminal"
- def apply(unit: CompilationUnit) {}
- }
-
+ // phaseName = "parser"
object syntaxAnalyzer extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]()
+ val runsRightAfter = None
} with SyntaxAnalyzer
+ // factory method for
+ // phaseName = "namer"
+ // phaseName = "parser"
object analyzer extends {
val global: Global.this.type = Global.this
} with Analyzer
+ // phaseName = "superaccessors"
object superAccessors extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]("typer")
+ val runsRightAfter = None
} with SuperAccessors
+ // phaseName = "pickler"
object pickler extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]("superaccessors")
+ val runsRightAfter = None
} with Pickler
+ // phaseName = "refchecks"
object refchecks extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]("pickler")
+ val runsRightAfter = None
} with RefChecks
-/*
- object devirtualize extends {
- val global: Global.this.type = Global.this
- } with DeVirtualize
-*/
+
+// object devirtualize extends {
+// val global: Global.this.type = Global.this
+// } with DeVirtualize
+
+ // phaseName = "liftcode"
object liftcode extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]("refchecks")
+ val runsRightAfter = None
} with LiftCode
+ // phaseName = "uncurry"
object uncurry extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]("refchecks","liftcode")
+ val runsRightAfter = None
} with UnCurry
+ // phaseName = "tailcalls"
object tailCalls extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]("uncurry")
+ val runsRightAfter = None
} with TailCalls
-// object checkDefined extends {
-// val global: Global.this.type = Global.this
-// } with CheckDefined
+ // object checkDefined extends {
+ // val global: Global.this.type = Global.this
+ // } with CheckDefined
+ // phaseName = "explicitouter"
object explicitOuter extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]("tailcalls")
+ val runsRightAfter = None
} with ExplicitOuter
+ // phaseName = "erasure"
object erasure extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]("explicitouter")
+ val runsRightAfter = Some("explicitouter")
} with Erasure
+ // phaseName = "lazyvals"
object lazyVals extends {
val global: Global.this.type = Global.this
final val FLAGS_PER_WORD = 32
+ val runsAfter = List[String]("erasure")
+ val runsRightAfter = None
} with LazyVals
+ // phaseName = "lambdalift"
object lambdaLift extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]("lazyvals")
+ val runsRightAfter = None
} with LambdaLift
+ // phaseName = "constructors"
object constructors extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]("lambdalift")
+ val runsRightAfter = None
} with Constructors
+ // phaseName = "flatten"
object flatten extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]("constructors")
+ val runsRightAfter = None
} with Flatten
-/*
- object detach extends {
- val global: Global.this.type = Global.this
- } with Detach
-*/
+
+// object detach extends {
+// val global: Global.this.type = Global.this
+// } with Detach
+
+ // phaseName = "mixin"
object mixer extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]("flatten","constructors")
+ val runsRightAfter = None
} with Mixin
+ // phaseName = "cleanup"
object cleanup extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]("mixin")
+ val runsRightAfter = None
} with CleanUp
- object sampleTransform extends {
- val global: Global.this.type = Global.this
- } with SampleTransform
-
+ // phaseName = "icode"
object genicode extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]("cleanup")
+ val runsRightAfter = None
} with GenICode
-/*
- object icodePrinter extends backend.icode.Printers {
- val global: Global.this.type = Global.this
- }
-*/
+
+// object icodePrinter extends backend.icode.Printers {
+// val global: Global.this.type = Global.this
+// }
+
+ // phaseName = "???"
object scalaPrimitives extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]()
+ val runsRightAfter = None
} with ScalaPrimitives
+ // phaseName = "inliner"
object inliner extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]("icode")
+ val runsRightAfter = None
} with Inliners
+ // phaseName = "closelim"
object closureElimination extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]("inliner")
+ val runsRightAfter = None
} with ClosureElimination
+ // phaseName = "dce"
object deadCode extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]("closelim")
+ val runsRightAfter = None
} with DeadCodeElimination
+ // phaseName = "jvm"
object genJVM extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]("dce")
+ val runsRightAfter = None
} with GenJVM
+ // phaseName = "msil"
object genMSIL extends {
val global: Global.this.type = Global.this
+ val runsAfter = List[String]("dce")
+ val runsRightAfter = None
} with GenMSIL
+ // phaseName = "terminal"
+ object terminal extends {
+ val global: Global.this.type = Global.this
+ val phaseName = "terminal"
+ val runsAfter = List[String]("jvm","msil")
+ val runsRightAfter = None
+ } with SubComponent {
+ private var cache: Option[GlobalPhase] = None
+
+ def newPhase(prev: Phase): GlobalPhase = {
+ if (cache.isEmpty) cache = Some(new TerminalPhase(prev))
+ cache.get
+ }
+
+ def reset() {
+ cache = None
+ }
+
+ class TerminalPhase(prev: Phase) extends GlobalPhase(prev) {
+ def name = "terminal"
+ def apply(unit: CompilationUnit) {}
+ }
+ }
+
+ // phaseName = "SAMPLE PHASE"
+ object sampleTransform extends {
+ val global: Global.this.type = Global.this
+ val runsAfter = List[String]()
+ val runsRightAfter = None
+ } with SampleTransform
+
object icodeChecker extends checkers.ICodeChecker()
object typer extends analyzer.Typer(
analyzer.NoContext.make(EmptyTree, Global.this.definitions.RootClass, newScope))
- /** The built-in components. The full list of components, including
- * plugins, is computed in the Plugins trait.
+ /* Add the internal compiler phases to the phases set
*/
- protected def builtInPhaseDescriptors: List[SubComponent] = List(
- analyzer.namerFactory: SubComponent, // note: types are there because otherwise
- analyzer.typerFactory: SubComponent, // consistency check after refchecks would fail.
- superAccessors, // add super accessors
- pickler // serialize symbol tables
-
- // Desugar virtual classes
- // if (false && settings.Xexperimental.value) List(devirtualize) else List()
-
- ) ::: List(
- refchecks // perform reference and override checking, translate nested objects
- ) ::: (
- if (forJVM) List(liftcode) else List() // generate reified trees
- ) ::: List(
- uncurry, // uncurry, translate function values to anonymous classes
- tailCalls, // replace tail calls by jumps
- explicitOuter, // replace C.this by explicit outer pointers, eliminate pattern matching
-// checkDefined,
- erasure, // erase generic types to Java 1.4 types, add interfaces for traits
- lazyVals, // transforms local lazy vals into vars and initialized bits
- lambdaLift, // move nested functions to top level
-// detach,
- constructors // move field definitions into constructors
- ) ::: (
- if (forMSIL) List() else List(flatten) // get rid of inner classes
- ) ::: List(
- mixer, // do mixin composition, translate lazy fields
- cleanup, // some platform-specific cleanups
-
- genicode, // generate portable intermediate code
- inliner, // optimization: do inlining
- closureElimination, // optimization: get rid of uncalled closures
- deadCode, // optimization: get rid of dead cpde
- if (forMSIL) genMSIL else genJVM, // generate .class files
- sampleTransform
- )
+ protected def computeInternalPhases() {
+ phasesSet += syntaxAnalyzer // The parser
+ phasesSet += analyzer.namerFactory // note: types are there because otherwise
+ phasesSet += analyzer.typerFactory // consistency check after refchecks would fail.
+ phasesSet += superAccessors // add super accessors
+ phasesSet += pickler // serialize symbol tables
+ phasesSet += refchecks // perform reference and override checking, translate nested objects
+
+// if (false && settings.Xexperimental.value)
+// phasesSet += devirtualize // Desugar virtual classes4
+
+ phasesSet += uncurry // uncurry, translate function values to anonymous classes
+ phasesSet += tailCalls // replace tail calls by jumps
+ phasesSet += explicitOuter // replace C.this by explicit outer pointers, eliminate pattern matching
+ phasesSet += erasure // erase generic types to Java 1.4 types, add interfaces for traits
+ phasesSet += lazyVals //
+ phasesSet += lambdaLift // move nested functions to top level
+ phasesSet += constructors // move field definitions into constructors
+ phasesSet += mixer // do mixin composition
+ phasesSet += cleanup // some platform-specific cleanups
+ phasesSet += genicode // generate portable intermediate code
+ phasesSet += inliner // optimization: do inlining
+ phasesSet += closureElimination // optimization: get rid of uncalled closures
+ phasesSet += deadCode // optimization: get rid of dead cpde
+ phasesSet += terminal // The last phase in the compiler chain
+
+ if (! forMSIL) {
+ phasesSet += flatten // get rid of inner classes
+ }
+ if (forJVM) {
+ phasesSet += liftcode // generate reified trees
+ phasesSet += genJVM // generate .class files
+ }
+ if (forMSIL) {
+ phasesSet += genMSIL // generate .msil files
+ }
+ }
+ /* Helper method for sequncing the phase assembly
+ */
+ private def computePhaseDescriptors: List[SubComponent] = {
+ computeInternalPhases() // Global.scala
+ computePluginPhases() // plugins/Plugins.scala
+ buildCompilerFromPhasesSet() // PhaseAssembly.scala
+ }
+
+ /* Simple option value to hold the compiler phase chain */
private var phasesCache: Option[List[SubComponent]] = None
+ /* The set of phase objects that is the basis for the compiler phase chain */
+ protected val phasesSet : HashSet[SubComponent] = new HashSet[SubComponent]
+
def phaseDescriptors = {
- if (phasesCache.isEmpty)
- phasesCache = Some(computePhaseDescriptors)
+ if (phasesCache.isEmpty) phasesCache = Some(computePhaseDescriptors)
phasesCache.get
}
@@ -471,6 +574,8 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
//Console.println("starting run: " + id)
var currentUnit: CompilationUnit = _
curRun = this
+ // Can not take the phaseDescriptors.head even though its the syntaxAnalyser, this will implicitly
+ // call definitions.init which uses phase and needs it to be != NoPhase
private val firstPhase = syntaxAnalyzer.newPhase(NoPhase)
phase = firstPhase
definitions.init // needs phase to be defined != NoPhase,
@@ -480,16 +585,24 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
var deprecationWarnings: Boolean = false
var uncheckedWarnings: Boolean = false
+ // The first phase in the compiler phase chain
private var p: Phase = firstPhase
- protected def stopPhase(name : String) =
- if (onlyPresentation) name == "superaccessors"
- else settings.stop.contains(name)
- // protected def stopPhase(name : String) = settings.stop.contains(name)
+ protected def stopPhase(name : String) = settings.stop.contains(name)
+
+ // Reset the cache in terminal, the chain could have been build before where nobody used it
+ // This happens in the interpreter
+ terminal.reset
- for (pd <- phaseDescriptors.takeWhile(pd => !(stopPhase(pd.phaseName))))
+ // Each subcomponent is asked to deliver a newPhase that is chained together. If -Ystop:phasename is
+ // given at command-line, this will stop with that phasename
+ for (pd <- phaseDescriptors.tail.takeWhile(pd => !(stopPhase(pd.phaseName))))
if (!(settings.skip contains pd.phaseName)) p = pd.newPhase(p)
+ // Ensure there is a terminal phase at the end, Normally there will then be two terminal phases at the end
+ // if -Ystop:phasename was given, this makes sure that there is a terminal phase at the end
+ p = terminal.newPhase(p)
+
def cancel { reporter.cancelled = true }
// progress tracking
@@ -509,7 +622,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
private def refreshProgress =
if (fileset.size > 0)
progress((phasec * fileset.size) + unitc,
- (phaseDescriptors.length+1) * fileset.size)
+ (phaseDescriptors.length-1) * fileset.size) // terminal phase not part of the progress display
def phaseNamed(name: String): Phase = {
var p: Phase = firstPhase
@@ -531,13 +644,6 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
private var unitbuf = new ListBuffer[CompilationUnit]
private var fileset = new HashSet[AbstractFile]
- lazy val terminalPhase : Phase = {
- //var q : Phase = firstPhase
- //while (q != null && !stopPhase(q.name)) q = q.next
- //if (q == null)
- new TerminalPhase(p)
- //else q
- }
private def addUnit(unit: CompilationUnit) {
unitbuf += unit
fileset += unit.source.file
@@ -568,7 +674,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
for (source <- sources) addUnit(new CompilationUnit(source))
globalPhase = firstPhase
- while (globalPhase != terminalPhase && !reporter.hasErrors) {
+ while (globalPhase != terminal.newPhase(NoPhase) && !reporter.hasErrors) {
val startTime = currentTime
phase = globalPhase
globalPhase.run
diff --git a/src/compiler/scala/tools/nsc/PhaseAssembly.scala b/src/compiler/scala/tools/nsc/PhaseAssembly.scala
new file mode 100644
index 0000000000..1b5306eb0e
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/PhaseAssembly.scala
@@ -0,0 +1,334 @@
+/* __ *\
+** ________ ___ / / ___ Scala Compiler **
+** / __/ __// _ | / / / _ | (c) 2008-2009 , LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+/**
+ *
+ * @author Anders Bach Nielsen
+ * @version 1.0
+ * $Id$
+ */
+
+package scala.tools.nsc
+
+import scala.collection.mutable.{HashSet,HashMap}
+import java.io.{BufferedWriter,FileWriter}
+
+/**
+ * PhaseAssembly
+ * Trait made to seperate the constraint solving of the phase order from
+ * the rest of the compiler. See SIP 00002
+ *
+ */
+trait PhaseAssembly { self: Global =>
+
+ /**
+ * Aux datastructure for solving the constraint system
+ * The depency graph container with helper methods for node and edge creation
+ */
+ class DependencyGraph {
+
+ /**
+ * Simple edge with to and from refs
+ */
+ class Edge(var frm: Node, var to: Node, var hard: Boolean)
+
+ /**
+ * Simple node with name and object ref for the phase object,
+ * also sets of in and out going dependencies
+ */
+ class Node(name:String) {
+ val phasename = name
+ var phaseobj: Option[List[SubComponent]] = None
+ val after = new HashSet[Edge]()
+ var before = new HashSet[Edge]()
+ var visited = false
+ var level = 0
+
+ def allPhaseNames(): String = phaseobj match {
+ case None => phasename
+ case Some(lst) => lst.map(sc => sc.phaseName).reduceLeft( (x,y) => x + "," + y)
+ }
+ }
+
+ val nodes = new HashMap[String,Node]()
+ val edges = new HashSet[Edge]()
+
+ /* Given a phase object, get the node for this phase object. If the
+ * node object does not exist, then create it.
+ */
+ def getNodeByPhase(phs : SubComponent) : Node = {
+ var node : Node = getNodeByPhase(phs.phaseName)
+ node.phaseobj match {
+ case None =>
+ node.phaseobj = Some(List[SubComponent](phs))
+ case _ =>
+ }
+ node
+ }
+
+ /* Given the name of a phase object, get the node for that name. If the
+ * node object does not exits, then create it.
+ */
+ def getNodeByPhase(name : String) : Node = {
+ nodes.get(name).getOrElse {
+ val node = new Node(name)
+ nodes += (name -> node)
+ node
+ }
+ }
+
+ /* Connect the frm and to nodes with an edge and make it soft.
+ * Also add the edge object to the set of edges, and to the dependency
+ * list of the nodes
+ */
+ def softConnectNodes(frm: Node, to: Node) {
+ var e = new Edge(frm, to, false)
+ this.edges += e
+
+ frm.after += e
+ to.before += e
+ }
+
+ /* Connect the frm and to nodes with an edge and make it hard.
+ * Also add the edge object to the set of edges, and to the dependency
+ * list of the nodes
+ */
+ def hardConnectNodes(frm: Node, to: Node) {
+ var e = new Edge(frm, to, true)
+ this.edges += e
+
+ frm.after += e
+ to.before += e
+ }
+
+ /* Given the entire graph, collect the phase objects at each level, where the phase
+ * names are sorted alphabetical at each level, into the compiler phase list
+ */
+ def compilerPhaseList() : List[SubComponent] = {
+ var chain : List[SubComponent] = Nil
+
+ var lvl = 1
+ var nds = nodes.values.filter(n => (n.level == lvl)).toList
+ while(nds.size > 0) {
+ nds = nds.sort((n1,n2) => (n1.phasename compareTo n2.phasename) < 0)
+ for( n <- nds ) {
+ chain = chain ::: n.phaseobj.get
+ }
+ lvl = lvl + 1
+ nds = nodes.values.filter(n => (n.level == lvl)).toList
+ }
+ return chain
+ }
+
+ /* Test if there are cycles in the graph, assign levels to the nodes
+ * and collapse hard links into nodes
+ */
+ def collapseHardLinksAndLevels(node : Node, lvl: Int) : Unit = {
+ if (node.visited) {
+ throw new FatalError("Cycle in compiler phase dependencies detected, phase " + node.phasename + " reacted twice!")
+ }
+
+ if (node.level < lvl) node.level = lvl
+
+ var hls = Nil ++ node.before.filter( e => e.hard )
+ while( hls.size > 0 ) {
+ for( hl <- hls ) {
+ node.phaseobj = Some(node.phaseobj.get ++ hl.frm.phaseobj.get)
+ node.before = hl.frm.before
+ nodes -= hl.frm.phasename
+ edges -= hl
+ for( edge <- node.before ) edge.to = node
+ }
+ hls = Nil ++ node.before.filter( e => e.hard )
+ }
+ node.visited = true
+
+ for( edge <- node.before ) {
+ collapseHardLinksAndLevels( edge.frm, lvl + 1)
+ }
+
+ node.visited = false
+ }
+
+ /* Find all edges in the given graph that are hard links. For each hard link we
+ * need to check that its the only dependency. If not, then we will promote the
+ * other dependencies down
+ */
+ def validateAndEnforceHardlinks() : Unit = {
+ var hardlinks = edges.filter(e => e.hard)
+ for(hl <- hardlinks) {
+ if (hl.frm.after.size > 1) {
+ throw new FatalError("phase " + hl.frm.phasename + " want to run right after " + hl.to.phasename + ", but some phase has declared to run before " + hl.frm.phasename + ". Re-run with -Xgenerate-phase-graph <filename> to better see the problem.")
+ }
+ }
+
+ var rerun = true
+ while(rerun) {
+ rerun = false
+ hardlinks = edges.filter(e => e.hard)
+ for(hl <- hardlinks) {
+ var sanity = Nil ++ hl.to.before.filter(e => e.hard)
+ if (sanity.length == 0) {
+ throw new FatalError("There is no runs right after dependency, where there should be one! This is not supposed to happen!")
+ } else if (sanity.length > 1) {
+ var msg = "Multiple phases want to run right after the phase " + sanity.first.to.phasename + "\n"
+ msg += "Phases: "
+ sanity = sanity.sort((e1,e2) => (e1.frm.phasename compareTo e2.frm.phasename) < 0)
+ for (edge <- sanity) {
+ msg += edge.frm.phasename + ", "
+ }
+ msg += "\nRe-run with -Xgenerate-phase-graph <filename> to better see the problem."
+ throw new FatalError(msg)
+
+ } else {
+
+ var promote = hl.to.before.filter(e => (!e.hard))
+ hl.to.before.clear
+ sanity foreach (edge => hl.to.before += edge)
+ for (edge <- promote) {
+ rerun = true
+ informProgress("promote the dependency of " + edge.frm.phasename + ": " + edge.to.phasename + " => " + hl.frm.phasename)
+ edge.to = hl.frm
+ hl.frm.before += edge
+ }
+ }
+ }
+ }
+ }
+
+ /* Remove all nodes in the given graph, that have no phase object
+ * Make sure to clean up all edges when removing the node object
+ * Inform with warnings, if an external phase has a dependency on something that is dropped.
+ */
+ def removeDanglingNodes() : Unit = {
+ var dnodes = nodes.values.filter(n => n.phaseobj.isEmpty )
+ for(node <- dnodes) {
+ informProgress("dropping dependency on node with no phase object: " + node.phasename)
+ nodes -= node.phasename
+ for(edge <- node.before) {
+ edges -= edge
+ edge.frm.after -= edge
+ edge.frm.phaseobj match {
+ case Some(lsc) => if (! lsc.head.internal) warning("dropping dependency on node with no phase object: " + node.phasename)
+ case _ =>
+ }
+ }
+ }
+ }
+
+ }
+
+
+ /* Method called from computePhaseDescriptors in class Global
+ */
+ def buildCompilerFromPhasesSet() : List[SubComponent] = {
+
+ // Add all phases in the set to the graph
+ val graph = phasesSetToDepGraph(phasesSet)
+
+ // Output the phase dependency graph at this stage
+ if (!(settings.genPhaseGraph.value == "")) graphToDotFile(graph, settings.genPhaseGraph.value + "1.dot")
+
+ // Remove nodes without phaseobj
+ graph.removeDanglingNodes()
+
+ // Output the phase dependency graph at this stage
+ if (!(settings.genPhaseGraph.value == "")) graphToDotFile(graph, settings.genPhaseGraph.value + "2.dot")
+
+ // Validate and Enforce hardlinks / runsRightAfter and promote nodes down the tree
+ graph.validateAndEnforceHardlinks()
+
+ // Output the phase dependency graph at this stage
+ if (!(settings.genPhaseGraph.value == "")) graphToDotFile(graph, settings.genPhaseGraph.value + "3.dot")
+
+ // test for cycles, assign levels and collapse hard links into nodes
+ graph.collapseHardLinksAndLevels(graph.getNodeByPhase("parser"), 1)
+
+ // Output the phase dependency graph at this stage
+ if (!(settings.genPhaseGraph.value == "")) graphToDotFile(graph, settings.genPhaseGraph.value + "4.dot")
+
+ // assemble the compiler
+ return graph.compilerPhaseList()
+ }
+
+
+ /* Given the phases set, will build a dependency graph from the phases set
+ * Using the aux. method of the DependencyGraph to create nodes and egdes
+ */
+ private def phasesSetToDepGraph(phsSet : HashSet[SubComponent]) : DependencyGraph = {
+ val graph = new DependencyGraph()
+
+ for(phs <- phsSet) {
+
+ var fromnode = graph.getNodeByPhase(phs)
+
+ phs.runsRightAfter match {
+ case None =>
+ for(phsname <- phs.runsAfter) {
+ if (! (phsname == "terminal")) {
+ var tonode = graph.getNodeByPhase(phsname)
+ graph.softConnectNodes(fromnode, tonode)
+ } else {
+ error("[phase assembly, after dependency on terminal phase not allowed: " + fromnode.phasename + " => "+ phsname + "]")
+ }
+ }
+ for(phsname <- phs.runsBefore) {
+ if (! (phsname == "parser")) {
+ var tonode = graph.getNodeByPhase(phsname)
+ graph.softConnectNodes(tonode, fromnode)
+ } else {
+ error("[phase assembly, before dependency on parser phase not allowed: " + phsname + " => "+ fromnode.phasename + "]")
+ }
+ }
+ case Some(phsname) =>
+ if (! (phsname == "terminal")) {
+ var tonode = graph.getNodeByPhase(phsname)
+ graph.hardConnectNodes(fromnode, tonode)
+ } else {
+ error("[phase assembly, right after dependency on terminal phase not allowed: " + fromnode.phasename + " => "+ phsname + "]")
+ }
+ }
+ }
+ graph
+ }
+
+ /* This is a helper method, that given a dependency graph will generate a graphviz dot
+ * file showing its structure.
+ * Plug-in supplied phases are marked as green nodes and hard links are marked as blue edges.
+ */
+ private def graphToDotFile(graph : DependencyGraph, filename : String) : Unit = {
+ var sbuf = new StringBuffer()
+ var extnodes = new HashSet[graph.Node]()
+ var fatnodes = new HashSet[graph.Node]()
+ sbuf.append("digraph G {\n")
+ for(edge <- graph.edges) {
+ sbuf.append("\"" + edge.frm.allPhaseNames + "(" + edge.frm.level + ")" + "\"->\"" + edge.to.allPhaseNames + "(" + edge.to.level + ")" + "\"")
+ if (! edge.frm.phaseobj.get.first.internal) {
+ extnodes += edge.frm
+ }
+ edge.frm.phaseobj match { case None => null case Some(ln) => if(ln.size > 1) fatnodes += edge.frm }
+ edge.to.phaseobj match { case None => null case Some(ln) => if(ln.size > 1) fatnodes += edge.to }
+ if (edge.hard) {
+ sbuf.append(" [color=\"#0000ff\"]\n")
+ } else {
+ sbuf.append(" [color=\"#000000\"]\n")
+ }
+ }
+ for(node <- extnodes) {
+ sbuf.append("\"" + node.allPhaseNames + "(" + node.level + ")" + "\" [color=\"#00ff00\"]\n")
+ }
+ for(node <- fatnodes) {
+ sbuf.append("\"" + node.allPhaseNames + "(" + node.level + ")" + "\" [color=\"#0000ff\"]\n")
+ }
+ sbuf.append("}\n")
+ var out = new BufferedWriter(new FileWriter(filename))
+ out.write(sbuf.toString)
+ out.flush()
+ out.close()
+ }
+
+}
diff --git a/src/compiler/scala/tools/nsc/ScalaDoc.scala b/src/compiler/scala/tools/nsc/ScalaDoc.scala
index b62f10d7c7..bb41e0fa04 100644
--- a/src/compiler/scala/tools/nsc/ScalaDoc.scala
+++ b/src/compiler/scala/tools/nsc/ScalaDoc.scala
@@ -45,6 +45,11 @@ object ScalaDoc {
}
try {
object compiler extends Global(command.settings, reporter) {
+ override protected def computeInternalPhases() : Unit = {
+ phasesSet += syntaxAnalyzer
+ phasesSet += analyzer.namerFactory
+ phasesSet += analyzer.typerFactory
+ }
override val onlyPresentation = true
}
if (reporter.hasErrors) {
diff --git a/src/compiler/scala/tools/nsc/Settings.scala b/src/compiler/scala/tools/nsc/Settings.scala
index 9673ed07d4..b36d82f741 100644
--- a/src/compiler/scala/tools/nsc/Settings.scala
+++ b/src/compiler/scala/tools/nsc/Settings.scala
@@ -124,6 +124,7 @@ class Settings(error: String => Unit) {
val Xshowcls = StringSetting ("-Xshow-class", "class", "Show class info", "").hideToIDE
val Xshowobj = StringSetting ("-Xshow-object", "object", "Show object info", "").hideToIDE
val showPhases = BooleanSetting ("-Xshow-phases", "Print a synopsis of compiler phases").hideToIDE
+ val genPhaseGraph = StringSetting ("-Xgenerate-phase-graph", "filename", "Generate the phase graphs (outputs .dot files) to filenameX.dot", "").hideToIDE
val sourceReader = StringSetting ("-Xsource-reader", "classname", "Specify a custom method for reading source files", "scala.tools.nsc.io.SourceReader").hideToIDE
// val migrate2_7_2 = BooleanSetting ("-Xmigrate-to-2.7.2", "Issue warning messages to help in migration to 2.7.2")
val future = BooleanSetting ("-Xfuture", "Turn on future language features")
diff --git a/src/compiler/scala/tools/nsc/SubComponent.scala b/src/compiler/scala/tools/nsc/SubComponent.scala
index 625d07bb98..42008b58d2 100644
--- a/src/compiler/scala/tools/nsc/SubComponent.scala
+++ b/src/compiler/scala/tools/nsc/SubComponent.scala
@@ -18,6 +18,22 @@ abstract class SubComponent {
/** The name of the phase */
val phaseName: String
+ /** List of phase names, this phase should run after */
+ val runsAfter: List[String]
+
+ /** List of phase names, this phase should run before */
+ val runsBefore: List[String] = Nil
+
+ /** Phase name this phase will attach itself to, not allowing any phase to come between it
+ * and the phase name declared */
+ val runsRightAfter: Option[String]
+
+ /** Internal flag to tell external from internal phases */
+ val internal: Boolean = true
+
+ /** SubComponent are added to a HashSet and two phases are the same if they have the same name */
+ override def hashCode() = phaseName.hashCode()
+
/** New flags defined by the phase which are not valid before */
def phaseNewFlags: Long = 0
@@ -40,7 +56,7 @@ abstract class SubComponent {
}
/** The phase defined by this subcomponent. Can be called only after phase is installed by newPhase. */
-// lazy val ownPhase: Phase = global.currentRun.phaseNamed(phaseName)
+ // lazy val ownPhase: Phase = global.currentRun.phaseNamed(phaseName)
/** A standard phase template */
abstract class StdPhase(prev: Phase) extends global.GlobalPhase(prev) {
diff --git a/src/compiler/scala/tools/nsc/plugins/PluginComponent.scala b/src/compiler/scala/tools/nsc/plugins/PluginComponent.scala
index 2b7b973a54..30c6d9bcb4 100644
--- a/src/compiler/scala/tools/nsc/plugins/PluginComponent.scala
+++ b/src/compiler/scala/tools/nsc/plugins/PluginComponent.scala
@@ -1,6 +1,7 @@
/* NSC -- new Scala compiler
* Copyright 2007-2009 LAMP/EPFL
* @author Lex Spoon
+ * Updated by Anders Bach Nielsen
*/
// $Id$
@@ -9,9 +10,16 @@ package scala.tools.nsc.plugins
/** A component that is part of a Plugin.
*
* @author Lex Spoon
- * @version 1.0, 2007/5/29
+ * @version 1.1, 2009/1/2
+ * Updated 2009/1/2 by Anders Bach Nielsen: Added features to implement SIP 00002
*/
abstract class PluginComponent extends SubComponent {
- /** the phase this plugin wants to run after */
- val runsAfter: String
+
+ /** Internal flag to tell external from internal phases */
+ final override val internal = false
+
+ /** Phases supplied by plugins should not have give the runsRightAfter constraint,
+ * but can override it */
+ val runsRightAfter: Option[String] = None
+
}
diff --git a/src/compiler/scala/tools/nsc/plugins/Plugins.scala b/src/compiler/scala/tools/nsc/plugins/Plugins.scala
index 03b8806e52..cd457dfec1 100644
--- a/src/compiler/scala/tools/nsc/plugins/Plugins.scala
+++ b/src/compiler/scala/tools/nsc/plugins/Plugins.scala
@@ -1,6 +1,7 @@
/* NSC -- new Scala compiler
* Copyright 2007-2009 LAMP/EPFL
* @author Lex Spoon
+ * Updated by Anders Bach Nielsen
*/
// $Id$
@@ -11,7 +12,8 @@ import java.io.File
/** Support for run-time loading of compiler plugins.
*
* @author Lex Spoon
- * @version 1.0, 2007-5-21
+ * @version 1.1, 2009/1/2
+ * Updated 2009/1/2 by Anders Bach Nielsen: Added features to implement SIP 00002
*/
trait Plugins { self: Global =>
@@ -90,7 +92,7 @@ trait Plugins { self: Global =>
val plugs =
pick(roughPluginsList,
Set.empty,
- Set.empty ++ builtInPhaseDescriptors.map(_.phaseName))
+ Set.empty ++ phasesSet.map(_.phaseName))
for (req <- settings.require.value; if !plugs.exists(p => p.name==req))
error("Missing required plugin: " + req)
@@ -133,49 +135,19 @@ trait Plugins { self: Global =>
messages.mkString("\n")
}
+ /**
+ * Extract all phases supplied by plugins and add them to the phasesSet.
+ * @see phasesSet
+ */
+ protected def computePluginPhases() {
- /** Compute a full list of phase descriptors, including
- * both built-in phases and those coming from plugins. */
- protected def computePhaseDescriptors: List[SubComponent] = {
- def insert(descs: List[SubComponent], component: PluginComponent)
- :List[SubComponent] =
- {
- descs match {
- case Nil => assert(false); Nil
- case hd::rest if "parser" == component.runsAfter =>
- component :: hd :: rest
- case hd::rest if hd.phaseName == component.runsAfter =>
- hd :: component :: rest
- case hd :: rest =>
- hd :: (insert(rest, component))
- }
- }
-
- var descriptors = builtInPhaseDescriptors
- var plugsLeft = plugins.flatMap(_.components)
-
- // Insert all the plugins, one by one. Note that
- // plugins are allowed to depend on each other, thus
- // complicating the algorithm.
-
- while (!plugsLeft.isEmpty) {
- val nextPlug = plugsLeft.find(plug =>
- plug.runsAfter == "parser" ||
- descriptors.exists(d => d.phaseName == plug.runsAfter))
- nextPlug match {
- case None =>
- error("Failed to load some plugin phases:")
- for (plug <- plugsLeft)
- error (plug.phaseName + " depends on " + plug.runsAfter)
- return descriptors
- case Some(nextPlug) =>
- descriptors = insert(descriptors, nextPlug)
- plugsLeft = plugsLeft.filter(p => !(p eq nextPlug))
- }
- }
+ var plugPhases = plugins.flatMap(_.components)
- descriptors
- }
+ // Add all phases supplied by plugins to the phasesSet
+ for (pPhase <- plugPhases) {
+ phasesSet += pPhase
+ }
+ }
/** Summary of the options for all loaded plugins */
def pluginOptionsHelp: String = {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala
index 1b32f752b9..bcb30edbf8 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala
@@ -24,6 +24,8 @@ trait Analyzer extends AnyRef
object namerFactory extends SubComponent {
val global: Analyzer.this.global.type = Analyzer.this.global
val phaseName = "namer"
+ val runsAfter = List[String]("parser")
+ val runsRightAfter = None
def newPhase(_prev: Phase): StdPhase = new StdPhase(_prev) {
override val checkable = false
def apply(unit: CompilationUnit) {
@@ -35,6 +37,8 @@ trait Analyzer extends AnyRef
object typerFactory extends SubComponent {
val global: Analyzer.this.global.type = Analyzer.this.global
val phaseName = "typer"
+ val runsAfter = List[String]()
+ val runsRightAfter = Some("namer")
def newPhase(_prev: Phase): StdPhase = new StdPhase(_prev) {
if (!inIDE) resetTyper()
def apply(unit: CompilationUnit) {