From 89cf2c1612d248a2b6dd7d8a8bba5a5953586b45 Mon Sep 17 00:00:00 2001 From: sergeych Date: Fri, 30 Jan 2026 14:00:02 +0300 Subject: [PATCH] Fix module scope resolution in cmd runtime --- .../lyng/bytecode/BytecodeCompiler.kt | 53 ++++++++++++++++++- .../net/sergeych/lyng/bytecode/CmdRuntime.kt | 27 +++++++--- lynglib/src/commonTest/kotlin/ScriptTest.kt | 4 -- 3 files changed, 72 insertions(+), 12 deletions(-) diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt index dd2ac81..1583f73 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt @@ -701,6 +701,12 @@ class BytecodeCompiler( builder.emit(Opcode.CMP_EQ_OBJ, left.slot, right.slot, out) return CompiledValue(out, SlotType.BOOL) } + if (a.type == SlotType.OBJ || b.type == SlotType.OBJ) { + val left = ensureObjSlot(a) + val right = ensureObjSlot(b) + builder.emit(Opcode.CMP_EQ_OBJ, left.slot, right.slot, out) + return CompiledValue(out, SlotType.BOOL) + } return when { a.type == SlotType.INT && b.type == SlotType.INT -> { builder.emit(Opcode.CMP_EQ_INT, a.slot, b.slot, out) @@ -737,6 +743,12 @@ class BytecodeCompiler( builder.emit(Opcode.CMP_NEQ_OBJ, left.slot, right.slot, out) return CompiledValue(out, SlotType.BOOL) } + if (a.type == SlotType.OBJ || b.type == SlotType.OBJ) { + val left = ensureObjSlot(a) + val right = ensureObjSlot(b) + builder.emit(Opcode.CMP_NEQ_OBJ, left.slot, right.slot, out) + return CompiledValue(out, SlotType.BOOL) + } return when { a.type == SlotType.INT && b.type == SlotType.INT -> { builder.emit(Opcode.CMP_NEQ_INT, a.slot, b.slot, out) @@ -773,6 +785,12 @@ class BytecodeCompiler( builder.emit(Opcode.CMP_LT_OBJ, left.slot, right.slot, out) return CompiledValue(out, SlotType.BOOL) } + if (a.type == SlotType.OBJ || b.type == SlotType.OBJ) { + val left = ensureObjSlot(a) + val right = ensureObjSlot(b) + builder.emit(Opcode.CMP_LT_OBJ, left.slot, right.slot, out) + return CompiledValue(out, SlotType.BOOL) + } return when { a.type == SlotType.INT && b.type == SlotType.INT -> { builder.emit(Opcode.CMP_LT_INT, a.slot, b.slot, out) @@ -805,6 +823,12 @@ class BytecodeCompiler( builder.emit(Opcode.CMP_LTE_OBJ, left.slot, right.slot, out) return CompiledValue(out, SlotType.BOOL) } + if (a.type == SlotType.OBJ || b.type == SlotType.OBJ) { + val left = ensureObjSlot(a) + val right = ensureObjSlot(b) + builder.emit(Opcode.CMP_LTE_OBJ, left.slot, right.slot, out) + return CompiledValue(out, SlotType.BOOL) + } return when { a.type == SlotType.INT && b.type == SlotType.INT -> { builder.emit(Opcode.CMP_LTE_INT, a.slot, b.slot, out) @@ -837,6 +861,12 @@ class BytecodeCompiler( builder.emit(Opcode.CMP_GT_OBJ, left.slot, right.slot, out) return CompiledValue(out, SlotType.BOOL) } + if (a.type == SlotType.OBJ || b.type == SlotType.OBJ) { + val left = ensureObjSlot(a) + val right = ensureObjSlot(b) + builder.emit(Opcode.CMP_GT_OBJ, left.slot, right.slot, out) + return CompiledValue(out, SlotType.BOOL) + } return when { a.type == SlotType.INT && b.type == SlotType.INT -> { builder.emit(Opcode.CMP_GT_INT, a.slot, b.slot, out) @@ -869,6 +899,12 @@ class BytecodeCompiler( builder.emit(Opcode.CMP_GTE_OBJ, left.slot, right.slot, out) return CompiledValue(out, SlotType.BOOL) } + if (a.type == SlotType.OBJ || b.type == SlotType.OBJ) { + val left = ensureObjSlot(a) + val right = ensureObjSlot(b) + builder.emit(Opcode.CMP_GTE_OBJ, left.slot, right.slot, out) + return CompiledValue(out, SlotType.BOOL) + } return when { a.type == SlotType.INT && b.type == SlotType.INT -> { builder.emit(Opcode.CMP_GTE_INT, a.slot, b.slot, out) @@ -2978,6 +3014,11 @@ class BytecodeCompiler( scopeSlotIndexByName[ref.name]?.let { return it } } if (ref.captureOwnerScopeId != null) { + val ownerKey = ScopeSlotKey(ref.captureOwnerScopeId, ref.captureOwnerSlot ?: refSlot(ref)) + val ownerLocal = localSlotIndexByKey[ownerKey] + if (ownerLocal != null) { + return scopeSlotCount + ownerLocal + } val scopeKey = ScopeSlotKey(refScopeId(ref), refSlot(ref)) return scopeSlotMap[scopeKey] } @@ -3530,7 +3571,17 @@ class BytecodeCompiler( if (stmt == null) return null val target = if (stmt is BytecodeStatement) stmt.original else stmt val expr = target as? ExpressionStatement ?: return null - return expr.ref as? RangeRef + val ref = expr.ref + if (ref is RangeRef) return ref + if (ref is ConstRef) { + val range = ref.constValue as? ObjRange ?: return null + val start = range.start as? ObjInt ?: return null + val end = range.end as? ObjInt ?: return null + val left = ConstRef(start.asReadonly) + val right = ConstRef(end.asReadonly) + return RangeRef(left, right, range.isEndInclusive) + } + return null } private fun extractRangeFromLocal(source: Statement): RangeRef? { 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 0d38577..0ab7eb8 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt @@ -18,6 +18,7 @@ package net.sergeych.lyng.bytecode import net.sergeych.lyng.Arguments import net.sergeych.lyng.ExecutionError +import net.sergeych.lyng.ModuleScope import net.sergeych.lyng.PerfFlags import net.sergeych.lyng.PerfStats import net.sergeych.lyng.Pos @@ -1498,7 +1499,7 @@ class CmdFrame( var ip: Int = 0 var scope: Scope = scope0 - private val moduleScope: Scope = scope0 + private val moduleScope: Scope = resolveModuleScope(scope0) val methodCallSites: MutableMap = CmdCallSiteCache.methodCallSites(fn) internal val scopeStack = ArrayDeque() @@ -1521,6 +1522,18 @@ class CmdFrame( } } + private fun resolveModuleScope(scope: Scope): Scope { + var current: Scope? = scope + var last: Scope = scope + while (current != null) { + if (current is ModuleScope) return current + if (current.parent is ModuleScope) return current + last = current + current = current.parent + } + return last + } + fun pushScope(plan: Map, captures: List) { val parentScope = scope val captureRecords = if (captures.isNotEmpty()) { @@ -1989,14 +2002,14 @@ class CmdFrame( return index } target.applySlotPlan(mapOf(name to index)) - val existing = target.getLocalRecordDirect(name) + val existing = target.getLocalRecordDirect(name) ?: target.localBindings[name] if (existing != null) { target.updateSlotFor(name, existing) - } else { - val resolved = target.get(name) - if (resolved != null) { - target.updateSlotFor(name, resolved) - } + return index + } + val resolved = target.parent?.get(name) ?: target.get(name) + if (resolved != null) { + target.updateSlotFor(name, resolved) } return index } diff --git a/lynglib/src/commonTest/kotlin/ScriptTest.kt b/lynglib/src/commonTest/kotlin/ScriptTest.kt index 127a577..c106952 100644 --- a/lynglib/src/commonTest/kotlin/ScriptTest.kt +++ b/lynglib/src/commonTest/kotlin/ScriptTest.kt @@ -1406,7 +1406,6 @@ class ScriptTest { ) } - @Ignore("incremental enable") @Test fun testOpenEndRanges3() = runTest { eval( @@ -1419,7 +1418,6 @@ class ScriptTest { ) } - @Ignore("incremental enable") @Test fun testCharacterRange() = runTest { eval( @@ -1434,7 +1432,6 @@ class ScriptTest { ) } - @Ignore("incremental enable") @Test fun testIs() = runTest { eval( @@ -1450,7 +1447,6 @@ class ScriptTest { ) } - @Ignore("incremental enable") @Test fun testForRange() = runTest { eval(