@@ -3,205 +3,19 @@ package api
3
3
4
4
/** This trait defines symbols and operations on them.
5
5
*
6
- * See the [[ docs.scala-lang.org/overviews/reflection/overview.html Reflection Guide ]] for a description of symbols
7
- * and infomation on getting started with Scala reflection API.
6
+ * Symbols are used to establish bindings between a name and the entity it refers to, such as a class or a method.
7
+ * Anything you define and can give a name to in Scala has a symbol.
8
8
*
9
- * === Symbols from a compile-time perspective ===
9
+ * Certain types of tree nodes, such as [[scala.reflect.api.Trees#Ident Ident ]] (references to identifiers) and
10
+ * [[scala.reflect.api.Trees#Select Select ]] (references to members) expose method [[Trees.SymTreeApi.symbol ]]
11
+ * to obtain the symbol that represents their declaration.
10
12
*
11
- * Anything you define in Scala has a symbol. If you give something a name, then it has a symbol associated with it.
12
- * If you didn't give it a name, but you could have, then it has a symbol .
13
+ * See the [[ docs.scala-lang.org/overviews/reflection/overview.html Reflection Guide ]] for more details and
14
+ * intricacies about symbols .
13
15
*
14
- * Symbols are used by the Scala compiler to establish bindings. When typechecking a Scala program,
15
- * the compiler populates [[scala.reflect.api.Trees#RefTrees ref trees ]], such as [[scala.reflect.api.Trees#Ident Ident ]]
16
- * (references to identifiers) and [[scala.reflect.api.Trees#Select Select ]] (references to members)
17
- * with symbols that represent the declarations being referred to. Populating means setting the `symbol`
18
- * field to a non-empty value.
19
- *
20
- * Here's an example of how trees look after the `typer` phase of the Scala compiler (this phase performs the typechecking).
21
- * {{{
22
- * >cat Test.scala
23
- * def foo[T: TypeTag](x: Any) = x.asInstanceOf[T]
24
- *
25
- * >scalac -Xprint:typer -uniqid Test.scala
26
- * [[syntax trees at end of typer ]]// Scala source: Test.scala
27
- * def foo#8339
28
- * [T#8340 >: Nothing#4658 <: Any#4657]
29
- * (x#9529: Any#4657)
30
- * (implicit evidence$1#9530: TypeTag#7861[T#8341])
31
- * : T#8340 =
32
- * x#9529.asInstanceOf#6023[T#8341];
33
- * }}}
34
- *
35
- * Shortly put, we write a small snippet and then compile it with scalac, asking the compiler to dump the trees
36
- * after the typer phase, printing unique ids of the symbols assigned to trees (if any).
37
- *
38
- * The resulting printout shows that identifiers have been linked to corresponding definitions.
39
- * For example, on the one hand, the `ValDef("x", ...)`, which represents the parameter of the method `foo`,
40
- * defines a method symbol with `id=9529`. On the other hand, the `Ident("x")` in the body of the method
41
- * got its `symbol` field set to the same symbol, which establishes the binding.
42
- *
43
- * In the light of this discussion, it might come as a surprise that the definition of the type parameter `T`
44
- * has a symbol with `id=8340`, whereas references to this type parameter all have a symbol with `id=8341`.
45
- * This is the only exception from the general principe outlined above. It happens because the Scala compiler
46
- * skolemizes type parameters (creates new symbols very similar to the original ones) before entering scopes
47
- * that define these parameters. This is an advanced feature of Scala, and the knowledge of it is needed only
48
- * when writing complex macros, but symbols in the macro universe [[scala.reflect.macros.Universe ]] have the
49
- * `deskolemize` method, which goes back from skolems to the originating type parameters.
50
- *
51
- * === Symbols from a runtime perspective ===
52
- *
53
- * From the point of view of a runtime reflection framework, symbols are akin to [[java.lang.reflect.Member ]] from Java
54
- * and [[System.Reflection.MemberInfo ]] from .NET. But not only they represent members - they also represent
55
- * classes, objects and even packages.
56
- *
57
- * Also similarly to the base classes in the reflection facilities of JVM and .NET, Scala symbols have subclasses
58
- * that describe particular flavors of definitions. [[scala.reflect.api.Symbols#TermSymbol ]] models term definitions
59
- * (such as lazy and eager vals, vars and parameters of methods). Its subclasses are [[scala.reflect.api.Symbols#MethodSymbol ]]
60
- * and [[scala.reflect.api.Symbols#ModuleSymbol ]] (representing "modules", which in Scala compiler speak mean "objects").
61
- * [[scala.reflect.api.Symbols#TypeSymbol ]] along with its subclass [[scala.reflect.api.Symbols#ClassSymbol ]]
62
- * describes type definitions in Scala (type aliases, type members, type parameters, classes and traits).
63
- *
64
- * Most reflection APIs that return symbols return non-specific [[scala.reflect.api.Symbols#Symbol ]], because upon failure
65
- * they don't raise exceptions, but rather produce `NoSymbol`, a special singleton, which is a null object for symbols.
66
- * Therefore to use such APIs one has to first check whether a callee returned a valid symbol and, if yes, then perform
67
- * a cast using one of the `asTerm`, `asMethod`, `asModule`, `asType` or `asClass` methods. This is arguably inconvenient
68
- * and might be improved in the future.
69
- *
70
- * Unlike [[scala.reflect.api.Trees trees ]] and [[scala.reflect.api.Types types ]], symbols should not be created directly.
71
- * Instead one should load the symbols from the global symbol table maintained by the compiler.
72
- * To get a symbol that corresponds to a top-level class or object, one can use the `staticClass` and `staticModule`
73
- * methods of [[scala.reflect.api.Mirror ]]. To get a symbol that corresponds to a certain member, there are `members`
74
- * and `declarations` methods of [[scala.reflect.api.Types#Type ]], which brings the discussion to the next point: type signatures.
75
- *
76
- * Each symbol has a type signature, which describes its type and is available via the `typeSignature` method
77
- * on [[scala.reflect.api.Symbols#Symbol ]]. Classes have signatures of the [[scala.reflect.api.Types#ClassInfoType ]] type,
78
- * which knows the list of its members and declarations. Modules per se don't have interesting signatures. To access members
79
- * of modules, one first has to obtain a module class (using the `moduleClass` method) and then inspect its signature.
80
- * Members have type signatures of their own: method signatures feature information about parameters and result types,
81
- * type member signatures store upper and lower bounds and so on.
82
- *
83
- * One thing to know about type signatures is that `typeSignature` method always returns signatures in the most generic
84
- * way possible, even if the underlying symbol is obtained from an instantiation of a generic type. For example, signature
85
- * of the method `def map[B](f: (A) ⇒ B): List[B]`, which refers to the type parameter `A` of the declaring class `List[A]`,
86
- * will always feature `A`, regardless of whether `map` is loaded from the `List[_]` or from `List[Int]`. To get a signature
87
- * with type parameters appropriately instantiated, one should use `typeSignatureIn`.
88
- *
89
- * Symbols are at the heart of the reflection API. Along with the type signatures, which are arguably the most important
90
- * use of reflection, they provide comprehensive information about the underlying definitions. This includes various
91
- * `isXXX` test methods such as `isPublic` or `isFinal`, `params` and `returnType` methods for method symbols,
92
- * `baseClasses` for class symbols and so on. Be prepared - some of these methods don't make sense on the ultimate
93
- * base class Symbol, so they are declared in subclasses.
94
- *
95
- * === Exploring symbols ===
96
- *
97
- * In this example we'll try to get a hold on a symbol that represents the `map` method of `List`,
98
- * and then do something interesting with it.
99
- *
100
- * First of all, to obtain a symbol, one needs to load its enclosing top-level class or module.
101
- * There are two ways of doing that. The first one is getting a symbol by its name using a mirror
102
- * (refer to [[scala.reflect.api.package the reflection overview ]] for information about mirrors).
103
- * Another one is getting a type with [[scaa.reflect.api.Types#typeOf ]] and using its `typeSymbol` method.
104
- * The second approach is preferable, because it's typesafe, but sometimes it's unavailable.
105
- *
106
- * {{{
107
- * scala> import scala.reflect.runtime.universe._
108
- * import scala.reflect.runtime.universe._
109
- *
110
- * scala> val cm = runtimeMirror(getClass.getClassLoader)
111
- * cm: reflect.runtime.universe.Mirror = JavaMirror with ...
112
- *
113
- * scala> val list = cm.staticClass("scala.List")
114
- * list: reflect.runtime.universe.ClassSymbol = class List
115
- *
116
- * scala> val list = typeOf[List[_]].typeSymbol
117
- * list: reflect.runtime.universe.Symbol = class List
118
- * }}}
119
- *
120
- * Now when the enclosing class is obtained, there's a straight path to getting its member
121
- * using `typeSignature` and `member` methods discussed above:
122
- *
123
- * {{{
124
- * scala> val map = list.typeSignature.member("map": TermName).asMethod
125
- * map: reflect.runtime.universe.MethodSymbol = method map
126
- *
127
- * scala> map.typeSignature
128
- * res0: reflect.runtime.universe.Type = [B, That](f: A => B)(implicit bf:
129
- * scala.collection.generic.CanBuildFrom[Repr,B,That])That
130
- *
131
- * scala> map.typeSignatureIn(typeOf[List[Int]])
132
- * res1: reflect.runtime.universe.Type = [B, That](f: Int => B)(implicit bf:
133
- * scala.collection.generic.CanBuildFrom[List[Int],B,That])That
134
- *
135
- * scala> map.params
136
- * res2: List[List[reflect.runtime.universe.Symbol]] = List(List(value f), List(value bf))
137
- *
138
- * scala> val filter = map.params(0)(0)
139
- * filter: reflect.runtime.universe.Symbol = value f
140
- *
141
- * scala> filter.name
142
- * res3: reflect.runtime.universe.Name = f
143
- *
144
- * scala> filter.typeSignature
145
- * res4: reflect.runtime.universe.Type = A => B
146
- * }}}
147
- *
148
- * === Gotcha #1: Overloaded methods ===
149
- *
150
- * Be careful though, because overloaded methods are represented as instances of TermSymbol
151
- * with multiple `alternatives` that have to be resolved manually. For example, a lookup
152
- * for a member named `mkString` will produce not a MethodSymbol, but a TermSymbol:
153
- *
154
- * {{{
155
- * scala> list.typeSignature.member("mkString": TermName)
156
- * res1: reflect.runtime.universe.Symbol = value mkString
157
- *
158
- * scala> val mkString = list.typeSignature.member("mkString": TermName).asTerm
159
- * mkString: reflect.runtime.universe.TermSymbol = value mkString
160
- *
161
- * scala> mkString.isMethod
162
- * res0: Boolean = false
163
- *
164
- * scala> mkString.alternatives
165
- * res1: List[reflect.runtime.universe.Symbol] = List(method mkString, method mkString, method mkString)
166
- *
167
- * scala> mkString.alternatives foreach println
168
- * method mkString
169
- * method mkString
170
- * method mkString
171
- *
172
- * scala> mkString.alternatives foreach (alt => println(alt.typeSignature))
173
- * => String
174
- * (sep: String)String
175
- * (start: String, sep: String, end: String)String
176
- * }}}
177
- *
178
- * Once one has a symbol, that symbol can be used for reflective invocations. For example,
179
- * having a TermSymbol corresponding to a field it's possible to get or set a value of that field.
180
- * Having a MethodSymbol makes it possible to invoke the corresponding methods. ClassSymbols
181
- * can be instantiated. ModuleSymbols can provide corresponding singleton instances. This is described
182
- * in detail on [[scala.reflect.api.package the reflection overview page ]].
183
- *
184
- * === Gotcha #2: Module classes ===
185
- *
186
- * Internally the Scala compiler represents objects with two symbols: a module symbol and a module class symbol.
187
- * The former is a term symbol, used everywhere a module is referenced (e.g. in singleton types or in expressions),
188
- * while the latter is a type symbol, which carries the type signature (i.e. the member list) of the module.
189
- * This implementation detail can be easily seen by compiling a trivial snippet of code. Invoking the Scala
190
- * compiler on `object C` will generate C$.class. That's exactly the module class.
191
- *
192
- * Note that module classes are different from companion classes. Say, for `case class C`, the compiler
193
- * will generate three symbols: `type C`, `term C` and (another one) `type C`, where the first type `C`
194
- * represents the class `C` (which contains auto-generated `copy`, `productPrefix`, `productArity` etc) and
195
- * the second type `C` represents the signature of object `C` (which contains auto-generated factory,
196
- * extractor etc). There won't be any name clashes, because the module class isn't added to the symbol table
197
- * directly and is only available through `<module>.moduleClass`. For the sake of completeness, it is possible
198
- * to go back from a module class to a module via `<module class>.module`.
199
- *
200
- * Separation between modules and module classes is something that we might eliminate in the future, but for now
201
- * this obscure implementation detail has to be taken into account when working with reflection. On the one hand,
202
- * it is necessary to go to a module class to get a list of members for an object. On the other hand, it is
203
- * necessary to go from a module class back to a module to get a singleton instance of an object. The latter
204
- * scenario is described at Stack Overflow: [[http://stackoverflow.com/questions/12128783 How can I get the actual object referred to by Scala 2.10 reflection? ]].
16
+ * @define SYM_ACCESSORS Class [[Symbol ]] defines `isXXX` test methods such as `isPublic` or `isFinal`, `params` and
17
+ * `returnType` methods for method symbols, `baseClasses` for class symbols and so on. Some of these methods don't
18
+ * make sense for certain subclasses of `Symbol` and return `NoSymbol`, `Nil` or other empty values.
205
19
*/
206
20
trait Symbols { self : Universe =>
207
21
@@ -278,6 +92,8 @@ trait Symbols { self: Universe =>
278
92
279
93
/** The API of symbols.
280
94
* The main source of information about symbols is the [[scala.reflect.api.Symbols ]] page.
95
+ *
96
+ * $SYM_ACCESSORS
281
97
*/
282
98
trait SymbolApi { this : Symbol =>
283
99
@@ -430,8 +246,12 @@ trait Symbols { self: Universe =>
430
246
def typeSignatureIn (site : Type ): Type
431
247
432
248
/** The type signature of this symbol.
433
- * Note if the symbol is a member of a class, one almost always is interested
434
- * in `typeSignatureIn` with a site type instead.
249
+ *
250
+ * This method always returns signatures in the most generic way possible, even if the underlying symbol is obtained from an
251
+ * instantiation of a generic type. For example, signature
252
+ * of the method `def map[B](f: (A) ⇒ B): List[B]`, which refers to the type parameter `A` of the declaring class `List[A]`,
253
+ * will always feature `A`, regardless of whether `map` is loaded from the `List[_]` or from `List[Int]`. To get a signature
254
+ * with type parameters appropriately instantiated, one should use `typeSignatureIn`.
435
255
*/
436
256
def typeSignature : Type
437
257
@@ -579,6 +399,8 @@ trait Symbols { self: Universe =>
579
399
580
400
/** The API of term symbols.
581
401
* The main source of information about symbols is the [[scala.reflect.api.Symbols ]] page.
402
+ *
403
+ * $SYM_ACCESSORS
582
404
*/
583
405
trait TermSymbolApi extends SymbolApi { this : TermSymbol =>
584
406
/** Term symbols have their names of type `TermName`.
@@ -662,6 +484,8 @@ trait Symbols { self: Universe =>
662
484
663
485
/** The API of type symbols.
664
486
* The main source of information about symbols is the [[scala.reflect.api.Symbols ]] page.
487
+ *
488
+ * $SYM_ACCESSORS
665
489
*/
666
490
trait TypeSymbolApi extends SymbolApi { this : TypeSymbol =>
667
491
/** Type symbols have their names of type `TypeName`.
@@ -729,6 +553,8 @@ trait Symbols { self: Universe =>
729
553
730
554
/** The API of method symbols.
731
555
* The main source of information about symbols is the [[scala.reflect.api.Symbols ]] page.
556
+ *
557
+ * $SYM_ACCESSORS
732
558
*/
733
559
trait MethodSymbolApi extends TermSymbolApi { this : MethodSymbol =>
734
560
final override def isMethod = true
@@ -765,6 +591,8 @@ trait Symbols { self: Universe =>
765
591
766
592
/** The API of module symbols.
767
593
* The main source of information about symbols is the [[scala.reflect.api.Symbols ]] page.
594
+ *
595
+ * $SYM_ACCESSORS
768
596
*/
769
597
trait ModuleSymbolApi extends TermSymbolApi { this : ModuleSymbol =>
770
598
/** The class implicitly associated with the object definition.
@@ -780,6 +608,8 @@ trait Symbols { self: Universe =>
780
608
781
609
/** The API of class symbols.
782
610
* The main source of information about symbols is the [[scala.reflect.api.Symbols ]] page.
611
+ *
612
+ * $SYM_ACCESSORS
783
613
*/
784
614
trait ClassSymbolApi extends TypeSymbolApi { this : ClassSymbol =>
785
615
final override def isClass = true
@@ -848,6 +678,8 @@ trait Symbols { self: Universe =>
848
678
849
679
/** The API of free term symbols.
850
680
* The main source of information about symbols is the [[scala.reflect.api.Symbols ]] page.
681
+ *
682
+ * $SYM_ACCESSORS
851
683
*/
852
684
trait FreeTermSymbolApi extends TermSymbolApi { this : FreeTermSymbol =>
853
685
final override def isFreeTerm = true
@@ -862,6 +694,8 @@ trait Symbols { self: Universe =>
862
694
863
695
/** The API of free type symbols.
864
696
* The main source of information about symbols is the [[scala.reflect.api.Symbols ]] page.
697
+ *
698
+ * $SYM_ACCESSORS
865
699
*/
866
700
trait FreeTypeSymbolApi extends TypeSymbolApi { this : FreeTypeSymbol =>
867
701
final override def isFreeType = true
0 commit comments