summaryrefslogtreecommitdiff
path: root/src/reflect/scala/reflect/internal/SymbolTable.scala
blob: ef63078f90850ebdd3acfd08bdab07eade1741e2 (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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
/* NSC -- new scala compiler
 * Copyright 2005-2013 LAMP/EPFL
 * @author  Martin Odersky
 */

package scala
package reflect
package internal

import scala.annotation.elidable
import scala.collection.{ mutable, immutable }
import util._
import java.util.concurrent.TimeUnit
import scala.reflect.internal.{TreeGen => InternalTreeGen}

abstract class SymbolTable extends macros.Universe
                              with Collections
                              with Names
                              with Symbols
                              with Types
                              with Variances
                              with Kinds
                              with ExistentialsAndSkolems
                              with FlagSets
                              with Scopes
                              with Mirrors
                              with Definitions
                              with Constants
                              with BaseTypeSeqs
                              with InfoTransformers
                              with transform.Transforms
                              with StdNames
                              with AnnotationInfos
                              with AnnotationCheckers
                              with Trees
                              with Printers
                              with Positions
                              with TypeDebugging
                              with Importers
                              with Required
                              with CapturedVariables
                              with StdAttachments
                              with StdCreators
                              with ReificationSupport
                              with PrivateWithin
                              with pickling.Translations
                              with FreshNames
                              with Internals
                              with Reporting
{

  val gen = new InternalTreeGen { val global: SymbolTable.this.type = SymbolTable.this }

  def log(msg: => AnyRef): Unit

  protected def elapsedMessage(msg: String, start: Long) =
    msg + " in " + (TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start) + "ms"

  def informProgress(msg: String)          = if (settings.verbose) inform("[" + msg + "]")
  def informTime(msg: String, start: Long) = informProgress(elapsedMessage(msg, start))

  def shouldLogAtThisPhase = false
  def isPastTyper = false
  protected def isDeveloper: Boolean = settings.debug

  @deprecated("Use devWarning if this is really a warning; otherwise use log", "2.11.0")
  def debugwarn(msg: => String): Unit = devWarning(msg)

  /** Override with final implementation for inlining. */
  def debuglog(msg:  => String): Unit = if (settings.debug) log(msg)
  def devWarning(msg: => String): Unit = if (isDeveloper) Console.err.println(msg)
  def throwableAsString(t: Throwable): String = "" + t
  def throwableAsString(t: Throwable, maxFrames: Int): String = t.getStackTrace take maxFrames mkString "\n  at "

  @inline final def devWarningDumpStack(msg: => String, maxFrames: Int): Unit =
    devWarning(msg + "\n" + throwableAsString(new Throwable, maxFrames))

  /** Prints a stack trace if -Ydebug or equivalent was given, otherwise does nothing. */
  def debugStack(t: Throwable): Unit  = devWarning(throwableAsString(t))

  private[scala] def printCaller[T](msg: String)(result: T) = {
    Console.err.println("%s: %s\nCalled from: %s".format(msg, result,
      (new Throwable).getStackTrace.drop(2).take(50).mkString("\n")))

    result
  }

  private[scala] def printResult[T](msg: String)(result: T) = {
    Console.err.println(msg + ": " + result)
    result
  }
  @inline
  final private[scala] def logResult[T](msg: => String)(result: T): T = {
    log(msg + ": " + result)
    result
  }
  @inline
  final private[scala] def debuglogResult[T](msg: => String)(result: T): T = {
    debuglog(msg + ": " + result)
    result
  }
  @inline
  final private[scala] def devWarningResult[T](msg: => String)(result: T): T = {
    devWarning(msg + ": " + result)
    result
  }
  @inline
  final private[scala] def logResultIf[T](msg: => String, cond: T => Boolean)(result: T): T = {
    if (cond(result))
      log(msg + ": " + result)

    result
  }
  @inline
  final private[scala] def debuglogResultIf[T](msg: => String, cond: T => Boolean)(result: T): T = {
    if (cond(result))
      debuglog(msg + ": " + result)

    result
  }

  @inline final def findSymbol(xs: TraversableOnce[Symbol])(p: Symbol => Boolean): Symbol = {
    xs find p getOrElse NoSymbol
  }

  // For too long have we suffered in order to sort NAMES.
  // I'm pretty sure there's a reasonable default for that.
  // Notice challenge created by Ordering's invariance.
  implicit def lowPriorityNameOrdering[T <: Names#Name]: Ordering[T] =
    SimpleNameOrdering.asInstanceOf[Ordering[T]]

  private object SimpleNameOrdering extends Ordering[Names#Name] {
    def compare(n1: Names#Name, n2: Names#Name) = (
      if (n1 eq n2) 0
      else n1.toString compareTo n2.toString
    )
  }

  /** Dump each symbol to stdout after shutdown.
   */
  final val traceSymbolActivity = sys.props contains "scalac.debug.syms"
  object traceSymbols extends {
    val global: SymbolTable.this.type = SymbolTable.this
  } with util.TraceSymbolActivity

  val treeInfo: TreeInfo { val global: SymbolTable.this.type }

  /** Check that the executing thread is the compiler thread. No-op here,
   *  overridden in interactive.Global. */
  @elidable(elidable.WARNING)
  def assertCorrectThread() {}

  /** A last effort if symbol in a select <owner>.<name> is not found.
   *  This is overridden by the reflection compiler to make up a package
   *  when it makes sense (i.e. <owner> is a package and <name> is a term name).
   */
  def missingHook(owner: Symbol, name: Name): Symbol = NoSymbol

  /** Returns the mirror that loaded given symbol */
  def mirrorThatLoaded(sym: Symbol): Mirror

  /** A period is an ordinal number for a phase in a run.
   *  Phases in later runs have higher periods than phases in earlier runs.
   *  Later phases have higher periods than earlier phases in the same run.
   */
  type Period = Int
  final val NoPeriod = 0

  /** An ordinal number for compiler runs. First run has number 1. */
  type RunId = Int
  final val NoRunId = 0

  // sigh, this has to be public or enteringPhase doesn't inline.
  var phStack: List[Phase] = Nil
  private[this] var ph: Phase = NoPhase
  private[this] var per = NoPeriod

  final def atPhaseStack: List[Phase] = phStack
  final def phase: Phase = {
    if (Statistics.hotEnabled)
      Statistics.incCounter(SymbolTableStats.phaseCounter)
    ph
  }

  def atPhaseStackMessage = atPhaseStack match {
    case Nil    => ""
    case ps     => ps.reverseMap("->" + _).mkString("(", " ", ")")
  }

  final def phase_=(p: Phase) {
    //System.out.println("setting phase to " + p)
    assert((p ne null) && p != NoPhase, p)
    ph = p
    per = period(currentRunId, p.id)
  }
  final def pushPhase(ph: Phase): Phase = {
    val current = phase
    phase = ph
    phStack ::= ph
    current
  }
  final def popPhase(ph: Phase) {
    phStack = phStack.tail
    phase = ph
  }

  /** The current compiler run identifier. */
  def currentRunId: RunId

  /** The run identifier of the given period. */
  final def runId(period: Period): RunId = period >> 8

  /** The phase identifier of the given period. */
  final def phaseId(period: Period): Phase#Id = period & 0xFF

  /** The current period. */
  final def currentPeriod: Period = {
    //assert(per == (currentRunId << 8) + phase.id)
    per
  }

  /** The phase associated with given period. */
  final def phaseOf(period: Period): Phase = phaseWithId(phaseId(period))

  final def period(rid: RunId, pid: Phase#Id): Period =
    (rid << 8) + pid

  /** Are we later than given phase in compilation? */
  final def isAtPhaseAfter(p: Phase) =
    p != NoPhase && phase.id > p.id

  /** Perform given operation at given phase. */
  @inline final def enteringPhase[T](ph: Phase)(op: => T): T = {
    val saved = pushPhase(ph)
    try op
    finally popPhase(saved)
  }

  final def findPhaseWithName(phaseName: String): Phase = {
    var ph = phase
    while (ph != NoPhase && ph.name != phaseName) {
      ph = ph.prev
    }
    if (ph eq NoPhase) phase else ph
  }
  final def enteringPhaseWithName[T](phaseName: String)(body: => T): T = {
    val phase = findPhaseWithName(phaseName)
    enteringPhase(phase)(body)
  }

  def slowButSafeEnteringPhase[T](ph: Phase)(op: => T): T = {
    if (isCompilerUniverse) enteringPhase(ph)(op)
    else op
  }

  @inline final def exitingPhase[T](ph: Phase)(op: => T): T = enteringPhase(ph.next)(op)
  @inline final def enteringPrevPhase[T](op: => T): T       = enteringPhase(phase.prev)(op)

  @inline final def enteringPhaseNotLaterThan[T](target: Phase)(op: => T): T =
    if (isAtPhaseAfter(target)) enteringPhase(target)(op) else op

  def slowButSafeEnteringPhaseNotLaterThan[T](target: Phase)(op: => T): T =
    if (isCompilerUniverse) enteringPhaseNotLaterThan(target)(op) else op

  final def isValid(period: Period): Boolean =
    period != 0 && runId(period) == currentRunId && {
      val pid = phaseId(period)
      if (phase.id > pid) infoTransformers.nextFrom(pid).pid >= phase.id
      else infoTransformers.nextFrom(phase.id).pid >= pid
    }

  final def isValidForBaseClasses(period: Period): Boolean = {
    def noChangeInBaseClasses(it: InfoTransformer, limit: Phase#Id): Boolean = (
      it.pid >= limit ||
      !it.changesBaseClasses && noChangeInBaseClasses(it.next, limit)
    )
    period != 0 && runId(period) == currentRunId && {
      val pid = phaseId(period)
      if (phase.id > pid) noChangeInBaseClasses(infoTransformers.nextFrom(pid), phase.id)
      else noChangeInBaseClasses(infoTransformers.nextFrom(phase.id), pid)
    }
  }

  def openPackageModule(container: Symbol, dest: Symbol) {
    // unlink existing symbols in the package
    for (member <- container.info.decls.iterator) {
      if (!member.isPrivate && !member.isConstructor) {
        // todo: handle overlapping definitions in some way: mark as errors
        // or treat as abstractions. For now the symbol in the package module takes precedence.
        for (existing <- dest.info.decl(member.name).alternatives)
          dest.info.decls.unlink(existing)
      }
    }
    // enter non-private decls the class
    for (member <- container.info.decls.iterator) {
      if (!member.isPrivate && !member.isConstructor) {
        dest.info.decls.enter(member)
      }
    }
    // enter decls of parent classes
    for (p <- container.parentSymbols) {
      if (p != definitions.ObjectClass) {
        openPackageModule(p, dest)
      }
    }
  }

  /** Convert array parameters denoting a repeated parameter of a Java method
   *  to `JavaRepeatedParamClass` types.
   */
  def arrayToRepeated(tp: Type): Type = tp match {
    case MethodType(params, rtpe) =>
      val formals = tp.paramTypes
      assert(formals.last.typeSymbol == definitions.ArrayClass, formals)
      val method = params.last.owner
      val elemtp = formals.last.typeArgs.head match {
        case RefinedType(List(t1, t2), _) if (t1.typeSymbol.isAbstractType && t2.typeSymbol == definitions.ObjectClass) =>
          t1 // drop intersection with Object for abstract types in varargs. UnCurry can handle them.
        case t =>
          t
      }
      val newParams = method.newSyntheticValueParams(formals.init :+ definitions.javaRepeatedType(elemtp))
      MethodType(newParams, rtpe)
    case PolyType(tparams, rtpe) =>
      PolyType(tparams, arrayToRepeated(rtpe))
  }

  abstract class SymLoader extends LazyType {
    def fromSource = false
  }

  /** if there's a `package` member object in `pkgClass`, enter its members into it. */
  def openPackageModule(pkgClass: Symbol) {

    val pkgModule = pkgClass.info.decl(nme.PACKAGEkw)
    def fromSource = pkgModule.rawInfo match {
      case ltp: SymLoader => ltp.fromSource
      case _ => false
    }
    if (pkgModule.isModule && !fromSource) {
      openPackageModule(pkgModule, pkgClass)
    }
  }

  object perRunCaches {
    import scala.collection.generic.Clearable

    // Weak references so the garbage collector will take care of
    // letting us know when a cache is really out of commission.
    import java.lang.ref.WeakReference
    private var caches = List[WeakReference[Clearable]]()

    def recordCache[T <: Clearable](cache: T): T = {
      caches ::= new WeakReference(cache)
      cache
    }

    /**
     * Removes a cache from the per-run caches. This is useful for testing: it allows running the
     * compiler and then inspect the state of a cache.
     */
    def unrecordCache[T <: Clearable](cache: T): Unit = {
      caches = caches.filterNot(_.get eq cache)
    }

    def clearAll() = {
      debuglog("Clearing " + caches.size + " caches.")
      caches foreach (ref => Option(ref.get).foreach(_.clear))
      caches = caches.filterNot(_.get == null)
    }

    def newWeakMap[K, V]()        = recordCache(mutable.WeakHashMap[K, V]())
    def newMap[K, V]()            = recordCache(mutable.HashMap[K, V]())
    def newSet[K]()               = recordCache(mutable.HashSet[K]())
    def newWeakSet[K <: AnyRef]() = recordCache(new WeakHashSet[K]())

    def newAnyRefMap[K <: AnyRef, V]() = recordCache(mutable.AnyRefMap[K, V]())
    def newGeneric[T](f: => T): () => T = {
      val NoCached: T = null.asInstanceOf[T]
      var cached: T = NoCached
      var cachedRunId = NoRunId
      recordCache(new Clearable {
        def clear(): Unit = cached = NoCached
      })
      () => {
        if (currentRunId != cachedRunId || cached == NoCached) {
          cached = f
          cachedRunId = currentRunId
        }
        cached
      }
    }
  }

  /** The set of all installed infotransformers. */
  var infoTransformers = new InfoTransformer {
    val pid = NoPhase.id
    val changesBaseClasses = true
    def transform(sym: Symbol, tpe: Type): Type = tpe
  }

  /** The phase which has given index as identifier. */
  val phaseWithId: Array[Phase]

  /** Is this symbol table a part of a compiler universe?
   */
  def isCompilerUniverse = false

  @deprecated("Use enteringPhase", "2.10.0") // Used in SBT 0.12.4
  @inline final def atPhase[T](ph: Phase)(op: => T): T = enteringPhase(ph)(op)


  /**
   * Adds the `sm` String interpolator to a [[scala.StringContext]].
   */
  implicit val StringContextStripMarginOps: StringContext => StringContextStripMarginOps = util.StringContextStripMarginOps
}

object SymbolTableStats {
  val phaseCounter = Statistics.newCounter("#phase calls")
}