From 28ccbebd527fd29b0307f9e5d6a703f5a46ffaa6 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sun, 20 Sep 2026 12:19:52 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20break/continue=20=E6=90=BA=E5=BD=93?= =?UTF-8?q?=E5=89=8D=E8=AF=AD=E5=8F=A5=E5=88=97=E8=A1=A8=E7=B4=AF=E7=A7=AF?= =?UTF-8?q?=E5=80=BC=E4=B8=94=E5=BE=AA=E7=8E=AF=E5=87=BA=E5=8F=A3=E5=80=BC?= =?UTF-8?q?=E6=8C=89=E8=BF=AD=E4=BB=A3=E5=9B=9E=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_emit/src/compile_ctx.rs | 110 ++++++++++++++++-- crates/oxide_emit/src/emit_ctx.rs | 41 ++++++- .../src/stmt/basic/break_continue.rs | 47 +++++++- crates/oxide_emit/src/stmt/basic/labeled.rs | 60 +++++++++- crates/oxide_emit/src/stmt/block.rs | 5 + crates/oxide_emit/src/stmt/control.rs | 6 + .../src/stmt/exception/try_catch.rs | 17 ++- .../oxide_emit/src/stmt/iteration/do_while.rs | 26 +++-- crates/oxide_emit/src/stmt/iteration/for_.rs | 20 ++-- .../oxide_emit/src/stmt/iteration/for_in.rs | 4 +- .../oxide_emit/src/stmt/iteration/for_of.rs | 8 +- .../src/stmt/iteration/while_loop.rs | 24 ++-- crates/oxide_emit/src/stmt/switch.rs | 28 +++-- crates/oxide_emit/src/stmt/with.rs | 4 + crates/oxide_vm/tests/eval_tests.rs | 95 +++++++++++++++ 15 files changed, 429 insertions(+), 66 deletions(-) diff --git a/crates/oxide_emit/src/compile_ctx.rs b/crates/oxide_emit/src/compile_ctx.rs index 5d77d807..b2ccdca1 100644 --- a/crates/oxide_emit/src/compile_ctx.rs +++ b/crates/oxide_emit/src/compile_ctx.rs @@ -8,10 +8,12 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use oxide_bytecode::module::{Constant, UpvalueCapture}; use oxide_ir::inst::Inst; -use oxide_ir::operand::LabelId; +use oxide_ir::operand::{LabelId, Operand}; use oxide_ir::IRFunction; -use crate::emit_ctx::{LabelCtx, LabelScope, LoopEntry, LoopKind, ScopeCtx}; +use crate::emit_ctx::{ + CompletionCarry, CompletionFrame, LabelCtx, LabelScope, LoopEntry, LoopKind, ScopeCtx, SwitchEntry, +}; use crate::program::{BUILTIN_GLOBALS, NON_WRITABLE_GLOBAL_BUILTINS}; use crate::symbol_table::{ScopeKind, SymbolTable}; use oxide_parser::VariableDeclarationKind; @@ -126,6 +128,11 @@ pub struct CompileCtx { /// try_stack 组成。每项标记是否为纯 catch handler(TRY_BEGIN,由 TRY_END /// 弹出):return 逃出 try 域时据此弹出栈顶连续纯 catch,防 handler 泄漏。 pub(crate) open_try_handlers: Vec, + /// 完成值帧栈(见 `CompletionFrame`):每个活动语句列表独立累积、if 支臂 / + /// with 体压边界帧、循环 / switch / 非迭代标签压出口目标帧;break/continue + /// 发射跳转前据此解析携值。压弹须在所有非 Err 路径配对(Err 直接终止本次 + /// 编译、ctx 丢弃,不做 RAII)。 + pub(crate) completion_frames: Vec, /// 循环 update 段中应走寄存器(而非 cell)的被捕获绑定名:C 风格 for 的 /// let/const 循环变量每迭代新分配一个 cell,update 写寄存器(不污染本迭代 /// 闭包捕获的 cell),下一迭代把这个寄存器值拷入新分配的 cell。 @@ -246,6 +253,7 @@ impl CompileCtx { const_overflow: false, with_stack: Vec::new(), open_try_handlers: Vec::new(), + completion_frames: Vec::new(), register_update_names: Vec::new(), for_head_store_registers: HashSet::new(), module_ns_reg: None, @@ -425,7 +433,11 @@ impl CompileCtx { id } - pub(crate) fn push_loop(&mut self, break_label: LabelId, continue_label: LabelId, kind: LoopKind) { + /// 打开一个循环并返回其出口结果寄存器:就地分配并初始化为 undefined + /// (覆盖零迭代路径,缺初始化则 `for(;;false;)` 形读垃圾泄漏前值)。 + /// 调用点须先于循环头标签落位:初始化指令随调用点发射,落在回边内会每 + /// 迭代重置出口值。 + pub(crate) fn push_loop(&mut self, break_label: LabelId, continue_label: LabelId, kind: LoopKind) -> u32 { let fd = self.labels.finally_depth; // 深度计数先递增再快照:条目记录"打开后(含自身)"的深度,与 // take_pending_loop_labels 的标签作用域快照一致,逃出计数才能对齐。 @@ -437,6 +449,9 @@ impl CompileCtx { } let fod = self.labels.for_of_depth; let fid = self.labels.for_in_depth; + let v_reg = self.alloc_reg(); + let undef_idx = self.add_constant(Constant::Undefined); + self.inst(Inst::load_const(Operand::Reg(v_reg), undef_idx)); self.labels.loop_stack.push(LoopEntry { break_label, continue_label, @@ -444,7 +459,9 @@ impl CompileCtx { for_of_depth_at_open: fod, for_in_depth_at_open: fid, kind, + v_reg, }); + v_reg } pub(crate) fn pop_loop(&mut self) { @@ -462,21 +479,90 @@ impl CompileCtx { self.labels.loop_stack.last() } - pub(crate) fn push_switch(&mut self, break_label: LabelId) { + pub(crate) fn push_switch(&mut self, break_label: LabelId, result_reg: u32) { let fd = self.labels.finally_depth; let fod = self.labels.for_of_depth; let fid = self.labels.for_in_depth; - self.labels.switch_stack.push((break_label, fd, fod, fid)); + self.labels.switch_stack.push(SwitchEntry { + break_label, + finally_depth_at_open: fd, + for_of_depth_at_open: fod, + for_in_depth_at_open: fid, + result_reg, + }); } pub(crate) fn pop_switch(&mut self) { self.labels.switch_stack.pop(); } - pub(crate) fn current_switch(&self) -> Option<&(LabelId, usize, usize, usize)> { + pub(crate) fn current_switch(&self) -> Option<&SwitchEntry> { self.labels.switch_stack.last() } + /// 压一个语句列表累积帧:列表内每条非空语句经 `set_completion_last` 记 + /// 最后非空值寄存器(空语句不覆写,规范 `UpdateEmpty` 的「非空才覆写」)。 + pub(crate) fn push_completion_list(&mut self) { + self.completion_frames.push(CompletionFrame::List { last: None }); + } + + pub(crate) fn pop_completion_list(&mut self) { + debug_assert!(matches!(self.completion_frames.last(), Some(CompletionFrame::List { .. }))); + self.completion_frames.pop(); + } + + /// 记栈顶列表帧的最后非空语句寄存器(调用点须刚压过列表帧)。 + pub(crate) fn set_completion_last(&mut self, reg: u32) { + if let CompletionFrame::List { last } = self.completion_frames.last_mut().expect("完成值帧栈顶为语句列表帧") + { + *last = Some(reg); + } + } + + /// 压一个边界帧(`UpdateEmpty(_, undefined)` 站点:if 支臂 / with 体): + /// break/continue 携值解析撞它物化 undefined。 + pub(crate) fn push_completion_boundary(&mut self) { + self.completion_frames.push(CompletionFrame::Boundary); + } + + pub(crate) fn pop_completion_boundary(&mut self) { + debug_assert!(matches!(self.completion_frames.last(), Some(CompletionFrame::Boundary))); + self.completion_frames.pop(); + } + + /// 压一个出口目标帧:break/continue 携值写入该寄存器,空携值保持其原值。 + pub(crate) fn push_completion_target(&mut self, v_reg: u32) { + self.completion_frames.push(CompletionFrame::Target { v_reg }); + } + + pub(crate) fn pop_completion_target(&mut self) { + debug_assert!(matches!(self.completion_frames.last(), Some(CompletionFrame::Target { .. }))); + self.completion_frames.pop(); + } + + /// 解析 break/continue 的携值:自栈顶向目标帧走,首个非空列表帧取 + /// `Value(r)`,撞边界帧取 `Undefined`,首个目标帧——与 `target_v_reg` + /// 相等(本跳转自身目标)取 `Empty`(不写,目标寄存器保持既有累积值, + /// 规范 `UpdateEmpty(break, iterationResult)` 等义),为内层非目标帧 + /// (labeled break 穿内层循环)取 `Value(该帧寄存器)`。 + pub(crate) fn resolve_completion_carry(&self, target_v_reg: u32) -> CompletionCarry { + for frame in self.completion_frames.iter().rev() { + match frame { + CompletionFrame::List { last: Some(r) } => return CompletionCarry::Value(*r), + CompletionFrame::List { last: None } => {} + CompletionFrame::Boundary => return CompletionCarry::Undefined, + CompletionFrame::Target { v_reg } => { + return if *v_reg == target_v_reg { + CompletionCarry::Empty + } else { + CompletionCarry::Value(*v_reg) + }; + } + } + } + CompletionCarry::Empty + } + /// 进入/离开一个 try/finally 域:break/continue 跨越 finally 计数用。 pub(crate) fn push_finally_domain(&mut self) { self.labels.finally_depth += 1; @@ -510,7 +596,7 @@ impl CompileCtx { } pub(crate) fn push_label_scope( - &mut self, name: &str, break_label: LabelId, continue_label: Option, + &mut self, name: &str, break_label: LabelId, continue_label: Option, completion_reg: Option, ) -> Result<(), String> { if self.labels.label_scopes.iter().any(|s| s.name == name) { return Err(format!("SyntaxError: Label '{name}' has already been declared")); @@ -525,6 +611,7 @@ impl CompileCtx { finally_depth_at_open: fd, for_of_depth_at_open: fod, for_in_depth_at_open: fid, + completion_reg, }); Ok(()) } @@ -549,9 +636,11 @@ impl CompileCtx { Ok(()) } - /// 把待绑定标签名落地为活动标签作用域,绑定到本次循环的 break/continue 目标。 - /// 返回压入的作用域个数(供事后对称弹出)。 - pub(crate) fn take_pending_loop_labels(&mut self, break_label: LabelId, continue_label: LabelId) -> usize { + /// 把待绑定标签名落地为活动标签作用域,绑定到本次循环的 break/continue 目标 + /// 与出口结果寄存器。返回压入的作用域个数(供事后对称弹出)。 + pub(crate) fn take_pending_loop_labels( + &mut self, break_label: LabelId, continue_label: LabelId, completion_reg: u32, + ) -> usize { let names = std::mem::take(&mut self.labels.pending_loop_labels); let count = names.len(); let fd = self.labels.finally_depth; @@ -565,6 +654,7 @@ impl CompileCtx { finally_depth_at_open: fd, for_of_depth_at_open: fod, for_in_depth_at_open: fid, + completion_reg: Some(completion_reg), }); } count diff --git a/crates/oxide_emit/src/emit_ctx.rs b/crates/oxide_emit/src/emit_ctx.rs index 574cc24c..9a910943 100644 --- a/crates/oxide_emit/src/emit_ctx.rs +++ b/crates/oxide_emit/src/emit_ctx.rs @@ -45,6 +45,29 @@ pub(crate) struct LoopEntry { /// 循环打开时已打开的 for-in 循环数。 pub(crate) for_in_depth_at_open: usize, pub(crate) kind: LoopKind, + /// 循环出口结果寄存器:入口初始 undefined,体正常完成回写,break/continue + /// 携值写入(空携值不写,保持前次累积值)。 + pub(crate) v_reg: u32, +} + +/// 完成值帧:编译期帧栈元素,承载「每语句列表独立累积」语义。 +/// `List` 的 `last` 记源序最后非空语句的寄存器(空语句不覆写);`Boundary` +/// 是 `UpdateEmpty(_, undefined)` 站点(if 支臂 / with 体),携值解析撞它物化 +/// undefined;`Target` 是循环 / switch / 非迭代标签的出口结果寄存器。 +#[derive(Debug, Clone, Copy)] +pub(crate) enum CompletionFrame { + List { last: Option }, + Boundary, + Target { v_reg: u32 }, +} + +/// break/continue 的携值:`Value(r)` 拷贝寄存器 `r`,`Undefined` 物化 undefined, +/// `Empty` 不写(出口目标寄存器保持既有累积值)。 +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum CompletionCarry { + Value(u32), + Undefined, + Empty, } /// 标签语句作用域:编译期内登记 `break label` / `continue label` 的跳转目标。 @@ -60,6 +83,20 @@ pub struct LabelScope { pub(crate) for_of_depth_at_open: usize, /// 标签打开时已打开的 for-in 循环数。 pub(crate) for_in_depth_at_open: usize, + /// 标签目标出口结果寄存器:迭代标签取所包裹循环的 `v_reg`;非迭代标签仅当 + /// 体含指向本标签的 break 时分配(体正常完成回写、break 携值写入)。 + pub(crate) completion_reg: Option, +} + +/// switch 打开时的词法快照:switch 内 break 的逃出计数以打开点为准,switch 之前 +/// 已打开的循环不属于本次逃出;另记 CaseBlock 出口结果寄存器(`resultValue` 累积槽)。 +#[derive(Debug, Clone, Copy)] +pub(crate) struct SwitchEntry { + pub(crate) break_label: LabelId, + pub(crate) finally_depth_at_open: usize, + pub(crate) for_of_depth_at_open: usize, + pub(crate) for_in_depth_at_open: usize, + pub(crate) result_reg: u32, } /// 跳转目标 / 标签语句解析状态。 @@ -68,10 +105,10 @@ pub(crate) struct LabelCtx { pub(crate) label_pos: Vec>, /// 每个条目记录循环打开时的词法快照(finally/for-of/for-in 深度)。 pub(crate) loop_stack: Vec, - /// 每个条目记录 switch 打开时的词法快照(finally/for-of/for-in 深度): + /// 每个条目记录 switch 打开时的词法快照与 CaseBlock 出口结果寄存器: /// switch 内 break 的逃出计数以打开点为准,switch 之前已打开的迭代器 /// 不属于本次逃出。 - pub(crate) switch_stack: Vec<(LabelId, usize, usize, usize)>, + pub(crate) switch_stack: Vec, /// 活动标签语句作用域(解析 `break label` / `continue label`)。 pub(crate) label_scopes: Vec, /// 等待绑定到下一个循环 continue 目标的标签名。 diff --git a/crates/oxide_emit/src/stmt/basic/break_continue.rs b/crates/oxide_emit/src/stmt/basic/break_continue.rs index ddd22dd5..51dd5e48 100644 --- a/crates/oxide_emit/src/stmt/basic/break_continue.rs +++ b/crates/oxide_emit/src/stmt/basic/break_continue.rs @@ -1,18 +1,39 @@ -//! break/continue 语句 emit:解析标签与最近循环的跳转目标。 +//! break/continue 语句 emit:解析标签与最近循环的跳转目标,发射前按完成值帧 +//! 栈解析携值并写入出口目标寄存器。 //! //! 计算 break/continue 逃出的 finally 域数(目标打开时的 finally 深度与当前深度之差) //! 与逃出的迭代器层数(for-of/for-in 深度之差,目标循环自身的关闭由 CLOSE / //! FOR_IN_CLEANUP 处理,不计入):逃出 ≥1 个 finally 域或 ≥1 层迭代器 → 发 //! BREAK/CONTINUE(运行时穿越 finally、关闭逃出迭代器后再跳转);否则发普通 JMP。 +use crate::emit_ctx::CompletionCarry; use crate::{CompileCtx, Emitter}; +use oxide_bytecode::opcode::OpCode; use oxide_ir::inst::Inst; +use oxide_ir::operand::Operand; impl Emitter { + /// break/continue 的携值写入:自完成值帧栈顶解析携值,非空携值在跳转前 + /// 写入出口目标寄存器——`Value(r)` 拷寄存器、`Undefined` 物化 undefined、 + /// `Empty` 不写(目标寄存器保持既有累积值,规范 + /// `UpdateEmpty(break, iterationResult)` 等义)。 + fn emit_completion_carry(&self, ctx: &mut CompileCtx, target_v_reg: u32) { + match ctx.resolve_completion_carry(target_v_reg) { + CompletionCarry::Value(r) => { + ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(target_v_reg), Operand::Reg(r), Operand::None)); + } + CompletionCarry::Undefined => { + let u = self.emit_undefined(ctx); + ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(target_v_reg), Operand::Reg(u), Operand::None)); + } + CompletionCarry::Empty => {} + } + } + pub(crate) fn emit_break_statement( &self, stmt: &oxide_parser::BreakStatement, ctx: &mut CompileCtx, ) -> Result, String> { - let (break_label, fd_at_open, fod_at_open, fid_at_open) = if let Some(label) = &stmt.label { + let (break_label, fd_at_open, fod_at_open, fid_at_open, target_v_reg) = if let Some(label) = &stmt.label { let name = label.name.as_str(); let scope = ctx .find_label(name) @@ -22,12 +43,19 @@ impl Emitter { scope.finally_depth_at_open, scope.for_of_depth_at_open, scope.for_in_depth_at_open, + scope.completion_reg, ) - } else if let Some((sw_label, fd, fod, fid)) = ctx.current_switch() { + } else if let Some(sw) = ctx.current_switch() { // switch 内 break:目标为 switch 出口。逃出计数以 switch 打开时的 // 深度快照为基准——switch 之前已打开的循环不属于本次逃出,仅当 // case 内新打开了迭代循环(并逃出)时才需关闭。 - (*sw_label, *fd, *fod, *fid) + ( + sw.break_label, + sw.finally_depth_at_open, + sw.for_of_depth_at_open, + sw.for_in_depth_at_open, + Some(sw.result_reg), + ) } else { let entry = ctx.current_loop().ok_or("break outside switch or loop".to_string())?; ( @@ -35,8 +63,12 @@ impl Emitter { entry.finally_depth_at_open, entry.for_of_depth_at_open, entry.for_in_depth_at_open, + Some(entry.v_reg), ) }; + if let Some(target_v_reg) = target_v_reg { + self.emit_completion_carry(ctx, target_v_reg); + } let crossed = ctx.labels.finally_depth.saturating_sub(fd_at_open) as u16; let (for_of_count, for_in_count) = escape_iter_counts(ctx, fod_at_open, fid_at_open); if crossed > 0 || for_of_count > 0 || for_in_count > 0 { @@ -50,7 +82,7 @@ impl Emitter { pub(crate) fn emit_continue_statement( &self, stmt: &oxide_parser::ContinueStatement, ctx: &mut CompileCtx, ) -> Result, String> { - let (continue_label, fd_at_open, fod_at_open, fid_at_open) = if let Some(label) = &stmt.label { + let (continue_label, fd_at_open, fod_at_open, fid_at_open, target_v_reg) = if let Some(label) = &stmt.label { let name = label.name.as_str(); let scope = ctx .find_label(name) @@ -62,6 +94,7 @@ impl Emitter { scope.finally_depth_at_open, scope.for_of_depth_at_open, scope.for_in_depth_at_open, + scope.completion_reg, ) } else { let entry = ctx.current_loop().ok_or("continue outside loop".to_string())?; @@ -70,8 +103,12 @@ impl Emitter { entry.finally_depth_at_open, entry.for_of_depth_at_open, entry.for_in_depth_at_open, + Some(entry.v_reg), ) }; + if let Some(target_v_reg) = target_v_reg { + self.emit_completion_carry(ctx, target_v_reg); + } let crossed = ctx.labels.finally_depth.saturating_sub(fd_at_open) as u16; let (for_of_count, for_in_count) = escape_iter_counts(ctx, fod_at_open, fid_at_open); if crossed > 0 || for_of_count > 0 || for_in_count > 0 { diff --git a/crates/oxide_emit/src/stmt/basic/labeled.rs b/crates/oxide_emit/src/stmt/basic/labeled.rs index 69f1354c..badb73cc 100644 --- a/crates/oxide_emit/src/stmt/basic/labeled.rs +++ b/crates/oxide_emit/src/stmt/basic/labeled.rs @@ -1,6 +1,10 @@ //! 标签语句 emit:`emit_labeled_statement` 登记 break/continue 标签作用域。 use crate::{CompileCtx, Emitter}; +use oxide_bytecode::module::Constant; +use oxide_bytecode::opcode::OpCode; +use oxide_ir::inst::Inst; +use oxide_ir::operand::Operand; use oxide_parser::Statement; impl Emitter { @@ -15,6 +19,42 @@ impl Emitter { ) } + /// 标签体是否含指向本标签的 `break `:递归走语句树,不下钻嵌套 + /// 函数体(嵌套函数是独立编译单元,其 break 无法指向外层标签)。 + fn body_contains_break_to_label(stmt: &Statement, name: &str) -> bool { + match stmt { + Statement::BreakStatement(b) => b.label.as_ref().is_some_and(|l| l.name.as_str() == name), + Statement::BlockStatement(b) => b.body.iter().any(|s| Self::body_contains_break_to_label(s, name)), + Statement::IfStatement(i) => { + Self::body_contains_break_to_label(&i.consequent, name) + || i.alternate + .as_ref() + .is_some_and(|a| Self::body_contains_break_to_label(a, name)) + } + Statement::WhileStatement(w) => Self::body_contains_break_to_label(&w.body, name), + Statement::DoWhileStatement(d) => Self::body_contains_break_to_label(&d.body, name), + Statement::ForStatement(f) => Self::body_contains_break_to_label(&f.body, name), + Statement::ForInStatement(f) => Self::body_contains_break_to_label(&f.body, name), + Statement::ForOfStatement(f) => Self::body_contains_break_to_label(&f.body, name), + Statement::LabeledStatement(l) => Self::body_contains_break_to_label(&l.body, name), + Statement::SwitchStatement(s) => s + .cases + .iter() + .any(|c| c.consequent.iter().any(|st| Self::body_contains_break_to_label(st, name))), + Statement::TryStatement(t) => { + t.block.body.iter().any(|s| Self::body_contains_break_to_label(s, name)) + || t.handler + .as_ref() + .is_some_and(|h| h.body.body.iter().any(|s| Self::body_contains_break_to_label(s, name))) + || t.finalizer + .as_ref() + .is_some_and(|f| f.body.iter().any(|s| Self::body_contains_break_to_label(s, name))) + } + Statement::WithStatement(w) => Self::body_contains_break_to_label(&w.body, name), + _ => false, + } + } + pub(crate) fn emit_labeled_statement( &self, stmt: &oxide_parser::LabeledStatement, ctx: &mut CompileCtx, ) -> Result, String> { @@ -24,9 +64,27 @@ impl Emitter { let body_result = if Self::is_iteration_statement(&stmt.body) { ctx.queue_loop_label(name)?; self.emit_statement(&stmt.body, ctx)? + } else if Self::body_contains_break_to_label(&stmt.body, name) { + // 体含指向本标签的 break:出口结果寄存器在此分配并初始 undefined, + // 体正常完成回写、break 携值就地写入(break 后死语句的寄存器不得 + // 成为完成值)。 + let v_reg = ctx.alloc_reg(); + let undef_idx = ctx.add_constant(Constant::Undefined); + ctx.inst(Inst::load_const(Operand::Reg(v_reg), undef_idx)); + let id = ctx.next_label_id(); + ctx.push_label_scope(name, id, None, Some(v_reg))?; + ctx.push_completion_target(v_reg); + let r = self.emit_statement(&stmt.body, ctx)?; + if let Some(r) = r { + ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(v_reg), Operand::Reg(r), Operand::None)); + } + ctx.pop_completion_target(); + ctx.labels.set_label_pos(id, ctx.insts.len()); + ctx.pop_label_scope(); + Some(v_reg) } else { let id = ctx.next_label_id(); - ctx.push_label_scope(name, id, None)?; + ctx.push_label_scope(name, id, None, None)?; let r = self.emit_statement(&stmt.body, ctx)?; ctx.labels.set_label_pos(id, ctx.insts.len()); ctx.pop_label_scope(); diff --git a/crates/oxide_emit/src/stmt/block.rs b/crates/oxide_emit/src/stmt/block.rs index 46db1229..afa9bf73 100644 --- a/crates/oxide_emit/src/stmt/block.rs +++ b/crates/oxide_emit/src/stmt/block.rs @@ -23,12 +23,17 @@ impl Emitter { for s in &block.body { self.emit_block_fn_entry_init_stmt(s, ctx)?; } + // 块语句列表独立累积:压列表帧,每条非空语句记最后非空值寄存器, + // break/continue 携值解析据此取块内累积值。 + ctx.push_completion_list(); let mut r = None; for s in &block.body { if let Some(rr) = self.emit_statement(s, ctx)? { r = Some(rr); + ctx.set_completion_last(rr); } } + ctx.pop_completion_list(); ctx.block_fn_entry_mats.pop(); ctx.pop_scope(); Ok(r) diff --git a/crates/oxide_emit/src/stmt/control.rs b/crates/oxide_emit/src/stmt/control.rs index 7d14cd1f..5e673469 100644 --- a/crates/oxide_emit/src/stmt/control.rs +++ b/crates/oxide_emit/src/stmt/control.rs @@ -19,7 +19,11 @@ impl Emitter { ctx.inst(Inst::jmp_if_false(test_reg, else_label)); + // if 支臂是 `UpdateEmpty(_, undefined)` 站点:压边界帧,支臂内空体 + // break/continue 穿越时携值物化 undefined。 + ctx.push_completion_boundary(); let cons_reg = self.emit_statement(&ifs.consequent, ctx)?; + ctx.pop_completion_boundary(); let result_reg = ctx.alloc_reg(); if let Some(r) = cons_reg { ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(r), Operand::None)); @@ -34,7 +38,9 @@ impl Emitter { ctx.labels.set_label_pos(else_label, ctx.insts.len()); if let Some(alt) = &ifs.alternate { + ctx.push_completion_boundary(); let alt_reg = self.emit_statement(alt, ctx)?; + ctx.pop_completion_boundary(); if let Some(r) = alt_reg { ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(r), Operand::None)); } else { diff --git a/crates/oxide_emit/src/stmt/exception/try_catch.rs b/crates/oxide_emit/src/stmt/exception/try_catch.rs index 25bba364..530da2e5 100644 --- a/crates/oxide_emit/src/stmt/exception/try_catch.rs +++ b/crates/oxide_emit/src/stmt/exception/try_catch.rs @@ -43,11 +43,16 @@ impl Emitter { for s in &ts.block.body { self.emit_block_fn_entry_init_stmt(s, ctx)?; } + // try 体语句列表自压列表帧(不经块语句 emit):非空语句记累积, + // 体内 break/continue 携值解析据此取 try 体累积值。 + ctx.push_completion_list(); for s in &ts.block.body { if let Some(r) = self.emit_statement(s, ctx)? { last_try_result = Some(r); + ctx.set_completion_last(r); } } + ctx.pop_completion_list(); ctx.block_fn_entry_mats.pop(); ctx.pop_scope(); ctx.inst(Inst::new( @@ -85,12 +90,16 @@ impl Emitter { for s in &catch.body.body { self.emit_block_fn_entry_init_stmt(s, ctx)?; } + // catch 体语句列表自压列表帧(不经块语句 emit)。 + ctx.push_completion_list(); let mut last_catch_result: Option = None; for s in &catch.body.body { if let Some(r) = self.emit_statement(s, ctx)? { last_catch_result = Some(r); + ctx.set_completion_last(r); } } + ctx.pop_completion_list(); ctx.block_fn_entry_mats.pop(); ctx.inst(Inst::new( OpCode::LOAD_VAR, @@ -119,9 +128,15 @@ impl Emitter { for s in &ts.finalizer.as_ref().unwrap().body { self.emit_block_fn_entry_init_stmt(s, ctx)?; } + // finally 体语句列表自压列表帧:体值本身被丢弃,但体内 break/continue + // 的携值解析须取 finally 体自身累积值(非空体穿透)。 + ctx.push_completion_list(); for s in &ts.finalizer.as_ref().unwrap().body { - self.emit_statement(s, ctx)?; + if let Some(r) = self.emit_statement(s, ctx)? { + ctx.set_completion_last(r); + } } + ctx.pop_completion_list(); ctx.block_fn_entry_mats.pop(); ctx.pop_scope(); ctx.inst(Inst::new(OpCode::TRY_FINALLY_END, Operand::None, Operand::None, Operand::None)); diff --git a/crates/oxide_emit/src/stmt/iteration/do_while.rs b/crates/oxide_emit/src/stmt/iteration/do_while.rs index 9d924a26..4d4d2cda 100644 --- a/crates/oxide_emit/src/stmt/iteration/do_while.rs +++ b/crates/oxide_emit/src/stmt/iteration/do_while.rs @@ -1,7 +1,9 @@ //! do-while 语句 emit:`emit_do_while_statement` 先执行体后测条件。 use crate::{CompileCtx, Emitter}; +use oxide_bytecode::opcode::OpCode; use oxide_ir::inst::Inst; +use oxide_ir::operand::Operand; use oxide_parser::Statement; impl Emitter { @@ -14,24 +16,26 @@ impl Emitter { let start_label = ctx.next_label_id(); let cont_label = ctx.next_label_id(); let end_label = ctx.next_label_id(); - ctx.labels.set_label_pos(start_label, ctx.insts.len()); // continue 目标是条件位置:体先执行,continue 须绕过体直接去求值条件, - // 不能指回体首(否则条件永不被求值)。 - ctx.push_loop(end_label, cont_label, crate::emit_ctx::LoopKind::Plain); - let n_labeled = ctx.take_pending_loop_labels(end_label, cont_label); + // 不能指回体首(否则条件永不被求值)。开环先于循环头标签落位:出口 + // 结果寄存器的 undefined 初始化落在回边外(入口执行一次)。 + let v_reg = ctx.push_loop(end_label, cont_label, crate::emit_ctx::LoopKind::Plain); + let n_labeled = ctx.take_pending_loop_labels(end_label, cont_label, v_reg); + ctx.labels.set_label_pos(start_label, ctx.insts.len()); let body_result = self.emit_statement(&dw.body, ctx)?; + // 体正常完成回写(回写点在 `cont_label` 落位前:体内 continue 绕过本点 + // 直达条件位置,不覆写本迭代累积值)。 + if let Some(r) = body_result { + ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(v_reg), Operand::Reg(r), Operand::None)); + } ctx.labels.set_label_pos(cont_label, ctx.insts.len()); let test_reg = self.emit_expression(&dw.test, ctx)?; ctx.inst(Inst::jmp_if_true(test_reg, start_label)); ctx.labels.set_label_pos(end_label, ctx.insts.len()); ctx.pop_label_scopes(n_labeled); ctx.pop_loop(); - // 循环完成值 = 循环体最后一次非空完成值(体收敛寄存器每迭代覆写);空体 - // 物化 undefined 作为带值完成返回,不沿用循环前的值。 - let result = match body_result { - Some(r) => r, - None => self.emit_undefined(ctx), - }; - Ok(Some(result)) + // 循环完成值 = 出口结果寄存器:入口 undefined、体正常完成每迭代覆写、 + // break/continue 携值写入(空携值不写,保持既有累积值)。 + Ok(Some(v_reg)) } } diff --git a/crates/oxide_emit/src/stmt/iteration/for_.rs b/crates/oxide_emit/src/stmt/iteration/for_.rs index 875e13d7..99da90e9 100644 --- a/crates/oxide_emit/src/stmt/iteration/for_.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_.rs @@ -47,8 +47,8 @@ impl Emitter { let start_label = ctx.next_label_id(); let update_label = ctx.next_label_id(); let end_label = ctx.next_label_id(); - ctx.push_loop(end_label, update_label, crate::emit_ctx::LoopKind::Plain); - let n_labeled = ctx.take_pending_loop_labels(end_label, update_label); + let v_reg = ctx.push_loop(end_label, update_label, crate::emit_ctx::LoopKind::Plain); + let n_labeled = ctx.take_pending_loop_labels(end_label, update_label, v_reg); // 循环头 let/const 声明名:update 段是 per-iteration 可变绑定(规范 // §14.7.4.4 CreatePerIterationEnvironment 用 CreateMutableBinding)—— // 规范允许 update 段写 let/const 循环变量,每迭代新建一个可变绑定; @@ -182,6 +182,12 @@ impl Emitter { ctx.inst(Inst::jmp_if_false(test_reg, end_label)); } let body_result = self.emit_statement(&fr.body, ctx)?; + // 体正常完成回写(回写点在 `update_label` 落位前:体内 continue 目标是 + // update 段,绕过本点不覆写本迭代累积值;update 段写循环变量寄存器, + // 独立于本寄存器)。 + if let Some(r) = body_result { + ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(v_reg), Operand::Reg(r), Operand::None)); + } ctx.labels.set_label_pos(update_label, ctx.insts.len()); if let Some(update) = &fr.update { // update 段写循环变量寄存器、不写 cell:被捕获的 let/const 循环变量每迭代 @@ -203,12 +209,8 @@ impl Emitter { ctx.pop_label_scopes(n_labeled); ctx.pop_loop(); ctx.pop_scope(); - // 循环完成值 = 循环体最后一次非空完成值;空体物化 undefined 作为带值完成 - // 返回,不沿用循环前的值。 - let result = match body_result { - Some(r) => r, - None => self.emit_undefined(ctx), - }; - Ok(Some(result)) + // 循环完成值 = 出口结果寄存器:入口 undefined、体正常完成每迭代覆写、 + // break/continue 携值写入(空携值不写,保持既有累积值)。 + Ok(Some(v_reg)) } } diff --git a/crates/oxide_emit/src/stmt/iteration/for_in.rs b/crates/oxide_emit/src/stmt/iteration/for_in.rs index 06a31f84..3e312624 100644 --- a/crates/oxide_emit/src/stmt/iteration/for_in.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_in.rs @@ -164,8 +164,8 @@ impl Emitter { } ctx.inst(Inst::new(OpCode::FOR_IN_INIT, Operand::None, Operand::Reg(obj_reg), Operand::None)); ctx.labels.set_label_pos(start_label, ctx.insts.len()); - ctx.push_loop(end_label, start_label, crate::emit_ctx::LoopKind::ForIn); - let n_labeled = ctx.take_pending_loop_labels(end_label, start_label); + let _v_reg = ctx.push_loop(end_label, start_label, crate::emit_ctx::LoopKind::ForIn); + let n_labeled = ctx.take_pending_loop_labels(end_label, start_label, _v_reg); let done_reg = ctx.alloc_reg(); ctx.inst(Inst::new(OpCode::FOR_IN_DONE, Operand::Reg(done_reg), Operand::None, Operand::None)); // done=true 表示迭代结束:done=false 时跳过 end jmp 继续迭代 diff --git a/crates/oxide_emit/src/stmt/iteration/for_of.rs b/crates/oxide_emit/src/stmt/iteration/for_of.rs index b4486261..5949354f 100644 --- a/crates/oxide_emit/src/stmt/iteration/for_of.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_of.rs @@ -38,8 +38,8 @@ impl Emitter { } ctx.inst(Inst::new(OpCode::FOR_OF_INIT, Operand::None, Operand::Reg(iter_src_reg), Operand::None)); ctx.labels.set_label_pos(start_label, ctx.insts.len()); - ctx.push_loop(end_label, start_label, crate::emit_ctx::LoopKind::ForOf); - let n_labeled = ctx.take_pending_loop_labels(end_label, start_label); + let _v_reg = ctx.push_loop(end_label, start_label, crate::emit_ctx::LoopKind::ForOf); + let n_labeled = ctx.take_pending_loop_labels(end_label, start_label, _v_reg); let has_reg = ctx.alloc_reg(); ctx.inst(Inst::new(OpCode::FOR_OF_DONE, Operand::Reg(has_reg), Operand::None, Operand::None)); ctx.inst(Inst::jmp_if_false(has_reg, end_label)); @@ -90,8 +90,8 @@ impl Emitter { Operand::None, )); ctx.labels.set_label_pos(start_label, ctx.insts.len()); - ctx.push_loop(end_label, start_label, crate::emit_ctx::LoopKind::ForAwaitOf); - let n_labeled = ctx.take_pending_loop_labels(end_label, start_label); + let _v_reg = ctx.push_loop(end_label, start_label, crate::emit_ctx::LoopKind::ForAwaitOf); + let n_labeled = ctx.take_pending_loop_labels(end_label, start_label, _v_reg); let next_reg = ctx.alloc_reg(); ctx.inst(Inst::new(OpCode::FOR_AWAIT_OF_NEXT, Operand::Reg(next_reg), Operand::None, Operand::None)); ctx.inst(Inst::await_expr(Operand::Reg(next_reg))); diff --git a/crates/oxide_emit/src/stmt/iteration/while_loop.rs b/crates/oxide_emit/src/stmt/iteration/while_loop.rs index e705b53a..5274f0f0 100644 --- a/crates/oxide_emit/src/stmt/iteration/while_loop.rs +++ b/crates/oxide_emit/src/stmt/iteration/while_loop.rs @@ -1,7 +1,9 @@ //! while 语句 emit:`emit_while_statement` 先测条件后执行体。 use crate::{CompileCtx, Emitter}; +use oxide_bytecode::opcode::OpCode; use oxide_ir::inst::Inst; +use oxide_ir::operand::Operand; use oxide_parser::Statement; impl Emitter { @@ -11,23 +13,25 @@ impl Emitter { }; let start_label = ctx.next_label_id(); let end_label = ctx.next_label_id(); + // 开环先于循环头标签落位:出口结果寄存器的 undefined 初始化须落在回边 + // 外(入口执行一次),否则每迭代重置、末迭代出口读 undefined。 + let v_reg = ctx.push_loop(end_label, start_label, crate::emit_ctx::LoopKind::Plain); + let n_labeled = ctx.take_pending_loop_labels(end_label, start_label, v_reg); ctx.labels.set_label_pos(start_label, ctx.insts.len()); - ctx.push_loop(end_label, start_label, crate::emit_ctx::LoopKind::Plain); - let n_labeled = ctx.take_pending_loop_labels(end_label, start_label); let test_reg = self.emit_expression(&wh.test, ctx)?; ctx.inst(Inst::jmp_if_false(test_reg, end_label)); let body_result = self.emit_statement(&wh.body, ctx)?; + // 体正常完成回写(回写点在 `jmp start` 前:体内 continue 绕过本点直达 + // 循环头,不覆写本迭代累积值)。 + if let Some(r) = body_result { + ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(v_reg), Operand::Reg(r), Operand::None)); + } ctx.inst(Inst::jmp(start_label)); ctx.labels.set_label_pos(end_label, ctx.insts.len()); ctx.pop_label_scopes(n_labeled); ctx.pop_loop(); - // 循环完成值 = 循环体最后一次非空完成值:体收敛寄存器每迭代覆写、退出时 - // 持最近值;空体无寄存器可沿用,物化 undefined 作为带值完成返回,不沿用 - // 循环前的值。 - let result = match body_result { - Some(r) => r, - None => self.emit_undefined(ctx), - }; - Ok(Some(result)) + // 循环完成值 = 出口结果寄存器:入口 undefined、体正常完成每迭代覆写、 + // break/continue 携值写入(空携值不写,保持既有累积值)。 + Ok(Some(v_reg)) } } diff --git a/crates/oxide_emit/src/stmt/switch.rs b/crates/oxide_emit/src/stmt/switch.rs index 71aa5e5a..b71bac26 100644 --- a/crates/oxide_emit/src/stmt/switch.rs +++ b/crates/oxide_emit/src/stmt/switch.rs @@ -16,7 +16,15 @@ impl Emitter { return Ok(None); }; let end_label = ctx.next_label_id(); - ctx.push_switch(end_label); + // CaseBlock 出口结果寄存器:入口初始 undefined(覆盖无命中/空块路径), + // 每条子句的非空语句完成值逐语句覆写(与规范逐子句 `UpdateEmpty` + // 累积终态等价:空子句不覆写前值);abrupt 出口不覆写,保留前子句 + // 累积值。 + let result_reg = ctx.alloc_reg(); + let undef = self.emit_undefined(ctx); + ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(undef), Operand::None)); + ctx.push_switch(end_label, result_reg); + ctx.push_completion_target(result_reg); // 判别式在 switch 外层词法环境求值,CaseBlock 环境尚未建立。 let disc_reg = self.emit_expression(&sw.discriminant, ctx)?; let cases = &sw.cases; @@ -60,28 +68,26 @@ impl Emitter { Some(label) => ctx.inst(Inst::jmp(label)), None => ctx.inst(Inst::jmp(end_label)), } - let mut last: Option = None; for (case_idx, case) in cases.iter().enumerate() { let case_label = case_labels[case_idx]; ctx.labels.set_label_pos(case_label, ctx.insts.len()); + // 子句体不经 `emit_block_statement`,其语句列表须自压列表帧: + // 非空语句记累积并逐语句覆写出口寄存器(运行期 resultValue)。 + ctx.push_completion_list(); for s in &case.consequent { if let Some(r) = self.emit_statement(s, ctx)? { - last = Some(r); + ctx.set_completion_last(r); + ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(r), Operand::None)); } } + ctx.pop_completion_list(); } ctx.block_fn_entry_mats.pop(); ctx.labels.set_label_pos(end_label, ctx.insts.len()); ctx.pop_scope(); + ctx.pop_completion_target(); ctx.pop_switch(); - // switch 完成值 = 已执行 case 链最后一次非空完成值(源序最后非空语句的 - // 收敛寄存器:命中后穿落到 break/尾即为其值,无命中则该寄存器不被写入而 - // 持 undefined);无命中物化 undefined 作为带值完成返回。 - let result = match last { - Some(r) => r, - None => self.emit_undefined(ctx), - }; - Ok(Some(result)) + Ok(Some(result_reg)) } pub(crate) fn emit_switch_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { diff --git a/crates/oxide_emit/src/stmt/with.rs b/crates/oxide_emit/src/stmt/with.rs index 2ef1baad..b2cde660 100644 --- a/crates/oxide_emit/src/stmt/with.rs +++ b/crates/oxide_emit/src/stmt/with.rs @@ -20,7 +20,11 @@ impl Emitter { let obj_reg = self.emit_expression(&with.object, ctx)?; ctx.inst(Inst::new(OpCode::TO_OBJECT, Operand::Reg(obj_reg), Operand::None, Operand::None)); ctx.push_with(obj_reg); + // with 体是 `UpdateEmpty(_, undefined)` 站点:压边界帧,体内空体 + // break/continue 穿越时携值物化 undefined。 + ctx.push_completion_boundary(); let body_result = self.emit_statement(&with.body, ctx)?; + ctx.pop_completion_boundary(); ctx.pop_with(); // with 完成值 = 体完成值;体为空(空完成)时按规范物化 undefined 作为带值 // 完成返回,不沿用 with 前的值。 diff --git a/crates/oxide_vm/tests/eval_tests.rs b/crates/oxide_vm/tests/eval_tests.rs index afed7dd0..196743f2 100644 --- a/crates/oxide_vm/tests/eval_tests.rs +++ b/crates/oxide_vm/tests/eval_tests.rs @@ -258,6 +258,51 @@ fn compound_statement_completion_values() { assert_num(result, 3.0); let result = eval(&mut vm, "1; with({}) {}").unwrap(); assert_eq!(result, JsValue::undefined()); + // 值语句-break-死语句:应取 break 前累积值(死语句值不得成为完成值)。 + // switch 域(resultValue 运行期累积):穿落累积值经 break 带出;空子句/ + // 无命中保持初值 undefined,不泄漏前值。 + let result = eval(&mut vm, r#"6; switch ("a") { case "a": 7; case "b": break; default: }"#).unwrap(); + assert_num(result, 7.0); + let result = eval(&mut vm, r#"1; switch ("a") { case "a": 2; case "b": 3; break; default: }"#).unwrap(); + assert_num(result, 3.0); + let result = eval(&mut vm, r#"1; switch ("a") { case "a": break; default: }"#).unwrap(); + assert_eq!(result, JsValue::undefined()); + let result = eval(&mut vm, r#"1; switch ("x") { case "a": 5; }"#).unwrap(); + assert_eq!(result, JsValue::undefined()); + let result = eval(&mut vm, r#"7; switch ("b") { case "a": 8; default: }"#).unwrap(); + assert_eq!(result, JsValue::undefined()); + // 块包裹子句:块列表帧携值出 switch。 + let result = eval(&mut vm, r#"2; switch ("a") { case "a": { 3; break; } default: }"#).unwrap(); + assert_num(result, 3.0); + // 嵌套 switch:内层 unlabeled break 只出内层,外层继续累积。 + let result = eval(&mut vm, r#"switch ("a") { case "a": switch ("a") { case "a": 5; break; } 7; }"#).unwrap(); + assert_num(result, 7.0); + // labeled break 穿 switch:取子句列表累积值。 + let result = eval(&mut vm, r#"9; a: { switch ("a") { case "a": 5; break a; } }"#).unwrap(); + assert_num(result, 5.0); + // with 边界:空体 break/continue 经边界物化 undefined,非空体取体累积值。 + let result = eval(&mut vm, "1; do { 2; with({}) { 3; break; } 4; } while (false)").unwrap(); + assert_num(result, 3.0); + let result = eval(&mut vm, "5; do { 6; with({}) { break; } 7; } while (false)").unwrap(); + assert_eq!(result, JsValue::undefined()); + let result = eval(&mut vm, "8; do { 9; with({}) { 10; continue; } 11; } while (false)").unwrap(); + assert_num(result, 10.0); + let result = eval(&mut vm, "12; do { 13; with({}) { continue; } 14; } while (false)").unwrap(); + assert_eq!(result, JsValue::undefined()); + // if 边界:同 with 形(支臂 UpdateEmpty 物化 undefined)。 + let result = eval(&mut vm, "1; do { 2; if (true) { 3; break; } 4; } while (false)").unwrap(); + assert_num(result, 3.0); + let result = eval(&mut vm, "5; do { 6; if (true) { break; } 7; } while (false)").unwrap(); + assert_eq!(result, JsValue::undefined()); + let result = eval(&mut vm, "8; do { 9; if (true) { 10; continue; } 11; } while (false)").unwrap(); + assert_num(result, 10.0); + let result = eval(&mut vm, "12; do { 13; if (true) { continue; } 14; } while (false)").unwrap(); + assert_eq!(result, JsValue::undefined()); + // try 体列表帧:非空体累积值穿透 labeled break(finally 体值不渗入)。 + let result = eval(&mut vm, "a: { try { 5; break a; } finally { 7 } }").unwrap(); + assert_num(result, 5.0); + let result = eval(&mut vm, "a: { try { throw 1; } catch (e) { 5; break a; } }").unwrap(); + assert_num(result, 5.0); } #[test] @@ -284,4 +329,54 @@ fn iteration_labeled_completion_values() { assert_num(result, 8.0); let result = eval(&mut vm, "test262id: 2;").unwrap(); assert_num(result, 2.0); + // 值语句-break-死语句(非迭代标签):取 break 前块列表累积值;无 break 的 + // 标签保持透传不回归。 + let result = eval(&mut vm, "test262id: { 5; break test262id; 9; }").unwrap(); + assert_num(result, 5.0); + let result = eval(&mut vm, "a: { 1; { break a; } 3; }").unwrap(); + assert_num(result, 1.0); + let result = eval(&mut vm, "9; a: {}").unwrap(); + assert_num(result, 9.0); + // 循环出口结果寄存器:break/continue 携本列表累积值写入(空携值不覆写)。 + let result = eval(&mut vm, "9; a: { 2; if(true){ break a; } }").unwrap(); + assert_eq!(result, JsValue::undefined()); + let result = eval(&mut vm, "for (var i = 0; i < 3; ++i) { if (i === 1) continue; i * 10; }").unwrap(); + assert_num(result, 20.0); + let result = eval(&mut vm, "a: for (;;) { if (true) { 3; break a; } }").unwrap(); + assert_num(result, 3.0); + let result = eval(&mut vm, "4; do { continue; } while (false)").unwrap(); + assert_eq!(result, JsValue::undefined()); + let result = eval(&mut vm, "2; do { 3; break; } while (false)").unwrap(); + assert_num(result, 3.0); + let result = eval(&mut vm, "1; while (true) { break; }").unwrap(); + assert_eq!(result, JsValue::undefined()); + // 子句级 continue 携值出 switch 再入循环出口寄存器(条款列表帧携值)。 + let result = eval( + &mut vm, + r#"13; do { switch ("a") { case "a": 14; case "b": continue; default: } } while (false)"#, + ) + .unwrap(); + assert_num(result, 14.0); + let result = eval( + &mut vm, + r#"11; do { switch ("a") { case "a": case "b": 12; continue; default: } } while (false)"#, + ) + .unwrap(); + assert_num(result, 12.0); + let result = eval( + &mut vm, + r#"8; do { switch ("a") { case "a": 9; case "b": 10; continue; default: } } while (false)"#, + ) + .unwrap(); + assert_num(result, 10.0); + let result = + eval(&mut vm, r#"5; do { switch ("a") { case "a": { 6; continue; } default: } } while (false)"#).unwrap(); + assert_num(result, 6.0); + let result = eval(&mut vm, r#"4; do { switch ("a") { case "a": continue; default: } } while (false)"#).unwrap(); + assert_eq!(result, JsValue::undefined()); + // labeled break/continue 穿 switch 出循环:取子句列表累积值写标签出口寄存器。 + let result = eval(&mut vm, "a: for (var i = 0; i < 2; ++i) { switch (i) { case 0: 5; break a; } }").unwrap(); + assert_num(result, 5.0); + let result = eval(&mut vm, "a: for (var i = 0; i < 2; ++i) { switch (i) { case 0: break a; } }").unwrap(); + assert_eq!(result, JsValue::undefined()); }