feat: Promise 静态方法(all/race/allSettled/any + AggregateError)+ async test262 解锁

- Promise.all/race/allSettled/any:共享聚合核心,逐元素 C.resolve + Invoke(then),
  共享记录对象承载剩余计数/结果数组/能力闭包,元素处理器按模式结算
- AggregateError 内建(构造器 + prototype,Promise.any 拒绝路径依赖)
- 修复 capability_executor:能力已持有非 undefined 值时二次调用抛 TypeError
- test262 runner:移除 async flag 无条件 skip;解锁 asyncHelpers/promiseHelper/
  doneprintHandle harness;注入 print 捕获 + ,run() 后按 marker 判定
- 排除 await-dictionary(Promise.allKeyed/allSettledKeyed 2025 proposal)特性
- iterator 遍历辅助改 pub 供 oxide_vm 复用
This commit is contained in:
爱特每羊队
2026-08-09 23:25:36 +08:00
parent 0d9e6eabc9
commit 9b2a56c1fa
8 changed files with 798 additions and 61 deletions
+1
View File
@@ -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"
+141 -56
View File
@@ -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<String> {
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<String> {
"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:<name>: <msg>`);其次成功 marker
/// (`Test262:AsyncTestComplete`);两者都无则测试未在 run() 内同步完成
/// (需 setTimeout 等异步 runner 的测试在此失败),回退到 run 结果判定。
fn judge_async_result(
path: &Path, run_result: Result<JsValue, String>, 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}"))
}
}