fix: Proxy stub 不装规范不存在的 prototype 属性

This commit is contained in:
2026-09-24 05:09:34 +00:00
parent d8ea3d8a65
commit 7a0d3ce336
2 changed files with 74 additions and 63 deletions
+22 -12
View File
@@ -8,34 +8,41 @@ use oxide_types::value::JsValue;
use crate::bindings::{bind_global_value, configure_native_constructor}; use crate::bindings::{bind_global_value, configure_native_constructor};
/// 未实现内置的 stub 表:(全局名、native 函数项指针、形参个数、原型 @@toStringTag)。 /// 未实现内置的 stub 表:(全局名、native 函数项指针、形参个数、
/// 原型 @@toStringTag、是否安装原型对象)。
/// ///
/// `tag` 为 `None` 的条目(Proxy)原型对象不设 @@toStringTag;四弱族各取族名。 /// `tag` 为 `None` 的条目(Proxy)原型对象不设 @@toStringTag;四弱族各取族名。
const STUBS: [(&str, *const (), u8, Option<&str>); 5] = [ /// 规范 25.1 的 %Proxy% 无 prototype 属性(`has_prototype` = false),
("Proxy", oxide_builtins::stubs::proxy_stub::<crate::vm::Vm> as *const (), 2, None), /// 四弱族各装带 constructor 回指的原型对象。
const STUBS: [(&str, *const (), u8, Option<&str>, bool); 5] = [
("Proxy", oxide_builtins::stubs::proxy_stub::<crate::vm::Vm> as *const (), 2, None, false),
( (
"WeakMap", "WeakMap",
oxide_builtins::stubs::weakmap_stub::<crate::vm::Vm> as *const (), oxide_builtins::stubs::weakmap_stub::<crate::vm::Vm> as *const (),
0, 0,
Some("WeakMap"), Some("WeakMap"),
true,
), ),
( (
"WeakSet", "WeakSet",
oxide_builtins::stubs::weakset_stub::<crate::vm::Vm> as *const (), oxide_builtins::stubs::weakset_stub::<crate::vm::Vm> as *const (),
0, 0,
Some("WeakSet"), Some("WeakSet"),
true,
), ),
( (
"WeakRef", "WeakRef",
oxide_builtins::stubs::weakref_stub::<crate::vm::Vm> as *const (), oxide_builtins::stubs::weakref_stub::<crate::vm::Vm> as *const (),
1, 1,
Some("WeakRef"), Some("WeakRef"),
true,
), ),
( (
"FinalizationRegistry", "FinalizationRegistry",
oxide_builtins::stubs::finalization_registry_stub::<crate::vm::Vm> as *const (), oxide_builtins::stubs::finalization_registry_stub::<crate::vm::Vm> as *const (),
1, 1,
Some("FinalizationRegistry"), Some("FinalizationRegistry"),
true,
), ),
]; ];
@@ -53,16 +60,17 @@ fn set_data_property(core: &Arc<KernelCore>, obj: &mut JsObject, si: u32, value:
/// 绑定到 global,并登记到 `stub_objects` 供快照跟踪。 /// 绑定到 global,并登记到 `stub_objects` 供快照跟踪。
/// ///
/// # 副作用 /// # 副作用
/// 每个 stub 按规范补装构造器面属性:`prototype`(新建 plain 对象,原型链落 /// 每个 stub 按规范补装构造器面属性:`length`、`name`(五族全装);四弱族
/// Object.prototype,其上带指回 stub 的 `constructor`,四弱族另带 @@toStringTag)、 /// 另装 `prototype`(新建 plain 对象,原型链落 Object.prototype,其上带指回
/// `length`、`name`;描述符均不可枚举,`prototype` 另不可写、不可配置。 /// stub 的 `constructor` 与 @@toStringTag)——规范 25.1 的 %Proxy% 无
/// prototype 属性,不装;描述符均不可枚举,`prototype` 另不可写、不可配置。
/// 原型对象登记进 world 释放登记表,与 stub 对象同生死,session 收尾统一释放。 /// 原型对象登记进 world 释放登记表,与 stub 对象同生死,session 收尾统一释放。
pub fn bind_stubs(core: &Arc<KernelCore>, session: &mut KernelSession, global: &mut JsObject) { pub fn bind_stubs(core: &Arc<KernelCore>, session: &mut KernelSession, global: &mut JsObject) {
let builtin_world = Arc::get_mut(&mut session.builtin_world) let builtin_world = Arc::get_mut(&mut session.builtin_world)
.expect("BuiltinWorld must be uniquely owned during init_kernel_builtins"); .expect("BuiltinWorld must be uniquely owned during init_kernel_builtins");
let object_proto_ptr = builtin_world.object_proto.as_ptr() as *mut JsObject; let object_proto_ptr = builtin_world.object_proto.as_ptr() as *mut JsObject;
for (name, native_fn, arg_count, tag) in STUBS { for (name, native_fn, arg_count, tag, has_prototype) in STUBS {
let mut stub = JsObject::new_empty( let mut stub = JsObject::new_empty(
EMPTY_SHAPE_ID, EMPTY_SHAPE_ID,
JsValue::from_js_object(builtin_world.function_proto.as_ptr() as *mut JsObject), JsValue::from_js_object(builtin_world.function_proto.as_ptr() as *mut JsObject),
@@ -73,8 +81,12 @@ pub fn bind_stubs(core: &Arc<KernelCore>, session: &mut KernelSession, global: &
let stub = P::new(stub); let stub = P::new(stub);
let stub_ptr = stub.as_ptr() as *mut JsObject; let stub_ptr = stub.as_ptr() as *mut JsObject;
// 原型对象:constructor 回指 + @@toStringTag(四弱族);属性装完再登记 // 构造器面属性:length / name 五族全装;prototype 仅四弱族(%Proxy% 无)。
// 释放登记表,登记点世代即洁净基线。 // SAFETY: stub 尚未发布到 global,局部 Arc 为唯一持有者,可变访问无读者冲突。
let stub_obj = unsafe { &mut *stub_ptr };
if has_prototype {
// 原型对象:constructor 回指 + @@toStringTag(四弱族);属性装完再
// 登记释放登记表,登记点世代即洁净基线。
let mut proto = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto_ptr)); let mut proto = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto_ptr));
let si_constructor = core.perm_interner().intern("constructor").0; let si_constructor = core.perm_interner().intern("constructor").0;
set_data_property( set_data_property(
@@ -95,9 +107,6 @@ pub fn bind_stubs(core: &Arc<KernelCore>, session: &mut KernelSession, global: &
let proto_ptr = Box::into_raw(Box::new(proto)); let proto_ptr = Box::into_raw(Box::new(proto));
builtin_world.track_leaked_object(proto_ptr); builtin_world.track_leaked_object(proto_ptr);
// 构造器面属性:prototype / length / name。
// SAFETY: stub 尚未发布到 global,局部 Arc 为唯一持有者,可变访问无读者冲突。
let stub_obj = unsafe { &mut *stub_ptr };
let si_prototype = core.perm_interner().intern("prototype").0; let si_prototype = core.perm_interner().intern("prototype").0;
set_data_property( set_data_property(
core, core,
@@ -106,6 +115,7 @@ pub fn bind_stubs(core: &Arc<KernelCore>, session: &mut KernelSession, global: &
JsValue::from_js_object(proto_ptr), JsValue::from_js_object(proto_ptr),
PropAttributes::new(false, false, false), PropAttributes::new(false, false, false),
); );
}
let si_length = core.perm_interner().intern("length").0; let si_length = core.perm_interner().intern("length").0;
set_data_property( set_data_property(
core, core,
+10 -9
View File
@@ -12,15 +12,15 @@ fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
vm.run(&Arc::new(module)) vm.run(&Arc::new(module))
} }
/// 五 stub 构造器原型链钉:prototype 为对象、原型链落 Object.prototype、 /// 四弱族 stub 构造器原型链钉:prototype 为对象、原型链落 Object.prototype、
/// constructor 回指构造器本体。 /// constructor 回指构造器本体;规范 25.1 的 %Proxy% 无 prototype 属性,不在名表。
#[test] #[test]
fn stub_ctor_prototype_chain_all_five() { fn stub_ctor_prototype_chain_weak_family() {
let mut vm = Vm::new(); let mut vm = Vm::new();
let result = eval( let result = eval(
&mut vm, &mut vm,
"(function () { \ "(function () { \
var names = ['Proxy', 'WeakMap', 'WeakSet', 'WeakRef', 'FinalizationRegistry']; \ var names = ['WeakMap', 'WeakSet', 'WeakRef', 'FinalizationRegistry']; \
for (var i = 0; i < names.length; i++) { \ for (var i = 0; i < names.length; i++) { \
var x = globalThis[names[i]]; \ var x = globalThis[names[i]]; \
if (typeof x.prototype !== 'object') { return false; } \ if (typeof x.prototype !== 'object') { return false; } \
@@ -33,8 +33,8 @@ fn stub_ctor_prototype_chain_all_five() {
assert!(result.as_bool()); assert!(result.as_bool());
} }
/// 五 stub 构造器 length/name 值钉 + 非枚举钉:构造器面三属性与原型对象自身 /// 五 stub 构造器 length/name 值钉 + 非枚举钉:构造器面属性(length/name 五族、
/// 属性均不泄漏进 Object.keys。 /// prototype 四弱族)与原型对象自身属性均不泄漏进 Object.keys。
#[test] #[test]
fn stub_ctor_length_name_identity_non_enumerable() { fn stub_ctor_length_name_identity_non_enumerable() {
let mut vm = Vm::new(); let mut vm = Vm::new();
@@ -51,7 +51,7 @@ fn stub_ctor_length_name_identity_non_enumerable() {
for (var k = 0; k < keys.length; k++) { \ for (var k = 0; k < keys.length; k++) { \
if (keys[k] === 'length' || keys[k] === 'name' || keys[k] === 'prototype') { return false; } \ if (keys[k] === 'length' || keys[k] === 'name' || keys[k] === 'prototype') { return false; } \
} \ } \
if (Object.keys(x.prototype).length !== 0) { return false; } \ if (x.prototype !== undefined && Object.keys(x.prototype).length !== 0) { return false; } \
} \ } \
return true; })()", return true; })()",
) )
@@ -76,7 +76,8 @@ fn weak_family_proto_to_string_tag() {
assert!(result.as_bool()); assert!(result.as_bool());
} }
/// 原型成员读不抛钉:缺失方法读返 undefined(非对象 receiver 抛错形态消除)。 /// 原型成员读不抛钉:缺失方法读返 undefined(非对象 receiver 抛错形态消除);
/// %Proxy% 无 prototype 属性,钉其缺失。
#[test] #[test]
fn stub_proto_member_read_returns_undefined() { fn stub_proto_member_read_returns_undefined() {
let mut vm = Vm::new(); let mut vm = Vm::new();
@@ -87,7 +88,7 @@ fn stub_proto_member_read_returns_undefined() {
&& typeof WeakSet.prototype.add === 'undefined' \ && typeof WeakSet.prototype.add === 'undefined' \
&& typeof WeakRef.prototype.deref === 'undefined' \ && typeof WeakRef.prototype.deref === 'undefined' \
&& typeof FinalizationRegistry.prototype.register === 'undefined' \ && typeof FinalizationRegistry.prototype.register === 'undefined' \
&& typeof Proxy.prototype.nonexistent === 'undefined'; })()", && Proxy.prototype === undefined; })()",
) )
.unwrap(); .unwrap();
assert!(result.as_bool()); assert!(result.as_bool());