diff --git a/docs/embedding.md b/docs/embedding.md index 3c16788..3ff3685 100644 --- a/docs/embedding.md +++ b/docs/embedding.md @@ -261,14 +261,14 @@ moduleScope.eval("class Counter { extern var value: Int; extern fun inc(by: Int) moduleScope.bind("Counter") { addVar( name = "value", - get = { _, self -> self.readField(this, "value").value }, - set = { _, self, v -> self.writeField(this, "value", v) } + get = { thisObj.readField(this, "value").value }, + set = { v -> thisObj.writeField(this, "value", v) } ) - addFun("inc") { _, self, args -> + addFun("inc") { val by = args.requiredArg(0).value - val current = self.readField(this, "value").value as ObjInt + val current = thisObj.readField(this, "value").value as ObjInt val next = ObjInt(current.value + by) - self.writeField(this, "value", next) + thisObj.writeField(this, "value", next) next } } @@ -303,16 +303,16 @@ moduleScope.eval(""" moduleScope.bindObject("HostObject") { classData = "OK" init { _ -> data = 0L } - addFun("add") { _, _, args -> + addFun("add") { val a = args.requiredArg(0).value val b = args.requiredArg(1).value ObjInt.of(a + b) } - addVal("status") { _, _ -> ObjString(classData as String) } + addVal("status") { ObjString(classData as String) } addVar( "count", - get = { _, inst -> ObjInt.of((inst as ObjInstance).data as Long) }, - set = { _, inst, value -> (inst as ObjInstance).data = (value as ObjInt).value } + get = { ObjInt.of((thisObj as ObjInstance).data as Long) }, + set = { value -> (thisObj as ObjInstance).data = (value as ObjInt).value } ) } ``` @@ -336,7 +336,7 @@ moduleScope.eval(""" """.trimIndent()) moduleScope.bindObject("HostObject") { - addFun("ping") { _, _, _ -> ObjInt.of(7) } + addFun("ping") { ObjInt.of(7) } } ``` diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bridge/ClassBridge.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bridge/ClassBridge.kt index 15ae8ef..e81be16 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bridge/ClassBridge.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bridge/ClassBridge.kt @@ -60,12 +60,54 @@ interface ClassBridgeBinder { /** Register an initialization hook that runs for each instance. */ fun init(block: suspend BridgeInstanceContext.(ScopeFacade) -> Unit) /** Register an initialization hook with direct access to the instance. */ + fun initWithInstance(block: suspend ScopeFacade.() -> Unit) + /** + * Legacy initWithInstance form. + * Prefer [initWithInstance] with [ScopeFacade] receiver and use [ScopeFacade.thisObj]. + */ + @Deprecated( + message = "Use initWithInstance { ... } with ScopeFacade receiver; use thisObj from scope", + replaceWith = ReplaceWith("initWithInstance { block(this, thisObj) }") + ) fun initWithInstance(block: suspend (ScopeFacade, Obj) -> Unit) /** Bind a Lyng function/member to a Kotlin implementation (requires `extern` in Lyng). */ + fun addFun(name: String, impl: suspend ScopeFacade.() -> Obj) + /** + * Legacy addFun form. + * Prefer [addFun] with [ScopeFacade] receiver and use [ScopeFacade.thisObj]/[ScopeFacade.args]. + */ + @Deprecated( + message = "Use addFun(name) { ... } with ScopeFacade receiver; use thisObj/args from scope", + replaceWith = ReplaceWith("addFun(name) { impl(this, thisObj, args) }") + ) fun addFun(name: String, impl: suspend (ScopeFacade, Obj, Arguments) -> Obj) /** Bind a read-only member (val/property getter) declared as `extern`. */ + fun addVal(name: String, impl: suspend ScopeFacade.() -> Obj) + /** + * Legacy addVal form. + * Prefer [addVal] with [ScopeFacade] receiver and use [ScopeFacade.thisObj]. + */ + @Deprecated( + message = "Use addVal(name) { ... } with ScopeFacade receiver; use thisObj from scope", + replaceWith = ReplaceWith("addVal(name) { impl(this, thisObj) }") + ) fun addVal(name: String, impl: suspend (ScopeFacade, Obj) -> Obj) /** Bind a mutable member (var/property getter/setter) declared as `extern`. */ + fun addVar( + name: String, + get: suspend ScopeFacade.() -> Obj, + set: suspend ScopeFacade.(Obj) -> Unit + ) + /** + * Legacy addVar form. + * Prefer [addVar] with [ScopeFacade] receiver and use [ScopeFacade.thisObj]. + */ + @Deprecated( + message = "Use addVar(name, get, set) with ScopeFacade receiver; use thisObj from scope", + replaceWith = ReplaceWith( + "addVar(name, get = { get(this, thisObj) }, set = { value -> set(this, thisObj, value) })" + ) + ) fun addVar( name: String, get: suspend (ScopeFacade, Obj) -> Obj, @@ -234,17 +276,27 @@ private class ClassBridgeBinderImpl( } } + override fun initWithInstance(block: suspend ScopeFacade.() -> Unit) { + initHooks.add { scope, _ -> + scope.block() + } + } + + @Deprecated( + message = "Use initWithInstance { ... } with ScopeFacade receiver; use thisObj from scope", + replaceWith = ReplaceWith("initWithInstance { block(this, thisObj) }") + ) override fun initWithInstance(block: suspend (ScopeFacade, Obj) -> Unit) { initHooks.add { scope, inst -> block(scope, inst) } } - override fun addFun(name: String, impl: suspend (ScopeFacade, Obj, Arguments) -> Obj) { + override fun addFun(name: String, impl: suspend ScopeFacade.() -> Obj) { ensureTemplateNotBuilt() val target = findMember(name) val callable = ObjExternCallable.fromBridge { - impl(this, thisObj, args) + impl() } val methodId = cls.ensureMethodIdForBridge(name, target.record) val newRecord = target.record.copy( @@ -255,14 +307,24 @@ private class ClassBridgeBinderImpl( replaceMember(target, newRecord) } - override fun addVal(name: String, impl: suspend (ScopeFacade, Obj) -> Obj) { + @Deprecated( + message = "Use addFun(name) { ... } with ScopeFacade receiver; use thisObj/args from scope", + replaceWith = ReplaceWith("addFun(name) { impl(this, thisObj, args) }") + ) + override fun addFun(name: String, impl: suspend (ScopeFacade, Obj, Arguments) -> Obj) { + addFun(name) { + impl(this, thisObj, args) + } + } + + override fun addVal(name: String, impl: suspend ScopeFacade.() -> Obj) { ensureTemplateNotBuilt() val target = findMember(name) if (target.record.isMutable) { throw ScriptError(Pos.builtIn, "extern val $name is mutable in class ${cls.className}") } val getter = ObjExternCallable.fromBridge { - impl(this, thisObj) + impl() } val prop = ObjProperty(name, getter, null) val isFieldLike = target.record.type == ObjRecord.Type.Field || @@ -287,10 +349,20 @@ private class ClassBridgeBinderImpl( replaceMember(target, newRecord) } + @Deprecated( + message = "Use addVal(name) { ... } with ScopeFacade receiver; use thisObj from scope", + replaceWith = ReplaceWith("addVal(name) { impl(this, thisObj) }") + ) + override fun addVal(name: String, impl: suspend (ScopeFacade, Obj) -> Obj) { + addVal(name) { + impl(this, thisObj) + } + } + override fun addVar( name: String, - get: suspend (ScopeFacade, Obj) -> Obj, - set: suspend (ScopeFacade, Obj, Obj) -> Unit + get: suspend ScopeFacade.() -> Obj, + set: suspend ScopeFacade.(Obj) -> Unit ) { ensureTemplateNotBuilt() val target = findMember(name) @@ -298,11 +370,11 @@ private class ClassBridgeBinderImpl( throw ScriptError(Pos.builtIn, "extern var $name is readonly in class ${cls.className}") } val getter = ObjExternCallable.fromBridge { - get(this, thisObj) + get() } val setter = ObjExternCallable.fromBridge { val value = requiredArg(0) - set(this, thisObj, value) + set(value) ObjVoid } val prop = ObjProperty(name, getter, setter) @@ -328,6 +400,24 @@ private class ClassBridgeBinderImpl( replaceMember(target, newRecord) } + @Deprecated( + message = "Use addVar(name, get, set) with ScopeFacade receiver; use thisObj from scope", + replaceWith = ReplaceWith( + "addVar(name, get = { get(this, thisObj) }, set = { value -> set(this, thisObj, value) })" + ) + ) + override fun addVar( + name: String, + get: suspend (ScopeFacade, Obj) -> Obj, + set: suspend (ScopeFacade, Obj, Obj) -> Unit + ) { + addVar( + name, + get = { get(this, thisObj) }, + set = { value -> set(this, thisObj, value) } + ) + } + fun commit() { if (initHooks.isNotEmpty()) { val target = cls.bridgeInitHooks ?: mutableListOf Unit>().also { @@ -399,16 +489,26 @@ private class ObjectBridgeBinderImpl( } } + override fun initWithInstance(block: suspend ScopeFacade.() -> Unit) { + initHooks.add { scope, _ -> + scope.block() + } + } + + @Deprecated( + message = "Use initWithInstance { ... } with ScopeFacade receiver; use thisObj from scope", + replaceWith = ReplaceWith("initWithInstance { block(this, thisObj) }") + ) override fun initWithInstance(block: suspend (ScopeFacade, Obj) -> Unit) { initHooks.add { scope, inst -> block(scope, inst) } } - override fun addFun(name: String, impl: suspend (ScopeFacade, Obj, Arguments) -> Obj) { + override fun addFun(name: String, impl: suspend ScopeFacade.() -> Obj) { val target = findMember(name) val callable = ObjExternCallable.fromBridge { - impl(this, thisObj, args) + impl() } val methodId = cls.ensureMethodIdForBridge(name, target.record) val newRecord = target.record.copy( @@ -420,13 +520,23 @@ private class ObjectBridgeBinderImpl( updateInstanceMember(target, newRecord) } - override fun addVal(name: String, impl: suspend (ScopeFacade, Obj) -> Obj) { + @Deprecated( + message = "Use addFun(name) { ... } with ScopeFacade receiver; use thisObj/args from scope", + replaceWith = ReplaceWith("addFun(name) { impl(this, thisObj, args) }") + ) + override fun addFun(name: String, impl: suspend (ScopeFacade, Obj, Arguments) -> Obj) { + addFun(name) { + impl(this, thisObj, args) + } + } + + override fun addVal(name: String, impl: suspend ScopeFacade.() -> Obj) { val target = findMember(name) if (target.record.isMutable) { throw ScriptError(Pos.builtIn, "extern val $name is mutable in class ${cls.className}") } val getter = ObjExternCallable.fromBridge { - impl(this, thisObj) + impl() } val prop = ObjProperty(name, getter, null) val isFieldLike = target.record.type == ObjRecord.Type.Field || @@ -452,21 +562,31 @@ private class ObjectBridgeBinderImpl( updateInstanceMember(target, newRecord) } + @Deprecated( + message = "Use addVal(name) { ... } with ScopeFacade receiver; use thisObj from scope", + replaceWith = ReplaceWith("addVal(name) { impl(this, thisObj) }") + ) + override fun addVal(name: String, impl: suspend (ScopeFacade, Obj) -> Obj) { + addVal(name) { + impl(this, thisObj) + } + } + override fun addVar( name: String, - get: suspend (ScopeFacade, Obj) -> Obj, - set: suspend (ScopeFacade, Obj, Obj) -> Unit + get: suspend ScopeFacade.() -> Obj, + set: suspend ScopeFacade.(Obj) -> Unit ) { val target = findMember(name) if (!target.record.isMutable) { throw ScriptError(Pos.builtIn, "extern var $name is readonly in class ${cls.className}") } val getter = ObjExternCallable.fromBridge { - get(this, thisObj) + get() } val setter = ObjExternCallable.fromBridge { val value = requiredArg(0) - set(this, thisObj, value) + set(value) ObjVoid } val prop = ObjProperty(name, getter, setter) @@ -493,6 +613,24 @@ private class ObjectBridgeBinderImpl( updateInstanceMember(target, newRecord) } + @Deprecated( + message = "Use addVar(name, get, set) with ScopeFacade receiver; use thisObj from scope", + replaceWith = ReplaceWith( + "addVar(name, get = { get(this, thisObj) }, set = { value -> set(this, thisObj, value) })" + ) + ) + override fun addVar( + name: String, + get: suspend (ScopeFacade, Obj) -> Obj, + set: suspend (ScopeFacade, Obj, Obj) -> Unit + ) { + addVar( + name, + get = { get(this, thisObj) }, + set = { value -> set(this, thisObj, value) } + ) + } + suspend fun commit() { if (initHooks.isNotEmpty()) { val target = cls.bridgeInitHooks ?: mutableListOf Unit>().also { diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjKotlinIterator.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjKotlinIterator.kt index 901c228..84e1950 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjKotlinIterator.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjKotlinIterator.kt @@ -45,18 +45,18 @@ class ObjKotlinIterator(val iterator: Iterator) : Obj() { } boundType = cls LyngClassBridge.bind(cls) { - addFun("next") { scope, self, _ -> - when (self) { + addFun("next") { + when (val self = thisObj) { is ObjKotlinIterator -> self.iterator.next().toObj() is ObjKotlinObjIterator -> self.iterator.next() - else -> scope.raiseClassCastError("Expected KotlinIterator, got ${self.objClass.className}") + else -> raiseClassCastError("Expected KotlinIterator, got ${self.objClass.className}") } } - addFun("hasNext") { scope, self, _ -> - when (self) { + addFun("hasNext") { + when (val self = thisObj) { is ObjKotlinIterator -> self.iterator.hasNext().toObj() is ObjKotlinObjIterator -> self.iterator.hasNext().toObj() - else -> scope.raiseClassCastError("Expected KotlinIterator, got ${self.objClass.className}") + else -> raiseClassCastError("Expected KotlinIterator, got ${self.objClass.className}") } } } diff --git a/lynglib/src/commonTest/kotlin/BridgeBindingTest.kt b/lynglib/src/commonTest/kotlin/BridgeBindingTest.kt index 08a7b75..660eb1f 100644 --- a/lynglib/src/commonTest/kotlin/BridgeBindingTest.kt +++ b/lynglib/src/commonTest/kotlin/BridgeBindingTest.kt @@ -61,39 +61,39 @@ class BridgeBindingTest { init { _ -> data = CounterState(0) } - addFun("add") { _, _, args -> + addFun("add") { 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) } + addVal("status") { ObjString(classData as String) } addVar( "count", - get = { _, instance -> - val st = (instance as net.sergeych.lyng.obj.ObjInstance).data as CounterState + get = { + val st = (thisObj 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 + set = { value -> + val st = (thisObj 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) } + 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) + initWithInstance { + (thisObj 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 + get = { + val st = (thisObj 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 + set = { value -> + val st = (thisObj as net.sergeych.lyng.obj.ObjInstance).data as CounterState st.count = (value as ObjInt).value } ) @@ -156,7 +156,7 @@ class BridgeBindingTest { val bindFailed = try { LyngClassBridge.bind(className = "Late", module = "bridge.late", importManager = im) { - addVal("status") { _, _ -> ObjString("late") } + addVal("status") { ObjString("late") } } false } catch (_: ScriptError) { @@ -183,20 +183,20 @@ class BridgeBindingTest { init { _ -> data = CounterState(5) } - addFun("add") { _, _, args -> + addFun("add") { 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) } + addVal("status") { ObjString(classData as String) } addVar( "count", - get = { _, instance -> - val st = (instance as net.sergeych.lyng.obj.ObjInstance).data as CounterState + get = { + val st = (thisObj 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 + set = { value -> + val st = (thisObj as net.sergeych.lyng.obj.ObjInstance).data as CounterState st.count = (value as ObjInt).value } ) diff --git a/notes/bridge_facade_draft.kt b/notes/bridge_facade_draft.kt index edbdf2e..e4201a2 100644 --- a/notes/bridge_facade_draft.kt +++ b/notes/bridge_facade_draft.kt @@ -146,10 +146,22 @@ interface ClassBridgeBinder { var classData: Any? fun init(block: suspend BridgeInstanceContext.(ScopeFacade) -> Unit) + fun initWithInstance(block: suspend ScopeFacade.() -> Unit) + @Deprecated("Use receiver-form initWithInstance and read thisObj from ScopeFacade") fun initWithInstance(block: suspend (ScopeFacade, Obj) -> Unit) + fun addFun(name: String, impl: suspend ScopeFacade.() -> Obj) + @Deprecated("Use receiver-form addFun and read thisObj/args from ScopeFacade") fun addFun(name: String, impl: suspend (ScopeFacade, Obj, Arguments) -> Obj) + fun addVal(name: String, impl: suspend ScopeFacade.() -> Obj) + @Deprecated("Use receiver-form addVal and read thisObj from ScopeFacade") fun addVal(name: String, impl: suspend (ScopeFacade, Obj) -> Obj) + fun addVar( + name: String, + get: suspend ScopeFacade.() -> Obj, + set: suspend ScopeFacade.(Obj) -> Unit + ) + @Deprecated("Use receiver-form addVar and read thisObj from ScopeFacade") fun addVar( name: String, get: suspend (ScopeFacade, Obj) -> Obj, diff --git a/notes/kotlin_bridge_binding.md b/notes/kotlin_bridge_binding.md index 95307b0..0c0c695 100644 --- a/notes/kotlin_bridge_binding.md +++ b/notes/kotlin_bridge_binding.md @@ -39,34 +39,34 @@ LyngClassBridge.bind(className = "Foo", module = "bridge.mod", importManager = i data = CounterState(0) } - addFun("add") { _, _, args -> + addFun("add") { 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) } + addVal("status") { ObjString(classData as String) } addVar( "count", - get = { _, instance -> - val st = (instance as ObjInstance).data as CounterState + get = { + val st = (thisObj as ObjInstance).data as CounterState ObjInt.of(st.count) }, - set = { _, instance, value -> - val st = (instance as ObjInstance).data as CounterState + set = { value -> + val st = (thisObj as ObjInstance).data as CounterState st.count = (value as ObjInt).value } ) - addFun("secret") { _, _, _ -> ObjInt.of(42) } - addFun("ping") { _, _, _ -> ObjInt.of(7) } + addFun("secret") { ObjInt.of(42) } + addFun("ping") { ObjInt.of(7) } } ``` ## Notes - Visibility is respected by usual Lyng access rules; private extern members can be used only within class code. -- Use `init { scope -> ... }` for receiver-style init, or `initWithInstance { scope, instance -> ... }` for explicit instance access. +- Use `init { ... }` / `initWithInstance { ... }` with `ScopeFacade` receiver; access instance via `thisObj`. - `classData` and `instance.data` are Kotlin-only payloads and do not appear in Lyng reflection. - Binding after the first instance of a class is created throws a `ScriptError`.