diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index 57cd93a..9b3bde7 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -2877,7 +2877,12 @@ class Compiler( override val pos: Pos = body.pos override suspend fun execute(scope: Scope): Obj { // and the source closure of the lambda which might have other thisObj. - val context = scope.applyClosure(closureScope, preferredThisType = expectedReceiverType) + val useBytecodeClosure = closureScope.captureRecords != null + val context = if (useBytecodeClosure) { + scope.applyClosureForBytecode(closureScope, preferredThisType = expectedReceiverType) + } else { + scope.applyClosure(closureScope, preferredThisType = expectedReceiverType) + } if (paramSlotPlanSnapshot.isNotEmpty()) context.applySlotPlan(paramSlotPlanSnapshot) if (captureSlots.isNotEmpty()) { val captureRecords = closureScope.captureRecords diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Scope.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Scope.kt index b280ff8..e90f156 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Scope.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Scope.kt @@ -832,6 +832,20 @@ open class Scope( open fun applyClosure(closure: Scope, preferredThisType: String? = null): Scope = ClosureScope(this, closure, preferredThisType) + internal fun applyClosureForBytecode(closure: Scope, preferredThisType: String? = null): Scope { + val context = createChildScope(newThisObj = closure.thisObj) + val desired = preferredThisType?.let { typeName -> + thisVariants.firstOrNull { it.objClass.className == typeName } + } + val merged = ArrayList(thisVariants.size + closure.thisVariants.size + 1) + desired?.let { merged.add(it) } + merged.addAll(thisVariants) + merged.addAll(closure.thisVariants) + context.setThisVariants(closure.thisObj, merged) + context.currentClassCtx = closure.currentClassCtx ?: currentClassCtx + return context + } + /** * Resolve and evaluate a qualified identifier exactly as compiled code would. * For input like `A.B.C`, it builds the same ObjRef chain the compiler emits: diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjDynamic.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjDynamic.kt index eb3d8ec..44d646a 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjDynamic.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjDynamic.kt @@ -18,7 +18,6 @@ package net.sergeych.lyng.obj import net.sergeych.lyng.Arguments -import net.sergeych.lyng.ClosureScope import net.sergeych.lyng.Scope import net.sergeych.lyng.Statement @@ -63,7 +62,7 @@ open class ObjDynamic(var readCallback: Statement? = null, var writeCallback: St * with method invocation which is implemented separately in [invokeInstanceMethod] below. */ override suspend fun readField(scope: Scope, name: String): ObjRecord { - val execBase = builderScope?.let { ClosureScope(scope, it) } ?: scope + val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope return readCallback?.execute(execBase.createChildScope(Arguments(ObjString(name))))?.let { if (writeCallback != null) it.asMutable @@ -83,26 +82,26 @@ open class ObjDynamic(var readCallback: Statement? = null, var writeCallback: St args: Arguments, onNotFoundResult: (suspend () -> Obj?)? ): Obj { - val execBase = builderScope?.let { ClosureScope(scope, it) } ?: scope + val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope val over = readCallback?.execute(execBase.createChildScope(Arguments(ObjString(name)))) return over?.invoke(scope, scope.thisObj, args) ?: super.invokeInstanceMethod(scope, name, args, onNotFoundResult) } override suspend fun writeField(scope: Scope, name: String, newValue: Obj) { - val execBase = builderScope?.let { ClosureScope(scope, it) } ?: scope + val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope writeCallback?.execute(execBase.createChildScope(Arguments(ObjString(name), newValue))) ?: super.writeField(scope, name, newValue) } override suspend fun getAt(scope: Scope, index: Obj): Obj { - val execBase = builderScope?.let { ClosureScope(scope, it) } ?: scope + val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope return readCallback?.execute(execBase.createChildScope(Arguments(index))) ?: super.getAt(scope, index) } override suspend fun putAt(scope: Scope, index: Obj, newValue: Obj) { - val execBase = builderScope?.let { ClosureScope(scope, it) } ?: scope + val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope writeCallback?.execute(execBase.createChildScope(Arguments(index, newValue))) ?: super.putAt(scope, index, newValue) } diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjProperty.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjProperty.kt index d29e535..437d05f 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjProperty.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjProperty.kt @@ -18,7 +18,6 @@ package net.sergeych.lyng.obj import net.sergeych.lyng.Arguments -import net.sergeych.lyng.ClosureScope import net.sergeych.lyng.Scope import net.sergeych.lyng.Statement @@ -35,9 +34,9 @@ class ObjProperty( suspend fun callGetter(scope: Scope, instance: Obj, declaringClass: ObjClass? = null): Obj { val g = getter ?: scope.raiseError("property $name has no getter") // Execute getter in a child scope of the instance with 'this' properly set - // Use ClosureScope to match extension function behavior (access to instance scope + call scope) + // Match extension function behavior (access to instance scope + call scope). val instanceScope = (instance as? ObjInstance)?.instanceScope ?: instance.autoInstanceScope(scope) - val execScope = ClosureScope(scope, instanceScope).createChildScope(newThisObj = instance) + val execScope = scope.applyClosure(instanceScope).createChildScope(newThisObj = instance) execScope.currentClassCtx = declaringClass return g.execute(execScope) } @@ -45,9 +44,9 @@ class ObjProperty( suspend fun callSetter(scope: Scope, instance: Obj, value: Obj, declaringClass: ObjClass? = null) { val s = setter ?: scope.raiseError("property $name has no setter") // Execute setter in a child scope of the instance with 'this' properly set and the value as an argument - // Use ClosureScope to match extension function behavior + // Match extension function behavior (access to instance scope + call scope). val instanceScope = (instance as? ObjInstance)?.instanceScope ?: instance.autoInstanceScope(scope) - val execScope = ClosureScope(scope, instanceScope).createChildScope(args = Arguments(value), newThisObj = instance) + val execScope = scope.applyClosure(instanceScope).createChildScope(args = Arguments(value), newThisObj = instance) execScope.currentClassCtx = declaringClass s.execute(execScope) }