From 47eb2d7d61574215b8aacccd47637fbab77d3382 Mon Sep 17 00:00:00 2001 From: sergeych Date: Sun, 12 Apr 2026 09:16:58 +0300 Subject: [PATCH] Fix transitive import re-exports and stabilize captures --- .../CliLocalModuleImportRegressionJvmTest.kt | 2 + .../kotlin/net/sergeych/lyng/Compiler.kt | 63 ++++++- .../kotlin/net/sergeych/lyng/FrameAccess.kt | 26 +++ .../kotlin/net/sergeych/lyng/ModuleScope.kt | 35 +--- .../kotlin/net/sergeych/lyng/Script.kt | 22 ++- .../lyng/bytecode/BytecodeStatement.kt | 7 +- .../net/sergeych/lyng/bytecode/CmdRuntime.kt | 175 +++++++++++++++--- .../net/sergeych/lyng/bytecode/SeedLocals.kt | 7 +- 8 files changed, 263 insertions(+), 74 deletions(-) diff --git a/lyng/src/jvmTest/kotlin/net/sergeych/CliLocalModuleImportRegressionJvmTest.kt b/lyng/src/jvmTest/kotlin/net/sergeych/CliLocalModuleImportRegressionJvmTest.kt index c2a2509..831c13a 100644 --- a/lyng/src/jvmTest/kotlin/net/sergeych/CliLocalModuleImportRegressionJvmTest.kt +++ b/lyng/src/jvmTest/kotlin/net/sergeych/CliLocalModuleImportRegressionJvmTest.kt @@ -27,6 +27,8 @@ class CliLocalModuleImportRegressionJvmTest { import lyng.io.net class Alpha { + val headers = Map() + fn startListen(port, host) { launch { println(port, host) diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index 514d860..8c6b3c9 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -3436,12 +3436,12 @@ class Compiler( val resolvedRecords = ArrayList(captureSlots.size) val resolvedNames = ArrayList(captureSlots.size) for (capture in captureSlots) { - val rec = closureScope.chainLookupIgnoreClosure( + val rec = resolveStableCaptureRecord( + closureScope, capture.name, - followClosure = true, - caller = context.currentClassCtx + context.currentClassCtx ) ?: closureScope.raiseSymbolNotFound("symbol ${capture.name} not found") - resolvedRecords.add(rec) + resolvedRecords.add(freezeImmutableCaptureRecord(rec)) resolvedNames.add(capture.name) } context.captureRecords = resolvedRecords @@ -8766,6 +8766,46 @@ class Compiler( } } + private fun freezeImmutableCaptureRecord(record: ObjRecord): ObjRecord { + val value = record.value as Obj? + if (record.isMutable || record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || value is ObjProperty) { + return record + } + return when (value) { + is FrameSlotRef -> value.resolvedCaptureValueOrNull()?.let { record.copy(value = it) } ?: record + is RecordSlotRef -> value.resolvedCaptureValueOrNull()?.let { record.copy(value = it) } ?: record + is ScopeSlotRef -> value.resolvedCaptureValueOrNull()?.let { record.copy(value = it) } ?: record + null -> record + else -> record.copy() + } + } + + private fun isTransientCapturePlaceholder(value: Obj?): Boolean { + return when (value) { + null, ObjVoid -> true + is FrameSlotRef -> value.resolvedCaptureValueOrNull().let { it == null || it === ObjVoid } + is RecordSlotRef -> value.resolvedCaptureValueOrNull().let { it == null || it === ObjVoid } + is ScopeSlotRef -> value.resolvedCaptureValueOrNull().let { it == null || it === ObjVoid } + else -> false + } + } + + private fun resolveStableCaptureRecord(scope: Scope, name: String, caller: ObjClass?): ObjRecord? { + val direct = scope.chainLookupIgnoreClosure(name, followClosure = true, caller = caller) ?: scope.get(name) + if (direct != null && !isTransientCapturePlaceholder(direct.value as Obj?)) { + return direct + } + var parent = scope.parent + while (parent != null) { + val candidate = parent.chainLookupIgnoreClosure(name, followClosure = true, caller = caller) ?: parent.get(name) + if (candidate != null && !isTransientCapturePlaceholder(candidate.value as Obj?)) { + return candidate + } + parent = parent.parent + } + return direct + } + private suspend fun parseFunctionDeclaration( visibility: Visibility = Visibility.Public, isAbstract: Boolean = false, @@ -9147,12 +9187,12 @@ class Compiler( } else if (captureBase != null && captureNames.isNotEmpty()) { val resolvedRecords = ArrayList(captureNames.size) for (name in captureNames) { - val rec = captureBase.chainLookupIgnoreClosure( + val rec = resolveStableCaptureRecord( + captureBase, name, - followClosure = true, - caller = context.currentClassCtx + context.currentClassCtx ) ?: captureBase.raiseSymbolNotFound("symbol $name not found") - resolvedRecords.add(rec) + resolvedRecords.add(freezeImmutableCaptureRecord(rec)) } context.captureRecords = resolvedRecords context.captureNames = captureNames @@ -9193,7 +9233,12 @@ class Compiler( val value = if (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || record.value is ObjProperty) { context.resolve(record, localName) } else { - record.value + when (val direct = record.value) { + is FrameSlotRef -> direct.read() + is RecordSlotRef -> direct.read(context, localName) + is ScopeSlotRef -> direct.read() + else -> direct + } } frame.frame.setObj(i, value) } diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/FrameAccess.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/FrameAccess.kt index d1c2d0c..72d5082 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/FrameAccess.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/FrameAccess.kt @@ -79,6 +79,13 @@ class FrameSlotRef( } } + internal fun resolvedCaptureValueOrNull(): Obj? { + return when (frame.getSlotTypeCode(slot)) { + SlotType.INT.code, SlotType.REAL.code, SlotType.BOOL.code -> read() + else -> peekValue()?.let { read() } + } + } + fun write(value: Obj) { when (value) { is ObjInt -> frame.setInt(slot, value.value) @@ -132,6 +139,16 @@ class ScopeSlotRef( } } + internal fun resolvedCaptureValueOrNull(): Obj? { + val record = scope.getSlotRecord(slot) + return when (val direct = record.value as Obj?) { + is FrameSlotRef -> direct.resolvedCaptureValueOrNull() + is RecordSlotRef -> direct.resolvedCaptureValueOrNull() + is ScopeSlotRef -> direct.resolvedCaptureValueOrNull() + else -> direct + } + } + fun write(value: Obj) { scope.setSlotValue(slot, value) } @@ -198,6 +215,15 @@ class RecordSlotRef( } } + internal fun resolvedCaptureValueOrNull(): Obj? { + return when (val direct = record.value as Obj?) { + is FrameSlotRef -> direct.resolvedCaptureValueOrNull() + is RecordSlotRef -> direct.resolvedCaptureValueOrNull() + is ScopeSlotRef -> direct.resolvedCaptureValueOrNull() + else -> direct + } + } + fun write(value: Obj) { val direct = record.value if (direct is ScopeSlotRef) { diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ModuleScope.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ModuleScope.kt index 3be9117..c9d206b 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ModuleScope.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ModuleScope.kt @@ -19,8 +19,6 @@ package net.sergeych.lyng import net.sergeych.lyng.bytecode.BytecodeFrame import net.sergeych.lyng.bytecode.CmdFunction -import net.sergeych.lyng.obj.ObjClass -import net.sergeych.lyng.obj.ObjExternCallable import net.sergeych.lyng.obj.ObjRecord import net.sergeych.lyng.obj.ObjString import net.sergeych.lyng.pacman.ImportProvider @@ -34,20 +32,7 @@ class ModuleScope( pos: Pos = Pos.builtIn, override val packageName: String ) : Scope(importProvider.rootScope, Arguments.EMPTY, pos) { - - private fun ObjRecord.isEquivalentStdlibAlias(other: ObjRecord): Boolean { - val leftOrigin = importedFrom?.packageName - val rightOrigin = other.importedFrom?.packageName - val origins = setOf(leftOrigin, rightOrigin) - val rootStdlibAlias = - "lyng.stdlib" in origins && - origins.all { it == null || it == "" || it == "lyng.stdlib" } - if (!rootStdlibAlias) return false - if (value !== other.value) return false - if (receiver !== other.receiver || delegate !== other.delegate) return false - if (memberName != other.memberName || fieldId != other.fieldId || methodId != other.methodId) return false - return value is ObjExternCallable || value is ObjClass - } + private fun ObjRecord.importedCopy(source: Scope): ObjRecord = copy(importedFrom = source) constructor(importProvider: ImportProvider, source: Source) : this(importProvider, source.startPos, source.fileName) @@ -100,7 +85,7 @@ class ModuleScope( override suspend fun importInto(scope: Scope, symbols: Map?) { val symbolsToImport = symbols?.keys?.toMutableSet() for ((symbol, record) in this.objects) { - if (record.visibility.isPublic) { + if (record.visibility.isPublic && record.importedFrom == null) { val newName = symbols?.let { ss: Map -> ss[symbol] ?.also { symbolsToImport!!.remove(symbol) } @@ -110,25 +95,21 @@ class ModuleScope( if (newName != null) { val existing = scope.objects[newName] if (existing != null) { - val sameBinding = - existing === record || - existing.importedFrom == record.importedFrom || - existing.isEquivalentStdlibAlias(record) + val sameBinding = existing.importedFrom == this if (!sameBinding) scope.raiseError("symbol ${existing.importedFrom?.packageName}.$newName already exists, redefinition on import is not allowed") // already imported } else { - // when importing records, we keep track of its package (not otherwise needed) - if (record.importedFrom == null) record.importedFrom = this - scope.objects[newName] = record - scope.updateSlotFor(newName, record) + val imported = record.importedCopy(this) + scope.objects[newName] = imported + scope.updateSlotFor(newName, imported) } } } } for ((cls, map) in this.extensions) { for ((symbol, record) in map) { - if (record.visibility.isPublic) { + if (record.visibility.isPublic && record.importedFrom == null) { val newName = symbols?.let { ss: Map -> ss[symbol] ?.also { symbolsToImport!!.remove(symbol) } @@ -136,7 +117,7 @@ class ModuleScope( } ?: if (symbols == null) symbol else null if (newName != null) { - scope.addExtension(cls, newName, record) + scope.addExtension(cls, newName, record.importedCopy(this)) } } } diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt index 14a0998..1e1846f 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt @@ -181,37 +181,42 @@ class Script( } } + private fun importedBindingRecord(record: ObjRecord, source: Scope): ObjRecord = + record.copy(importedFrom = source) + private suspend fun seedImportBindings(scope: Scope, seedScope: Scope) { val provider = scope.currentImportProvider val importedModules = LinkedHashSet() for (moduleRef in this.importedModules) { importedModules.add(provider.prepareImport(moduleRef.pos, moduleRef.name, null)) } - if (scope is ModuleScope) { - scope.importedModules = importedModules.toList() - } for (module in importedModules) { module.importInto(scope, null) } for ((name, binding) in importBindings) { - val record = when (val source = binding.source) { + val sourceScope: Scope + val baseRecord = when (val source = binding.source) { is ImportBindingSource.Module -> { val module = provider.prepareImport(source.pos, source.name, null) importedModules.add(module) + sourceScope = module module.objects[binding.symbol]?.takeIf { it.visibility.isPublic } ?: scope.raiseSymbolNotFound("symbol ${source.name}.${binding.symbol} not found") } ImportBindingSource.Root -> { + sourceScope = provider.rootScope provider.rootScope.objects[binding.symbol]?.takeIf { it.visibility.isPublic } ?: scope.raiseSymbolNotFound("symbol ${binding.symbol} not found") } ImportBindingSource.Seed -> { + sourceScope = seedScope findSeedRecord(seedScope, binding.symbol) ?: scope.raiseSymbolNotFound("symbol ${binding.symbol} not found") } } + val record = importedBindingRecord(baseRecord, sourceScope) if (name == "Exception" && record.value !is ObjClass) { - scope.updateSlotFor(name, ObjRecord(ObjException.Root, isMutable = false)) + scope.updateSlotFor(name, ObjRecord(ObjException.Root, isMutable = false, importedFrom = sourceScope)) } else { scope.updateSlotFor(name, record) } @@ -219,12 +224,15 @@ class Script( for (module in importedModules) { for ((cls, map) in module.extensions) { for ((symbol, record) in map) { - if (record.visibility.isPublic) { - scope.addExtension(cls, symbol, record) + if (record.visibility.isPublic && record.importedFrom == null) { + scope.addExtension(cls, symbol, importedBindingRecord(record, module)) } } } } + if (scope is ModuleScope) { + scope.importedModules = importedModules.toList() + } } private fun findSeedRecord(scope: Scope?, name: String): ObjRecord? { diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeStatement.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeStatement.kt index b88d13b..d4591d8 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeStatement.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeStatement.kt @@ -53,7 +53,12 @@ class BytecodeStatement private constructor( val value = if (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || record.value is net.sergeych.lyng.obj.ObjProperty) { scope.resolve(record, name) } else { - record.value + when (val direct = record.value) { + is FrameSlotRef -> direct.read() + is RecordSlotRef -> direct.read(scope, name) + is ScopeSlotRef -> direct.read() + else -> direct + } } frame.frame.setObj(i, value) } diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt index af10be9..adb929b 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt @@ -2560,6 +2560,46 @@ private fun captureNamesForStatement(stmt: Statement?): List { return ordered.toList() } +private fun freezeImmutableCaptureRecord(record: ObjRecord): ObjRecord { + val value = record.value as Obj? + if (record.isMutable || record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || value is ObjProperty) { + return record + } + return when (value) { + is FrameSlotRef -> value.resolvedCaptureValueOrNull()?.let { record.copy(value = it) } ?: record + is RecordSlotRef -> value.resolvedCaptureValueOrNull()?.let { record.copy(value = it) } ?: record + is ScopeSlotRef -> value.resolvedCaptureValueOrNull()?.let { record.copy(value = it) } ?: record + null -> record + else -> record.copy() + } +} + +private fun isTransientCapturePlaceholder(value: Obj?): Boolean { + return when (value) { + null, ObjVoid -> true + is FrameSlotRef -> value.resolvedCaptureValueOrNull().let { it == null || it === ObjVoid } + is RecordSlotRef -> value.resolvedCaptureValueOrNull().let { it == null || it === ObjVoid } + is ScopeSlotRef -> value.resolvedCaptureValueOrNull().let { it == null || it === ObjVoid } + else -> false + } +} + +private fun resolveStableCaptureRecord(scope: Scope, name: String): ObjRecord? { + val direct = scope.chainLookupIgnoreClosure(name, followClosure = true) ?: scope.get(name) + if (direct != null && !isTransientCapturePlaceholder(direct.value as Obj?)) { + return direct + } + var parent = scope.parent + while (parent != null) { + val candidate = parent.chainLookupIgnoreClosure(name, followClosure = true) ?: parent.get(name) + if (candidate != null && !isTransientCapturePlaceholder(candidate.value as Obj?)) { + return candidate + } + parent = parent.parent + } + return direct +} + private fun buildFunctionCaptureRecords(frame: CmdFrame, captureNames: List): List? { if (captureNames.isEmpty()) return null val records = ArrayList(captureNames.size) @@ -2575,12 +2615,16 @@ private fun buildFunctionCaptureRecords(frame: CmdFrame, captureNames: List= 0) { val target = frame.scopeTarget(scopeSlot) val index = frame.fn.scopeSlotIndices[scopeSlot] - records += target.getSlotRecord(index) + records += freezeImmutableCaptureRecord(target.getSlotRecord(index)) continue } val scopeCaptures = frame.scope.captureRecords @@ -2605,7 +2649,7 @@ private fun buildFunctionCaptureRecords(frame: CmdFrame, captureNames: List value.read() + is RecordSlotRef -> value.read(frame.ensureScope(), name) + is ScopeSlotRef -> value.read() + else -> value + } } } if (receiver is ObjQualifiedView) { @@ -3785,13 +3834,14 @@ class BytecodeLambdaCallable( ) : Statement(), BytecodeCallable { private fun freezeRecord(record: ObjRecord): ObjRecord { if (record.isMutable) return record - val frozenValue = when (val raw = record.value) { - is net.sergeych.lyng.FrameSlotRef -> raw.read() - is net.sergeych.lyng.RecordSlotRef -> raw.read() - is net.sergeych.lyng.ScopeSlotRef -> raw.read() - else -> raw + val raw = record.value as Obj? + return when (raw) { + is net.sergeych.lyng.FrameSlotRef -> raw.resolvedCaptureValueOrNull()?.let { record.copy(value = it) } ?: record + is net.sergeych.lyng.RecordSlotRef -> raw.resolvedCaptureValueOrNull()?.let { record.copy(value = it) } ?: record + is net.sergeych.lyng.ScopeSlotRef -> raw.resolvedCaptureValueOrNull()?.let { record.copy(value = it) } ?: record + null -> record + else -> record.copy() } - return record.copy(value = frozenValue) } private fun resolveCaptureRecords(base: Scope): List? { @@ -3924,7 +3974,12 @@ class BytecodeLambdaCallable( if (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || record.value is ObjProperty) { context.resolve(record, name) } else { - record.value + when (val direct = record.value) { + is FrameSlotRef -> direct.read() + is RecordSlotRef -> direct.read(context, name) + is ScopeSlotRef -> direct.read() + else -> direct + } } frame.frame.setObj(i, value) } @@ -4091,7 +4146,31 @@ class CmdFrame( frame.setObj(localIndex, record.delegate ?: ObjNull) } else { val value = record.value - if (value is FrameSlotRef) { + if (!record.isMutable && value is FrameSlotRef) { + val resolved = value.peekValue() + if (resolved != null) { + if (value.refersTo(frame, localIndex)) continue + frame.setObj(localIndex, value.read()) + } else { + frame.setObj(localIndex, value) + } + } else if (!record.isMutable && value is RecordSlotRef) { + val resolved = value.peekValue() + if (resolved != null) { + frame.setObj(localIndex, value.read()) + } else { + frame.setObj(localIndex, value) + } + } else if (!record.isMutable && value is ScopeSlotRef) { + val resolved = value.peekValue() + if (resolved != null) { + frame.setObj(localIndex, value.read()) + } else { + frame.setObj(localIndex, value) + } + } else if (!record.isMutable) { + frame.setObj(localIndex, value) + } else if (value is FrameSlotRef) { if (value.refersTo(frame, localIndex)) continue frame.setObj(localIndex, value) } else { @@ -4106,7 +4185,30 @@ class CmdFrame( frame.setObj(idx, record.delegate ?: ObjNull) } else { val value = record.value - if (value is FrameSlotRef) { + if (!record.isMutable && value is FrameSlotRef) { + val resolved = value.peekValue() + if (resolved != null) { + frame.setObj(idx, value.read()) + } else { + frame.setObj(idx, value) + } + } else if (!record.isMutable && value is RecordSlotRef) { + val resolved = value.peekValue() + if (resolved != null) { + frame.setObj(idx, value.read()) + } else { + frame.setObj(idx, value) + } + } else if (!record.isMutable && value is ScopeSlotRef) { + val resolved = value.peekValue() + if (resolved != null) { + frame.setObj(idx, value.read()) + } else { + frame.setObj(idx, value) + } + } else if (!record.isMutable) { + frame.setObj(idx, value) + } else if (value is FrameSlotRef) { frame.setObj(idx, value) } else { frame.setObj(idx, RecordSlotRef(record)) @@ -5085,6 +5187,7 @@ class CmdFrame( when (obj) { is FrameSlotRef -> obj.read() is RecordSlotRef -> obj.read(scope, localName) + is ScopeSlotRef -> obj.read() is ObjProperty -> resolvePropertyLikeLocal(localName, obj) ObjUnset -> resolveUnsetLocal(localName) else -> obj @@ -5096,6 +5199,7 @@ class CmdFrame( when (obj) { is FrameSlotRef -> obj.read() is RecordSlotRef -> obj.read(scope, localName) + is ScopeSlotRef -> obj.read() is ObjProperty -> resolvePropertyLikeLocal(localName, obj) ObjUnset -> resolveUnsetLocal(localName) else -> obj @@ -5120,7 +5224,24 @@ class CmdFrame( if (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || record.value is ObjProperty) { return scope.resolve(record, localName) } - return record.value + return when (val value = record.value) { + is FrameSlotRef -> value.read() + is RecordSlotRef -> value.read(scope, localName) + is ScopeSlotRef -> value.read() + else -> value + } + } + + private suspend fun readResolvedScopeRecord(target: Scope, name: String, record: ObjRecord): Obj { + val value = record.value + return when { + record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || value is ObjProperty -> + target.resolve(record, name) + value is FrameSlotRef -> value.read() + value is RecordSlotRef -> value.read(target, name) + value is ScopeSlotRef -> value.read() + else -> value + } } private suspend fun getScopeSlotValue(slot: Int): Obj { @@ -5135,10 +5256,7 @@ class CmdFrame( if (name != null && record.memberName != null && record.memberName != name) { val resolved = target.get(name) if (resolved != null) { - val resolvedValue = resolved.value - if (resolved.type == ObjRecord.Type.Delegated || resolved.type == ObjRecord.Type.Property || resolvedValue is ObjProperty) { - return target.resolve(resolved, name) - } + val resolvedValue = readResolvedScopeRecord(target, name, resolved) if (resolvedValue !== ObjUnset) { target.updateSlotFor(name, resolved) } @@ -5157,12 +5275,13 @@ class CmdFrame( failMissingPreparedModuleBinding(slot, name, hadNamedBinding, record) return record.value } - if (resolved.value !== ObjUnset) { + val resolvedValue = readResolvedScopeRecord(target, name, resolved) + if (resolvedValue !== ObjUnset) { target.updateSlotFor(name, resolved) } else { failMissingPreparedModuleBinding(slot, name, hadNamedBinding, resolved) } - return resolved.value + return resolvedValue } private suspend fun getScopeSlotValueAtAddr(addrSlot: Int): Obj { @@ -5178,10 +5297,7 @@ class CmdFrame( if (name != null && record.memberName != null && record.memberName != name) { val resolved = target.get(name) if (resolved != null) { - val resolvedValue = resolved.value - if (resolved.type == ObjRecord.Type.Delegated || resolved.type == ObjRecord.Type.Property || resolvedValue is ObjProperty) { - return target.resolve(resolved, name) - } + val resolvedValue = readResolvedScopeRecord(target, name, resolved) if (resolvedValue !== ObjUnset) { target.updateSlotFor(name, resolved) } @@ -5200,12 +5316,13 @@ class CmdFrame( failMissingPreparedModuleBinding(slotId, name, hadNamedBinding, record) return record.value } - if (resolved.value !== ObjUnset) { + val resolvedValue = readResolvedScopeRecord(target, name, resolved) + if (resolvedValue !== ObjUnset) { target.updateSlotFor(name, resolved) } else { failMissingPreparedModuleBinding(slotId, name, hadNamedBinding, resolved) } - return resolved.value + return resolvedValue } private suspend fun setScopeSlotValueAtAddr(addrSlot: Int, value: Obj) { diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/SeedLocals.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/SeedLocals.kt index 5af9b41..d538517 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/SeedLocals.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/SeedLocals.kt @@ -34,7 +34,12 @@ internal suspend fun seedFrameLocalsFromScope(frame: CmdFrame, scope: Scope) { val value = if (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || record.value is net.sergeych.lyng.obj.ObjProperty) { scope.resolve(record, name) } else { - record.value + when (val direct = record.value) { + is net.sergeych.lyng.FrameSlotRef -> direct.resolvedCaptureValueOrNull() ?: direct + is net.sergeych.lyng.RecordSlotRef -> direct.resolvedCaptureValueOrNull() ?: direct + is net.sergeych.lyng.ScopeSlotRef -> direct.resolvedCaptureValueOrNull() ?: direct + else -> direct + } } if (value is net.sergeych.lyng.FrameSlotRef && value.refersTo(frame.frame, i)) { continue