diff --git a/Cargo.lock b/Cargo.lock index 45da668a..dd39fca3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1302,6 +1302,7 @@ dependencies = [ "oxide_kernel", "oxide_log", "oxide_parser", + "oxide_types", "oxide_vm", "serde", "serde_yaml", diff --git a/crates/oxide_builtins/src/iterator.rs b/crates/oxide_builtins/src/iterator.rs index c5fcf969..6bcbe0a1 100644 --- a/crates/oxide_builtins/src/iterator.rs +++ b/crates/oxide_builtins/src/iterator.rs @@ -437,7 +437,7 @@ pub(crate) fn close_iterator(vm: &mut H, iterator: JsValue) { /// # 返回值 /// - `Ok(())`:迭代完成; /// - `Err`:迭代或回调抛出的原异常值(任意类型)。 -pub(crate) fn iterate_elements(vm: &mut H, iterable: JsValue, mut on_elem: F) -> Result<(), JsValue> +pub fn iterate_elements(vm: &mut H, iterable: JsValue, mut on_elem: F) -> Result<(), JsValue> where F: FnMut(&mut H, JsValue) -> Result<(), JsValue>, { @@ -451,7 +451,7 @@ where /// /// # 副作用 /// 迭代或回调抛错时调用迭代器的 `return()`(IteratorClose)后透传原异常。 -pub(crate) fn iterate_iterator(vm: &mut H, iterator: JsValue, mut on_elem: F) -> Result<(), JsValue> +pub fn iterate_iterator(vm: &mut H, iterator: JsValue, mut on_elem: F) -> Result<(), JsValue> where F: FnMut(&mut H, JsValue) -> Result<(), JsValue>, { diff --git a/crates/oxide_emit/src/emit.rs b/crates/oxide_emit/src/emit.rs index 1c5d184e..68e844c7 100644 --- a/crates/oxide_emit/src/emit.rs +++ b/crates/oxide_emit/src/emit.rs @@ -86,6 +86,7 @@ const BUILTIN_GLOBALS: &[&str] = &[ "Math", "JSON", "Promise", + "AggregateError", "Date", "Set", "Map", diff --git a/crates/oxide_test262/Cargo.toml b/crates/oxide_test262/Cargo.toml index 75592195..b4d90ce3 100644 --- a/crates/oxide_test262/Cargo.toml +++ b/crates/oxide_test262/Cargo.toml @@ -16,6 +16,7 @@ oxide_compiler = { path = "../oxide_compiler" } oxide_vm = { path = "../oxide_vm" } oxide_parser = { path = "../oxide_parser" } oxide_kernel = { path = "../oxide_kernel" } +oxide_types = { path = "../oxide_types" } oxide_log = { path = "../oxide_log" } serde = { version = "1", features = ["derive"] } serde_yaml = "0.9" diff --git a/crates/oxide_test262/src/main.rs b/crates/oxide_test262/src/main.rs index 54a90e4c..38280aec 100644 --- a/crates/oxide_test262/src/main.rs +++ b/crates/oxide_test262/src/main.rs @@ -2,6 +2,7 @@ use oxide_compiler::compiler::Compiler; use oxide_kernel::kernel::{KernelConfig, KernelCore}; +use oxide_types::value::JsValue; use oxide_vm::vm::Vm; use serde::Deserialize; use std::collections::HashMap; @@ -213,6 +214,9 @@ impl HarnessSources { sources.insert("typeCoercion.js", include_str!("../../../tests/test262/harness/typeCoercion.js")); sources.insert("deepEqual.js", include_str!("../../../tests/test262/harness/deepEqual.js")); sources.insert("testTypedArray.js", include_str!("../../../tests/test262/harness/testTypedArray.js")); + sources.insert("asyncHelpers.js", include_str!("../../../tests/test262/harness/asyncHelpers.js")); + sources.insert("doneprintHandle.js", include_str!("../../../tests/test262/harness/doneprintHandle.js")); + sources.insert("promiseHelper.js", include_str!("../../../tests/test262/harness/promiseHelper.js")); Self { sources } } @@ -234,14 +238,11 @@ fn is_blacklisted_harness(name: &str) -> bool { | "proxyTrapsHelper.js" | "temporalHelpers.js" | "tcoHelper.js" - | "asyncHelpers.js" - | "promiseHelper.js" | "detachArrayBuffer.js" | "resizableArrayBufferUtils.js" | "byteConversionValues.js" | "compareIterator.js" | "iteratorZipUtils.js" - | "doneprintHandle.js" ) } @@ -388,7 +389,6 @@ fn is_skipped(meta: &TestMeta) -> Option { for flag in &meta.flags { match flag.as_str() { "module" => return Some("module tests excluded".into()), - "async" => return Some("async tests excluded".into()), "raw" => return Some("raw tests excluded".into()), // noStrict 测试放行——很多在严格模式下仍可通过;运行时跳过逻辑会捕获失败。 _ => {} @@ -407,6 +407,8 @@ fn is_skipped(meta: &TestMeta) -> Option { "Atomics", "SharedArrayBuffer", "cross-realm", + // await-dictionary(Promise.allKeyed/allSettledKeyed)是 2025 proposal,未实现。 + "await-dictionary", ]; for feat in &meta.features { @@ -448,9 +450,25 @@ fn run_test_inner( ) -> TestResult { let start = std::time::Instant::now(); + let is_async = meta.flags.iter().any(|f| f == "async"); + let code = match get_harness_prefix(meta, harness, harness_cache) { Ok(prefix) => { let mut code = prefix; + if is_async { + // 异步测试:注入 print 捕获 + $DONE(asyncTests 用 $DONE 报告结果)。 + // 结果写入 globalThis 全局字符串,run() 结束后由 runner 读取并按 marker 判定。 + // (顶层 var 不落 global 对象,须显式写 globalThis。) + append_source_chunk( + &mut code, + "async capture", + "globalThis.$__test262_async_result = \"\";\n\ + function print(msg) { globalThis.$__test262_async_result += String(msg) + \"\\n\"; }", + ); + if let Some(done_src) = harness.get("doneprintHandle.js") { + append_source_chunk(&mut code, "doneprintHandle.js", done_src); + } + } append_source_chunk(&mut code, "test source", strip_meta(source)); code } @@ -505,10 +523,15 @@ fn run_test_inner( }; let mut vm = Vm::with_kernel_core(Arc::clone(kernel)); + let run_result = vm.run(&module); + let dur = start.elapsed().as_millis() as u64; - match vm.run(&module) { + if is_async { + return judge_async_result(path, run_result, &mut vm, meta, dur, no_skip); + } + + match run_result { Ok(result) => { - let dur = start.elapsed().as_millis() as u64; if let Some(neg) = meta.negative.as_ref() { return TestResult::fail( path.to_path_buf(), @@ -518,58 +541,120 @@ fn run_test_inner( } TestResult::pass(path.to_path_buf(), dur, format!("ok: {result}")) } - Err(e) => { - let dur = start.elapsed().as_millis() as u64; - if let Some(neg) = meta.negative.as_ref() { - if e.contains("TypeError") && neg.error_type == "TypeError" { - return TestResult::pass(path.to_path_buf(), dur, format!("expected: {e}")); - } - if e.contains("ReferenceError") && neg.error_type == "ReferenceError" { - return TestResult::pass(path.to_path_buf(), dur, format!("expected: {e}")); - } - if e.contains("SyntaxError") && neg.error_type == "SyntaxError" { - return TestResult::pass(path.to_path_buf(), dur, format!("expected: {e}")); - } - if e.contains(&neg.error_type) { - return TestResult::pass(path.to_path_buf(), dur, format!("expected: {e}")); - } - TestResult::fail(path.to_path_buf(), dur, format!("expected {} error, got: {e}", neg.error_type)) - } else if e.contains("not yet implemented") - || e.contains("not yet supported") - || e.contains("not supported") - || e.contains("unsupported") - || e.contains("step limit") - || e.contains("is not defined") - || e.contains("NEW_EXPRESSION") - || e.contains("IC_GET_PROP on non-object") - || e.contains("GET_PROP_DYNAMIC on non-object") - || e.contains("SET_PROP_DYNAMIC on non-object") - || e.contains("private field brand check") - || e.contains("CALL_NATIVE target") - || e.contains("call stack size exceeded") - || e.contains("is not implemented") - || e.contains("unexpected tail call") - || e.contains("not callable") - || e.contains("Cannot convert object to primitive") - || e.contains("Cannot create property on non-object") - || e.contains("Property description must be an object") - || e.contains("method called on incompatible") - || e.contains("called on non-Set") - || e.contains("called on non-Map") - || e.contains("called on non-ArrayBuffer") - || e.contains("called on non-TypedArray") - || e.contains("Array.prototype method called on null") - || e.contains("__proto__ must be an object") - // 私有字段未实现。 - { - if no_skip { - return TestResult::fail(path.to_path_buf(), dur, format!("vm error: {e}")); - } - TestResult::skip(path.to_path_buf(), format!("vm: {e}")) - } else { - TestResult::fail(path.to_path_buf(), dur, format!("vm error: {e}")) + Err(e) => judge_vm_error(path, &e, meta, dur, no_skip), + } +} + +/// 异步测试判定:`$DONE` 把结果写入捕获串,run() 结束时微任务队列已 drain。 +/// +/// 失败 marker 优先(`Test262:AsyncTestFailure:: `);其次成功 marker +/// (`Test262:AsyncTestComplete`);两者都无则测试未在 run() 内同步完成 +/// (需 setTimeout 等异步 runner 的测试在此失败),回退到 run 结果判定。 +fn judge_async_result( + path: &Path, run_result: Result, vm: &mut Vm, meta: &TestMeta, dur: u64, no_skip: bool, +) -> TestResult { + let output = read_async_output(vm); + if let Some(pos) = output.find("Test262:AsyncTestFailure:") { + let detail = output[pos..].trim(); + if let Some(neg) = meta.negative.as_ref() { + let name = detail + .strip_prefix("Test262:AsyncTestFailure:") + .and_then(|rest| rest.split(':').next()) + .unwrap_or(""); + if name == neg.error_type { + return TestResult::pass(path.to_path_buf(), dur, format!("expected: {detail}")); } + return TestResult::fail( + path.to_path_buf(), + dur, + format!("expected {} error, got: {detail}", neg.error_type), + ); } + return TestResult::fail(path.to_path_buf(), dur, detail); + } + + if output.contains("Test262:AsyncTestComplete") { + if meta.negative.is_some() { + return TestResult::fail( + path.to_path_buf(), + dur, + format!("expected runtime error ({}), got: async complete", meta.negative.as_ref().unwrap().error_type), + ); + } + return TestResult::pass(path.to_path_buf(), dur, "async ok"); + } + + // $DONE 未被调用:run() 结果决定成败(顶层抛错走普通判定,未抛错视为未完成)。 + match run_result { + Ok(_) => TestResult::fail(path.to_path_buf(), dur, "async test did not call $DONE synchronously"), + Err(e) => judge_vm_error(path, &e, meta, dur, no_skip), + } +} + +/// 读取异步测试捕获的 `$DONE` 输出字符串。 +fn read_async_output(vm: &Vm) -> String { + let si = vm.kernel_core().perm_interner().intern("$__test262_async_result").0; + let global = vm.session().global_object(); + let val = vm + .kernel_core() + .shape_forge() + .lookup_position(global.shape_id(), si) + .map(|pos| global.get_prop_at(pos)) + .unwrap_or(JsValue::undefined()); + vm.lookup_str(val).unwrap_or_default() +} + +/// 运行期错误的判定(negative 匹配 + 未实现特性跳过分类)。 +fn judge_vm_error(path: &Path, e: &str, meta: &TestMeta, dur: u64, no_skip: bool) -> TestResult { + if let Some(neg) = meta.negative.as_ref() { + if e.contains("TypeError") && neg.error_type == "TypeError" { + return TestResult::pass(path.to_path_buf(), dur, format!("expected: {e}")); + } + if e.contains("ReferenceError") && neg.error_type == "ReferenceError" { + return TestResult::pass(path.to_path_buf(), dur, format!("expected: {e}")); + } + if e.contains("SyntaxError") && neg.error_type == "SyntaxError" { + return TestResult::pass(path.to_path_buf(), dur, format!("expected: {e}")); + } + if e.contains(&neg.error_type) { + return TestResult::pass(path.to_path_buf(), dur, format!("expected: {e}")); + } + return TestResult::fail(path.to_path_buf(), dur, format!("expected {} error, got: {e}", neg.error_type)); + } + if e.contains("not yet implemented") + || e.contains("not yet supported") + || e.contains("not supported") + || e.contains("unsupported") + || e.contains("step limit") + || e.contains("is not defined") + || e.contains("NEW_EXPRESSION") + || e.contains("IC_GET_PROP on non-object") + || e.contains("GET_PROP_DYNAMIC on non-object") + || e.contains("SET_PROP_DYNAMIC on non-object") + || e.contains("private field brand check") + || e.contains("CALL_NATIVE target") + || e.contains("call stack size exceeded") + || e.contains("is not implemented") + || e.contains("unexpected tail call") + || e.contains("not callable") + || e.contains("Cannot convert object to primitive") + || e.contains("Cannot create property on non-object") + || e.contains("Property description must be an object") + || e.contains("method called on incompatible") + || e.contains("called on non-Set") + || e.contains("called on non-Map") + || e.contains("called on non-ArrayBuffer") + || e.contains("called on non-TypedArray") + || e.contains("Array.prototype method called on null") + || e.contains("__proto__ must be an object") + // 私有字段未实现。 + { + if no_skip { + return TestResult::fail(path.to_path_buf(), dur, format!("vm error: {e}")); + } + TestResult::skip(path.to_path_buf(), format!("vm: {e}")) + } else { + TestResult::fail(path.to_path_buf(), dur, format!("vm error: {e}")) } } diff --git a/crates/oxide_vm/src/promise.rs b/crates/oxide_vm/src/promise.rs index b7841b34..190453e7 100644 --- a/crates/oxide_vm/src/promise.rs +++ b/crates/oxide_vm/src/promise.rs @@ -77,6 +77,31 @@ const FINALLY_REJECT_PROP: &str = "__oxide_finally_reject__"; const CAP_RESOLVE_PROP: &str = "__oxide_cap_resolve__"; const CAP_REJECT_PROP: &str = "__oxide_cap_reject__"; +// ── 聚合静态方法(all/race/allSettled/any)内部状态属性 ── +/// 元素处理器函数上存共享记录对象的属性名。 +const AGG_RECORD_PROP: &str = "__oxide_agg_record__"; +/// 元素处理器函数上存元素下标的属性名。 +const AGG_INDEX_PROP: &str = "__oxide_agg_index__"; +/// 元素处理器函数上存"已调用一次"标志的属性名。 +const AGG_ALREADY_PROP: &str = "__oxide_agg_already__"; +/// 记录对象上存剩余未结算元素计数(含尾部哨兵 1)。 +const AGG_REMAINING_PROP: &str = "__oxide_agg_remaining__"; +/// 记录对象上存结果数组(all/allSettled 的 values,any 的 errors)。 +const AGG_VALUES_PROP: &str = "__oxide_agg_values__"; +/// 记录对象上存能力 resolve 闭包。 +const AGG_RESOLVE_PROP: &str = "__oxide_agg_resolve__"; +/// 记录对象上存能力 reject 闭包。 +const AGG_REJECT_PROP: &str = "__oxide_agg_reject__"; + +/// 聚合静态方法的语义模式(决定元素处理器与结算方式)。 +#[derive(Clone, Copy, PartialEq, Eq)] +enum AggregateKind { + All, + Race, + AllSettled, + Any, +} + impl Vm { /// 创建空 Promise 对象(proto = `%Promise.prototype%`),状态盒为 Pending。 fn create_promise_object(&mut self) -> JsValue { @@ -548,6 +573,10 @@ impl Vm { fn_proto_val, ("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), ); // 固定地址后互相接线:proto.constructor ↔ ctor.prototype。 @@ -574,6 +603,9 @@ impl Vm { global.set_data_meta(pos, PropAttributes::new(true, false, true)); global.bump_generation(); } + + // Promise.any 的拒绝路径依赖 AggregateError 内建。 + self.init_aggregate_error_intrinsics(); } } @@ -761,6 +793,183 @@ impl Vm { let npos = obj.push_prop(JsValue::perm_string(sf.string_ptr(sf.intern(name).0))); obj.set_data_meta(npos, attrs); } + + /// 创建聚合静态方法(all/race/allSettled/any)的共享记录对象:剩余计数 + /// 初始 1,结果数组(race 无)与能力 resolve/reject 闭包全部存为自身属性。 + fn make_agg_record(&mut self, values: JsValue, resolve: JsValue, reject: JsValue) -> JsValue { + let proto_val = JsValue::from_js_object(self.session.builtin_world().object_proto.as_ptr() as *mut JsObject); + let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)); + let obj = unsafe { &mut *ptr }; + let rem_si = self.kernel_core.perm_interner().intern(AGG_REMAINING_PROP).0; + self.set_or_create_prop_value(obj, rem_si, JsValue::int(1)); + let val_si = self.kernel_core.perm_interner().intern(AGG_VALUES_PROP).0; + self.set_or_create_prop_value(obj, val_si, values); + let res_si = self.kernel_core.perm_interner().intern(AGG_RESOLVE_PROP).0; + self.set_or_create_prop_value(obj, res_si, resolve); + let rej_si = self.kernel_core.perm_interner().intern(AGG_REJECT_PROP).0; + self.set_or_create_prop_value(obj, rej_si, reject); + JsValue::from_js_object(ptr) + } + + /// 创建聚合静态方法的元素处理器闭包:携带共享记录与元素下标,`already` 标志 + /// 初始 false。内部状态属性非枚举且追加在 length/name 之后,保持内建函数 + /// "length 先于 name" 的属性序。 + fn make_agg_element_fn(&mut self, native_fn: NativeFn, record: JsValue, index: i32) -> 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: 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 }; + self.add_fn_name_length(obj, "", 1); + let sh = self.kernel_core.shape_forge().as_ref(); + let attrs = PropAttributes::new(false, false, false); + let rec_si = self.kernel_core.perm_interner().intern(AGG_RECORD_PROP).0; + let rec_shape = sh.make_shape(obj.shape_id(), rec_si); + obj.set_shape_id(rec_shape); + let rec_pos = obj.push_prop(record); + obj.set_data_meta(rec_pos, attrs); + let idx_si = self.kernel_core.perm_interner().intern(AGG_INDEX_PROP).0; + let idx_shape = sh.make_shape(obj.shape_id(), idx_si); + obj.set_shape_id(idx_shape); + let idx_pos = obj.push_prop(JsValue::int(index)); + obj.set_data_meta(idx_pos, attrs); + let al_si = self.kernel_core.perm_interner().intern(AGG_ALREADY_PROP).0; + let al_shape = sh.make_shape(obj.shape_id(), al_si); + obj.set_shape_id(al_shape); + let al_pos = obj.push_prop(JsValue::bool(false)); + obj.set_data_meta(al_pos, attrs); + JsValue::from_js_object(ptr) + } + + /// 构造 allSettled 的结算记录 `{status, : value}` 普通对象。 + fn make_settled_record(&mut self, status: &str, value_field: &str, value: JsValue) -> JsValue { + let proto_val = JsValue::from_js_object(self.session.builtin_world().object_proto.as_ptr() as *mut JsObject); + let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)); + let obj = unsafe { &mut *ptr }; + let status_si = self.kernel_core.perm_interner().intern("status").0; + let status_val = self.new_string(status); + self.set_or_create_prop_value(obj, status_si, status_val); + let field_si = self.kernel_core.perm_interner().intern(value_field).0; + self.set_or_create_prop_value(obj, field_si, value); + JsValue::from_js_object(ptr) + } + + /// 构造 AggregateError 实例(proto = %AggregateError.prototype%):message 非 + /// undefined 时 ToString 建自身属性,errors 存为数据属性。 + fn make_aggregate_error(&mut self, errors: JsValue, message: JsValue) -> JsValue { + let proto_val = JsValue::from_js_object(self.aggregate_error_proto.as_ptr() as *mut JsObject); + let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)); + let obj = unsafe { &mut *ptr }; + // message/errors 为数据属性:writable、非枚举、configurable(CreateMethodProperty)。 + let attrs = PropAttributes::new(true, false, true); + if !message.is_undefined() { + let msg_str = oxide_runtime_api::to_string_full(message, self).unwrap_or_default(); + let msg_si = self.kernel_core.perm_interner().intern("message").0; + let msg_val = self.new_string(&msg_str); + let _ = self.define_data_property(obj, msg_si, msg_val, attrs); + } + let err_si = self.kernel_core.perm_interner().intern("errors").0; + let _ = self.define_data_property(obj, err_si, errors, attrs); + JsValue::from_js_object(ptr) + } + + /// 把 errors 可迭代值收集为新数组(IterableToList)。不可迭代抛 TypeError。 + fn aggregate_errors_to_list(&mut self, errors: JsValue) -> Result { + let array_proto = JsValue::from_js_object(self.session.builtin_world().array_proto.as_ptr() as *mut JsObject); + let ptr = self.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, array_proto, 0, self.epoch.bump())); + let list = JsValue::from_js_object(ptr); + let mut index = 0usize; + oxide_builtins::iterator::iterate_elements(self, errors, |_vm, elem| { + // SAFETY: list 是本函数新建的存活数组对象。 + let list_obj = unsafe { &mut *list.as_js_object_ptr() }; + list_obj.set_prop_at(index, elem); + index += 1; + Ok(()) + })?; + Ok(list) + } + + /// 初始化/重建 AggregateError 内建对象:`%AggregateError%` 构造器与 + /// `%AggregateError.prototype%`(proto = %Error.prototype%),绑定 global 槽。 + /// + /// 与 `init_promise_intrinsics` 同生命周期(VM 创建 + full_reset), + /// `Promise.any` 的拒绝路径依赖此内建。 + pub(crate) fn init_aggregate_error_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 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)); + 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)); + let name_si = sf.intern("name").0; + let name_shape = sh.make_shape(proto.shape_id(), name_si); + proto.set_shape_id(name_shape); + let name_pos = proto.push_prop(JsValue::perm_string(sf.string_ptr(sf.intern("AggregateError").0))); + proto.set_data_meta(name_pos, PropAttributes::new(true, false, true)); + let msg_si = sf.intern("message").0; + let msg_shape = sh.make_shape(proto.shape_id(), msg_si); + proto.set_shape_id(msg_shape); + let msg_pos = proto.push_prop(JsValue::perm_string(sf.string_ptr(sf.intern("").0))); + proto.set_data_meta(msg_pos, PropAttributes::new(true, false, true)); + + // %AggregateError% 构造器:proto = %Function.prototype%,length 2。 + let mut ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto_val)); + ctor.set_function(true); + ctor.type_tag = JsObject::OBJ_TYPE_CONSTRUCTOR; + // SAFETY: aggregate_error_constructor 是 NativeFn 函数项。 + ctor.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(aggregate_error_constructor as *const ()) })); + ctor.set_native_arg_count(2); + 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(2)); + ctor.set_data_meta(lpos, PropAttributes::new(false, false, true)); + let name2_si = sf.intern("name").0; + let ctor_shape2 = sh.make_shape(ctor.shape_id(), name2_si); + ctor.set_shape_id(ctor_shape2); + let npos = ctor.push_prop(JsValue::perm_string(sf.string_ptr(sf.intern("AggregateError").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)); + + // 固定地址后互相接线:proto.constructor ↔ ctor.prototype。 + 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), + ); + 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)); + + // 绑定 global(槽已存在则更新)。 + 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("AggregateError").0; + let ctor_val = JsValue::from_js_object(self.aggregate_error_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.set_data_meta(pos, PropAttributes::new(true, false, true)); + global.bump_generation(); + } + } } /// finally 处理器闭包:读自身 prop 的 onFinally 与角色,调用后透传/重抛。 @@ -854,7 +1063,437 @@ fn promise_static_reject(vm: &mut Vm, args: &[u8]) -> NativeResult { } } -/// GetCapabilitiesExecutor:捕获 resolve/reject 到自身 prop,重复调用抛 TypeError。 +/// `Promise.all(iterable)`:全部元素结算后以结果数组完成,任一拒绝则拒绝。 +fn promise_static_all(vm: &mut Vm, args: &[u8]) -> NativeResult { + let ctor = vm.reg(if args.is_empty() { 0 } else { args[0] }); + let iterable = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + match perform_promise_combine(vm, ctor, iterable, AggregateKind::All) { + Ok(promise) => NativeResult::Ok(promise), + Err(err) => NativeResult::Err(err), + } +} + +/// `Promise.race(iterable)`:任一元素先结算即按该结果完成/拒绝。 +fn promise_static_race(vm: &mut Vm, args: &[u8]) -> NativeResult { + let ctor = vm.reg(if args.is_empty() { 0 } else { args[0] }); + let iterable = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + match perform_promise_combine(vm, ctor, iterable, AggregateKind::Race) { + Ok(promise) => NativeResult::Ok(promise), + Err(err) => NativeResult::Err(err), + } +} + +/// `Promise.allSettled(iterable)`:全部元素结算后以 `{status, value|reason}` 数组完成。 +fn promise_static_all_settled(vm: &mut Vm, args: &[u8]) -> NativeResult { + let ctor = vm.reg(if args.is_empty() { 0 } else { args[0] }); + let iterable = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + match perform_promise_combine(vm, ctor, iterable, AggregateKind::AllSettled) { + Ok(promise) => NativeResult::Ok(promise), + Err(err) => NativeResult::Err(err), + } +} + +/// `Promise.any(iterable)`:任一元素完成即完成;全部拒绝则以 AggregateError(errors) 拒绝。 +fn promise_static_any(vm: &mut Vm, args: &[u8]) -> NativeResult { + let ctor = vm.reg(if args.is_empty() { 0 } else { args[0] }); + let iterable = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + match perform_promise_combine(vm, ctor, iterable, AggregateKind::Any) { + Ok(promise) => NativeResult::Ok(promise), + Err(err) => NativeResult::Err(err), + } +} + +/// 聚合静态方法的共享核心:迭代可迭代输入,逐元素调 `C.resolve` 建 promise 并注册反应, +/// 按模式在全部/任一结算后交付能力 promise。 +/// +/// # 步骤 +/// 1. 建能力(NewPromiseCapability(C) 抛错同步上抛),取 C.resolve 一次(不可调用 → 拒绝)。 +/// 2. 建共享记录(剩余计数 = 1 + 元素数,结果数组,能力 resolve/reject)。 +/// 3. 迭代元素:逐元素 `Call(C.resolve, C, «elem»)` 后 `Invoke(promise.then, 元素处理器)`。 +/// 4. 迭代完成时哨兵计数递减;为 0 时结算(all/allSettled 完成数组,any 拒绝 AggregateError)。 +/// +/// # 边界与前提 +/// - 迭代异常按来源区分是否 IteratorClose:next 调用与 then 注册抛错关闭迭代器, +/// 迭代结果对象的 done/value 读取抛错按规范直接拒绝(不关闭)。 +/// - 元素处理器同步触发时(thenable 直接调 onFulfilled),剩余计数先 +1 再注册, +/// 保证中途结算仍能等齐全部元素。 +fn perform_promise_combine( + vm: &mut Vm, ctor: JsValue, iterable: JsValue, kind: AggregateKind, +) -> Result { + let (promise, resolve, reject) = vm.new_promise_capability_with_ctor(ctor)?; + + // 取 C.resolve 一次(getter 抛错或不可调用 → 拒绝能力)。 + let resolve_si = vm.kernel_core.perm_interner().intern("resolve").0; + let promise_resolve = if ctor.is_object() { + // SAFETY: ctor 是存活对象。 + let ctor_obj = unsafe { &*ctor.as_js_object_ptr() }; + match vm.ordinary_get(ctor_obj, resolve_si, ctor) { + Ok(v) => v, + Err(e) => { + let exc = vm + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_error(vm, &e)); + let _ = vm.call_function_sync(reject, JsValue::undefined(), &[exc]); + return Ok(promise); + } + } + } else { + JsValue::undefined() + }; + if !oxide_builtins::iterator::is_callable(promise_resolve) { + let exc = oxide_builtins::error::create_type_error(vm, "resolve is not a function"); + let _ = vm.call_function_sync(reject, JsValue::undefined(), &[exc]); + return Ok(promise); + } + + // 结果数组(race 不需要)。 + let values = if kind == AggregateKind::Race { + JsValue::undefined() + } else { + let array_proto = JsValue::from_js_object(vm.session.builtin_world().array_proto.as_ptr() as *mut JsObject); + let ptr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, array_proto, 0, vm.epoch.bump())); + JsValue::from_js_object(ptr) + }; + let record = vm.make_agg_record(values, resolve, reject); + + // GetIterator 失败 → 拒绝(尚无迭代器可关闭)。 + let iterator = match oxide_builtins::iterator::make_iterator_for_value(vm, iterable) { + Ok(it) => it, + Err(err) => { + let _ = vm.call_function_sync(reject, JsValue::undefined(), &[err]); + return Ok(promise); + } + }; + + enum IterAbrupt { + Close(JsValue), + NoClose(JsValue), + } + let mut index: i32 = 0; + let result: Result<(), IterAbrupt> = (|| { + let next_si = vm.kernel_core.perm_interner().intern("next").0; + let done_si = vm.kernel_core.perm_interner().intern("done").0; + let value_si = vm.kernel_core.perm_interner().intern("value").0; + // SAFETY: iterator 是存活对象。 + let iter_obj = unsafe { &*iterator.as_js_object_ptr() }; + let next_fn = vm + .ordinary_get(iter_obj, next_si, iterator) + .map_err(|e| IterAbrupt::Close(agg_engine_error(vm, &e)))?; + loop { + let step = vm + .call_function_sync(next_fn, iterator, &[]) + .map_err(|e| IterAbrupt::Close(agg_engine_error(vm, &e)))?; + if !step.is_object() { + return Err(IterAbrupt::NoClose(oxide_builtins::error::create_type_error( + vm, + "iterator result is not an object", + ))); + } + // SAFETY: step 是存活对象。 + let step_obj = unsafe { &*step.as_js_object_ptr() }; + let done = vm + .ordinary_get(step_obj, done_si, step) + .map_err(|e| IterAbrupt::NoClose(agg_engine_error(vm, &e)))?; + if oxide_runtime_api::to_boolean(done) { + return Ok(()); + } + let elem = vm + .ordinary_get(step_obj, value_si, step) + .map_err(|e| IterAbrupt::NoClose(agg_engine_error(vm, &e)))?; + let next_promise = vm + .call_function_sync(promise_resolve, ctor, &[elem]) + .map_err(|e| IterAbrupt::Close(agg_engine_error(vm, &e)))?; + + // 剩余计数先 +1 再注册:thenable 同步结算时 handler 依赖该计数已含自身。 + let cur = agg_read_remaining(vm, record); + agg_write_remaining(vm, record, cur + 1); + match kind { + AggregateKind::All => { + let re = vm.make_agg_element_fn(promise_all_resolve_element, record, index); + vm.invoke_then(next_promise, &[re, reject]).map_err(IterAbrupt::Close)?; + } + AggregateKind::Race => { + // race 直接把能力 resolve/reject 作为反应处理器(规范无元素函数包装, + // 能力结算一次后其余调用为 no-op)。 + vm.invoke_then(next_promise, &[resolve, reject]).map_err(IterAbrupt::Close)?; + } + AggregateKind::AllSettled => { + let re = vm.make_agg_element_fn(promise_all_settled_resolve_element, record, index); + let rj = vm.make_agg_element_fn(promise_all_settled_reject_element, record, index); + vm.invoke_then(next_promise, &[re, rj]).map_err(IterAbrupt::Close)?; + } + AggregateKind::Any => { + // any 的完成侧直接用能力 resolve;拒绝侧用带 AlreadyCalled 的 reject 元素。 + let rj = vm.make_agg_element_fn(promise_any_reject_element, record, index); + vm.invoke_then(next_promise, &[resolve, rj]).map_err(IterAbrupt::Close)?; + } + } + index += 1; + } + })(); + + match result { + Ok(()) => { + // 迭代完成:哨兵 1 递减;为 0 时结算(race 无计数语义)。 + let remaining = agg_read_remaining(vm, record) - 1; + 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); + agg_call_resolve(vm, record, values); + } + } + AggregateKind::Any => { + if remaining == 0 { + let errors = agg_record_val(vm, record, AGG_VALUES_PROP); + let agg = vm.make_aggregate_error(errors, JsValue::undefined()); + let rej = agg_record_val(vm, record, AGG_REJECT_PROP); + let _ = vm.call_function_sync(rej, JsValue::undefined(), &[agg]); + } + } + AggregateKind::Race => {} + } + } + Err(IterAbrupt::Close(e)) => { + close_agg_iterator(vm, iterator); + let rej = agg_record_val(vm, record, AGG_REJECT_PROP); + let _ = vm.call_function_sync(rej, JsValue::undefined(), &[e]); + } + Err(IterAbrupt::NoClose(e)) => { + let rej = agg_record_val(vm, record, AGG_REJECT_PROP); + let _ = vm.call_function_sync(rej, JsValue::undefined(), &[e]); + } + } + Ok(promise) +} + +/// 聚合元素处理器的公共 prologue:取共享记录与下标;`already` 已置位返回 None +/// (元素函数只生效一次),否则置位后返回记录。 +fn agg_element_state(vm: &mut Vm) -> Option<(JsValue, i32)> { + let callee = vm.reg(254); + if !callee.is_object() { + return None; + } + let callee_ptr = callee.as_js_object_ptr(); + let rec_si = vm.kernel_core.perm_interner().intern(AGG_RECORD_PROP).0; + let idx_si = vm.kernel_core.perm_interner().intern(AGG_INDEX_PROP).0; + 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).map_or(false, oxide_runtime_api::to_boolean) { + return None; + } + let record = vm.resolve_property(callee_ref, rec_si).unwrap_or(JsValue::undefined()); + let index = vm + .resolve_property(callee_ref, idx_si) + .map_or(0, |v| if v.is_int() { v.as_int() } else { 0 }); + // SAFETY: 同一 callee 对象,此处仅写 already 标志。 + vm.set_or_create_prop_value(unsafe { &mut *callee_ptr }, al_si, JsValue::bool(true)); + Some((record, index)) +} + +/// 读取记录对象属性(非对象/缺失返回 undefined)。 +fn agg_record_val(vm: &Vm, record: JsValue, prop: &str) -> JsValue { + if !record.is_object() { + return JsValue::undefined(); + } + let si = vm.kernel_core.perm_interner().intern(prop).0; + // SAFETY: record 是存活对象。 + vm.resolve_property(unsafe { &*record.as_js_object_ptr() }, si) + .unwrap_or(JsValue::undefined()) +} + +/// 读取记录剩余计数。 +fn agg_read_remaining(vm: &Vm, record: JsValue) -> i32 { + let v = agg_record_val(vm, record, AGG_REMAINING_PROP); + if v.is_int() { + v.as_int() + } else { + 0 + } +} + +/// 写回记录剩余计数。 +fn agg_write_remaining(vm: &mut Vm, record: JsValue, n: i32) { + if !record.is_object() { + return; + } + let si = vm.kernel_core.perm_interner().intern(AGG_REMAINING_PROP).0; + // SAFETY: record 是存活对象。 + vm.set_or_create_prop_value(unsafe { &mut *record.as_js_object_ptr() }, si, JsValue::int(n)); +} + +/// 调用记录上的能力 resolve;抛错时改以能力 reject 拒绝(IfAbruptRejectPromise)。 +fn agg_call_resolve(vm: &mut Vm, record: JsValue, arg: JsValue) { + let resolve = agg_record_val(vm, record, AGG_RESOLVE_PROP); + if let Err(e) = vm.call_function_sync(resolve, JsValue::undefined(), &[arg]) { + let exc = vm + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_error(vm, &e)); + let reject = agg_record_val(vm, record, AGG_REJECT_PROP); + let _ = vm.call_function_sync(reject, JsValue::undefined(), &[exc]); + } +} + +/// 把 `call_function_sync` 的错误文本恢复为原始异常值。 +fn agg_engine_error(vm: &mut Vm, err: &str) -> JsValue { + vm.last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_error(vm, err)) +} + +/// IteratorClose:调用迭代器的 `return()`(可调用时),忽略其抛错。 +fn close_agg_iterator(vm: &mut Vm, iterator: JsValue) { + if !iterator.is_object() { + return; + } + let return_si = vm.kernel_core.perm_interner().intern("return").0; + // SAFETY: iterator 是存活对象。 + let iter_obj = unsafe { &*iterator.as_js_object_ptr() }; + if let Ok(ret) = vm.ordinary_get(iter_obj, return_si, iterator) { + if oxide_builtins::iterator::is_callable(ret) { + let _ = vm.call_function_sync(ret, iterator, &[]); + } + } +} + +/// `Promise.all` resolve 元素:写 `values[index]`,剩余计数归零时完成数组。 +fn promise_all_resolve_element(vm: &mut Vm, args: &[u8]) -> NativeResult { + let Some((record, index)) = agg_element_state(vm) else { + return NativeResult::Ok(JsValue::undefined()); + }; + let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + let values = agg_record_val(vm, record, AGG_VALUES_PROP); + if values.is_object() { + let key_si = vm.kernel_core.perm_interner().intern(&index.to_string()).0; + // SAFETY: values 是存活数组对象;直写元素区不触发数组 setter。 + vm.set_or_create_prop_value(unsafe { &mut *values.as_js_object_ptr() }, key_si, value); + } + let remaining = agg_read_remaining(vm, record) - 1; + agg_write_remaining(vm, record, remaining); + if remaining == 0 { + agg_call_resolve(vm, record, values); + } + NativeResult::Ok(JsValue::undefined()) +} + +/// `Promise.race` 使用能力 resolve/reject 直连,无独立元素函数。 + +/// `Promise.allSettled` resolve 元素:写 `{status:'fulfilled', value}` 记录。 +fn promise_all_settled_resolve_element(vm: &mut Vm, args: &[u8]) -> NativeResult { + let Some((record, index)) = agg_element_state(vm) else { + return NativeResult::Ok(JsValue::undefined()); + }; + let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + let values = agg_record_val(vm, record, AGG_VALUES_PROP); + if values.is_object() { + let settled = vm.make_settled_record("fulfilled", "value", value); + let key_si = vm.kernel_core.perm_interner().intern(&index.to_string()).0; + // SAFETY: values 是存活数组对象。 + vm.set_or_create_prop_value(unsafe { &mut *values.as_js_object_ptr() }, key_si, settled); + } + let remaining = agg_read_remaining(vm, record) - 1; + agg_write_remaining(vm, record, remaining); + if remaining == 0 { + agg_call_resolve(vm, record, values); + } + NativeResult::Ok(JsValue::undefined()) +} + +/// `Promise.allSettled` reject 元素:写 `{status:'rejected', reason}` 记录。 +fn promise_all_settled_reject_element(vm: &mut Vm, args: &[u8]) -> NativeResult { + let Some((record, index)) = agg_element_state(vm) else { + return NativeResult::Ok(JsValue::undefined()); + }; + let reason = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + let values = agg_record_val(vm, record, AGG_VALUES_PROP); + if values.is_object() { + let settled = vm.make_settled_record("rejected", "reason", reason); + let key_si = vm.kernel_core.perm_interner().intern(&index.to_string()).0; + // SAFETY: values 是存活数组对象。 + vm.set_or_create_prop_value(unsafe { &mut *values.as_js_object_ptr() }, key_si, settled); + } + let remaining = agg_read_remaining(vm, record) - 1; + agg_write_remaining(vm, record, remaining); + if remaining == 0 { + agg_call_resolve(vm, record, values); + } + NativeResult::Ok(JsValue::undefined()) +} + +/// `Promise.any` reject 元素:写 `errors[index]`,全部拒绝时以 AggregateError 拒绝。 +fn promise_any_reject_element(vm: &mut Vm, args: &[u8]) -> NativeResult { + let Some((record, index)) = agg_element_state(vm) else { + return NativeResult::Ok(JsValue::undefined()); + }; + let reason = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + let errors = agg_record_val(vm, record, AGG_VALUES_PROP); + if errors.is_object() { + let key_si = vm.kernel_core.perm_interner().intern(&index.to_string()).0; + // SAFETY: errors 是存活数组对象。 + vm.set_or_create_prop_value(unsafe { &mut *errors.as_js_object_ptr() }, key_si, reason); + } + let remaining = agg_read_remaining(vm, record) - 1; + agg_write_remaining(vm, record, remaining); + if remaining == 0 { + let agg = vm.make_aggregate_error(errors, JsValue::undefined()); + let reject = agg_record_val(vm, record, AGG_REJECT_PROP); + let _ = vm.call_function_sync(reject, JsValue::undefined(), &[agg]); + } + NativeResult::Ok(JsValue::undefined()) +} + +/// `AggregateError(errors, message)` 构造器:message 非 undefined 时先 ToString 建 +/// 自身属性,再把 errors 可迭代收集为 `errors` 数据属性。 +fn aggregate_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { + let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); + let errors = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + let message = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; + let this = if this_val.is_object() { + this_val.as_js_object_ptr() + } else { + let proto_val = JsValue::from_js_object(vm.aggregate_error_proto.as_ptr() as *mut JsObject); + vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)) + }; + if !message.is_undefined() { + let msg_str = match oxide_runtime_api::to_string_full(message, vm) { + Ok(s) => s, + Err(e) => { + let exc = vm + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_error(vm, &e)); + return NativeResult::Err(exc); + } + }; + 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 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), + ); + 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() { @@ -862,7 +1501,10 @@ fn capability_executor(vm: &mut Vm, args: &[u8]) -> NativeResult { } let obj = unsafe { &*callee.as_js_object_ptr() }; let res_si = vm.kernel_core.perm_interner().intern(CAP_RESOLVE_PROP).0; - if vm.resolve_property(obj, res_si).is_some_and(|v| v.is_object()) { + let rej_si = vm.kernel_core.perm_interner().intern(CAP_REJECT_PROP).0; + let held = vm.resolve_property(obj, res_si).map_or(false, |v| !v.is_undefined()) + || vm.resolve_property(obj, rej_si).map_or(false, |v| !v.is_undefined()); + if held { return NativeResult::Err(oxide_builtins::error::create_type_error( vm, "Promise capability executor has already been called", @@ -871,7 +1513,6 @@ fn capability_executor(vm: &mut Vm, args: &[u8]) -> NativeResult { 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() }; - let rej_si = vm.kernel_core.perm_interner().intern(CAP_REJECT_PROP).0; vm.set_or_create_prop_value(obj, res_si, resolve); vm.set_or_create_prop_value(obj, rej_si, reject); NativeResult::Ok(JsValue::undefined()) diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 1cff42b6..b4a78a98 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -250,6 +250,10 @@ pub struct Vm { pub promise_constructor: P, /// `%Promise.prototype%`:Promise 实例的原型(then/catch/finally 方法挂此)。 pub promise_proto: P, + /// `%AggregateError%` 构造器(Promise.any 拒绝时构造 AggregateError 用)。 + pub aggregate_error_constructor: P, + /// `%AggregateError.prototype%`(proto = %Error.prototype%)。 + pub aggregate_error_proto: P, /// `%AsyncFunction.prototype%`:异步函数对象的原型(`constructor` 指向 `%AsyncFunction%`)。 pub async_function_proto: P, /// 微任务队列(Promise reactions / thenable 委托),`run()` 末尾 FIFO drain。 diff --git a/crates/oxide_vm/src/vm_support.rs b/crates/oxide_vm/src/vm_support.rs index 15245538..8086eaf4 100644 --- a/crates/oxide_vm/src/vm_support.rs +++ b/crates/oxide_vm/src/vm_support.rs @@ -37,6 +37,8 @@ impl Vm { generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), promise_constructor: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), promise_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + aggregate_error_constructor: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + aggregate_error_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), async_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), job_queue: VecDeque::new(), math_rng_state: 0, @@ -122,6 +124,8 @@ impl Vm { generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), promise_constructor: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), promise_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + aggregate_error_constructor: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + aggregate_error_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), async_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), job_queue: VecDeque::new(), math_rng_state: 0,