mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: 裸 return 物化 undefined,防调用残留值冒充完成值
This commit is contained in:
@@ -307,10 +307,15 @@ impl Emitter {
|
||||
.position(|u| u.name == "@@field_keys")
|
||||
.map(|i| i as u8);
|
||||
emit_instance_fields(self, &mut field_ctx)?;
|
||||
// 构造器尾裸 return 须物化 undefined:RETURN 读 rd 槽值,字段初始化器
|
||||
// 尾表达式为返回对象的调用时,该对象会漏出为构造结果。
|
||||
let undef_idx = field_ctx.add_constant(Constant::Undefined);
|
||||
let undef_reg = field_ctx.alloc_reg();
|
||||
field_ctx.inst(Inst::load_const(Operand::Reg(undef_reg), undef_idx));
|
||||
module.insts.extend(field_ctx.insts);
|
||||
module.constants = field_ctx.constants;
|
||||
module.n_registers = field_ctx.max_regs.max(1);
|
||||
module.insts.push(Inst::ret(Operand::None, 0, 0));
|
||||
module.insts.push(Inst::ret(Operand::Reg(undef_reg), 0, 0));
|
||||
}
|
||||
module
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! return 语句 emit:`emit_return_statement` 求值返回值并生成返回指令。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
@@ -32,7 +33,12 @@ impl Emitter {
|
||||
if for_of_count == 0 && for_in_count == 0 {
|
||||
self.emit_return_try_cleanup(ctx);
|
||||
}
|
||||
ctx.inst(Inst::ret(Operand::None, for_of_count, for_in_count));
|
||||
// 裸 return 须显式物化 undefined 为完成值:RETURN 从 rd 槽读值,
|
||||
// 不物化则交付槽内残留值(如最近一次调用的结果)冒充完成值。
|
||||
let undef_idx = ctx.add_constant(Constant::Undefined);
|
||||
let undef_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(undef_reg), undef_idx));
|
||||
ctx.inst(Inst::ret(Operand::Reg(undef_reg), for_of_count, for_in_count));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
//! 裸 return 完成值测试:无值 return 物化 undefined,防 rd 槽残留
|
||||
//! (如最近一次调用的结果)冒充完成值泄漏;覆盖函数体臂与派生类
|
||||
//! 隐式构造器臂两条发射路径。
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use oxide_compiler::compiler::Compiler;
|
||||
use oxide_parser::Allocator;
|
||||
use oxide_types::value::JsValue;
|
||||
use oxide_vm::promise::promise_settled_value;
|
||||
use oxide_vm::vm::Vm;
|
||||
|
||||
/// parse → compile → run,返回 VM 与顶层执行结果。
|
||||
fn run(source: &str) -> Result<(Vm, JsValue), String> {
|
||||
let allocator = Allocator::default();
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("parse error: {:?}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("compile error: {}", e))?;
|
||||
let mut vm = Vm::new();
|
||||
let result = vm.run(&Arc::new(module)).map_err(|e| format!("vm error: {}", e))?;
|
||||
Ok((vm, result))
|
||||
}
|
||||
|
||||
/// 断言顶层结果为真值布尔。
|
||||
fn assert_true(source: &str) {
|
||||
let (_vm, result) = run(source).unwrap();
|
||||
assert!(result.is_bool() && result.as_bool(), "expected true, got: {result:?}");
|
||||
}
|
||||
|
||||
/// 断言 async 函数完成值为 undefined(微任务已在 run 收尾 drain)。
|
||||
fn assert_async_undefined(source: &str) {
|
||||
let (_vm, result) = run(source).unwrap();
|
||||
let obj = unsafe { &*result.as_js_object_ptr() };
|
||||
assert!(obj.is_promise_obj(), "expected promise, got: {result:?}");
|
||||
let settled = promise_settled_value(obj).expect("promise should be settled after drain").1;
|
||||
assert_eq!(settled, JsValue::undefined());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bare_return_after_call_materializes_undefined() {
|
||||
// 裸 return 前最后一次求值为调用:槽内残留值不得冒充完成值。
|
||||
assert_true("function g(){return true} function f(){if(1){g();return;}return 'END'} f() === undefined");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn async_bare_return_after_call_materializes_undefined() {
|
||||
// async 函数体与同步函数体共用同一条裸 return 发射臂。
|
||||
assert_async_undefined("function g(){return true} async function h(){if(1){g();return;}return 'END'} h()");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bare_return_escaping_for_of_materializes_undefined() {
|
||||
// 裸 return 逃出 for-of:迭代器关闭计数透传臂同样物化 undefined。
|
||||
assert_true(
|
||||
"function g(){return true} function f(){for(var x of [1,2]){g();return;}return 'END'} f() === undefined",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derived_class_implicit_constructor_returns_instance() {
|
||||
// 隐式派生构造器尾裸 return:字段初始化器返回对象的调用结果
|
||||
// 不得漏出为构造结果。
|
||||
assert_true("class A{} class B extends A { x = (function(){return {mark:1};})() } new B() instanceof B");
|
||||
}
|
||||
Reference in New Issue
Block a user