summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala
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/scala/tools/nsc/symtab/SymbolTrackers.scala
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/scala/tools/nsc/symtab/SymbolTrackers.scala')
-rw-r--r--src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala129
1 files changed, 129 insertions, 0 deletions
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", ""
+ )
+ )
+ }
+ }
+}