mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 19:21:11 +08:00
fix: 补 Promise species 派生与 finally 语义,静态 resolve 守卫
This commit is contained in:
@@ -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),
|
||||
|
||||
@@ -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__";
|
||||
|
||||
@@ -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<JsValue, JsValue> {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<JsValue, JsValue> {
|
||||
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。
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user