Files
OxideJS-New/crates/oxide_emit/src/compile_ctx.rs
T

1087 lines
55 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.
//! `CompileCtx` 中心状态族:单函数编译上下文与其常量池键、类字段缓冲。
//!
//! 执行流字段(insts/registers/constants/labels)平铺在 `CompileCtx` 上,标识符
//! 绑定与闭包捕获分别下沉到 `SymbolTable` / `captured_bindings`。`assemble_ir`
//! 组装产出分域组合的 `IRFunction`,由 `oxide_ir::lower` 降为 bytecode。
use std::collections::{BTreeMap, HashMap, HashSet};
use oxide_bytecode::module::{Constant, UpvalueCapture};
use oxide_bytecode::opcode::OpCode;
use oxide_ir::inst::Inst;
use oxide_ir::operand::{LabelId, Operand};
use oxide_ir::IRFunction;
use crate::emit_ctx::{
CompletionCarry, CompletionFrame, LabelCtx, LabelScope, LoopEntry, LoopKind, ScopeCtx, SwitchEntry,
};
use crate::program::{BUILTIN_GLOBALS, NON_WRITABLE_GLOBAL_BUILTINS};
use crate::symbol_table::{ScopeKind, SymbolTable};
use oxide_parser::VariableDeclarationKind;
pub(crate) struct FieldBuffer {
pub(crate) insts: Vec<Inst>,
pub(crate) labels: Vec<(LabelId, usize)>,
}
/// 单函数编译上下文:执行流 + 作用域 + 闭包捕获的聚合状态。
/// 指令、常量池、寄存器分配平铺于此,作用域/绑定见 `ScopeCtx`,跳转见 `LabelCtx`。
pub struct CompileCtx {
pub(crate) insts: Vec<Inst>,
pub(crate) constants: Vec<Constant>,
constant_map: HashMap<ConstantKey, u16>,
pub(crate) next_reg: u32,
pub(crate) max_regs: u32,
pub(crate) reserved_reg_start: u32,
pub(crate) labels: LabelCtx,
pub(crate) scopes: ScopeCtx,
pub(crate) nested: Vec<IRFunction>,
/// 外层函数上下文中持有 `this` 的寄存器。
/// 箭头函数用它捕获词法 `this`;顶层初始化为 254(约定 this 寄存器)。
pub(crate) enclosing_this_reg: u8,
pub(crate) in_derived_constructor: bool,
pub(crate) in_instance_method: bool,
pub(crate) in_static_method: bool,
/// 本函数是否为生成器函数体(`function*`),`assemble_ir` 回写到 IR。
pub(crate) is_generator: bool,
/// 本函数是否为异步函数体(`async function` / async 箭头),`assemble_ir` 回写到 IR。
pub(crate) is_async: bool,
/// 本函数是否为严格模式函数体:自身 directive ‖ 外层严格继承(编译入口设置 +
/// 嵌套继承,见 compile_function_body),`assemble_ir` 回写到 IR。
pub(crate) is_strict: bool,
/// 是否为脚本顶层模块(emit_program 的根上下文)。顶层 var/function 声明需
/// 同步写全局对象属性(脚本环境记录的 var 可经 globalThis 反射);函数体为 false。
pub(crate) is_global_scope: bool,
/// 是否为 REPL 持久模式:脚本顶层 let/const 也写全局对象属性,使跨轮次
/// 读取(LOAD_GLOBAL)可见变量绑定。仅 `eval_repl` 设置。
pub(crate) repl_persist: bool,
/// 是否为 eval 脚本:脚本顶层 var/function 声明落全局对象时属性
/// configurable:true(普通脚本顶层为 false)。仅动态脚本入口设置。
pub(crate) is_eval_script: bool,
/// 编译树是否源自 eval 脚本:eval 代码自身顶层 var/function 名物化 c:true
/// 全局属性、可删,本标志随嵌套函数继承,供 delete 分类判定。不直接继承
/// `is_eval_script`——该标志只标识当前顶层程序,且还控制顶层全局写点与块级
/// 函数门控,整体传播语义过宽。
pub(crate) is_eval_origin: bool,
/// 是否处于首个 sub-pass(提升函数声明发射期):声明点据此区分顶层函数
/// 声明(名绑定恒不可写)与块级函数声明(Annex B.3.3 块绑定为可变 var 绑定)。
/// 仅程序/函数体发射入口在提升 sub-pass 内置位,不跨嵌套函数继承。
pub(crate) in_hoisting_subpass: bool,
/// 源码是否为 `source_escape` 产物(动态编译入口):正则字面量源文本切片
/// 的池键形态据此选择(见 [`Emitter::source_encoded`])。
pub(crate) source_encoded: bool,
/// 静态块内 `this` 的构造器寄存器(vreg 号,无 u8 上限,RegAlloc 期压缩到物理域)。
/// None 表示非静态块上下文,`this` 走常规 this 槽。
pub(crate) static_block_this_reg: Option<u32>,
pub(crate) field_buffer: Option<FieldBuffer>,
/// 类构造器模块中 `@@field_keys` upvalue 下标(实例字段 computed key 数组)。
/// 类定义期求值一次存入 cell,构造器经此 upvalue 读取。
pub(crate) field_keys_uv: Option<u8>,
/// 类定义期全部 computed key 数组寄存器(方法/静态字段阶段按 slot 读取)。
pub(crate) class_keys_reg: Option<u32>,
pub(crate) current_upvalue_captures: Vec<UpvalueCapture>,
/// 嵌套类私有 brand 链:每类一条 `(brand_id, cell_idx, parent_uv_idx)`,
/// 描述本帧如何到达该类的 brand cell——`parent_uv_idx` 为 None 时 cell 在
/// 本帧 own cell 表(cell_idx),为 Some(k) 时经本帧第 k 个 upvalue 可达。
/// 子函数体编译时按父链逐条追加为链式 upvalue 捕获,使内类方法能对
/// 外类声明的私有名做跨类 brand 检查。
pub(crate) brand_chain: Vec<(u32, u8, Option<u8>)>,
/// 本函数作用域声明的绑定名(参数 + 变量/函数声明,AST 收集,emit 前确定)。
pub(crate) own_bindings: HashSet<String>,
/// 本函数形参名集(含解构形参叶子与 rest,编译入口收集):块级函数名与
/// 形参同名时不建外层绑定、不求值写回(规范 paramNames 守卫面)。子函数
/// ctx 为新建,不继承。
pub(crate) param_names: HashSet<String>,
/// 参数序言默认值发射期的形参名 TDZ 集:当前形参自身名(含解构叶)加其后
/// 全部形参名。发射期临时置位,每形参处理完即清空;重名形参不建集(规范
/// 变量环境面无 TDZ)。标识符静态读入口命中此集时发射运行时 ReferenceError。
pub(crate) param_tdz_names: HashSet<String>,
/// 块级函数名按 sloppy 模式下与浏览器/web 实现惯例兼容的行为建外层绑定的抑制集
/// (形参 ∪ 函数作用域树内词法声明名):抑制集内名字退化为纯块作用域;预声明期定稿,实例化与声明点写回两侧同查。
pub(crate) block_fn_suppressed: HashSet<String>,
/// 块入口已物化声明登记表(按块层级压/弹):块直接子与标签直接子体
/// 的函数声明在块入口物化闭包后,按 名 → 声明节点 arena 指针 登记(同名
/// 重复声明只登记首个节点,任一直接子声明点都复用块槽取入口当前值)。
/// 声明点据此三分:同节点命中(本声明已入口物化)→ 复用块槽保闭包
/// 同一;仅同名命中(支臂声明与直接子同名共享块槽)→ 闭包落 fresh
/// 寄存器只写回外层 var、块槽不动;均未命中 → 执行期自物化入块槽。
/// 节点指针解析产物 arena 地址,同一编译单元内稳定。
pub(crate) block_fn_entry_mats: Vec<HashMap<String, *const ()>>,
/// 本函数被嵌套函数捕获的绑定名 → cell_idx(名字排序分配,稳定跨 run)。
/// 捕获判断(MAKE_CELL / CELL_GET / CELL_SET)与子函数 upvalue cell_idx 统一查此映射,
/// 消除符号表时序依赖与 cell 索引错位。
pub(crate) captured_bindings: BTreeMap<String, u8>,
/// 下一可用 cell 索引(计数器):捕获分析后初始化为名字集大小(函数级索引
/// 0..n-1 占满),块级遮蔽绑定预声明追加、catch 参数就地声明与合成 cell 经
/// `alloc_cell_idx` 递增取号,保证与函数级名字排序索引及历次追加互不碰撞。
pub(crate) next_cell_idx: u8,
/// 块入口已发每迭代 TDZ 占位 MAKE_CELL 的 cell 索引集:被捕获的块级
/// let/const 在块入口每迭代新建未初始化 cell(循环体重执行即新 cell),
/// 声明点据此改发原位 MAKE_CELL(闭包捕获占位 cell 后跟随初始化),
/// 不发 MAKE_CELL_FRESH(替换会孤儿化声明前创建的闭包)。
pub(crate) per_iter_predeclared_cells: HashSet<u8>,
/// 词法循环头(C-for / for-in / for-of)名集:头名索引由 ForHeadEnv 覆盖
/// 捕获映射分配(TDZ 占位与体区 fresh cell),声明点据此走映射回退而非
/// 就地追加新索引。begin 时并入、restore 时移除,嵌套循环成对。
pub(crate) for_head_env_names: HashSet<String>,
/// 正在发射的 C 风格 for 头 let/const 声明名(含解构叶)。头名不像块级
/// let/const 那样在块入口预声明,init 表达式内创建的嵌套函数引用同头尚未
/// declare 的前向名时,父捕获可见性过滤据此放行;init 发射完毕后移除,
/// 嵌套 for 头取并集。
pub(crate) pending_for_head_names: HashSet<String>,
/// 顶层已声明 var 名与顶层函数声明名(脚本全局声明实例化提升名):裸读走全局
/// 对象属性(LOAD_GLOBAL)、裸写走感知全局对象属性描述符的写;值的唯一存储是
/// 全局对象属性(顶层 var 的唯一存储),引擎侧不再保留镜像副本。
/// 仅脚本顶层 emit_program 计算,逐层继承给嵌套函数(嵌套函数的局部同名遮蔽不属此集,由作用域索引判定)。
pub(crate) global_tier_names: HashSet<String>,
/// 本函数从父函数捕获的 const 绑定名:子 ctx 不继承父函数作用域符号表,
/// 捕获 const 信息随 upvalue 收集一并快照,供 const 写检查(编译期拦截)使用。
pub(crate) upvalue_const_flags: HashSet<String>,
/// 本函数从父函数捕获的函数名不可写绑定名:子 ctx 不继承父函数作用域符号表,
/// 捕获不可写信息随 upvalue 收集一并快照,供写路径 non_writable 守卫(编译期拦截)使用。
pub(crate) upvalue_non_writable_flags: HashSet<String>,
/// 未声明标识符读所分配的全局槽寄存器集合:标识符首次读未命中任何作用域时,
/// `lookup_or_builtin` 按隐式全局登记并记录其寄存器,后续读取据此发射
/// LOAD_GLOBAL(运行期查 global object 属性,缺失抛 ReferenceError)。
/// 按寄存器而非名字记录:块作用域同名新绑定持不同槽位,不会被误判为隐式全局。
pub(crate) implicit_global_reads: HashSet<u32>,
/// 未声明标识符写所登记的全局槽寄存器集合:`lookup_or_global` 未命中任何作用域
/// 时登记全局作用域绑定并记录其寄存器,写调用点据此(经 `is_implicit_global_reg`
/// 与读集合取并)补全局对象属性写(sloppy)或抛 ReferenceError(strict)。
/// 子函数 ctx 从父继承——继承绑定命中同一寄存器,补写/抛错判定跨嵌套函数一致。
pub(crate) implicit_global_writes: HashSet<u32>,
/// 函数 `length` 属性值:首个带默认值形参之前的形参数(rest 不计)。
/// emit_params_prologue 前由编译入口从 param_specs 计算。
pub(crate) function_length: u32,
/// 形参列表是否 simple(全部无初始值标识符且无 rest):arguments 对象
/// callee 形态(数据属性 vs 受限访问器)按 strict ‖ !simple 分流。
pub(crate) has_simple_params: bool,
pub(crate) const_overflow: bool,
/// with 语句作用域栈:元素为 (with 对象寄存器, 打开时的作用域深度)。
/// 非空时 with 体内的自由标识符需动态解析(先查对象属性,回退外层)。
pub(crate) with_stack: Vec<(u32, usize)>,
/// 打开中(尚未 emit 对应 END)的 try handler 栈,自底向上镜像运行时
/// try_stack 组成。每项标记是否为纯 catch handler(TRY_BEGIN,由 TRY_END
/// 弹出):return 逃出 try 域时据此弹出栈顶连续纯 catch,防 handler 泄漏。
pub(crate) open_try_handlers: Vec<bool>,
/// 完成值帧栈(见 `CompletionFrame`):每个活动语句列表独立累积、if 支臂 /
/// with 体压边界帧、循环 / switch / 非迭代标签压出口目标帧;break/continue
/// 发射跳转前据此解析携值。压弹须在所有非 Err 路径配对(Err 直接终止本次
/// 编译、ctx 丢弃,不做 RAII)。
pub(crate) completion_frames: Vec<CompletionFrame>,
/// 循环 update 段中应走寄存器(而非 cell)的被捕获绑定名:C 风格 for 的
/// let/const 循环变量每迭代新分配一个 cell,update 写寄存器(不污染本迭代
/// 闭包捕获的 cell),下一迭代把这个寄存器值拷入新分配的 cell。
pub(crate) register_update_names: Vec<String>,
/// 正在发射的 C 风格 for 头词法绑定名(含解构叶):这些头名在 init 中经覆盖
/// cell 建 `MAKE_CELL`,但循环体/test/update 读的是绑定寄存器、每迭代 fresh
/// 拷贝的源也是寄存器,故 `emit_bind_target` 对集合内名字在 cell 写之外补写
/// 绑定寄存器。init 发射完毕后清空。
pub(crate) for_head_store_registers: HashSet<String>,
/// 词法循环头保留集:C-for 填被闭包引用或与顶层 var/函数同名的头名,
/// for-in/for-of 填全头名(无撤出覆盖)。`emit_bind_target` 头名回填据此
/// 门控,非保留头名不置位绑定字段(否则体区读命中陈旧 cell 致死循环)。
/// C-for 在 init 发射完毕后清空;for-in/for-of 经 `restore_for_head_env`
/// 逐头名移除。
pub(crate) for_head_keep: HashSet<String>,
/// 词法头声明所在作用域深度(`push_scope` 后的 `scopes.len()`):头名回填
/// 仅在该深度(for 语句自身作用域)的声明点执行;for 体内嵌套作用域的同名
/// 遮蔽声明与 catch 参数不在此深度,不触发回填,防遮蔽绑定的追加索引被头名
/// 索引覆写、声明写落入头绑定 cell。嵌套循环由 `ForHeadEnv` 保存前值并恢复。
pub(crate) for_head_depth: Option<usize>,
/// 模块编译上下文:当前模块命名空间对象寄存器(`__moduleObject` 返回值)。
pub(crate) module_ns_reg: Option<u32>,
/// 写穿反演表:本模块源绑定槽寄存器 → 引用它的导出名集合。仅自导入 ns 的
/// live 模块填充;顶层赋值按解析后的绑定槽命中此表时同步命名空间条目。
/// 以槽位而非名字为键,块级/catch 同名遮蔽解析到不同槽位,天然不误触发。
/// 嵌套函数 ctx 为新建,不继承本字段,写穿只作用于模块顶层。
pub(crate) module_local_export_regs: HashMap<u32, Vec<String>>,
/// 已求值依赖模块的命名空间对象寄存器(按 import/export source 字符串索引)。
pub(crate) module_dep_ns_regs: HashMap<String, u32>,
/// defer 依赖的 deferred namespace 对象寄存器(按 import source 字符串索引):
/// `import defer * as ns` 绑定此对象(`__moduleDeferObject` 产物),eager 命名空间
/// 绑定仍走 `module_dep_ns_regs`。仅含 defer 导入的 spec 登记。
pub(crate) module_defer_ns_regs: HashMap<String, u32>,
/// 依赖模块规范路径(按 import/export source 字符串索引):再导出来源身份用。
pub(crate) module_dep_paths: HashMap<String, String>,
/// 非自导入的导入局部名 → (依赖模块规范路径, 导入名)。命名空间导入的导入名为
/// `oxide_types::MODULE_NAMESPACE_BINDING`;再导出据此重分类为间接导出。
pub(crate) module_import_origins: HashMap<String, (String, String)>,
/// 依赖 source 字符串 → 依赖模块是否含可重赋导出(`compile_js_dep` 回传;
/// Json/Text 数据模块恒 false)。导入方据此把命名/默认导入登记为活读。
pub(crate) module_dep_reassignable: HashMap<String, bool>,
/// 导入本地名 → (依赖命名空间对象寄存器, 导出名):可重赋依赖的命名/默认导入
/// 活读映射。顶层读点据此改发 `__moduleGet` 活读;仅模块顶层 ctx 填充,嵌套
/// 函数 ctx 不继承,闭包内读退化为链接期快照。
pub(crate) module_live_imports: HashMap<String, (u32, String)>,
/// 本模块是否含可重赋导出。仅在依赖模块(`top_level == false`)且捕获/写穿
/// 消费者需要时置真;入口模块恒假,保证纯导出入口 IR 零变化。
pub(crate) module_live_dep: bool,
/// live 命名空间激活:自导入 ns 或本模块含可重赋导出时为真。写穿反演与
/// 导出预注册据此发射;非 live 模块编译产物逐字节不变。
pub(crate) module_live_ns_active: bool,
/// 自导入(import from 自身)的 source 字符串集合:绑定走别名语义,不能链接期快照。
pub(crate) module_self_import_specs: HashSet<String>,
/// 自导入别名:导出名 → 本地绑定槽寄存器(export 语句执行时回写绑定值)。
/// 仅承载无法静态解析源绑定的退化占位路径(star 转发的自导入名);同名多绑定
/// (如 default 双绑定)经 Vec 并列回写。
pub(crate) module_self_aliases: HashMap<String, Vec<u32>>,
/// 自导入别名(本地名,基源绑定名):供别名捕获后处理合并源/别名 cell。
pub(crate) module_alias_pairs: Vec<(String, String)>,
/// 标签模板 site 计数器:本编译树内全局唯一(子 ctx 继承父值继续递增)。
/// 运行时与模块 flat_id 组成模板对象缓存键,保证同一编译树同 site 恒返回
/// 同一对象、不同编译树(eval 每次编译)互不共享。
pub(crate) next_template_site: u32,
/// 本函数登记的函数名(非抑制面):`assemble_ir` 回写到 IR 供调试与
/// 帧槽写入判定。抑制面(形参/var 同名)不登记名绑定,此字段仍记名
/// 供 `instantiate_var_bindings` 排除入口 undefined 写。
pub(crate) function_name: Option<String>,
/// 函数名不可写绑定的寄存器(帧槽写入目标):压帧时把 callee 函数对象
/// 写入该槽。非抑制面在名登记时分配;抑制面(var 同名)在
/// `instantiate_var_bindings` 回填为 var 槽寄存器。
pub(crate) function_name_reg: Option<u32>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum ConstantKey {
Number(u64),
Int(i32),
BigInt(num_bigint::BigInt),
String(String),
Boolean(bool),
Null,
Undefined,
}
impl CompileCtx {
pub(crate) fn new() -> Self {
Self {
insts: Vec::new(),
constants: Vec::new(),
constant_map: HashMap::new(),
next_reg: 1,
max_regs: 1,
reserved_reg_start: 1,
labels: LabelCtx {
label_pos: Vec::new(),
loop_stack: Vec::new(),
switch_stack: Vec::new(),
label_scopes: Vec::new(),
pending_loop_labels: Vec::new(),
finally_depth: 0,
for_of_depth: 0,
for_in_depth: 0,
dispose_depth: 0,
label_counter: 0,
},
scopes: ScopeCtx {
symbols: SymbolTable::new(),
builtin_reg_map: Vec::new(),
private_name_map: HashMap::new(),
private_element_kinds: HashMap::new(),
private_brand_id: None,
next_private_name_id: 1,
},
nested: Vec::new(),
enclosing_this_reg: 254, // conventional this register at top level
in_derived_constructor: false,
in_instance_method: false,
in_static_method: false,
is_generator: false,
is_async: false,
is_strict: false,
is_global_scope: false,
repl_persist: false,
is_eval_script: false,
is_eval_origin: false,
in_hoisting_subpass: false,
source_encoded: false,
static_block_this_reg: None,
field_buffer: None,
field_keys_uv: None,
class_keys_reg: None,
current_upvalue_captures: Vec::new(),
brand_chain: Vec::new(),
own_bindings: HashSet::new(),
param_names: HashSet::new(),
param_tdz_names: HashSet::new(),
block_fn_suppressed: HashSet::new(),
block_fn_entry_mats: Vec::new(),
captured_bindings: BTreeMap::new(),
next_cell_idx: 0,
per_iter_predeclared_cells: HashSet::new(),
for_head_env_names: HashSet::new(),
pending_for_head_names: HashSet::new(),
global_tier_names: HashSet::new(),
upvalue_const_flags: HashSet::new(),
upvalue_non_writable_flags: HashSet::new(),
implicit_global_reads: HashSet::new(),
implicit_global_writes: HashSet::new(),
function_length: 0,
has_simple_params: false,
const_overflow: false,
with_stack: Vec::new(),
open_try_handlers: Vec::new(),
completion_frames: Vec::new(),
register_update_names: Vec::new(),
for_head_store_registers: HashSet::new(),
for_head_keep: HashSet::new(),
for_head_depth: None,
module_ns_reg: None,
module_local_export_regs: HashMap::new(),
module_dep_ns_regs: HashMap::new(),
module_defer_ns_regs: HashMap::new(),
module_dep_paths: HashMap::new(),
module_import_origins: HashMap::new(),
module_dep_reassignable: HashMap::new(),
module_live_imports: HashMap::new(),
module_live_dep: false,
module_live_ns_active: false,
module_self_import_specs: HashSet::new(),
module_self_aliases: HashMap::new(),
module_alias_pairs: Vec::new(),
next_template_site: 0,
function_name: None,
function_name_reg: None,
}
}
pub(crate) fn inst(&mut self, inst: Inst) {
self.insts.push(inst);
}
pub(crate) fn alloc_reg(&mut self) -> u32 {
let r = self.next_reg;
// vreg 化:寄存器号无上限,RegAlloc 阶段负责压缩到物理域(≤253)。
// 254/255 是 VM 保留的 this/new.target,vreg 世界允许虚拟号越过它们,
// 只有 RegAlloc 完成映射后 lower 的物理域检查才相关。
self.next_reg += 1;
if self.next_reg > self.max_regs {
self.max_regs = self.next_reg;
}
r
}
pub(crate) fn reset_regs(&mut self) {
self.next_reg = self.builtin_reg_floor().max(self.reserved_reg_start);
self.labels.label_counter = 0;
}
pub(crate) fn reserve_reg(&mut self, reg: u32) {
let next = reg.wrapping_add(1);
if self.next_reg <= reg {
self.next_reg = next;
}
if self.max_regs < next {
self.max_regs = next;
}
}
pub(crate) fn add_constant(&mut self, c: Constant) -> u16 {
if let Some(key) = ConstantKey::from_constant(&c) {
if let Some(&idx) = self.constant_map.get(&key) {
return idx;
}
if self.constants.len() >= u16::MAX as usize {
self.const_overflow = true;
return u16::MAX;
}
let idx = self.constants.len() as u16;
self.constants.push(c);
self.constant_map.insert(key, idx);
return idx;
}
let idx = self.constants.len();
if idx >= u16::MAX as usize {
self.const_overflow = true;
return u16::MAX;
}
self.constants.push(c);
idx as u16
}
pub(crate) fn push_scope(&mut self) {
self.scopes.symbols.push_scope();
}
pub(crate) fn pop_scope(&mut self) {
self.scopes.symbols.pop_scope();
}
pub(crate) fn declare(&mut self, name: &str, reg: u32, kind: VariableDeclarationKind) -> Result<(), String> {
self.scopes.symbols.declare(name, reg, kind)
}
pub(crate) fn declare_initialized(
&mut self, name: &str, reg: u32, kind: VariableDeclarationKind,
) -> Result<(), String> {
self.scopes.symbols.declare_initialized(name, reg, kind)
}
pub(crate) fn declare_predeclared(
&mut self, name: &str, reg: u32, kind: VariableDeclarationKind,
) -> Result<(), String> {
self.scopes.symbols.declare_predeclared(name, reg, kind)
}
pub(crate) fn push_scope_with_kind(&mut self, kind: ScopeKind) {
self.scopes.symbols.push_scope_with_kind(kind);
}
pub(crate) fn lookup(&self, name: &str) -> Result<u32, String> {
self.scopes.symbols.lookup(name)
}
pub(crate) fn lookup_or_builtin(&mut self, name: &str) -> Result<u32, String> {
match self.scopes.symbols.lookup(name) {
Ok(reg) => Ok(reg),
// 未声明标识符按隐式全局登记:读取语义(读未声明应抛 ReferenceError)由
// 发射端按 implicit_global_reads 择 LOAD_GLOBAL 实现;`typeof X` 守卫等
// 合法 JS 在运行期判定未定义(typeof 特判走 LOAD_GLOBAL_TYPEOF)。
Err(err) if err.contains("is not defined") => {
let reg = self.alloc_reg();
self.scopes.symbols.pre_register_global(name, reg);
self.scopes.builtin_reg_map.push((name.to_string(), reg));
// 内置名(预扫描阶段在空符号表上登记)不是用户未声明读,不标记——
// 否则内置标识符读取全部改走 LOAD_GLOBAL(热路径退化 + 槽位被修剪)。
if !Self::is_known_builtin(name) {
self.implicit_global_reads.insert(reg);
}
Ok(reg)
}
Err(err) => Err(err),
}
}
pub(crate) fn lookup_or_global(&mut self, name: &str) -> u32 {
if let Some(reg) = self.scopes.symbols.lookup_any(name) {
return reg;
}
let reg = self.alloc_reg();
// 未声明标识符写:登记全局作用域绑定,记录寄存器供写调用点补全局对象属性
// 写(sloppy)或抛 ReferenceError(strict)。
self.implicit_global_writes.insert(reg);
self.scopes.symbols.lookup_or_global(name, reg)
}
/// 寄存器是否承载未声明标识符的全局槽(写调用点据此补全局对象属性写或抛
/// ReferenceError)。读写两个登记集合取并:未声明名谁先引用谁登记,读侧登记
/// (LOAD_GLOBAL 槽)与写侧登记同属一个全局槽,写都必须穿透到全局对象。
pub(crate) fn is_implicit_global_reg(&self, reg: u32) -> bool {
self.implicit_global_writes.contains(&reg) || self.implicit_global_reads.contains(&reg)
}
/// 名字在当前作用域链内是否存在真实词法绑定(排除隐式全局登记),命中时返回其寄存器。
///
/// 未声明名的隐式全局登记(写侧 `lookup_or_global`、读侧 `lookup_or_builtin` 经
/// `pre_register_global`)会在全局作用域插入占位绑定,使 `lookup_any_binding` 按名
/// 命中;但它不是词法声明。捕获名退出作用域后若曾被登记为隐式全局,按名命中会
/// 误读残留 cell——捕获 cell 的可见性判定统一经本入口。
pub(crate) fn visible_binding_reg(&self, name: &str) -> Option<u32> {
let (binding, _) = self.scopes.symbols.lookup_any_binding(name)?;
(!self.is_implicit_global_reg(binding.reg)).then_some(binding.reg)
}
/// 解析点 cell 索引与绑定寄存器:名字当前可见(真实词法绑定、非隐式全局
/// 登记)时返回 (cell_idx, binding_reg)。索引优先取绑定自身字段(函数级
/// 回填与块级追加的真源),绑定未分配索引时回退捕获映射(for 头覆盖 /
/// catch 参数等容器面,后续子任务逐面收编)。块级遮蔽绑定在块退出后不可
/// 解析,返回 None(落全局解析),不得按名误读已失效 cell。tier 绑定
/// (scope 0 顶层 var/函数)的全局对象属性是唯一存储,不建 cell;映射中
/// 被同名块级绑定保留的 entry 不属该 tier 绑定,回退不得命中——for 头
/// 前向名除外(头绑定未 declare,映射值是 ForHeadEnv 的 TDZ 索引)。
pub(crate) fn visible_cell(&self, name: &str) -> Option<(u8, u32)> {
let (binding, scope_idx) = self.scopes.symbols.lookup_any_binding(name)?;
if self.is_implicit_global_reg(binding.reg) {
return None;
}
// for 头前向名(C 风格 for 头 pending / for-in/for-of env)读 TDZ cell:
// 头绑定未 declare,按名回查命中外层同名 tier 绑定,映射回退须放行以命中
// ForHeadEnv 覆盖的 TDZ 索引。
let is_for_head = self.pending_for_head_names.contains(name) || self.for_head_env_names.contains(name);
let is_tier_binding = scope_idx == 0 && self.global_tier_names.contains(name);
let idx = binding.cell_idx.or_else(|| {
if is_tier_binding && !is_for_head {
None
} else {
self.captured_bindings.get(name).copied()
}
})?;
Some((idx, binding.reg))
}
/// 设置捕获集并同步 cell 索引计数器:映射值为 0..n-1 的名字排序索引,
/// 计数器自 n 起保证后续追加(块级预声明 / 合成 cell)不与既有索引碰撞。
pub(crate) fn set_captured_bindings(&mut self, map: BTreeMap<String, u8>) {
self.next_cell_idx = map.len() as u8;
self.captured_bindings = map;
}
/// 分配一个新 cell 索引:取当前计数器值并递增。块级遮蔽绑定预声明追加、
/// catch 参数就地声明与合成 cell 共用,分配序由发射序决定,跨 run 稳定。
pub(crate) fn alloc_cell_idx(&mut self) -> u8 {
let idx = self.next_cell_idx;
self.next_cell_idx = self.next_cell_idx.saturating_add(1);
idx
}
/// 声明点 cell 索引解析:索引按绑定实例取(绑定自身字段是真源)。绑定未分配
/// 索引时,词法循环头名(ForHeadEnv 覆盖捕获映射)走映射回退;最内层为块
/// 作用域的就地声明(catch 参数)现场追加新索引并写回绑定,与函数级同名
/// 绑定的名字排序索引区分。头名判定按作用域深度门控:仅 for 语句自身作用域
/// 的声明点是头绑定本身(走映射回退);体内嵌套作用域的同名就地声明(catch
/// 参数)不属头绑定,走块级追加臂取独立索引,不共用头 cell。名字不可见(隐式
/// 全局登记)或不在捕获集时返回 None,调用方落 with/全局臂。顶层 tier 名
/// (顶层 var/函数)的全局对象属性是唯一存储,不建 cell,返回 None。
///
/// # 副作用
/// - 就地追加臂写回绑定 cell 索引并递增计数器;指令流不改动。
pub(crate) fn resolve_bind_cell_idx(&mut self, name: &str) -> Option<u8> {
let (existing, is_implicit, block_scope, for_head, is_tier) = {
let symbols = &self.scopes.symbols;
let (binding, scope_idx) = symbols.lookup_any_binding(name)?;
(
binding.cell_idx,
self.is_implicit_global_reg(binding.reg),
symbols.scopes.last().is_some_and(|s| s.kind == ScopeKind::BlockScope),
self.for_head_env_names.contains(name) && self.for_head_depth == Some(symbols.scopes.len()),
self.is_global_scope && self.global_tier_names.contains(name) && scope_idx == 0,
)
};
if is_implicit {
return None;
}
if let Some(idx) = existing {
return Some(idx);
}
if is_tier {
return None;
}
let base = *self.captured_bindings.get(name)?;
if block_scope && !for_head {
let fresh = self.alloc_cell_idx();
self.set_binding_cell_idx(name, fresh);
Some(fresh)
} else {
Some(base)
}
}
/// 给最内层同名可见绑定写入 cell 索引(委托符号表)。
pub(crate) fn set_binding_cell_idx(&mut self, name: &str, idx: u8) {
self.scopes.symbols.set_binding_cell_idx(name, idx);
}
/// 函数级绑定 cell 索引回填:捕获集(名字排序分配)的索引写入对应绑定
/// 实例,使解析点按绑定身份取索引。预声明完成后调用,函数级绑定(参数 /
/// var / 直接子级词法 / 块级函数外层 var / arguments)此时已齐。顶层 tier
/// 名(顶层 var/函数)的全局对象属性是唯一存储,不回填 cell 索引。
pub(crate) fn backfill_captured_cell_idxs(&mut self) {
let entries: Vec<(String, u8)> = self
.captured_bindings
.iter()
.filter(|(name, _)| !(self.is_global_scope && self.global_tier_names.contains(*name)))
.map(|(name, &idx)| (name.clone(), idx))
.collect();
for (name, idx) in entries {
self.set_binding_cell_idx(&name, idx);
}
}
pub(crate) fn lookup_const_flag(&self, name: &str) -> bool {
// upvalue 捕获的 const:子 ctx 符号表不含父函数作用域绑定,查快照标志。
self.scopes.symbols.lookup_is_const(name) || self.upvalue_const_flags.contains(name)
}
/// 写路径 non_writable 守卫查询:名字是否为函数名不可写绑定(本作用域或 upvalue 捕获)。
pub(crate) fn lookup_non_writable_flag(&self, name: &str) -> bool {
// upvalue 捕获的函数名:子 ctx 符号表不含父函数作用域绑定,查快照标志。
self.scopes.symbols.lookup_non_writable(name) || self.upvalue_non_writable_flags.contains(name)
}
pub(crate) fn init_var(&mut self, name: &str) {
self.scopes.symbols.init_var(name);
}
/// 声明本地名为源绑定活引用的别名(模块自导入):槽位复用源寄存器,
/// TDZ/提升/活值/不可变全部委托源绑定。
pub(crate) fn add_alias(&mut self, local: &str, source: &str) -> Result<(), String> {
self.scopes.symbols.add_alias(local, source)
}
pub(crate) fn next_label_id(&mut self) -> u32 {
let id = self.labels.label_counter;
self.labels.label_counter += 1;
id
}
/// 打开一个循环并返回其出口结果寄存器:就地分配并初始化为 undefined
/// (覆盖零迭代路径,缺初始化则 `for(;;false;)` 形读垃圾泄漏前值)。
/// 调用点须先于循环头标签落位:初始化指令随调用点发射,落在回边内会每
/// 迭代重置出口值。
pub(crate) fn push_loop(&mut self, break_label: LabelId, continue_label: LabelId, kind: LoopKind) -> u32 {
let fd = self.labels.finally_depth;
// 深度计数先递增再快照:条目记录"打开后(含自身)"的深度,与
// 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;
let dd = self.labels.dispose_depth;
let v_reg = self.alloc_reg();
let undef_idx = self.add_constant(Constant::Undefined);
self.inst(Inst::load_const(Operand::Reg(v_reg), undef_idx));
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,
dispose_depth_at_open: dd,
kind,
v_reg,
});
v_reg
}
pub(crate) fn pop_loop(&mut self) {
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<&LoopEntry> {
self.labels.loop_stack.last()
}
pub(crate) fn push_switch(&mut self, break_label: LabelId, result_reg: u32) {
let fd = self.labels.finally_depth;
let fod = self.labels.for_of_depth;
let fid = self.labels.for_in_depth;
let dd = self.labels.dispose_depth;
self.labels.switch_stack.push(SwitchEntry {
break_label,
finally_depth_at_open: fd,
for_of_depth_at_open: fod,
for_in_depth_at_open: fid,
dispose_depth_at_open: dd,
result_reg,
});
}
pub(crate) fn pop_switch(&mut self) {
self.labels.switch_stack.pop();
}
pub(crate) fn current_switch(&self) -> Option<&SwitchEntry> {
self.labels.switch_stack.last()
}
/// 压一个语句列表累积帧:列表内每条非空语句经 `set_completion_last` 记
/// 最后非空值寄存器(空语句不覆写,规范 `UpdateEmpty` 的「非空才覆写」)。
pub(crate) fn push_completion_list(&mut self) {
self.completion_frames.push(CompletionFrame::List { last: None });
}
pub(crate) fn pop_completion_list(&mut self) {
debug_assert!(matches!(self.completion_frames.last(), Some(CompletionFrame::List { .. })));
self.completion_frames.pop();
}
/// 记栈顶列表帧的最后非空语句寄存器(调用点须刚压过列表帧)。
pub(crate) fn set_completion_last(&mut self, reg: u32) {
if let CompletionFrame::List { last } = self.completion_frames.last_mut().expect("完成值帧栈顶为语句列表帧")
{
*last = Some(reg);
}
}
/// 压一个边界帧(`UpdateEmpty(_, undefined)` 站点:if 支臂 / with 体):
/// break/continue 携值解析撞它物化 undefined。
pub(crate) fn push_completion_boundary(&mut self) {
self.completion_frames.push(CompletionFrame::Boundary);
}
pub(crate) fn pop_completion_boundary(&mut self) {
debug_assert!(matches!(self.completion_frames.last(), Some(CompletionFrame::Boundary)));
self.completion_frames.pop();
}
/// 压一个出口目标帧:break/continue 携值写入该寄存器,空携值保持其原值。
pub(crate) fn push_completion_target(&mut self, v_reg: u32) {
self.completion_frames.push(CompletionFrame::Target { v_reg });
}
pub(crate) fn pop_completion_target(&mut self) {
debug_assert!(matches!(self.completion_frames.last(), Some(CompletionFrame::Target { .. })));
self.completion_frames.pop();
}
/// 解析 break/continue 的携值:自栈顶向目标帧走,首个非空列表帧取
/// `Value(r)`,撞边界帧取 `Undefined`,首个目标帧——与 `target_v_reg`
/// 相等(本跳转自身目标)取 `Empty`(不写,目标寄存器保持既有累积值,
/// 规范 `UpdateEmpty(break, iterationResult)` 等义),为内层非目标帧
/// (labeled break 穿内层循环)取 `Value(该帧寄存器)`。
pub(crate) fn resolve_completion_carry(&self, target_v_reg: u32) -> CompletionCarry {
for frame in self.completion_frames.iter().rev() {
match frame {
CompletionFrame::List { last: Some(r) } => return CompletionCarry::Value(*r),
CompletionFrame::List { last: None } => {}
CompletionFrame::Boundary => return CompletionCarry::Undefined,
CompletionFrame::Target { v_reg } => {
return if *v_reg == target_v_reg {
CompletionCarry::Empty
} else {
CompletionCarry::Value(*v_reg)
};
}
}
}
CompletionCarry::Empty
}
/// 进入/离开一个 try/finally 域:break/continue 跨越 finally 计数用。
pub(crate) fn push_finally_domain(&mut self) {
self.labels.finally_depth += 1;
}
pub(crate) fn pop_finally_domain(&mut self) {
self.labels.finally_depth -= 1;
}
/// 发 DISPOSE_MARK:作用域入口登记释放栈水位,dispose_depth 递增。
///
/// # 边界与前提
/// - 与 `emit_dispose_pop` 成对;编译期不变式是函数体收尾 dispose_depth 归零
/// (`assemble_ir` 前 debug_assert 校验)。
pub(crate) fn emit_dispose_mark(&mut self) {
self.inst(Inst::new(OpCode::DISPOSE_MARK, Operand::None, Operand::None, Operand::None));
self.labels.dispose_depth += 1;
}
/// 发 DISPOSE_POP:作用域出口逆序释放水位以上资源,dispose_depth 递减。
pub(crate) fn emit_dispose_pop(&mut self) {
self.inst(Inst::new(OpCode::DISPOSE_POP, Operand::None, Operand::None, Operand::None));
self.labels.dispose_depth -= 1;
}
/// 记录一个纯 catch handler 打开(对应 emit try_begin 后的运行时 TRY_BEGIN)。
pub(crate) fn push_open_catch_handler(&mut self) {
self.open_try_handlers.push(true);
}
/// 记录一个 finally handler 打开(对应 emit try_finally_begin 后的运行时
/// TRY_FINALLY_BEGIN)。
pub(crate) fn push_open_finally_handler(&mut self) {
self.open_try_handlers.push(false);
}
/// 弹出最近打开的 handler(对应 emit 的 TRY_END / TRY_FINALLY_END)。
pub(crate) fn pop_open_try_handler(&mut self) {
self.open_try_handlers.pop();
}
/// 栈顶连续打开的纯 catch handler 数(遇 finally handler 即停)。
/// return 逃出本函数时,这些 handler 可由 TRY_END 直接从栈顶弹出;
/// finally 之下的 catch 无法经 TRY_END 弹出,交给运行时统一清理。
pub(crate) fn top_open_catch_handlers(&self) -> usize {
self.open_try_handlers.iter().rev().take_while(|&&is_catch| is_catch).count()
}
pub(crate) fn push_label_scope(
&mut self, name: &str, break_label: LabelId, continue_label: Option<LabelId>, completion_reg: Option<u32>,
) -> Result<(), String> {
if self.labels.label_scopes.iter().any(|s| s.name == name) {
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;
let dd = self.labels.dispose_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,
dispose_depth_at_open: dd,
completion_reg,
});
Ok(())
}
pub(crate) fn pop_label_scope(&mut self) {
self.labels.label_scopes.pop();
}
pub(crate) fn find_label(&self, name: &str) -> Option<&LabelScope> {
self.labels.label_scopes.iter().rev().find(|s| s.name == name)
}
/// 登记一个待绑定标签名:该标签将作为下一个 emit 的循环(标签语句体)的
/// continue 目标。活动集合与待绑定集合中出现重名报错。
pub(crate) fn queue_loop_label(&mut self, name: &str) -> Result<(), String> {
if self.labels.label_scopes.iter().any(|s| s.name == name)
|| self.labels.pending_loop_labels.iter().any(|n| n == name)
{
return Err(format!("SyntaxError: Label '{name}' has already been declared"));
}
self.labels.pending_loop_labels.push(name.to_string());
Ok(())
}
/// 把待绑定标签名落地为活动标签作用域,绑定到本次循环的 break/continue 目标
/// 与出口结果寄存器。返回压入的作用域个数(供事后对称弹出)。
pub(crate) fn take_pending_loop_labels(
&mut self, break_label: LabelId, continue_label: LabelId, completion_reg: u32,
) -> usize {
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;
let dd = self.labels.dispose_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,
dispose_depth_at_open: dd,
completion_reg: Some(completion_reg),
});
}
count
}
pub(crate) fn pop_label_scopes(&mut self, n: usize) {
for _ in 0..n {
self.labels.label_scopes.pop();
}
}
pub(crate) fn is_builtin(&self, name: &str) -> bool {
self.scopes.builtin_reg_map.iter().any(|(n, _)| n == name)
}
/// 内置名是否被局部声明遮蔽(`var parseInt = ...` 等)。
/// builtin_reg_map 记录的是全局内置槽;若该名在静态作用域另有绑定,则调用应解析局部。
pub(crate) fn is_local_shadowing_builtin(&self, name: &str) -> bool {
if let Some(reg) = self.scopes.symbols.lookup_any(name) {
let is_builtin_slot = self.scopes.builtin_reg_map.iter().any(|(n, r)| n == name && *r == reg);
return !is_builtin_slot;
}
false
}
/// 名字是否解析到全局内置槽:builtin_reg_map 登记(预注册镜像槽),或绑定
/// 落全局作用域(scope 0,顶层 var 预声明/隐式全局,含继承的全局槽)。局部
/// var/let/参数遮蔽同名时解析到局部寄存器/局部作用域,两条件均不命中。
/// scope 0 臂拒词法绑定:顶层 let/const/class/导入绑定遮蔽同名全局属性,
/// 值存储是其自身槽位,写不落全局对象。
fn resolves_to_global_builtin_slot(&self, name: &str, reg: u32) -> bool {
if self.scopes.builtin_reg_map.iter().any(|(n, r)| n == name && *r == reg) {
return true;
}
matches!(self.scopes.symbols.lookup_any_binding(name), Some((binding, 0)) if !binding.lexical)
}
/// 名字是否解析到顶层词法绑定(全局词法环境):scope 0 的 let/const/class/
/// 导入绑定。顶层词法绑定的值存储是全局词法环境记录(镜像),所有写路径
/// (声明/赋值/复合/逻辑/update/解构)须同步镜像,否则跨脚本(间接 eval)
/// 读经镜像见陈旧值。块级词法(scope >0)与顶层 var/隐式全局(lexical 不置位)
/// 不属全局词法环境,不写镜像。
pub(crate) fn is_global_lexical_binding(&self, name: &str) -> bool {
matches!(self.scopes.symbols.lookup_any_binding(name), Some((binding, 0)) if binding.lexical)
}
/// 写目标是否为只读全局内置(undefined/NaN/Infinity 的全局绑定——全局对象上
/// 数据属性 {writable:false, configurable:false},put 永不成功)。名字在只读
/// 名单内且解析到全局内置槽;声明臂与赋值臂共用本谓词。
pub(crate) fn targets_readonly_builtin(&self, name: &str, reg: u32) -> bool {
NON_WRITABLE_GLOBAL_BUILTINS.contains(&name) && self.resolves_to_global_builtin_slot(name, reg)
}
/// 写目标是否为可写全局内置(全局对象上数据属性 {writable:true,
/// configurable:true})。标识符写须双写镜像槽 + 全局对象属性,否则裸读
/// (镜像)与 globalThis 反射(属性)失步。
pub(crate) fn targets_writable_builtin(&self, name: &str, reg: u32) -> bool {
Self::is_writable_builtin_global(name) && self.resolves_to_global_builtin_slot(name, reg)
}
/// 规范可写全局名:BUILTIN_GLOBALS 减只读三常量集——11 个 w:true 函数、
/// 48 个构造器/命名空间、globalThis 与宿主名,描述符皆
/// {writable:true, enumerable:false, configurable:true}。
pub(crate) fn is_writable_builtin_global(name: &str) -> bool {
BUILTIN_GLOBALS.contains(&name) && !NON_WRITABLE_GLOBAL_BUILTINS.contains(&name)
}
/// 可删除全局内置名:可写全局名再除宿主名 $262(harness 全局
/// {configurable:false},删除会破坏宿主面)——自只读/可写单一真源派生,
/// 不另立名单。余名描述符皆 {writable:true, configurable:true},delete
/// 真删且返 true。
pub(crate) fn is_deletable_global_builtin(name: &str) -> bool {
Self::is_writable_builtin_global(name) && name != "$262"
}
/// delete 标识符是否解析到可删除的全局内置镜像槽:名可删(上谓词)且预注册
/// 镜像槽在册时返回槽寄存器。局部 var/let/参数遮蔽同名时该名不注册镜像槽
/// (预扫描解析到局部绑定),不命中;槽值在 run/帧入口由全局属性预载。
pub(crate) fn global_builtin_delete_slot(&self, name: &str) -> Option<u32> {
if !Self::is_deletable_global_builtin(name) {
return None;
}
self.scopes.builtin_reg_map.iter().find(|(n, _)| n == name).map(|(_, r)| *r)
}
pub(crate) fn is_known_builtin(name: &str) -> bool {
BUILTIN_GLOBALS.contains(&name)
}
/// 规范不可声明的全局名:只读三常量(全局对象上数据属性
/// {writable:false, configurable:false})——顶层块级函数名仅这一面不建
/// 外层绑定、不求值写回(sloppy put 永不成功,跳过与执行等价);其余
/// 已知 builtin(parseInt 等)属性可配置,可声明(求值期覆写全局属性)。
pub(crate) fn is_non_writable_global_builtin(name: &str) -> bool {
NON_WRITABLE_GLOBAL_BUILTINS.contains(&name)
}
/// 早期错误跳过谓词(Annex B.3.3.3 对 EvalDeclarationInstantiation 的增补):
/// sloppy eval 代码内块函数声明的名若与同树词法声明/形参同名,替换为变量
/// 语句即产生早期错误,规范不为其建任何绑定——函数体只求值副作用,不写块
/// Let、不写外层 var、不写全局对象属性。预声明门与存储臂共用本谓词。
pub(crate) fn is_eval_early_error_suppressed(&self, name: &str) -> bool {
self.is_eval_script && !self.is_strict && self.block_fn_suppressed.contains(name)
}
/// 进入 with 语句体:记录对象寄存器与当前作用域深度,供动态标识符解析。
pub(crate) fn push_with(&mut self, obj_reg: u32) {
let depth = self.scopes.symbols.scopes.len();
self.with_stack.push((obj_reg, depth));
}
/// 离开 with 语句体。
pub(crate) fn pop_with(&mut self) {
self.with_stack.pop();
}
/// 最内层 with 对象寄存器;无 with 时为 None。
pub(crate) fn innermost_with_obj(&self) -> Option<u32> {
self.with_stack.last().map(|&(reg, _)| reg)
}
/// 指定名字是否在 with 语句体**内**(with 打开之后的块作用域)声明。
/// with 内声明优先于对象属性;with 之前的外层声明被对象遮蔽。
pub(crate) fn is_with_internal_binding(&self, name: &str) -> bool {
let Some(&(_, depth)) = self.with_stack.last() else {
return false;
};
matches!(self.scopes.symbols.lookup_any_binding(name), Some((_, idx)) if idx >= depth)
}
pub(crate) fn builtin_reg_floor(&self) -> u32 {
self.scopes
.builtin_reg_map
.iter()
.map(|(_, reg)| reg.saturating_add(1))
.max()
.unwrap_or(0)
}
/// 组装 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 {
// 编译期不变式:每个 DISPOSE_MARK 都有配对的 DISPOSE_POP,函数体收尾
// 释放作用域全部关闭(早 return 跳过 POP 归穿越面,不在此列)。
debug_assert!(self.labels.dispose_depth == 0, "函数体收尾释放作用域深度须归零(mark/pop 未配对)");
let upvalue_captures = self
.current_upvalue_captures
.iter()
.map(|u| {
let enclosing_reg = parent_ctx
.and_then(|p| p.scopes.symbols.lookup_any(u.name.as_str()))
.unwrap_or(0);
UpvalueCapture {
name: u.name.clone(),
enclosing_reg,
cell_idx: u.cell_idx,
parent_uv_idx: u.parent_uv_idx,
}
})
.collect();
// 容量覆盖全部已分配 cell 索引:函数级名字排序索引(映射值 0..n-1)
// 与追加索引(计数器,块级预声明 / 合成 cell)取较大者加一;别名
// 捕获合并让多个名字共享同一 cell,下标可重复。
let cells_needed = self
.captured_bindings
.values()
.copied()
.max()
.map_or(0, |m| m.saturating_add(1))
.max(self.next_cell_idx);
// own cell 绑定名表:下标 = cell 下标,值 = 绑定名;合成 cell 与块级
// 预声明追加无源名,留空串。
let mut cell_names = vec![String::new(); cells_needed as usize];
for (name, idx) in &self.captured_bindings {
cell_names[*idx as usize] = name.clone();
}
IRFunction {
insts: std::mem::take(&mut self.insts),
label_pos: std::mem::take(&mut self.labels.label_pos),
label_count: self.labels.label_counter,
constants: std::mem::take(&mut self.constants),
param_layout,
builtin_reg_map: std::mem::take(&mut self.scopes.builtin_reg_map),
upvalue_captures,
cells_needed,
cell_names,
n_registers: self.max_regs,
is_arrow: false,
is_class_constructor: false,
is_derived_constructor: false,
needs_home_object: false,
is_generator: self.is_generator,
is_async: self.is_async,
is_strict: self.is_strict,
captured_this_const_idx: 0,
function_name: None,
function_name_reg: self.function_name_reg,
function_length: self.function_length,
has_simple_params: self.has_simple_params,
is_top_level: parent_ctx.is_none(),
const_overflow: self.const_overflow,
nested: std::mem::take(&mut self.nested),
}
}
}
impl ConstantKey {
fn from_constant(value: &Constant) -> Option<Self> {
match value {
Constant::Number(v) => Some(Self::Number(v.to_bits())),
Constant::Int(v) => Some(Self::Int(*v)),
Constant::BigInt(v) => Some(Self::BigInt(v.clone())),
Constant::String(v) => Some(Self::String(v.clone())),
Constant::Boolean(v) => Some(Self::Boolean(*v)),
Constant::Null => Some(Self::Null),
Constant::Undefined => Some(Self::Undefined),
}
}
}