docs: 补齐 Promise 聚合与反应的安全注记

This commit is contained in:
2026-09-17 15:08:48 +00:00
parent 604a785bf9
commit b7bf0193bd
2 changed files with 16 additions and 0 deletions
+10
View File
@@ -24,6 +24,7 @@ impl Vm {
fn make_agg_record(&mut self, values: JsValue, resolve: JsValue, reject: JsValue) -> JsValue {
let proto_val = JsValue::from_js_object(self.session.builtin_world().object_proto.as_ptr() as *mut JsObject);
let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;本次 native 调用内不搬移,借出期间无别名。
let obj = unsafe { &mut *ptr };
let rem_si = self.kernel_core.perm_interner().intern(AGG_REMAINING_PROP).0;
self.set_or_create_prop_value(obj, rem_si, JsValue::int(1));
@@ -47,6 +48,7 @@ impl Vm {
func.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(native_fn as *const ()) }));
func.set_native_arg_count(1);
let ptr = self.alloc_object(func);
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;后续 add_fn_name_length 的 shape/属性分配不改对象地址,借出期间无别名。
let obj = unsafe { &mut *ptr };
self.add_fn_name_length(obj, "", 1);
let sh = self.kernel_core.shape_forge().as_ref();
@@ -73,6 +75,7 @@ impl Vm {
fn make_settled_record(&mut self, status: &str, value_field: &str, value: JsValue) -> JsValue {
let proto_val = JsValue::from_js_object(self.session.builtin_world().object_proto.as_ptr() as *mut JsObject);
let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;写 status/value 属性(含 new_string 分配)期间不搬移,借出期间无别名。
let obj = unsafe { &mut *ptr };
let status_si = self.kernel_core.perm_interner().intern("status").0;
let status_val = self.new_string(status);
@@ -87,6 +90,7 @@ impl Vm {
fn make_aggregate_error(&mut self, errors: JsValue, message: JsValue) -> JsValue {
let proto_val = JsValue::from_js_object(self.aggregate_error_proto.as_ptr() as *mut JsObject);
let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;写 message/errors 数据属性期间不搬移,借出期间无别名。
let obj = unsafe { &mut *ptr };
// message/errors 为数据属性:writable、非枚举、configurable(CreateMethodProperty)。
let attrs = PropAttributes::new(true, false, true);
@@ -173,14 +177,17 @@ impl Vm {
// 固定地址后互相接线:proto.constructor ↔ ctor.prototype。
Self::swap_intrinsic_proto(&mut self.aggregate_error_proto, *proto);
Self::swap_intrinsic_proto(&mut self.aggregate_error_constructor, *ctor);
// SAFETY: aggregate_error_proto 为堆址固定的 P<JsObject>(Arc 透明包装),本行前刚经 swap_intrinsic_proto 落地;与 ctor_mut 分属不同对象,无别名。
let proto_mut = unsafe { &mut *self.aggregate_error_proto.as_mut_ptr() };
proto_mut
.set_prop_at(0u32, JsValue::from_js_object(self.aggregate_error_constructor.as_ptr() as *mut JsObject));
// SAFETY: aggregate_error_constructor 同为堆址固定的 P<JsObject>(Arc 透明包装);此处写 prototype 槽位(下标 2),与 proto_mut 分属不同对象,无别名。
let ctor_mut = unsafe { &mut *self.aggregate_error_constructor.as_mut_ptr() };
ctor_mut.set_prop_at(2u32, JsValue::from_js_object(self.aggregate_error_proto.as_ptr() as *mut JsObject));
// 绑定 global(槽已存在则更新)。
let global_ptr = self.session.global_object().as_ptr() as *mut JsObject;
// SAFETY: global 对象由 session 持有,存活整个 session;本函数内只改其 shape/属性区,期间无 reset 或对象搬移。
let global = unsafe { &mut *global_ptr };
let si = self.kernel_core.perm_interner().intern("AggregateError").0;
let ctor_val = JsValue::from_js_object(self.aggregate_error_constructor.as_ptr() as *mut JsObject);
@@ -202,6 +209,7 @@ pub(super) fn promise_static_resolve(vm: &mut Vm, args: &[u8]) -> NativeResult {
let ctor = vm.reg(if args.is_empty() { 0 } else { args[0] });
let x = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
if vm.is_promise_value(x) {
// SAFETY: is_promise_value 已验证 x 为原生 Promise 对象,指针非空且存活;此处只读 constructor,即时消费。
let x_obj = unsafe { &*x.as_js_object_ptr() };
let ctor_si = vm.kernel_core.perm_interner().intern("constructor").0;
let x_ctor = match vm.ordinary_get(x_obj, ctor_si, x) {
@@ -273,6 +281,7 @@ pub(super) fn promise_static_with_resolvers(vm: &mut Vm, args: &[u8]) -> NativeR
};
let object_proto = JsValue::from_js_object(vm.session.builtin_world().object_proto.as_ptr() as *mut JsObject);
let ptr = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, object_proto));
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;写 promise/resolve/reject 属性期间不搬移,借出期间无别名。
let obj = unsafe { &mut *ptr };
let sf = vm.kernel_core.perm_interner().as_ref();
let sh = vm.kernel_core.shape_forge().as_ref();
@@ -707,6 +716,7 @@ fn aggregate_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
Err(err) => return NativeResult::Err(err),
};
let err_si = vm.kernel_core.perm_interner().intern("errors").0;
// SAFETY: this 为本次构造得到的既有或新建存活对象;此处写 errors 数据属性,与上方 message 写入顺序独占同一对象,无别名。
let _ = vm.define_data_property(unsafe { &mut *this }, err_si, errors_list, PropAttributes::new(true, false, true));
NativeResult::Ok(JsValue::from_js_object(this))
}
+6
View File
@@ -54,11 +54,13 @@ impl Vm {
let (new_promise, resolve, reject) = capability;
let state_ptr = self.promise_state_ptr(this_val);
let (kind, result) = {
// SAFETY: this_val 已过 is_promise_value 校验,state_ptr 取自 promise_state_ptr;状态盒非空且随对象存活,本块唯一可变借用,读完 state/result 即结束。
let state = unsafe { &mut *state_ptr };
(state.state, state.result)
};
match kind {
PromiseStateKind::Pending => {
// SAFETY: 同一 state_ptr,前一可变借用已结束;状态盒非空且随 this_val 存活,此处仅向 reactions 追加两条反应,无别名。
let state = unsafe { &mut *state_ptr };
state.reactions.push(PromiseReaction {
promise: new_promise,
@@ -126,6 +128,7 @@ impl Vm {
/// undefined 会错误退回内置;`@@species` 改写支持待补
/// `Promise[Symbol.species]` getter 时一并落地。
fn species_constructor(&mut self, promise: JsValue) -> Result<JsValue, JsValue> {
// SAFETY: 调用方已按函数文档前提做 is_promise_value 校验,指针非空且指向存活 Promise 对象;此处只读 constructor。
let obj = unsafe { &*promise.as_js_object_ptr() };
let ctor_si = self.kernel_core.perm_interner().intern("constructor").0;
let ctor = match self.ordinary_get(obj, ctor_si, promise) {
@@ -211,6 +214,7 @@ impl Vm {
Err(e) => return Err(oxide_builtins::error::create_type_error(self, &e)),
}
};
// SAFETY: get_receiver 两分支均为对象(is_object 原值或 to_object 返回对象),指针非空且存活;此处只读 then 属性。
let receiver_obj = unsafe { &*get_receiver.as_js_object_ptr() };
let then = match self.ordinary_get(receiver_obj, then_si, this_val) {
Ok(v) => v,
@@ -244,6 +248,7 @@ impl Vm {
func.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(promise_finally_handler as *const ()) }));
func.set_native_arg_count(0);
let ptr = self.alloc_object(func);
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;本次 native 调用内不搬移,借出期间无别名。
let obj = unsafe { &mut *ptr };
let of_si = self.kernel_core.perm_interner().intern(ON_FINALLY_PROP).0;
self.set_or_create_prop_value(obj, of_si, on_finally);
@@ -278,6 +283,7 @@ fn promise_finally_handler(vm: &mut Vm, args: &[u8]) -> NativeResult {
if !callee.is_object() {
return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "finally handler is invalid"));
}
// SAFETY: callee 来自 reg(254) 且 is_object 守卫保证指针非空存活;此处只读 ON_FINALLY/FINALLY_REJECT 属性。
let callee_obj = unsafe { &*callee.as_js_object_ptr() };
let of_si = vm.kernel_core.perm_interner().intern(ON_FINALLY_PROP).0;
let on_finally = vm.resolve_property(callee_obj, of_si).unwrap_or(JsValue::undefined());