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:
@@ -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);
|
||||
|
||||
@@ -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 实参传递区。
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user