diff options
author | Paul Phillips <paulp@improving.org> | 2011-02-06 07:41:38 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2011-02-06 07:41:38 +0000 |
commit | a4bbb15aa23198e1b482e2ef6ab18cf5c7f4b2be (patch) | |
tree | fc6f901ad16e5ec3b4fc79a90a9bc8a0009a697d /src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala | |
parent | 1f189a0d91deb00ad86ef213780a4374c7332e50 (diff) | |
download | scala-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.scala | 129 |
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", "" + ) + ) + } + } +} |