summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-02-06 07:41:38 +0000
committerPaul Phillips <paulp@improving.org>2011-02-06 07:41:38 +0000
commita4bbb15aa23198e1b482e2ef6ab18cf5c7f4b2be (patch)
treefc6f901ad16e5ec3b4fc79a90a9bc8a0009a697d /src/compiler
parent1f189a0d91deb00ad86ef213780a4374c7332e50 (diff)
downloadscala-a4bbb15aa23198e1b482e2ef6ab18cf5c7f4b2be.tar.gz
scala-a4bbb15aa23198e1b482e2ef6ab18cf5c7f4b2be.tar.bz2
scala-a4bbb15aa23198e1b482e2ef6ab18cf5c7f4b2be.zip
Added new option -Yshow-syms.
symbols. Specifically, it extracts all the symbols attached to AST nodes, and then prints them hierarchically along with indicators for new symbols and other interesting factoids like disappearing symbols. A small demonstration. Output shown is obviously only a fraction of what is produced. // a.scala class A { Nil foreach println } % scalac -Yshow-syms -uniqid a.scala [[symbol layout at end of selectivecps]] class A#17 constructor A#8019 value <local A>#8020 value $anonfun#10961 <synthetic> value x#10971 <synthetic> [[symbol layout at end of uncurry]] class A#17 constructor A#8019 value <local A>#8020 * anonymous class $anonfun#10993 final <synthetic> * constructor $anonfun#11001 * method apply#10994 final value x#10971 <synthetic> * value <local $anonfun>#10999 No review.
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala22
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala1
-rw-r--r--src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala129
3 files changed, 151 insertions, 1 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index b132b47417..56426cfcfd 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -16,7 +16,7 @@ import reporters.{ Reporter, ConsoleReporter }
import util.{ Exceptional, ClassPath, SourceFile, Statistics, BatchSourceFile, ScriptSourceFile, ShowPickled, returning }
import reflect.generic.{ PickleBuffer, PickleFormat }
-import symtab.{ Flags, SymbolTable, SymbolLoaders }
+import symtab.{ Flags, SymbolTable, SymbolLoaders, SymbolTrackers }
import symtab.classfile.Pickler
import dependencies.DependencyAnalysis
import plugins.Plugins
@@ -249,6 +249,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
def profileMem = settings.YprofileMem.value
def richExes = settings.YrichExes.value
def showTrees = settings.Xshowtrees.value
+ def showSymbols = settings.Yshowsyms.value
def target = settings.target.value
def typerDebug = settings.Ytyperdebug.value
def unchecked = settings.unchecked.value
@@ -854,6 +855,21 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
// If -Yprofile isn't given this will never be triggered.
lazy val profiler = Class.forName(opt.profileClass).newInstance().asInstanceOf[Profiling]
+ // Similarly, this will only be created under -Yshow-syms.
+ object trackerFactory extends SymbolTrackers {
+ val global: Global.this.type = Global.this
+ lazy val trackers = currentRun.units.toList map (x => SymbolTracker(x))
+ def snapshot() = {
+ inform("\n[[symbol layout at end of " + phase + "]]")
+ atPhase(phase.next) {
+ trackers foreach { t =>
+ t.snapshot()
+ inform(t.show())
+ }
+ }
+ }
+ }
+
/** Compile list of source files */
def compileSources(_sources: List[SourceFile]) {
val depSources = dependencyAnalysis.calculateFiles(_sources.distinct) // bug #1268, scalac confused by duplicated filenames
@@ -898,6 +914,10 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
if (opt.showTrees) nodePrinters.printAll()
else printAllUnits()
}
+ // print the symbols presently attached to AST nodes
+ if (opt.showSymbols)
+ trackerFactory.snapshot()
+
// print members
if (opt.showPhase)
showMembers()
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index 2819d3f9ed..4aa7c5c1ce 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -120,6 +120,7 @@ trait ScalaSettings extends AbsScalaSettings with StandardScalaSettings {
val Yrecursion = IntSetting ("-Yrecursion", "Set recursion depth used when locking symbols.", 0, Some(0, Int.MaxValue), (_: String) => None)
val selfInAnnots = BooleanSetting ("-Yself-in-annots", "Include a \"self\" identifier inside of annotations.")
val Xshowtrees = BooleanSetting ("-Yshow-trees", "(Requires -Xprint:) Print detailed ASTs.")
+ val Yshowsyms = BooleanSetting ("-Yshow-syms", "Print the AST symbol hierarchy after each phase.")
val skip = PhasesSetting ("-Yskip", "Skip")
val Ynosqueeze = BooleanSetting ("-Yno-squeeze", "Disable creation of compact code in matching.")
val Ystatistics = BooleanSetting ("-Ystatistics", "Print compiler statistics.") .
diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala b/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala
new file mode 100644
index 0000000000..353b1ffd89
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala
@@ -0,0 +1,129 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+package symtab
+
+/** Printing the symbol graph (for those symbols attached to an AST node)
+ * after each phase.
+ */
+trait SymbolTrackers {
+ val global: Global
+ import global._
+
+ private implicit lazy val SymbolOrdering: Ordering[Symbol] =
+ Ordering by (x => (x.kindString, x.name.toString))
+
+ private implicit def toList[T: Ordering](xs: Set[T]): List[T] = xs.toList.sorted
+
+ /** Reversing the direction of Symbol's owner arrow. */
+ trait Hierarchy {
+ def root: Symbol
+ def children: List[Hierarchy]
+ def flatten: Set[Symbol]
+ def indentString(indent: String): String
+ def symString(sym: Symbol): String
+
+ override def toString() = indentString("")
+ }
+ case class Change(
+ added: Set[Symbol],
+ removed: Set[Symbol],
+ changedOwner: Map[Symbol, Symbol]
+ )
+
+ object SymbolTracker {
+ def containsSymbol(t: Tree) = t.symbol != null && t.symbol != NoSymbol
+
+ def symbolsInUnit(unit: CompilationUnit): Set[Symbol] = {
+ if (unit.body == null) Set.empty[Symbol]
+ else unit.body filter containsSymbol map (_.symbol) toSet
+ }
+ def apply(unit: CompilationUnit) = new SymbolTracker(
+ () => symbolsInUnit(unit) filterNot (_.ownerChain.exists(_ hasFlag Flags.SPECIALIZED))
+ )
+ }
+
+ class SymbolTracker(sourceFn: () => Set[Symbol]) {
+ private var history = List[Change]()
+ private var prev = Set[Symbol]()
+ private var prevOwners = Map[Symbol, Symbol]()
+ private def changedOwner(sym: Symbol) = prevOwners get sym filter (_ != sym.owner)
+ private def changedOwnerString(sym: Symbol) = changedOwner(sym) match {
+ case Some(prev) => "[Owner changed: was " + ownersString(sym, 2) + "]"
+ case _ => ""
+ }
+ private implicit def NodeOrdering: Ordering[Node] = Ordering by (_.root)
+ private def ownersString(sym: Symbol, num: Int) = sym.ownerChain drop 1 take num mkString " -> "
+ private def allOwnersString(sym: Symbol) = sym.ownerChain mkString " -> "
+ private def isAdded(sym: Symbol) = history.nonEmpty && history.head.added(sym)
+ private def isOwnerChange(sym: Symbol) = history.nonEmpty && (history.head.changedOwner contains sym)
+
+ object Node {
+ def nodes(syms: Set[Symbol]): List[Node] = {
+ def descendents(s: Symbol) = (syms - s) filter (_ hasTransOwner s)
+ def rooted(root: Symbol) = new Node(root, nodes(descendents(root)))
+
+ val roots = syms filterNot (_.ownerChain drop 1 exists syms)
+ val deep = roots map rooted
+ val deepSyms = deep flatMap (_.flatten)
+
+ deep ++ (syms filterNot deepSyms map (x => Node(x)))
+ }
+
+ def apply(sym: Symbol): Node = new Node(sym, Nil)
+ def apply(syms: Set[Symbol]): Node = nodes(syms) match {
+ case List(x) => x
+ case xs => new Node(NoSymbol, xs)
+ }
+ }
+ class Node(val root: Symbol, val children: List[Hierarchy]) extends Hierarchy {
+ def indicatorString = if (isAdded(root)) "* " else " "
+
+ def symString(sym: Symbol) = (
+ sym + changedOwnerString(sym) + " " +
+ sym.hasFlagsToString(Flags.PrintableFlags)
+ )
+
+ def flatten = children.foldLeft(Set(root))(_ ++ _.flatten)
+ def indentString(indent: String): String = {
+ if (root == NoSymbol)
+ children map (c => c.indentString(indent)) mkString "\n"
+ else {
+ indicatorString + indent + symString(root) + (
+ if (children.isEmpty) ""
+ else children map (c => c.indentString(indent + " ")) mkString ("\n", "\n", "")
+ )
+ }
+ }
+ }
+
+ def snapshot(): Unit = {
+ val syms = sourceFn()
+ val added = syms filterNot prev
+ val removed = prev filterNot syms
+ val changed = ({
+ for (sym <- prev intersect syms; old <- changedOwner(sym)) yield
+ (sym, old)
+ }).toMap
+
+ val change = Change(added, removed, changed)
+ prev = syms
+ prevOwners = syms map (s => (s, s.owner)) toMap;
+ history = change :: history
+ }
+ def show(): String = {
+ val hierarchy = Node(sourceFn())
+ val removed = if (history.isEmpty) Set() else history.head.removed
+
+ hierarchy.toString() + (
+ if (removed.isEmpty) ""
+ else removed map allOwnersString mkString (
+ "\n\n!!! " + removed.size + " symbols vanished:\n", "\n", ""
+ )
+ )
+ }
+ }
+}