diff --git a/crates/oxide_builtins/src/function.rs b/crates/oxide_builtins/src/function.rs index b7949ff8..10b797ec 100644 --- a/crates/oxide_builtins/src/function.rs +++ b/crates/oxide_builtins/src/function.rs @@ -2,7 +2,7 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes}; use oxide_types::value::JsValue; -use oxide_runtime_api::{to_integer_or_infinity, to_units_full, NativeResult, VmHost}; +use oxide_runtime_api::{to_integer_or_infinity, to_length, to_units_full, NativeResult, VmHost}; fn invoke_target(vm: &mut H, target_val: JsValue, this_val: JsValue, arg_regs: &[u8]) -> NativeResult { let args: Vec = arg_regs.iter().map(|&r| vm.reg(r)).collect(); @@ -167,8 +167,11 @@ pub fn function_call(vm: &mut H, args: &[u8]) -> NativeResult { invoke_target(vm, target_val, this_val, &arg_regs) } -/// `Function.prototype.apply(thisArg, argsArray)`:以指定 this 和参数数组调用目标函数。 -/// 数组元素直接物化为实参切片经 TailCall 下发(帧参数区),不受寄存器窗口限制。 +/// `Function.prototype.apply(thisArg, argsArray)`:以指定 this 和参数对象调用 +/// 目标函数。argsArray 为 null/undefined → 无实参;对象(数组 / Arguments / +/// 数组类对象)读 `length`(传播读,强转为长度)后逐下标传播读元素(空位取 +/// undefined);元素物化为实参切片经 TailCall 下发(帧参数区),不受寄存器 +/// 窗口限制。length/元素 getter 抛错时透传原异常值。 pub fn function_apply(vm: &mut H, args: &[u8]) -> NativeResult { if args.is_empty() { return NativeResult::Err(JsValue::undefined()); @@ -182,12 +185,19 @@ pub fn function_apply(vm: &mut H, args: &[u8]) -> NativeResult { if arr_val.is_object() { let arr_ptr = arr_val.as_js_object_ptr(); if !arr_ptr.is_null() { + // SAFETY: is_object 与非空守卫保证存活对象;length/下标读取即时消费,不跨 GC/reset。 let arr = unsafe { &*arr_ptr }; - if arr.is_array() { - let n = arr.prop_count() as usize; - call_args.reserve(n); - for i in 0..n { - call_args.push(arr.get_prop_at(i)); + let length_si = vm.kernel_core().perm_interner().intern("length").0; + let count = match vm.ordinary_get(arr, length_si, arr_val) { + Ok(v) => to_length(v).min(oxide_types::private_key::INT_KEY_COUNT as u64) as u32, + Err(e) => return NativeResult::Err(to_string_error_value(vm, &e)), + }; + call_args.reserve(count as usize); + for i in 0..count { + let key_si = oxide_types::private_key::make_int_key(i); + match vm.ordinary_get(arr, key_si, arr_val) { + Ok(v) => call_args.push(v), + Err(e) => return NativeResult::Err(to_string_error_value(vm, &e)), } } } diff --git a/crates/oxide_vm/src/promise/aggregate.rs b/crates/oxide_vm/src/promise/aggregate.rs index 43649a6d..bcd73c48 100644 --- a/crates/oxide_vm/src/promise/aggregate.rs +++ b/crates/oxide_vm/src/promise/aggregate.rs @@ -265,11 +265,18 @@ impl Vm { } } -/// `Promise.resolve(x)`:`x` 为原生 Promise 且 `x.constructor === this` 时直接返回; -/// 否则按 `this`(构造器)建能力并 PromiseResolve。 +/// `Promise.resolve(x)`:`this` 非 Object 抛 TypeError(先于同值快路径); +/// `x` 为原生 Promise 且 `x.constructor === this` 时直接返回;否则按 `this` +///(构造器)建能力并 PromiseResolve。 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 !ctor.is_object() { + return NativeResult::Err(oxide_builtins::error::create_type_error( + vm, + "Promise.resolve called on non-object this", + )); + } if vm.is_promise_value(x) { // SAFETY: is_promise_value 已验证 x 为原生 Promise 对象,指针非空且存活;此处只读 constructor,即时消费。 let x_obj = unsafe { &*x.as_js_object_ptr() }; @@ -305,10 +312,17 @@ pub(super) fn promise_static_resolve(vm: &mut Vm, args: &[u8]) -> NativeResult { } } -/// `Promise.reject(x)`:按 `this`(构造器)建能力并直接拒绝。 +/// `Promise.reject(x)`:`this` 非 Object 抛 TypeError(与 resolve 同形镜像); +/// 否则按 `this`(构造器)建能力并直接拒绝。 pub(super) fn promise_static_reject(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 !ctor.is_object() { + return NativeResult::Err(oxide_builtins::error::create_type_error( + vm, + "Promise.reject called on non-object this", + )); + } let (promise, _, reject) = match vm.new_promise_capability_with_ctor(ctor) { Ok(t) => t, Err(err) => return NativeResult::Err(err), diff --git a/crates/oxide_vm/src/promise/mod.rs b/crates/oxide_vm/src/promise/mod.rs index bed3fe5b..126801de 100644 --- a/crates/oxide_vm/src/promise/mod.rs +++ b/crates/oxide_vm/src/promise/mod.rs @@ -91,6 +91,10 @@ const DELEGATED_PROP: &str = "__oxide_delegated__"; const ON_FINALLY_PROP: &str = "__oxide_on_finally__"; /// finally 处理器上区分 reject 角色的属性名。 const FINALLY_REJECT_PROP: &str = "__oxide_finally_reject__"; +/// finally 处理器上存派生构造器的属性名。 +const FINALLY_CTOR_PROP: &str = "__oxide_finally_ctor__"; +/// finally 直通 thunk 上存透传值/拒绝原因的属性名。 +const FINALLY_THUNK_VALUE_PROP: &str = "__oxide_finally_thunk_value__"; /// 能力 executor 上捕获 resolve/reject 的属性名。 const CAP_RESOLVE_PROP: &str = "__oxide_cap_resolve__"; const CAP_REJECT_PROP: &str = "__oxide_cap_reject__"; diff --git a/crates/oxide_vm/src/promise/reactions.rs b/crates/oxide_vm/src/promise/reactions.rs index 5ad48881..93626264 100644 --- a/crates/oxide_vm/src/promise/reactions.rs +++ b/crates/oxide_vm/src/promise/reactions.rs @@ -1,19 +1,22 @@ //! Promise then 链:反应注册(PerformPromiseThen)、species 构造器 //! 派生、finally 处理与原型方法闭包(then/catch/finally)。 //! -//! `finally` 处理器为携带 onFinally 回调的 native 闭包,角色以属性标志区分; -//! `invoke_then` 经 Invoke 语义调用用户可覆盖的 `then`。 +//! `finally` 处理器为携带 onFinally 回调与派生构造器的 native 闭包,角色以 +//! 属性标志区分;处理器经 PromiseResolve 解包 onFinally 返回值后以直通 +//! thunk 调 `Invoke(p, "then")` 透传原值/重抛原原因;`invoke_then` 经 +//! Invoke 语义调用用户可覆盖的 `then`。 use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_runtime_api::NativeResult; use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes}; +use oxide_types::private_key::{make_well_known_symbol_key, WELL_KNOWN_SYMBOL_SPECIES}; use oxide_types::value::JsValue; use crate::vm::Vm; use super::{ - is_constructor_value, promise_state_ref, Microtask, PromiseReaction, PromiseStateKind, FINALLY_REJECT_PROP, - ON_FINALLY_PROP, + is_constructor_value, promise_state_ref, Microtask, PromiseReaction, PromiseStateKind, FINALLY_CTOR_PROP, + FINALLY_REJECT_PROP, FINALLY_THUNK_VALUE_PROP, ON_FINALLY_PROP, }; impl Vm { @@ -115,18 +118,18 @@ impl Vm { self.perform_promise_then_cap(this_val, on_fulfilled, on_rejected, capability) } - /// 读 `promise.constructor` 选派生构造器(SpeciesConstructor 的 constructor - /// 语义):constructor 为 undefined 时退回内置 `%Promise%`,非可构造函数抛 - /// TypeError。constructor getter 抛错时透传原异常值。 + /// SpeciesConstructor(promise, %Promise%):读 `promise.constructor` 为 C, + /// C 为 undefined 时退回内置 `%Promise%`,C 非对象(含 null)抛 TypeError; + /// 再从 C 读 `@@species` 为 S,S 为 undefined/null 时取 C,S 非构造器抛 + /// TypeError。constructor 与 `@@species` getter 抛错时透传原异常值。 /// /// # 边界与前提 /// - 调用方已做 IsPromise 校验,`promise` 必为 Promise 对象。 - /// - constructor 为 null/非对象/非构造器均抛 TypeError。 /// /// # 注意事项 - /// - 不读 `@@species`:`%Promise%` 未注册 species getter,子类经原型链读到 - /// undefined 会错误退回内置;`@@species` 改写支持待补 - /// `Promise[Symbol.species]` getter 时一并落地。 + /// - 标准链零漂移:`%Promise%` 自身 `@@species` getter 返回 receiver, + /// 子类构造器继承同一 getter,终值与 C 本身一致;仅显式覆写/新定义 + /// `@@species` 时行为改变。 fn species_constructor(&mut self, promise: JsValue) -> Result { // SAFETY: 调用方已按函数文档前提做 is_promise_value 校验,指针非空且指向存活 Promise 对象;此处只读 constructor。 let obj = unsafe { &*promise.as_js_object_ptr() }; @@ -144,10 +147,32 @@ impl Vm { if ctor.is_undefined() { return Ok(JsValue::from_js_object(self.promise_constructor.as_ptr() as *mut JsObject)); } - if !is_constructor_value(ctor) { + if !ctor.is_object() { + return Err(oxide_builtins::error::create_type_error( + self, + "The .constructor property is not an object", + )); + } + // SAFETY: is_object 保证指针非空且指向存活对象;此处只读 @@species,不跨 GC/reset。 + let c_obj = unsafe { &*ctor.as_js_object_ptr() }; + let species_si = make_well_known_symbol_key(WELL_KNOWN_SYMBOL_SPECIES); + let s = match self.ordinary_get(c_obj, species_si, ctor) { + Ok(v) => v, + Err(e) => { + let exc = self + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e)); + return Err(exc); + } + }; + if s.is_undefined() || s.is_null() { + return Ok(ctor); + } + if !is_constructor_value(s) { return Err(oxide_builtins::error::create_type_error(self, "Species constructor is not a constructor")); } - Ok(ctor) + Ok(s) } } @@ -185,17 +210,37 @@ pub(super) fn promise_catch(vm: &mut Vm, args: &[u8]) -> NativeResult { } } -/// `Promise.prototype.finally(onFinally)`:调 onFinally 后透传原值/原拒绝原因; -/// 经 Invoke 语义调用 `this.then`。 +/// `Promise.prototype.finally(onFinally)`:调 onFinally 后经 PromiseResolve +/// 解包其返回值,再透传原值/原拒绝原因;经 Invoke 语义调用 `this.then`。 +/// +/// # 步骤 +/// 1. this 非 Object → TypeError。 +/// 2. C = ? SpeciesConstructor(this, %Promise%)。 +/// 3. onFinally 不可调用 → 双处理器均取 onFinally 自身(值原样传给 then)。 +/// 4. 否则建 fulfill/reject 两个 finally 处理器闭包。 +/// 5. 返回 ? Invoke(this, "then", « thenFinally, catchFinally »)。 pub(super) fn promise_finally(vm: &mut Vm, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let on_finally = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; - let (then_finally, reject_finally) = if oxide_builtins::iterator::is_callable(on_finally) { - (vm.make_finally_handler(on_finally, false), vm.make_finally_handler(on_finally, true)) - } else { - (JsValue::undefined(), JsValue::undefined()) + if !this_val.is_object() { + return NativeResult::Err(oxide_builtins::error::create_type_error( + vm, + "Method Promise.prototype.finally called on incompatible receiver", + )); + } + let ctor = match vm.species_constructor(this_val) { + Ok(c) => c, + Err(err) => return NativeResult::Err(err), }; - match vm.invoke_then(this_val, &[then_finally, reject_finally]) { + let (then_finally, catch_finally) = if oxide_builtins::iterator::is_callable(on_finally) { + ( + vm.make_finally_handler(on_finally, ctor, false), + vm.make_finally_handler(on_finally, ctor, true), + ) + } else { + (on_finally, on_finally) + }; + match vm.invoke_then(this_val, &[then_finally, catch_finally]) { Ok(result) => NativeResult::Ok(result), Err(err) => NativeResult::Err(err), } @@ -238,26 +283,49 @@ impl Vm { } } - /// 构造 finally 处理器 native 闭包:调 onFinally 后,fulfill 角色返回原值, - /// reject 角色重抛原拒绝原因。 - fn make_finally_handler(&mut self, on_finally: JsValue, reject_role: bool) -> JsValue { + /// 构造 finally 处理器 native 闭包(CreateBuiltinFunction(closure, 1, "")): + /// 调 onFinally 后经 PromiseResolve(ctor, 返回值) 解包,再以直通 thunk + /// 调 Invoke(p, "then")——fulfill 角色透传原值,reject 角色重抛原拒绝原因。 + fn make_finally_handler(&mut self, on_finally: JsValue, ctor: JsValue, reject_role: bool) -> 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: promise_finally_handler 是 NativeFn 函数项。 func.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(promise_finally_handler as *const ()) })); - func.set_native_arg_count(0); + func.set_native_arg_count(1); 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); + let c_si = self.kernel_core.perm_interner().intern(FINALLY_CTOR_PROP).0; + self.set_or_create_prop_value(obj, c_si, ctor); let rj_si = self.kernel_core.perm_interner().intern(FINALLY_REJECT_PROP).0; self.set_or_create_prop_value(obj, rj_si, JsValue::bool(reject_role)); self.add_fn_name_length(obj, "", 1); JsValue::from_js_object(ptr) } + /// 构造 finally 直通 thunk(CreateBuiltinFunction(closure, 0, "")): + /// fulfill 角色返回携带值,reject 角色抛携带的拒绝原因。 + fn make_finally_thunk(&mut self, reject_role: bool, value: JsValue) -> 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: finally_thunk 是 NativeFn 函数项。 + func.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(finally_thunk 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 v_si = self.kernel_core.perm_interner().intern(FINALLY_THUNK_VALUE_PROP).0; + self.set_or_create_prop_value(obj, v_si, value); + let rj_si = self.kernel_core.perm_interner().intern(FINALLY_REJECT_PROP).0; + self.set_or_create_prop_value(obj, rj_si, JsValue::bool(reject_role)); + self.add_fn_name_length(obj, "", 0); + JsValue::from_js_object(ptr) + } + /// 给 native 函数对象补 `length`/`name` 数据属性(length 先于 name,符合 /// CreateBuiltinFunction 属性顺序)。 pub(crate) fn add_fn_name_length(&mut self, obj: &mut JsObject, name: &str, length: u8) { @@ -277,35 +345,68 @@ impl Vm { } } -/// finally 处理器闭包:读自身 prop 的 onFinally 与角色,调用后透传/重抛。 +/// finally 处理器闭包:读自身 prop 的 onFinally/构造器/角色,调 onFinally 后 +/// 经 PromiseResolve(ctor, 返回值) 解包,再以直通 thunk 调 Invoke(p, "then") +/// 透传原值/重抛原拒绝原因;onFinally 抛错时原值上抛。 fn promise_finally_handler(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, "finally handler is invalid")); } - // SAFETY: callee 来自 reg(254) 且 is_object 守卫保证指针非空存活;此处只读 ON_FINALLY/FINALLY_REJECT 属性。 + // SAFETY: callee 来自 reg(254) 且 is_object 守卫保证指针非空存活;此处只读 ON_FINALLY/FINALLY_CTOR/FINALLY_REJECT 属性,不跨 GC/reset。 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()); + let c_si = vm.kernel_core.perm_interner().intern(FINALLY_CTOR_PROP).0; + let ctor = vm + .resolve_property(callee_obj, c_si) + .unwrap_or_else(|| JsValue::from_js_object(vm.promise_constructor.as_ptr() as *mut JsObject)); let rj_si = vm.kernel_core.perm_interner().intern(FINALLY_REJECT_PROP).0; let is_reject = vm .resolve_property(callee_obj, rj_si) .is_some_and(oxide_runtime_api::to_boolean); let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; - match vm.call_function_sync(on_finally, JsValue::undefined(), &[]) { - Ok(_) => { - if is_reject { - NativeResult::Err(value) - } else { - NativeResult::Ok(value) - } - } + // 调 onFinally,抛错透传原异常值。 + let result = match vm.call_function_sync(on_finally, JsValue::undefined(), &[]) { + Ok(r) => r, Err(e) => { let exc = vm .last_uncaught_value .take() .unwrap_or_else(|| oxide_builtins::error::create_from_text(vm, &e)); - NativeResult::Err(exc) + return NativeResult::Err(exc); } + }; + // PromiseResolve(ctor, result):原生 promise 且 constructor 同值时快路径原样返回。 + let p = match vm.promise_resolve_ctor(ctor, result) { + Ok(p) => p, + Err(exc) => return NativeResult::Err(exc), + }; + // 直通 thunk + Invoke(p, "then", « thunk »)。 + let thunk = vm.make_finally_thunk(is_reject, value); + match vm.invoke_then(p, &[thunk]) { + Ok(v) => NativeResult::Ok(v), + Err(exc) => NativeResult::Err(exc), + } +} + +/// finally 直通 thunk:读自身 prop 的携带值,fulfill 角色返回、reject 角色抛出。 +fn finally_thunk(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, "finally thunk is invalid")); + } + // SAFETY: callee 来自 reg(254) 且 is_object 守卫保证指针非空存活;此处只读携带值/角色属性,不跨 GC/reset。 + let callee_obj = unsafe { &*callee.as_js_object_ptr() }; + let v_si = vm.kernel_core.perm_interner().intern(FINALLY_THUNK_VALUE_PROP).0; + let value = vm.resolve_property(callee_obj, v_si).unwrap_or(JsValue::undefined()); + let rj_si = vm.kernel_core.perm_interner().intern(FINALLY_REJECT_PROP).0; + let is_reject = vm + .resolve_property(callee_obj, rj_si) + .is_some_and(oxide_runtime_api::to_boolean); + if is_reject { + NativeResult::Err(value) + } else { + NativeResult::Ok(value) } } diff --git a/crates/oxide_vm/src/promise/settlement.rs b/crates/oxide_vm/src/promise/settlement.rs index e6e17e72..c5e1c1c7 100644 --- a/crates/oxide_vm/src/promise/settlement.rs +++ b/crates/oxide_vm/src/promise/settlement.rs @@ -157,6 +157,43 @@ impl Vm { 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。 /// diff --git a/crates/oxide_vm/tests/promise_tests.rs b/crates/oxide_vm/tests/promise_tests.rs index 44d71fb3..2b765259 100644 --- a/crates/oxide_vm/tests/promise_tests.rs +++ b/crates/oxide_vm/tests/promise_tests.rs @@ -279,6 +279,165 @@ fn await_subclass_promise_resumes_with_value() { assert_eq!(obj.get_prop_at(1).as_int(), 2, "ctor calls: new once + thenable delegation once"); } +#[test] +fn finally_subclass_count_resolve_side() { + // finally ES2020 语义:子类 resolved 面共 7 次构造 + //(new + finally-then + 语料 then + 尾部 then + PromiseResolve 能力 + // + thunk-then + 委托任务 then)。 + let mut vm = Vm::new(); + let (ok, val) = settled( + &mut vm, + "var count = 0; var P = class extends Promise { constructor(exec) { count++; super(exec); } }; \ + P.resolve().finally(() => {}).then(() => count).then(c => c)", + ); + assert!(ok); + assert_eq!(val.as_int(), 7, "resolved-side finally should create 7 subclass promises"); +} + +#[test] +fn finally_subclass_count_reject_side() { + // 子类 rejected 面镜像:计数同为 7,拒绝原因透传。 + let mut vm = Vm::new(); + let (ok, val) = settled( + &mut vm, + "var count = 0; var P = class extends Promise { constructor(exec) { count++; super(exec); } }; \ + P.reject('r').finally(() => {}).then(() => 'ok', e => 'caught:' + e + ':' + count).then(s => s)", + ); + assert!(ok); + assert_eq!(vm.lookup_str(val).as_deref(), Some("caught:r:7")); +} + +#[test] +fn finally_resolved_observable_sequence() { + // resolved 面 then 可观测序列 [1,2,3,4,5]:finally 返回的 promise 被 + // 下游捕获,拒绝原因经 th 链路透传。 + let mut vm = Vm::new(); + let (ok, val) = settled( + &mut vm, + "var seq = []; var yes = Promise.resolve(1); \ + yes.then = function() { seq.push(1); return Promise.prototype.then.apply(this, arguments); }; \ + var no = Promise.reject(2); \ + no.then = function() { seq.push(4); return Promise.prototype.then.apply(this, arguments); }; \ + yes.then(x => { seq.push(2); return x; }).finally(() => { seq.push(3); return no; }) \ + .catch(e => { seq.push(5); return e; }) \ + .then(e => seq.join(','))", + ); + assert!(ok); + assert_eq!(vm.lookup_str(val).as_deref(), Some("1,2,3,4,5")); +} + +#[test] +fn finally_rejected_observable_sequence() { + // rejected 面镜像序列 [1,2,3,4,5]:重抛经 reject 角色 thunk 原值透传。 + let mut vm = Vm::new(); + let (ok, val) = settled( + &mut vm, + "var seq = []; var no = Promise.reject('r'); \ + no.then = function() { seq.push(1); return Promise.prototype.then.apply(this, arguments); }; \ + var yes = Promise.resolve(1); \ + yes.then = function() { seq.push(4); return Promise.prototype.then.apply(this, arguments); }; \ + no.catch(e => { seq.push(2); throw e; }).finally(() => { seq.push(3); return yes; }) \ + .catch(e => { seq.push(5); return e; }) \ + .then(v => seq.join(','))", + ); + assert!(ok); + assert_eq!(vm.lookup_str(val).as_deref(), Some("1,2,3,4,5")); +} + +#[test] +fn finally_non_callable_on_finally_passed_through() { + // onFinally 不可调用时,双处理器取 onFinally 自身原值传给 then。 + let mut vm = Vm::new(); + let (ok, val) = settled( + &mut vm, + "var seen; var p = Promise.resolve(1); \ + p.then = function(f, r) { seen = [f === 1, r === 1]; return Promise.prototype.then.call(this, f, r); }; \ + p.finally(1).then(v => seen.join(','))", + ); + assert!(ok); + assert_eq!(vm.lookup_str(val).as_deref(), Some("true,true")); +} + +#[test] +fn then_species_override_used_for_derivation() { + // constructor 上的 @@species 覆写优先于 constructor 本身:派生走 + // SpeciesConstructor 构造器且恰调用一次。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var callCount = 0; \ + var SpeciesCtor = class extends Promise { constructor(a) { super(a); callCount++; } }; \ + var p1 = new Promise(function() {}); \ + p1.constructor = function() {}; \ + p1.constructor[Symbol.species] = SpeciesCtor; \ + var d = p1.then(v => v); \ + [d.constructor === SpeciesCtor, callCount].join(',')", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).as_deref(), Some("true,1")); +} + +#[test] +fn then_species_ctor_throw_preserves_original_value() { + // %Promise%[Symbol.species] 覆写为抛错构造器:then 透传原异常值。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var boom = new Error('boom'); \ + var original = Object.getOwnPropertyDescriptor(Promise, Symbol.species); \ + Object.defineProperty(Promise, Symbol.species, { value: function() { throw boom; } }); \ + var thrown; \ + try { new Promise(r => r(1)).then(); } catch (e) { thrown = e; } \ + Object.defineProperty(Promise, Symbol.species, original); \ + thrown === boom", + ) + .unwrap(); + assert!(result.as_bool(), "species getter throw should propagate the original value"); +} + +#[test] +fn finally_species_returning_promise_keeps_intrinsic() { + // 子类 @@species getter 返回 %Promise%:finally 派生留在内建,不进子类。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var FooPromise = class extends Promise { static get [Symbol.species]() { return Promise; } }; \ + var p = Promise.resolve().finally(() => FooPromise.resolve()); \ + [p instanceof Promise, p instanceof FooPromise].join(',')", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).as_deref(), Some("true,false")); +} + +#[test] +fn static_resolve_non_object_this_throws() { + // this 非 Object 在 Type 守卫步抛 TypeError,先于同值快路径短路。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var promise = new Promise(function() {}); promise.constructor = undefined; \ + var name = 'no-throw'; \ + try { Promise.resolve.call(undefined, promise); } catch (e) { name = e.name; } \ + name", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).as_deref(), Some("TypeError")); +} + +#[test] +fn static_reject_non_object_this_throws() { + // reject 镜像守卫:this 非 Object 抛 TypeError。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var name = 'no-throw'; \ + try { Promise.reject.call(null, 1); } catch (e) { name = e.name; } \ + name", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).as_deref(), Some("TypeError")); +} + #[test] fn async_gen_yield_subclass_promise_skips_species() { // 内部 await 能力不走 species:async generator yield 子类 promise 时,