Add iterator cancellation for bytecode for-in loops

This commit is contained in:
Sergey Chernov 2026-01-29 06:47:27 +03:00
parent 0e069382a2
commit e346e7e56e
9 changed files with 215 additions and 40 deletions

View File

@ -3401,26 +3401,15 @@ class Compiler(
val names = mutableListOf<String>() val names = mutableListOf<String>()
pattern.forEachVariable { names.add(it) } pattern.forEachVariable { names.add(it) }
return object : Statement() { return DestructuringVarDeclStatement(
override val pos: Pos = start pattern,
override suspend fun execute(context: Scope): Obj { names,
val value = initialExpression.execute(context) initialExpression,
for (name in names) { isMutable,
context.addItem(name, true, ObjVoid, visibility, isTransient = isTransient) visibility,
} isTransient,
pattern.setAt(start, context, value) start
if (!isMutable) { )
for (name in names) {
val rec = context.objects[name]!!
val immutableRec = rec.copy(isMutable = false)
context.objects[name] = immutableRec
context.localBindings[name] = immutableRec
context.updateSlotFor(name, immutableRec)
}
}
return ObjVoid
}
}
} }
if (nextToken.type != Token.Type.ID) if (nextToken.type != Token.Type.ID)

View File

@ -0,0 +1,49 @@
/*
* Copyright 2026 Sergey S. Chernov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sergeych.lyng
import net.sergeych.lyng.obj.ListLiteralRef
import net.sergeych.lyng.obj.Obj
import net.sergeych.lyng.obj.ObjVoid
class DestructuringVarDeclStatement(
val pattern: ListLiteralRef,
val names: List<String>,
val initializer: Statement,
val isMutable: Boolean,
val visibility: Visibility,
val isTransient: Boolean,
override val pos: Pos,
) : Statement() {
override suspend fun execute(context: Scope): Obj {
val value = initializer.execute(context)
for (name in names) {
context.addItem(name, true, ObjVoid, visibility, isTransient = isTransient)
}
pattern.setAt(pos, context, value)
if (!isMutable) {
for (name in names) {
val rec = context.objects[name]!!
val immutableRec = rec.copy(isMutable = false)
context.objects[name] = immutableRec
context.localBindings[name] = immutableRec
context.updateSlotFor(name, immutableRec)
}
}
return ObjVoid
}
}

View File

@ -17,6 +17,7 @@
package net.sergeych.lyng.bytecode package net.sergeych.lyng.bytecode
import net.sergeych.lyng.BlockStatement import net.sergeych.lyng.BlockStatement
import net.sergeych.lyng.DestructuringVarDeclStatement
import net.sergeych.lyng.ExpressionStatement import net.sergeych.lyng.ExpressionStatement
import net.sergeych.lyng.IfStatement import net.sergeych.lyng.IfStatement
import net.sergeych.lyng.ParsedArgument import net.sergeych.lyng.ParsedArgument
@ -69,6 +70,7 @@ class BytecodeCompiler(
val continueLabel: CmdBuilder.Label, val continueLabel: CmdBuilder.Label,
val breakFlagSlot: Int, val breakFlagSlot: Int,
val resultSlot: Int?, val resultSlot: Int?,
val hasIterator: Boolean,
) )
fun compileStatement(name: String, stmt: net.sergeych.lyng.Statement): CmdFunction? { fun compileStatement(name: String, stmt: net.sergeych.lyng.Statement): CmdFunction? {
@ -1973,6 +1975,7 @@ class BytecodeCompiler(
} }
is BlockStatement -> emitBlock(target, true) is BlockStatement -> emitBlock(target, true)
is VarDeclStatement -> emitVarDecl(target) is VarDeclStatement -> emitVarDecl(target)
is DestructuringVarDeclStatement -> emitStatementEval(target)
is net.sergeych.lyng.ExtensionPropertyDeclStatement -> emitExtensionPropertyDecl(target) is net.sergeych.lyng.ExtensionPropertyDeclStatement -> emitExtensionPropertyDecl(target)
is net.sergeych.lyng.ClassDeclStatement -> emitStatementEval(target) is net.sergeych.lyng.ClassDeclStatement -> emitStatementEval(target)
is net.sergeych.lyng.FunctionDeclStatement -> emitStatementEval(target) is net.sergeych.lyng.FunctionDeclStatement -> emitStatementEval(target)
@ -2018,6 +2021,7 @@ class BytecodeCompiler(
} }
is BlockStatement -> emitBlock(target, false) is BlockStatement -> emitBlock(target, false)
is VarDeclStatement -> emitVarDecl(target) is VarDeclStatement -> emitVarDecl(target)
is DestructuringVarDeclStatement -> emitStatementEval(target)
is net.sergeych.lyng.ExtensionPropertyDeclStatement -> emitExtensionPropertyDecl(target) is net.sergeych.lyng.ExtensionPropertyDeclStatement -> emitExtensionPropertyDecl(target)
is net.sergeych.lyng.BreakStatement -> compileBreak(target) is net.sergeych.lyng.BreakStatement -> compileBreak(target)
is net.sergeych.lyng.ContinueStatement -> compileContinue(target) is net.sergeych.lyng.ContinueStatement -> compileContinue(target)
@ -2202,6 +2206,7 @@ class BytecodeCompiler(
val iterSlot = allocSlot() val iterSlot = allocSlot()
val iteratorId = builder.addConst(BytecodeConst.StringVal("iterator")) val iteratorId = builder.addConst(BytecodeConst.StringVal("iterator"))
builder.emit(Opcode.CALL_VIRTUAL, sourceObj.slot, iteratorId, 0, 0, iterSlot) builder.emit(Opcode.CALL_VIRTUAL, sourceObj.slot, iteratorId, 0, 0, iterSlot)
builder.emit(Opcode.ITER_PUSH, iterSlot)
val breakFlagSlot = allocSlot() val breakFlagSlot = allocSlot()
val falseId = builder.addConst(BytecodeConst.Bool(false)) val falseId = builder.addConst(BytecodeConst.Bool(false))
@ -2235,7 +2240,14 @@ class BytecodeCompiler(
updateSlotTypeByName(stmt.loopVarName, SlotType.OBJ) updateSlotTypeByName(stmt.loopVarName, SlotType.OBJ)
loopStack.addLast( loopStack.addLast(
LoopContext(stmt.label, endLabel, continueLabel, breakFlagSlot, if (wantResult) resultSlot else null) LoopContext(
stmt.label,
endLabel,
continueLabel,
breakFlagSlot,
if (wantResult) resultSlot else null,
hasIterator = true
)
) )
val bodyValue = compileLoopBody(stmt.body, wantResult) ?: return null val bodyValue = compileLoopBody(stmt.body, wantResult) ?: return null
loopStack.removeLast() loopStack.removeLast()
@ -2247,6 +2259,13 @@ class BytecodeCompiler(
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(loopLabel))) builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(loopLabel)))
builder.mark(endLabel) builder.mark(endLabel)
val afterPop = builder.label()
builder.emit(
Opcode.JMP_IF_TRUE,
listOf(CmdBuilder.Operand.IntVal(breakFlagSlot), CmdBuilder.Operand.LabelRef(afterPop))
)
builder.emit(Opcode.ITER_POP)
builder.mark(afterPop)
if (stmt.elseStatement != null) { if (stmt.elseStatement != null) {
val afterElse = builder.label() val afterElse = builder.label()
builder.emit( builder.emit(
@ -2316,7 +2335,14 @@ class BytecodeCompiler(
updateSlotType(loopSlotId, SlotType.INT) updateSlotType(loopSlotId, SlotType.INT)
updateSlotTypeByName(stmt.loopVarName, SlotType.INT) updateSlotTypeByName(stmt.loopVarName, SlotType.INT)
loopStack.addLast( loopStack.addLast(
LoopContext(stmt.label, endLabel, continueLabel, breakFlagSlot, if (wantResult) resultSlot else null) LoopContext(
stmt.label,
endLabel,
continueLabel,
breakFlagSlot,
if (wantResult) resultSlot else null,
hasIterator = false
)
) )
val bodyValue = compileLoopBody(stmt.body, wantResult) ?: return null val bodyValue = compileLoopBody(stmt.body, wantResult) ?: return null
loopStack.removeLast() loopStack.removeLast()
@ -2375,7 +2401,14 @@ class BytecodeCompiler(
updateSlotType(loopSlotId, SlotType.INT) updateSlotType(loopSlotId, SlotType.INT)
updateSlotTypeByName(stmt.loopVarName, SlotType.INT) updateSlotTypeByName(stmt.loopVarName, SlotType.INT)
loopStack.addLast( loopStack.addLast(
LoopContext(stmt.label, endLabel, continueLabel, breakFlagSlot, if (wantResult) resultSlot else null) LoopContext(
stmt.label,
endLabel,
continueLabel,
breakFlagSlot,
if (wantResult) resultSlot else null,
hasIterator = false
)
) )
val bodyValue = compileLoopBody(stmt.body, wantResult) ?: return null val bodyValue = compileLoopBody(stmt.body, wantResult) ?: return null
loopStack.removeLast() loopStack.removeLast()
@ -2429,7 +2462,14 @@ class BytecodeCompiler(
listOf(CmdBuilder.Operand.IntVal(condition.slot), CmdBuilder.Operand.LabelRef(endLabel)) listOf(CmdBuilder.Operand.IntVal(condition.slot), CmdBuilder.Operand.LabelRef(endLabel))
) )
loopStack.addLast( loopStack.addLast(
LoopContext(stmt.label, endLabel, continueLabel, breakFlagSlot, if (wantResult) resultSlot else null) LoopContext(
stmt.label,
endLabel,
continueLabel,
breakFlagSlot,
if (wantResult) resultSlot else null,
hasIterator = false
)
) )
val bodyValue = compileLoopBody(stmt.body, wantResult) ?: return null val bodyValue = compileLoopBody(stmt.body, wantResult) ?: return null
loopStack.removeLast() loopStack.removeLast()
@ -2471,7 +2511,14 @@ class BytecodeCompiler(
val endLabel = builder.label() val endLabel = builder.label()
builder.mark(loopLabel) builder.mark(loopLabel)
loopStack.addLast( loopStack.addLast(
LoopContext(stmt.label, endLabel, continueLabel, breakFlagSlot, if (wantResult) resultSlot else null) LoopContext(
stmt.label,
endLabel,
continueLabel,
breakFlagSlot,
if (wantResult) resultSlot else null,
hasIterator = false
)
) )
val bodyValue = compileStatementValueOrFallback(stmt.body, wantResult) ?: return null val bodyValue = compileStatementValueOrFallback(stmt.body, wantResult) ?: return null
loopStack.removeLast() loopStack.removeLast()
@ -2577,17 +2624,28 @@ class BytecodeCompiler(
} }
} }
private fun findLoopContext(label: String?): LoopContext? { private fun findLoopContextIndex(label: String?): Int? {
if (loopStack.isEmpty()) return null if (loopStack.isEmpty()) return null
if (label == null) return loopStack.last() val stack = loopStack.toList()
for (ctx in loopStack.reversed()) { if (label == null) return stack.lastIndex
if (ctx.label == label) return ctx for (i in stack.indices.reversed()) {
if (stack[i].label == label) return i
} }
return null return null
} }
private fun emitIteratorCancel(stack: List<LoopContext>, startIndex: Int) {
for (i in stack.lastIndex downTo startIndex) {
if (stack[i].hasIterator) {
builder.emit(Opcode.ITER_CANCEL)
}
}
}
private fun compileBreak(stmt: net.sergeych.lyng.BreakStatement): CompiledValue? { private fun compileBreak(stmt: net.sergeych.lyng.BreakStatement): CompiledValue? {
val ctx = findLoopContext(stmt.label) ?: return null val stack = loopStack.toList()
val targetIndex = findLoopContextIndex(stmt.label) ?: return null
val ctx = stack[targetIndex]
val value = stmt.resultExpr?.let { compileStatementValueOrFallback(it) } val value = stmt.resultExpr?.let { compileStatementValueOrFallback(it) }
if (ctx.resultSlot != null) { if (ctx.resultSlot != null) {
val objValue = value?.let { ensureObjSlot(it) } ?: run { val objValue = value?.let { ensureObjSlot(it) } ?: run {
@ -2601,6 +2659,7 @@ class BytecodeCompiler(
} else if (value != null) { } else if (value != null) {
ensureObjSlot(value) ensureObjSlot(value)
} }
emitIteratorCancel(stack, targetIndex)
val trueId = builder.addConst(BytecodeConst.Bool(true)) val trueId = builder.addConst(BytecodeConst.Bool(true))
builder.emit(Opcode.CONST_BOOL, trueId, ctx.breakFlagSlot) builder.emit(Opcode.CONST_BOOL, trueId, ctx.breakFlagSlot)
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(ctx.breakLabel))) builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(ctx.breakLabel)))
@ -2608,7 +2667,12 @@ class BytecodeCompiler(
} }
private fun compileContinue(stmt: net.sergeych.lyng.ContinueStatement): CompiledValue? { private fun compileContinue(stmt: net.sergeych.lyng.ContinueStatement): CompiledValue? {
val ctx = findLoopContext(stmt.label) ?: return null val stack = loopStack.toList()
val targetIndex = findLoopContextIndex(stmt.label) ?: return null
val ctx = stack[targetIndex]
if (targetIndex < stack.lastIndex) {
emitIteratorCancel(stack, targetIndex + 1)
}
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(ctx.continueLabel))) builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(ctx.continueLabel)))
return CompiledValue(ctx.breakFlagSlot, SlotType.BOOL) return CompiledValue(ctx.breakFlagSlot, SlotType.BOOL)
} }

View File

@ -19,6 +19,7 @@ package net.sergeych.lyng.bytecode
import net.sergeych.lyng.Pos import net.sergeych.lyng.Pos
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.Statement import net.sergeych.lyng.Statement
import net.sergeych.lyng.DestructuringVarDeclStatement
import net.sergeych.lyng.WhenCase import net.sergeych.lyng.WhenCase
import net.sergeych.lyng.WhenCondition import net.sergeych.lyng.WhenCondition
import net.sergeych.lyng.WhenEqualsCondition import net.sergeych.lyng.WhenEqualsCondition
@ -100,6 +101,8 @@ class BytecodeStatement private constructor(
target.statements().any { containsUnsupportedStatement(it) } target.statements().any { containsUnsupportedStatement(it) }
is net.sergeych.lyng.VarDeclStatement -> is net.sergeych.lyng.VarDeclStatement ->
target.initializer?.let { containsUnsupportedStatement(it) } ?: false target.initializer?.let { containsUnsupportedStatement(it) } ?: false
is net.sergeych.lyng.DestructuringVarDeclStatement ->
containsUnsupportedStatement(target.initializer)
is net.sergeych.lyng.BreakStatement -> is net.sergeych.lyng.BreakStatement ->
target.resultExpr?.let { containsUnsupportedStatement(it) } ?: false target.resultExpr?.let { containsUnsupportedStatement(it) } ?: false
is net.sergeych.lyng.ContinueStatement -> false is net.sergeych.lyng.ContinueStatement -> false
@ -147,6 +150,17 @@ class BytecodeStatement private constructor(
stmt.pos stmt.pos
) )
} }
is net.sergeych.lyng.DestructuringVarDeclStatement -> {
net.sergeych.lyng.DestructuringVarDeclStatement(
stmt.pattern,
stmt.names,
unwrapDeep(stmt.initializer),
stmt.isMutable,
stmt.visibility,
stmt.isTransient,
stmt.pos
)
}
is net.sergeych.lyng.IfStatement -> { is net.sergeych.lyng.IfStatement -> {
net.sergeych.lyng.IfStatement( net.sergeych.lyng.IfStatement(
unwrapDeep(stmt.condition), unwrapDeep(stmt.condition),

View File

@ -192,6 +192,10 @@ class CmdBuilder {
listOf(OperandKind.ID, OperandKind.SLOT) listOf(OperandKind.ID, OperandKind.SLOT)
Opcode.EVAL_FALLBACK, Opcode.EVAL_REF, Opcode.EVAL_STMT, Opcode.EVAL_VALUE_FN -> Opcode.EVAL_FALLBACK, Opcode.EVAL_REF, Opcode.EVAL_STMT, Opcode.EVAL_VALUE_FN ->
listOf(OperandKind.ID, OperandKind.SLOT) listOf(OperandKind.ID, OperandKind.SLOT)
Opcode.ITER_PUSH ->
listOf(OperandKind.SLOT)
Opcode.ITER_POP, Opcode.ITER_CANCEL ->
emptyList()
} }
} }
@ -387,6 +391,9 @@ class CmdBuilder {
Opcode.EVAL_REF -> CmdEvalRef(operands[0], operands[1]) Opcode.EVAL_REF -> CmdEvalRef(operands[0], operands[1])
Opcode.EVAL_STMT -> CmdEvalStmt(operands[0], operands[1]) Opcode.EVAL_STMT -> CmdEvalStmt(operands[0], operands[1])
Opcode.EVAL_VALUE_FN -> CmdEvalValueFn(operands[0], operands[1]) Opcode.EVAL_VALUE_FN -> CmdEvalValueFn(operands[0], operands[1])
Opcode.ITER_PUSH -> CmdIterPush(operands[0])
Opcode.ITER_POP -> CmdIterPop()
Opcode.ITER_CANCEL -> CmdIterCancel()
} }
} }
} }

View File

@ -191,6 +191,9 @@ object CmdDisassembler {
is CmdEvalRef -> Opcode.EVAL_REF to intArrayOf(cmd.id, cmd.dst) is CmdEvalRef -> Opcode.EVAL_REF to intArrayOf(cmd.id, cmd.dst)
is CmdEvalStmt -> Opcode.EVAL_STMT to intArrayOf(cmd.id, cmd.dst) is CmdEvalStmt -> Opcode.EVAL_STMT to intArrayOf(cmd.id, cmd.dst)
is CmdEvalValueFn -> Opcode.EVAL_VALUE_FN to intArrayOf(cmd.id, cmd.dst) is CmdEvalValueFn -> Opcode.EVAL_VALUE_FN to intArrayOf(cmd.id, cmd.dst)
is CmdIterPush -> Opcode.ITER_PUSH to intArrayOf(cmd.iterSlot)
is CmdIterPop -> Opcode.ITER_POP to intArrayOf()
is CmdIterCancel -> Opcode.ITER_CANCEL to intArrayOf()
} }
} }
@ -205,7 +208,8 @@ object CmdDisassembler {
private fun operandKinds(op: Opcode): List<OperandKind> { private fun operandKinds(op: Opcode): List<OperandKind> {
return when (op) { return when (op) {
Opcode.NOP, Opcode.RET_VOID, Opcode.POP_SCOPE, Opcode.POP_SLOT_PLAN -> emptyList() Opcode.NOP, Opcode.RET_VOID, Opcode.POP_SCOPE, Opcode.POP_SLOT_PLAN,
Opcode.ITER_POP, Opcode.ITER_CANCEL -> emptyList()
Opcode.MOVE_OBJ, Opcode.MOVE_INT, Opcode.MOVE_REAL, Opcode.MOVE_BOOL, Opcode.BOX_OBJ, Opcode.MOVE_OBJ, Opcode.MOVE_INT, Opcode.MOVE_REAL, Opcode.MOVE_BOOL, Opcode.BOX_OBJ,
Opcode.INT_TO_REAL, Opcode.REAL_TO_INT, Opcode.BOOL_TO_INT, Opcode.INT_TO_BOOL, Opcode.INT_TO_REAL, Opcode.REAL_TO_INT, Opcode.BOOL_TO_INT, Opcode.INT_TO_BOOL,
Opcode.NEG_INT, Opcode.NEG_REAL, Opcode.NOT_BOOL, Opcode.INV_INT -> Opcode.NEG_INT, Opcode.NEG_REAL, Opcode.NOT_BOOL, Opcode.INV_INT ->
@ -248,7 +252,7 @@ object CmdDisassembler {
Opcode.ADD_OBJ, Opcode.SUB_OBJ, Opcode.MUL_OBJ, Opcode.DIV_OBJ, Opcode.MOD_OBJ, Opcode.CONTAINS_OBJ, Opcode.ADD_OBJ, Opcode.SUB_OBJ, Opcode.MUL_OBJ, Opcode.DIV_OBJ, Opcode.MOD_OBJ, Opcode.CONTAINS_OBJ,
Opcode.AND_BOOL, Opcode.OR_BOOL -> Opcode.AND_BOOL, Opcode.OR_BOOL ->
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT) listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
Opcode.INC_INT, Opcode.DEC_INT, Opcode.RET -> Opcode.INC_INT, Opcode.DEC_INT, Opcode.RET, Opcode.ITER_PUSH ->
listOf(OperandKind.SLOT) listOf(OperandKind.SLOT)
Opcode.JMP -> Opcode.JMP ->
listOf(OperandKind.IP) listOf(OperandKind.IP)

View File

@ -32,11 +32,17 @@ class CmdVm {
result = null result = null
val frame = CmdFrame(this, fn, scope0, args) val frame = CmdFrame(this, fn, scope0, args)
val cmds = fn.cmds val cmds = fn.cmds
try {
while (result == null) { while (result == null) {
val cmd = cmds[frame.ip] val cmd = cmds[frame.ip]
frame.ip += 1 frame.ip += 1
cmd.perform(frame) cmd.perform(frame)
} }
} catch (e: Throwable) {
frame.cancelIterators()
throw e
}
frame.cancelIterators()
return result ?: ObjVoid return result ?: ObjVoid
} }
} }
@ -1419,6 +1425,27 @@ class CmdEvalValueFn(internal val id: Int, internal val dst: Int) : Cmd() {
} }
} }
class CmdIterPush(internal val iterSlot: Int) : Cmd() {
override suspend fun perform(frame: CmdFrame) {
frame.pushIterator(frame.slotToObj(iterSlot))
return
}
}
class CmdIterPop : Cmd() {
override suspend fun perform(frame: CmdFrame) {
frame.popIterator()
return
}
}
class CmdIterCancel : Cmd() {
override suspend fun perform(frame: CmdFrame) {
frame.cancelTopIterator()
return
}
}
class CmdFrame( class CmdFrame(
val vm: CmdVm, val vm: CmdVm,
val fn: CmdFunction, val fn: CmdFunction,
@ -1440,6 +1467,7 @@ class CmdFrame(
internal val slotPlanScopeStack = ArrayDeque<Boolean>() internal val slotPlanScopeStack = ArrayDeque<Boolean>()
private var scopeDepth = 0 private var scopeDepth = 0
private var virtualDepth = 0 private var virtualDepth = 0
private val iterStack = ArrayDeque<Obj>()
internal val frame = BytecodeFrame(fn.localCount, args.size) internal val frame = BytecodeFrame(fn.localCount, args.size)
private val addrScopes: Array<Scope?> = arrayOfNulls(fn.addrCount) private val addrScopes: Array<Scope?> = arrayOfNulls(fn.addrCount)
@ -1484,6 +1512,26 @@ class CmdFrame(
scopeDepth -= 1 scopeDepth -= 1
} }
fun pushIterator(iter: Obj) {
iterStack.addLast(iter)
}
fun popIterator() {
iterStack.removeLastOrNull()
}
suspend fun cancelTopIterator() {
val iter = iterStack.removeLastOrNull() ?: return
iter.invokeInstanceMethod(scope, "cancelIteration") { ObjVoid }
}
suspend fun cancelIterators() {
while (iterStack.isNotEmpty()) {
val iter = iterStack.removeLast()
iter.invokeInstanceMethod(scope, "cancelIteration") { ObjVoid }
}
}
fun pushSlotPlan(plan: Map<String, Int>) { fun pushSlotPlan(plan: Map<String, Int>) {
if (scope.hasSlotPlanConflict(plan)) { if (scope.hasSlotPlanConflict(plan)) {
scopeStack.addLast(scope) scopeStack.addLast(scope)

View File

@ -149,6 +149,9 @@ enum class Opcode(val code: Int) {
EVAL_REF(0xBC), EVAL_REF(0xBC),
EVAL_STMT(0xBD), EVAL_STMT(0xBD),
EVAL_VALUE_FN(0xBE), EVAL_VALUE_FN(0xBE),
ITER_PUSH(0xBF),
ITER_POP(0xC0),
ITER_CANCEL(0xC1),
; ;
companion object { companion object {

View File

@ -186,7 +186,6 @@ class ScriptTest {
assertEquals(0, ti.cancelCount) assertEquals(0, ti.cancelCount)
} }
@Ignore("Bytecode: unsupported or incorrect behavior")
@Test @Test
fun testForLoopCancelsOnBreak() = runTest { fun testForLoopCancelsOnBreak() = runTest {
val scope = Script.newScope() val scope = Script.newScope()
@ -202,7 +201,6 @@ class ScriptTest {
assertEquals(1, ti.cancelCount) assertEquals(1, ti.cancelCount)
} }
@Ignore("Bytecode: unsupported or incorrect behavior")
@Test @Test
fun testForLoopCancelsOnException() = runTest { fun testForLoopCancelsOnException() = runTest {
val scope = Script.newScope() val scope = Script.newScope()
@ -4567,7 +4565,6 @@ class ScriptTest {
} }
@Ignore("Bytecode: unsupported or incorrect behavior")
@Test @Test
fun testDestructuringAssignment() = runTest { fun testDestructuringAssignment() = runTest {
eval( eval(