mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
feat: for-await-of 多层逃出逐层 LIFO 关闭并接通异步生成器路径
This commit is contained in:
@@ -360,3 +360,29 @@ fn t18_throw_close_non_object_result_typeerror() {
|
||||
"\"caught:IteratorResult is not an object:true\""
|
||||
);
|
||||
}
|
||||
|
||||
// T19:异步生成器体内 for-await-of 多层 return 逃出——LIFO 顺序关闭(内层先),
|
||||
// 完成值经 next() 的 promise 交付({value:42, done:true})。
|
||||
#[test]
|
||||
fn t19_async_gen_multi_layer_return_escape_lifo() {
|
||||
let source = "let log=[];\
|
||||
const mk=(n)=>({[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:n,done:false})},return(){log.push('c'+n);return Promise.resolve({done:true})}}}});\
|
||||
async function* g() { for await (const a of mk(1)) { for await (const b of mk(2)) { log.push('body'); return 42; } } }\
|
||||
(async function run() { const it = g(); const r = await it.next(); \
|
||||
return log.join(',') + '|' + r.done + ':' + r.value; })()"
|
||||
.to_string();
|
||||
assert_eq!(eval(&source), "\"body,c2,c1|true:42\"");
|
||||
}
|
||||
|
||||
// T20:异步生成器体内 for-await-of break 逃出——关闭迭代器后 body 继续,
|
||||
// 后续 yield 正常交付(验证 break 跳转 target_pc 正确、生成器未误完成)。
|
||||
#[test]
|
||||
fn t20_async_gen_break_escape_continues() {
|
||||
let source = "let log=[];\
|
||||
const it={[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:1,done:false})},return(){log.push('close');return Promise.resolve({done:true})}}}};\
|
||||
async function* g() { for await (const x of it) { log.push('body'); break; } yield 'after'; }\
|
||||
(async function run() { const it = g(); const r = await it.next(); \
|
||||
return log.join(',') + '|' + r.value + ':' + r.done; })()"
|
||||
.to_string();
|
||||
assert_eq!(eval(&source), "\"body,close|after:false\"");
|
||||
}
|
||||
|
||||
@@ -281,6 +281,104 @@ fn for_await_of_close_throw_object_identity_preserved() {
|
||||
);
|
||||
}
|
||||
|
||||
// ── 逃出关闭测试矩阵(E1-E10):多层 LIFO 关闭与 Completion 边角 ──
|
||||
//
|
||||
// for-await-of 的 next() 经 await 结算,循环体在 N 层嵌套时深 N 个微任务 tick;
|
||||
// 读取 log 的 then 回调须比最深的结算闭包更晚入队,故用足够长的 then 链延迟读取。
|
||||
|
||||
/// 延迟 N 个微任务 tick 后读取 log(N 层逃出需 N+2 个 tick 让全部结算闭包跑完)。
|
||||
fn delay_read(n: usize) -> String {
|
||||
let mut chain = "Promise.resolve()".to_string();
|
||||
for _ in 0..n {
|
||||
chain.push_str(".then(()=>{})");
|
||||
}
|
||||
chain.push_str(".then(()=>log.join(','))");
|
||||
chain
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_await_of_break_escape_middle_layer() {
|
||||
// E2b:break 逃出中间 for-await-of 层——内层迭代器关闭,外层继续迭代。
|
||||
let source = "let log=[];\
|
||||
const mk=(n)=>({[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:n,done:false})},return(){log.push('c'+n);return Promise.resolve({done:true})}}}});\
|
||||
(async()=>{ for await (const a of mk(1)) { for await (const b of mk(2)) { log.push('body'); break; } log.push('after'); break; } })();\
|
||||
".to_string()
|
||||
+ &delay_read(4);
|
||||
assert_eq!(eval(&source), "\"body,c2,after,c1\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_await_of_labeled_continue_closes_inner() {
|
||||
// E3:labeled continue 逃出内层 for-await-of——内层迭代器每轮关闭,外层继续。
|
||||
// 外层迭代器有限(两轮后 done),验证 continue 不关闭外层、外层继续迭代。
|
||||
let source = "let log=[];\
|
||||
let outerN=0;\
|
||||
const outer={[Symbol.asyncIterator](){return{next(){outerN++;if(outerN>2)return Promise.resolve({value:undefined,done:true});return Promise.resolve({value:outerN,done:false});},return(){log.push('cO');return Promise.resolve({done:true})}}}};\
|
||||
const inner={[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:1,done:false})},return(){log.push('cI');return Promise.resolve({done:true})}}}};\
|
||||
(async()=>{ outer: for await (const a of outer) { for await (const b of inner) { log.push('body'); continue outer; } log.push('after-inner'); } })();\
|
||||
".to_string()
|
||||
+ &delay_read(6);
|
||||
assert_eq!(eval(&source), "\"body,cI,body,cI\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_await_of_multi_layer_return_escape_lifo() {
|
||||
// E4:多层 return 逃出(两层 for-await-of)——LIFO 顺序关闭(内层先)。
|
||||
// 内层 return() 先被调(c2),外层后(c1),关闭顺序验证 LIFO。
|
||||
let source = "let log=[];\
|
||||
const mk=(n)=>({[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:n,done:false})},return(){log.push('c'+n);return Promise.resolve({done:true})}}}});\
|
||||
(async()=>{ for await (const a of mk(1)) { for await (const b of mk(2)) { log.push('body'); return 42; } } })();\
|
||||
".to_string()
|
||||
+ &delay_read(4);
|
||||
assert_eq!(eval(&source), "\"body,c2,c1\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_await_of_multi_layer_labeled_break_escape_lifo() {
|
||||
// E5:多层 labeled break 逃出——LIFO 顺序关闭(内层先),跳转目标正确。
|
||||
let source = "let log=[];\
|
||||
const mk=(n)=>({[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:n,done:false})},return(){log.push('c'+n);return Promise.resolve({done:true})}}}});\
|
||||
(async()=>{ outer: for await (const a of mk(1)) { for await (const b of mk(2)) { log.push('body'); break outer; } } log.push('after'); })();\
|
||||
".to_string()
|
||||
+ &delay_read(4);
|
||||
assert_eq!(eval(&source), "\"body,c2,c1,after\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_await_of_escape_close_reject_outer_catch() {
|
||||
// E6:逃出时 return() 拒绝——拒绝原因替代完成值,外围 catch 捕获。
|
||||
let source = "let log=[];\
|
||||
const it={[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:1,done:false})},return(){return Promise.reject('rej');}}}};\
|
||||
(async()=>{ try { for await (const x of it) { log.push('body'); return 1; } } catch (e) { log.push('caught:'+e); } log.push('done'); })();\
|
||||
".to_string()
|
||||
+ &delay_read(3);
|
||||
assert_eq!(eval(&source), "\"body,caught:rej,done\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_await_of_escape_no_return_method() {
|
||||
// E8:迭代器无 return 方法——不登记挂起,完成直接继续。
|
||||
let source = "let log=[];\
|
||||
const it={[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:1,done:false})}}}};\
|
||||
(async()=>{ for await (const x of it) { log.push('body'); return 1; } })();\
|
||||
"
|
||||
.to_string()
|
||||
+ &delay_read(2);
|
||||
assert_eq!(eval(&source), "\"body\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_await_of_escape_settlement_precedes_then() {
|
||||
// E10:微任务时序——结算闭包(执行完成)先于后续 then 回调。
|
||||
// break 逃出后,完成跳转(log.push('after'))在结算闭包内执行,须先于 then 回调。
|
||||
let source = "let log=[];\
|
||||
const it={[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:1,done:false})},return(){return Promise.resolve({done:true})}}}};\
|
||||
(async()=>{ for await (const x of it) { log.push('body'); break; } log.push('after'); })();\
|
||||
".to_string()
|
||||
+ &delay_read(3);
|
||||
assert_eq!(eval(&source), "\"body,after\"");
|
||||
}
|
||||
|
||||
// ── class/object 生成器方法(异步) ──
|
||||
|
||||
// class async 生成器方法:yield 顺序与 done 收敛。顶层 then 链驱动
|
||||
|
||||
Reference in New Issue
Block a user