From f9d6f834b6700ce90f2235604e7f269748c92276 Mon Sep 17 00:00:00 2001 From: nielsen Date: Thu, 5 Feb 2009 14:07:53 +0000 Subject: Compiler implementation of SIP 00002 and fixes ... Compiler implementation of SIP 00002 and fixes for tests and other tools like scaladoc --- src/compiler/scala/tools/nsc/Global.scala | 270 ++++++++++++----- src/compiler/scala/tools/nsc/PhaseAssembly.scala | 334 +++++++++++++++++++++ src/compiler/scala/tools/nsc/ScalaDoc.scala | 5 + src/compiler/scala/tools/nsc/Settings.scala | 1 + src/compiler/scala/tools/nsc/SubComponent.scala | 18 +- .../scala/tools/nsc/plugins/PluginComponent.scala | 14 +- src/compiler/scala/tools/nsc/plugins/Plugins.scala | 58 +--- .../scala/tools/nsc/typechecker/Analyzer.scala | 4 + 8 files changed, 575 insertions(+), 129 deletions(-) create mode 100644 src/compiler/scala/tools/nsc/PhaseAssembly.scala (limited to 'src') 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 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 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) { -- cgit v1.2.3