diff --git a/crates/oxide_emit/src/stmt/basic/labeled.rs b/crates/oxide_emit/src/stmt/basic/labeled.rs index 0e7b957d..69f1354c 100644 --- a/crates/oxide_emit/src/stmt/basic/labeled.rs +++ b/crates/oxide_emit/src/stmt/basic/labeled.rs @@ -19,16 +19,19 @@ impl Emitter { &self, stmt: &oxide_parser::LabeledStatement, ctx: &mut CompileCtx, ) -> Result, String> { let name = stmt.label.name.as_str(); - if Self::is_iteration_statement(&stmt.body) { + // 标签语句完成值 = 体完成值(透传):迭代体返回其循环值,块/表达式体返回其 + // 收敛值;标签本身不改变完成值。 + let body_result = if Self::is_iteration_statement(&stmt.body) { ctx.queue_loop_label(name)?; - self.emit_statement(&stmt.body, ctx)?; + self.emit_statement(&stmt.body, ctx)? } else { let id = ctx.next_label_id(); ctx.push_label_scope(name, id, None)?; - self.emit_statement(&stmt.body, ctx)?; + let r = self.emit_statement(&stmt.body, ctx)?; ctx.labels.set_label_pos(id, ctx.insts.len()); ctx.pop_label_scope(); - } - Ok(None) + r + }; + Ok(body_result) } } diff --git a/crates/oxide_emit/src/stmt/exception/try_catch.rs b/crates/oxide_emit/src/stmt/exception/try_catch.rs index 6f281ac0..ce624efe 100644 --- a/crates/oxide_emit/src/stmt/exception/try_catch.rs +++ b/crates/oxide_emit/src/stmt/exception/try_catch.rs @@ -87,23 +87,16 @@ impl Emitter { // 落到 finally_label 起始的这条标记,统一完成置位;catch 体直落路径此前 // 无任何置位,finally 内 break/continue/return/throw 会重入 finally 体。 ctx.inst(Inst::try_finally_enter()); - let mut last_finally_result: Option = None; + // finally 体按规范只求值副作用,其完成值被丢弃(不覆写 result_reg): + // try/catch 收敛值保持 try 体或 catch 体值,finally 值不渗入。 // finally block 同为独立块作用域,lexical 声明互不泄漏。 ctx.push_scope(); // finally 块 lexical 声明同为局部绑定:不做受限全局名检查。 let _ = self.predeclare_lexical_declarations(&ts.finalizer.as_ref().unwrap().body, ctx, false); for s in &ts.finalizer.as_ref().unwrap().body { - if let Some(r) = self.emit_statement(s, ctx)? { - last_finally_result = Some(r); - } + self.emit_statement(s, ctx)?; } ctx.pop_scope(); - ctx.inst(Inst::new( - OpCode::LOAD_VAR, - Operand::Reg(result_reg), - Operand::Reg(last_finally_result.unwrap_or(result_reg)), - Operand::None, - )); ctx.inst(Inst::new(OpCode::TRY_FINALLY_END, Operand::None, Operand::None, Operand::None)); ctx.pop_open_try_handler(); ctx.pop_finally_domain(); diff --git a/crates/oxide_emit/src/stmt/iteration/do_while.rs b/crates/oxide_emit/src/stmt/iteration/do_while.rs index cf96dcb1..9d924a26 100644 --- a/crates/oxide_emit/src/stmt/iteration/do_while.rs +++ b/crates/oxide_emit/src/stmt/iteration/do_while.rs @@ -19,13 +19,19 @@ impl Emitter { // 不能指回体首(否则条件永不被求值)。 ctx.push_loop(end_label, cont_label, crate::emit_ctx::LoopKind::Plain); let n_labeled = ctx.take_pending_loop_labels(end_label, cont_label); - self.emit_statement(&dw.body, ctx)?; + let body_result = self.emit_statement(&dw.body, ctx)?; 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(); - Ok(None) + // 循环完成值 = 循环体最后一次非空完成值(体收敛寄存器每迭代覆写);空体 + // 物化 undefined 作为带值完成返回,不沿用循环前的值。 + let result = match body_result { + Some(r) => r, + None => self.emit_undefined(ctx), + }; + Ok(Some(result)) } } diff --git a/crates/oxide_emit/src/stmt/iteration/for_.rs b/crates/oxide_emit/src/stmt/iteration/for_.rs index 3c0dba69..c741b645 100644 --- a/crates/oxide_emit/src/stmt/iteration/for_.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_.rs @@ -134,7 +134,7 @@ impl Emitter { let test_reg = self.emit_expression(test, ctx)?; ctx.inst(Inst::jmp_if_false(test_reg, end_label)); } - self.emit_statement(&fr.body, ctx)?; + let body_result = self.emit_statement(&fr.body, ctx)?; ctx.labels.set_label_pos(update_label, ctx.insts.len()); if let Some(update) = &fr.update { // update 段写寄存器(而非 cell):被捕获的 let/const 循环变量每迭代 fresh, @@ -150,6 +150,12 @@ impl Emitter { ctx.pop_label_scopes(n_labeled); ctx.pop_loop(); ctx.pop_scope(); - Ok(None) + // 循环完成值 = 循环体最后一次非空完成值;空体物化 undefined 作为带值完成 + // 返回,不沿用循环前的值。 + let result = match body_result { + Some(r) => r, + None => self.emit_undefined(ctx), + }; + Ok(Some(result)) } } diff --git a/crates/oxide_emit/src/stmt/iteration/for_in.rs b/crates/oxide_emit/src/stmt/iteration/for_in.rs index 1783577e..8a6ae7e9 100644 --- a/crates/oxide_emit/src/stmt/iteration/for_in.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_in.rs @@ -123,13 +123,19 @@ impl Emitter { } _ => return Err("unsupported for-in left-hand side".into()), } - self.emit_statement(&fi.body, ctx)?; + let body_result = self.emit_statement(&fi.body, ctx)?; ctx.inst(Inst::jmp(start_label)); ctx.labels.set_label_pos(end_label, ctx.insts.len()); ctx.inst(Inst::new(OpCode::FOR_IN_CLEANUP, Operand::None, Operand::None, Operand::None)); ctx.pop_label_scopes(n_labeled); ctx.pop_loop(); ctx.pop_scope(); - Ok(None) + // 循环完成值 = 循环体最后一次非空完成值;空体物化 undefined 作为带值完成 + // 返回,不沿用循环前的值。 + let result = match body_result { + Some(r) => r, + None => self.emit_undefined(ctx), + }; + Ok(Some(result)) } } diff --git a/crates/oxide_emit/src/stmt/iteration/for_of.rs b/crates/oxide_emit/src/stmt/iteration/for_of.rs index b4732724..349f12f4 100644 --- a/crates/oxide_emit/src/stmt/iteration/for_of.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_of.rs @@ -37,14 +37,20 @@ impl Emitter { let val_reg = ctx.alloc_reg(); ctx.inst(Inst::new(OpCode::FOR_OF_NEXT, Operand::Reg(val_reg), Operand::None, Operand::None)); self.emit_for_of_left_assignment(&fo.left, val_reg, ctx)?; - self.emit_statement(&fo.body, ctx)?; + let body_result = self.emit_statement(&fo.body, ctx)?; ctx.inst(Inst::jmp(start_label)); ctx.labels.set_label_pos(end_label, ctx.insts.len()); ctx.inst(Inst::new(OpCode::FOR_OF_CLOSE, Operand::None, Operand::None, Operand::None)); ctx.pop_label_scopes(n_labeled); ctx.pop_loop(); ctx.pop_scope(); - Ok(None) + // 循环完成值 = 循环体最后一次非空完成值;空体物化 undefined 作为带值完成 + // 返回,不沿用循环前的值。 + let result = match body_result { + Some(r) => r, + None => self.emit_undefined(ctx), + }; + Ok(Some(result)) } /// for-await-of:异步迭代器协议遍历,每步经 `AWAIT` 挂起等待 next() 结果。 @@ -82,14 +88,20 @@ impl Emitter { let val_reg = ctx.alloc_reg(); ctx.inst(Inst::new(OpCode::FOR_OF_NEXT, Operand::Reg(val_reg), Operand::None, Operand::None)); self.emit_for_of_left_assignment(&fo.left, val_reg, ctx)?; - self.emit_statement(&fo.body, ctx)?; + let body_result = self.emit_statement(&fo.body, ctx)?; ctx.inst(Inst::jmp(start_label)); ctx.labels.set_label_pos(end_label, ctx.insts.len()); ctx.inst(Inst::new(OpCode::FOR_AWAIT_OF_CLOSE, Operand::None, Operand::None, Operand::None)); ctx.pop_label_scopes(n_labeled); ctx.pop_loop(); ctx.pop_scope(); - Ok(None) + // 循环完成值 = 循环体最后一次非空完成值;空体物化 undefined 作为带值完成 + // 返回,不沿用循环前的值。 + let result = match body_result { + Some(r) => r, + None => self.emit_undefined(ctx), + }; + Ok(Some(result)) } /// for-of/for-await-of 左侧绑定:把当前迭代值 `val_reg` 写入声明/赋值目标。 diff --git a/crates/oxide_emit/src/stmt/iteration/while_loop.rs b/crates/oxide_emit/src/stmt/iteration/while_loop.rs index 8ccee239..e705b53a 100644 --- a/crates/oxide_emit/src/stmt/iteration/while_loop.rs +++ b/crates/oxide_emit/src/stmt/iteration/while_loop.rs @@ -16,11 +16,18 @@ impl Emitter { 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)); - self.emit_statement(&wh.body, ctx)?; + let body_result = self.emit_statement(&wh.body, ctx)?; 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(); - Ok(None) + // 循环完成值 = 循环体最后一次非空完成值:体收敛寄存器每迭代覆写、退出时 + // 持最近值;空体无寄存器可沿用,物化 undefined 作为带值完成返回,不沿用 + // 循环前的值。 + let result = match body_result { + Some(r) => r, + None => self.emit_undefined(ctx), + }; + Ok(Some(result)) } } diff --git a/crates/oxide_emit/src/stmt/switch.rs b/crates/oxide_emit/src/stmt/switch.rs index ca950a57..7ef8365d 100644 --- a/crates/oxide_emit/src/stmt/switch.rs +++ b/crates/oxide_emit/src/stmt/switch.rs @@ -35,16 +35,26 @@ impl Emitter { if !has_default { 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()); for s in &case.consequent { - self.emit_statement(s, ctx)?; + if let Some(r) = self.emit_statement(s, ctx)? { + last = Some(r); + } } } ctx.labels.set_label_pos(end_label, ctx.insts.len()); ctx.pop_switch(); - Ok(None) + // switch 完成值 = 已执行 case 链最后一次非空完成值(源序最后非空语句的 + // 收敛寄存器:命中后穿落到 break/尾即为其值,无命中则该寄存器不被写入而 + // 持 undefined);无命中物化 undefined 作为带值完成返回。 + let result = match last { + Some(r) => r, + None => self.emit_undefined(ctx), + }; + Ok(Some(result)) } 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 dc768178..2ef1baad 100644 --- a/crates/oxide_emit/src/stmt/with.rs +++ b/crates/oxide_emit/src/stmt/with.rs @@ -20,9 +20,15 @@ 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); - let result = self.emit_statement(&with.body, ctx)?; + let body_result = self.emit_statement(&with.body, ctx)?; ctx.pop_with(); - Ok(result) + // with 完成值 = 体完成值;体为空(空完成)时按规范物化 undefined 作为带值 + // 完成返回,不沿用 with 前的值。 + let result = match body_result { + Some(r) => r, + None => self.emit_undefined(ctx), + }; + Ok(Some(result)) } pub(crate) fn emit_with_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { diff --git a/crates/oxide_vm/tests/eval_tests.rs b/crates/oxide_vm/tests/eval_tests.rs index cfb7277b..afed7dd0 100644 --- a/crates/oxide_vm/tests/eval_tests.rs +++ b/crates/oxide_vm/tests/eval_tests.rs @@ -224,3 +224,64 @@ fn eval_script_throw_still_rethrows() { let result = eval(&mut vm, "try { (0,eval)('throw 5') } catch(e) { e }").unwrap(); assert_num(result, 5.0); } + +#[test] +fn compound_statement_completion_values() { + let mut vm = Vm::new(); + // 混合形完成值:switch 命中 case 链最后非空值、无命中 undefined;空命中不沿用前值。 + let result = eval(&mut vm, "7; switch(1){case 1: 9}").unwrap(); + assert_num(result, 9.0); + let result = eval(&mut vm, "7; switch(2){case 1: 9}").unwrap(); + assert_eq!(result, JsValue::undefined()); + // if 完成值 = 执行分支值,空/缺失分支物化 undefined(不沿用前值)。 + let result = eval(&mut vm, "7; if(true){}").unwrap(); + assert_eq!(result, JsValue::undefined()); + let result = eval(&mut vm, "7; if(true){3}").unwrap(); + assert_num(result, 3.0); + // for 循环体完成值 = 末次迭代体值;不迭代则 undefined(不沿用前值)。 + let result = eval(&mut vm, "7; for(var i = 0; i < 2; i++) i * 10").unwrap(); + assert_num(result, 10.0); + let result = eval(&mut vm, "7; for(; false;) 3").unwrap(); + assert_eq!(result, JsValue::undefined()); + // try 完成值 = try/catch 值,finally 体值按规范被丢弃。 + let result = eval(&mut vm, "7; try { 5 } finally { 6 }").unwrap(); + assert_num(result, 5.0); + let result = eval(&mut vm, "7; try { throw 3 } catch (e) { 6 } finally { 8 }").unwrap(); + assert_num(result, 6.0); + // while 循环体完成值 = 末次体值;不迭代则 undefined。 + let result = eval(&mut vm, "7; while (true) { 8; break }").unwrap(); + assert_num(result, 8.0); + let result = eval(&mut vm, "2; while (false) { 3 }").unwrap(); + assert_eq!(result, JsValue::undefined()); + // with 完成值 = 体值;空体物化 undefined(不沿用前值)。 + let result = eval(&mut vm, "2; with({}) { 3 }").unwrap(); + assert_num(result, 3.0); + let result = eval(&mut vm, "1; with({}) {}").unwrap(); + assert_eq!(result, JsValue::undefined()); +} + +#[test] +fn iteration_labeled_completion_values() { + let mut vm = Vm::new(); + // do-while 循环体完成值 = 末次体值。 + let result = eval(&mut vm, "7; do { 8 } while (false)").unwrap(); + assert_num(result, 8.0); + let result = eval(&mut vm, "1; do { 2; break; } while (true)").unwrap(); + assert_num(result, 2.0); + // for-of 循环体完成值 = 末次迭代体值;空可迭代对象不迭代则 undefined。 + let result = eval(&mut vm, "7; for (var x of [1, 2]) x * 10").unwrap(); + assert_num(result, 20.0); + let result = eval(&mut vm, "7; for (var x of [1, 2]) { x * 10; break }").unwrap(); + assert_num(result, 10.0); + let result = eval(&mut vm, "7; for (var x of []) x * 10").unwrap(); + assert_eq!(result, JsValue::undefined()); + // for-in 循环体完成值 = 末次迭代体值。 + let result = eval(&mut vm, "7; for (var k in { a: 1 }) k").unwrap(); + let rendered = vm.lookup_str(result).unwrap_or_default(); + assert_eq!(rendered, "a"); + // labeled 完成值 = 体值透传:块体取块收敛值,标签前缀/表达式体取体值。 + let result = eval(&mut vm, "9; a: { 8 }").unwrap(); + assert_num(result, 8.0); + let result = eval(&mut vm, "test262id: 2;").unwrap(); + assert_num(result, 2.0); +}