//! Promise 能力与结算:能力构造、resolve/reject 闭包构造, //! PromiseResolve / fulfill / reject 结算路径。 //! //! 结算单次性:闭包首调置位 `already_resolved`,`state != Pending` //! 最终守卫;结算后反应入微任务队列。 use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_runtime_api::NativeResult; use oxide_types::object::{JsObject, NativeFnPtr}; use oxide_types::value::JsValue; use crate::native::NativeFn; use crate::vm::Vm; use super::{ Microtask, PromiseState, PromiseStateKind, CAP_REJECT_PROP, CAP_RESOLVE_PROP, DELEGATED_PROP, PROMISE_PROP, }; impl Vm { /// 新建 Promise 能力 `(promise, resolve, reject)`:resolve/reject 为携带 /// 目标 promise 的 native 闭包,同时写入状态盒供 thenable 委托取用。 pub(crate) fn new_promise_capability(&mut self) -> (JsValue, JsValue, JsValue) { let promise = self.create_promise_object(); let resolve = self.make_resolve_reject_fn(promise, false, false); let reject = self.make_resolve_reject_fn(promise, true, false); let state = self.promise_state_ptr(promise); // SAFETY: `state` 来自 create_promise_object 写入的状态盒,非空且随对象存活;此处块内唯一可变借用,无别名。 let state = unsafe { &mut *state }; state.resolve_fn = resolve; state.reject_fn = reject; (promise, resolve, reject) } /// NewPromiseCapability(C):按构造器 C 建能力。C 为内置 Promise 时走直接路径, /// 否则经 GetCapabilitiesExecutor 间接构造 `new C(executor)`。 pub(super) fn new_promise_capability_with_ctor( &mut self, ctor: JsValue, ) -> Result<(JsValue, JsValue, JsValue), JsValue> { let intrinsic = JsValue::from_js_object(self.realm.promise_constructor.borrow().as_ptr() as *mut JsObject); if oxide_runtime_api::same_value(ctor, intrinsic) { return Ok(self.new_promise_capability()); } // GetCapabilitiesExecutor:捕获 resolve/reject 到自身 prop。 let executor = self.make_capability_executor(); // 构造 C(executor);结果须为对象。 let promise = self.construct_ctor(ctor, &[executor])?; if !promise.is_object() { return Err(oxide_builtins::error::create_type_error( self, "Promise constructor returned a non-object", )); } let (resolve, reject) = { // SAFETY: `executor` 由 make_capability_executor 新建,为存活的 arena 函数 // 对象、指针非空;此处只读 CAP_* 属性,不跨 GC/reset 点。 let exec_obj = unsafe { &*executor.as_js_object_ptr() }; let res_si = self.kernel_core.perm_interner().intern(CAP_RESOLVE_PROP).0; let rej_si = self.kernel_core.perm_interner().intern(CAP_REJECT_PROP).0; let r = self.resolve_property(exec_obj, res_si).unwrap_or(JsValue::undefined()); let j = self.resolve_property(exec_obj, rej_si).unwrap_or(JsValue::undefined()); (r, j) }; if !oxide_builtins::iterator::is_callable(resolve) || !oxide_builtins::iterator::is_callable(reject) { return Err(oxide_builtins::error::create_type_error( self, "Promise resolve and reject functions must be callable", )); } Ok((promise, resolve, reject)) } /// 构造 GetCapabilitiesExecutor:调用时把 resolve/reject 存入自身 prop。 fn make_capability_executor(&mut self) -> JsValue { let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: capability_executor 是 NativeFn 函数项。 func.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(capability_executor as *const ()) })); func.set_native_arg_count(2); let ptr = self.alloc_object(func); // SAFETY: `alloc_object` 返回非空 arena 指针,本次 native 调用内不搬移;借出期间无别名。 let obj = unsafe { &mut *ptr }; self.add_fn_name_length(obj, "", 2); JsValue::from_js_object(ptr) } /// 构造携带目标 promise 的 native 闭包(resolve 或 reject 角色)。 /// `delegated` 为 true 表示 thenable 委托用的结算代理:绕过 alreadyResolved /// (委托是唯一真正的结算路径,由 then 的 resolve/reject 触发),但 state != Pending /// 仍阻止二次结算。 pub(super) fn make_resolve_reject_fn(&mut self, promise: JsValue, reject_role: bool, delegated: bool) -> JsValue { let native_fn: NativeFn = if reject_role { promise_reject_closure } else { promise_resolve_closure }; let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: native_fn 是 NativeFn 函数项。 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: `alloc_object` 返回非空 arena 指针;后续属性/shape 分配不改对象地址,无别名。 let obj = unsafe { &mut *ptr }; let si = self.kernel_core.perm_interner().intern(PROMISE_PROP).0; self.set_or_create_prop_value(obj, si, promise); let dsi = self.kernel_core.perm_interner().intern(DELEGATED_PROP).0; self.set_or_create_prop_value(obj, dsi, JsValue::bool(delegated)); self.add_fn_name_length(obj, "", 1); JsValue::from_js_object(ptr) } /// 从 resolve/reject 闭包函数对象读取目标 promise。 fn promise_from_callee(&self) -> Option { let callee = self.regs[254]; if !callee.is_object() { return None; } // SAFETY: `is_object` 已保证指针非空且指向存活对象;只读 PROMISE_PROP,不跨 GC/reset。 let obj = unsafe { &*callee.as_js_object_ptr() }; let si = self.kernel_core.perm_interner().intern(PROMISE_PROP).0; match self.resolve_property(obj, si) { Some(v) if v.is_object() => Some(v), _ => None, } } /// 当前 resolve/reject 闭包是否为 thenable 委托结算代理(绕过 alreadyResolved)。 fn callee_delegated_flag(&self) -> bool { let callee = self.regs[254]; if !callee.is_object() { return false; } // SAFETY: `is_object` 已保证指针非空且指向存活对象;只读 DELEGATED_PROP,不跨 GC/reset。 let obj = unsafe { &*callee.as_js_object_ptr() }; let si = self.kernel_core.perm_interner().intern(DELEGATED_PROP).0; self.resolve_property(obj, si) .map(|v| v.is_bool() && v.as_bool()) .unwrap_or(false) } /// 取出 Promise 状态盒指针(调用方须先校验 `is_promise_value`)。 pub(super) fn promise_state_ptr(&self, promise: JsValue) -> *mut PromiseState { // SAFETY: 调用方已按文档前提校验 `is_promise_value`,对象指针非空且 native_data // 指向由 Box::into_raw 分配、随对象释放的 Box;裸指针由调用方即时解引用。 unsafe { (*promise.as_js_object_ptr()).native_data() as *mut PromiseState } } /// 是否原生 Promise 对象。 pub(crate) fn is_promise_value(&self, v: JsValue) -> bool { v.is_object() && { let ptr = v.as_js_object_ptr(); // SAFETY: `is_object` 与 `ptr` 非空双守卫保证可解引用;只读 type_tag 判定,立即消费。 !ptr.is_null() && unsafe { &*ptr }.is_promise_obj() } } /// 入队一条微任务(追加到队尾,保证 FIFO)。 pub(super) fn enqueue(&mut self, job: Microtask) { self.job_queue.push_back(job); } /// PromiseResolve(C, value):value 为原生 Promise 且其 `constructor` 与 C /// 同值时原样返回(快路径,不新建能力);否则按 C 建能力并经能力 resolve /// 结算(thenable 委托入队)。constructor getter / 能力构造 / resolve 抛错时 /// 透传原异常值。 /// /// # 副作用 /// - 快路径无副作用;其余路径新建 Promise 并可能直接结算或入队 Thenable 微任务。 pub(super) fn promise_resolve_ctor(&mut self, ctor: JsValue, value: JsValue) -> Result { if self.is_promise_value(value) { // SAFETY: is_promise_value 保证指针非空且 type_tag 为 Promise;此处只读 `constructor`,即时消费。 let obj = unsafe { &*value.as_js_object_ptr() }; let ctor_si = self.kernel_core.perm_interner().intern("constructor").0; let v_ctor = match self.ordinary_get(obj, ctor_si, value) { Ok(c) => c, Err(e) => { let exc = self .last_uncaught_value .take() .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e)); return Err(exc); } }; if oxide_runtime_api::same_value(v_ctor, ctor) { return Ok(value); } } let (promise, resolve, _) = self.new_promise_capability_with_ctor(ctor)?; if let Err(e) = self.call_function_sync(resolve, JsValue::undefined(), &[value]) { let exc = self .last_uncaught_value .take() .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e)); return Err(exc); } Ok(promise) } /// `PromiseResolve` 核心:`x === promise` 抛 TypeError;thenable 委托入队; /// 其余直接以 `x` 完成 promise。 /// /// # 副作用 /// - 可能入队 Thenable 微任务,或直接 settle 目标 promise。 /// - then getter 抛错时拒绝目标 promise(错误在内部消化,调用方无需处理)。 pub(crate) fn resolve_promise(&mut self, promise: JsValue, x: JsValue) -> Result<(), String> { // alreadyResolved 守卫在 resolve 闭包层(首次调用置位,含 thenable 委托期间)。 // 本函数自身不检查:委托闭包调用它时须放行(委托才是真正结算路径); // 二次结算由 fulfill/reject 的 state != Pending 守卫兜底。 if oxide_runtime_api::same_value(x, promise) { let err = oxide_builtins::error::create_type_error(self, "Chaining cycle detected for promise"); return self.reject_promise(promise, err); } if x.is_object() { // SAFETY: `is_object` 保证指针非空且指向存活对象;只读 `then` 属性,本次读取内即时消费。 let x_obj = unsafe { &*x.as_js_object_ptr() }; let then_si = self.kernel_core.perm_interner().intern("then").0; let then = match self.ordinary_get(x_obj, then_si, x) { Ok(v) => v, Err(e) => { let exc = self .last_uncaught_value .take() .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e)); return self.reject_promise(promise, exc); } }; if oxide_builtins::iterator::is_callable(then) { // thenable 委托期间本 promise 仍未 settle,但 already_resolved 已置位, // 后续 executor 的 resolve/reject 均 no-op。委托用独立结算代理闭包 // (绕过 alreadyResolved——委托才是真正结算路径),仍受 state != Pending 守卫。 let state = self.promise_state_ptr(promise); // SAFETY: 状态盒随已建 Promise 存活、指针非空;本块唯一可变借用,写 already_resolved 后结束。 let state = unsafe { &mut *state }; state.already_resolved = true; let resolve = self.make_resolve_reject_fn(promise, false, true); let reject = self.make_resolve_reject_fn(promise, true, true); self.enqueue(Microtask::Thenable { thenable: x, then, resolve, reject, }); return Ok(()); } } self.fulfill_promise(promise, x) } /// 以 `value` 完成 promise:只 settle 一次,完成后触发该态反应入队。 pub(crate) fn fulfill_promise(&mut self, promise: JsValue, value: JsValue) -> Result<(), String> { let state_ptr = self.promise_state_ptr(promise); let settled = { // SAFETY: `state_ptr` 取自 promise_state_ptr,盒非空且随 promise 存活;本块唯一可变借用,块末释放。 let state = unsafe { &mut *state_ptr }; if state.state != PromiseStateKind::Pending { return Ok(()); } state.state = PromiseStateKind::Fulfilled; state.result = value; std::mem::take(&mut state.reactions) }; // 只触发 fulfill 反应;reject 反应留待拒绝路径。 for r in settled.into_iter().filter(|r| r.is_fulfill) { self.enqueue(Microtask::Reaction { is_fulfill: true, handler: r.handler, argument: value, resolve: r.resolve, reject: r.reject, }); } Ok(()) } /// 以 `reason` 拒绝 promise:只 settle 一次,完成后触发该态反应入队。 pub(crate) fn reject_promise(&mut self, promise: JsValue, reason: JsValue) -> Result<(), String> { let state_ptr = self.promise_state_ptr(promise); let settled = { // SAFETY: `state_ptr` 取自 promise_state_ptr,盒非空且随 promise 存活;本块唯一可变借用,块末释放。 let state = unsafe { &mut *state_ptr }; if state.state != PromiseStateKind::Pending { return Ok(()); } state.state = PromiseStateKind::Rejected; state.result = reason; std::mem::take(&mut state.reactions) }; // 只触发 reject 反应;fulfill 反应留待完成路径。 for r in settled.into_iter().filter(|r| !r.is_fulfill) { self.enqueue(Microtask::Reaction { is_fulfill: false, handler: r.handler, argument: reason, resolve: r.resolve, reject: r.reject, }); } Ok(()) } /// PromiseResolve(%Promise%, value)核心:value 为原生 Promise 时读取其 /// `constructor`,与 %Promise% 相同则原样返回;否则新建能力并经能力 resolve /// 结算(thenable 委托)。constructor getter / 结算抛错时透传原异常值。 pub(crate) fn promise_resolve(&mut self, value: JsValue) -> Result { if self.is_promise_value(value) { // SAFETY: `is_promise_value` 保证指针非空且 type_tag 为 Promise;只读 `constructor`,即时消费。 let obj = unsafe { &*value.as_js_object_ptr() }; let ctor_si = self.kernel_core.perm_interner().intern("constructor").0; let ctor = match self.ordinary_get(obj, ctor_si, value) { Ok(c) => c, Err(e) => { let exc = self .last_uncaught_value .take() .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e)); return Err(exc); } }; let intrinsic = JsValue::from_js_object(self.realm.promise_constructor.borrow().as_ptr() as *mut JsObject); if oxide_runtime_api::same_value(ctor, intrinsic) { return Ok(value); } } let (promise, _resolve, _) = self.new_promise_capability(); if let Err(e) = self.resolve_promise(promise, value) { let exc = self .last_uncaught_value .take() .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e)); return Err(exc); } Ok(promise) } } /// resolve 闭包:`resolve(x)` 对目标 promise 执行 PromiseResolve。 /// 非委托闭包入口检查 alreadyResolved(首次调用置位,后续 no-op); /// 委托闭包(thenable 的 resolve)绕过该检查直接结算。 fn promise_resolve_closure(vm: &mut Vm, args: &[u8]) -> NativeResult { let Some(promise) = vm.promise_from_callee() else { return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "resolve function is invalid")); }; let delegated = vm.callee_delegated_flag(); let x = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; if !delegated { // 非委托:alreadyResolved 守卫——首次调用置位,含 thenable 委托期间。 let state = vm.promise_state_ptr(promise); // SAFETY: 非委托分支的唯一可变借用;状态盒随 promise 存活,写 already_resolved 后结束。 let state = unsafe { &mut *state }; if state.already_resolved { return NativeResult::Ok(JsValue::undefined()); } state.already_resolved = true; } if let Err(e) = vm.resolve_promise(promise, x) { let exc = vm .last_uncaught_value .take() .unwrap_or_else(|| oxide_builtins::error::create_from_text(vm, &e)); let _ = vm.reject_promise(promise, exc); } NativeResult::Ok(JsValue::undefined()) } /// reject 闭包:`reject(x)` 直接以 x 拒绝目标 promise。 fn promise_reject_closure(vm: &mut Vm, args: &[u8]) -> NativeResult { let Some(promise) = vm.promise_from_callee() else { return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "reject function is invalid")); }; let delegated = vm.callee_delegated_flag(); let reason = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; if !delegated { let state = vm.promise_state_ptr(promise); // SAFETY: 非委托分支的唯一可变借用;状态盒随 promise 存活,写 already_resolved 后结束。 let state = unsafe { &mut *state }; if state.already_resolved { return NativeResult::Ok(JsValue::undefined()); } state.already_resolved = true; } let _ = vm.reject_promise(promise, reason); NativeResult::Ok(JsValue::undefined()) } /// GetCapabilitiesExecutor:捕获 resolve/reject 到自身 prop;能力已持有任一 /// 非 undefined 值时再次调用抛 TypeError。 fn capability_executor(vm: &mut Vm, args: &[u8]) -> NativeResult { let callee = vm.reg(254); if !callee.is_object() { return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "executor is invalid")); } // SAFETY: `callee` 来自 reg(254) 且 `is_object` 守卫保证指针非空存活;只读 CAP_* 判定,不跨 GC/reset。 let obj = unsafe { &*callee.as_js_object_ptr() }; let res_si = vm.kernel_core.perm_interner().intern(CAP_RESOLVE_PROP).0; let rej_si = vm.kernel_core.perm_interner().intern(CAP_REJECT_PROP).0; let held = vm.resolve_property(obj, res_si).is_some_and(|v| !v.is_undefined()) || vm.resolve_property(obj, rej_si).is_some_and(|v| !v.is_undefined()); if held { return NativeResult::Err(oxide_builtins::error::create_type_error( vm, "Promise capability executor has already been called", )); } let resolve = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; let reject = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; // SAFETY: 同一 `callee`,前一读借用已结束;对象存活且本块唯一可变借用,写 CAP_* 无别名。 let obj = unsafe { &mut *callee.as_js_object_ptr() }; vm.set_or_create_prop_value(obj, res_si, resolve); vm.set_or_create_prop_value(obj, rej_si, reject); NativeResult::Ok(JsValue::undefined()) }