forked from mockk/mockk
/
ObjenesisInstantiator.kt
94 lines (79 loc) · 2.95 KB
/
ObjenesisInstantiator.kt
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
package io.mockk.proxy.jvm
import io.mockk.proxy.MockKAgentLogger
import io.mockk.proxy.MockKInstantiatior
import io.mockk.proxy.jvm.transformation.CacheKey
import net.bytebuddy.ByteBuddy
import net.bytebuddy.TypeCache
import org.objenesis.ObjenesisStd
import org.objenesis.instantiator.ObjectInstantiator
import java.lang.reflect.Modifier
import java.util.*
class ObjenesisInstantiator(
private val log: MockKAgentLogger,
private val byteBuddy: ByteBuddy
) : MockKInstantiatior {
private val objenesis = ObjenesisStd(false)
private val typeCache = TypeCache<CacheKey>(TypeCache.Sort.WEAK)
private val instantiators = Collections.synchronizedMap(WeakHashMap<Class<*>, ObjectInstantiator<*>>())
override fun <T : Any> instance(cls: Class<T>): T {
val kcls = cls.kotlin
if (kcls == Any::class) {
@Suppress("UNCHECKED_CAST")
return Any() as T
} else if (kcls.isSealed) {
kcls.sealedSubclasses.firstNotNullOfOrNull { subCls ->
runCatching { instance(subCls.java) }.getOrNull()
} ?: error("could not find subclass for sealed class $cls")
} else if (kcls.isFinal) {
try {
val instance = instantiateViaProxy(cls)
if (instance != null) {
return instance
}
} catch (ex: Exception) {
log.trace(
ex, "Failed to instantiate via proxy " + cls + ". " +
"Doing objenesis instantiation"
)
}
}
return instanceViaObjenesis(cls)
}
private fun <T> instantiateViaProxy(cls: Class<T>): T? {
val proxyCls = if (!Modifier.isAbstract(cls.modifiers)) {
log.trace("Skipping instantiation subsclassing $cls because class is not abstract.")
cls
} else {
log.trace("Instantiating $cls via subclass proxy")
val classLoader = cls.classLoader
typeCache.findOrInsert(
classLoader,
CacheKey(cls, setOf()),
{
byteBuddy.subclass(cls)
.annotateType(*cls.annotations)
.make()
.load(classLoader)
.loaded
}, classLoader ?: bootstrapMonitor
)
}
return cls.cast(instanceViaObjenesis(proxyCls))
}
private fun <T> instanceViaObjenesis(clazz: Class<T>): T {
log.trace("Creating new empty instance of $clazz")
return clazz.cast(
getOrCreateInstantiator(clazz)
.newInstance()
)
}
private fun <T> getOrCreateInstantiator(clazz: Class<T>) =
instantiators[clazz] ?: let {
objenesis.getInstantiatorOf(clazz).also {
instantiators[clazz] = it
}
}
companion object {
private val bootstrapMonitor = Any()
}
}