This repository has been archived on 2026-09-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
OxideJS/crates/oxide_vm/tests/promise_tests.rs
T

299 lines
10 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.
use oxide_compiler::compiler::Compiler;
use oxide_types::value::JsValue;
use oxide_vm::promise::promise_settled_value;
use oxide_vm::vm::Vm;
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
vm.run(&module)
}
/// 顶层表达式返回 Promise 时,run() 末尾 drain 已执行微任务,读其结算值。
fn settled(vm: &mut Vm, source: &str) -> (bool, JsValue) {
let result = eval(vm, source).expect("run should succeed");
assert!(result.is_object(), "expected a promise, got {result:?}");
let obj = unsafe { &*result.as_js_object_ptr() };
promise_settled_value(obj).expect("promise should be settled")
}
#[test]
fn resolve_then_drains_chain() {
let mut vm = Vm::new();
let (ok, val) = settled(&mut vm, "Promise.resolve(42).then(v => v * 2)");
assert!(ok);
assert_eq!(val.as_double(), 84.0);
}
#[test]
fn chain_orders_fifo() {
let mut vm = Vm::new();
let (ok, val) = settled(&mut vm, "Promise.resolve(1).then(v => v + 1).then(v => v * 10)");
assert!(ok);
assert_eq!(val.as_double(), 20.0);
}
#[test]
fn settle_only_once() {
let mut vm = Vm::new();
let (ok, val) = settled(&mut vm, "new Promise((res, rej) => { res(1); res(2); rej(3); }).then(v => v)");
assert!(ok);
assert_eq!(val.as_int(), 1);
}
#[test]
fn throw_in_handler_goes_to_catch() {
let mut vm = Vm::new();
let (ok, val) = settled(&mut vm, "Promise.resolve(1).then(v => { throw 'x' }).catch(e => e)");
assert!(ok);
assert_eq!(vm.lookup_str(val).as_deref(), Some("x"));
}
#[test]
fn thenable_delegation() {
let mut vm = Vm::new();
let (ok, val) = settled(&mut vm, "Promise.resolve({ then: function(r) { r(7) } }).then(v => v)");
assert!(ok);
assert_eq!(val.as_int(), 7);
}
#[test]
fn finally_passes_through() {
let mut vm = Vm::new();
let (ok, val) = settled(&mut vm, "new Promise(r => { r(10) }).finally(() => {}).then(v => v)");
assert!(ok);
assert_eq!(val.as_int(), 10);
}
#[test]
fn reject_propagates_through_empty_then() {
let mut vm = Vm::new();
let (ok, val) = settled(&mut vm, "Promise.reject('e').then(undefined, e => 'caught:' + e)");
assert!(ok);
assert_eq!(vm.lookup_str(val).as_deref(), Some("caught:e"));
}
#[test]
fn ctor_receiver_used_for_capability() {
let mut vm = Vm::new();
// Promise.resolve.call(NotPromise) 应构造 NotPromise 并捕获其 executor。
let result = eval(
&mut vm,
"var ef; function NotP(exec) { ef = exec; exec(function(){}, function(){}); } \
Promise.resolve.call(NotP); ef.length",
)
.unwrap();
assert_eq!(result.as_int(), 2);
}
#[test]
fn custom_ctor_failure_is_type_error() {
let mut vm = Vm::new();
// executor 未捕获 resolve/reject → TypeError。
let result = eval(
&mut vm,
"function NotP(exec) { exec(function(){}); } \
try { Promise.resolve.call(NotP); 'no-throw' } catch (e) { e.name }",
)
.unwrap();
assert_eq!(vm.lookup_str(result).as_deref(), Some("TypeError"));
}
#[test]
fn resolve_thenable_then_reject_keeps_fulfilled() {
// P3:resolve(thenable) 置位 alreadyResolved,随后同调用内 reject 不覆盖。
let mut vm = Vm::new();
let (ok, val) = settled(
&mut vm,
"new Promise((res, rej) => { res({ then(res2) { res2({ then(r3) { r3('final'); } }); } }); rej('oops'); }).then(v => v, e => 'rejected:' + e)",
);
assert!(ok);
assert_eq!(vm.lookup_str(val).as_deref(), Some("final"));
}
#[test]
fn double_resolve_second_noop() {
let mut vm = Vm::new();
let (ok, val) = settled(&mut vm, "new Promise((res) => { res(1); res(2); }).then(v => v)");
assert!(ok);
assert_eq!(val.as_int(), 1);
}
#[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)",
);
assert!(ok);
assert_eq!(vm.lookup_str(val).as_deref(), Some("rejected:e"));
}
#[test]
fn thenable_resolve_after_reject_noop() {
// reject 先行置位,随后 resolve(thenable) 不得触发委托结算。
let mut vm = Vm::new();
let (ok, val) = settled(
&mut vm,
"new Promise((res, rej) => { rej('e'); res({ then(r) { r(1); } }); }).then(v => v, e => 'rejected:' + e)",
);
assert!(ok);
assert_eq!(vm.lookup_str(val).as_deref(), Some("rejected:e"));
}
#[test]
fn thenable_chain_after_already_resolved_noop() {
// 双重 resolve(thenable):第二次委托不结算,结果保持第一次。
let mut vm = Vm::new();
let (ok, val) = settled(
&mut vm,
"new Promise((res) => { res({ then(r) { r(5); } }); res({ then(r) { r(9); } }); }).then(v => v)",
);
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");
}