From 4b66454bf3a800da21c615e02026f12a7288ffea Mon Sep 17 00:00:00 2001 From: sergeych Date: Fri, 30 Jan 2026 17:03:51 +0300 Subject: [PATCH] Handle labeled break and catch locals in ScriptTest --- .../kotlin/net/sergeych/lyng/Compiler.kt | 20 ++++++++++++++++--- .../lyng/bytecode/BytecodeCompiler.kt | 8 +++++++- lynglib/src/commonTest/kotlin/ScriptTest.kt | 4 ---- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index 131279c..6661d76 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -712,7 +712,8 @@ class Compiler( private fun containsLoopControl(stmt: Statement, inLoop: Boolean = false): Boolean { val target = if (stmt is BytecodeStatement) stmt.original else stmt return when (target) { - is BreakStatement, is ContinueStatement -> !inLoop + is BreakStatement -> target.label != null || !inLoop + is ContinueStatement -> target.label != null || !inLoop is IfStatement -> { containsLoopControl(target.ifBody, inLoop) || (target.elseBody?.let { containsLoopControl(it, inLoop) } ?: false) @@ -2571,7 +2572,7 @@ class Compiler( val block = try { resolutionSink?.enterScope(ScopeKind.BLOCK, catchVar.pos, null) resolutionSink?.declareSymbol(catchVar.value, SymbolKind.LOCAL, isMutable = false, pos = catchVar.pos) - withCatchSlot(unwrapBytecodeDeep(parseBlock()), catchVar.value) + withCatchSlot(unwrapBytecodeDeep(parseBlockWithPredeclared(listOf(catchVar.value to false))), catchVar.value) } finally { resolutionSink?.exitScope(cc.currentPos()) } @@ -2585,7 +2586,10 @@ class Compiler( val block = try { resolutionSink?.enterScope(ScopeKind.BLOCK, itToken.pos, null) resolutionSink?.declareSymbol(itToken.value, SymbolKind.LOCAL, isMutable = false, pos = itToken.pos) - withCatchSlot(unwrapBytecodeDeep(parseBlock(true)), itToken.value) + withCatchSlot( + unwrapBytecodeDeep(parseBlockWithPredeclared(listOf(itToken.value to false), skipLeadingBrace = true)), + itToken.value + ) } finally { resolutionSink?.exitScope(cc.currentPos()) } @@ -3765,6 +3769,13 @@ class Compiler( } private suspend fun parseBlock(skipLeadingBrace: Boolean = false): Statement { + return parseBlockWithPredeclared(emptyList(), skipLeadingBrace) + } + + private suspend fun parseBlockWithPredeclared( + predeclared: List>, + skipLeadingBrace: Boolean = false + ): Statement { val startPos = cc.currentPos() if (!skipLeadingBrace) { val t = cc.next() @@ -3773,6 +3784,9 @@ class Compiler( } resolutionSink?.enterScope(ScopeKind.BLOCK, startPos, null) val blockSlotPlan = SlotPlan(mutableMapOf(), 0, nextScopeId++) + for ((name, isMutable) in predeclared) { + declareSlotNameIn(blockSlotPlan, name, isMutable, isDelegated = false) + } slotPlanStack.add(blockSlotPlan) val capturePlan = CapturePlan(blockSlotPlan) capturePlanStack.add(capturePlan) 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 468c901..d22e77d 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt @@ -2942,7 +2942,13 @@ class BytecodeCompiler( private fun compileBreak(stmt: net.sergeych.lyng.BreakStatement): CompiledValue? { val stack = loopStack.toList() - val targetIndex = findLoopContextIndex(stmt.label) ?: return null + val targetIndex = findLoopContextIndex(stmt.label) ?: run { + val labels = stack.joinToString(prefix = "[", postfix = "]") { it.label ?: "" } + throw BytecodeFallbackException( + "Bytecode fallback: break label '${stmt.label}' not found in $labels", + stmt.pos + ) + } val ctx = stack[targetIndex] val value = stmt.resultExpr?.let { compileStatementValueOrFallback(it) } if (ctx.resultSlot != null) { diff --git a/lynglib/src/commonTest/kotlin/ScriptTest.kt b/lynglib/src/commonTest/kotlin/ScriptTest.kt index a2a0d1f..9dd6f37 100644 --- a/lynglib/src/commonTest/kotlin/ScriptTest.kt +++ b/lynglib/src/commonTest/kotlin/ScriptTest.kt @@ -2092,7 +2092,6 @@ class ScriptTest { ) } - @Ignore("incremental enable") @Test fun testForLabelNreakTest() = runTest { eval( @@ -2115,7 +2114,6 @@ class ScriptTest { ) } - @Ignore("incremental enable") @Test fun testThrowExisting() = runTest { eval( @@ -2145,7 +2143,6 @@ class ScriptTest { ) } - @Ignore("incremental enable") @Test fun testCatchShort1() = runTest { eval( @@ -2170,7 +2167,6 @@ class ScriptTest { ) } - @Ignore("incremental enable") @Test fun testCatchShort2() = runTest { eval(