Skip to content
This repository was archived by the owner on Jan 20, 2023. It is now read-only.

Commit 220a73c

Browse files
authored
Merge pull request #9 from k163377/single_arg_fastkfunction
Add SingleArgFastKFunction.
2 parents 7a1ae61 + 55bcaf9 commit 220a73c

File tree

4 files changed

+231
-5
lines changed

4 files changed

+231
-5
lines changed

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ data class Sample(
9393
val arg3: String? = null
9494
)
9595

96-
private val fastKFunction: FastKFunction<Sample> = FastKFunction.of(::Sample)
96+
val fastKFunction: FastKFunction<Sample> = FastKFunction.of(::Sample)
9797

9898
fun map(src: Map<String, Any?>): Sample {
9999
return fastKFunction.generateBucket()
@@ -105,6 +105,17 @@ fun map(src: Map<String, Any?>): Sample {
105105
}
106106
```
107107

108+
### For functions that can be called from a single argument.
109+
For a function that can be called with a single argument, you can use the `SingleArgFastKFunction`.
110+
111+
```kotlin
112+
data class Sample(val arg: Int)
113+
114+
val fastKFunction: SingleArgFastKFunction<Sample> = SingleArgFastKFunction.of(::Sample)
115+
116+
val result: Sample = fastKFunction.call(1)
117+
```
118+
108119
## Benchmark
109120
You can run the benchmark with the `./gradlew jmh`.
110121
Please note that it will take about 2 hours in total if executed with the default settings.

src/main/kotlin/com/mapk/fastkfunction/FastKFunction.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,13 @@ sealed class FastKFunction<T> {
129129
}
130130

131131
companion object {
132-
private fun List<KParameter>.checkParameters(instance: Any?) = also {
133-
if (isEmpty() || (instance != null && size == 1))
132+
private fun List<KParameter>.checkParameters() = also {
133+
val requireInstanceParameter = this[0].kind != KParameter.Kind.VALUE
134+
135+
if (isEmpty() || (requireInstanceParameter && size == 1))
134136
throw IllegalArgumentException("This function is not require arguments.")
135137

136-
if (3 <= size && get(0).kind != KParameter.Kind.VALUE && get(1).kind != KParameter.Kind.VALUE)
138+
if (3 <= size && requireInstanceParameter && get(1).kind != KParameter.Kind.VALUE)
137139
throw IllegalArgumentException("This function is require multiple instances.")
138140
}
139141

@@ -191,7 +193,7 @@ sealed class FastKFunction<T> {
191193

192194
fun <T> of(function: KFunction<T>, instance: Any? = null): FastKFunction<T> {
193195
// 引数を要求しないか、複数のインスタンスを求める場合エラーとする
194-
val parameters: List<KParameter> = function.parameters.checkParameters(instance)
196+
val parameters: List<KParameter> = function.parameters.checkParameters()
195197

196198
// この関数には確実にアクセスするためアクセシビリティ書き換え
197199
function.isAccessible = true
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package com.mapk.fastkfunction
2+
3+
import java.lang.reflect.Method
4+
import java.lang.reflect.Modifier
5+
import kotlin.reflect.KFunction
6+
import kotlin.reflect.KParameter
7+
import kotlin.reflect.jvm.isAccessible
8+
import kotlin.reflect.jvm.javaConstructor
9+
import kotlin.reflect.jvm.javaMethod
10+
import java.lang.reflect.Constructor as JavaConstructor
11+
12+
sealed class SingleArgFastKFunction<T> {
13+
abstract val valueParameter: KParameter
14+
abstract fun call(arg: Any?): T
15+
16+
internal class Constructor<T>(
17+
override val valueParameter: KParameter,
18+
private val constructor: JavaConstructor<T>
19+
) : SingleArgFastKFunction<T>() {
20+
override fun call(arg: Any?): T = constructor.newInstance(arg)
21+
}
22+
23+
internal class Function<T>(
24+
override val valueParameter: KParameter,
25+
private val function: KFunction<T>
26+
) : SingleArgFastKFunction<T>() {
27+
override fun call(arg: Any?): T = function.call(arg)
28+
}
29+
30+
internal class TopLevelFunction<T>(
31+
override val valueParameter: KParameter,
32+
private val method: Method
33+
) : SingleArgFastKFunction<T>() {
34+
@Suppress("UNCHECKED_CAST")
35+
override fun call(arg: Any?): T = method.invoke(null, arg) as T
36+
}
37+
38+
internal class TopLevelExtensionFunction<T>(
39+
override val valueParameter: KParameter,
40+
private val method: Method,
41+
private val extensionReceiver: Any
42+
) : SingleArgFastKFunction<T>() {
43+
@Suppress("UNCHECKED_CAST")
44+
override fun call(arg: Any?): T = method.invoke(null, extensionReceiver, arg) as T
45+
}
46+
47+
internal class InstanceFunction<T>(
48+
override val valueParameter: KParameter,
49+
private val method: Method,
50+
private val instance: Any
51+
) : SingleArgFastKFunction<T>() {
52+
@Suppress("UNCHECKED_CAST")
53+
override fun call(arg: Any?): T = method.invoke(instance, arg) as T
54+
}
55+
56+
companion object {
57+
private fun List<KParameter>.checkParameters() = also {
58+
val requireInstanceParameter = this[0].kind != KParameter.Kind.VALUE
59+
60+
if (isEmpty() || (requireInstanceParameter && size == 1))
61+
throw IllegalArgumentException("This function is not require arguments.")
62+
63+
if (!(this.size == 1 || (this.size == 2 && requireInstanceParameter)))
64+
throw IllegalArgumentException("This function is require multiple arguments.")
65+
}
66+
67+
private fun <T> topLevelFunctionOf(
68+
function: KFunction<T>,
69+
instance: Any?,
70+
parameters: List<KParameter>,
71+
method: Method
72+
): SingleArgFastKFunction<T> = when {
73+
// KParameter.Kind.EXTENSION_RECEIVERの要求が有れば確定で拡張関数
74+
parameters[0].kind == KParameter.Kind.EXTENSION_RECEIVER -> {
75+
// 対象が拡張関数ならinstanceはreceiver、指定が無ければエラー
76+
instance ?: throw IllegalArgumentException(
77+
"Function requires EXTENSION_RECEIVER instance, but is not present."
78+
)
79+
80+
TopLevelExtensionFunction(parameters[1], method, instance)
81+
}
82+
// javaMethodのパラメータサイズとKFunctionのパラメータサイズが違う場合も拡張関数
83+
// インスタンスが設定されていれば高速呼び出し、そうじゃなければ通常の関数呼び出し
84+
method.parameters.size != parameters.size ->
85+
instance
86+
?.let { TopLevelExtensionFunction(parameters[0], method, instance) }
87+
?: Function(parameters[0], function)
88+
// トップレベル関数
89+
else -> TopLevelFunction(parameters[0], method)
90+
}
91+
92+
private fun <T> instanceFunctionOf(
93+
function: KFunction<T>,
94+
inputtedInstance: Any?,
95+
parameters: List<KParameter>,
96+
method: Method
97+
): SingleArgFastKFunction<T> {
98+
val instance = inputtedInstance ?: method.declaringObject
99+
100+
return when {
101+
parameters[0].kind == KParameter.Kind.INSTANCE ->
102+
instance
103+
?.let { InstanceFunction(parameters[1], method, it) }
104+
?: throw IllegalArgumentException("Function requires INSTANCE parameter, but is not present.")
105+
instance != null -> InstanceFunction(parameters[0], method, instance)
106+
else -> Function(parameters[0], function)
107+
}
108+
}
109+
110+
fun <T> of(function: KFunction<T>, instance: Any? = null): SingleArgFastKFunction<T> {
111+
// 引数を要求しないか、複数のインスタンスを求める場合エラーとする
112+
val parameters: List<KParameter> = function.parameters.checkParameters()
113+
114+
// この関数には確実にアクセスするためアクセシビリティ書き換え
115+
function.isAccessible = true
116+
117+
val constructor = function.javaConstructor
118+
119+
return if (constructor != null) {
120+
Constructor(parameters[0], constructor)
121+
} else {
122+
val method = function.javaMethod!!
123+
124+
// Methodがstatic関数ならfunctionはトップレベル関数
125+
if (Modifier.isStatic(method.modifiers)) {
126+
topLevelFunctionOf(function, instance, parameters, method)
127+
} else {
128+
instanceFunctionOf(function, instance, parameters, method)
129+
}
130+
}
131+
}
132+
}
133+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.mapk.fastkfunction.singleargfastkfunction
2+
3+
import com.mapk.fastkfunction.SingleArgFastKFunction
4+
import org.junit.jupiter.api.Assertions
5+
import org.junit.jupiter.api.TestInstance
6+
import org.junit.jupiter.api.assertDoesNotThrow
7+
import org.junit.jupiter.params.ParameterizedTest
8+
import org.junit.jupiter.params.provider.Arguments
9+
import org.junit.jupiter.params.provider.MethodSource
10+
import java.util.stream.Stream
11+
import kotlin.reflect.KFunction
12+
import kotlin.reflect.full.companionObject
13+
import kotlin.reflect.full.companionObjectInstance
14+
import kotlin.reflect.full.functions
15+
16+
private fun topLevelFunc(arg: Int): SingleArgSingleArgFastKFunctionTest.Dst = SingleArgSingleArgFastKFunctionTest.Dst(arg)
17+
private fun SingleArgSingleArgFastKFunctionTest.Class.topLevelExtensionFunc(arg: Int): SingleArgSingleArgFastKFunctionTest.Dst =
18+
SingleArgSingleArgFastKFunctionTest.Dst(this.arg + arg)
19+
20+
/**
21+
* 網羅しているパターン
22+
* - コンストラクタ
23+
* - インスタンスメソッド
24+
* - インスタンスメソッド + インスタンス
25+
* - コンパニオンオブジェクトに定義したメソッド
26+
* - コンパニオンオブジェクトに定義したメソッド + コンパニオンオブジェクトインスタンス
27+
* - リフレクションで取得したコンパニオンオブジェクトに定義したメソッド
28+
* - リフレクションで取得したコンパニオンオブジェクトに定義したメソッド + コンパニオンオブジェクトインスタンス
29+
* - トップレベル関数
30+
* - クラスから取得したトップレベル拡張関数 + レシーバインスタンス
31+
* - インスタンスから取得したトップレベル拡張関数
32+
* - インスタンスから取得したトップレベル拡張関数 + インスタンス
33+
*/
34+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
35+
private class SingleArgSingleArgFastKFunctionTest {
36+
class Class(val arg: Int)
37+
38+
data class Dst(val arg: Int) {
39+
companion object {
40+
fun of(arg: Int) = Dst(arg)
41+
}
42+
}
43+
44+
private fun instanceFunction(arg: Int) = Dst(arg)
45+
46+
@ParameterizedTest
47+
@MethodSource("argumentsProvider")
48+
fun call(target: KFunction<Dst>, instance: Any?, message: String) {
49+
val sut = SingleArgFastKFunction.of(target, instance)
50+
assertDoesNotThrow("Fail $message") {
51+
Assertions.assertEquals(Dst(100), sut.call(100), message)
52+
}
53+
}
54+
55+
fun argumentsProvider(): Stream<Arguments> {
56+
val companionRawFunc = Dst::class.companionObject!!.functions.first { it.name == "of" }
57+
58+
return listOf(
59+
Arguments.of(::Dst, null, "constructor"),
60+
Arguments.of(::instanceFunction, null, "instance func"),
61+
Arguments.of(::instanceFunction, this, "instance func with instance"),
62+
Arguments.of((Dst)::of, null, "companion object func"),
63+
Arguments.of((Dst)::of, Dst::class.companionObjectInstance, "companion object func with instance"),
64+
Arguments.of(companionRawFunc, null, "companion object func from reflection"),
65+
Arguments.of(
66+
companionRawFunc,
67+
Dst::class.companionObjectInstance,
68+
"companion object func from reflection with instance"
69+
),
70+
Arguments.of(::topLevelFunc, null, "top level func"),
71+
Arguments.of(Class::topLevelExtensionFunc, Class(0), "top level extension func from class"),
72+
Class(0).let {
73+
Arguments.of(it::topLevelExtensionFunc, null, "top level extension func from instance")
74+
},
75+
Class(0).let {
76+
Arguments.of(it::topLevelExtensionFunc, it, "top level extension func from instance with instance")
77+
}
78+
).stream()
79+
}
80+
}

0 commit comments

Comments
 (0)