feat: for-await-of + Symbol.asyncIterator + AsyncFromSyncIterator

This commit is contained in:
2026-08-10 03:21:37 +08:00
parent 5ced83e8c0
commit a5a910a24e
15 changed files with 696 additions and 16 deletions
+68 -10
View File
@@ -1,16 +1,28 @@
//! for-of 语句 emit:`emit_for_of_statement` 迭代器协议遍历。
//! for-of 与 for-await-of 语句 emit。
//!
//! 同步 for-of 用 FOR_OF_* 指令;`for await` 用 FOR_AWAIT_OF_*:初始化取异步
//! 迭代器(缺失 `@@asyncIterator` 时回退 AsyncFromSyncIterator),每步
//! `next()` 结果经 `AWAIT` 挂起后检查 done。体内 await 语义由 `AWAIT` 指令
//! 原生提供(每次迭代都 await)。
use crate::{CompileCtx, Emitter};
use oxide_bytecode::opcode::OpCode;
use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand;
use oxide_parser::{ForStatementLeft, Statement};
use oxide_parser::{ForOfStatement, ForStatementLeft, Statement};
impl Emitter {
pub(crate) fn emit_for_of_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u32>, String> {
let Statement::ForOfStatement(fo) = stmt else {
return Ok(None);
};
if fo.r#await {
return self.emit_for_await_of_statement(fo, ctx);
}
self.emit_sync_for_of_statement(fo, ctx)
}
fn emit_sync_for_of_statement(&self, fo: &ForOfStatement, ctx: &mut CompileCtx) -> Result<Option<u32>, String> {
let start_label = ctx.next_label_id();
let end_label = ctx.next_label_id();
let iter_src_reg = self.emit_expression(&fo.right, ctx)?;
@@ -23,7 +35,59 @@ impl Emitter {
ctx.inst(Inst::jmp_if_false(has_reg, end_label));
let val_reg = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::FOR_OF_NEXT, Operand::Reg(val_reg), Operand::None, Operand::None));
match &fo.left {
self.emit_for_of_left_assignment(&fo.left, val_reg, ctx)?;
self.emit_statement(&fo.body, ctx)?;
ctx.inst(Inst::jmp(start_label));
ctx.labels.set_label_pos(end_label, ctx.insts.len());
ctx.inst(Inst::new(OpCode::FOR_OF_CLOSE, Operand::None, Operand::None, Operand::None));
ctx.pop_label_scopes(n_labeled);
ctx.pop_loop();
Ok(None)
}
/// for-await-of:异步迭代器协议遍历,每步经 `AWAIT` 挂起等待 next() 结果。
///
/// 指令序列:INIT 取异步迭代器 → 循环顶 NEXT 调 next() 写 promise → `AWAIT`
/// 挂起(恢复值经 reg 0 交付)→ LOAD_VAR 拷贝为结果对象 → DONE 检查 done →
/// FOR_OF_NEXT 读 value → 绑定左侧 → 循环体 → CLOSE 异步收尾。
fn emit_for_await_of_statement(&self, fo: &ForOfStatement, ctx: &mut CompileCtx) -> Result<Option<u32>, String> {
let start_label = ctx.next_label_id();
let end_label = ctx.next_label_id();
let iter_src_reg = self.emit_expression(&fo.right, ctx)?;
ctx.inst(Inst::new(OpCode::FOR_AWAIT_OF_INIT, Operand::None, Operand::Reg(iter_src_reg), Operand::None));
ctx.labels.set_label_pos(start_label, ctx.insts.len());
ctx.push_loop(end_label, start_label);
let n_labeled = ctx.take_pending_loop_labels(end_label, start_label);
let next_reg = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::FOR_AWAIT_OF_NEXT, Operand::Reg(next_reg), Operand::None, Operand::None));
ctx.inst(Inst::await_expr(Operand::Reg(next_reg)));
let result_reg = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::None, Operand::None));
let has_reg = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::FOR_AWAIT_OF_DONE,
Operand::Reg(has_reg),
Operand::Reg(result_reg),
Operand::None,
));
ctx.inst(Inst::jmp_if_false(has_reg, end_label));
let val_reg = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::FOR_OF_NEXT, Operand::Reg(val_reg), Operand::None, Operand::None));
self.emit_for_of_left_assignment(&fo.left, val_reg, ctx)?;
self.emit_statement(&fo.body, ctx)?;
ctx.inst(Inst::jmp(start_label));
ctx.labels.set_label_pos(end_label, ctx.insts.len());
ctx.inst(Inst::new(OpCode::FOR_AWAIT_OF_CLOSE, Operand::None, Operand::None, Operand::None));
ctx.pop_label_scopes(n_labeled);
ctx.pop_loop();
Ok(None)
}
/// for-of/for-await-of 左侧绑定:把当前迭代值 `val_reg` 写入声明/赋值目标。
fn emit_for_of_left_assignment(
&self, left: &ForStatementLeft, val_reg: u32, ctx: &mut CompileCtx,
) -> Result<(), String> {
match left {
ForStatementLeft::VariableDeclaration(decl) => {
for d in &decl.declarations {
self.emit_binding_pattern(&d.id, val_reg, decl.kind, false, ctx)?;
@@ -42,12 +106,6 @@ impl Emitter {
}
_ => return Err("unsupported for-of left-hand side".into()),
}
self.emit_statement(&fo.body, ctx)?;
ctx.inst(Inst::jmp(start_label));
ctx.labels.set_label_pos(end_label, ctx.insts.len());
ctx.inst(Inst::new(OpCode::FOR_OF_CLOSE, Operand::None, Operand::None, Operand::None));
ctx.pop_label_scopes(n_labeled);
ctx.pop_loop();
Ok(None)
Ok(())
}
}