fix: for-of/for-in 提前退出时按规范顺序完成 IteratorClose

return/break/continue 逃出 for-of 时此前完全不执行 IteratorClose:
BREAK/CONTINUE/RETURN 指令现在经 ext 字携带词法逃出的迭代器层数
(for-of + for-in 各 16 位),运行时在全部 finally 穿越之后、真正
跳转/返回之前按 LIFO 关闭——与规范 §13.7.5.4(循环体完成值已含
内部 try-finally 处理,IteratorClose 在后)顺序一致。

- emit 词法:LabelCtx 增加 for-of/for-in 深度计数,loop/switch/label
  打开时快照,break/continue/return 按当前深度减目标深度计算逃出层数
  (switch 内 break 以 switch 打开点为基准,目标循环自身不计入)
- 指令编码:brk/cont/ret 三参构造,lower 跳转路径补发 ext 字
- 运行时:dispatch_break/continue/return 读取逃出计数,无 finally 时
  直接关闭后跳转/返回,有 finally 时随 Completion 悬挂由 TRY_FINALLY_END
  在穿越完成后消费;生成器 .return()/.throw() 注入经 complete_generator_return
  全关快照栈迭代器;return() 抛错经 unwind 替代完成值并关闭剩余迭代器
- 顺带:for-in 逃出残留(无 return() 语义,仅弹栈);for-await-of 的
  异步迭代器不做同步关闭(return() promise 须 await,走异步机制)
- 新增 15 项回归测试:return/labeled break/continue/switch/while/finally
  顺序/抛错优先/生成器注入/for-in/for-await-of 嵌套
This commit is contained in:
2026-08-16 09:31:39 +08:00
parent b885b672b9
commit a8378694be
23 changed files with 893 additions and 204 deletions
+11 -11
View File
@@ -129,12 +129,7 @@ impl Emitter {
// accessor calls (which clear the register file). The synthetic key keeps user
// bindings with the same name (outer scopes) untouched.
let class_self_cell: Option<u8> = ctor_name.as_deref().map(|_| {
let cell_idx = ctx
.captured_bindings
.values()
.copied()
.max()
.map_or(0, |m| m.saturating_add(1));
let cell_idx = ctx.captured_bindings.values().copied().max().map_or(0, |m| m.saturating_add(1));
ctx.captured_bindings.insert(format!("@@class_self_{cell_idx}"), cell_idx);
cell_idx
});
@@ -302,9 +297,7 @@ impl Emitter {
module.insts.extend(field_ctx.insts);
module.constants = field_ctx.constants;
module.n_registers = field_ctx.max_regs.max(1);
module
.insts
.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None));
module.insts.push(Inst::ret(Operand::None, 0, 0));
}
module
};
@@ -377,10 +370,17 @@ impl Emitter {
Operand::None,
));
}
self.emit_class_methods(&class.body.body, ctor_reg, proto_reg, &self_binding, class_self_cell, &key_slots, ctx)?;
self.emit_class_methods(
&class.body.body,
ctor_reg,
proto_reg,
&self_binding,
class_self_cell,
&key_slots,
ctx,
)?;
self.emit_class_static_elements(&class.body.body, ctor_reg, &key_slots, ctx)?;
// 类构建完成:初始化类名绑定并写入类构造器(类内方法引用该寄存器/捕获)。
if let Some(name) = ctor_name.as_deref() {
ctx.init_var(name);
+54 -11
View File
@@ -24,7 +24,7 @@ pub fn is_anonymous_function_definition(expr: &oxide_parser::Expression) -> bool
}
}
use crate::emit_ctx::{LabelCtx, ScopeCtx};
use crate::emit_ctx::{LabelCtx, LoopEntry, LoopKind, ScopeCtx};
use crate::symbol_table::{Binding, ScopeKind, SymbolTable};
/// 常量池项(bytecode module 类型)re-export,供调用方构造常量。
@@ -144,6 +144,10 @@ pub struct LabelScope {
pub(crate) continue_label: Option<LabelId>,
/// 标签打开时嵌套的 finally 域数:break/continue 跨越 finally 的计数依据。
pub(crate) finally_depth_at_open: usize,
/// 标签打开时已打开的 for-of 循环数:break/continue 逃出计数依据。
pub(crate) for_of_depth_at_open: usize,
/// 标签打开时已打开的 for-in 循环数。
pub(crate) for_in_depth_at_open: usize,
}
/// 单函数编译上下文:执行流 + 作用域 + 闭包捕获的聚合状态。
@@ -283,6 +287,8 @@ impl CompileCtx {
label_scopes: Vec::new(),
pending_loop_labels: Vec::new(),
finally_depth: 0,
for_of_depth: 0,
for_in_depth: 0,
label_counter: 0,
},
scopes: ScopeCtx {
@@ -456,29 +462,55 @@ impl CompileCtx {
id
}
pub(crate) fn push_loop(&mut self, break_label: LabelId, continue_label: LabelId) {
pub(crate) fn push_loop(&mut self, break_label: LabelId, continue_label: LabelId, kind: LoopKind) {
let fd = self.labels.finally_depth;
self.labels.loop_stack.push((break_label, continue_label, fd));
// 深度计数先递增再快照:条目记录"打开后(含自身)"的深度,与
// take_pending_loop_labels 的标签作用域快照一致,逃出计数才能对齐。
if kind.is_for_of() {
self.labels.for_of_depth += 1;
}
if kind.is_for_in() {
self.labels.for_in_depth += 1;
}
let fod = self.labels.for_of_depth;
let fid = self.labels.for_in_depth;
self.labels.loop_stack.push(LoopEntry {
break_label,
continue_label,
finally_depth_at_open: fd,
for_of_depth_at_open: fod,
for_in_depth_at_open: fid,
kind,
});
}
pub(crate) fn pop_loop(&mut self) {
self.labels.loop_stack.pop();
if let Some(entry) = self.labels.loop_stack.pop() {
if entry.kind.is_for_of() {
self.labels.for_of_depth -= 1;
}
if entry.kind.is_for_in() {
self.labels.for_in_depth -= 1;
}
}
}
pub(crate) fn current_loop(&self) -> Option<&(LabelId, LabelId, usize)> {
pub(crate) fn current_loop(&self) -> Option<&LoopEntry> {
self.labels.loop_stack.last()
}
pub(crate) fn push_switch(&mut self, break_label: LabelId) {
let fd = self.labels.finally_depth;
self.labels.switch_stack.push((break_label, fd));
let fod = self.labels.for_of_depth;
let fid = self.labels.for_in_depth;
self.labels.switch_stack.push((break_label, fd, fod, fid));
}
pub(crate) fn pop_switch(&mut self) {
self.labels.switch_stack.pop();
}
pub(crate) fn current_switch(&self) -> Option<&(LabelId, usize)> {
pub(crate) fn current_switch(&self) -> Option<&(LabelId, usize, usize, usize)> {
self.labels.switch_stack.last()
}
@@ -521,11 +553,15 @@ impl CompileCtx {
return Err(format!("SyntaxError: Label '{name}' has already been declared"));
}
let fd = self.labels.finally_depth;
let fod = self.labels.for_of_depth;
let fid = self.labels.for_in_depth;
self.labels.label_scopes.push(LabelScope {
name: name.to_string(),
break_label,
continue_label,
finally_depth_at_open: fd,
for_of_depth_at_open: fod,
for_in_depth_at_open: fid,
});
Ok(())
}
@@ -556,12 +592,16 @@ impl CompileCtx {
let names = std::mem::take(&mut self.labels.pending_loop_labels);
let count = names.len();
let fd = self.labels.finally_depth;
let fod = self.labels.for_of_depth;
let fid = self.labels.for_in_depth;
for name in names {
self.labels.label_scopes.push(LabelScope {
name,
break_label,
continue_label: Some(continue_label),
finally_depth_at_open: fd,
for_of_depth_at_open: fod,
for_in_depth_at_open: fid,
});
}
count
@@ -627,7 +667,9 @@ impl CompileCtx {
/// 组装 IRFunction(两出口共用),take 走编译产物状态。
/// `parent_ctx` 用于补全 upvalue_captures 的 enclosing_reg(父符号表在父 emit 完成后完整)。
pub(crate) fn assemble_ir(&mut self, param_layout: oxide_ir::ParamLayout, parent_ctx: Option<&CompileCtx>) -> IRFunction {
pub(crate) fn assemble_ir(
&mut self, param_layout: oxide_ir::ParamLayout, parent_ctx: Option<&CompileCtx>,
) -> IRFunction {
let upvalue_captures = self
.current_upvalue_captures
.iter()
@@ -1089,20 +1131,21 @@ impl Emitter {
let last_result_reg = self.emit_body_stmts(body_stmts, &mut ctx)?;
// 隐式 RETURN:表达式体返回最后表达式,语句体返回 undefined。
// 函数尾词法上不在任何循环内,迭代器逃出计数恒 0。
if is_expression_body {
if let Some(reg) = last_result_reg {
ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(reg), Operand::None, Operand::None));
ctx.inst(Inst::ret(Operand::Reg(reg), 0, 0));
} else {
let undef_idx = ctx.add_constant(Constant::Undefined);
let undef_reg = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(undef_reg), undef_idx));
ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(undef_reg), Operand::None, Operand::None));
ctx.inst(Inst::ret(Operand::Reg(undef_reg), 0, 0));
}
} else {
let undef_idx = ctx.add_constant(Constant::Undefined);
let undef_reg = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(undef_reg), undef_idx));
ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(undef_reg), Operand::None, Operand::None));
ctx.inst(Inst::ret(Operand::Reg(undef_reg), 0, 0));
}
// 调用契约参数段只含固定形参:rest 是函数体内普通变量,不在 VM 实参传递区。
+48 -4
View File
@@ -10,20 +10,64 @@ use crate::LabelScope;
use oxide_ir::operand::LabelId;
use oxide_parser::MethodDefinitionKind;
/// 循环语句类别:决定逃出计数是否计入 for-of / for-in 迭代器关闭。
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum LoopKind {
/// while / do-while / C 风格 for:无迭代器关闭语义。
Plain,
ForOf,
ForAwaitOf,
ForIn,
}
impl LoopKind {
/// 是否计入 for-of 逃出关闭计数。for-await-of 的逃出(labeled break /
/// continue / return)需要异步 await 迭代器 return() 的 promise,走
/// 异步挂起机制另行实现——此处只计同步 for-of,避免运行时同步调用异步
/// 迭代器的 return()。
pub(crate) fn is_for_of(self) -> bool {
matches!(self, LoopKind::ForOf)
}
pub(crate) fn is_for_in(self) -> bool {
matches!(self, LoopKind::ForIn)
}
}
/// 循环打开时的词法快照:break/continue/return 逃出时据此计算需关闭的迭代器层数。
#[derive(Debug, Clone, Copy)]
pub(crate) struct LoopEntry {
pub(crate) break_label: LabelId,
pub(crate) continue_label: LabelId,
/// 循环打开时嵌套的 finally 域数(finally 逃出计数用)。
pub(crate) finally_depth_at_open: usize,
/// 循环打开时已打开的 for-of/for-await-of 循环数。
pub(crate) for_of_depth_at_open: usize,
/// 循环打开时已打开的 for-in 循环数。
pub(crate) for_in_depth_at_open: usize,
pub(crate) kind: LoopKind,
}
/// 跳转目标 / 标签语句解析状态。
pub(crate) struct LabelCtx {
/// label id → 指令下标。id 连续递增,Vec 索引即 id;写入前须扩容。
pub(crate) label_pos: Vec<Option<usize>>,
/// 每个条目记录循环打开时嵌套的 finally 域数(break/continue 跨越 finally 计数用)。
pub(crate) loop_stack: Vec<(LabelId, LabelId, usize)>,
/// 每个条目记录 switch 打开时嵌套的 finally 域数。
pub(crate) switch_stack: Vec<(LabelId, usize)>,
/// 每个条目记录循环打开时的词法快照(finally/for-of/for-in 深度)。
pub(crate) loop_stack: Vec<LoopEntry>,
/// 每个条目记录 switch 打开时的词法快照(finally/for-of/for-in 深度):
/// switch 内 break 的逃出计数以打开点为准,switch 之前已打开的迭代器
/// 不属于本次逃出。
pub(crate) switch_stack: Vec<(LabelId, usize, usize, usize)>,
/// 活动标签语句作用域(解析 `break label` / `continue label`)。
pub(crate) label_scopes: Vec<LabelScope>,
/// 等待绑定到下一个循环 continue 目标的标签名。
pub(crate) pending_loop_labels: Vec<String>,
/// 当前打开(正在 emit)的 try/finally 域数。
pub(crate) finally_depth: usize,
/// 当前打开的 for-of/for-await-of 循环数(逃出计数基数)。
pub(crate) for_of_depth: usize,
/// 当前打开的 for-in 循环数(逃出计数基数)。
pub(crate) for_in_depth: usize,
pub(crate) label_counter: u32,
}
+53 -34
View File
@@ -130,8 +130,7 @@ impl Emitter {
/// 编译 ES module:顶层 body + 递归依赖。
/// `module_path` 为模块文件的规范路径(依赖解析基准 = 其父目录)。
pub fn emit_program_module(
&self, program: &oxide_parser::Program, module_path: &str,
loader: &mut dyn ModuleSourceLoader,
&self, program: &oxide_parser::Program, module_path: &str, loader: &mut dyn ModuleSourceLoader,
) -> Result<IRFunction, String> {
crate::emit_debug!("emit_program_module: {} stmts", program.body.len());
let mut ctx = CompileCtx::new();
@@ -145,8 +144,8 @@ impl Emitter {
/// 模块体 emit(顶层与依赖模块共用入口)。
#[allow(clippy::too_many_arguments)]
fn emit_module_into_ctx(
&self, program: &oxide_parser::Program, module_path: &str,
loader: &mut dyn ModuleSourceLoader, path_stack: &mut Vec<String>, ctx: &mut CompileCtx,
&self, program: &oxide_parser::Program, module_path: &str, loader: &mut dyn ModuleSourceLoader,
path_stack: &mut Vec<String>, ctx: &mut CompileCtx,
) -> Result<(), String> {
let body = &program.body;
@@ -249,16 +248,18 @@ impl Emitter {
let imported = module_export_name_str(&s.imported);
if is_self {
if !own_export_names.contains(&imported) && !has_unnamed_star {
return Err(format!(
"requested module export is not exported: {imported}"
));
return Err(format!("requested module export is not exported: {imported}"));
}
let undef_idx = ctx.add_constant(Constant::Undefined);
let undef_reg = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(undef_reg), undef_idx));
self.emit_bind_target(
s.local.name.as_str(), undef_reg, VariableDeclarationKind::Const, true,
false, ctx,
s.local.name.as_str(),
undef_reg,
VariableDeclarationKind::Const,
true,
false,
ctx,
)?;
if let Ok(reg) = ctx.lookup(s.local.name.as_str()) {
ctx.module_self_aliases.insert(imported, reg);
@@ -268,7 +269,11 @@ impl Emitter {
let val_reg =
self.emit_module_call(ctx, "__moduleLinkGet", &[dep_ns_reg, name_reg])?;
self.emit_bind_target(
s.local.name.as_str(), val_reg, VariableDeclarationKind::Const, true, false,
s.local.name.as_str(),
val_reg,
VariableDeclarationKind::Const,
true,
false,
ctx,
)?;
}
@@ -282,8 +287,12 @@ impl Emitter {
let undef_reg = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(undef_reg), undef_idx));
self.emit_bind_target(
s.local.name.as_str(), undef_reg, VariableDeclarationKind::Const, true,
false, ctx,
s.local.name.as_str(),
undef_reg,
VariableDeclarationKind::Const,
true,
false,
ctx,
)?;
if let Ok(reg) = ctx.lookup(s.local.name.as_str()) {
ctx.module_self_aliases.insert("default".to_string(), reg);
@@ -293,7 +302,11 @@ impl Emitter {
let val_reg =
self.emit_module_call(ctx, "__moduleLinkGet", &[dep_ns_reg, name_reg])?;
self.emit_bind_target(
s.local.name.as_str(), val_reg, VariableDeclarationKind::Const, true, false,
s.local.name.as_str(),
val_reg,
VariableDeclarationKind::Const,
true,
false,
ctx,
)?;
}
@@ -302,7 +315,11 @@ impl Emitter {
// 命名空间绑定引用 ns 对象本身:自导入时同一对象,
// body 执行后导出自然可见。
self.emit_bind_target(
s.local.name.as_str(), dep_ns_reg, VariableDeclarationKind::Const, true, false,
s.local.name.as_str(),
dep_ns_reg,
VariableDeclarationKind::Const,
true,
false,
ctx,
)?;
}
@@ -353,14 +370,13 @@ impl Emitter {
// —— 收尾:封冻命名空间并返回(顶层 RETURN 亦终止 run)——
self.emit_module_call(ctx, "__moduleSeal", &[ns_reg])?;
ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(ns_reg), Operand::None, Operand::None));
ctx.inst(Inst::ret(Operand::Reg(ns_reg), 0, 0));
Ok(())
}
/// 编译 JS 依赖模块(递归)。
fn compile_js_dep(
&self, resolved: &ResolvedModule, loader: &mut dyn ModuleSourceLoader,
path_stack: &mut Vec<String>,
&self, resolved: &ResolvedModule, loader: &mut dyn ModuleSourceLoader, path_stack: &mut Vec<String>,
) -> Result<IRFunction, String> {
let alloc = oxide_parser::Allocator::default();
let program = oxide_parser::parse_module(&alloc, &resolved.source).map_err(|errs| {
@@ -381,25 +397,19 @@ impl Emitter {
let kind_reg = self.load_string_const(kind, &mut ctx);
let content_reg = self.load_string_const(content, &mut ctx);
let val_reg = self.emit_module_call(&mut ctx, "__moduleData", &[kind_reg, content_reg])?;
ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(val_reg), Operand::None, Operand::None));
ctx.inst(Inst::ret(Operand::Reg(val_reg), 0, 0));
Ok(ctx.assemble_ir(ParamLayout { base: 0, count: 0 }, None))
}
/// 内部辅助调用:CALL_NATIVE 到全局 native(__module*),返回结果寄存器。
pub(crate) fn emit_module_call(
&self, ctx: &mut CompileCtx, name: &str, arg_regs: &[u32],
) -> Result<u32, String> {
pub(crate) fn emit_module_call(&self, ctx: &mut CompileCtx, name: &str, arg_regs: &[u32]) -> Result<u32, String> {
let callee_reg = ctx.lookup_or_builtin(name)?;
let this_reg = ctx.alloc_reg();
let undef_idx = ctx.add_constant(Constant::Undefined);
ctx.inst(Inst::load_const(Operand::Reg(this_reg), undef_idx));
// CALL_NATIVE 按 regs[first_arg + i] 连续读参数:把非连续 vreg 打包为连续块。
let mut packed = arg_regs.to_vec();
let first_arg_reg = if packed.is_empty() {
this_reg
} else {
pack_arg_regs(&mut packed, ctx)
};
let first_arg_reg = if packed.is_empty() { this_reg } else { pack_arg_regs(&mut packed, ctx) };
ctx.inst(Inst::call_native(
Operand::Reg(callee_reg),
Operand::Reg(this_reg),
@@ -448,9 +458,7 @@ impl Emitter {
pub(crate) fn emit_module_export_domain(
&self, stmt: &Statement, ctx: &mut CompileCtx,
) -> Result<Option<u32>, String> {
let ns_reg = ctx
.module_ns_reg
.ok_or_else(|| "export outside module context".to_string())?;
let ns_reg = ctx.module_ns_reg.ok_or_else(|| "export outside module context".to_string())?;
match stmt {
Statement::ExportNamedDeclaration(exp) => {
if let Some(decl) = &exp.declaration {
@@ -487,9 +495,10 @@ impl Emitter {
_ => return Err("unsupported export declaration".into()),
}
} else {
let dep_ns_opt = exp.source.as_ref().map(|src| {
ctx.module_dep_ns_regs.get(src.value.as_str()).copied()
});
let dep_ns_opt = exp
.source
.as_ref()
.map(|src| ctx.module_dep_ns_regs.get(src.value.as_str()).copied());
for spec in &exp.specifiers {
let local_name = module_export_name_str(&spec.local);
let exported_name = module_export_name_str(&spec.exported);
@@ -516,7 +525,12 @@ impl Emitter {
let reg = self.emit_function_expression(fd, ctx)?;
if let Some(id) = &fd.id {
self.emit_bind_target(
id.name.as_str(), reg, VariableDeclarationKind::Const, true, false, ctx,
id.name.as_str(),
reg,
VariableDeclarationKind::Const,
true,
false,
ctx,
)?;
}
reg
@@ -525,7 +539,12 @@ impl Emitter {
let reg = self.emit_class_expression(cl, ctx)?;
if let Some(id) = &cl.id {
self.emit_bind_target(
id.name.as_str(), reg, VariableDeclarationKind::Const, true, false, ctx,
id.name.as_str(),
reg,
VariableDeclarationKind::Const,
true,
false,
ctx,
)?;
}
reg
@@ -1,8 +1,9 @@
//! break/continue 语句 emit:解析标签与最近循环的跳转目标。
//!
//! 计算 break/continue 逃出的 finally 域数(目标打开时的 finally 深度与当前深度之差):
//! 逃出 ≥1 个 finally → 发 BREAK/CONTINUE(运行时穿越 finally 后再跳转);
//! 不逃出(目标在 try 域内,如 switch/循环内部的 break)→ 发普通 JMP。
//! 计算 break/continue 逃出的 finally 域数(目标打开时的 finally 深度与当前深度之差)
//! 与逃出的迭代器层数(for-of/for-in 深度之差,目标循环自身的关闭由 CLOSE /
//! FOR_IN_CLEANUP 处理,不计入):逃出 ≥1 个 finally 域或 ≥1 层迭代器 → 发
//! BREAK/CONTINUE(运行时穿越 finally、关闭逃出迭代器后再跳转);否则发普通 JMP。
use crate::{CompileCtx, Emitter};
use oxide_ir::inst::Inst;
@@ -11,21 +12,35 @@ impl Emitter {
pub(crate) fn emit_break_statement(
&self, stmt: &oxide_parser::BreakStatement, ctx: &mut CompileCtx,
) -> Result<Option<u32>, String> {
let (break_label, fd_at_open) = if let Some(label) = &stmt.label {
let (break_label, fd_at_open, fod_at_open, fid_at_open) = if let Some(label) = &stmt.label {
let name = label.name.as_str();
let scope = ctx
.find_label(name)
.ok_or_else(|| format!("SyntaxError: Undefined label '{name}'"))?;
(scope.break_label, scope.finally_depth_at_open)
} else if let Some((sw_label, fd)) = ctx.current_switch() {
(*sw_label, *fd)
(
scope.break_label,
scope.finally_depth_at_open,
scope.for_of_depth_at_open,
scope.for_in_depth_at_open,
)
} else if let Some((sw_label, fd, fod, fid)) = ctx.current_switch() {
// switch 内 break:目标为 switch 出口。逃出计数以 switch 打开时的
// 深度快照为基准——switch 之前已打开的循环不属于本次逃出,仅当
// case 内新打开了迭代循环(并逃出)时才需关闭。
(*sw_label, *fd, *fod, *fid)
} else {
let (bl, _, fd) = ctx.current_loop().ok_or("break outside switch or loop".to_string())?;
(*bl, *fd)
let entry = ctx.current_loop().ok_or("break outside switch or loop".to_string())?;
(
entry.break_label,
entry.finally_depth_at_open,
entry.for_of_depth_at_open,
entry.for_in_depth_at_open,
)
};
let crossed = ctx.labels.finally_depth.saturating_sub(fd_at_open) as u16;
if crossed > 0 {
ctx.inst(Inst::brk(break_label, crossed));
let (for_of_count, for_in_count) = escape_iter_counts(ctx, fod_at_open, fid_at_open);
if crossed > 0 || for_of_count > 0 || for_in_count > 0 {
ctx.inst(Inst::brk(break_label, crossed, for_of_count, for_in_count));
} else {
ctx.inst(Inst::jmp(break_label));
}
@@ -35,7 +50,7 @@ impl Emitter {
pub(crate) fn emit_continue_statement(
&self, stmt: &oxide_parser::ContinueStatement, ctx: &mut CompileCtx,
) -> Result<Option<u32>, String> {
let (continue_label, fd_at_open) = if let Some(label) = &stmt.label {
let (continue_label, fd_at_open, fod_at_open, fid_at_open) = if let Some(label) = &stmt.label {
let name = label.name.as_str();
let scope = ctx
.find_label(name)
@@ -45,17 +60,37 @@ impl Emitter {
format!("SyntaxError: Illegal continue statement: '{name}' does not denote an iteration statement")
})?,
scope.finally_depth_at_open,
scope.for_of_depth_at_open,
scope.for_in_depth_at_open,
)
} else {
let (_, cl, fd) = ctx.current_loop().ok_or("continue outside loop".to_string())?;
(*cl, *fd)
let entry = ctx.current_loop().ok_or("continue outside loop".to_string())?;
(
entry.continue_label,
entry.finally_depth_at_open,
entry.for_of_depth_at_open,
entry.for_in_depth_at_open,
)
};
let crossed = ctx.labels.finally_depth.saturating_sub(fd_at_open) as u16;
if crossed > 0 {
ctx.inst(Inst::cont(continue_label, crossed));
let (for_of_count, for_in_count) = escape_iter_counts(ctx, fod_at_open, fid_at_open);
if crossed > 0 || for_of_count > 0 || for_in_count > 0 {
ctx.inst(Inst::cont(continue_label, crossed, for_of_count, for_in_count));
} else {
ctx.inst(Inst::jmp(continue_label));
}
Ok(None)
}
}
/// 计算逃出目标循环需要关闭的 for-of / for-in 迭代器层数。
///
/// 深度快照(`fod_at_open`/`fid_at_open`)取目标循环打开后(含自身)的深度:
/// 当前深度与之差 = 被逃出的中间层数。目标循环自身的关闭由循环收尾指令
/// (FOR_OF_CLOSE / FOR_IN_CLEANUP)处理,不计入——break 的跳转目标在收尾指令
/// 之后(含其 CLOSE),continue 则目标循环继续迭代。
fn escape_iter_counts(ctx: &CompileCtx, fod_at_open: usize, fid_at_open: usize) -> (usize, usize) {
let for_of_count = ctx.labels.for_of_depth.saturating_sub(fod_at_open);
let for_in_count = ctx.labels.for_in_depth.saturating_sub(fid_at_open);
(for_of_count, for_in_count)
}
@@ -11,6 +11,9 @@ impl Emitter {
let Statement::ReturnStatement(ret) = stmt else {
return Ok(None);
};
// return 逃出全部打开的迭代循环:for-of 需 IteratorClose,for-in 弹出迭代器。
let for_of_count = ctx.labels.for_of_depth;
let for_in_count = ctx.labels.for_in_depth;
match &ret.argument {
Some(expr) => {
let r = self.emit_expression(expr, ctx)?;
@@ -18,12 +21,18 @@ impl Emitter {
// 弹出连续纯 catch handler(finally handler 走运行时完成穿越逐个
// 执行)。否则 return 跳过 TRY_END 会让 handler 残留在 try_stack,
// 后续异常 unwind 会跳回已返回函数的 catch 形成死循环。
self.emit_return_try_cleanup(ctx);
ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(r), Operand::None, Operand::None));
// 逃出迭代器时(close_count>0)保留 handler:return() 抛错须经
// unwind 被外围 catch 捕获(新错误替代完成值),弹出后无法展开。
if for_of_count == 0 && for_in_count == 0 {
self.emit_return_try_cleanup(ctx);
}
ctx.inst(Inst::ret(Operand::Reg(r), for_of_count, for_in_count));
}
None => {
self.emit_return_try_cleanup(ctx);
ctx.inst(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None));
if for_of_count == 0 && for_in_count == 0 {
self.emit_return_try_cleanup(ctx);
}
ctx.inst(Inst::ret(Operand::None, for_of_count, for_in_count));
}
}
Ok(None)
@@ -14,7 +14,7 @@ impl Emitter {
let start_label = ctx.next_label_id();
let end_label = ctx.next_label_id();
ctx.labels.set_label_pos(start_label, ctx.insts.len());
ctx.push_loop(end_label, start_label);
ctx.push_loop(end_label, start_label, crate::emit_ctx::LoopKind::Plain);
let n_labeled = ctx.take_pending_loop_labels(end_label, start_label);
self.emit_statement(&dw.body, ctx)?;
let test_reg = self.emit_expression(&dw.test, ctx)?;
+1 -1
View File
@@ -40,7 +40,7 @@ impl Emitter {
let start_label = ctx.next_label_id();
let update_label = ctx.next_label_id();
let end_label = ctx.next_label_id();
ctx.push_loop(end_label, update_label);
ctx.push_loop(end_label, update_label, crate::emit_ctx::LoopKind::Plain);
let n_labeled = ctx.take_pending_loop_labels(end_label, update_label);
// 循环头声明中被嵌套函数捕获的 let/const 绑定:每迭代 fresh cell。
let mut fresh_bindings: Vec<(String, u8)> = Vec::new();
@@ -17,7 +17,7 @@ impl Emitter {
let obj_reg = self.emit_expression(&fi.right, ctx)?;
ctx.inst(Inst::new(OpCode::FOR_IN_INIT, Operand::None, Operand::Reg(obj_reg), Operand::None));
ctx.labels.set_label_pos(start_label, ctx.insts.len());
ctx.push_loop(end_label, start_label);
ctx.push_loop(end_label, start_label, crate::emit_ctx::LoopKind::ForIn);
let n_labeled = ctx.take_pending_loop_labels(end_label, start_label);
let done_reg = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::FOR_IN_DONE, Operand::Reg(done_reg), Operand::None, Operand::None));
@@ -29,7 +29,7 @@ impl Emitter {
let iter_src_reg = self.emit_expression(&fo.right, ctx)?;
ctx.inst(Inst::new(OpCode::FOR_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);
ctx.push_loop(end_label, start_label, crate::emit_ctx::LoopKind::ForOf);
let n_labeled = ctx.take_pending_loop_labels(end_label, start_label);
let has_reg = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::FOR_OF_DONE, Operand::Reg(has_reg), Operand::None, Operand::None));
@@ -64,7 +64,7 @@ impl Emitter {
Operand::None,
));
ctx.labels.set_label_pos(start_label, ctx.insts.len());
ctx.push_loop(end_label, start_label);
ctx.push_loop(end_label, start_label, crate::emit_ctx::LoopKind::ForAwaitOf);
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));
@@ -12,7 +12,7 @@ impl Emitter {
let start_label = ctx.next_label_id();
let end_label = ctx.next_label_id();
ctx.labels.set_label_pos(start_label, ctx.insts.len());
ctx.push_loop(end_label, start_label);
ctx.push_loop(end_label, start_label, crate::emit_ctx::LoopKind::Plain);
let n_labeled = ctx.take_pending_loop_labels(end_label, start_label);
let test_reg = self.emit_expression(&wh.test, ctx)?;
ctx.inst(Inst::jmp_if_false(test_reg, end_label));