Files
OxideJS-New/crates/oxide_emit/src/stmt/block.rs
T

91 lines
4.4 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.
//! 块语句 emit:`emit_block_statement` 压/弹作用域并顺序编译子语句(含域分发)。
use std::collections::HashMap;
use crate::{CompileCtx, Emitter};
use oxide_parser::Statement;
impl Emitter {
fn emit_block_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u32>, String> {
let Statement::BlockStatement(block) = stmt else {
return Ok(None);
};
ctx.push_scope();
// 函数预声明先于 lexical:`{ let g; function g(){} }` 时 lexical 的预声明
// 命中已存在的函数绑定自然报重复声明错,不被静默覆盖破坏 TDZ。
self.predeclare_block_function_declarations(&block.body, ctx, false);
// 块 lexical 声明是局部绑定:不做受限全局名检查(重复声明错在 emit 期报)。
let _ = self.predeclare_lexical_declarations(&block.body, ctx);
// 块级函数声明的块入口初始化:直接子(含标签直接子体)闭包物化入预
// 声明块槽,声明语句前读命中函数对象(sloppy 与 strict 同形);声明
// 点不再重编闭包,只处理外层 var 写回(见 emit_function_declaration)。
ctx.block_fn_entry_mats.push(HashMap::new());
for s in &block.body {
self.emit_block_fn_entry_init_stmt(s, ctx)?;
}
// 块语句列表独立累积:压列表帧,每条非空语句记最后非空值寄存器,
// break/continue 携值解析据此取块内累积值。
ctx.push_completion_list();
let mut r = None;
for s in &block.body {
if let Some(rr) = self.emit_statement(s, ctx)? {
r = Some(rr);
ctx.set_completion_last(rr);
}
}
ctx.pop_completion_list();
ctx.block_fn_entry_mats.pop();
ctx.pop_scope();
Ok(r)
}
/// 块入口初始化的单语句走法:函数声明就地物化并登记(同名重复声明共享
/// 块绑定,源序物化使末次声明天然成为入口值);标签不改变执行流(V8 对
/// 标签直接子的函数声明同样块入口可见),随标签体递归一层;if/while/
/// do-while/with 体不进入——支臂声明的执行期才生效(仅被求值支物化,
/// while/do-while/with 体直挂声明双侧均为语法错误不可达);嵌套块/循环/
/// try 各自压作用域、由其自身块入口处理,不进入。
///
/// # 前提
/// - 预声明已为各声明名建立块 Let 绑定;`materialize_function_declaration`
/// 经 `lookup` 解析槽位。
///
/// # 副作用
/// - 当前指令流在块入口发 CREATE_CLOSURE(及 MAKE_CELL / STORE_VAR);
/// 物化登记写入 `block_fn_entry_mats` 栈顶帧。
///
/// # 注意事项
/// - 同名重复的直接子声明只登记首个节点:该名任一直接子声明点都复用块槽
/// (取其当前值),而非重编本节点闭包——块绑定入口值按源序末次物化,
/// 复用槽才与外层 var 写回后的块槽值一致(仅登记末次会让较早声明点把
/// 本节点闭包写回外层 var,与规范的块绑定写回口径不符)。
pub(crate) fn emit_block_fn_entry_init_stmt(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<(), String> {
match stmt {
Statement::FunctionDeclaration(fd) => {
let Some(id) = &fd.id else {
return Ok(());
};
let name = id.name.to_string();
self.materialize_function_declaration(fd, &name, ctx)?;
// 登记声明节点 arena 地址(Box 解引到 arena 本体):声明点
// 以同一节点地址判同一,区分本声明入口物化与同名支臂。
ctx.block_fn_entry_mats
.last_mut()
.unwrap()
.entry(name)
.or_insert_with(|| &**fd as *const _ as *const ());
Ok(())
}
Statement::LabeledStatement(ls) => self.emit_block_fn_entry_init_stmt(&ls.body, ctx),
_ => Ok(()),
}
}
pub(crate) fn emit_block_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u32>, String> {
match stmt {
Statement::BlockStatement(_) => self.emit_block_statement(stmt, ctx),
_ => Ok(None),
}
}
}