mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-26 19:51:11 +08:00
refactor: builtin 脏重建段拆出独立 rebuild 模块
This commit is contained in:
@@ -5,13 +5,14 @@ use oxide_types::mem::P;
|
|||||||
use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes};
|
use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes};
|
||||||
use oxide_types::value::JsValue;
|
use oxide_types::value::JsValue;
|
||||||
|
|
||||||
use crate::kernel::{BuiltinDirtySet, BuiltinId};
|
use crate::kernel::BuiltinId;
|
||||||
use crate::kernel_info;
|
use crate::kernel_info;
|
||||||
use crate::shape_forge::{ShapeForge, EMPTY_SHAPE_ID};
|
use crate::shape_forge::{ShapeForge, EMPTY_SHAPE_ID};
|
||||||
use crate::string_forge::PermInterner;
|
use crate::string_forge::PermInterner;
|
||||||
|
|
||||||
mod bind;
|
mod bind;
|
||||||
mod methods;
|
mod methods;
|
||||||
|
mod rebuild;
|
||||||
pub use methods::{ArrayMethods, ErrorMethods, FunctionMethods, ObjectMethods, RegExpMethods, StringMethods};
|
pub use methods::{ArrayMethods, ErrorMethods, FunctionMethods, ObjectMethods, RegExpMethods, StringMethods};
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
@@ -678,113 +679,6 @@ impl BuiltinWorld {
|
|||||||
self.leaked_objects.borrow_mut().append(&mut from.leaked_objects.borrow_mut());
|
self.leaked_objects.borrow_mut().append(&mut from.leaked_objects.borrow_mut());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 选择性重建收尾:把保留对象(登记表 wrapper + 新旧 world 共用的保留 P
|
|
||||||
/// 字段)的 proto 槽从被替换旧指针重指到新指针,随后逐一释放被替换旧 P
|
|
||||||
/// 对象的属性区(含保活钉住的 Function/Object 四件;本体钉保留,见
|
|
||||||
/// `rebuild_with_dirty` 的保活注记)。
|
|
||||||
///
|
|
||||||
/// # 边界与前提
|
|
||||||
/// - 须在 `inherit_leaked_objects` 之后、旧 world 换出前调用(登记表完整;
|
|
||||||
/// full_reset 安全点无并发读者);重指必须先于释放完成,否则保留对象
|
|
||||||
/// 的原型链读到已释放属性区。
|
|
||||||
/// - 替换集由逐字段新旧指针比较(stubs 按指针集合)判定,与保留集天然
|
|
||||||
/// 不相交:未替换字段新旧 world 沿用同一 Arc,其属性区归 session 收尾
|
|
||||||
/// (`teardown_heap_data`)释放,此处不碰,不双放。
|
|
||||||
/// - 保活钉住的 4 件本体不经任何路径释放(Arc 计数已被 `mem::forget`
|
|
||||||
/// 抬升);此处只释放其属性区,漏重指时读者降级为属性静默缺失
|
|
||||||
/// (zombie 本体)而非 UAF。
|
|
||||||
/// - 只重指 proto 槽;属性值链接由同家族同批替换与绑定层 sync_* 覆盖,
|
|
||||||
/// 不在此重指。
|
|
||||||
///
|
|
||||||
/// # 副作用
|
|
||||||
/// 保留对象 proto 槽重指(generation 递增);被替换旧 P 对象属性区四区
|
|
||||||
/// 释放并置空(幂等,重入为 no-op)。
|
|
||||||
pub fn retire_replaced(&self, old: &BuiltinWorld) {
|
|
||||||
// 替换映射:逐字段新旧指针比较(stubs 按指针集合),记录被换出的
|
|
||||||
// 旧指针 → 新指针;stub 无继任者记空指针,只进释放集。
|
|
||||||
let old_fields = old.all_p_fields();
|
|
||||||
let new_fields = self.all_p_fields();
|
|
||||||
let mut remap: std::collections::HashMap<*mut JsObject, *mut JsObject> = std::collections::HashMap::new();
|
|
||||||
for (o, n) in old_fields.iter().zip(new_fields.iter()) {
|
|
||||||
let (op, np) = (o.as_ptr() as *mut JsObject, n.as_ptr() as *mut JsObject);
|
|
||||||
if !std::ptr::eq(op, np) {
|
|
||||||
remap.insert(op, np);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for p in &old.stub_objects {
|
|
||||||
let op = p.as_ptr() as *mut JsObject;
|
|
||||||
if !self.stub_objects.iter().any(|q| std::ptr::eq(q.as_ptr() as *mut JsObject, op)) {
|
|
||||||
remap.insert(op, std::ptr::null_mut());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if remap.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 重指:保留对象(新旧 world 同一指针)proto 槽仍指被替换旧指针的,
|
|
||||||
// 换新指针——与 `wire_builtin_world_links` 的 set_proto_if_changed 同模式。
|
|
||||||
let repoint = |obj: &mut JsObject| {
|
|
||||||
let cur = obj.proto();
|
|
||||||
if !cur.is_object() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let cur_ptr = cur.as_js_object_ptr();
|
|
||||||
let Some(np) = remap.get(&cur_ptr).copied() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
if np.is_null() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// SAFETY: np 是本 world 的 P 对象;full_reset 安全点无并发读者,
|
|
||||||
// 成环检查由 set_proto 内部完成。
|
|
||||||
obj.set_proto(JsValue::from_js_object(np)).ok();
|
|
||||||
};
|
|
||||||
for (o, n) in old_fields.iter().zip(new_fields.iter()) {
|
|
||||||
let op = o.as_ptr() as *mut JsObject;
|
|
||||||
let np = n.as_ptr() as *mut JsObject;
|
|
||||||
if std::ptr::eq(op, np) {
|
|
||||||
// SAFETY: op/np 指向同一保留 P 对象,重指安全点无并发读者。
|
|
||||||
unsafe {
|
|
||||||
repoint(&mut *np);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for slot in self.leaked_objects.borrow().iter() {
|
|
||||||
let ptr = slot.ptr;
|
|
||||||
// SAFETY: 登记表指针 session 存活期内有效,重指安全点无并发读者。
|
|
||||||
unsafe {
|
|
||||||
repoint(&mut *ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 守约:重指后保留对象 proto 槽不得残留任何被替换旧指针。
|
|
||||||
for (o, n) in old_fields.iter().zip(new_fields.iter()) {
|
|
||||||
let op = o.as_ptr() as *mut JsObject;
|
|
||||||
let np = n.as_ptr() as *mut JsObject;
|
|
||||||
if std::ptr::eq(op, np) {
|
|
||||||
let cur = unsafe { (*np).proto() };
|
|
||||||
debug_assert!(
|
|
||||||
!cur.is_object() || !remap.contains_key(&cur.as_js_object_ptr()),
|
|
||||||
"保留字段 proto 槽不得残留被替换旧指针"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for slot in self.leaked_objects.borrow().iter() {
|
|
||||||
let cur = unsafe { (*slot.ptr).proto() };
|
|
||||||
debug_assert!(
|
|
||||||
!cur.is_object() || !remap.contains_key(&cur.as_js_object_ptr()),
|
|
||||||
"登记表 wrapper proto 槽不得残留被替换旧指针"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// 释放:被替换旧 P 对象属性区逐一恰好释放一次(本体不释放;保活钉住
|
|
||||||
// 的 4 件保留本体钉、属性区同样释放)。
|
|
||||||
for &op in remap.keys() {
|
|
||||||
// SAFETY: op 是旧 world 被换出的 P 对象,属性区仅此一处释放并置空
|
|
||||||
// (幂等);full_reset 安全点无并发读者。
|
|
||||||
unsafe {
|
|
||||||
(&mut *op).release_raw_heap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 枚举本 world 全部固定 P 对象字段(按结构体字段序,含迭代器原型族、
|
/// 枚举本 world 全部固定 P 对象字段(按结构体字段序,含迭代器原型族、
|
||||||
/// stub 之外的全部命名空间对象与 console)。
|
/// stub 之外的全部命名空间对象与 console)。
|
||||||
///
|
///
|
||||||
@@ -1233,448 +1127,6 @@ impl BuiltinWorld {
|
|||||||
kernel_info!("BuiltinWorld initialized");
|
kernel_info!("BuiltinWorld initialized");
|
||||||
world
|
world
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 按脏标记选择性重建 builtin world:仅重建被污染的对象家族,未污染的保留原指针。
|
|
||||||
///
|
|
||||||
/// # 注意事项
|
|
||||||
/// - Function/Object 家族脏时,旧 fn_proto/object_proto 对须先保活再重建:
|
|
||||||
/// 释放表统一持有的方法 wrapper 永久泄漏(`Box::into_raw`),其 proto 裸指针
|
|
||||||
/// 指向绑定时的 function_proto——执行期原型链查找(如 `push.call` 沿 wrapper
|
|
||||||
/// 原型链取 `call`)仍走这些对象,reset 清空执行状态不阻断该路径,旧对
|
|
||||||
/// Arc 归零即悬空。`retire_replaced` 重指完成后旧对无读者;本体钉保留
|
|
||||||
/// (每次 dirty rebuild 至多 4 个对象本体永久泄漏)作重指遗漏兜底——漏
|
|
||||||
/// 重指时读者降级为属性静默缺失(zombie 本体)而非 UAF,属性区于重指
|
|
||||||
/// 完成时由 `retire_replaced` 释放。
|
|
||||||
pub fn rebuild_with_dirty(
|
|
||||||
current: &BuiltinWorld, string_forge: &PermInterner, shape_forge: &ShapeForge, dirty: &BuiltinDirtySet,
|
|
||||||
) -> BuiltinWorld {
|
|
||||||
let labels = builtin_labels(string_forge);
|
|
||||||
|
|
||||||
// 保活:钉住旧 Function/Object 对的 Arc 计数使其永不归零——保留对象的
|
|
||||||
// proto 槽重指在 `retire_replaced`(本函数返回后、旧 world 换出前)
|
|
||||||
// 完成,钉住的本体是重指遗漏的兜底,不经任何路径释放。
|
|
||||||
if dirty.function || dirty.object {
|
|
||||||
std::mem::forget(current.function_proto.clone());
|
|
||||||
std::mem::forget(current.function_constructor.clone());
|
|
||||||
std::mem::forget(current.object_proto.clone());
|
|
||||||
std::mem::forget(current.object_constructor.clone());
|
|
||||||
}
|
|
||||||
let (object_proto, object_constructor) = if dirty.object {
|
|
||||||
make_named_pair(string_forge, shape_forge, labels, "Object")
|
|
||||||
} else {
|
|
||||||
(current.object_proto.clone(), current.object_constructor.clone())
|
|
||||||
};
|
|
||||||
let (array_proto, array_constructor) = if dirty.array {
|
|
||||||
make_named_pair(string_forge, shape_forge, labels, "Array")
|
|
||||||
} else {
|
|
||||||
(current.array_proto.clone(), current.array_constructor.clone())
|
|
||||||
};
|
|
||||||
let (function_proto, function_constructor) = if dirty.function {
|
|
||||||
make_named_pair(string_forge, shape_forge, labels, "Function")
|
|
||||||
} else {
|
|
||||||
(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")
|
|
||||||
} else {
|
|
||||||
(current.string_proto.clone(), current.string_constructor.clone())
|
|
||||||
};
|
|
||||||
let (number_proto, number_constructor) = if dirty.number {
|
|
||||||
make_named_pair(string_forge, shape_forge, labels, "Number")
|
|
||||||
} else {
|
|
||||||
(current.number_proto.clone(), current.number_constructor.clone())
|
|
||||||
};
|
|
||||||
let (boolean_proto, boolean_constructor) = if dirty.boolean {
|
|
||||||
make_named_pair(string_forge, shape_forge, labels, "Boolean")
|
|
||||||
} else {
|
|
||||||
(current.boolean_proto.clone(), current.boolean_constructor.clone())
|
|
||||||
};
|
|
||||||
let (error_proto, error_constructor, error_subtypes) = if dirty.error_family {
|
|
||||||
let (error_proto, error_constructor) = make_named_pair(string_forge, shape_forge, labels, "Error");
|
|
||||||
let error_subtypes = make_error_subtypes(&error_proto);
|
|
||||||
(error_proto, error_constructor, error_subtypes)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
current.error_proto.clone(),
|
|
||||||
current.error_constructor.clone(),
|
|
||||||
ErrorSubtypeProtos {
|
|
||||||
type_error_proto: current.type_error_proto.clone(),
|
|
||||||
reference_error_proto: current.reference_error_proto.clone(),
|
|
||||||
range_error_proto: current.range_error_proto.clone(),
|
|
||||||
syntax_error_proto: current.syntax_error_proto.clone(),
|
|
||||||
uri_error_proto: current.uri_error_proto.clone(),
|
|
||||||
eval_error_proto: current.eval_error_proto.clone(),
|
|
||||||
suppressed_error_proto: current.suppressed_error_proto.clone(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let (
|
|
||||||
symbol_proto,
|
|
||||||
symbol_constructor,
|
|
||||||
sym_match,
|
|
||||||
sym_replace,
|
|
||||||
sym_search,
|
|
||||||
sym_split,
|
|
||||||
sym_iterator,
|
|
||||||
sym_to_primitive,
|
|
||||||
sym_has_instance,
|
|
||||||
sym_match_all,
|
|
||||||
sym_async_iterator,
|
|
||||||
sym_to_string_tag,
|
|
||||||
sym_species,
|
|
||||||
sym_async_dispose,
|
|
||||||
sym_dispose,
|
|
||||||
) = if dirty.symbol_family {
|
|
||||||
let (symbol_proto, symbol_constructor) = make_named_pair(string_forge, shape_forge, labels, "Symbol");
|
|
||||||
(
|
|
||||||
symbol_proto,
|
|
||||||
symbol_constructor,
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
current.symbol_proto.clone(),
|
|
||||||
current.symbol_constructor.clone(),
|
|
||||||
current.sym_match.clone(),
|
|
||||||
current.sym_replace.clone(),
|
|
||||||
current.sym_search.clone(),
|
|
||||||
current.sym_split.clone(),
|
|
||||||
current.sym_iterator.clone(),
|
|
||||||
current.sym_to_primitive.clone(),
|
|
||||||
current.sym_has_instance.clone(),
|
|
||||||
current.sym_match_all.clone(),
|
|
||||||
current.sym_async_iterator.clone(),
|
|
||||||
current.sym_to_string_tag.clone(),
|
|
||||||
current.sym_species.clone(),
|
|
||||||
current.sym_async_dispose.clone(),
|
|
||||||
current.sym_dispose.clone(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let math_object = if dirty.math {
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))
|
|
||||||
} else {
|
|
||||||
current.math_object.clone()
|
|
||||||
};
|
|
||||||
let json_object = if dirty.json {
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))
|
|
||||||
} else {
|
|
||||||
current.json_object.clone()
|
|
||||||
};
|
|
||||||
let (date_proto, date_constructor) = if dirty.date {
|
|
||||||
make_named_pair(string_forge, shape_forge, labels, "Date")
|
|
||||||
} else {
|
|
||||||
(current.date_proto.clone(), current.date_constructor.clone())
|
|
||||||
};
|
|
||||||
let (set_proto, set_constructor) = if dirty.set {
|
|
||||||
make_named_pair(string_forge, shape_forge, labels, "Set")
|
|
||||||
} else {
|
|
||||||
(current.set_proto.clone(), current.set_constructor.clone())
|
|
||||||
};
|
|
||||||
let (map_proto, map_constructor) = if dirty.map {
|
|
||||||
make_named_pair(string_forge, shape_forge, labels, "Map")
|
|
||||||
} else {
|
|
||||||
(current.map_proto.clone(), current.map_constructor.clone())
|
|
||||||
};
|
|
||||||
let (regexp_proto, regexp_constructor) = if dirty.regexp {
|
|
||||||
make_named_pair(string_forge, shape_forge, labels, "RegExp")
|
|
||||||
} else {
|
|
||||||
(current.regexp_proto.clone(), current.regexp_constructor.clone())
|
|
||||||
};
|
|
||||||
let (array_buffer_proto, array_buffer_constructor) = if dirty.array_buffer {
|
|
||||||
make_named_pair(string_forge, shape_forge, labels, "ArrayBuffer")
|
|
||||||
} else {
|
|
||||||
(current.array_buffer_proto.clone(), current.array_buffer_constructor.clone())
|
|
||||||
};
|
|
||||||
let (data_view_proto, data_view_constructor) = if dirty.data_view {
|
|
||||||
make_named_pair(string_forge, shape_forge, labels, "DataView")
|
|
||||||
} else {
|
|
||||||
(current.data_view_proto.clone(), current.data_view_constructor.clone())
|
|
||||||
};
|
|
||||||
let typed_arrays = if dirty.typed_array_family {
|
|
||||||
make_typed_array_family(string_forge, shape_forge, labels, &object_proto)
|
|
||||||
} else {
|
|
||||||
TypedArrayFamily {
|
|
||||||
typed_array_proto: current.typed_array_proto.clone(),
|
|
||||||
typed_array_constructor: current.typed_array_constructor.clone(),
|
|
||||||
int8array_constructor: current.int8array_constructor.clone(),
|
|
||||||
int8array_proto: current.int8array_proto.clone(),
|
|
||||||
uint8array_constructor: current.uint8array_constructor.clone(),
|
|
||||||
uint8array_proto: current.uint8array_proto.clone(),
|
|
||||||
uint8clampedarray_constructor: current.uint8clampedarray_constructor.clone(),
|
|
||||||
uint8clampedarray_proto: current.uint8clampedarray_proto.clone(),
|
|
||||||
int16array_constructor: current.int16array_constructor.clone(),
|
|
||||||
int16array_proto: current.int16array_proto.clone(),
|
|
||||||
uint16array_constructor: current.uint16array_constructor.clone(),
|
|
||||||
uint16array_proto: current.uint16array_proto.clone(),
|
|
||||||
int32array_constructor: current.int32array_constructor.clone(),
|
|
||||||
int32array_proto: current.int32array_proto.clone(),
|
|
||||||
uint32array_constructor: current.uint32array_constructor.clone(),
|
|
||||||
uint32array_proto: current.uint32array_proto.clone(),
|
|
||||||
float32array_constructor: current.float32array_constructor.clone(),
|
|
||||||
float32array_proto: current.float32array_proto.clone(),
|
|
||||||
float64array_constructor: current.float64array_constructor.clone(),
|
|
||||||
float64array_proto: current.float64array_proto.clone(),
|
|
||||||
bigint64array_constructor: current.bigint64array_constructor.clone(),
|
|
||||||
bigint64array_proto: current.bigint64array_proto.clone(),
|
|
||||||
biguint64array_constructor: current.biguint64array_constructor.clone(),
|
|
||||||
biguint64array_proto: current.biguint64array_proto.clone(),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let (
|
|
||||||
temporal_object,
|
|
||||||
temporal_now_object,
|
|
||||||
instant_proto,
|
|
||||||
instant_constructor,
|
|
||||||
plain_date_proto,
|
|
||||||
plain_date_constructor,
|
|
||||||
plain_time_proto,
|
|
||||||
plain_time_constructor,
|
|
||||||
duration_proto,
|
|
||||||
duration_constructor,
|
|
||||||
zoned_date_time_proto,
|
|
||||||
zoned_date_time_constructor,
|
|
||||||
plain_date_time_proto,
|
|
||||||
plain_date_time_constructor,
|
|
||||||
plain_month_day_proto,
|
|
||||||
plain_month_day_constructor,
|
|
||||||
plain_year_month_proto,
|
|
||||||
plain_year_month_constructor,
|
|
||||||
) = if dirty.temporal {
|
|
||||||
let (instant_proto, instant_constructor) = make_named_pair(string_forge, shape_forge, labels, "Instant");
|
|
||||||
let (plain_date_proto, plain_date_constructor) =
|
|
||||||
make_named_pair(string_forge, shape_forge, labels, "PlainDate");
|
|
||||||
let (plain_time_proto, plain_time_constructor) =
|
|
||||||
make_named_pair(string_forge, shape_forge, labels, "PlainTime");
|
|
||||||
let (duration_proto, duration_constructor) = make_named_pair(string_forge, shape_forge, labels, "Duration");
|
|
||||||
let (zoned_date_time_proto, zoned_date_time_constructor) =
|
|
||||||
make_named_pair(string_forge, shape_forge, labels, "ZonedDateTime");
|
|
||||||
let (plain_date_time_proto, plain_date_time_constructor) =
|
|
||||||
make_named_pair(string_forge, shape_forge, labels, "PlainDateTime");
|
|
||||||
let (plain_month_day_proto, plain_month_day_constructor) =
|
|
||||||
make_named_pair(string_forge, shape_forge, labels, "PlainMonthDay");
|
|
||||||
let (plain_year_month_proto, plain_year_month_constructor) =
|
|
||||||
make_named_pair(string_forge, shape_forge, labels, "PlainYearMonth");
|
|
||||||
(
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
instant_proto,
|
|
||||||
instant_constructor,
|
|
||||||
plain_date_proto,
|
|
||||||
plain_date_constructor,
|
|
||||||
plain_time_proto,
|
|
||||||
plain_time_constructor,
|
|
||||||
duration_proto,
|
|
||||||
duration_constructor,
|
|
||||||
zoned_date_time_proto,
|
|
||||||
zoned_date_time_constructor,
|
|
||||||
plain_date_time_proto,
|
|
||||||
plain_date_time_constructor,
|
|
||||||
plain_month_day_proto,
|
|
||||||
plain_month_day_constructor,
|
|
||||||
plain_year_month_proto,
|
|
||||||
plain_year_month_constructor,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
current.temporal_object.clone(),
|
|
||||||
current.temporal_now_object.clone(),
|
|
||||||
current.instant_proto.clone(),
|
|
||||||
current.instant_constructor.clone(),
|
|
||||||
current.plain_date_proto.clone(),
|
|
||||||
current.plain_date_constructor.clone(),
|
|
||||||
current.plain_time_proto.clone(),
|
|
||||||
current.plain_time_constructor.clone(),
|
|
||||||
current.duration_proto.clone(),
|
|
||||||
current.duration_constructor.clone(),
|
|
||||||
current.zoned_date_time_proto.clone(),
|
|
||||||
current.zoned_date_time_constructor.clone(),
|
|
||||||
current.plain_date_time_proto.clone(),
|
|
||||||
current.plain_date_time_constructor.clone(),
|
|
||||||
current.plain_month_day_proto.clone(),
|
|
||||||
current.plain_month_day_constructor.clone(),
|
|
||||||
current.plain_year_month_proto.clone(),
|
|
||||||
current.plain_year_month_constructor.clone(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let (bigint_proto, bigint_constructor) = if dirty.stubs {
|
|
||||||
make_named_pair(string_forge, shape_forge, labels, "BigInt")
|
|
||||||
} else {
|
|
||||||
(current.bigint_proto.clone(), current.bigint_constructor.clone())
|
|
||||||
};
|
|
||||||
let stub_objects = if dirty.stubs { Vec::new() } else { current.stub_objects.clone() };
|
|
||||||
let console_object = if dirty.console {
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))
|
|
||||||
} else {
|
|
||||||
current.console_object.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
// 迭代器原型与资源栈原型依赖 Object.prototype(链到其上):object 家族重建时
|
|
||||||
// 一并重建,否则旧原型链指向已释放的 object_proto。
|
|
||||||
let (
|
|
||||||
iterator_proto,
|
|
||||||
array_iterator_proto,
|
|
||||||
map_iterator_proto,
|
|
||||||
set_iterator_proto,
|
|
||||||
string_iterator_proto,
|
|
||||||
regexp_string_iterator_proto,
|
|
||||||
iterator_helper_proto,
|
|
||||||
disposable_stack_proto,
|
|
||||||
async_disposable_stack_proto,
|
|
||||||
) = if dirty.object {
|
|
||||||
(
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
current.iterator_proto.clone(),
|
|
||||||
current.array_iterator_proto.clone(),
|
|
||||||
current.map_iterator_proto.clone(),
|
|
||||||
current.set_iterator_proto.clone(),
|
|
||||||
current.string_iterator_proto.clone(),
|
|
||||||
current.regexp_string_iterator_proto.clone(),
|
|
||||||
current.iterator_helper_proto.clone(),
|
|
||||||
current.disposable_stack_proto.clone(),
|
|
||||||
current.async_disposable_stack_proto.clone(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let world = BuiltinWorld {
|
|
||||||
object_proto,
|
|
||||||
string_default_iterator: std::cell::Cell::new(if dirty.string {
|
|
||||||
std::ptr::null()
|
|
||||||
} else {
|
|
||||||
current.string_default_iterator.get()
|
|
||||||
}),
|
|
||||||
array_proto,
|
|
||||||
function_proto,
|
|
||||||
string_proto,
|
|
||||||
number_proto,
|
|
||||||
boolean_proto,
|
|
||||||
error_proto,
|
|
||||||
symbol_proto,
|
|
||||||
object_constructor,
|
|
||||||
array_constructor,
|
|
||||||
function_constructor,
|
|
||||||
string_constructor,
|
|
||||||
number_constructor,
|
|
||||||
boolean_constructor,
|
|
||||||
error_constructor,
|
|
||||||
symbol_constructor,
|
|
||||||
type_error_proto: error_subtypes.type_error_proto,
|
|
||||||
reference_error_proto: error_subtypes.reference_error_proto,
|
|
||||||
range_error_proto: error_subtypes.range_error_proto,
|
|
||||||
syntax_error_proto: error_subtypes.syntax_error_proto,
|
|
||||||
uri_error_proto: error_subtypes.uri_error_proto,
|
|
||||||
eval_error_proto: error_subtypes.eval_error_proto,
|
|
||||||
suppressed_error_proto: error_subtypes.suppressed_error_proto,
|
|
||||||
math_object,
|
|
||||||
json_object,
|
|
||||||
date_constructor,
|
|
||||||
date_proto,
|
|
||||||
set_constructor,
|
|
||||||
set_proto,
|
|
||||||
map_constructor,
|
|
||||||
map_proto,
|
|
||||||
regexp_constructor,
|
|
||||||
regexp_proto,
|
|
||||||
array_buffer_constructor,
|
|
||||||
array_buffer_proto,
|
|
||||||
data_view_constructor,
|
|
||||||
data_view_proto,
|
|
||||||
typed_array_proto: typed_arrays.typed_array_proto,
|
|
||||||
typed_array_constructor: typed_arrays.typed_array_constructor,
|
|
||||||
int8array_constructor: typed_arrays.int8array_constructor,
|
|
||||||
int8array_proto: typed_arrays.int8array_proto,
|
|
||||||
uint8array_constructor: typed_arrays.uint8array_constructor,
|
|
||||||
uint8array_proto: typed_arrays.uint8array_proto,
|
|
||||||
uint8clampedarray_constructor: typed_arrays.uint8clampedarray_constructor,
|
|
||||||
uint8clampedarray_proto: typed_arrays.uint8clampedarray_proto,
|
|
||||||
int16array_constructor: typed_arrays.int16array_constructor,
|
|
||||||
int16array_proto: typed_arrays.int16array_proto,
|
|
||||||
uint16array_constructor: typed_arrays.uint16array_constructor,
|
|
||||||
uint16array_proto: typed_arrays.uint16array_proto,
|
|
||||||
int32array_constructor: typed_arrays.int32array_constructor,
|
|
||||||
int32array_proto: typed_arrays.int32array_proto,
|
|
||||||
uint32array_constructor: typed_arrays.uint32array_constructor,
|
|
||||||
uint32array_proto: typed_arrays.uint32array_proto,
|
|
||||||
float32array_constructor: typed_arrays.float32array_constructor,
|
|
||||||
float32array_proto: typed_arrays.float32array_proto,
|
|
||||||
float64array_constructor: typed_arrays.float64array_constructor,
|
|
||||||
float64array_proto: typed_arrays.float64array_proto,
|
|
||||||
bigint64array_constructor: typed_arrays.bigint64array_constructor,
|
|
||||||
bigint64array_proto: typed_arrays.bigint64array_proto,
|
|
||||||
biguint64array_constructor: typed_arrays.biguint64array_constructor,
|
|
||||||
biguint64array_proto: typed_arrays.biguint64array_proto,
|
|
||||||
sym_match,
|
|
||||||
sym_replace,
|
|
||||||
sym_search,
|
|
||||||
sym_split,
|
|
||||||
sym_iterator,
|
|
||||||
sym_to_primitive,
|
|
||||||
sym_has_instance,
|
|
||||||
sym_match_all,
|
|
||||||
sym_async_iterator,
|
|
||||||
sym_to_string_tag,
|
|
||||||
sym_species,
|
|
||||||
sym_async_dispose,
|
|
||||||
sym_dispose,
|
|
||||||
temporal_object,
|
|
||||||
temporal_now_object,
|
|
||||||
instant_constructor,
|
|
||||||
instant_proto,
|
|
||||||
plain_date_constructor,
|
|
||||||
plain_date_proto,
|
|
||||||
plain_time_constructor,
|
|
||||||
plain_time_proto,
|
|
||||||
duration_constructor,
|
|
||||||
duration_proto,
|
|
||||||
zoned_date_time_constructor,
|
|
||||||
zoned_date_time_proto,
|
|
||||||
plain_date_time_constructor,
|
|
||||||
plain_date_time_proto,
|
|
||||||
plain_month_day_constructor,
|
|
||||||
plain_month_day_proto,
|
|
||||||
plain_year_month_constructor,
|
|
||||||
plain_year_month_proto,
|
|
||||||
bigint_constructor,
|
|
||||||
bigint_proto,
|
|
||||||
iterator_proto,
|
|
||||||
array_iterator_proto,
|
|
||||||
map_iterator_proto,
|
|
||||||
set_iterator_proto,
|
|
||||||
string_iterator_proto,
|
|
||||||
regexp_string_iterator_proto,
|
|
||||||
iterator_helper_proto,
|
|
||||||
disposable_stack_proto,
|
|
||||||
async_disposable_stack_proto,
|
|
||||||
stub_objects,
|
|
||||||
console_object,
|
|
||||||
leaked_objects: std::cell::RefCell::new(Vec::new()),
|
|
||||||
};
|
|
||||||
wire_builtin_world_links(&world);
|
|
||||||
world
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -0,0 +1,566 @@
|
|||||||
|
//! 脏重建职责:按脏标记选择性重建 builtin world(干净家族 Arc::clone 复用、
|
||||||
|
//! Function/Object 四件套保活钉),收尾重指保留对象 proto 槽并恰好释放一次被替换
|
||||||
|
//! 旧 P 对象的属性区。
|
||||||
|
|
||||||
|
use oxide_types::mem::P;
|
||||||
|
use oxide_types::object::JsObject;
|
||||||
|
use oxide_types::value::JsValue;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
builtin_labels, make_error_subtypes, make_named_pair, make_typed_array_family, wire_builtin_world_links,
|
||||||
|
BuiltinWorld, ErrorSubtypeProtos, TypedArrayFamily,
|
||||||
|
};
|
||||||
|
use crate::kernel::BuiltinDirtySet;
|
||||||
|
use crate::shape_forge::{ShapeForge, EMPTY_SHAPE_ID};
|
||||||
|
use crate::string_forge::PermInterner;
|
||||||
|
|
||||||
|
impl BuiltinWorld {
|
||||||
|
/// 选择性重建收尾:把保留对象(登记表 wrapper + 新旧 world 共用的保留 P
|
||||||
|
/// 字段)的 proto 槽从被替换旧指针重指到新指针,随后逐一释放被替换旧 P
|
||||||
|
/// 对象的属性区(含保活钉住的 Function/Object 四件;本体钉保留,见
|
||||||
|
/// `rebuild_with_dirty` 的保活注记)。
|
||||||
|
///
|
||||||
|
/// # 边界与前提
|
||||||
|
/// - 须在 `inherit_leaked_objects` 之后、旧 world 换出前调用(登记表完整;
|
||||||
|
/// full_reset 安全点无并发读者);重指必须先于释放完成,否则保留对象
|
||||||
|
/// 的原型链读到已释放属性区。
|
||||||
|
/// - 替换集由逐字段新旧指针比较(stubs 按指针集合)判定,与保留集天然
|
||||||
|
/// 不相交:未替换字段新旧 world 沿用同一 Arc,其属性区归 session 收尾
|
||||||
|
/// (`teardown_heap_data`)释放,此处不碰,不双放。
|
||||||
|
/// - 保活钉住的 4 件本体不经任何路径释放(Arc 计数已被 `mem::forget`
|
||||||
|
/// 抬升);此处只释放其属性区,漏重指时读者降级为属性静默缺失
|
||||||
|
/// (zombie 本体)而非 UAF。
|
||||||
|
/// - 只重指 proto 槽;属性值链接由同家族同批替换与绑定层 sync_* 覆盖,
|
||||||
|
/// 不在此重指。
|
||||||
|
///
|
||||||
|
/// # 副作用
|
||||||
|
/// 保留对象 proto 槽重指(generation 递增);被替换旧 P 对象属性区四区
|
||||||
|
/// 释放并置空(幂等,重入为 no-op)。
|
||||||
|
pub fn retire_replaced(&self, old: &BuiltinWorld) {
|
||||||
|
// 替换映射:逐字段新旧指针比较(stubs 按指针集合),记录被换出的
|
||||||
|
// 旧指针 → 新指针;stub 无继任者记空指针,只进释放集。
|
||||||
|
let old_fields = old.all_p_fields();
|
||||||
|
let new_fields = self.all_p_fields();
|
||||||
|
let mut remap: std::collections::HashMap<*mut JsObject, *mut JsObject> = std::collections::HashMap::new();
|
||||||
|
for (o, n) in old_fields.iter().zip(new_fields.iter()) {
|
||||||
|
let (op, np) = (o.as_ptr() as *mut JsObject, n.as_ptr() as *mut JsObject);
|
||||||
|
if !std::ptr::eq(op, np) {
|
||||||
|
remap.insert(op, np);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for p in &old.stub_objects {
|
||||||
|
let op = p.as_ptr() as *mut JsObject;
|
||||||
|
if !self.stub_objects.iter().any(|q| std::ptr::eq(q.as_ptr() as *mut JsObject, op)) {
|
||||||
|
remap.insert(op, std::ptr::null_mut());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if remap.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 重指:保留对象(新旧 world 同一指针)proto 槽仍指被替换旧指针的,
|
||||||
|
// 换新指针——与 `wire_builtin_world_links` 的 set_proto_if_changed 同模式。
|
||||||
|
let repoint = |obj: &mut JsObject| {
|
||||||
|
let cur = obj.proto();
|
||||||
|
if !cur.is_object() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let cur_ptr = cur.as_js_object_ptr();
|
||||||
|
let Some(np) = remap.get(&cur_ptr).copied() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if np.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// SAFETY: np 是本 world 的 P 对象;full_reset 安全点无并发读者,
|
||||||
|
// 成环检查由 set_proto 内部完成。
|
||||||
|
obj.set_proto(JsValue::from_js_object(np)).ok();
|
||||||
|
};
|
||||||
|
for (o, n) in old_fields.iter().zip(new_fields.iter()) {
|
||||||
|
let op = o.as_ptr() as *mut JsObject;
|
||||||
|
let np = n.as_ptr() as *mut JsObject;
|
||||||
|
if std::ptr::eq(op, np) {
|
||||||
|
// SAFETY: op/np 指向同一保留 P 对象,重指安全点无并发读者。
|
||||||
|
unsafe {
|
||||||
|
repoint(&mut *np);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for slot in self.leaked_objects.borrow().iter() {
|
||||||
|
let ptr = slot.ptr;
|
||||||
|
// SAFETY: 登记表指针 session 存活期内有效,重指安全点无并发读者。
|
||||||
|
unsafe {
|
||||||
|
repoint(&mut *ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 守约:重指后保留对象 proto 槽不得残留任何被替换旧指针。
|
||||||
|
for (o, n) in old_fields.iter().zip(new_fields.iter()) {
|
||||||
|
let op = o.as_ptr() as *mut JsObject;
|
||||||
|
let np = n.as_ptr() as *mut JsObject;
|
||||||
|
if std::ptr::eq(op, np) {
|
||||||
|
let cur = unsafe { (*np).proto() };
|
||||||
|
debug_assert!(
|
||||||
|
!cur.is_object() || !remap.contains_key(&cur.as_js_object_ptr()),
|
||||||
|
"保留字段 proto 槽不得残留被替换旧指针"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for slot in self.leaked_objects.borrow().iter() {
|
||||||
|
let cur = unsafe { (*slot.ptr).proto() };
|
||||||
|
debug_assert!(
|
||||||
|
!cur.is_object() || !remap.contains_key(&cur.as_js_object_ptr()),
|
||||||
|
"登记表 wrapper proto 槽不得残留被替换旧指针"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 释放:被替换旧 P 对象属性区逐一恰好释放一次(本体不释放;保活钉住
|
||||||
|
// 的 4 件保留本体钉、属性区同样释放)。
|
||||||
|
for &op in remap.keys() {
|
||||||
|
// SAFETY: op 是旧 world 被换出的 P 对象,属性区仅此一处释放并置空
|
||||||
|
// (幂等);full_reset 安全点无并发读者。
|
||||||
|
unsafe {
|
||||||
|
(&mut *op).release_raw_heap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 按脏标记选择性重建 builtin world:仅重建被污染的对象家族,未污染的保留原指针。
|
||||||
|
///
|
||||||
|
/// # 注意事项
|
||||||
|
/// - Function/Object 家族脏时,旧 fn_proto/object_proto 对须先保活再重建:
|
||||||
|
/// 释放表统一持有的方法 wrapper 永久泄漏(`Box::into_raw`),其 proto 裸指针
|
||||||
|
/// 指向绑定时的 function_proto——执行期原型链查找(如 `push.call` 沿 wrapper
|
||||||
|
/// 原型链取 `call`)仍走这些对象,reset 清空执行状态不阻断该路径,旧对
|
||||||
|
/// Arc 归零即悬空。`retire_replaced` 重指完成后旧对无读者;本体钉保留
|
||||||
|
/// (每次 dirty rebuild 至多 4 个对象本体永久泄漏)作重指遗漏兜底——漏
|
||||||
|
/// 重指时读者降级为属性静默缺失(zombie 本体)而非 UAF,属性区于重指
|
||||||
|
/// 完成时由 `retire_replaced` 释放。
|
||||||
|
pub fn rebuild_with_dirty(
|
||||||
|
current: &BuiltinWorld, string_forge: &PermInterner, shape_forge: &ShapeForge, dirty: &BuiltinDirtySet,
|
||||||
|
) -> BuiltinWorld {
|
||||||
|
let labels = builtin_labels(string_forge);
|
||||||
|
|
||||||
|
// 保活:钉住旧 Function/Object 对的 Arc 计数使其永不归零——保留对象的
|
||||||
|
// proto 槽重指在 `retire_replaced`(本函数返回后、旧 world 换出前)
|
||||||
|
// 完成,钉住的本体是重指遗漏的兜底,不经任何路径释放。
|
||||||
|
if dirty.function || dirty.object {
|
||||||
|
std::mem::forget(current.function_proto.clone());
|
||||||
|
std::mem::forget(current.function_constructor.clone());
|
||||||
|
std::mem::forget(current.object_proto.clone());
|
||||||
|
std::mem::forget(current.object_constructor.clone());
|
||||||
|
}
|
||||||
|
let (object_proto, object_constructor) = if dirty.object {
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "Object")
|
||||||
|
} else {
|
||||||
|
(current.object_proto.clone(), current.object_constructor.clone())
|
||||||
|
};
|
||||||
|
let (array_proto, array_constructor) = if dirty.array {
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "Array")
|
||||||
|
} else {
|
||||||
|
(current.array_proto.clone(), current.array_constructor.clone())
|
||||||
|
};
|
||||||
|
let (function_proto, function_constructor) = if dirty.function {
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "Function")
|
||||||
|
} else {
|
||||||
|
(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")
|
||||||
|
} else {
|
||||||
|
(current.string_proto.clone(), current.string_constructor.clone())
|
||||||
|
};
|
||||||
|
let (number_proto, number_constructor) = if dirty.number {
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "Number")
|
||||||
|
} else {
|
||||||
|
(current.number_proto.clone(), current.number_constructor.clone())
|
||||||
|
};
|
||||||
|
let (boolean_proto, boolean_constructor) = if dirty.boolean {
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "Boolean")
|
||||||
|
} else {
|
||||||
|
(current.boolean_proto.clone(), current.boolean_constructor.clone())
|
||||||
|
};
|
||||||
|
let (error_proto, error_constructor, error_subtypes) = if dirty.error_family {
|
||||||
|
let (error_proto, error_constructor) = make_named_pair(string_forge, shape_forge, labels, "Error");
|
||||||
|
let error_subtypes = make_error_subtypes(&error_proto);
|
||||||
|
(error_proto, error_constructor, error_subtypes)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
current.error_proto.clone(),
|
||||||
|
current.error_constructor.clone(),
|
||||||
|
ErrorSubtypeProtos {
|
||||||
|
type_error_proto: current.type_error_proto.clone(),
|
||||||
|
reference_error_proto: current.reference_error_proto.clone(),
|
||||||
|
range_error_proto: current.range_error_proto.clone(),
|
||||||
|
syntax_error_proto: current.syntax_error_proto.clone(),
|
||||||
|
uri_error_proto: current.uri_error_proto.clone(),
|
||||||
|
eval_error_proto: current.eval_error_proto.clone(),
|
||||||
|
suppressed_error_proto: current.suppressed_error_proto.clone(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let (
|
||||||
|
symbol_proto,
|
||||||
|
symbol_constructor,
|
||||||
|
sym_match,
|
||||||
|
sym_replace,
|
||||||
|
sym_search,
|
||||||
|
sym_split,
|
||||||
|
sym_iterator,
|
||||||
|
sym_to_primitive,
|
||||||
|
sym_has_instance,
|
||||||
|
sym_match_all,
|
||||||
|
sym_async_iterator,
|
||||||
|
sym_to_string_tag,
|
||||||
|
sym_species,
|
||||||
|
sym_async_dispose,
|
||||||
|
sym_dispose,
|
||||||
|
) = if dirty.symbol_family {
|
||||||
|
let (symbol_proto, symbol_constructor) = make_named_pair(string_forge, shape_forge, labels, "Symbol");
|
||||||
|
(
|
||||||
|
symbol_proto,
|
||||||
|
symbol_constructor,
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
current.symbol_proto.clone(),
|
||||||
|
current.symbol_constructor.clone(),
|
||||||
|
current.sym_match.clone(),
|
||||||
|
current.sym_replace.clone(),
|
||||||
|
current.sym_search.clone(),
|
||||||
|
current.sym_split.clone(),
|
||||||
|
current.sym_iterator.clone(),
|
||||||
|
current.sym_to_primitive.clone(),
|
||||||
|
current.sym_has_instance.clone(),
|
||||||
|
current.sym_match_all.clone(),
|
||||||
|
current.sym_async_iterator.clone(),
|
||||||
|
current.sym_to_string_tag.clone(),
|
||||||
|
current.sym_species.clone(),
|
||||||
|
current.sym_async_dispose.clone(),
|
||||||
|
current.sym_dispose.clone(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let math_object = if dirty.math {
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))
|
||||||
|
} else {
|
||||||
|
current.math_object.clone()
|
||||||
|
};
|
||||||
|
let json_object = if dirty.json {
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))
|
||||||
|
} else {
|
||||||
|
current.json_object.clone()
|
||||||
|
};
|
||||||
|
let (date_proto, date_constructor) = if dirty.date {
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "Date")
|
||||||
|
} else {
|
||||||
|
(current.date_proto.clone(), current.date_constructor.clone())
|
||||||
|
};
|
||||||
|
let (set_proto, set_constructor) = if dirty.set {
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "Set")
|
||||||
|
} else {
|
||||||
|
(current.set_proto.clone(), current.set_constructor.clone())
|
||||||
|
};
|
||||||
|
let (map_proto, map_constructor) = if dirty.map {
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "Map")
|
||||||
|
} else {
|
||||||
|
(current.map_proto.clone(), current.map_constructor.clone())
|
||||||
|
};
|
||||||
|
let (regexp_proto, regexp_constructor) = if dirty.regexp {
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "RegExp")
|
||||||
|
} else {
|
||||||
|
(current.regexp_proto.clone(), current.regexp_constructor.clone())
|
||||||
|
};
|
||||||
|
let (array_buffer_proto, array_buffer_constructor) = if dirty.array_buffer {
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "ArrayBuffer")
|
||||||
|
} else {
|
||||||
|
(current.array_buffer_proto.clone(), current.array_buffer_constructor.clone())
|
||||||
|
};
|
||||||
|
let (data_view_proto, data_view_constructor) = if dirty.data_view {
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "DataView")
|
||||||
|
} else {
|
||||||
|
(current.data_view_proto.clone(), current.data_view_constructor.clone())
|
||||||
|
};
|
||||||
|
let typed_arrays = if dirty.typed_array_family {
|
||||||
|
make_typed_array_family(string_forge, shape_forge, labels, &object_proto)
|
||||||
|
} else {
|
||||||
|
TypedArrayFamily {
|
||||||
|
typed_array_proto: current.typed_array_proto.clone(),
|
||||||
|
typed_array_constructor: current.typed_array_constructor.clone(),
|
||||||
|
int8array_constructor: current.int8array_constructor.clone(),
|
||||||
|
int8array_proto: current.int8array_proto.clone(),
|
||||||
|
uint8array_constructor: current.uint8array_constructor.clone(),
|
||||||
|
uint8array_proto: current.uint8array_proto.clone(),
|
||||||
|
uint8clampedarray_constructor: current.uint8clampedarray_constructor.clone(),
|
||||||
|
uint8clampedarray_proto: current.uint8clampedarray_proto.clone(),
|
||||||
|
int16array_constructor: current.int16array_constructor.clone(),
|
||||||
|
int16array_proto: current.int16array_proto.clone(),
|
||||||
|
uint16array_constructor: current.uint16array_constructor.clone(),
|
||||||
|
uint16array_proto: current.uint16array_proto.clone(),
|
||||||
|
int32array_constructor: current.int32array_constructor.clone(),
|
||||||
|
int32array_proto: current.int32array_proto.clone(),
|
||||||
|
uint32array_constructor: current.uint32array_constructor.clone(),
|
||||||
|
uint32array_proto: current.uint32array_proto.clone(),
|
||||||
|
float32array_constructor: current.float32array_constructor.clone(),
|
||||||
|
float32array_proto: current.float32array_proto.clone(),
|
||||||
|
float64array_constructor: current.float64array_constructor.clone(),
|
||||||
|
float64array_proto: current.float64array_proto.clone(),
|
||||||
|
bigint64array_constructor: current.bigint64array_constructor.clone(),
|
||||||
|
bigint64array_proto: current.bigint64array_proto.clone(),
|
||||||
|
biguint64array_constructor: current.biguint64array_constructor.clone(),
|
||||||
|
biguint64array_proto: current.biguint64array_proto.clone(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let (
|
||||||
|
temporal_object,
|
||||||
|
temporal_now_object,
|
||||||
|
instant_proto,
|
||||||
|
instant_constructor,
|
||||||
|
plain_date_proto,
|
||||||
|
plain_date_constructor,
|
||||||
|
plain_time_proto,
|
||||||
|
plain_time_constructor,
|
||||||
|
duration_proto,
|
||||||
|
duration_constructor,
|
||||||
|
zoned_date_time_proto,
|
||||||
|
zoned_date_time_constructor,
|
||||||
|
plain_date_time_proto,
|
||||||
|
plain_date_time_constructor,
|
||||||
|
plain_month_day_proto,
|
||||||
|
plain_month_day_constructor,
|
||||||
|
plain_year_month_proto,
|
||||||
|
plain_year_month_constructor,
|
||||||
|
) = if dirty.temporal {
|
||||||
|
let (instant_proto, instant_constructor) = make_named_pair(string_forge, shape_forge, labels, "Instant");
|
||||||
|
let (plain_date_proto, plain_date_constructor) =
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "PlainDate");
|
||||||
|
let (plain_time_proto, plain_time_constructor) =
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "PlainTime");
|
||||||
|
let (duration_proto, duration_constructor) = make_named_pair(string_forge, shape_forge, labels, "Duration");
|
||||||
|
let (zoned_date_time_proto, zoned_date_time_constructor) =
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "ZonedDateTime");
|
||||||
|
let (plain_date_time_proto, plain_date_time_constructor) =
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "PlainDateTime");
|
||||||
|
let (plain_month_day_proto, plain_month_day_constructor) =
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "PlainMonthDay");
|
||||||
|
let (plain_year_month_proto, plain_year_month_constructor) =
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "PlainYearMonth");
|
||||||
|
(
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
instant_proto,
|
||||||
|
instant_constructor,
|
||||||
|
plain_date_proto,
|
||||||
|
plain_date_constructor,
|
||||||
|
plain_time_proto,
|
||||||
|
plain_time_constructor,
|
||||||
|
duration_proto,
|
||||||
|
duration_constructor,
|
||||||
|
zoned_date_time_proto,
|
||||||
|
zoned_date_time_constructor,
|
||||||
|
plain_date_time_proto,
|
||||||
|
plain_date_time_constructor,
|
||||||
|
plain_month_day_proto,
|
||||||
|
plain_month_day_constructor,
|
||||||
|
plain_year_month_proto,
|
||||||
|
plain_year_month_constructor,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
current.temporal_object.clone(),
|
||||||
|
current.temporal_now_object.clone(),
|
||||||
|
current.instant_proto.clone(),
|
||||||
|
current.instant_constructor.clone(),
|
||||||
|
current.plain_date_proto.clone(),
|
||||||
|
current.plain_date_constructor.clone(),
|
||||||
|
current.plain_time_proto.clone(),
|
||||||
|
current.plain_time_constructor.clone(),
|
||||||
|
current.duration_proto.clone(),
|
||||||
|
current.duration_constructor.clone(),
|
||||||
|
current.zoned_date_time_proto.clone(),
|
||||||
|
current.zoned_date_time_constructor.clone(),
|
||||||
|
current.plain_date_time_proto.clone(),
|
||||||
|
current.plain_date_time_constructor.clone(),
|
||||||
|
current.plain_month_day_proto.clone(),
|
||||||
|
current.plain_month_day_constructor.clone(),
|
||||||
|
current.plain_year_month_proto.clone(),
|
||||||
|
current.plain_year_month_constructor.clone(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let (bigint_proto, bigint_constructor) = if dirty.stubs {
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "BigInt")
|
||||||
|
} else {
|
||||||
|
(current.bigint_proto.clone(), current.bigint_constructor.clone())
|
||||||
|
};
|
||||||
|
let stub_objects = if dirty.stubs { Vec::new() } else { current.stub_objects.clone() };
|
||||||
|
let console_object = if dirty.console {
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))
|
||||||
|
} else {
|
||||||
|
current.console_object.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
// 迭代器原型与资源栈原型依赖 Object.prototype(链到其上):object 家族重建时
|
||||||
|
// 一并重建,否则旧原型链指向已释放的 object_proto。
|
||||||
|
let (
|
||||||
|
iterator_proto,
|
||||||
|
array_iterator_proto,
|
||||||
|
map_iterator_proto,
|
||||||
|
set_iterator_proto,
|
||||||
|
string_iterator_proto,
|
||||||
|
regexp_string_iterator_proto,
|
||||||
|
iterator_helper_proto,
|
||||||
|
disposable_stack_proto,
|
||||||
|
async_disposable_stack_proto,
|
||||||
|
) = if dirty.object {
|
||||||
|
(
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
current.iterator_proto.clone(),
|
||||||
|
current.array_iterator_proto.clone(),
|
||||||
|
current.map_iterator_proto.clone(),
|
||||||
|
current.set_iterator_proto.clone(),
|
||||||
|
current.string_iterator_proto.clone(),
|
||||||
|
current.regexp_string_iterator_proto.clone(),
|
||||||
|
current.iterator_helper_proto.clone(),
|
||||||
|
current.disposable_stack_proto.clone(),
|
||||||
|
current.async_disposable_stack_proto.clone(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let world = BuiltinWorld {
|
||||||
|
object_proto,
|
||||||
|
string_default_iterator: std::cell::Cell::new(if dirty.string {
|
||||||
|
std::ptr::null()
|
||||||
|
} else {
|
||||||
|
current.string_default_iterator.get()
|
||||||
|
}),
|
||||||
|
array_proto,
|
||||||
|
function_proto,
|
||||||
|
string_proto,
|
||||||
|
number_proto,
|
||||||
|
boolean_proto,
|
||||||
|
error_proto,
|
||||||
|
symbol_proto,
|
||||||
|
object_constructor,
|
||||||
|
array_constructor,
|
||||||
|
function_constructor,
|
||||||
|
string_constructor,
|
||||||
|
number_constructor,
|
||||||
|
boolean_constructor,
|
||||||
|
error_constructor,
|
||||||
|
symbol_constructor,
|
||||||
|
type_error_proto: error_subtypes.type_error_proto,
|
||||||
|
reference_error_proto: error_subtypes.reference_error_proto,
|
||||||
|
range_error_proto: error_subtypes.range_error_proto,
|
||||||
|
syntax_error_proto: error_subtypes.syntax_error_proto,
|
||||||
|
uri_error_proto: error_subtypes.uri_error_proto,
|
||||||
|
eval_error_proto: error_subtypes.eval_error_proto,
|
||||||
|
suppressed_error_proto: error_subtypes.suppressed_error_proto,
|
||||||
|
math_object,
|
||||||
|
json_object,
|
||||||
|
date_constructor,
|
||||||
|
date_proto,
|
||||||
|
set_constructor,
|
||||||
|
set_proto,
|
||||||
|
map_constructor,
|
||||||
|
map_proto,
|
||||||
|
regexp_constructor,
|
||||||
|
regexp_proto,
|
||||||
|
array_buffer_constructor,
|
||||||
|
array_buffer_proto,
|
||||||
|
data_view_constructor,
|
||||||
|
data_view_proto,
|
||||||
|
typed_array_proto: typed_arrays.typed_array_proto,
|
||||||
|
typed_array_constructor: typed_arrays.typed_array_constructor,
|
||||||
|
int8array_constructor: typed_arrays.int8array_constructor,
|
||||||
|
int8array_proto: typed_arrays.int8array_proto,
|
||||||
|
uint8array_constructor: typed_arrays.uint8array_constructor,
|
||||||
|
uint8array_proto: typed_arrays.uint8array_proto,
|
||||||
|
uint8clampedarray_constructor: typed_arrays.uint8clampedarray_constructor,
|
||||||
|
uint8clampedarray_proto: typed_arrays.uint8clampedarray_proto,
|
||||||
|
int16array_constructor: typed_arrays.int16array_constructor,
|
||||||
|
int16array_proto: typed_arrays.int16array_proto,
|
||||||
|
uint16array_constructor: typed_arrays.uint16array_constructor,
|
||||||
|
uint16array_proto: typed_arrays.uint16array_proto,
|
||||||
|
int32array_constructor: typed_arrays.int32array_constructor,
|
||||||
|
int32array_proto: typed_arrays.int32array_proto,
|
||||||
|
uint32array_constructor: typed_arrays.uint32array_constructor,
|
||||||
|
uint32array_proto: typed_arrays.uint32array_proto,
|
||||||
|
float32array_constructor: typed_arrays.float32array_constructor,
|
||||||
|
float32array_proto: typed_arrays.float32array_proto,
|
||||||
|
float64array_constructor: typed_arrays.float64array_constructor,
|
||||||
|
float64array_proto: typed_arrays.float64array_proto,
|
||||||
|
bigint64array_constructor: typed_arrays.bigint64array_constructor,
|
||||||
|
bigint64array_proto: typed_arrays.bigint64array_proto,
|
||||||
|
biguint64array_constructor: typed_arrays.biguint64array_constructor,
|
||||||
|
biguint64array_proto: typed_arrays.biguint64array_proto,
|
||||||
|
sym_match,
|
||||||
|
sym_replace,
|
||||||
|
sym_search,
|
||||||
|
sym_split,
|
||||||
|
sym_iterator,
|
||||||
|
sym_to_primitive,
|
||||||
|
sym_has_instance,
|
||||||
|
sym_match_all,
|
||||||
|
sym_async_iterator,
|
||||||
|
sym_to_string_tag,
|
||||||
|
sym_species,
|
||||||
|
sym_async_dispose,
|
||||||
|
sym_dispose,
|
||||||
|
temporal_object,
|
||||||
|
temporal_now_object,
|
||||||
|
instant_constructor,
|
||||||
|
instant_proto,
|
||||||
|
plain_date_constructor,
|
||||||
|
plain_date_proto,
|
||||||
|
plain_time_constructor,
|
||||||
|
plain_time_proto,
|
||||||
|
duration_constructor,
|
||||||
|
duration_proto,
|
||||||
|
zoned_date_time_constructor,
|
||||||
|
zoned_date_time_proto,
|
||||||
|
plain_date_time_constructor,
|
||||||
|
plain_date_time_proto,
|
||||||
|
plain_month_day_constructor,
|
||||||
|
plain_month_day_proto,
|
||||||
|
plain_year_month_constructor,
|
||||||
|
plain_year_month_proto,
|
||||||
|
bigint_constructor,
|
||||||
|
bigint_proto,
|
||||||
|
iterator_proto,
|
||||||
|
array_iterator_proto,
|
||||||
|
map_iterator_proto,
|
||||||
|
set_iterator_proto,
|
||||||
|
string_iterator_proto,
|
||||||
|
regexp_string_iterator_proto,
|
||||||
|
iterator_helper_proto,
|
||||||
|
disposable_stack_proto,
|
||||||
|
async_disposable_stack_proto,
|
||||||
|
stub_objects,
|
||||||
|
console_object,
|
||||||
|
leaked_objects: std::cell::RefCell::new(Vec::new()),
|
||||||
|
};
|
||||||
|
wire_builtin_world_links(&world);
|
||||||
|
world
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user