feat: 函数代理 apply 与 construct trap 路由落地

This commit is contained in:
2026-10-17 07:53:31 +00:00
parent addaab394d
commit e5db10581e
12 changed files with 437 additions and 152 deletions
+20 -2
View File
@@ -17,6 +17,12 @@ use super::common::{
pub fn array_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let proto = vm.builtin_proto(ProtoKind::ArrayProto);
let proto_val = JsValue::from_js_object(proto);
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
// 构造形态(newTarget 在场):产物须为 Array exotic 对象(Array.isArray 认)
// 且原型取 newTarget.prototype(派生类 / 代理 newTarget 由此拿到子类实例),
// 原型已由构造入口预写入 receiver,直接读取复用;普通调用形态自建
// Array.prototype 原型的新数组。
let in_construct = vm.constructing_native() && this_val.is_object();
if args.len() == 2 {
let val = vm.reg(args[1]);
@@ -25,7 +31,13 @@ pub fn array_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
Ok(n) => n,
Err(err) => return NativeResult::Err(err),
};
let arr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, proto_val, n));
let arr_proto = if in_construct {
// SAFETY: receiver 是构造帧分配的 this(本 session 存活),只读原型。
unsafe { &*this_val.as_js_object_ptr() }.proto()
} else {
proto_val
};
let arr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, arr_proto, n));
// 数值长度分支建 n 个空洞而非 present-undefined:`in`、for-in、
// Object.keys 按缺失处理,元素 Get 落原型链。物理区以稠密上限
// 钳位(越界索引无物理槽,逻辑长度已覆盖)。
@@ -39,7 +51,13 @@ pub fn array_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
}
let n_elems = if args.len() > 1 { args.len() - 1 } else { 0 };
let arr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, proto_val, n_elems));
let arr_proto = if in_construct {
// SAFETY: receiver 是构造帧分配的 this(本 session 存活),只读原型。
unsafe { &*this_val.as_js_object_ptr() }.proto()
} else {
proto_val
};
let arr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, arr_proto, n_elems));
for i in 0..n_elems {
unsafe {
(*arr).set_prop_at(i, vm.reg(args[1 + i]));
+13 -5
View File
@@ -70,15 +70,23 @@ fn check_target_and_handler(target: JsValue, handler: JsValue) -> Result<(), &'s
Ok(())
}
/// 不变量检查(规范 10.2.4 步骤 4-5):handler 的 isExtensible 与 setPrototypeOf
/// trap 若存在,其布尔值须与 target 实际状态一致,否则抛 TypeError。
/// 不变量检查(规范 10.2.4 步骤 1、4-5):target 可调用时须可扩展,handler
/// 的 isExtensible 与 setPrototypeOf trap 若存在,其布尔值须与 target 实际
/// 状态一致,否则抛 TypeError。
///
/// # 边界与前提
/// - 仅当 handler 自有对应 trap 属性时检查(继承属性不触发)。
/// - trap 读为传播读,getter 抛错原值上抛。
fn proxy_create_invariants<H: VmHost>(
vm: &mut H, target_obj: &JsObject, handler_obj: &JsObject, handler: JsValue,
vm: &mut H, target: JsValue, target_obj: &JsObject, handler_obj: &JsObject, handler: JsValue,
) -> Result<(), JsValue> {
// target 可调用且不可扩展:函数代理的不变量无法满足,抛 TypeError。
if crate::iterator::is_callable(target) && !target_obj.is_extensible() {
return Err(crate::error::create_type_error(
vm,
"Proxy target must be extensible if it is callable",
));
}
// isExtensible trap:handler 报 true 时 target 须可扩展,报 false 时须不可扩展。
let si_is_extensible = vm.perm_intern("isExtensible");
if vm.get_own_property_slot(handler_obj, si_is_extensible).is_some() {
@@ -160,7 +168,7 @@ pub fn proxy_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
// SAFETY: check_target_and_handler 已校验非空对象指针。
let target_obj = unsafe { &*target.as_js_object_ptr() };
let handler_obj = unsafe { &*handler.as_js_object_ptr() };
if let Err(exc) = proxy_create_invariants(vm, target_obj, handler_obj, handler) {
if let Err(exc) = proxy_create_invariants(vm, target, target_obj, handler_obj, handler) {
return NativeResult::Err(exc);
}
NativeResult::Ok(create_proxy_object(vm, target, handler))
@@ -187,7 +195,7 @@ pub fn proxy_revocable<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
// SAFETY: check_target_and_handler 已校验非空对象指针。
let target_obj = unsafe { &*target.as_js_object_ptr() };
let handler_obj = unsafe { &*handler.as_js_object_ptr() };
if let Err(exc) = proxy_create_invariants(vm, target_obj, handler_obj, handler) {
if let Err(exc) = proxy_create_invariants(vm, target, target_obj, handler_obj, handler) {
return NativeResult::Err(exc);
}
let proxy_val = create_proxy_object(vm, target, handler);