-
Notifications
You must be signed in to change notification settings - Fork 106
/
VariableState.kt
94 lines (81 loc) · 2.39 KB
/
VariableState.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 org.jetbrains.kotlinx.jupyter.api
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty1
import kotlin.reflect.jvm.isAccessible
interface VariableState {
val property: KProperty<*>
val scriptInstance: Any?
val stringValue: String?
val value: Result<Any?>
}
data class VariableStateImpl(
override val property: KProperty1<Any, *>,
override val scriptInstance: Any,
) : VariableState {
private val stringCache = VariableStateCache<String?> {
value.getOrNull()?.let { value ->
try {
value.toString()
} catch (e: Throwable) {
"${value::class.simpleName}: [exception thrown: $e]"
}
}
}
private val valCache = VariableStateCache<Result<Any?>>(
{ oldValue, newValue ->
oldValue.getOrNull() !== newValue.getOrNull()
},
{
property.asAccessible { prop ->
try {
Result.success(prop.get(scriptInstance))
} catch (ex: Throwable) {
Result.failure(ex)
}
}
},
)
fun update(): Boolean {
return (valCache.forceUpdate()).also { isChanged ->
if (isChanged) stringCache.update()
}
}
override val stringValue: String? get() = stringCache.get()
override val value: Result<Any?> get() = valCache.get()
companion object {
private fun <T : KProperty<*>, R> T.asAccessible(action: (T) -> R): R {
val wasAccessible = isAccessible
isAccessible = true
val res = action(this)
isAccessible = wasAccessible
return res
}
}
}
private class VariableStateCache<T>(
val equalityChecker: (T, T) -> Boolean = { x, y -> x == y },
val calculate: (T?) -> T,
) {
private var cachedVal: T? = null
private var shouldRenew: Boolean = true
fun getOrNull(): T? {
return if (shouldRenew) {
calculate(cachedVal).also {
cachedVal = it
shouldRenew = false
}
} else {
cachedVal
}
}
fun get(): T = getOrNull()!!
fun update() {
shouldRenew = true
}
fun forceUpdate(): Boolean {
val oldVal = getOrNull()
update()
val newVal = get()
return oldVal != null && equalityChecker(oldVal, newVal)
}
}