Step 24D: bytecode-only closure path for lambdas

This commit is contained in:
Sergey Chernov 2026-02-10 07:56:08 +03:00
parent c066dc7150
commit abbebec153
4 changed files with 29 additions and 12 deletions

View File

@ -2877,7 +2877,12 @@ class Compiler(
override val pos: Pos = body.pos override val pos: Pos = body.pos
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
// and the source closure of the lambda which might have other thisObj. // 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 (paramSlotPlanSnapshot.isNotEmpty()) context.applySlotPlan(paramSlotPlanSnapshot)
if (captureSlots.isNotEmpty()) { if (captureSlots.isNotEmpty()) {
val captureRecords = closureScope.captureRecords val captureRecords = closureScope.captureRecords

View File

@ -832,6 +832,20 @@ open class Scope(
open fun applyClosure(closure: Scope, preferredThisType: String? = null): Scope = open fun applyClosure(closure: Scope, preferredThisType: String? = null): Scope =
ClosureScope(this, closure, preferredThisType) 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<Obj>(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. * 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: * For input like `A.B.C`, it builds the same ObjRef chain the compiler emits:

View File

@ -18,7 +18,6 @@
package net.sergeych.lyng.obj package net.sergeych.lyng.obj
import net.sergeych.lyng.Arguments import net.sergeych.lyng.Arguments
import net.sergeych.lyng.ClosureScope
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.Statement 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. * with method invocation which is implemented separately in [invokeInstanceMethod] below.
*/ */
override suspend fun readField(scope: Scope, name: String): ObjRecord { 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 { return readCallback?.execute(execBase.createChildScope(Arguments(ObjString(name))))?.let {
if (writeCallback != null) if (writeCallback != null)
it.asMutable it.asMutable
@ -83,26 +82,26 @@ open class ObjDynamic(var readCallback: Statement? = null, var writeCallback: St
args: Arguments, args: Arguments,
onNotFoundResult: (suspend () -> Obj?)? onNotFoundResult: (suspend () -> Obj?)?
): 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)))) val over = readCallback?.execute(execBase.createChildScope(Arguments(ObjString(name))))
return over?.invoke(scope, scope.thisObj, args) return over?.invoke(scope, scope.thisObj, args)
?: super.invokeInstanceMethod(scope, name, args, onNotFoundResult) ?: super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
} }
override suspend fun writeField(scope: Scope, name: String, newValue: Obj) { 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))) writeCallback?.execute(execBase.createChildScope(Arguments(ObjString(name), newValue)))
?: super.writeField(scope, name, newValue) ?: super.writeField(scope, name, newValue)
} }
override suspend fun getAt(scope: Scope, index: Obj): Obj { 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))) return readCallback?.execute(execBase.createChildScope(Arguments(index)))
?: super.getAt(scope, index) ?: super.getAt(scope, index)
} }
override suspend fun putAt(scope: Scope, index: Obj, newValue: Obj) { 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))) writeCallback?.execute(execBase.createChildScope(Arguments(index, newValue)))
?: super.putAt(scope, index, newValue) ?: super.putAt(scope, index, newValue)
} }

View File

@ -18,7 +18,6 @@
package net.sergeych.lyng.obj package net.sergeych.lyng.obj
import net.sergeych.lyng.Arguments import net.sergeych.lyng.Arguments
import net.sergeych.lyng.ClosureScope
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.Statement import net.sergeych.lyng.Statement
@ -35,9 +34,9 @@ class ObjProperty(
suspend fun callGetter(scope: Scope, instance: Obj, declaringClass: ObjClass? = null): Obj { suspend fun callGetter(scope: Scope, instance: Obj, declaringClass: ObjClass? = null): Obj {
val g = getter ?: scope.raiseError("property $name has no getter") val g = getter ?: scope.raiseError("property $name has no getter")
// Execute getter in a child scope of the instance with 'this' properly set // 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 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 execScope.currentClassCtx = declaringClass
return g.execute(execScope) return g.execute(execScope)
} }
@ -45,9 +44,9 @@ class ObjProperty(
suspend fun callSetter(scope: Scope, instance: Obj, value: Obj, declaringClass: ObjClass? = null) { suspend fun callSetter(scope: Scope, instance: Obj, value: Obj, declaringClass: ObjClass? = null) {
val s = setter ?: scope.raiseError("property $name has no setter") 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 // 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 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 execScope.currentClassCtx = declaringClass
s.execute(execScope) s.execute(execScope)
} }