forked from mockito/mockito-scala
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathReflectionUtils.scala
More file actions
92 lines (79 loc) · 3.18 KB
/
Copy pathReflectionUtils.scala
File metadata and controls
92 lines (79 loc) · 3.18 KB
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
package org.mockito
import org.mockito.JavaReflectionUtils.resolveWithJavaGenerics
import org.mockito.invocation.InvocationOnMock
import org.scalactic.TripleEquals.*
import java.lang.reflect.Method
import scala.reflect.ClassTag
import scala.util.Try as uTry
object ReflectionUtils {
import scala.reflect.runtime.universe as ru
import ru.*
private val mirror = runtimeMirror(getClass.getClassLoader)
private val methodToJavaMethod: Method =
mirror.getClass.getMethods.find(m => m.getName == "methodToJava" && m.getParameterCount == 1 && m.getParameterTypes.head.getName.endsWith("Symbol")).get
private def asJavaMethod(sym: Symbol): Method = methodToJavaMethod.invoke(mirror, sym).asInstanceOf[Method]
private[mockito] def returnType(invocation: InvocationOnMock): Class[?] = {
val javaReturnType = invocation.method.getReturnType
if (javaReturnType == classOf[Object])
resolveWithScalaGenerics(invocation)
.orElse(resolveWithJavaGenerics(invocation))
.getOrElse(javaReturnType)
else javaReturnType
}
private[mockito] def returnsValueClass(invocation: InvocationOnMock): Boolean =
findTypeSymbol(invocation).exists { s =>
val typeSymbol = s.asMethod.returnType.typeSymbol
typeSymbol.isClass && typeSymbol.asClass.toType <:< typeOf[AnyVal]
}
private def resolveWithScalaGenerics(invocation: InvocationOnMock): Option[Class[?]] =
uTry {
findTypeSymbol(invocation)
.filter(_.asMethod.returnType.typeSymbol.isClass)
.map(_.asMethod.returnType.typeSymbol.asClass)
.map(mirror.runtimeClass)
}.toOption.flatten
private def findTypeSymbol(invocation: InvocationOnMock) =
uTry {
mirror
.classSymbol(invocation.method.getDeclaringClass)
.info
.decls
.collectFirst {
case symbol if isNonConstructorMethod(symbol) && asJavaMethod(symbol) === invocation.method => symbol
}
}.toOption.flatten
private def isNonConstructorMethod(d: ru.Symbol): Boolean = d.isMethod && !d.isConstructor
def extraInterfaces[T](implicit $wtt: WeakTypeTag[T], $ct: ClassTag[T]): List[Class[?]] =
uTry {
val cls = clazz($ct)
$wtt.tpe match {
case RefinedType(types, _) =>
types.map($wtt.mirror.runtimeClass).collect {
case c: Class[?] if c.isInterface && c != cls => c
}
case _ => List.empty
}
}.toOption
.getOrElse(List.empty)
def methodsWithLazyOrVarArgs(classes: Seq[Class[?]]): Seq[(Method, Set[Int])] =
classes.flatMap { clazz =>
uTry {
mirror
.classSymbol(clazz)
.info
.members
.collect {
case symbol if isNonConstructorMethod(symbol) =>
symbol -> symbol.typeSignature.paramLists.flatten.zipWithIndex.collect {
case (p, idx) if p.typeSignature.toString.startsWith("=>") => idx
case (p, idx) if p.typeSignature.toString.endsWith("*") => idx
}.toSet
}
.collect {
case (symbol, indices) if indices.nonEmpty => asJavaMethod(symbol) -> indices
}
.toSeq
}.toOption
.getOrElse(Seq.empty)
}
}