Add Kotlin bridge reflection handles and tests

This commit is contained in:
sergeych committed 2026-02-15 01:23:13 +03:00
1 parent cd145f6a96
commit 9cf87d1075
10 files changed
+1339

No files matched your search

@@ -0,0 +1,148 @@
package net.sergeych.lyng
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.bridge.LyngClassBridge
import net.sergeych.lyng.bridge.data
import net.sergeych.lyng.obj.ObjInt
import net.sergeych.lyng.obj.ObjString
class BridgeBindingTest {
private data class CounterState(var count: Long)
@Test
fun testExternClassBinding() = runTest {
val im = Script.defaultImportManager.copy()
im.addPackage("bridge.mod") { scope ->
scope.eval(
"""
class Foo {
extern fun add(a: Int, b: Int): Int
extern val status: String
extern var count: Int
private extern fun secret(): Int
static extern fun ping(): Int
fun callAdd() = add(2, 3)
fun callSecret() = secret()
fun bump() { count = count + 1 }
}
class Bar {
extern var count: Int
fun inc() { count = count + 1 }
}
""".trimIndent()
)
}
LyngClassBridge.bind(className = "Foo", module = "bridge.mod", importManager = im) {
classData = "OK"
init { _ ->
data = CounterState(0)
}
addFun("add") { _, _, args ->
val a = (args.list[0] as ObjInt).value
val b = (args.list[1] as ObjInt).value
ObjInt.of(a + b)
}
addVal("status") { _, _ -> ObjString(classData as String) }
addVar(
"count",
get = { _, instance ->
val st = (instance as net.sergeych.lyng.obj.ObjInstance).data as CounterState
ObjInt.of(st.count)
},
set = { _, instance, value ->
val st = (instance as net.sergeych.lyng.obj.ObjInstance).data as CounterState
st.count = (value as ObjInt).value
}
)
addFun("secret") { _, _, _ -> ObjInt.of(42) }
addFun("ping") { _, _, _ -> ObjInt.of(7) }
}
LyngClassBridge.bind(className = "Bar", module = "bridge.mod", importManager = im) {
initWithInstance { _, instance ->
(instance as net.sergeych.lyng.obj.ObjInstance).data = CounterState(10)
}
addVar(
"count",
get = { _, instance ->
val st = (instance as net.sergeych.lyng.obj.ObjInstance).data as CounterState
ObjInt.of(st.count)
},
set = { _, instance, value ->
val st = (instance as net.sergeych.lyng.obj.ObjInstance).data as CounterState
st.count = (value as ObjInt).value
}
)
}
val scope = im.newStdScope()
scope.eval(
"""
import bridge.mod
val f = Foo()
assertEquals(5, f.callAdd())
assertEquals("OK", f.status)
assertEquals(0, f.count)
f.bump()
assertEquals(1, f.count)
assertEquals(42, f.callSecret())
assertEquals(7, Foo.ping())
val b = Bar()
assertEquals(10, b.count)
b.inc()
assertEquals(11, b.count)
""".trimIndent()
)
val privateCallFails = try {
scope.eval(
"""
import bridge.mod
Foo().secret()
""".trimIndent()
)
false
} catch (_: ScriptError) {
true
}
assertTrue(privateCallFails)
}
@Test
fun testBindAfterInstanceFails() = runTest {
val im = Script.defaultImportManager.copy()
im.addPackage("bridge.late") { scope ->
scope.eval(
"""
class Late {
extern val status: String
}
""".trimIndent()
)
}
val scope = im.newStdScope()
scope.eval(
"""
import bridge.late
val l = Late()
""".trimIndent()
)
val bindFailed = try {
LyngClassBridge.bind(className = "Late", module = "bridge.late", importManager = im) {
addVal("status") { _, _ -> ObjString("late") }
}
false
} catch (_: ScriptError) {
true
}
assertTrue(bindFailed)
}
}
@@ -0,0 +1,54 @@
package net.sergeych.lyng
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.bridge.BridgeCallByName
import net.sergeych.lyng.bridge.resolver
import net.sergeych.lyng.obj.ObjInstance
import net.sergeych.lyng.obj.ObjInt
class BridgeResolverTest {
@Test
fun testLocalAndMemberHandles() = runTest {
val im = Script.defaultImportManager.copy()
val scope = im.newStdScope()
scope.eval(
"""
var x = 1
fun add(a: Int, b: Int) = a + b
class Foo {
var count: Int = 0
fun bump() { count = count + 1 }
}
val f = Foo()
""".trimIndent()
)
val facade = scope.asFacade()
val resolver = facade.resolver()
val xHandle = resolver.resolveVar("x")
assertEquals(1, (xHandle.get(facade) as ObjInt).value)
xHandle.set(facade, ObjInt.of(5))
assertEquals(5, (xHandle.get(facade) as ObjInt).value)
val addHandle = resolver.resolveCallable("add")
val addResult = addHandle.call(facade, Arguments(ObjInt.of(2), ObjInt.of(3)))
assertEquals(5, (addResult as ObjInt).value)
val fRec = scope["f"] ?: error("f not found")
val fObj = scope.resolve(fRec, "f") as ObjInstance
val countHandle = resolver.resolveMemberVar(fObj, "count")
assertEquals(0, (countHandle.get(facade) as ObjInt).value)
val bumpHandle = resolver.resolveMemberCallable(fObj, "bump")
bumpHandle.call(facade)
assertEquals(1, (countHandle.get(facade) as ObjInt).value)
val callByName = resolver as BridgeCallByName
val addByName = callByName.callByName(facade, "add", Arguments(ObjInt.of(4), ObjInt.of(6)))
assertEquals(10, (addByName as ObjInt).value)
}
}