fix: 复合语句域收敛真实完成值,空体物化 undefined 不沿用前值

This commit is contained in:
2026-09-14 22:51:29 +00:00
parent 92df76565b
commit c50eb9e566
10 changed files with 141 additions and 31 deletions
+8 -5
View File
@@ -19,16 +19,19 @@ impl Emitter {
&self, stmt: &oxide_parser::LabeledStatement, ctx: &mut CompileCtx,
) -> Result<Option<u32>, 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)
}
}
@@ -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<u32> = 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();
@@ -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))
}
}
+8 -2
View File
@@ -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))
}
}
@@ -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))
}
}
+16 -4
View File
@@ -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` 写入声明/赋值目标。
@@ -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))
}
}
+12 -2
View File
@@ -35,16 +35,26 @@ impl Emitter {
if !has_default {
ctx.inst(Inst::jmp(end_label));
}
let mut last: Option<u32> = 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<Option<u32>, String> {
+8 -2
View File
@@ -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<Option<u32>, String> {
+61
View File
@@ -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);
}