1
package scala.reflect.reify
4
import scala.collection._
5
import scala.compat.Platform.EOL
14
class SymbolTable private[SymbolTable] (
15
private[SymbolTable] val symtab: immutable.ListMap[Symbol, Tree] = immutable.ListMap[Symbol, Tree](),
16
private[SymbolTable] val aliases: List[(Symbol, TermName)] = List[(Symbol, TermName)](),
17
private[SymbolTable] val original: Option[List[Tree]] = None) {
19
def syms: List[Symbol] = symtab.keys.toList
20
def isConcrete: Boolean = symtab.values forall (sym => !FreeTypeDef.unapply(sym).isDefined)
22
// def aliases: Map[Symbol, List[TermName]] = aliases.distinct groupBy (_._1) mapValues (_ map (_._2))
24
def symDef(sym: Symbol): Tree =
25
symtab.getOrElse(sym, EmptyTree)
27
def symName(sym: Symbol): TermName =
28
symtab.get(sym) match {
29
case Some(FreeDef(_, name, _, _, _)) => name
30
case Some(SymDef(_, name, _, _)) => name
31
case None => nme.EMPTY
34
def symAliases(sym: Symbol): List[TermName] =
36
case name if name.isEmpty => Nil
37
case _ => (aliases.distinct groupBy (_._1) mapValues (_ map (_._2)))(sym)
40
def symBinding(sym: Symbol): Tree =
41
symtab.get(sym) match {
42
case Some(FreeDef(_, _, binding, _, _)) => binding
43
case Some(SymDef(_, _, _, _)) => throw new UnsupportedOperationException(s"${symtab(sym)} is a symdef, hence it doesn't have a binding")
44
case None => EmptyTree
47
def symRef(sym: Symbol): Tree =
48
symtab.get(sym) match {
49
case Some(FreeDef(_, name, binding, _, _)) => Ident(name) updateAttachment binding
50
case Some(SymDef(_, name, _, _)) => Ident(name) updateAttachment ReifyBindingAttachment(Ident(sym))
51
case None => EmptyTree
54
def +(sym: Symbol, name: TermName, reification: Tree): SymbolTable = add(sym, name, reification)
55
def +(symDef: Tree): SymbolTable = add(symDef)
56
def ++(symDefs: TraversableOnce[Tree]): SymbolTable = (this /: symDefs)((symtab, symDef) => symtab.add(symDef))
57
def ++(symtab: SymbolTable): SymbolTable = { val updated = this ++ symtab.symtab.values; new SymbolTable(updated.symtab, updated.aliases ++ symtab.aliases) }
58
def -(sym: Symbol): SymbolTable = remove(sym)
59
def -(name: TermName): SymbolTable = remove(name)
60
def -(symDef: Tree): SymbolTable = remove(reifyBinding(symDef).symbol)
61
def --(syms: GenTraversableOnce[Symbol]): SymbolTable = (this /: syms)((symtab, sym) => symtab.remove(sym))
62
def --(names: Iterable[TermName]): SymbolTable = (this /: names)((symtab, name) => symtab.remove(name))
63
def --(symDefs: TraversableOnce[Tree]): SymbolTable = this -- (symDefs map (reifyBinding(_)))
64
def --(symtab: SymbolTable): SymbolTable = { val updated = this -- symtab.symtab.values; new SymbolTable(updated.symtab, updated.aliases diff symtab.aliases) }
65
def filterSyms(p: Symbol => Boolean): SymbolTable = this -- (syms filterNot p)
66
def filterAliases(p: (Symbol, TermName) => Boolean): SymbolTable = this -- (aliases filterNot (tuple => p(tuple._1, tuple._2)) map (_._2))
68
private def add(symDef: Tree): SymbolTable = {
69
val sym = reifyBinding(symDef).symbol
70
assert(sym != NoSymbol, showRaw(symDef))
71
val name = symDef match {
72
case FreeDef(_, name, _, _, _) => name
73
case SymDef(_, name, _, _) => name
75
val newSymtab = if (!(symtab contains sym)) symtab + (sym -> symDef) else symtab
76
val newAliases = aliases :+ (sym -> name)
77
new SymbolTable(newSymtab, newAliases)
80
private def add(sym: Symbol, name0: TermName, reification: Tree): SymbolTable = {
81
def freshName(name0: TermName): TermName = {
82
var name = name0.toString
83
name = name.replace(".type", "$type")
84
name = name.replace(" ", "$")
85
val fresh = typer.context.unit.fresh
86
newTermName(fresh.newName(name))
88
val bindingAttachment = reification.attachments.get[ReifyBindingAttachment].get
89
add(ValDef(NoMods, freshName(name0), TypeTree(), reification) updateAttachment bindingAttachment)
92
private def add(sym: Symbol, name: TermName): SymbolTable = {
93
if (!(syms contains sym)) error("cannot add an alias to a symbol not in the symbol table")
94
add(sym, name, EmptyTree)
97
private def remove(sym: Symbol): SymbolTable = {
98
val newSymtab = symtab - sym
99
val newAliases = aliases filter (_._1 != sym)
100
new SymbolTable(newSymtab, newAliases)
103
private def remove(name: TermName): SymbolTable = {
104
var newSymtab = symtab
105
val newAliases = aliases filter (_._2 != name)
106
newSymtab = newSymtab filter { case ((sym, _)) => newAliases exists (_._1 == sym) }
107
newSymtab = newSymtab map { case ((sym, tree)) =>
108
val ValDef(mods, primaryName, tpt, rhs) = tree
110
if (!(newAliases contains (sym, primaryName))) {
111
val primaryName1 = newAliases.find(_._1 == sym).get._2
112
ValDef(mods, primaryName1, tpt, rhs).copyAttrs(tree)
116
new SymbolTable(newSymtab, newAliases)
119
private val cache = mutable.Map[SymbolTable, List[Tree]]()
120
def encode: List[Tree] = cache.getOrElseUpdate(this, SymbolTable.encode(this)) map (_.duplicate)
122
override def toString = {
123
val symtabString = symtab.keys.map(symName(_)).mkString(", ")
124
val trueAliases = aliases.distinct.filter(entry => symName(entry._1) != entry._2)
125
val aliasesString = trueAliases.map(entry => s"${symName(entry._1)} -> ${entry._2}").mkString(", ")
126
s"""symtab = [$symtabString], aliases = [$aliasesString]${if (original.isDefined) ", has original" else ""}"""
129
def debugString: String = {
130
val buf = new StringBuilder
131
buf.append("symbol table = " + (if (syms.length == 0) "<empty>" else "")).append(EOL)
132
syms foreach (sym => buf.append(symDef(sym)).append(EOL))
133
buf.delete(buf.length - EOL.length, buf.length)
139
def apply(): SymbolTable =
142
def apply(encoded: List[Tree]): SymbolTable = {
143
var result = new SymbolTable(original = Some(encoded))
144
encoded foreach (entry => (entry.attachments.get[ReifyBindingAttachment], entry.attachments.get[ReifyAliasAttachment]) match {
145
case (Some(ReifyBindingAttachment(_)), _) => result += entry
146
case (_, Some(ReifyAliasAttachment(sym, alias))) => result = new SymbolTable(result.symtab, result.aliases :+ (sym, alias))
147
case _ => // do nothing, this is boilerplate that can easily be recreated by subsequent `result.encode`
152
private[SymbolTable] def encode(symtab0: SymbolTable): List[Tree] = {
153
if (symtab0.original.isDefined) return symtab0.original.get.map(_.duplicate)
154
else assert(hasReifier, "encoding a symbol table requires a reifier")
155
// during `encode` we might need to do some reifications
156
// these reifications might lead to changes in `reifier.symtab`
157
// reifier is mutable, symtab is immutable. this is a tough friendship
158
val backup = reifier.state.backup
159
reifier.state.symtab = symtab0.asInstanceOf[reifier.SymbolTable]
160
def currtab = reifier.symtab.asInstanceOf[SymbolTable]
162
val cumulativeSymtab = mutable.ArrayBuffer[Tree](symtab0.symtab.values.toList: _*)
163
val cumulativeAliases = mutable.ArrayBuffer[(Symbol, TermName)](symtab0.aliases: _*)
165
def fillInSymbol(sym: Symbol): Tree = {
166
if (reifyDebug) println("Filling in: %s (%s)".format(sym, sym.accurateKindString))
167
val isFreeTerm = FreeTermDef.unapply(currtab.symDef(sym)).isDefined
168
// SI-6204 don't reify signatures for incomplete symbols, because this might lead to cyclic reference errors
170
if (sym.isInitialized) {
171
if (sym.isCapturedVariable) capturedVariableType(sym)
172
else if (isFreeTerm) sym.tpe
175
val rset = reifier.mirrorBuildCall(nme.setTypeSignature, currtab.symRef(sym), reifier.reify(signature))
176
// `Symbol.annotations` doesn't initialize the symbol, so we don't need to do anything special here
177
// also since we call `sym.info` a few lines above, by now the symbol will be initialized (if possible)
178
// so the annotations will be filled in and will be waiting to be reified (unless symbol initialization is prohibited as described above)
179
if (sym.annotations.isEmpty) rset
180
else reifier.mirrorBuildCall(nme.setAnnotations, rset, reifier.mkList(sym.annotations map reifier.reifyAnnotationInfo))
183
// `fillInSymbol` might add symbols to `symtab`, that's why this is done iteratively
185
while (progress < cumulativeSymtab.length) {
186
val sym = reifyBinding(cumulativeSymtab(progress)).symbol
187
if (sym != NoSymbol) {
188
val symtabProgress = currtab.symtab.size
189
val aliasesProgress = currtab.aliases.length
190
val fillIn = fillInSymbol(sym)
191
cumulativeSymtab ++= currtab.symtab.values drop symtabProgress
192
cumulativeAliases ++= currtab.aliases drop aliasesProgress
193
cumulativeSymtab += fillIn
198
val withAliases = cumulativeSymtab flatMap (entry => {
199
val result = mutable.ListBuffer[Tree]()
201
val sym = reifyBinding(entry).symbol
203
result ++= cumulativeAliases.distinct filter (alias => alias._1 == sym && alias._2 != currtab.symName(sym)) map (alias => {
204
val canonicalName = currtab.symName(sym)
205
val aliasName = alias._2
206
ValDef(NoMods, aliasName, TypeTree(), Ident(canonicalName)) updateAttachment ReifyAliasAttachment(sym, aliasName)
213
reifier.state.restore(backup)
b'\\ No newline at end of file'