mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
fix: 复合语句域收敛真实完成值,空体物化 undefined 不沿用前值
This commit is contained in:
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user