summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala
blob: daaa625164ab07c434780f3a74f2c0154d2265db (plain) (blame)
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
/* NSC -- new Scala compiler
 * Copyright 2005-2013 LAMP/EPFL
 * @author  Paul Phillips
 */

package scala.tools.nsc
package symtab

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 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 && 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(_, removed, symMap, _, _) = 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
      )
    }
  }
}