Skip to content

Commit a6d9344

Browse files
authored
perf(function): cache ValueFactory dispatch table (#442)
<!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/iamgio/quarkdown/pull/442" target="_blank"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin"> </picture> </a> <!-- devin-review-badge-end -->
2 parents 041b86e + bec3058 commit a6d9344

File tree

1 file changed

+27
-7
lines changed

1 file changed

+27
-7
lines changed

quarkdown-core/src/main/kotlin/com/quarkdown/core/function/reflect/DynamicValueConverter.kt

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,26 @@ import kotlin.reflect.full.findAnnotations
1515
import kotlin.reflect.full.functions
1616
import kotlin.reflect.full.isSubclassOf
1717

18+
/**
19+
* A cached entry in the [FromDynamicType] dispatch table,
20+
* pairing a factory method with its annotation metadata.
21+
*/
22+
private data class FactoryEntry(
23+
val function: KFunction<*>,
24+
val annotation: FromDynamicType,
25+
)
26+
27+
/**
28+
* Cached dispatch table of [ValueFactory] methods annotated with [FromDynamicType].
29+
*/
30+
private val factoryEntries: List<FactoryEntry> by lazy {
31+
ValueFactory::class.declaredFunctions.flatMap { function ->
32+
function.findAnnotations<FromDynamicType>().map { annotation ->
33+
FactoryEntry(function, annotation)
34+
}
35+
}
36+
}
37+
1838
/**
1939
* A converter of a value that potentially holds any type (its value is stored as a plain string)
2040
* to a specific, statically defined [Value] type that can be used as an input for a function call argument.
@@ -54,25 +74,25 @@ class DynamicValueConverter(
5474
?: throw NoSuchElementException(element = raw, values)
5575
}
5676

57-
// Gets ValueFactory methods annotated with @FromDynamicType(X::class),
58-
// and the one with a matching type is invoked.
59-
for (function in ValueFactory::class.declaredFunctions) {
60-
val annotations = function.findAnnotations<FromDynamicType>()
61-
val from = annotations.find { type.isSubclassOf(it.unwrappedType) } ?: continue
77+
// Looks up the pre-built dispatch table for a matching factory method.
78+
for ((function, annotation) in factoryEntries) {
79+
if (!type.isSubclassOf(annotation.unwrappedType)) continue
6280

6381
// The factory method is suitable. Invoking it.
6482

6583
return try {
6684
when {
6785
// Fetch the context from the function call if it's required.
68-
from.requiresContext -> {
86+
annotation.requiresContext -> {
6987
if (context == null) {
7088
throw IllegalStateException("Function call does not have an attached context")
7189
}
7290
function.call(ValueFactory, raw, context)
7391
}
7492

75-
else -> function.call(ValueFactory, raw)
93+
else -> {
94+
function.call(ValueFactory, raw)
95+
}
7696
} as InputValue<*>?
7797
} catch (e: InvocationTargetException) {
7898
throw e.cause ?: e

0 commit comments

Comments
 (0)