diff --git a/crates/oxide_types/src/object.rs b/crates/oxide_types/src/object.rs index 17629d62..441fc974 100644 --- a/crates/oxide_types/src/object.rs +++ b/crates/oxide_types/src/object.rs @@ -14,8 +14,8 @@ //! `JsObject` / `ShapeId` / `MAX_DENSE_PROPS`),显式名单防 `pub` 面无声扩大。 //! //! 布局说明块位于 `JsObject` 定义上方(随结构体,不在模块头文档); -//! 尺寸守护为 `object_size_bounds` 测试的 256B 上界,与布局块字段总字节数 -//! 并存、不统一。 +//! 尺寸守护为 `js_object_exact_size` 测试的精确字节钉,与 `object_size_bounds` +//! 的 256B 上界并存。 use crate::value::JsValue; @@ -75,18 +75,19 @@ pub const MAX_DENSE_PROPS: usize = 1_000_000; /// generation: u32 (4 字节) /// array_prop_count: u32 (4 字节,数组元素数,普通对象恒 0) /// array_len_override: u32 (4 字节,逻辑长度覆盖,length 超 dense 上限时非 0) -/// native_fn: Option\ (16 字节 — 裸 `*const ()` 无法利用 Option\ -/// 优化;因 repr(Rust) 布局规则存为包装 8 字节指针的 Option,带 8 字节 -/// 判别式填充) +/// native_fn: Option\ (8 字节 — NativeFnPtr 是 NonNull 指针, +/// Option 借 null niche 缩为单指针) /// sub_module_index: u32 (4 字节,子模块平表下标,与 table_gen 配对解析) /// table_gen: u32 (4 字节,创建期所属子模块平表的表代际) /// captured_this: JsValue (8 字节,箭头函数的词法 this) /// home_object: JsValue (8 字节,\[\[HomeObject\]\],供 super 查找) /// boxed_value: JsValue (8 字节,装箱对象的被包基元载荷,见字段注释) +/// regexp_source: JsValue (8 字节,RegExp 实例的 source 字符串) +/// regexp_flags: JsValue (8 字节,RegExp 实例的 flags 字符串) /// upvalues: *mut u8 (8 字节,指向闭包的 Box>) /// -/// 字段自和:124 字节,另有 4 字节对齐填充(native_fn 之前) -/// 总计:128 字节 +/// 字段自和:132 字节,另有 4 字节尾部对齐填充 +/// 总计:136 字节 /// 对齐:8 字节 pub struct JsObject { header: u32, diff --git a/crates/oxide_types/src/object/native_fn.rs b/crates/oxide_types/src/object/native_fn.rs index 2e0a9fdf..7c94713f 100644 --- a/crates/oxide_types/src/object/native_fn.rs +++ b/crates/oxide_types/src/object/native_fn.rs @@ -1,14 +1,16 @@ //! 原生函数指针的类型安全不透明包装。 //! -//! 以 `*const ()` 存储而非具体 `fn` 类型,使本 crate 无需依赖 `oxide_vm`; +//! 以 `NonNull<()>` 存储而非具体 `fn` 类型,使本 crate 无需依赖 `oxide_vm`; //! 指针必须由合法的 `NativeFn` 函数项创建且永不为空,`Send + Sync` 依赖 //! 函数项指针天然线程安全。 +use std::ptr::NonNull; + /// 原生函数指针的类型安全不透明包装。 /// -/// 以 `*const ()` 存储而非具体 `fn` 类型,使 `oxide_types` 无需依赖 -/// `oxide_vm::Vm`。`oxide_vm` 中的调用方经 `NativeFnPtr::call_with` 转回 -/// `NativeFn`——transmute 被限制在单个泛型辅助函数中。 +/// 以 `NonNull<()>` 存储而非具体 `fn` 类型,使 `oxide_types` 无需依赖 +/// `oxide_vm::Vm`。`oxide_vm` 中的调用方经 `native_fn_ptr_to_fn` 转回 +/// `NativeFn`——transmute 被限制在单个辅助函数中。 /// /// # Safety 不变量 /// @@ -17,7 +19,7 @@ /// 函数项指针天然线程安全(不含数据)。 #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(transparent)] -pub struct NativeFnPtr(pub *const ()); +pub struct NativeFnPtr(pub NonNull<()>); impl NativeFnPtr { /// 包装裸函数指针。指针必须指向合法的 `NativeFn` 函数项。 @@ -28,13 +30,15 @@ impl NativeFnPtr { #[inline(always)] pub unsafe fn from_raw(ptr: *const ()) -> Self { debug_assert!(!ptr.is_null(), "NativeFnPtr must not be null"); - Self(ptr) + // SAFETY: 非空性由调用方契约保证,debug 断言兜底;const→mut 仅为 + // NonNull 构造所需的类型形态,不改变所指。 + Self(unsafe { NonNull::new_unchecked(ptr as *mut ()) }) } /// 返回底层裸指针。 #[inline(always)] pub fn as_ptr(self) -> *const () { - self.0 + self.0.as_ptr() } } diff --git a/crates/oxide_types/src/object/tests.rs b/crates/oxide_types/src/object/tests.rs index 17915c4e..7b3264d6 100644 --- a/crates/oxide_types/src/object/tests.rs +++ b/crates/oxide_types/src/object/tests.rs @@ -7,6 +7,14 @@ fn object_size_bounds() { assert!(sz <= 256, "JsObject grew unexpectedly: {sz}B"); } +#[test] +fn js_object_exact_size() { + // 精确钉当前布局:字段自和 132 字节 + 4 字节尾部对齐填充。 + // 对象头尺寸是 GC 账目与内存基线的锚,静默膨胀须在此显式失败。 + let sz = std::mem::size_of::(); + assert_eq!(sz, 136, "JsObject size drifted: {sz}B"); +} + #[test] fn new_empty_defaults() { let obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null());