perf: 原生函数指针改 NonNull 使对象头缩至 136 字节

This commit is contained in:
2026-09-26 17:57:25 +00:00
parent 6a9ae54e5d
commit f1cd2cd612
3 changed files with 27 additions and 14 deletions
+8 -7
View File
@@ -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\<NativeFnPtr\> (16 字节 — 裸 `*const ()` 无法利用 Option\<NonNull\>
/// 优化;因 repr(Rust) 布局规则存为包装 8 字节指针的 Option,带 8 字节
/// 判别式填充)
/// native_fn: Option\<NativeFnPtr\> (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<Vec<*mut Cell>>)
///
/// 字段自和:124 字节,另有 4 字节对齐填充(native_fn 之前)
/// 总计:128 字节
/// 字段自和:132 字节,另有 4 字节尾部对齐填充
/// 总计:136 字节
/// 对齐:8 字节
pub struct JsObject {
header: u32,
+11 -7
View File
@@ -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()
}
}
+8
View File
@@ -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::<JsObject>();
assert_eq!(sz, 136, "JsObject size drifted: {sz}B");
}
#[test]
fn new_empty_defaults() {
let obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null());