fix: Promise.prototype.then 按构造器建派生能力支持子类

This commit is contained in:
2026-08-15 22:16:05 +08:00
parent 26db7ae07c
commit 3a40024e99
5 changed files with 326 additions and 43 deletions
+169 -39
View File
@@ -15,7 +15,7 @@ use oxide_types::private_key::make_int_key;
use oxide_types::value::JsValue;
use crate::native::NativeFn;
use crate::vm::Vm;
use crate::vm::{FrameArgs, FrameContinuation, Vm};
use crate::vm_warn;
/// Promise 的 settled 状态。
@@ -188,28 +188,86 @@ impl Vm {
JsValue::from_js_object(ptr)
}
/// 构造调用:分配 proto = ctor.prototype(缺省 Object.prototype)的 this 后以
/// 普通调用执行 ctor,返回值非对象时回退到 this(与 Reflect.construct 同路径)。
/// 构造调用(Construct(C, args)):native 构造器值传递调用,bytecode 构造器
/// 压构造帧执行(含 derived 构造器 super() 语义),返回值非对象时回退到新对象。
fn construct_ctor(&mut self, ctor: JsValue, args: &[JsValue]) -> Result<JsValue, JsValue> {
if !oxide_builtins::iterator::is_callable(ctor) {
return Err(oxide_builtins::error::create_type_error(self, "constructor is not callable"));
}
let ctor_obj = unsafe { &*ctor.as_js_object_ptr() };
if ctor_obj.is_arrow()
|| (ctor_obj.native_fn().is_some() && ctor_obj.type_tag != JsObject::OBJ_TYPE_CONSTRUCTOR)
{
// 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),
};
let this_ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
let this_val = JsValue::from_js_object(this_ptr);
match self.call_function_sync(ctor, this_val, args) {
Ok(ret) if ret.is_object() => Ok(ret),
Ok(_) => Ok(this_val),
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<JsValue, JsValue> {
let sub_idx = ctor_obj.sub_module_index() as usize;
if sub_idx == 0 || sub_idx >= self.sub_modules.len() {
return Err(oxide_builtins::error::create_type_error(self, "constructor is not a constructor"));
}
// 生成器/异步函数不是构造器。
if self.sub_modules[sub_idx].is_generator || self.sub_modules[sub_idx].is_async {
return Err(oxide_builtins::error::create_type_error(self, "constructor is not a constructor"));
}
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
};
let window = self.active_reg_limit.max(self.sub_modules[sub_idx].n_registers).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()
@@ -261,7 +319,9 @@ impl Vm {
}
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)
self.resolve_property(obj, si)
.map(|v| v.is_bool() && v.as_bool())
.unwrap_or(false)
}
/// 取出 Promise 状态盒指针(调用方须先校验 `is_promise_value`)。
@@ -436,9 +496,23 @@ impl Vm {
Ok(promise)
}
/// `PerformPromiseThen` 核心:注册 fulfill/reject 两条反应;已 settle 则直接入队。
/// `PerformPromiseThen` 入口(内置能力):注册 fulfill/reject 反应并返回派生
/// promise。能力恒为 `%Promise%`——供引擎内部 await 机制(async /
/// async generator / AsyncFromSync)使用,规范上这些路径由调用方提供能力,
/// 不读 `this.constructor`,避免 await 子类 promise 时意外经子类构造器派生。
pub(crate) fn perform_promise_then(
&mut self, this_val: JsValue, on_fulfilled: JsValue, on_rejected: JsValue,
) -> Result<JsValue, JsValue> {
let capability = self.new_promise_capability();
self.perform_promise_then_cap(this_val, on_fulfilled, on_rejected, capability)
}
/// `PerformPromiseThen` 核心:以调用方给定能力注册 fulfill/reject 两条反应;
/// 已 settle 则直接入队。能力三元组由调用方提供——`promise_then_species`
/// 传 species 派生能力,内部 await 路径经薄包装传内置能力。
fn perform_promise_then_cap(
&mut self, this_val: JsValue, on_fulfilled: JsValue, on_rejected: JsValue,
capability: (JsValue, JsValue, JsValue),
) -> Result<JsValue, JsValue> {
if !self.is_promise_value(this_val) {
return Err(oxide_builtins::error::create_type_error(
@@ -456,7 +530,7 @@ impl Vm {
} else {
JsValue::undefined()
};
let (new_promise, resolve, reject) = self.new_promise_capability();
let (new_promise, resolve, reject) = capability;
let state_ptr = self.promise_state_ptr(this_val);
let (kind, result) = {
let state = unsafe { &mut *state_ptr };
@@ -502,6 +576,56 @@ impl Vm {
Ok(new_promise)
}
/// `Promise.prototype.then` 的派生入口:IsPromise 校验后按 `this.constructor`
/// 选派生构造器(SpeciesConstructor 的 constructor 语义),建能力并注册反应。
fn promise_then_species(
&mut self, this_val: JsValue, on_fulfilled: JsValue, on_rejected: JsValue,
) -> Result<JsValue, JsValue> {
if !self.is_promise_value(this_val) {
return Err(oxide_builtins::error::create_type_error(
self,
"Method Promise.prototype.then called on incompatible receiver",
));
}
let ctor = self.species_constructor(this_val)?;
let capability = self.new_promise_capability_with_ctor(ctor)?;
self.perform_promise_then_cap(this_val, on_fulfilled, on_rejected, capability)
}
/// 读 `promise.constructor` 选派生构造器(SpeciesConstructor 的 constructor
/// 语义):constructor 为 undefined 时退回内置 `%Promise%`,非可构造函数抛
/// TypeError。constructor getter 抛错时透传原异常值。
///
/// # 边界与前提
/// - 调用方已做 IsPromise 校验,`promise` 必为 Promise 对象。
/// - constructor 为 null/非对象/非构造器均抛 TypeError。
///
/// # 注意事项
/// - 不读 `@@species`:`%Promise%` 未注册 species getter,子类经原型链读到
/// undefined 会错误退回内置;`@@species` 改写支持待补
/// `Promise[Symbol.species]` getter 时一并落地。
fn species_constructor(&mut self, promise: JsValue) -> Result<JsValue, JsValue> {
let obj = unsafe { &*promise.as_js_object_ptr() };
let ctor_si = self.kernel_core.perm_interner().intern("constructor").0;
let ctor = match self.ordinary_get(obj, ctor_si, promise) {
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 ctor.is_undefined() {
return Ok(JsValue::from_js_object(self.promise_constructor.as_ptr() as *mut JsObject));
}
if !is_constructor_value(ctor) {
return Err(oxide_builtins::error::create_type_error(self, "Species constructor is not a constructor"));
}
Ok(ctor)
}
/// drain 微任务队列:FIFO 逐条处理直到清空或达到上限。
///
/// # 副作用
@@ -672,6 +796,18 @@ impl Vm {
}
}
/// IsConstructor 近似判定:可调用、非 arrow,且 native 函数须带构造器 tag
/// (与 `construct_ctor` 的校验一致)。
fn is_constructor_value(c: JsValue) -> bool {
if !c.is_object() {
return false;
}
let c_obj = unsafe { &*c.as_js_object_ptr() };
c_obj.is_function()
&& !c_obj.is_arrow()
&& !(c_obj.native_fn().is_some() && c_obj.type_tag != JsObject::OBJ_TYPE_CONSTRUCTOR)
}
/// 读取 Promise 的 settled 值(CLI 格式化用):`Some((is_fulfilled, value))`,
/// Pending 返回 `None`。
pub fn promise_settled_value(obj: &JsObject) -> Option<(bool, JsValue)> {
@@ -768,12 +904,13 @@ fn promise_reject_closure(vm: &mut Vm, args: &[u8]) -> NativeResult {
NativeResult::Ok(JsValue::undefined())
}
/// `Promise.prototype.then(onFulfilled, onRejected)`:注册反应并返回派生 promise。
/// `Promise.prototype.then(onFulfilled, onRejected)`:按 `this.constructor` 派生
/// 并注册反应,返回派生 promise(子类实例走子类构造器)。
fn promise_then(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let on_fulfilled = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let on_rejected = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
match vm.perform_promise_then(this_val, on_fulfilled, on_rejected) {
match vm.promise_then_species(this_val, on_fulfilled, on_rejected) {
Ok(promise) => NativeResult::Ok(promise),
Err(err) => NativeResult::Err(err),
}
@@ -986,7 +1123,8 @@ impl Vm {
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 error_proto_val = JsValue::from_js_object(self.session.builtin_world().error_proto.as_ptr() as *mut JsObject);
let error_proto_val =
JsValue::from_js_object(self.session.builtin_world().error_proto.as_ptr() as *mut JsObject);
// %AggregateError.prototype%:proto = %Error.prototype%,constructor/name/message 数据属性。
let mut proto = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_proto_val));
@@ -1033,10 +1171,8 @@ impl Vm {
self.aggregate_error_proto = P::new(*proto);
self.aggregate_error_constructor = P::new(*ctor);
let proto_mut = unsafe { &mut *self.aggregate_error_proto.as_mut_ptr() };
proto_mut.set_prop_at(
0u32,
JsValue::from_js_object(self.aggregate_error_constructor.as_ptr() as *mut JsObject),
);
proto_mut
.set_prop_at(0u32, JsValue::from_js_object(self.aggregate_error_constructor.as_ptr() as *mut JsObject));
let ctor_mut = unsafe { &mut *self.aggregate_error_constructor.as_mut_ptr() };
ctor_mut.set_prop_at(2u32, JsValue::from_js_object(self.aggregate_error_proto.as_ptr() as *mut JsObject));
@@ -1322,7 +1458,8 @@ fn perform_promise_combine(
Ok(()) => {
// 迭代完成:哨兵 1 递减;为 0 时结算(race 无计数语义)。
let remaining = agg_read_remaining(vm, record) - 1;
agg_write_remaining(vm, record, remaining); match kind {
agg_write_remaining(vm, record, remaining);
match kind {
AggregateKind::All | AggregateKind::AllSettled => {
if remaining == 0 {
let values = agg_record_val(vm, record, AGG_VALUES_PROP);
@@ -1366,7 +1503,10 @@ fn agg_element_state(vm: &mut Vm) -> Option<(JsValue, i32)> {
let al_si = vm.kernel_core.perm_interner().intern(AGG_ALREADY_PROP).0;
// SAFETY: callee 是当前调用的存活函数对象。
let callee_ref = unsafe { &*callee_ptr };
if vm.resolve_property(callee_ref, al_si).is_some_and(oxide_runtime_api::to_boolean) {
if vm
.resolve_property(callee_ref, al_si)
.is_some_and(oxide_runtime_api::to_boolean)
{
return None;
}
let record = vm.resolve_property(callee_ref, rec_si).unwrap_or(JsValue::undefined());
@@ -1556,24 +1696,14 @@ fn aggregate_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let msg_si = vm.kernel_core.perm_interner().intern("message").0;
let msg_val = vm.new_string(&msg_str);
// SAFETY: this 是本次构造的存活对象。
let _ = vm.define_data_property(
unsafe { &mut *this },
msg_si,
msg_val,
PropAttributes::new(true, false, true),
);
let _ = vm.define_data_property(unsafe { &mut *this }, msg_si, msg_val, PropAttributes::new(true, false, true));
}
let errors_list = match vm.aggregate_errors_to_list(errors) {
Ok(list) => list,
Err(err) => return NativeResult::Err(err),
};
let err_si = vm.kernel_core.perm_interner().intern("errors").0;
let _ = vm.define_data_property(
unsafe { &mut *this },
err_si,
errors_list,
PropAttributes::new(true, false, true),
);
let _ = vm.define_data_property(unsafe { &mut *this }, err_si, errors_list, PropAttributes::new(true, false, true));
NativeResult::Ok(JsValue::from_js_object(this))
}
+4
View File
@@ -436,6 +436,10 @@ pub struct Vm {
/// 当前是否处于异步函数内嵌 dispatch 循环:异步帧弹出且 frames 清空时,
/// `do_return` 据此把结果交付给恢复方(与 generator_dispatch 同语义)。
pub(crate) async_dispatch: bool,
/// 当前是否处于构造器内嵌 dispatch 循环(`call_constructor_bytecode_inline`):
/// 构造帧弹出且 frames 清空时,`do_return` 据此把构造结果(regs[0],
/// 已做非对象回退 this)交付给恢复方(与 generator_dispatch 同语义)。
pub(crate) construct_dispatch: bool,
/// 当前正在执行的异步生成器上下文对象(`OBJ_TYPE_ASYNC_GENERATOR`,持有
/// `AsyncGeneratorState` 快照)。AWAIT dispatch 据此登记异步生成器恢复反应;
/// 跨嵌套 async 调用保存/恢复(与 `async_context` 同生命周期,二者互斥占用)。
+5 -3
View File
@@ -447,9 +447,11 @@ impl Vm {
}
}
}
// 生成器/异步内嵌 dispatch:帧全部弹出后把结果交付恢复方,而非继续执行。
if self.frames.is_empty() && (self.generator_dispatch || self.async_dispatch) {
return Ok(Some(result));
// 生成器/异步/构造内嵌 dispatch:帧全部弹出后把结果交付恢复方,而非继续执行。
if self.frames.is_empty() && (self.generator_dispatch || self.async_dispatch || self.construct_dispatch) {
// 构造内嵌:弹帧已把构造结果(非对象回退 this)写入 regs[0],直接交付。
let delivered = if self.construct_dispatch { self.regs[0] } else { result };
return Ok(Some(delivered));
}
Ok(None)
} else {
+2
View File
@@ -84,6 +84,7 @@ impl Vm {
async_context: None,
async_suspended: false,
async_dispatch: false,
construct_dispatch: false,
async_gen_context: None,
async_gen_dispatch: false,
async_gen_suspended: false,
@@ -188,6 +189,7 @@ impl Vm {
async_context: None,
async_suspended: false,
async_dispatch: false,
construct_dispatch: false,
async_gen_context: None,
async_gen_dispatch: false,
async_gen_suspended: false,
+146 -1
View File
@@ -123,7 +123,10 @@ fn double_resolve_second_noop() {
#[test]
fn reject_then_resolve_keeps_rejected() {
let mut vm = Vm::new();
let (ok, val) = settled(&mut vm, "new Promise((res, rej) => { rej('e'); res(1); }).then(v => v, e => 'rejected:' + e)");
let (ok, val) = settled(
&mut vm,
"new Promise((res, rej) => { rej('e'); res(1); }).then(v => v, e => 'rejected:' + e)",
);
assert!(ok);
assert_eq!(vm.lookup_str(val).as_deref(), Some("rejected:e"));
}
@@ -151,3 +154,145 @@ fn thenable_chain_after_already_resolved_noop() {
assert!(ok);
assert_eq!(val.as_int(), 5);
}
#[test]
fn subclass_then_derives_subclass_instance() {
// 子类 then 派生:class P extends Promise {},p.then 返回 P 实例(经子类构造器)。
// 单次 eval 完成断言:跨 eval 时旧 module 的 bytecode 函数对象随 sub_modules
// 替换失效(引擎既有局限),故不拆分执行。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var P = class extends Promise {}; var d = new P(r => r(1)).then(v => v); d.constructor === P",
)
.unwrap();
assert_eq!(result.as_bool(), true, "subclass then should derive via subclass ctor");
}
#[test]
fn subclass_catch_finally_derive_subclass() {
// catch/finally 经 this.then 调用,自动获得子类派生语义。
// 每次 eval 独立定义 P:跨 eval 复用旧 module 函数对象会随 sub_modules 替换失效。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var P = class extends Promise {}; var d = new P(r => r(1)).catch(()=>{}); d.constructor === P",
)
.unwrap();
assert_eq!(result.as_bool(), true);
let result = eval(
&mut vm,
"var Q = class extends Promise {}; var f = new Q(r => r(1)).finally(()=>{}); f.constructor === Q",
)
.unwrap();
assert_eq!(result.as_bool(), true);
}
#[test]
fn subclass_chain_keeps_subclass_ctor() {
// 链式 then 每次派生均保持子类构造器。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var P = class extends Promise {}; var d = new P(r => r(1)).then(v => v + 1).then(v => v); d.constructor === P",
)
.unwrap();
assert_eq!(result.as_bool(), true);
}
#[test]
fn rewritten_prototype_constructor_used_for_derivation() {
// P.prototype.constructor 改写后,then 用改写值派生。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var P = class extends Promise {}; function Alt(exec) { exec(function(){}, function(){}); } \
P.prototype.constructor = Alt; var d = new P(r => r(1)).then(v => v); d.constructor === Alt",
)
.unwrap();
assert_eq!(result.as_bool(), true);
}
#[test]
fn constructor_undefined_uses_intrinsic() {
// constructor 为 undefined → 退回内置 %Promise% 派生。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var q = new Promise(r => r(1)); Object.defineProperty(q, 'constructor', { value: undefined }); \
var d = q.then(v => v); d.constructor === Promise",
)
.unwrap();
assert_eq!(result.as_bool(), true);
}
#[test]
fn constructor_non_object_throws_type_error() {
// constructor 非对象 → then 抛 TypeError。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var r = new Promise(res => res(1)); Object.defineProperty(r, 'constructor', { value: 42 }); \
try { r.then(()=>{}); 'no-throw' } catch (e) { e.name }",
)
.unwrap();
assert_eq!(vm.lookup_str(result).as_deref(), Some("TypeError"));
}
#[test]
fn constructor_getter_throw_preserves_original_value() {
// constructor getter 抛错 → 透传原异常对象(catch 收到同一引用)。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var boom = new Error('boom'); var r = new Promise(res => res(1)); \
Object.defineProperty(r, 'constructor', { get() { throw boom; } }); \
try { r.then(()=>{}); 'no-throw' } catch (e) { e === boom }",
)
.unwrap();
assert_eq!(result.as_bool(), true);
}
#[test]
fn intrinsic_promise_then_regression() {
// 回归:内置 promise 的 then 派生路径不变。
let mut vm = Vm::new();
let result = eval(&mut vm, "var d = new Promise(r => r(1)).then(v => v); d.constructor === Promise").unwrap();
assert_eq!(result.as_bool(), true);
}
#[test]
fn await_subclass_promise_resumes_with_value() {
// async function 内 await 子类 promise:值正确,额外派生仅来自 thenable
// 委托(await 机制自身的能力恒为内置,不额外调用子类构造器)。
let mut vm = Vm::new();
let (ok, val) = settled(
&mut vm,
"var ctorCalls = 0; var P = class extends Promise { constructor(exec) { ctorCalls++; super(exec); } }; \
async function f() { return await new P(r => r(1)); } \
f().then(v => [v, ctorCalls])",
);
assert!(ok);
let obj = unsafe { &*val.as_js_object_ptr() };
assert_eq!(obj.prop_count(), 2);
assert_eq!(obj.get_prop_at(0).as_int(), 1, "await value should pass through");
assert_eq!(obj.get_prop_at(1).as_int(), 2, "ctor calls: new once + thenable delegation once");
}
#[test]
fn async_gen_yield_subclass_promise_skips_species() {
// 内部 await 能力不走 species:async generator yield 子类 promise 时,
// yield 包装(await 展开)不得再经子类构造器派生。
let mut vm = Vm::new();
let (ok, val) = settled(
&mut vm,
"var ctorCalls = 0; var P = class extends Promise { constructor(exec) { ctorCalls++; super(exec); } }; \
async function* g() { yield new P(r => r(1)); } \
var it = g(); it.next().then(r => [r.value, ctorCalls])",
);
assert!(ok);
let obj = unsafe { &*val.as_js_object_ptr() };
assert_eq!(obj.prop_count(), 2);
assert_eq!(obj.get_prop_at(0).as_int(), 1, "yield value should pass through");
assert_eq!(obj.get_prop_at(1).as_int(), 1, "internal await must not derive via subclass ctor");
}