fix: 补 Function 构造器残面与 TypedArray/Atomics 边

This commit is contained in:
2026-09-24 21:19:27 +00:00
parent 963c91ba75
commit 2dc119874d
8 changed files with 137 additions and 11 deletions
+66 -2
View File
@@ -1,8 +1,9 @@
use std::sync::Arc;
use oxide_kernel::builtin::FunctionMethods;
use oxide_kernel::builtin::{FnWrapperKey, FunctionMethods};
use oxide_kernel::kernel::{KernelCore, KernelSession};
use oxide_types::object::{JsObject, PropAttributes};
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes};
use oxide_types::value::JsValue;
use super::bind_global_value;
@@ -57,5 +58,68 @@ pub fn bind_function(core: &Arc<KernelCore>, session: &KernelSession, global: &m
let name_pos = proto.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
proto.set_data_meta(name_pos, PropAttributes::new(false, false, true));
// caller/arguments 受限访问器:两属性 get/set 共用同一 %ThrowTypeError%
// 函数对象,任何访问抛 TypeError(AddRestrictedFunctionProperties 语义)。
bind_function_proto_restricted(core, session, proto);
bind_global_value(core, global, "Function", JsValue::from_js_object(function_ctor));
}
/// 在 Function.prototype 上绑定 caller/arguments 受限访问器:两属性的
/// get/set 共用同一 %ThrowTypeError% 函数对象(name="get caller"、length=0),
/// 描述符 { enumerable:false, configurable:true }。
///
/// # 副作用
/// - 为 caller/arguments 各开 shape 槽位并写入访问器 meta;thrower 函数对象
/// 登记进 world 释放表(与 `bind_accessor_getter` 的 getter 同一生命周期)。
fn bind_function_proto_restricted(core: &Arc<KernelCore>, session: &KernelSession, proto: &mut JsObject) {
let shape_forge = core.shape_forge().as_ref();
let string_forge = core.perm_interner().as_ref();
let world = session.builtin_world();
let family = world.wrapper_family_of(proto as *const JsObject);
let si_name = string_forge.intern("name").0;
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;
// 构造单一 thrower 函数对象,供两属性 get/set 共用(选择性重建复用:
// 同家族同槽旧 thrower 迁移到新 proto 的访问器槽,不再新建对象)。
// SAFETY: thrower 函数项指针转为 *const ()。
let thrower_fn_ptr = unsafe {
NativeFnPtr::from_raw(oxide_builtins::function::function_restricted_thrower::<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,
None => {
let fn_proto_val = JsValue::from_js_object(world.function_proto.as_ptr() as *mut JsObject);
let mut thrower = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto_val));
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));
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));
let ptr = Box::into_raw(thrower);
world.track_fn_wrapper(ptr, reuse_key);
ptr
}
};
let thrower_val = JsValue::from_js_object(thrower_ptr);
let attrs = PropAttributes::new(false, false, true);
for key in [si_caller, si_arguments] {
let new_shape = shape_forge.make_shape(proto.shape_id(), key);
proto.set_shape_id(new_shape);
let pos = proto.push_prop(JsValue::undefined());
proto.set_accessor_meta(pos, thrower_val, thrower_val, attrs);
proto.bump_generation();
}
}
+4 -2
View File
@@ -34,9 +34,11 @@ impl Vm {
/// # 副作用
/// - 扩展当前代际平表(`tables[current_gen]`)与常量缓存。
pub fn create_dynamic_function(&mut self, params: &[String], body: &str) -> Result<JsValue, String> {
// wrap 源码:末尾换行防止 body 以行注释结尾吞掉右花括号。
// wrap 源码:body 两端换行防止以行注释结尾吞掉右花括号;形参串后补
// 换行,使形参以 HTML 注释(`<!--`/`-->`)结尾时注释在行末终止、
// 右括号不被吞(规范按形参串与体串各自独立解析,此处以换行等价)。
let params_str = params.join(", ");
let source = format!("function anonymous({params_str}) {{\n{body}\n}}");
let source = format!("function anonymous({params_str}\n) {{\n{body}\n}}");
let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, &source)
+5
View File
@@ -174,6 +174,11 @@ impl oxide_runtime_api::VmHost for Vm {
})
.collect()
}
fn function_is_strict(&self, obj: &JsObject) -> bool {
// native 函数(sub_module_index == 0)无子模块,callee_module 返回 None,
// 按严格处理(受限访问器对其抛错)。
self.callee_module(obj).map(|m| m.is_strict).unwrap_or(true)
}
fn promote_if_needed_for_write_ptr(&mut self, target_ptr: *mut JsObject, value: JsValue) -> JsValue {
self.promote_if_needed_for_write_ptr(target_ptr, value)
}