1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
|
/* NSC -- new Scala compiler
* Copyright 2005-2012 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools.nsc
package symtab
import scala.collection.{ mutable, immutable }
import scala.language.implicitConversions
import scala.language.postfixOps
/** 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],
trees: Map[Symbol, Set[Tree]], // symbol -> trees which proudly display it
owners: Map[Symbol, Symbol], // symbol -> previous owner
flags: Map[Symbol, Long] // symbol -> previous flags
)
object SymbolTracker {
def containsSymbol(t: Tree) = t.symbol != null && t.symbol != NoSymbol
// This is noise reduction only.
def dropSymbol(sym: Symbol) = sym.ownerChain exists (_ hasFlag Flags.SPECIALIZED)
def symbolSnapshot(unit: CompilationUnit): Map[Symbol, Set[Tree]] = {
if (unit.body == null) Map()
else unit.body filter containsSymbol groupBy (_.symbol) mapValues (_.toSet) toMap
}
def apply(unit: CompilationUnit) = new SymbolTracker(
() => symbolSnapshot(unit) filterNot { case (k, _) => dropSymbol(k) }
)
}
class SymbolTracker(snapshotFn: () => Map[Symbol, Set[Tree]]) {
def flagsMask: Long = Flags.PrintableFlags
private var currentMap = Map[Symbol, Set[Tree]]()
private var prevMap = Map[Symbol, Set[Tree]]()
private def current = currentMap.keySet
private def prev = prevMap.keySet
private var history = List[Change](Change(Set(), Set(), Map(), Map(), Map()))
private var prevFlags = Map[Symbol, Long]()
private var prevOwners = Map[Symbol, Symbol]()
private def changed = history.head
private def isAdded(sym: Symbol) = changed added sym
private def isOwnerChange(sym: Symbol) = changed.owners contains sym
private def isFlagsChange(sym: Symbol) = changed.flags contains sym
private implicit def NodeOrdering: Ordering[Node] = Ordering by (_.root)
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 masked = root.flags & flagsMask
def indicatorString =
if (isAdded(root)) "* "
else List(
if (isFlagsChange(root)) "F" else "",
if (isOwnerChange(root)) "O" else "",
" "
).mkString take 2
def changedOwnerString = changed.owners get root match {
case Some(prev) => " [Owner was " + prev + ", now " + root.owner + "]"
case _ => ""
}
def flagSummaryString = changed.flags get root match {
case Some(oldFlags) =>
val added = masked & ~oldFlags
val removed = oldFlags & ~masked
val steady = masked & ~(added | removed)
val all = masked | oldFlags
val strs = 0 to 63 map { bit =>
val flag = 1L << bit
val prefix = (
if ((added & flag) != 0L) "+"
else if ((removed & flag) != 0L) "-"
else ""
)
if ((all & flag) == 0L) ""
else prefix + Flags.flagToString(flag)
}
" " + strs.filterNot(_ == "").mkString("[", " ", "]")
case _ =>
if (masked == 0L) ""
else " (" + Flags.flagsToString(masked) + ")"
}
def symString(sym: Symbol) = (
if (settings.debug.value && sym.hasCompleteInfo) {
val s = sym.defString take 240
if (s.length == 240) s + "..." else s
}
else sym + changedOwnerString + flagSummaryString
)
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 = {
currentMap = snapshotFn()
val added = current filterNot prev
val removed = prev filterNot current
val steady = prev intersect current
def changedOwner(sym: Symbol) = prevOwners get sym filter (_ != sym.owner)
def changedFlags(sym: Symbol) = prevFlags get sym filter (_ != (sym.flags & flagsMask))
val owners = ({
for (sym <- steady; old <- changedOwner(sym)) yield
(sym, old)
}).toMap
val flags = ({
for (sym <- steady; old <- changedFlags(sym)) yield
(sym, old)
}).toMap
val change = Change(added, removed, prevMap, owners, flags)
prevMap = currentMap
prevOwners = current map (s => (s, s.owner)) toMap;
prevFlags = current map (s => (s, (s.flags & flagsMask))) toMap;
history = change :: history
}
def show(label: String): String = {
val hierarchy = Node(current)
val Change(added, removed, symMap, owners, flags) = history.head
def detailString(sym: Symbol) = {
val ownerString = sym.ownerChain splitAt 3 match {
case (front, back) =>
val xs = if (back.isEmpty) front else front :+ "..."
xs mkString " -> "
}
val treeStrings = symMap(sym) map { t =>
"%10s: %s".format(t.shortClass, t)
}
ownerString :: treeStrings mkString "\n"
}
def removedString = (removed: List[Symbol]).zipWithIndex map {
case (t, i) => "(%2s) ".format(i + 1) + detailString(t)
} mkString "\n"
"" + hierarchy + (
if (removed.isEmpty) ""
else "\n\n!!! " + label + ", " + removed.size + " symbols vanished:\n" + removedString
)
}
}
}
|