From 90260124967e49f4b3fcd61c6f95ecd088947c2b Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Wed, 16 Sep 2026 18:04:29 +0000 Subject: [PATCH] =?UTF-8?q?refactor:=20Promise=20=E7=BB=93=E7=AE=97?= =?UTF-8?q?=E6=9E=84=E9=80=A0=E9=98=9F=E5=88=97=E6=8B=86=E5=85=A5=20promis?= =?UTF-8?q?e=20=E5=AD=90=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_vm/src/promise/constructor.rs | 272 +++++++ crates/oxide_vm/src/promise/jobs.rs | 138 ++++ .../src/{promise.rs => promise/mod.rs} | 757 +----------------- crates/oxide_vm/src/promise/settlement.rs | 389 +++++++++ 4 files changed, 806 insertions(+), 750 deletions(-) create mode 100644 crates/oxide_vm/src/promise/constructor.rs create mode 100644 crates/oxide_vm/src/promise/jobs.rs rename crates/oxide_vm/src/{promise.rs => promise/mod.rs} (63%) create mode 100644 crates/oxide_vm/src/promise/settlement.rs diff --git a/crates/oxide_vm/src/promise/constructor.rs b/crates/oxide_vm/src/promise/constructor.rs new file mode 100644 index 00000000..72f48da2 --- /dev/null +++ b/crates/oxide_vm/src/promise/constructor.rs @@ -0,0 +1,272 @@ +//! Promise 构造与内建安装:`Construct(C, args)` 通用路径(native / +//! bytecode 构造器)与 %Promise% 内建对象初始化。 +//! +//! `init_promise_intrinsics` 在 VM 创建与 `full_reset` 后调用:原型继承 +//! session 的 Object/Function 原型,session 重建后须重挂并重绑 global 槽。 + +use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; +use oxide_runtime_api::NativeResult; +use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes}; +use oxide_types::value::JsValue; + +use crate::vm::{FrameArgs, FrameContinuation, Vm}; + +use super::{ + is_constructor_value, promise_catch, promise_finally, promise_static_all, promise_static_all_settled, + promise_static_any, promise_static_race, promise_static_reject, promise_static_resolve, + promise_static_with_resolvers, promise_then, PromiseState, PromiseStateKind, +}; + +impl Vm { + /// 创建空 Promise 对象(proto = `%Promise.prototype%`),状态盒为 Pending。 + pub(super) fn create_promise_object(&mut self) -> JsValue { + let proto_val = JsValue::from_js_object(self.promise_proto.as_ptr() as *mut JsObject); + let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)); + let obj = unsafe { &mut *ptr }; + obj.type_tag = JsObject::OBJ_TYPE_PROMISE; + let state = Box::new(PromiseState { + state: PromiseStateKind::Pending, + result: JsValue::undefined(), + reactions: Vec::new(), + resolve_fn: JsValue::undefined(), + reject_fn: JsValue::undefined(), + already_resolved: false, + promoted_clone: std::ptr::null_mut(), + }); + obj.set_native_data(Box::into_raw(state) as *mut u8); + JsValue::from_js_object(ptr) + } + + /// 构造调用(Construct(C, args)):native 构造器值传递调用,bytecode 构造器 + /// 压构造帧执行(含 derived 构造器 super() 语义),返回值非对象时回退到新对象。 + pub(super) fn construct_ctor(&mut self, ctor: JsValue, args: &[JsValue]) -> Result { + // IsConstructor 校验:arrow / 非构造 native / 普通值拒绝。 + if !is_constructor_value(ctor) { + return Err(oxide_builtins::error::create_type_error(self, "constructor is not a constructor")); + } + let ctor_obj = unsafe { &*ctor.as_js_object_ptr() }; + // native 构造器:receiver 为新对象,值传递调用(%Promise% 主路径)。 + if ctor_obj.native_fn().is_some() { + let this_ptr = self.alloc_ctor_this(ctor_obj)?; + let this_val = JsValue::from_js_object(this_ptr); + return match self.call_function_sync(ctor, this_val, args) { + Ok(ret) if ret.is_object() => Ok(ret), + Ok(_) => Ok(this_val), + Err(e) => Err(self + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e))), + }; + } + self.call_constructor_bytecode_inline(ctor, ctor_obj, args) + } + + /// 分配构造 this:proto = ctor.prototype(缺省 Object.prototype)。 + fn alloc_ctor_this(&mut self, ctor_obj: &JsObject) -> Result<*mut JsObject, JsValue> { + let proto_si = self.kernel_core.perm_interner().intern("prototype").0; + let proto_val = match self.resolve_property(ctor_obj, proto_si) { + Some(p) if p.is_object() => p, + _ => JsValue::from_js_object(self.session.builtin_world().object_proto.as_ptr() as *mut JsObject), + }; + Ok(self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val))) + } + + /// bytecode 构造器构造调用:压构造帧内嵌 dispatch 执行(derived 构造器 + /// super() 前 this = undefined,new.target = ctor),结果经 `do_return` 交付 + /// regs[0](非对象回退构造 this)。 + /// + /// # 副作用 + /// - 经 `save_inline_state` / `restore_inline_state` 保存恢复调用方执行状态。 + /// - `construct_dispatch` 标志令构造帧弹出时交付结果而非继续执行。 + fn call_constructor_bytecode_inline( + &mut self, ctor: JsValue, ctor_obj: &JsObject, args: &[JsValue], + ) -> Result { + // 按构造器自身记录的表代际解析:代际表缺失(已回收)或下标越界与 + // 原生函数哨兵同口径报 TypeError。 + let callee_module = match self.callee_module(ctor_obj) { + Some(m) => m, + None => { + return Err(oxide_builtins::error::create_type_error(self, "constructor is not a constructor")); + } + }; + if callee_module.is_generator || callee_module.is_async { + return Err(oxide_builtins::error::create_type_error(self, "constructor is not a constructor")); + } + // 先拷出寄存器数,释放对代际表的借用后再进入可变借用区。 + let callee_reg_count = callee_module.n_registers; + let new_obj_ptr = self.alloc_ctor_this(ctor_obj)?; + let new_obj_val = JsValue::from_js_object(new_obj_ptr); + // derived 构造器 super() 前 this 为 undefined,基类 this = 新对象。 + let this_value = if ctor_obj.is_derived_constructor() { + JsValue::undefined() + } else { + new_obj_val + }; + // 入口已解析代际表:读 n_registers 定保存窗口。 + let window = self.active_reg_limit.max(callee_reg_count).max(1) as usize; + let saved = self.save_inline_state(window); + let prev_construct = self.construct_dispatch; + self.construct_dispatch = true; + let result = self + .push_bytecode_frame( + ctor, + this_value, + FrameArgs::Slice(args), + Some(0), + Some(new_obj_val), + ctor, + FrameContinuation::None, + 0, + ) + .and_then(|_| self.dispatch()); + self.construct_dispatch = prev_construct; + self.restore_inline_state(saved); + match result { + Ok(v) => Ok(v), + Err(e) => Err(self + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e))), + } + } + + /// 初始化/重建 Promise 内建对象:`%Promise%` 构造器与 `%Promise.prototype%`, + /// 绑定到 global 的 `Promise` 槽。 + /// + /// 在 VM 创建与 `full_reset` 后调用——原型继承自 session 的 Object/Function + /// 原型,session 重建后须重挂并重绑 global 槽。 + pub(crate) fn init_promise_intrinsics(&mut self) { + let sf = self.kernel_core.perm_interner().as_ref(); + let sh = self.kernel_core.shape_forge().as_ref(); + let fn_proto_val = self.session.builtin_world().fn_proto_val(); + let world = self.session.builtin_world(); + let object_proto_val = + JsValue::from_js_object(self.session.builtin_world().object_proto.as_ptr() as *mut JsObject); + + // %Promise.prototype%:proto = Object.prototype,方法 then/catch/finally + @@toStringTag。 + let mut proto = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, object_proto_val)); + let ctor_si = sf.intern("constructor").0; + let ctor_shape = sh.make_shape(proto.shape_id(), ctor_si); + proto.set_shape_id(ctor_shape); + proto.push_prop(JsValue::undefined()); + proto.set_data_meta(0u32, PropAttributes::new(true, false, true)); + oxide_kernel::bind_methods_static!( + &mut proto, + sf, + sh, + world, + ("then", promise_then as *const (), 2), + ("catch", promise_catch as *const (), 1), + ("finally", promise_finally as *const (), 1), + ); + let tag_si = sf.intern("@@toStringTag").0; + let tag_shape = sh.make_shape(proto.shape_id(), tag_si); + proto.set_shape_id(tag_shape); + let tag_pos = proto.push_prop(JsValue::perm_string(sf.string_ptr(sf.intern("Promise").0))); + proto.set_data_meta(tag_pos, PropAttributes::new(false, false, true)); + + // %Promise% 构造器:proto = Function.prototype,静态方法 resolve/reject。 + // 自身属性顺序:length、name、prototype(CreateBuiltinFunction 顺序)。 + let mut ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto_val)); + ctor.set_function(true); + ctor.set_native_arg_count(1); + // SAFETY: promise_constructor 是 NativeFn 函数项。 + ctor.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(promise_constructor as *const ()) })); + ctor.type_tag = JsObject::OBJ_TYPE_CONSTRUCTOR; + let length_si = sf.intern("length").0; + let ctor_shape1 = sh.make_shape(ctor.shape_id(), length_si); + ctor.set_shape_id(ctor_shape1); + let lpos = ctor.push_prop(JsValue::int(1)); + ctor.set_data_meta(lpos, PropAttributes::new(false, false, true)); + let name_si = sf.intern("name").0; + let ctor_shape2 = sh.make_shape(ctor.shape_id(), name_si); + ctor.set_shape_id(ctor_shape2); + let npos = ctor.push_prop(JsValue::perm_string(sf.string_ptr(sf.intern("Promise").0))); + ctor.set_data_meta(npos, PropAttributes::new(false, false, true)); + let proto2_si = sf.intern("prototype").0; + let ctor_shape3 = sh.make_shape(ctor.shape_id(), proto2_si); + ctor.set_shape_id(ctor_shape3); + ctor.push_prop(JsValue::undefined()); + ctor.set_data_meta(2u32, PropAttributes::new(false, false, false)); + oxide_kernel::bind_methods_static!( + &mut ctor, + sf, + sh, + world, + ("resolve", promise_static_resolve as *const (), 1), + ("reject", promise_static_reject as *const (), 1), + ("all", promise_static_all as *const (), 1), + ("race", promise_static_race as *const (), 1), + ("allSettled", promise_static_all_settled as *const (), 1), + ("any", promise_static_any as *const (), 1), + ("withResolvers", promise_static_with_resolvers as *const (), 0), + ); + + // 固定地址后互相接线:proto.constructor ↔ ctor.prototype。 + Self::swap_intrinsic_proto(&mut self.promise_proto, *proto); + Self::swap_intrinsic_proto(&mut self.promise_constructor, *ctor); + let proto_mut = unsafe { &mut *self.promise_proto.as_mut_ptr() }; + proto_mut.set_prop_at(0u32, JsValue::from_js_object(self.promise_constructor.as_ptr() as *mut JsObject)); + let ctor_mut = unsafe { &mut *self.promise_constructor.as_mut_ptr() }; + // prototype 槽位在 length/name 之后(下标 2)。 + ctor_mut.set_prop_at(2u32, JsValue::from_js_object(self.promise_proto.as_ptr() as *mut JsObject)); + + // 绑定 global:槽已存在则更新(full_reset 未重建 global 时旧槽指向已弃 ctor)。 + let global_ptr = self.session.global_object().as_ptr() as *mut JsObject; + let global = unsafe { &mut *global_ptr }; + let si = self.kernel_core.perm_interner().intern("Promise").0; + let ctor_val = JsValue::from_js_object(self.promise_constructor.as_ptr() as *mut JsObject); + if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) { + global.set_prop_at(pos, ctor_val); + } else { + let shape = self.kernel_core.shape_forge().make_shape(global.shape_id(), si); + global.set_shape_id(shape); + let pos = global.push_prop(ctor_val); + // global 数据属性:writable:true、enumerable:false、configurable:true。 + global.set_data_meta(pos, PropAttributes::new(true, false, true)); + global.bump_generation(); + } + + // Promise.any 的拒绝路径依赖 AggregateError 内建。 + self.init_aggregate_error_intrinsics(); + } +} + +/// `Promise` 构造器:设置状态盒并同步调用 executor(resolve, reject)。 +fn promise_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { + let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); + // `new` 调用时 this 是 proto 链含 %Promise.prototype% 的新对象;裸调用拒绝。 + if !vm.has_promise_proto(this_val) { + return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "Promise must be called with new")); + } + let obj = unsafe { &mut *this_val.as_js_object_ptr() }; + obj.type_tag = JsObject::OBJ_TYPE_PROMISE; + let resolve = vm.make_resolve_reject_fn(this_val, false, false); + let reject = vm.make_resolve_reject_fn(this_val, true, false); + let state = Box::new(PromiseState { + state: PromiseStateKind::Pending, + result: JsValue::undefined(), + reactions: Vec::new(), + resolve_fn: resolve, + reject_fn: reject, + already_resolved: false, + promoted_clone: std::ptr::null_mut(), + }); + obj.set_native_data(Box::into_raw(state) as *mut u8); + let executor = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + if !oxide_builtins::iterator::is_callable(executor) { + return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "Promise executor is not a function")); + } + match vm.call_function_sync(executor, JsValue::undefined(), &[resolve, reject]) { + Ok(_) => NativeResult::Ok(this_val), + Err(e) => { + // executor 抛错:以抛出的值为拒绝原因(原值经 last_uncaught_value 保留)。 + let exc = vm + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_from_text(vm, &e)); + let _ = vm.reject_promise(this_val, exc); + NativeResult::Ok(this_val) + } + } +} diff --git a/crates/oxide_vm/src/promise/jobs.rs b/crates/oxide_vm/src/promise/jobs.rs new file mode 100644 index 00000000..fa53c28b --- /dev/null +++ b/crates/oxide_vm/src/promise/jobs.rs @@ -0,0 +1,138 @@ +//! 微任务队列:FIFO drain 执行与 session GC 的队列值遍历 / 改写。 + +use oxide_types::value::JsValue; + +use crate::vm::Vm; +use crate::vm_warn; + +use super::{Microtask, MAX_DRAIN_JOBS}; + +impl Vm { + /// drain 微任务队列:FIFO 逐条处理直到清空或达到上限。 + /// + /// # 副作用 + /// - 任务内调用 JS 回调(`call_function_sync`),可能入队新任务。 + /// - 单任务抛错不中断 drain(未处理拒绝按规范不可见)。 + pub(crate) fn drain_job_queue(&mut self) { + let mut count = 0usize; + while let Some(job) = self.job_queue.pop_front() { + count += 1; + if count > MAX_DRAIN_JOBS { + vm_warn!("drain_job_queue: exceeded {MAX_DRAIN_JOBS} microtasks, aborting"); + break; + } + self.run_microtask(job); + } + } + + /// 执行单条微任务。 + fn run_microtask(&mut self, job: Microtask) { + match job { + Microtask::Reaction { + is_fulfill, + handler, + argument, + resolve, + reject, + } => { + // 空处理器(undefined/非可调用)→ 值直通:fulfill 调 resolve,reject 调 reject。 + let handler_result = if oxide_builtins::iterator::is_callable(handler) { + self.call_function_sync(handler, JsValue::undefined(), &[argument]) + } else if is_fulfill { + Ok(argument) + } else { + let _ = self.call_function_sync(reject, JsValue::undefined(), &[argument]); + return; + }; + match handler_result { + Ok(x) => { + let _ = self.call_function_sync(resolve, JsValue::undefined(), &[x]); + } + Err(e) => { + let exc = self + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e)); + let _ = self.call_function_sync(reject, JsValue::undefined(), &[exc]); + } + } + } + Microtask::Thenable { + thenable, + then, + resolve, + reject, + } => { + // 委托调用;抛错则拒绝目标 promise。 + match self.call_function_sync(then, thenable, &[resolve, reject]) { + Ok(_) => {} + Err(e) => { + let exc = self + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e)); + let _ = self.call_function_sync(reject, JsValue::undefined(), &[exc]); + } + } + } + } + } +} + +/// 供外部遍历微任务队列(GC mark/rewrite 用)。 +pub(crate) fn for_each_job_value(job: &Microtask, mut f: impl FnMut(JsValue)) { + match job { + Microtask::Reaction { + handler, + argument, + resolve, + reject, + .. + } => { + f(*handler); + f(*argument); + f(*resolve); + f(*reject); + } + Microtask::Thenable { + thenable, + then, + resolve, + reject, + } => { + f(*thenable); + f(*then); + f(*resolve); + f(*reject); + } + } +} + +/// 供外部改写微任务队列中的 JsValue(session GC sweep 用)。 +pub(crate) fn rewrite_job_values(job: &mut Microtask, mut rewrite: impl FnMut(JsValue) -> JsValue) { + match job { + Microtask::Reaction { + handler, + argument, + resolve, + reject, + .. + } => { + *handler = rewrite(*handler); + *argument = rewrite(*argument); + *resolve = rewrite(*resolve); + *reject = rewrite(*reject); + } + Microtask::Thenable { + thenable, + then, + resolve, + reject, + } => { + *thenable = rewrite(*thenable); + *then = rewrite(*then); + *resolve = rewrite(*resolve); + *reject = rewrite(*reject); + } + } +} diff --git a/crates/oxide_vm/src/promise.rs b/crates/oxide_vm/src/promise/mod.rs similarity index 63% rename from crates/oxide_vm/src/promise.rs rename to crates/oxide_vm/src/promise/mod.rs index 15e0a0e4..04f07596 100644 --- a/crates/oxide_vm/src/promise.rs +++ b/crates/oxide_vm/src/promise/mod.rs @@ -14,8 +14,13 @@ use oxide_types::private_key::make_int_key; use oxide_types::value::JsValue; use crate::native::NativeFn; -use crate::vm::{FrameArgs, FrameContinuation, Vm}; -use crate::vm_warn; +use crate::vm::Vm; + +mod constructor; +mod jobs; +mod settlement; + +pub(crate) use jobs::{for_each_job_value, rewrite_job_values}; /// Promise 的 settled 状态。 #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -114,413 +119,6 @@ enum AggregateKind { } impl Vm { - /// 创建空 Promise 对象(proto = `%Promise.prototype%`),状态盒为 Pending。 - fn create_promise_object(&mut self) -> JsValue { - let proto_val = JsValue::from_js_object(self.promise_proto.as_ptr() as *mut JsObject); - let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)); - let obj = unsafe { &mut *ptr }; - obj.type_tag = JsObject::OBJ_TYPE_PROMISE; - let state = Box::new(PromiseState { - state: PromiseStateKind::Pending, - result: JsValue::undefined(), - reactions: Vec::new(), - resolve_fn: JsValue::undefined(), - reject_fn: JsValue::undefined(), - already_resolved: false, - promoted_clone: std::ptr::null_mut(), - }); - obj.set_native_data(Box::into_raw(state) as *mut u8); - JsValue::from_js_object(ptr) - } - - /// 新建 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); - 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)`。 - fn new_promise_capability_with_ctor(&mut self, ctor: JsValue) -> Result<(JsValue, JsValue, JsValue), JsValue> { - let intrinsic = JsValue::from_js_object(self.promise_constructor.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) = { - 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.session.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); - let obj = unsafe { &mut *ptr }; - self.add_fn_name_length(obj, "", 2); - JsValue::from_js_object(ptr) - } - - /// 构造调用(Construct(C, args)):native 构造器值传递调用,bytecode 构造器 - /// 压构造帧执行(含 derived 构造器 super() 语义),返回值非对象时回退到新对象。 - fn construct_ctor(&mut self, ctor: JsValue, args: &[JsValue]) -> Result { - // IsConstructor 校验:arrow / 非构造 native / 普通值拒绝。 - if !is_constructor_value(ctor) { - return Err(oxide_builtins::error::create_type_error(self, "constructor is not a constructor")); - } - let ctor_obj = unsafe { &*ctor.as_js_object_ptr() }; - // native 构造器:receiver 为新对象,值传递调用(%Promise% 主路径)。 - if ctor_obj.native_fn().is_some() { - let this_ptr = self.alloc_ctor_this(ctor_obj)?; - let this_val = JsValue::from_js_object(this_ptr); - return match self.call_function_sync(ctor, this_val, args) { - Ok(ret) if ret.is_object() => Ok(ret), - Ok(_) => Ok(this_val), - Err(e) => Err(self - .last_uncaught_value - .take() - .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e))), - }; - } - self.call_constructor_bytecode_inline(ctor, ctor_obj, args) - } - - /// 分配构造 this:proto = ctor.prototype(缺省 Object.prototype)。 - fn alloc_ctor_this(&mut self, ctor_obj: &JsObject) -> Result<*mut JsObject, JsValue> { - let proto_si = self.kernel_core.perm_interner().intern("prototype").0; - let proto_val = match self.resolve_property(ctor_obj, proto_si) { - Some(p) if p.is_object() => p, - _ => JsValue::from_js_object(self.session.builtin_world().object_proto.as_ptr() as *mut JsObject), - }; - Ok(self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val))) - } - - /// bytecode 构造器构造调用:压构造帧内嵌 dispatch 执行(derived 构造器 - /// super() 前 this = undefined,new.target = ctor),结果经 `do_return` 交付 - /// regs[0](非对象回退构造 this)。 - /// - /// # 副作用 - /// - 经 `save_inline_state` / `restore_inline_state` 保存恢复调用方执行状态。 - /// - `construct_dispatch` 标志令构造帧弹出时交付结果而非继续执行。 - fn call_constructor_bytecode_inline( - &mut self, ctor: JsValue, ctor_obj: &JsObject, args: &[JsValue], - ) -> Result { - // 按构造器自身记录的表代际解析:代际表缺失(已回收)或下标越界与 - // 原生函数哨兵同口径报 TypeError。 - let callee_module = match self.callee_module(ctor_obj) { - Some(m) => m, - None => { - return Err(oxide_builtins::error::create_type_error(self, "constructor is not a constructor")); - } - }; - if callee_module.is_generator || callee_module.is_async { - return Err(oxide_builtins::error::create_type_error(self, "constructor is not a constructor")); - } - // 先拷出寄存器数,释放对代际表的借用后再进入可变借用区。 - let callee_reg_count = callee_module.n_registers; - let new_obj_ptr = self.alloc_ctor_this(ctor_obj)?; - let new_obj_val = JsValue::from_js_object(new_obj_ptr); - // derived 构造器 super() 前 this 为 undefined,基类 this = 新对象。 - let this_value = if ctor_obj.is_derived_constructor() { - JsValue::undefined() - } else { - new_obj_val - }; - // 入口已解析代际表:读 n_registers 定保存窗口。 - let window = self.active_reg_limit.max(callee_reg_count).max(1) as usize; - let saved = self.save_inline_state(window); - let prev_construct = self.construct_dispatch; - self.construct_dispatch = true; - let result = self - .push_bytecode_frame( - ctor, - this_value, - FrameArgs::Slice(args), - Some(0), - Some(new_obj_val), - ctor, - FrameContinuation::None, - 0, - ) - .and_then(|_| self.dispatch()); - self.construct_dispatch = prev_construct; - self.restore_inline_state(saved); - match result { - Ok(v) => Ok(v), - Err(e) => Err(self - .last_uncaught_value - .take() - .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e))), - } - } - - /// 构造携带目标 promise 的 native 闭包(resolve 或 reject 角色)。 - /// `delegated` 为 true 表示 thenable 委托用的结算代理:绕过 alreadyResolved - /// (委托是唯一真正的结算路径,由 then 的 resolve/reject 触发),但 state != Pending - /// 仍阻止二次结算。 - 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.session.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); - 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; - } - 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; - } - 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`)。 - fn promise_state_ptr(&self, promise: JsValue) -> *mut PromiseState { - 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(); - !ptr.is_null() && unsafe { &*ptr }.is_promise_obj() - } - } - - /// 校验值的原型链含 `%Promise.prototype%`(`new Promise` 构造出的实例特征)。 - fn has_promise_proto(&self, v: JsValue) -> bool { - if !v.is_object() { - return false; - } - let target = self.promise_proto.as_ptr() as *mut JsObject; - let mut cursor = v; - for _ in 0..crate::vm::MAX_PROTO_CHAIN_DEPTH { - if !cursor.is_object() { - return false; - } - let ptr = cursor.as_js_object_ptr(); - if ptr.is_null() { - return false; - } - if std::ptr::eq(ptr, target) { - return true; - } - cursor = unsafe { &*ptr }.proto(); - } - false - } - - /// 入队一条微任务(追加到队尾,保证 FIFO)。 - fn enqueue(&mut self, job: Microtask) { - self.job_queue.push_back(job); - } - - /// `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() { - 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); - 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 = { - 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, - }); - } - // 原件若已晋升出克隆,把结算传导到克隆:克隆上晋升后新挂的反应方随此触发 - // (顶层 var 读克隆,原件反应已在本处直接触发,不重复传导)。 - let clone_ptr = unsafe { (*state_ptr).promoted_clone }; - if !clone_ptr.is_null() { - let clone = JsValue::from_js_object(clone_ptr); - let _ = self.fulfill_promise(clone, value); - } - 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 = { - 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, - }); - } - // 原件若已晋升出克隆,把拒绝传导到克隆(同 fulfill:顶层 var 读克隆)。 - let clone_ptr = unsafe { (*state_ptr).promoted_clone }; - if !clone_ptr.is_null() { - let clone = JsValue::from_js_object(clone_ptr); - let _ = self.reject_promise(clone, reason); - } - 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) { - 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.promise_constructor.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) - } - /// `PerformPromiseThen` 入口(内置能力):注册 fulfill/reject 反应并返回派生 /// promise。能力恒为 `%Promise%`——供引擎内部 await 机制(async / /// async generator / AsyncFromSync)使用,规范上这些路径由调用方提供能力, @@ -650,177 +248,6 @@ impl Vm { } Ok(ctor) } - - /// drain 微任务队列:FIFO 逐条处理直到清空或达到上限。 - /// - /// # 副作用 - /// - 任务内调用 JS 回调(`call_function_sync`),可能入队新任务。 - /// - 单任务抛错不中断 drain(未处理拒绝按规范不可见)。 - pub(crate) fn drain_job_queue(&mut self) { - let mut count = 0usize; - while let Some(job) = self.job_queue.pop_front() { - count += 1; - if count > MAX_DRAIN_JOBS { - vm_warn!("drain_job_queue: exceeded {MAX_DRAIN_JOBS} microtasks, aborting"); - break; - } - self.run_microtask(job); - } - } - - /// 执行单条微任务。 - fn run_microtask(&mut self, job: Microtask) { - match job { - Microtask::Reaction { - is_fulfill, - handler, - argument, - resolve, - reject, - } => { - // 空处理器(undefined/非可调用)→ 值直通:fulfill 调 resolve,reject 调 reject。 - let handler_result = if oxide_builtins::iterator::is_callable(handler) { - self.call_function_sync(handler, JsValue::undefined(), &[argument]) - } else if is_fulfill { - Ok(argument) - } else { - let _ = self.call_function_sync(reject, JsValue::undefined(), &[argument]); - return; - }; - match handler_result { - Ok(x) => { - let _ = self.call_function_sync(resolve, JsValue::undefined(), &[x]); - } - Err(e) => { - let exc = self - .last_uncaught_value - .take() - .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e)); - let _ = self.call_function_sync(reject, JsValue::undefined(), &[exc]); - } - } - } - Microtask::Thenable { - thenable, - then, - resolve, - reject, - } => { - // 委托调用;抛错则拒绝目标 promise。 - match self.call_function_sync(then, thenable, &[resolve, reject]) { - Ok(_) => {} - Err(e) => { - let exc = self - .last_uncaught_value - .take() - .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e)); - let _ = self.call_function_sync(reject, JsValue::undefined(), &[exc]); - } - } - } - } - } - - /// 初始化/重建 Promise 内建对象:`%Promise%` 构造器与 `%Promise.prototype%`, - /// 绑定到 global 的 `Promise` 槽。 - /// - /// 在 VM 创建与 `full_reset` 后调用——原型继承自 session 的 Object/Function - /// 原型,session 重建后须重挂并重绑 global 槽。 - pub(crate) fn init_promise_intrinsics(&mut self) { - let sf = self.kernel_core.perm_interner().as_ref(); - let sh = self.kernel_core.shape_forge().as_ref(); - let fn_proto_val = self.session.builtin_world().fn_proto_val(); - let world = self.session.builtin_world(); - let object_proto_val = - JsValue::from_js_object(self.session.builtin_world().object_proto.as_ptr() as *mut JsObject); - - // %Promise.prototype%:proto = Object.prototype,方法 then/catch/finally + @@toStringTag。 - let mut proto = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, object_proto_val)); - let ctor_si = sf.intern("constructor").0; - let ctor_shape = sh.make_shape(proto.shape_id(), ctor_si); - proto.set_shape_id(ctor_shape); - proto.push_prop(JsValue::undefined()); - proto.set_data_meta(0u32, PropAttributes::new(true, false, true)); - oxide_kernel::bind_methods_static!( - &mut proto, - sf, - sh, - world, - ("then", promise_then as *const (), 2), - ("catch", promise_catch as *const (), 1), - ("finally", promise_finally as *const (), 1), - ); - let tag_si = sf.intern("@@toStringTag").0; - let tag_shape = sh.make_shape(proto.shape_id(), tag_si); - proto.set_shape_id(tag_shape); - let tag_pos = proto.push_prop(JsValue::perm_string(sf.string_ptr(sf.intern("Promise").0))); - proto.set_data_meta(tag_pos, PropAttributes::new(false, false, true)); - - // %Promise% 构造器:proto = Function.prototype,静态方法 resolve/reject。 - // 自身属性顺序:length、name、prototype(CreateBuiltinFunction 顺序)。 - let mut ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto_val)); - ctor.set_function(true); - ctor.set_native_arg_count(1); - // SAFETY: promise_constructor 是 NativeFn 函数项。 - ctor.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(promise_constructor as *const ()) })); - ctor.type_tag = JsObject::OBJ_TYPE_CONSTRUCTOR; - let length_si = sf.intern("length").0; - let ctor_shape1 = sh.make_shape(ctor.shape_id(), length_si); - ctor.set_shape_id(ctor_shape1); - let lpos = ctor.push_prop(JsValue::int(1)); - ctor.set_data_meta(lpos, PropAttributes::new(false, false, true)); - let name_si = sf.intern("name").0; - let ctor_shape2 = sh.make_shape(ctor.shape_id(), name_si); - ctor.set_shape_id(ctor_shape2); - let npos = ctor.push_prop(JsValue::perm_string(sf.string_ptr(sf.intern("Promise").0))); - ctor.set_data_meta(npos, PropAttributes::new(false, false, true)); - let proto2_si = sf.intern("prototype").0; - let ctor_shape3 = sh.make_shape(ctor.shape_id(), proto2_si); - ctor.set_shape_id(ctor_shape3); - ctor.push_prop(JsValue::undefined()); - ctor.set_data_meta(2u32, PropAttributes::new(false, false, false)); - oxide_kernel::bind_methods_static!( - &mut ctor, - sf, - sh, - world, - ("resolve", promise_static_resolve as *const (), 1), - ("reject", promise_static_reject as *const (), 1), - ("all", promise_static_all as *const (), 1), - ("race", promise_static_race as *const (), 1), - ("allSettled", promise_static_all_settled as *const (), 1), - ("any", promise_static_any as *const (), 1), - ("withResolvers", promise_static_with_resolvers as *const (), 0), - ); - - // 固定地址后互相接线:proto.constructor ↔ ctor.prototype。 - Self::swap_intrinsic_proto(&mut self.promise_proto, *proto); - Self::swap_intrinsic_proto(&mut self.promise_constructor, *ctor); - let proto_mut = unsafe { &mut *self.promise_proto.as_mut_ptr() }; - proto_mut.set_prop_at(0u32, JsValue::from_js_object(self.promise_constructor.as_ptr() as *mut JsObject)); - let ctor_mut = unsafe { &mut *self.promise_constructor.as_mut_ptr() }; - // prototype 槽位在 length/name 之后(下标 2)。 - ctor_mut.set_prop_at(2u32, JsValue::from_js_object(self.promise_proto.as_ptr() as *mut JsObject)); - - // 绑定 global:槽已存在则更新(full_reset 未重建 global 时旧槽指向已弃 ctor)。 - let global_ptr = self.session.global_object().as_ptr() as *mut JsObject; - let global = unsafe { &mut *global_ptr }; - let si = self.kernel_core.perm_interner().intern("Promise").0; - let ctor_val = JsValue::from_js_object(self.promise_constructor.as_ptr() as *mut JsObject); - if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) { - global.set_prop_at(pos, ctor_val); - } else { - let shape = self.kernel_core.shape_forge().make_shape(global.shape_id(), si); - global.set_shape_id(shape); - let pos = global.push_prop(ctor_val); - // global 数据属性:writable:true、enumerable:false、configurable:true。 - global.set_data_meta(pos, PropAttributes::new(true, false, true)); - global.bump_generation(); - } - - // Promise.any 的拒绝路径依赖 AggregateError 内建。 - self.init_aggregate_error_intrinsics(); - } } /// IsConstructor 近似判定:可调用、非 arrow,且 native 函数须带构造器 tag @@ -846,92 +273,6 @@ pub fn promise_settled_value(obj: &JsObject) -> Option<(bool, JsValue)> { } } -/// `Promise` 构造器:设置状态盒并同步调用 executor(resolve, reject)。 -fn promise_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { - let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); - // `new` 调用时 this 是 proto 链含 %Promise.prototype% 的新对象;裸调用拒绝。 - if !vm.has_promise_proto(this_val) { - return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "Promise must be called with new")); - } - let obj = unsafe { &mut *this_val.as_js_object_ptr() }; - obj.type_tag = JsObject::OBJ_TYPE_PROMISE; - let resolve = vm.make_resolve_reject_fn(this_val, false, false); - let reject = vm.make_resolve_reject_fn(this_val, true, false); - let state = Box::new(PromiseState { - state: PromiseStateKind::Pending, - result: JsValue::undefined(), - reactions: Vec::new(), - resolve_fn: resolve, - reject_fn: reject, - already_resolved: false, - promoted_clone: std::ptr::null_mut(), - }); - obj.set_native_data(Box::into_raw(state) as *mut u8); - let executor = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; - if !oxide_builtins::iterator::is_callable(executor) { - return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "Promise executor is not a function")); - } - match vm.call_function_sync(executor, JsValue::undefined(), &[resolve, reject]) { - Ok(_) => NativeResult::Ok(this_val), - Err(e) => { - // executor 抛错:以抛出的值为拒绝原因(原值经 last_uncaught_value 保留)。 - let exc = vm - .last_uncaught_value - .take() - .unwrap_or_else(|| oxide_builtins::error::create_from_text(vm, &e)); - let _ = vm.reject_promise(this_val, exc); - NativeResult::Ok(this_val) - } - } -} - -/// 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); - 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); - 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()) -} - /// `Promise.prototype.then(onFulfilled, onRejected)`:按 `this.constructor` 派生 /// 并注册反应,返回派生 promise(子类实例走子类构造器)。 fn promise_then(vm: &mut Vm, args: &[u8]) -> NativeResult { @@ -1768,32 +1109,6 @@ fn aggregate_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(this)) } -/// 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")); - } - 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() }; - 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()) -} - // ── session GC 支撑:状态盒中的 JsValues 作为 Promise 对象边追踪 ── fn promise_state_ref(obj: &JsObject) -> Option<&PromiseState> { @@ -2001,61 +1316,3 @@ pub(crate) fn drop_promise_native(obj: &JsObject) -> u64 { drop(state); bytes } - -/// 供外部遍历微任务队列(GC mark/rewrite 用)。 -pub(crate) fn for_each_job_value(job: &Microtask, mut f: impl FnMut(JsValue)) { - match job { - Microtask::Reaction { - handler, - argument, - resolve, - reject, - .. - } => { - f(*handler); - f(*argument); - f(*resolve); - f(*reject); - } - Microtask::Thenable { - thenable, - then, - resolve, - reject, - } => { - f(*thenable); - f(*then); - f(*resolve); - f(*reject); - } - } -} - -/// 供外部改写微任务队列中的 JsValue(session GC sweep 用)。 -pub(crate) fn rewrite_job_values(job: &mut Microtask, mut rewrite: impl FnMut(JsValue) -> JsValue) { - match job { - Microtask::Reaction { - handler, - argument, - resolve, - reject, - .. - } => { - *handler = rewrite(*handler); - *argument = rewrite(*argument); - *resolve = rewrite(*resolve); - *reject = rewrite(*reject); - } - Microtask::Thenable { - thenable, - then, - resolve, - reject, - } => { - *thenable = rewrite(*thenable); - *then = rewrite(*then); - *resolve = rewrite(*resolve); - *reject = rewrite(*reject); - } - } -} diff --git a/crates/oxide_vm/src/promise/settlement.rs b/crates/oxide_vm/src/promise/settlement.rs new file mode 100644 index 00000000..9ea30107 --- /dev/null +++ b/crates/oxide_vm/src/promise/settlement.rs @@ -0,0 +1,389 @@ +//! Promise 能力与结算:能力构造、resolve/reject 闭包构造, +//! PromiseResolve / fulfill / reject 结算路径。 +//! +//! 结算单次性:闭包首调置位 `already_resolved`,`state != Pending` +//! 最终守卫;结算后反应入微任务队列,并沿 `promoted_clone` 链传导克隆。 + +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); + 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.promise_constructor.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) = { + 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.session.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); + 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.session.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); + 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; + } + 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; + } + 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 { + 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(); + !ptr.is_null() && unsafe { &*ptr }.is_promise_obj() + } + } + + /// 校验值的原型链含 `%Promise.prototype%`(`new Promise` 构造出的实例特征)。 + pub(super) fn has_promise_proto(&self, v: JsValue) -> bool { + if !v.is_object() { + return false; + } + let target = self.promise_proto.as_ptr() as *mut JsObject; + let mut cursor = v; + for _ in 0..crate::vm::MAX_PROTO_CHAIN_DEPTH { + if !cursor.is_object() { + return false; + } + let ptr = cursor.as_js_object_ptr(); + if ptr.is_null() { + return false; + } + if std::ptr::eq(ptr, target) { + return true; + } + cursor = unsafe { &*ptr }.proto(); + } + false + } + + /// 入队一条微任务(追加到队尾,保证 FIFO)。 + pub(super) fn enqueue(&mut self, job: Microtask) { + self.job_queue.push_back(job); + } + + /// `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() { + 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); + 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 = { + 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, + }); + } + // 原件若已晋升出克隆,把结算传导到克隆:克隆上晋升后新挂的反应方随此触发 + // (顶层 var 读克隆,原件反应已在本处直接触发,不重复传导)。 + let clone_ptr = unsafe { (*state_ptr).promoted_clone }; + if !clone_ptr.is_null() { + let clone = JsValue::from_js_object(clone_ptr); + let _ = self.fulfill_promise(clone, value); + } + 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 = { + 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, + }); + } + // 原件若已晋升出克隆,把拒绝传导到克隆(同 fulfill:顶层 var 读克隆)。 + let clone_ptr = unsafe { (*state_ptr).promoted_clone }; + if !clone_ptr.is_null() { + let clone = JsValue::from_js_object(clone_ptr); + let _ = self.reject_promise(clone, reason); + } + 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) { + 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.promise_constructor.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); + 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); + 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")); + } + 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() }; + 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()) +}