feat: 共享 ThrowTypeError 对象并拆分 arguments callee 四象限

This commit is contained in:
2026-09-26 15:01:50 +00:00
parent d773cc7282
commit de236fb5d1
11 changed files with 138 additions and 67 deletions
+27 -16
View File
@@ -66,8 +66,14 @@ pub fn bind_function(core: &Arc<KernelCore>, session: &KernelSession, global: &m
}
/// 在 Function.prototype 上绑定 caller/arguments 受限访问器:两属性的
/// get/set 共用同一 %ThrowTypeError% 函数对象(name="get caller"、length=0),
/// 描述符 { enumerable:false, configurable:true }。
/// get/set 共用同一 %ThrowTypeError% 函数对象(匿名、length=0、冻结、
/// 不可扩展),描述符 { enumerable:false, configurable:true }。
///
/// # 步骤
/// 1. 取共享 %ThrowTypeError% 对象:选择性重建复用(同键旧对象迁移),
/// 未命中时新建(length 先于 name、描述符三标志全 false)。
/// 2. 对象指针写入 world Cell(运行期创建 arguments 对象时读取,跨形态恒等)。
/// 3. 为 caller/arguments 各开 shape 槽位并写入访问器 meta。
///
/// # 副作用
/// - 为 caller/arguments 各开 shape 槽位并写入访问器 meta;thrower 函数对象
@@ -81,14 +87,13 @@ fn bind_function_proto_restricted(core: &Arc<KernelCore>, session: &KernelSessio
let si_length = string_forge.intern("length").0;
let si_caller = string_forge.intern("caller").0;
let si_arguments = string_forge.intern("arguments").0;
let si_label = string_forge.intern("get caller").0;
let si_label = string_forge.intern("ThrowTypeError").0;
// 构造单一 thrower 函数对象,供两属性 get/set 共用(选择性重建复用:
// 同家族同槽旧 thrower 迁移到新 proto 的访问器槽,不再新建对象)。
// 取共享 %ThrowTypeError% 对象,供两属性 get/set 与 unmapped arguments.callee
// 访问器共用(选择性重建复用:同键旧对象迁移,不新建,跨形态恒等)。
// SAFETY: thrower 函数项指针转为 *const ()。
let thrower_fn_ptr = unsafe {
NativeFnPtr::from_raw(oxide_builtins::function::function_restricted_thrower::<crate::vm::Vm> as *const ())
};
let thrower_fn_ptr =
unsafe { NativeFnPtr::from_raw(oxide_builtins::function::throw_type_error::<crate::vm::Vm> as *const ()) };
let reuse_key = FnWrapperKey::new(family, si_label, si_caller, si_name);
let thrower_ptr = match world.find_fn_wrapper(reuse_key, thrower_fn_ptr, 0) {
Some(ptr) => ptr,
@@ -98,23 +103,29 @@ fn bind_function_proto_restricted(core: &Arc<KernelCore>, session: &KernelSessio
thrower.set_function(true);
thrower.set_native_fn(Some(thrower_fn_ptr));
thrower.set_native_arg_count(0);
let name_shape = shape_forge.make_shape(thrower.shape_id(), si_name);
thrower.set_shape_id(name_shape);
thrower
.ensure_hash_props()
.push(JsValue::perm_string(string_forge.string_ptr(si_label)));
let name_pos = thrower.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
thrower.set_data_meta(name_pos, PropAttributes::new(false, false, true));
// length 先于 name(ownNames 序 ["length","name"]),描述符
// { writable:false, enumerable:false, configurable:false }。
let length_shape = shape_forge.make_shape(thrower.shape_id(), si_length);
thrower.set_shape_id(length_shape);
thrower.ensure_hash_props().push(JsValue::int(0));
let length_pos = thrower.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
thrower.set_data_meta(length_pos, PropAttributes::new(false, false, true));
thrower.set_data_meta(length_pos, PropAttributes::new(false, false, false));
let name_shape = shape_forge.make_shape(thrower.shape_id(), si_name);
thrower.set_shape_id(name_shape);
thrower
.ensure_hash_props()
.push(JsValue::perm_string(string_forge.string_ptr(string_forge.intern("").0)));
let name_pos = thrower.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
thrower.set_data_meta(name_pos, PropAttributes::new(false, false, false));
thrower.set_extensible(false);
thrower.set_frozen(true);
let ptr = Box::into_raw(thrower);
world.track_fn_wrapper(ptr, reuse_key);
ptr
}
};
// 指针写入 world Cell:运行期创建 arguments 对象时读取,保证跨形态恒等。
world.throw_type_error.set(thrower_ptr);
let thrower_val = JsValue::from_js_object(thrower_ptr);
let attrs = PropAttributes::new(false, false, true);
for key in [si_caller, si_arguments] {
+58 -5
View File
@@ -265,13 +265,13 @@ impl Vm {
}
/// 创建 arguments 对象:索引属性取当前帧(或 inline 同步调用)的完整实参,
/// 附 length / callee / @@iterator 属性。第一版为 unmapped(非严格)语义,
/// 索引与形参不同步。
/// 附 length / callee / @@iterator 属性。索引与形参不同步(mapped 同步语义
/// 未实现);callee 按 strict ‖ !simple 分流为受限访问器或数据属性。
///
/// # 边界与前提
/// - 实参区由统一压帧入口 `push_bytecode_frame` 在推帧时写入 spill 栈;
/// frames 为空(inline 同步调用)时读 `inline_args_*`。
/// - 索引属性用 shape 槽存储(普通对象),length/callee 为不可枚举数据属性。
/// - 索引属性用 shape 槽存储(普通对象),length 为不可枚举数据属性。
pub(crate) fn dispatch_create_arguments(&mut self, rd: usize) -> Result<(), String> {
let (base, count) = match self.frames.last() {
Some(frame) => (frame.arguments_base, frame.arguments_count),
@@ -301,13 +301,66 @@ impl Vm {
return self.raise_error_kind("TypeError", &msg);
}
// callee:当前执行函数,可写、不可枚举、可配置(严格模式应抛 TypeError,未支持)。
// callee:按 strict 与 simple 参数列表四分流(规范 CreateMappedArgumentsObject /
// CreateUnmappedArgumentsObject):strict ‖ !simple → 受限访问器(get/set
// 共享 %ThrowTypeError%,访问即抛);sloppy simple → 数据属性(值 = 当前
// 函数本身,可写、不可枚举、可配置)。
let callee_si = self.kernel_core.perm_interner().intern("callee").0;
let callee = self.current_callee().unwrap_or(JsValue::undefined());
if let Err(msg) = self.define_data_property(obj, callee_si, callee, PropAttributes::new(true, false, true)) {
let strict = self.current_strict();
let simple = self.active_module().is_some_and(|m| m.has_simple_params);
if strict || !simple {
// SAFETY: 指针由绑定层在 session 构造期写入,session 存活期内有效。
let thrower_ptr = self.session.builtin_world().throw_type_error.get();
if thrower_ptr.is_null() {
// 绑定层尚未写入共享对象(正常路径不出现):退化为数据属性保调用可用。
if let Err(msg) =
self.define_data_property(obj, callee_si, callee, PropAttributes::new(true, false, true))
{
return self.raise_error_kind("TypeError", &msg);
}
} else {
// SAFETY: thrower_ptr 由绑定层写入的存活对象,转 *mut 供 from_js_object 消费。
let thrower_val = JsValue::from_js_object(unsafe { &*thrower_ptr } as *const JsObject as *mut JsObject);
if let Err(msg) = self.define_accessor_property(
obj,
callee_si,
thrower_val,
thrower_val,
PropAttributes::new(false, false, false),
) {
return self.raise_error_kind("TypeError", &msg);
}
}
} else if let Err(msg) =
self.define_data_property(obj, callee_si, callee, PropAttributes::new(true, false, true))
{
return self.raise_error_kind("TypeError", &msg);
}
// sloppy 函数:own caller/arguments 数据属性(ES5 遗留,node 行为)——
// caller = 调用方函数(帧栈次顶帧 callee),arguments = 本对象。strict
// 函数无 own caller/arguments,经原型链解析到 FP 受限访问器(访问即抛)。
if !strict && callee.is_object() {
let caller = self
.frames
.get(self.frames.len().saturating_sub(2))
.map(|f| f.callee)
.unwrap_or(JsValue::undefined());
let callee_obj = unsafe { &mut *callee.as_js_object_ptr() };
let caller_si = self.kernel_core.perm_interner().intern("caller").0;
let arguments_si = self.kernel_core.perm_interner().intern("arguments").0;
let data_attrs = PropAttributes::new(true, false, true);
if let Err(msg) = self.define_data_property(callee_obj, caller_si, caller, data_attrs) {
return self.raise_error_kind("TypeError", &msg);
}
if let Err(msg) =
self.define_data_property(callee_obj, arguments_si, JsValue::from_js_object(obj_ptr), data_attrs)
{
return self.raise_error_kind("TypeError", &msg);
}
}
// @@iterator:与 Array.prototype[Symbol.iterator] 共享同一函数对象,
// 使 arguments 对象可迭代(可写、不可枚举、可配置)。
let sym_iter_si = make_well_known_symbol_key(0);