feat: String 盒迁移 boxed_value 载荷并物化固有属性面

This commit is contained in:
2026-09-22 16:40:01 +00:00
parent 1be90d90aa
commit 2252ed7bbc
13 changed files with 432 additions and 149 deletions
@@ -87,6 +87,26 @@ pub(crate) fn tag_number_proto(proto: &P<JsObject>) {
obj.set_boxed_value(JsValue::int(0));
}
/// 把 String.prototype 标成 String 对象本体:规范 22.7.3 规定其是
/// [[StringData]] = 空串的 String object。type_tag 是品牌表判据,boxed 值
/// 供 thisStringValue/valueOf 的通用分支读取;length 自身属性按构造期
/// 物化约定落地(writable:false / enumerable:false / configurable:false)。
pub(crate) fn tag_string_proto(proto: &P<JsObject>, string_forge: &PermInterner, shape_forge: &ShapeForge) {
let ptr = proto.as_ptr() as *mut JsObject;
// SAFETY: proto 是 make_named_pair 刚建的本进程对象,P 引用与裸指针同址。
let obj = unsafe { &mut *ptr };
obj.type_tag = JsObject::OBJ_TYPE_STRING_OBJ;
obj.set_boxed_value(JsValue::perm_string(crate::string_forge::empty_string_ptr()));
let length_si = string_forge.intern("length").0;
let shape_id = shape_forge.make_shape(obj.shape_id(), length_si);
obj.set_shape_id(shape_id);
// push_prop 返回绝对存储下标(尾部槽),meta 按同下标落位,
// 避免写死槽号覆盖既有属性(如 constructor)的元数据。
let length_pos = obj.push_prop(JsValue::int(0));
obj.set_data_meta(length_pos, PropAttributes::new(false, false, false));
obj.bump_generation();
}
pub(crate) fn make_error_subtypes(error_proto: &P<JsObject>) -> ErrorSubtypeProtos {
let error_proto_val = JsValue::from_js_object(error_proto.as_ptr() as *mut JsObject);
ErrorSubtypeProtos {
@@ -448,6 +468,7 @@ impl BuiltinWorld {
let (array_proto, array_constructor) = make_named_pair(string_forge, shape_forge, labels, "Array");
let (function_proto, function_constructor) = make_named_pair(string_forge, shape_forge, labels, "Function");
let (string_proto, string_constructor) = make_named_pair(string_forge, shape_forge, labels, "String");
tag_string_proto(&string_proto, string_forge, shape_forge);
let (number_proto, number_constructor) = make_named_pair(string_forge, shape_forge, labels, "Number");
tag_number_proto(&number_proto);
let (boolean_proto, boolean_constructor) = make_named_pair(string_forge, shape_forge, labels, "Boolean");
+6 -2
View File
@@ -7,7 +7,7 @@ use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use super::construct::{
builtin_labels, make_error_subtypes, make_named_pair, make_typed_array_family, tag_number_proto,
builtin_labels, make_error_subtypes, make_named_pair, make_typed_array_family, tag_number_proto, tag_string_proto,
wire_builtin_world_links, ErrorSubtypeProtos, TypedArrayFamily,
};
use super::BuiltinWorld;
@@ -178,7 +178,11 @@ impl BuiltinWorld {
(current.function_proto.clone(), current.function_constructor.clone())
};
let (string_proto, string_constructor) = if dirty.string {
make_named_pair(string_forge, shape_forge, labels, "String")
// 脏重建与全量构造同形:原型本体须带 String 对象 tag 与空串包值
// (+length 物化),漏此分支 full_reset 后 tag 翻回 "Object"。
let (proto, ctor) = make_named_pair(string_forge, shape_forge, labels, "String");
tag_string_proto(&proto, string_forge, shape_forge);
(proto, ctor)
} else {
(current.string_proto.clone(), current.string_constructor.clone())
};
+23
View File
@@ -399,6 +399,29 @@ pub fn single_char_ptr(ch: u8) -> *const JsString {
}
}
/// 空串永久 `JsString` 指针,首用惰性物化后恒复用同一地址。
/// 供 %StringPrototype% 的 `[[StringData]]` 载荷等零分配场景复用。
static EMPTY_STRING: OnceLock<StringPtr> = OnceLock::new();
/// 取空串的永久 `JsString` 指针,惰性物化一次后恒返回同一地址。
pub fn empty_string_ptr() -> *const JsString {
match EMPTY_STRING.get() {
Some(ptr) => ptr.0,
None => {
let ptr = Box::into_raw(Box::new(JsString::new(String::new())));
match EMPTY_STRING.set(StringPtr(ptr)) {
Ok(()) => ptr,
Err(_) => {
let existing = EMPTY_STRING.get().expect("set 失败时槽必已初始化").0;
// SAFETY: ptr 来自本线程的 Box::into_raw,无任何外部引用。
unsafe { drop(Box::from_raw(ptr)) };
existing
}
}
}
}
}
/// 小整数(0..=99)永久 `JsString` 指针表:`s += j` 等数字叶子拼接的高频命中路径,
/// 免每次 `to_string` + 登记 2 次分配。物化面严格有界:100 条目 ≈ 4KB,
/// 随进程生命周期有意保留(同 `SINGLE_CHAR_TABLE` 论证)。