Files
OxideJS-New/crates/oxide_vm/src/promise/constructor.rs
T

462 lines
24 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 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::aggregate::{
promise_static_all, promise_static_all_keyed, promise_static_all_settled, promise_static_all_settled_keyed,
promise_static_any, promise_static_race, promise_static_reject, promise_static_resolve, promise_static_try,
promise_static_with_resolvers,
};
use super::reactions::{promise_catch, promise_finally, promise_then};
use super::{is_constructor_value, 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.realm.promise_proto.borrow().as_ptr() as *mut JsObject);
let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
// SAFETY: `alloc_object` 返回非空 arena 指针;此处写 type_tag 与 native_data
// (Box<PromiseState> 由 Box::into_raw 分配,随对象释放),无别名。
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,
});
obj.set_native_data(Box::into_raw(state) as *mut u8);
JsValue::from_js_object(ptr)
}
/// 构造调用(Construct(C, args)):native 构造器值传递调用,bytecode 构造器
/// 压构造帧执行(含 derived 构造器 super() 语义),返回值非对象时回退到新对象。
///
/// # 边界与前提
/// - `ctor` 非可构造值(箭头 / 非构造 native / 普通值)返回 TypeError 的 `Err`;
/// 消费 `last_uncaught_value`,调用方不得再取槽。
pub(crate) fn construct_ctor(&mut self, ctor: JsValue, args: &[JsValue]) -> Result<JsValue, JsValue> {
self.construct_with(ctor, ctor, args)
}
/// 带 newTarget 覆写的构造调用(Construct(C, args, newTarget)):this 与
/// new.target 均按 `new_target` 推导——this = OrdinaryCreateFromConstructor(newTarget),
/// 构造帧 new.target 槽 = newTarget。Reflect.construct 面的入口;newTarget 与
/// 构造器本身同值时与 `construct_ctor` 逐位等价。
///
/// bound 包装按 BoundFunctionExoticConstruct 逐层解包:绑定实参前置(内层先)、
/// newTarget 逐层指针相等替换为包装体,最内层非构造器判定后递归进入对应臂。
///
/// # 边界与前提
/// - `ctor` 非可构造值返回 TypeError 的 `Err`;`new_target` 的可构造性由调用方
/// 入口校验。失败时消费 `last_uncaught_value`,`Err` 携带原始抛出值,
/// 调用方不得再取槽。
pub(crate) fn construct_with(
&mut self, ctor: JsValue, new_target: JsValue, args: &[JsValue],
) -> Result<JsValue, JsValue> {
// IsConstructor 校验:arrow / 非构造 native / 普通值拒绝。
if !is_constructor_value(ctor) {
return Err(oxide_builtins::error::create_type_error(self, "constructor is not a constructor"));
}
// SAFETY: `is_constructor_value` 已含对象校验,指针非空且指向存活对象;
// 此处只读 native_fn() 判定分支,不跨 GC/reset。
let ctor_obj = unsafe { &*ctor.as_js_object_ptr() };
// SAFETY: 调用方入口已对 new_target 做过同谓词 IsConstructor 校验,
// 指针非空且指向存活对象。
let nt_obj = unsafe { &*new_target.as_js_object_ptr() };
// bound 包装:解包链(绑定实参前置、newTarget 逐层指针相等替换),
// 最内层非构造器判定后递归进入既有臂。
if ctor_obj.type_tag == JsObject::OBJ_TYPE_BOUND {
let mut cur = ctor;
let mut cur_obj = ctor_obj;
let mut nt = new_target;
let mut combined = args.to_vec();
while cur_obj.type_tag == JsObject::OBJ_TYPE_BOUND {
// 纯读无 GC 点:状态对象布局 [target, thisArg, ...boundArgs]。
let state = oxide_builtins::function::bound_state_values(cur_obj);
let target = state.first().copied().unwrap_or(JsValue::undefined());
if !target.is_object() {
// 非对象的最内层 target 不可构造。
return Err(oxide_builtins::error::create_type_error(self, "object is not a constructor"));
}
for v in state.iter().skip(2) {
combined.insert(0, *v);
}
if cur == nt {
nt = target;
}
cur = target;
// SAFETY: is_object 守卫保证指针非空且指向存活对象;只读 type_tag
// 判定分支,不跨 GC/reset。
cur_obj = unsafe { &*cur.as_js_object_ptr() };
}
if !is_constructor_value(cur) {
return Err(oxide_builtins::error::create_type_error(self, "object is not a constructor"));
}
return self.construct_with(cur, nt, &combined);
}
// native 构造器:receiver 为新对象(this 取 newTarget.prototype),值传递调用。
if ctor_obj.native_fn().is_some() {
// DataView 构造器不预分配 this:其体内先做全部校验、后读 newTarget.prototype
// (抛错的 prototype getter 不得先于越界 RangeError 触发),占位 receiver
// 不可观测(构造器恒返回自建对象);原型取 %DataView.prototype% 使构造器
// 的 receiver 原型链判定通过。
let is_data_view = std::ptr::eq(
ctor.as_js_object_ptr(),
self.realm.session.borrow().builtin_world().data_view_constructor.as_ptr() as *mut JsObject,
);
let this_val = if is_data_view {
let data_view_proto_val = JsValue::from_js_object(
self.realm.session.borrow().builtin_world().data_view_proto.as_ptr() as *mut JsObject,
);
JsValue::from_js_object(self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, data_view_proto_val)))
} else {
let this_ptr = self.alloc_ctor_this(nt_obj, ctor_obj, new_target)?;
JsValue::from_js_object(this_ptr)
};
// newTarget 经 reg(255) 暴露给 native 构造器(快照/恢复:call 窗口
// 不触及 255);构造形态标记同窗夹持(调用后恢复)。
let saved_nt = self.regs[255];
let saved_constructing = self.constructing_native;
self.regs[255] = new_target;
self.constructing_native = true;
let result = self.call_function_sync(ctor, this_val, args);
self.constructing_native = saved_constructing;
self.regs[255] = saved_nt;
return match result {
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, nt_obj, new_target, args)
}
/// 分配构造 this:proto = newTarget.prototype(缺省 Object.prototype)。
///
/// 按**构造器**(非 newTarget)种类分读形:native 构造器保持裸存储读
/// (占位 receiver 的原型不可观测,可观测面由各构造体自重读覆盖,native
/// 面的 getter 触发权归构造体的规范步序位);字节码构造器走传播读
/// (Ordinary [[Construct]] 的 GpFC 先于函数体求值且读体是唯一读点,
/// 访问器 getter 的异常须原值上抛,非对象结果回落 Object.prototype)。
fn alloc_ctor_this(
&mut self, nt_obj: &JsObject, ctor_obj: &JsObject, nt_val: JsValue,
) -> Result<*mut JsObject, JsValue> {
let proto_si = self.kernel_core.perm_interner().intern("prototype").0;
let object_proto =
JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
let is_native_ctor = ctor_obj.native_fn().is_some() && ctor_obj.type_tag == JsObject::OBJ_TYPE_CONSTRUCTOR;
let proto_val = if is_native_ctor {
match self.resolve_property(nt_obj, proto_si) {
Some(p) if p.is_object() => p,
_ => object_proto,
}
} else {
let pc_before = self.pc;
match self.ordinary_get(nt_obj, proto_si, nt_val) {
Ok(v) if self.pc == pc_before => {
if v.is_object() {
v
} else {
object_proto
}
}
Ok(_) => {
// depth-0 getter 抛已就地展开跳走(构造入口恒在 native 上下文,
// 结构性不可达):停掉构造流,不续行。
return Err(JsValue::undefined());
}
Err(err) => {
// 重入路径:恢复原始异常值,按 construct_with 契约携带原值。
return Err(self
.last_uncaught_value
.take()
.unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &err)));
}
}
};
Ok(self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)))
}
/// bytecode 构造器构造调用:压构造帧内嵌 dispatch 执行(derived 构造器
/// super() 前 this = undefined,new.target = newTarget),this 与构造 this
/// 均按 newTarget 分配,结果经 `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, nt_obj: &JsObject, new_target: JsValue, args: &[JsValue],
) -> Result<JsValue, JsValue> {
// 按构造器自身记录的表代际解析:代际表缺失(已回收)或下标越界与
// 原生函数哨兵同口径报 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(nt_obj, ctor_obj, new_target)?;
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),
new_target,
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.realm.session.borrow().builtin_world().fn_proto_val();
let session = self.realm.session.borrow();
let world = session.builtin_world();
let object_proto_val =
JsValue::from_js_object(self.realm.session.borrow().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_key = oxide_types::private_key::encode_symbol_key(
self.realm_id(),
oxide_types::private_key::WELL_KNOWN_SYMBOL_TO_STRING_TAG,
);
let tag_shape = sh.make_shape(proto.shape_id(), tag_key);
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),
("allKeyed", promise_static_all_keyed as *const (), 1),
("allSettledKeyed", promise_static_all_settled_keyed as *const (), 1),
("any", promise_static_any as *const (), 1),
("withResolvers", promise_static_with_resolvers as *const (), 0),
("try", promise_static_try as *const (), 1),
);
// world 的最后一处使用在上:显式释放 session 读守卫,
// 使后续 &mut self 的初始化调用可借 self。
drop(session);
// 固定地址后互相接线:proto.constructor ↔ ctor.prototype。
Self::swap_intrinsic_proto(&mut self.realm.promise_proto.borrow_mut(), *proto);
Self::swap_intrinsic_proto(&mut self.realm.promise_constructor.borrow_mut(), *ctor);
// SAFETY: `promise_proto` 为 `P<JsObject>`(Arc 透明包装),堆址固定且本行前刚经
// swap_intrinsic_proto 落地;与下一处 ctor_mut 指向不同对象,无别名。
let proto_mut = unsafe { &mut *self.realm.promise_proto.borrow().as_mut_ptr() };
proto_mut.set_prop_at(
0u32,
JsValue::from_js_object(self.realm.promise_constructor.borrow().as_ptr() as *mut JsObject),
);
// SAFETY: `promise_constructor` 同为堆址固定的存活 `P<JsObject>`(Arc 透明包装);
// 此处写 prototype 槽位(下标 2),与 proto_mut 分属不同对象,无别名。
let ctor_mut = unsafe { &mut *self.realm.promise_constructor.borrow().as_mut_ptr() };
// prototype 槽位在 length/name 之后(下标 2)。
ctor_mut
.set_prop_at(2u32, JsValue::from_js_object(self.realm.promise_proto.borrow().as_ptr() as *mut JsObject));
// Promise[Symbol.species] 访问器:getter 返回 receiver,派生类沿静态原型链
// 解析 @@species 得自身构造器。ctor 经本函数每次调用全新构造,同键槽不累积。
crate::bindings::bind_accessor_getter_key(
&self.kernel_core,
&self.realm.session.borrow(),
ctor_mut,
oxide_types::private_key::encode_symbol_key(
self.realm_id(),
oxide_types::private_key::WELL_KNOWN_SYMBOL_SPECIES,
),
"get [Symbol.species]",
oxide_builtins::array::array_species_get::<crate::vm::Vm> as *const (),
);
// 绑定 global:槽已存在则更新(full_reset 未重建 global 时旧槽指向已弃 ctor)。
let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject;
// SAFETY: global 对象由 session 持有,存活整个 session;本函数内只改其
// shape/属性区,期间无 reset 或对象搬移。
let global = unsafe { &mut *global_ptr };
let si = self.kernel_core.perm_interner().intern("Promise").0;
let ctor_val = JsValue::from_js_object(self.realm.promise_constructor.borrow().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)。
///
/// # 步骤
/// 1. NewTarget 缺失形态(普通调用 / .call 入口)→ TypeError。
/// 2. executor 可读性检查(步序先于 GpFC 原型读)。
/// 3. GetPrototypeFromConstructor:传播式读 new.target 的 "prototype"
/// (访问器副作用与异常原值传播);非对象结果回落 %Promise.prototype%。
/// 4. 物化 receiver(原型重设 + type_tag + 状态盒)。
fn promise_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
// NewTarget 缺失 = 非构造形态:构造入口置位、普通调用入口清零,据此拒绝。
if !vm.constructing_native {
return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "Promise must be called with new"));
}
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"));
}
// GetPrototypeFromConstructor:异常原值上抛;非对象结果回落内建原型。
let promise_proto_val = JsValue::from_js_object(vm.realm.promise_proto.borrow().as_ptr() as *mut JsObject);
let new_target = vm.reg(255);
let proto = if new_target.is_object() {
let nt_ptr = new_target.as_js_object_ptr();
// SAFETY: is_object 保证指针非空且对象本 session 存活。
let nt_obj = unsafe { &*nt_ptr };
let proto_si = vm.kernel_core.perm_interner().intern("prototype").0;
match vm.ordinary_get(nt_obj, proto_si, new_target) {
Ok(v) if v.is_object() => v,
Ok(_) => promise_proto_val,
Err(err) => {
return NativeResult::Err(
vm.last_uncaught_value
.take()
.unwrap_or_else(|| oxide_builtins::error::create_from_text(vm, &err)),
);
}
}
} else {
promise_proto_val
};
// receiver 原型重设为 GpFC 结果(恒等重设是 no-op,非对象回落重设是语义修正),
// 随后物化状态盒。
// SAFETY: this 为构造帧分配的存活对象;此处写 type_tag 后即新建状态盒,无别名。
let obj = unsafe { &mut *this_val.as_js_object_ptr() };
if let Err(msg) = obj.set_proto(proto) {
return NativeResult::Err(oxide_builtins::error::create_type_error(vm, msg));
}
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,
});
obj.set_native_data(Box::into_raw(state) as *mut u8);
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)
}
}
}