From 7a0d3ce336e97d414f44a8c48ebc9c2c027957a7 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Thu, 24 Sep 2026 05:09:34 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20Proxy=20stub=20=E4=B8=8D=E8=A3=85?= =?UTF-8?q?=E8=A7=84=E8=8C=83=E4=B8=8D=E5=AD=98=E5=9C=A8=E7=9A=84=20protot?= =?UTF-8?q?ype=20=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_vm/src/bindings/bind_stubs.rs | 86 +++++++++++-------- crates/oxide_vm/tests/builtins_stubs_tests.rs | 51 +++++------ 2 files changed, 74 insertions(+), 63 deletions(-) diff --git a/crates/oxide_vm/src/bindings/bind_stubs.rs b/crates/oxide_vm/src/bindings/bind_stubs.rs index 724d1a62..c50bff6b 100644 --- a/crates/oxide_vm/src/bindings/bind_stubs.rs +++ b/crates/oxide_vm/src/bindings/bind_stubs.rs @@ -8,34 +8,41 @@ use oxide_types::value::JsValue; use crate::bindings::{bind_global_value, configure_native_constructor}; -/// 未实现内置的 stub 表:(全局名、native 函数项指针、形参个数、原型 @@toStringTag)。 +/// 未实现内置的 stub 表:(全局名、native 函数项指针、形参个数、 +/// 原型 @@toStringTag、是否安装原型对象)。 /// /// `tag` 为 `None` 的条目(Proxy)原型对象不设 @@toStringTag;四弱族各取族名。 -const STUBS: [(&str, *const (), u8, Option<&str>); 5] = [ - ("Proxy", oxide_builtins::stubs::proxy_stub:: as *const (), 2, None), +/// 规范 25.1 的 %Proxy% 无 prototype 属性(`has_prototype` = false), +/// 四弱族各装带 constructor 回指的原型对象。 +const STUBS: [(&str, *const (), u8, Option<&str>, bool); 5] = [ + ("Proxy", oxide_builtins::stubs::proxy_stub:: as *const (), 2, None, false), ( "WeakMap", oxide_builtins::stubs::weakmap_stub:: as *const (), 0, Some("WeakMap"), + true, ), ( "WeakSet", oxide_builtins::stubs::weakset_stub:: as *const (), 0, Some("WeakSet"), + true, ), ( "WeakRef", oxide_builtins::stubs::weakref_stub:: as *const (), 1, Some("WeakRef"), + true, ), ( "FinalizationRegistry", oxide_builtins::stubs::finalization_registry_stub:: as *const (), 1, Some("FinalizationRegistry"), + true, ), ]; @@ -53,16 +60,17 @@ fn set_data_property(core: &Arc, obj: &mut JsObject, si: u32, value: /// 绑定到 global,并登记到 `stub_objects` 供快照跟踪。 /// /// # 副作用 -/// 每个 stub 按规范补装构造器面属性:`prototype`(新建 plain 对象,原型链落 -/// Object.prototype,其上带指回 stub 的 `constructor`,四弱族另带 @@toStringTag)、 -/// `length`、`name`;描述符均不可枚举,`prototype` 另不可写、不可配置。 +/// 每个 stub 按规范补装构造器面属性:`length`、`name`(五族全装);四弱族 +/// 另装 `prototype`(新建 plain 对象,原型链落 Object.prototype,其上带指回 +/// stub 的 `constructor` 与 @@toStringTag)——规范 25.1 的 %Proxy% 无 +/// prototype 属性,不装;描述符均不可枚举,`prototype` 另不可写、不可配置。 /// 原型对象登记进 world 释放登记表,与 stub 对象同生死,session 收尾统一释放。 pub fn bind_stubs(core: &Arc, session: &mut KernelSession, global: &mut JsObject) { let builtin_world = Arc::get_mut(&mut session.builtin_world) .expect("BuiltinWorld must be uniquely owned during init_kernel_builtins"); 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( EMPTY_SHAPE_ID, JsValue::from_js_object(builtin_world.function_proto.as_ptr() as *mut JsObject), @@ -73,39 +81,41 @@ pub fn bind_stubs(core: &Arc, session: &mut KernelSession, global: & let stub = P::new(stub); let stub_ptr = stub.as_ptr() as *mut JsObject; - // 原型对象:constructor 回指 + @@toStringTag(四弱族);属性装完再登记 - // 释放登记表,登记点世代即洁净基线。 - 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; - set_data_property( - core, - &mut proto, - si_constructor, - JsValue::from_js_object(stub_ptr), - PropAttributes::new(true, false, true), - ); - if let Some(tag_name) = tag { - let tag_key = oxide_types::private_key::make_well_known_symbol_key( - oxide_types::private_key::WELL_KNOWN_SYMBOL_TO_STRING_TAG, - ); - let tag_val = - JsValue::perm_string(core.perm_interner().string_ptr(core.perm_interner().intern(tag_name).0)); - set_data_property(core, &mut proto, tag_key, tag_val, PropAttributes::new(false, false, true)); - } - let proto_ptr = Box::into_raw(Box::new(proto)); - builtin_world.track_leaked_object(proto_ptr); - - // 构造器面属性:prototype / length / name。 + // 构造器面属性:length / name 五族全装;prototype 仅四弱族(%Proxy% 无)。 // SAFETY: stub 尚未发布到 global,局部 Arc 为唯一持有者,可变访问无读者冲突。 let stub_obj = unsafe { &mut *stub_ptr }; - let si_prototype = core.perm_interner().intern("prototype").0; - set_data_property( - core, - stub_obj, - si_prototype, - JsValue::from_js_object(proto_ptr), - PropAttributes::new(false, false, false), - ); + if has_prototype { + // 原型对象:constructor 回指 + @@toStringTag(四弱族);属性装完再 + // 登记释放登记表,登记点世代即洁净基线。 + 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; + set_data_property( + core, + &mut proto, + si_constructor, + JsValue::from_js_object(stub_ptr), + PropAttributes::new(true, false, true), + ); + if let Some(tag_name) = tag { + let tag_key = oxide_types::private_key::make_well_known_symbol_key( + oxide_types::private_key::WELL_KNOWN_SYMBOL_TO_STRING_TAG, + ); + let tag_val = + JsValue::perm_string(core.perm_interner().string_ptr(core.perm_interner().intern(tag_name).0)); + set_data_property(core, &mut proto, tag_key, tag_val, PropAttributes::new(false, false, true)); + } + let proto_ptr = Box::into_raw(Box::new(proto)); + builtin_world.track_leaked_object(proto_ptr); + + let si_prototype = core.perm_interner().intern("prototype").0; + set_data_property( + core, + stub_obj, + si_prototype, + JsValue::from_js_object(proto_ptr), + PropAttributes::new(false, false, false), + ); + } let si_length = core.perm_interner().intern("length").0; set_data_property( core, diff --git a/crates/oxide_vm/tests/builtins_stubs_tests.rs b/crates/oxide_vm/tests/builtins_stubs_tests.rs index 1c3b7e60..1d98877b 100644 --- a/crates/oxide_vm/tests/builtins_stubs_tests.rs +++ b/crates/oxide_vm/tests/builtins_stubs_tests.rs @@ -12,15 +12,15 @@ fn eval(vm: &mut Vm, source: &str) -> Result { vm.run(&Arc::new(module)) } -/// 五 stub 构造器原型链钉:prototype 为对象、原型链落 Object.prototype、 -/// constructor 回指构造器本体。 +/// 四弱族 stub 构造器原型链钉:prototype 为对象、原型链落 Object.prototype、 +/// constructor 回指构造器本体;规范 25.1 的 %Proxy% 无 prototype 属性,不在名表。 #[test] -fn stub_ctor_prototype_chain_all_five() { +fn stub_ctor_prototype_chain_weak_family() { let mut vm = Vm::new(); let result = eval( &mut vm, "(function () { \ - var names = ['Proxy', 'WeakMap', 'WeakSet', 'WeakRef', 'FinalizationRegistry']; \ + var names = ['WeakMap', 'WeakSet', 'WeakRef', 'FinalizationRegistry']; \ for (var i = 0; i < names.length; i++) { \ var x = globalThis[names[i]]; \ if (typeof x.prototype !== 'object') { return false; } \ @@ -33,27 +33,27 @@ fn stub_ctor_prototype_chain_all_five() { assert!(result.as_bool()); } -/// 五 stub 构造器 length/name 值钉 + 非枚举钉:构造器面三属性与原型对象自身 -/// 属性均不泄漏进 Object.keys。 +/// 五 stub 构造器 length/name 值钉 + 非枚举钉:构造器面属性(length/name 五族、 +/// prototype 四弱族)与原型对象自身属性均不泄漏进 Object.keys。 #[test] fn stub_ctor_length_name_identity_non_enumerable() { let mut vm = Vm::new(); let result = eval( &mut vm, "(function () { \ - var names = ['Proxy', 'WeakMap', 'WeakSet', 'WeakRef', 'FinalizationRegistry']; \ - var expect = { Proxy: 2, WeakMap: 0, WeakSet: 0, WeakRef: 1, FinalizationRegistry: 1 }; \ - for (var i = 0; i < names.length; i++) { \ - var name = names[i]; \ - var x = globalThis[name]; \ - if (x.length !== expect[name] || x.name !== name) { return false; } \ - var keys = Object.keys(x); \ - for (var k = 0; k < keys.length; k++) { \ - if (keys[k] === 'length' || keys[k] === 'name' || keys[k] === 'prototype') { return false; } \ - } \ - if (Object.keys(x.prototype).length !== 0) { return false; } \ - } \ - return true; })()", + var names = ['Proxy', 'WeakMap', 'WeakSet', 'WeakRef', 'FinalizationRegistry']; \ + var expect = { Proxy: 2, WeakMap: 0, WeakSet: 0, WeakRef: 1, FinalizationRegistry: 1 }; \ + for (var i = 0; i < names.length; i++) { \ + var name = names[i]; \ + var x = globalThis[name]; \ + if (x.length !== expect[name] || x.name !== name) { return false; } \ + var keys = Object.keys(x); \ + for (var k = 0; k < keys.length; k++) { \ + if (keys[k] === 'length' || keys[k] === 'name' || keys[k] === 'prototype') { return false; } \ + } \ + if (x.prototype !== undefined && Object.keys(x.prototype).length !== 0) { return false; } \ + } \ + return true; })()", ) .unwrap(); assert!(result.as_bool()); @@ -76,18 +76,19 @@ fn weak_family_proto_to_string_tag() { assert!(result.as_bool()); } -/// 原型成员读不抛钉:缺失方法读返 undefined(非对象 receiver 抛错形态消除)。 +/// 原型成员读不抛钉:缺失方法读返 undefined(非对象 receiver 抛错形态消除); +/// %Proxy% 无 prototype 属性,钉其缺失。 #[test] fn stub_proto_member_read_returns_undefined() { let mut vm = Vm::new(); let result = eval( &mut vm, "(function () { \ - return typeof WeakMap.prototype.get === 'undefined' \ - && typeof WeakSet.prototype.add === 'undefined' \ - && typeof WeakRef.prototype.deref === 'undefined' \ - && typeof FinalizationRegistry.prototype.register === 'undefined' \ - && typeof Proxy.prototype.nonexistent === 'undefined'; })()", + return typeof WeakMap.prototype.get === 'undefined' \ + && typeof WeakSet.prototype.add === 'undefined' \ + && typeof WeakRef.prototype.deref === 'undefined' \ + && typeof FinalizationRegistry.prototype.register === 'undefined' \ + && Proxy.prototype === undefined; })()", ) .unwrap(); assert!(result.as_bool());