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
|
/* __ *\
** ________ ___ / / ___ Scala API **
** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
** /____/\___/_/ |_/____/_/ | | **
** |/ **
\* */
package scala
package runtime
import java.lang.reflect.{ Method => JMethod }
import java.lang.{ Class => JClass }
import scala.annotation.tailrec
/** An element of a polymorphic object cache.
* This class is referred to by the `CleanUp` phase. Each `PolyMethodCache` chain
* must only relate to one method as `PolyMethodCache` does not identify
* the method name and argument types. In practice, one variable will be
* generated per call point, and will uniquely relate to the method called
* at that point, making the method name and argument types irrelevant. */
/* TODO: if performance is acceptable, PolyMethodCache should be made generic on the method type */
private[scala] sealed abstract class MethodCache {
/** Searches for a cached method in the `MethodCache` chain that
* is compatible with receiver class `forReceiver`. If none is cached,
* `null` is returned. If `null` is returned, find's caller should look-
* up the right method using whichever means it prefers, and add it to
* the cache for later use. */
def find(forReceiver: JClass[_]): JMethod
def add(forReceiver: JClass[_], forMethod: JMethod): MethodCache
}
private[scala] final class EmptyMethodCache extends MethodCache {
def find(forReceiver: JClass[_]): JMethod = null
def add(forReceiver: JClass[_], forMethod: JMethod): MethodCache =
new PolyMethodCache(this, forReceiver, forMethod, 1)
}
private[scala] final class MegaMethodCache(
private[this] val forName: String,
private[this] val forParameterTypes: Array[JClass[_]]
) extends MethodCache {
def find(forReceiver: JClass[_]): JMethod =
forReceiver.getMethod(forName, forParameterTypes:_*)
def add(forReceiver: JClass[_], forMethod: JMethod): MethodCache = this
}
private[scala] final class PolyMethodCache(
private[this] val next: MethodCache,
private[this] val receiver: JClass[_],
private[this] val method: JMethod,
private[this] val complexity: Int
) extends MethodCache {
/** To achieve tail recursion this must be a separate method
* from `find`, because the type of next is not `PolyMethodCache`.
*/
@tailrec private def findInternal(forReceiver: JClass[_]): JMethod =
if (forReceiver eq receiver) method
else next match {
case x: PolyMethodCache => x findInternal forReceiver
case _ => next find forReceiver
}
def find(forReceiver: JClass[_]): JMethod = findInternal(forReceiver)
// TODO: come up with a more realistic number
final private val MaxComplexity = 160
def add(forReceiver: JClass[_], forMethod: JMethod): MethodCache =
if (complexity < MaxComplexity)
new PolyMethodCache(this, forReceiver, forMethod, complexity + 1)
else
new MegaMethodCache(forMethod.getName, forMethod.getParameterTypes)
}
|