fix(compiler): 重写闭包捕获分析为 AST 级,修复 MAKE_CELL 时序
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
use std::cell::Cell;
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::emit_ctx::{LabelCtx, ScopeCtx};
|
use crate::emit_ctx::{LabelCtx, ScopeCtx};
|
||||||
use crate::ir::inst::Inst;
|
use crate::ir::inst::Inst;
|
||||||
@@ -144,6 +143,11 @@ pub(crate) struct CompileCtx {
|
|||||||
pub(crate) static_block_this_reg: Option<u8>,
|
pub(crate) static_block_this_reg: Option<u8>,
|
||||||
pub(crate) field_buffer: Option<FieldBuffer>,
|
pub(crate) field_buffer: Option<FieldBuffer>,
|
||||||
pub(crate) current_upvalue_captures: Vec<UpvalueCapture>,
|
pub(crate) current_upvalue_captures: Vec<UpvalueCapture>,
|
||||||
|
/// 本函数作用域声明的绑定名(参数 + 变量/函数声明,AST 收集,emit 前确定)。
|
||||||
|
pub(crate) own_bindings: HashSet<String>,
|
||||||
|
/// 本函数被嵌套函数捕获的绑定名(AST 分析,emit 前确定)。
|
||||||
|
/// 捕获判断(MAKE_CELL / CELL_GET / CELL_SET)统一查此集合,消除符号表时序依赖。
|
||||||
|
pub(crate) captured_bindings: HashSet<String>,
|
||||||
/// Set when alloc_reg() overflows into the reserved this/new.target range (≥254).
|
/// Set when alloc_reg() overflows into the reserved this/new.target range (≥254).
|
||||||
/// Checked after each emit phase to produce a compile error rather than silent corruption.
|
/// Checked after each emit phase to produce a compile error rather than silent corruption.
|
||||||
pub(crate) reg_overflow: bool,
|
pub(crate) reg_overflow: bool,
|
||||||
@@ -216,6 +220,8 @@ impl CompileCtx {
|
|||||||
static_block_this_reg: None,
|
static_block_this_reg: None,
|
||||||
field_buffer: None,
|
field_buffer: None,
|
||||||
current_upvalue_captures: Vec::new(),
|
current_upvalue_captures: Vec::new(),
|
||||||
|
own_bindings: HashSet::new(),
|
||||||
|
captured_bindings: HashSet::new(),
|
||||||
reg_overflow: false,
|
reg_overflow: false,
|
||||||
const_overflow: false,
|
const_overflow: false,
|
||||||
}
|
}
|
||||||
@@ -457,7 +463,22 @@ impl CompileCtx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 组装 IRFunction(两出口共用),take 走编译产物状态。
|
/// 组装 IRFunction(两出口共用),take 走编译产物状态。
|
||||||
fn assemble_ir(&mut self, param_layout: crate::ir::ParamLayout) -> IRFunction {
|
/// `parent_ctx` 用于补全 upvalue_captures 的 enclosing_reg(父符号表在父 emit 完成后完整)。
|
||||||
|
fn assemble_ir(&mut self, param_layout: crate::ir::ParamLayout, parent_ctx: Option<&CompileCtx>) -> IRFunction {
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
IRFunction {
|
IRFunction {
|
||||||
insts: std::mem::take(&mut self.insts),
|
insts: std::mem::take(&mut self.insts),
|
||||||
label_pos: std::mem::take(&mut self.labels.label_pos),
|
label_pos: std::mem::take(&mut self.labels.label_pos),
|
||||||
@@ -465,15 +486,8 @@ impl CompileCtx {
|
|||||||
constants: std::mem::take(&mut self.constants),
|
constants: std::mem::take(&mut self.constants),
|
||||||
param_layout,
|
param_layout,
|
||||||
builtin_reg_map: std::mem::take(&mut self.scopes.builtin_reg_map),
|
builtin_reg_map: std::mem::take(&mut self.scopes.builtin_reg_map),
|
||||||
upvalue_captures: self.current_upvalue_captures.clone(),
|
upvalue_captures,
|
||||||
cells_needed: self
|
cells_needed: self.captured_bindings.len() as u8,
|
||||||
.scopes
|
|
||||||
.symbols
|
|
||||||
.scopes
|
|
||||||
.iter()
|
|
||||||
.flat_map(|s| s.bindings.values())
|
|
||||||
.filter(|b| b.is_captured.get())
|
|
||||||
.count() as u8,
|
|
||||||
n_registers: self.max_regs,
|
n_registers: self.max_regs,
|
||||||
is_arrow: false,
|
is_arrow: false,
|
||||||
is_class_constructor: false,
|
is_class_constructor: false,
|
||||||
@@ -506,97 +520,314 @@ impl Compiler {
|
|||||||
Self
|
Self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn analyze_upvalue_captures(
|
// ── 闭包捕获分析(AST 级,时序无关)──
|
||||||
&self, body_stmts: &[Statement], parent_ctx: &CompileCtx, nested_symbols: &SymbolTable,
|
|
||||||
) -> (Vec<UpvalueCapture>, u8) {
|
|
||||||
let mut captures: Vec<UpvalueCapture> = Vec::new();
|
|
||||||
let mut seen: HashMap<String, usize> = HashMap::new();
|
|
||||||
|
|
||||||
for stmt in body_stmts {
|
/// 收集当前函数作用域声明的绑定名(参数 + 变量/函数声明,含嵌套 block,不含嵌套函数体)。
|
||||||
self.collect_upvalue_stmt(stmt, parent_ctx, nested_symbols, &mut captures, &mut seen);
|
fn collect_own_binding_names(&self, param_names: &[&str], stmts: &[Statement]) -> HashSet<String> {
|
||||||
|
let mut names = HashSet::new();
|
||||||
|
for p in param_names {
|
||||||
|
names.insert(p.to_string());
|
||||||
|
}
|
||||||
|
self.collect_decl_names_stmt(stmts, &mut names);
|
||||||
|
names
|
||||||
}
|
}
|
||||||
|
|
||||||
let count = captures.len() as u8;
|
fn collect_decl_names_stmt(&self, stmts: &[Statement], out: &mut HashSet<String>) {
|
||||||
(captures, count)
|
for stmt in stmts {
|
||||||
|
match stmt {
|
||||||
|
Statement::VariableDeclaration(vd) => {
|
||||||
|
for d in &vd.declarations {
|
||||||
|
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id {
|
||||||
|
out.insert(bi.name.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Statement::FunctionDeclaration(fd) => {
|
||||||
|
if let Some(id) = &fd.id {
|
||||||
|
out.insert(id.name.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Statement::BlockStatement(b) => self.collect_decl_names_stmt(&b.body, out),
|
||||||
|
Statement::IfStatement(is) => {
|
||||||
|
self.collect_decl_names_stmt(std::slice::from_ref(&is.consequent), out);
|
||||||
|
if let Some(alt) = &is.alternate {
|
||||||
|
self.collect_decl_names_stmt(std::slice::from_ref(alt), out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Statement::WhileStatement(w) => self.collect_decl_names_stmt(std::slice::from_ref(&w.body), out),
|
||||||
|
Statement::DoWhileStatement(d) => self.collect_decl_names_stmt(std::slice::from_ref(&d.body), out),
|
||||||
|
Statement::ForStatement(f) => {
|
||||||
|
if let Some(oxide_parser::ForStatementInit::VariableDeclaration(vd)) = &f.init {
|
||||||
|
for d in &vd.declarations {
|
||||||
|
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id {
|
||||||
|
out.insert(bi.name.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.collect_decl_names_stmt(std::slice::from_ref(&f.body), out);
|
||||||
|
}
|
||||||
|
Statement::SwitchStatement(sw) => {
|
||||||
|
for case in &sw.cases {
|
||||||
|
self.collect_decl_names_stmt(&case.consequent, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Statement::TryStatement(ts) => {
|
||||||
|
self.collect_decl_names_stmt(&ts.block.body, out);
|
||||||
|
if let Some(h) = &ts.handler {
|
||||||
|
self.collect_decl_names_stmt(&h.body.body, out);
|
||||||
|
}
|
||||||
|
if let Some(f) = &ts.finalizer {
|
||||||
|
self.collect_decl_names_stmt(&f.body, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Statement::LabeledStatement(ls) => self.collect_decl_names_stmt(std::slice::from_ref(&ls.body), out),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_upvalue_stmt(
|
/// 扫描 stmts 内(含任意深度嵌套函数)对 `ref_set` 的引用,写入 out。
|
||||||
&self, stmt: &Statement, parent_ctx: &CompileCtx, nested_symbols: &SymbolTable,
|
/// 递归进入嵌套函数时累加其局部绑定为遮蔽集,避免把内层局部误判为捕获。
|
||||||
captures: &mut Vec<UpvalueCapture>, seen: &mut HashMap<String, usize>,
|
fn collect_capture_names(&self, stmts: &[Statement], ref_set: &HashSet<String>, out: &mut HashSet<String>) {
|
||||||
|
let shadow = HashSet::new();
|
||||||
|
self.collect_capture_names_shadowed(stmts, ref_set, &shadow, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_capture_names_shadowed(
|
||||||
|
&self, stmts: &[Statement], ref_set: &HashSet<String>, shadow: &HashSet<String>, out: &mut HashSet<String>,
|
||||||
|
) {
|
||||||
|
for stmt in stmts {
|
||||||
|
self.collect_capture_names_stmt(stmt, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_capture_names_stmt(
|
||||||
|
&self, stmt: &Statement, ref_set: &HashSet<String>, shadow: &HashSet<String>, out: &mut HashSet<String>,
|
||||||
) {
|
) {
|
||||||
match stmt {
|
match stmt {
|
||||||
Statement::ExpressionStatement(es) => {
|
Statement::ExpressionStatement(es) => self.collect_capture_names_expr(&es.expression, ref_set, shadow, out),
|
||||||
self.collect_upvalue_expr(&es.expression, parent_ctx, nested_symbols, captures, seen);
|
Statement::ReturnStatement(rs) => {
|
||||||
|
if let Some(a) = &rs.argument {
|
||||||
|
self.collect_capture_names_expr(a, ref_set, shadow, out);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Statement::VariableDeclaration(vd) => {
|
Statement::VariableDeclaration(vd) => {
|
||||||
for decl in &vd.declarations {
|
for d in &vd.declarations {
|
||||||
if let Some(init) = &decl.init {
|
if let Some(init) = &d.init {
|
||||||
self.collect_upvalue_expr(init, parent_ctx, nested_symbols, captures, seen);
|
self.collect_capture_names_expr(init, ref_set, shadow, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::ReturnStatement(rs) => {
|
Statement::FunctionDeclaration(fd) => {
|
||||||
if let Some(expr) = &rs.argument {
|
let body: &[Statement] = fd.body.as_ref().map(|b| &b.statements[..]).unwrap_or(&[]);
|
||||||
self.collect_upvalue_expr(expr, parent_ctx, nested_symbols, captures, seen);
|
let mut inner = shadow.clone();
|
||||||
|
inner.extend(self.collect_own_binding_names(&[], body));
|
||||||
|
self.collect_capture_names_shadowed(body, ref_set, &inner, out);
|
||||||
|
}
|
||||||
|
Statement::IfStatement(is) => {
|
||||||
|
self.collect_capture_names_expr(&is.test, ref_set, shadow, out);
|
||||||
|
self.collect_capture_names_stmt(&is.consequent, ref_set, shadow, out);
|
||||||
|
if let Some(alt) = &is.alternate {
|
||||||
|
self.collect_capture_names_stmt(alt, ref_set, shadow, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::ForStatement(fs) => {
|
Statement::ForStatement(fs) => {
|
||||||
if let Some(init) = &fs.init {
|
if let Some(init) = &fs.init {
|
||||||
if let Some(e) = init.as_expression() {
|
if let Some(e) = init.as_expression() {
|
||||||
self.collect_upvalue_expr(e, parent_ctx, nested_symbols, captures, seen);
|
self.collect_capture_names_expr(e, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
if let oxide_parser::ForStatementInit::VariableDeclaration(vd) = init {
|
||||||
|
for d in &vd.declarations {
|
||||||
|
if let Some(i) = &d.init {
|
||||||
|
self.collect_capture_names_expr(i, ref_set, shadow, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(test) = &fs.test {
|
|
||||||
self.collect_upvalue_expr(test, parent_ctx, nested_symbols, captures, seen);
|
|
||||||
}
|
|
||||||
if let Some(update) = &fs.update {
|
|
||||||
self.collect_upvalue_expr(update, parent_ctx, nested_symbols, captures, seen);
|
|
||||||
}
|
|
||||||
self.collect_upvalue_stmt(&fs.body, parent_ctx, nested_symbols, captures, seen);
|
|
||||||
}
|
|
||||||
Statement::IfStatement(is) => {
|
|
||||||
self.collect_upvalue_expr(&is.test, parent_ctx, nested_symbols, captures, seen);
|
|
||||||
self.collect_upvalue_stmt(&is.consequent, parent_ctx, nested_symbols, captures, seen);
|
|
||||||
if let Some(alt) = &is.alternate {
|
|
||||||
self.collect_upvalue_stmt(alt, parent_ctx, nested_symbols, captures, seen);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::WhileStatement(ws) => {
|
if let Some(t) = &fs.test {
|
||||||
self.collect_upvalue_expr(&ws.test, parent_ctx, nested_symbols, captures, seen);
|
self.collect_capture_names_expr(t, ref_set, shadow, out);
|
||||||
self.collect_upvalue_stmt(&ws.body, parent_ctx, nested_symbols, captures, seen);
|
|
||||||
}
|
}
|
||||||
Statement::DoWhileStatement(dw) => {
|
if let Some(u) = &fs.update {
|
||||||
self.collect_upvalue_stmt(&dw.body, parent_ctx, nested_symbols, captures, seen);
|
self.collect_capture_names_expr(u, ref_set, shadow, out);
|
||||||
self.collect_upvalue_expr(&dw.test, parent_ctx, nested_symbols, captures, seen);
|
|
||||||
}
|
}
|
||||||
Statement::BlockStatement(bs) => {
|
self.collect_capture_names_stmt(&fs.body, ref_set, shadow, out);
|
||||||
for s in &bs.body {
|
|
||||||
self.collect_upvalue_stmt(s, parent_ctx, nested_symbols, captures, seen);
|
|
||||||
}
|
}
|
||||||
|
Statement::WhileStatement(w) => {
|
||||||
|
self.collect_capture_names_expr(&w.test, ref_set, shadow, out);
|
||||||
|
self.collect_capture_names_stmt(&w.body, ref_set, shadow, out);
|
||||||
}
|
}
|
||||||
|
Statement::DoWhileStatement(d) => {
|
||||||
|
self.collect_capture_names_stmt(&d.body, ref_set, shadow, out);
|
||||||
|
self.collect_capture_names_expr(&d.test, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
Statement::ForInStatement(fi) => {
|
||||||
|
self.collect_capture_names_expr(&fi.right, ref_set, shadow, out);
|
||||||
|
self.collect_capture_names_stmt(&fi.body, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
Statement::ForOfStatement(fo) => {
|
||||||
|
self.collect_capture_names_expr(&fo.right, ref_set, shadow, out);
|
||||||
|
self.collect_capture_names_stmt(&fo.body, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
Statement::BlockStatement(b) => self.collect_capture_names_shadowed(&b.body, ref_set, shadow, out),
|
||||||
Statement::TryStatement(ts) => {
|
Statement::TryStatement(ts) => {
|
||||||
for s in &ts.block.body {
|
for s in &ts.block.body {
|
||||||
self.collect_upvalue_stmt(s, parent_ctx, nested_symbols, captures, seen);
|
self.collect_capture_names_stmt(s, ref_set, shadow, out);
|
||||||
}
|
}
|
||||||
if let Some(handler) = &ts.handler {
|
if let Some(h) = &ts.handler {
|
||||||
for s in &handler.body.body {
|
for s in &h.body.body {
|
||||||
self.collect_upvalue_stmt(s, parent_ctx, nested_symbols, captures, seen);
|
self.collect_capture_names_stmt(s, ref_set, shadow, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(finalizer) = &ts.finalizer {
|
if let Some(f) = &ts.finalizer {
|
||||||
for s in &finalizer.body {
|
for s in &f.body {
|
||||||
self.collect_upvalue_stmt(s, parent_ctx, nested_symbols, captures, seen);
|
self.collect_capture_names_stmt(s, ref_set, shadow, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::ThrowStatement(ts) => {
|
Statement::ThrowStatement(ts) => self.collect_capture_names_expr(&ts.argument, ref_set, shadow, out),
|
||||||
self.collect_upvalue_expr(&ts.argument, parent_ctx, nested_symbols, captures, seen);
|
Statement::SwitchStatement(sw) => {
|
||||||
}
|
self.collect_capture_names_expr(&sw.discriminant, ref_set, shadow, out);
|
||||||
Statement::SwitchStatement(ss) => {
|
for case in &sw.cases {
|
||||||
self.collect_upvalue_expr(&ss.discriminant, parent_ctx, nested_symbols, captures, seen);
|
|
||||||
for case in &ss.cases {
|
|
||||||
for s in &case.consequent {
|
for s in &case.consequent {
|
||||||
self.collect_upvalue_stmt(s, parent_ctx, nested_symbols, captures, seen);
|
self.collect_capture_names_stmt(s, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Statement::LabeledStatement(ls) => self.collect_capture_names_stmt(&ls.body, ref_set, shadow, out),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_capture_names_expr(
|
||||||
|
&self, expr: &Expression, ref_set: &HashSet<String>, shadow: &HashSet<String>, out: &mut HashSet<String>,
|
||||||
|
) {
|
||||||
|
match expr {
|
||||||
|
Expression::Identifier(id) => {
|
||||||
|
let name = id.name.as_str();
|
||||||
|
if ref_set.contains(name) && !shadow.contains(name) {
|
||||||
|
out.insert(id.name.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expression::AssignmentExpression(ae) => {
|
||||||
|
if let oxide_parser::AssignmentTarget::AssignmentTargetIdentifier(ati) = &ae.left {
|
||||||
|
let name = ati.name.as_str();
|
||||||
|
if ref_set.contains(name) && !shadow.contains(name) {
|
||||||
|
out.insert(ati.name.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.collect_capture_names_expr(&ae.right, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
Expression::UpdateExpression(ue) => {
|
||||||
|
if let oxide_parser::SimpleAssignmentTarget::AssignmentTargetIdentifier(ati) = &ue.argument {
|
||||||
|
let name = ati.name.as_str();
|
||||||
|
if ref_set.contains(name) && !shadow.contains(name) {
|
||||||
|
out.insert(ati.name.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expression::FunctionExpression(fe) => {
|
||||||
|
let body: &[Statement] = fe.body.as_ref().map(|b| &b.statements[..]).unwrap_or(&[]);
|
||||||
|
let mut inner = shadow.clone();
|
||||||
|
inner.extend(self.collect_own_binding_names(&[], body));
|
||||||
|
self.collect_capture_names_shadowed(body, ref_set, &inner, out);
|
||||||
|
}
|
||||||
|
Expression::ArrowFunctionExpression(ae) => {
|
||||||
|
let mut inner = shadow.clone();
|
||||||
|
inner.extend(self.collect_own_binding_names(&[], &ae.body.statements));
|
||||||
|
self.collect_capture_names_shadowed(&ae.body.statements, ref_set, &inner, out);
|
||||||
|
}
|
||||||
|
Expression::BinaryExpression(be) => {
|
||||||
|
self.collect_capture_names_expr(&be.left, ref_set, shadow, out);
|
||||||
|
self.collect_capture_names_expr(&be.right, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
Expression::UnaryExpression(ue) => self.collect_capture_names_expr(&ue.argument, ref_set, shadow, out),
|
||||||
|
Expression::CallExpression(ce) => {
|
||||||
|
self.collect_capture_names_expr(&ce.callee, ref_set, shadow, out);
|
||||||
|
for a in &ce.arguments {
|
||||||
|
if let Some(e) = a.as_expression() {
|
||||||
|
self.collect_capture_names_expr(e, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expression::NewExpression(ne) => {
|
||||||
|
self.collect_capture_names_expr(&ne.callee, ref_set, shadow, out);
|
||||||
|
for a in &ne.arguments {
|
||||||
|
if let Some(e) = a.as_expression() {
|
||||||
|
self.collect_capture_names_expr(e, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expression::SequenceExpression(se) => {
|
||||||
|
for e in &se.expressions {
|
||||||
|
self.collect_capture_names_expr(e, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expression::ConditionalExpression(ce) => {
|
||||||
|
self.collect_capture_names_expr(&ce.test, ref_set, shadow, out);
|
||||||
|
self.collect_capture_names_expr(&ce.consequent, ref_set, shadow, out);
|
||||||
|
self.collect_capture_names_expr(&ce.alternate, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
Expression::ArrayExpression(ae) => {
|
||||||
|
for e in &ae.elements {
|
||||||
|
if let Some(e) = e.as_expression() {
|
||||||
|
self.collect_capture_names_expr(e, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expression::LogicalExpression(le) => {
|
||||||
|
self.collect_capture_names_expr(&le.left, ref_set, shadow, out);
|
||||||
|
self.collect_capture_names_expr(&le.right, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
Expression::ComputedMemberExpression(m) => {
|
||||||
|
self.collect_capture_names_expr(&m.object, ref_set, shadow, out);
|
||||||
|
self.collect_capture_names_expr(&m.expression, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
Expression::StaticMemberExpression(m) => self.collect_capture_names_expr(&m.object, ref_set, shadow, out),
|
||||||
|
Expression::PrivateFieldExpression(m) => self.collect_capture_names_expr(&m.object, ref_set, shadow, out),
|
||||||
|
Expression::ParenthesizedExpression(p) => self.collect_capture_names_expr(&p.expression, ref_set, shadow, out),
|
||||||
|
Expression::TemplateLiteral(tl) => {
|
||||||
|
for e in &tl.expressions {
|
||||||
|
self.collect_capture_names_expr(e, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expression::TaggedTemplateExpression(tt) => {
|
||||||
|
self.collect_capture_names_expr(&tt.tag, ref_set, shadow, out);
|
||||||
|
for e in &tt.quasi.expressions {
|
||||||
|
self.collect_capture_names_expr(e, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expression::ObjectExpression(o) => {
|
||||||
|
for prop in &o.properties {
|
||||||
|
if let oxide_parser::ObjectPropertyKind::ObjectProperty(p) = prop {
|
||||||
|
self.collect_capture_names_expr(&p.value, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expression::ChainExpression(c) => self.collect_capture_names_chain(&c.expression, ref_set, shadow, out),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_capture_names_chain(
|
||||||
|
&self, element: &oxide_parser::ChainElement, ref_set: &HashSet<String>, shadow: &HashSet<String>,
|
||||||
|
out: &mut HashSet<String>,
|
||||||
|
) {
|
||||||
|
match element {
|
||||||
|
oxide_parser::ChainElement::StaticMemberExpression(m) => {
|
||||||
|
self.collect_capture_names_expr(&m.object, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
oxide_parser::ChainElement::ComputedMemberExpression(m) => {
|
||||||
|
self.collect_capture_names_expr(&m.object, ref_set, shadow, out);
|
||||||
|
self.collect_capture_names_expr(&m.expression, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
oxide_parser::ChainElement::PrivateFieldExpression(m) => {
|
||||||
|
self.collect_capture_names_expr(&m.object, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
oxide_parser::ChainElement::CallExpression(call) => {
|
||||||
|
self.collect_capture_names_expr(&call.callee, ref_set, shadow, out);
|
||||||
|
for a in &call.arguments {
|
||||||
|
if let Some(e) = a.as_expression() {
|
||||||
|
self.collect_capture_names_expr(e, ref_set, shadow, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -604,122 +835,147 @@ impl Compiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_upvalue_expr(
|
/// 分析本函数:哪些绑定被任意深度嵌套函数捕获 → captured_bindings。
|
||||||
&self, expr: &Expression, parent_ctx: &CompileCtx, nested_symbols: &SymbolTable,
|
fn collect_captured_bindings(&self, stmts: &[Statement], own: &HashSet<String>) -> HashSet<String> {
|
||||||
captures: &mut Vec<UpvalueCapture>, seen: &mut HashMap<String, usize>,
|
let mut captured = HashSet::new();
|
||||||
) {
|
for stmt in stmts {
|
||||||
|
self.collect_captured_stmt(stmt, own, &mut captured);
|
||||||
|
}
|
||||||
|
captured
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 只从嵌套函数节点进入扫描(本函数直接引用不算捕获)。
|
||||||
|
fn collect_captured_stmt(&self, stmt: &Statement, own: &HashSet<String>, out: &mut HashSet<String>) {
|
||||||
|
match stmt {
|
||||||
|
Statement::FunctionDeclaration(fd) => {
|
||||||
|
let body: &[Statement] = fd.body.as_ref().map(|b| &b.statements[..]).unwrap_or(&[]);
|
||||||
|
self.collect_capture_names(body, own, out);
|
||||||
|
}
|
||||||
|
Statement::ExpressionStatement(es) => self.collect_captured_expr(&es.expression, own, out),
|
||||||
|
Statement::ReturnStatement(rs) => {
|
||||||
|
if let Some(a) = &rs.argument {
|
||||||
|
self.collect_captured_expr(a, own, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Statement::VariableDeclaration(vd) => {
|
||||||
|
for d in &vd.declarations {
|
||||||
|
if let Some(init) = &d.init {
|
||||||
|
self.collect_captured_expr(init, own, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Statement::IfStatement(is) => {
|
||||||
|
self.collect_captured_expr(&is.test, own, out);
|
||||||
|
self.collect_captured_stmt(&is.consequent, own, out);
|
||||||
|
if let Some(alt) = &is.alternate {
|
||||||
|
self.collect_captured_stmt(alt, own, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Statement::ForStatement(fs) => {
|
||||||
|
if let Some(init) = &fs.init {
|
||||||
|
if let Some(e) = init.as_expression() {
|
||||||
|
self.collect_captured_expr(e, own, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(t) = &fs.test {
|
||||||
|
self.collect_captured_expr(t, own, out);
|
||||||
|
}
|
||||||
|
if let Some(u) = &fs.update {
|
||||||
|
self.collect_captured_expr(u, own, out);
|
||||||
|
}
|
||||||
|
self.collect_captured_stmt(&fs.body, own, out);
|
||||||
|
}
|
||||||
|
Statement::WhileStatement(w) => {
|
||||||
|
self.collect_captured_expr(&w.test, own, out);
|
||||||
|
self.collect_captured_stmt(&w.body, own, out);
|
||||||
|
}
|
||||||
|
Statement::DoWhileStatement(d) => {
|
||||||
|
self.collect_captured_stmt(&d.body, own, out);
|
||||||
|
self.collect_captured_expr(&d.test, own, out);
|
||||||
|
}
|
||||||
|
Statement::ForInStatement(fi) => {
|
||||||
|
self.collect_captured_expr(&fi.right, own, out);
|
||||||
|
self.collect_captured_stmt(&fi.body, own, out);
|
||||||
|
}
|
||||||
|
Statement::ForOfStatement(fo) => {
|
||||||
|
self.collect_captured_expr(&fo.right, own, out);
|
||||||
|
self.collect_captured_stmt(&fo.body, own, out);
|
||||||
|
}
|
||||||
|
Statement::BlockStatement(b) => {
|
||||||
|
for s in &b.body {
|
||||||
|
self.collect_captured_stmt(s, own, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Statement::TryStatement(ts) => {
|
||||||
|
for s in &ts.block.body {
|
||||||
|
self.collect_captured_stmt(s, own, out);
|
||||||
|
}
|
||||||
|
if let Some(h) = &ts.handler {
|
||||||
|
for s in &h.body.body {
|
||||||
|
self.collect_captured_stmt(s, own, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(f) = &ts.finalizer {
|
||||||
|
for s in &f.body {
|
||||||
|
self.collect_captured_stmt(s, own, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Statement::ThrowStatement(ts) => self.collect_captured_expr(&ts.argument, own, out),
|
||||||
|
Statement::SwitchStatement(sw) => {
|
||||||
|
self.collect_captured_expr(&sw.discriminant, own, out);
|
||||||
|
for case in &sw.cases {
|
||||||
|
for s in &case.consequent {
|
||||||
|
self.collect_captured_stmt(s, own, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Statement::LabeledStatement(ls) => self.collect_captured_stmt(&ls.body, own, out),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_captured_expr(&self, expr: &Expression, own: &HashSet<String>, out: &mut HashSet<String>) {
|
||||||
match expr {
|
match expr {
|
||||||
Expression::Identifier(ident) => {
|
Expression::FunctionExpression(fe) => {
|
||||||
let name = ident.name.as_str();
|
let body: &[Statement] = fe.body.as_ref().map(|b| &b.statements[..]).unwrap_or(&[]);
|
||||||
if nested_symbols.lookup_any_binding(name).is_some() {
|
self.collect_capture_names(body, own, out);
|
||||||
// Check if this is a parent function-scope binding (real upvalue)
|
|
||||||
let is_parent_func_var = parent_ctx
|
|
||||||
.scopes
|
|
||||||
.symbols
|
|
||||||
.scopes
|
|
||||||
.iter()
|
|
||||||
.skip(1)
|
|
||||||
.any(|s| s.bindings.contains_key(name));
|
|
||||||
if !is_parent_func_var {
|
|
||||||
return; // true local or global, not upvalue
|
|
||||||
}
|
|
||||||
// falls through to capture
|
|
||||||
}
|
|
||||||
if let Some((binding, _)) = parent_ctx.scopes.symbols.lookup_any_binding(name) {
|
|
||||||
if seen.contains_key(name) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let cell_idx = captures.len() as u8;
|
|
||||||
seen.insert(name.to_string(), cell_idx as usize);
|
|
||||||
captures.push(UpvalueCapture {
|
|
||||||
name: name.to_string(),
|
|
||||||
enclosing_reg: binding.reg,
|
|
||||||
cell_idx,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expression::AssignmentExpression(ae) => {
|
|
||||||
if let oxide_parser::AssignmentTarget::AssignmentTargetIdentifier(ati) = &ae.left {
|
|
||||||
let name = ati.name.as_str();
|
|
||||||
let in_nested = nested_symbols.lookup_any_binding(name).is_some();
|
|
||||||
let is_parent_func_var = parent_ctx
|
|
||||||
.scopes
|
|
||||||
.symbols
|
|
||||||
.scopes
|
|
||||||
.iter()
|
|
||||||
.skip(1)
|
|
||||||
.any(|s| s.bindings.contains_key(name));
|
|
||||||
if !in_nested || is_parent_func_var {
|
|
||||||
if let Some((binding, _)) = parent_ctx.scopes.symbols.lookup_any_binding(name) {
|
|
||||||
if !seen.contains_key(name) {
|
|
||||||
let cell_idx = captures.len() as u8;
|
|
||||||
seen.insert(name.to_string(), cell_idx as usize);
|
|
||||||
captures.push(UpvalueCapture {
|
|
||||||
name: name.to_string(),
|
|
||||||
enclosing_reg: binding.reg,
|
|
||||||
cell_idx,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.collect_upvalue_expr(&ae.right, parent_ctx, nested_symbols, captures, seen);
|
|
||||||
}
|
|
||||||
Expression::BinaryExpression(be) => {
|
|
||||||
self.collect_upvalue_expr(&be.left, parent_ctx, nested_symbols, captures, seen);
|
|
||||||
self.collect_upvalue_expr(&be.right, parent_ctx, nested_symbols, captures, seen);
|
|
||||||
}
|
|
||||||
Expression::UnaryExpression(ue) => {
|
|
||||||
self.collect_upvalue_expr(&ue.argument, parent_ctx, nested_symbols, captures, seen);
|
|
||||||
}
|
|
||||||
Expression::UpdateExpression(ue) => {
|
|
||||||
if let oxide_parser::SimpleAssignmentTarget::AssignmentTargetIdentifier(ati) = &ue.argument {
|
|
||||||
let name = ati.name.as_str();
|
|
||||||
let in_nested = nested_symbols.lookup_any_binding(name).is_some();
|
|
||||||
let is_parent_func_var = parent_ctx
|
|
||||||
.scopes
|
|
||||||
.symbols
|
|
||||||
.scopes
|
|
||||||
.iter()
|
|
||||||
.skip(1)
|
|
||||||
.any(|s| s.bindings.contains_key(name));
|
|
||||||
if !in_nested || is_parent_func_var {
|
|
||||||
if let Some((binding, _)) = parent_ctx.scopes.symbols.lookup_any_binding(name) {
|
|
||||||
if !seen.contains_key(name) {
|
|
||||||
let cell_idx = captures.len() as u8;
|
|
||||||
seen.insert(name.to_string(), cell_idx as usize);
|
|
||||||
captures.push(UpvalueCapture {
|
|
||||||
name: name.to_string(),
|
|
||||||
enclosing_reg: binding.reg,
|
|
||||||
cell_idx,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Expression::ArrowFunctionExpression(ae) => {
|
||||||
|
self.collect_capture_names(&ae.body.statements, own, out);
|
||||||
}
|
}
|
||||||
Expression::CallExpression(ce) => {
|
Expression::CallExpression(ce) => {
|
||||||
self.collect_upvalue_expr(&ce.callee, parent_ctx, nested_symbols, captures, seen);
|
self.collect_captured_expr(&ce.callee, own, out);
|
||||||
for arg in &ce.arguments {
|
for a in &ce.arguments {
|
||||||
if let Some(e) = arg.as_expression() {
|
if let Some(e) = a.as_expression() {
|
||||||
self.collect_upvalue_expr(e, parent_ctx, nested_symbols, captures, seen);
|
self.collect_captured_expr(e, own, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::SequenceExpression(se) => {
|
Expression::BinaryExpression(be) => {
|
||||||
for sub_expr in &se.expressions {
|
self.collect_captured_expr(&be.left, own, out);
|
||||||
self.collect_upvalue_expr(sub_expr, parent_ctx, nested_symbols, captures, seen);
|
self.collect_captured_expr(&be.right, own, out);
|
||||||
}
|
}
|
||||||
|
Expression::UnaryExpression(ue) => self.collect_captured_expr(&ue.argument, own, out),
|
||||||
|
Expression::LogicalExpression(le) => {
|
||||||
|
self.collect_captured_expr(&le.left, own, out);
|
||||||
|
self.collect_captured_expr(&le.right, own, out);
|
||||||
}
|
}
|
||||||
Expression::ConditionalExpression(ce) => {
|
Expression::ConditionalExpression(ce) => {
|
||||||
self.collect_upvalue_expr(&ce.test, parent_ctx, nested_symbols, captures, seen);
|
self.collect_captured_expr(&ce.test, own, out);
|
||||||
self.collect_upvalue_expr(&ce.consequent, parent_ctx, nested_symbols, captures, seen);
|
self.collect_captured_expr(&ce.consequent, own, out);
|
||||||
self.collect_upvalue_expr(&ce.alternate, parent_ctx, nested_symbols, captures, seen);
|
self.collect_captured_expr(&ce.alternate, own, out);
|
||||||
}
|
}
|
||||||
|
Expression::SequenceExpression(se) => {
|
||||||
|
for e in &se.expressions {
|
||||||
|
self.collect_captured_expr(e, own, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expression::AssignmentExpression(ae) => self.collect_captured_expr(&ae.right, own, out),
|
||||||
Expression::ArrayExpression(ae) => {
|
Expression::ArrayExpression(ae) => {
|
||||||
for elem in &ae.elements {
|
for e in &ae.elements {
|
||||||
if let Some(e) = elem.as_expression() {
|
if let Some(e) = e.as_expression() {
|
||||||
self.collect_upvalue_expr(e, parent_ctx, nested_symbols, captures, seen);
|
self.collect_captured_expr(e, own, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -727,6 +983,24 @@ impl Compiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 分析子函数 body:引用父级绑定的名字 → upvalue_captures(enclosing_reg 由父 emit 完成后填充)。
|
||||||
|
fn collect_upvalue_names(
|
||||||
|
&self, body_stmts: &[Statement], parent_own: &HashSet<String>, sub_own: &HashSet<String>,
|
||||||
|
) -> Vec<UpvalueCapture> {
|
||||||
|
let mut names = HashSet::new();
|
||||||
|
self.collect_capture_names_shadowed(body_stmts, parent_own, sub_own, &mut names);
|
||||||
|
let mut captures = Vec::with_capacity(names.len());
|
||||||
|
for (cell_idx, name) in names.iter().enumerate() {
|
||||||
|
captures.push(UpvalueCapture {
|
||||||
|
name: name.clone(),
|
||||||
|
enclosing_reg: 0, // assemble_ir 时从父符号表填充
|
||||||
|
cell_idx: cell_idx as u8,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
captures
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub(crate) fn extract_function_parts<'a>(
|
pub(crate) fn extract_function_parts<'a>(
|
||||||
&self, function: &'a oxide_parser::Function<'a>,
|
&self, function: &'a oxide_parser::Function<'a>,
|
||||||
) -> Result<(Vec<ParamSpec<'a>>, &'a [Statement<'a>]), String> {
|
) -> Result<(Vec<ParamSpec<'a>>, &'a [Statement<'a>]), String> {
|
||||||
@@ -790,48 +1064,6 @@ impl Compiler {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn pre_scan_function_expressions(&self, stmts: &[Statement], parent_ctx: &mut CompileCtx) -> Result<(), String> {
|
|
||||||
for stmt in stmts {
|
|
||||||
self.pre_scan_stmt(stmt, parent_ctx)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pre_scan_stmt(&self, stmt: &Statement, parent_ctx: &mut CompileCtx) -> Result<(), String> {
|
|
||||||
match stmt {
|
|
||||||
Statement::ExpressionStatement(es) => self.pre_scan_expr(&es.expression, parent_ctx)?,
|
|
||||||
Statement::ReturnStatement(rs) => {
|
|
||||||
if let Some(a) = &rs.argument {
|
|
||||||
self.pre_scan_expr(a, parent_ctx)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Statement::IfStatement(is) => {
|
|
||||||
self.pre_scan_expr(&is.test, parent_ctx)?;
|
|
||||||
self.pre_scan_stmt(&is.consequent, parent_ctx)?;
|
|
||||||
if let Some(alt) = &is.alternate {
|
|
||||||
self.pre_scan_stmt(alt, parent_ctx)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Statement::ForStatement(fs) => {
|
|
||||||
if let Some(init) = &fs.init {
|
|
||||||
if let Some(e) = init.as_expression() {
|
|
||||||
self.pre_scan_expr(e, parent_ctx)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(t) = &fs.test {
|
|
||||||
self.pre_scan_expr(t, parent_ctx)?;
|
|
||||||
}
|
|
||||||
if let Some(u) = &fs.update {
|
|
||||||
self.pre_scan_expr(u, parent_ctx)?;
|
|
||||||
}
|
|
||||||
self.pre_scan_stmt(&fs.body, parent_ctx)?;
|
|
||||||
}
|
|
||||||
Statement::BlockStatement(bs) => self.pre_scan_function_expressions(&bs.body, parent_ctx)?,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pre-register all builtin identifiers referenced anywhere in this body
|
/// Pre-register all builtin identifiers referenced anywhere in this body
|
||||||
/// (expressions, member objects, call args, class fields, etc.) so their
|
/// (expressions, member objects, call args, class fields, etc.) so their
|
||||||
@@ -1095,58 +1327,6 @@ impl Compiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_scan_expr(&self, expr: &Expression, parent_ctx: &mut CompileCtx) -> Result<(), String> {
|
|
||||||
match expr {
|
|
||||||
Expression::FunctionExpression(fe) => {
|
|
||||||
let body: &[Statement] = fe.body.as_ref().map(|b| &b.statements[..]).unwrap_or(&[]);
|
|
||||||
let nested_symbols = SymbolTable::new();
|
|
||||||
let (captures, _) = self.analyze_upvalue_captures(body, parent_ctx, &nested_symbols);
|
|
||||||
for up in &captures {
|
|
||||||
if let Some((binding, _)) = parent_ctx.scopes.symbols.lookup_any_binding(&up.name) {
|
|
||||||
binding.is_captured.set(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expression::ArrowFunctionExpression(_ae) => {}
|
|
||||||
Expression::CallExpression(ce) => {
|
|
||||||
self.pre_scan_expr(&ce.callee, parent_ctx)?;
|
|
||||||
for arg in &ce.arguments {
|
|
||||||
if let Some(e) = arg.as_expression() {
|
|
||||||
self.pre_scan_expr(e, parent_ctx)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expression::BinaryExpression(be) => {
|
|
||||||
self.pre_scan_expr(&be.left, parent_ctx)?;
|
|
||||||
self.pre_scan_expr(&be.right, parent_ctx)?;
|
|
||||||
}
|
|
||||||
Expression::ConditionalExpression(ce) => {
|
|
||||||
self.pre_scan_expr(&ce.test, parent_ctx)?;
|
|
||||||
self.pre_scan_expr(&ce.consequent, parent_ctx)?;
|
|
||||||
self.pre_scan_expr(&ce.alternate, parent_ctx)?;
|
|
||||||
}
|
|
||||||
Expression::ArrayExpression(ae) => {
|
|
||||||
for e in &ae.elements {
|
|
||||||
if let Some(e) = e.as_expression() {
|
|
||||||
self.pre_scan_expr(e, parent_ctx)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expression::SequenceExpression(se) => {
|
|
||||||
for e in &se.expressions {
|
|
||||||
self.pre_scan_expr(e, parent_ctx)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expression::AssignmentExpression(ae) => {
|
|
||||||
self.pre_scan_expr(&ae.right, parent_ctx)?;
|
|
||||||
}
|
|
||||||
Expression::UnaryExpression(ue) => {
|
|
||||||
self.pre_scan_expr(&ue.argument, parent_ctx)?;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn predeclare_function_declarations(&self, statements: &[Statement], ctx: &mut CompileCtx) {
|
fn predeclare_function_declarations(&self, statements: &[Statement], ctx: &mut CompileCtx) {
|
||||||
for statement in statements {
|
for statement in statements {
|
||||||
@@ -1269,7 +1449,6 @@ impl Compiler {
|
|||||||
reg: binding.reg,
|
reg: binding.reg,
|
||||||
initialized: binding.initialized,
|
initialized: binding.initialized,
|
||||||
is_const: binding.is_const,
|
is_const: binding.is_const,
|
||||||
is_captured: binding.is_captured.clone(),
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
inherited_reg_start = inherited_reg_start.max(binding.reg.saturating_add(1));
|
inherited_reg_start = inherited_reg_start.max(binding.reg.saturating_add(1));
|
||||||
@@ -1281,7 +1460,6 @@ impl Compiler {
|
|||||||
reg: *reg,
|
reg: *reg,
|
||||||
initialized: true,
|
initialized: true,
|
||||||
is_const: true,
|
is_const: true,
|
||||||
is_captured: Cell::new(false),
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
inherited_reg_start = inherited_reg_start.max(reg.saturating_add(1));
|
inherited_reg_start = inherited_reg_start.max(reg.saturating_add(1));
|
||||||
@@ -1292,22 +1470,6 @@ impl Compiler {
|
|||||||
// same register offset (params go after builtin slots).
|
// same register offset (params go after builtin slots).
|
||||||
ctx.reset_regs();
|
ctx.reset_regs();
|
||||||
|
|
||||||
// Free variable analysis for upvalue capture (Ordinary + Arrow functions only)
|
|
||||||
if matches!(body_context, FunctionBodyContext::Ordinary | FunctionBodyContext::Arrow) {
|
|
||||||
let (captures, _cells) = self.analyze_upvalue_captures(body_stmts, parent_ctx, &ctx.scopes.symbols);
|
|
||||||
ctx.current_upvalue_captures = captures;
|
|
||||||
ctx.scopes.cell_registry = ctx
|
|
||||||
.current_upvalue_captures
|
|
||||||
.iter()
|
|
||||||
.map(|u| (u.name.clone(), u.cell_idx))
|
|
||||||
.collect();
|
|
||||||
for up in &ctx.current_upvalue_captures {
|
|
||||||
if let Some((binding, _)) = parent_ctx.scopes.symbols.lookup_any_binding(&up.name) {
|
|
||||||
binding.is_captured.set(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function body scope - params and local vars
|
// Function body scope - params and local vars
|
||||||
ctx.push_scope_with_kind(ScopeKind::FunctionScope);
|
ctx.push_scope_with_kind(ScopeKind::FunctionScope);
|
||||||
|
|
||||||
@@ -1327,10 +1489,23 @@ impl Compiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.predeclare_function_declarations(body_stmts, &mut ctx);
|
// 闭包捕获分析(AST 级,emit 前确定,时序无关)
|
||||||
|
let param_names: Vec<&str> = param_specs.iter().map(|s| s.register_name()).collect();
|
||||||
|
ctx.own_bindings = self.collect_own_binding_names(¶m_names, body_stmts);
|
||||||
|
ctx.captured_bindings = self.collect_captured_bindings(body_stmts, &ctx.own_bindings);
|
||||||
|
|
||||||
// Pre-scan: run analysis for nested function expressions to mark parent captures
|
// Free variable analysis for upvalue capture (Ordinary + Arrow functions only)
|
||||||
self.pre_scan_function_expressions(body_stmts, &mut ctx)?;
|
if matches!(body_context, FunctionBodyContext::Ordinary | FunctionBodyContext::Arrow) {
|
||||||
|
let captures = self.collect_upvalue_names(body_stmts, &parent_ctx.own_bindings, &ctx.own_bindings);
|
||||||
|
ctx.current_upvalue_captures = captures;
|
||||||
|
ctx.scopes.cell_registry = ctx
|
||||||
|
.current_upvalue_captures
|
||||||
|
.iter()
|
||||||
|
.map(|u| (u.name.clone(), u.cell_idx))
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.predeclare_function_declarations(body_stmts, &mut ctx);
|
||||||
|
|
||||||
// Pre-register builtin identifier references before emitting any temporary
|
// Pre-register builtin identifier references before emitting any temporary
|
||||||
// register, so builtin slots never collide with reused temporaries.
|
// register, so builtin slots never collide with reused temporaries.
|
||||||
@@ -1401,10 +1576,13 @@ impl Compiler {
|
|||||||
ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(undef_reg as u32), Operand::None, Operand::None));
|
ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(undef_reg as u32), Operand::None, Operand::None));
|
||||||
}
|
}
|
||||||
|
|
||||||
let ir = ctx.assemble_ir(crate::ir::ParamLayout {
|
let ir = ctx.assemble_ir(
|
||||||
|
crate::ir::ParamLayout {
|
||||||
base: param_base as u32,
|
base: param_base as u32,
|
||||||
count: param_specs.len() as u32,
|
count: param_specs.len() as u32,
|
||||||
});
|
},
|
||||||
|
Some(parent_ctx),
|
||||||
|
);
|
||||||
Ok(ir)
|
Ok(ir)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1481,6 +1659,10 @@ impl Compiler {
|
|||||||
// (emitted in the first sub-pass below) can resolve the outer vars.
|
// (emitted in the first sub-pass below) can resolve the outer vars.
|
||||||
self.predeclare_var_declarations(&program.body, &mut ctx);
|
self.predeclare_var_declarations(&program.body, &mut ctx);
|
||||||
|
|
||||||
|
// 闭包捕获分析(AST 级,emit 前确定)
|
||||||
|
ctx.own_bindings = self.collect_own_binding_names(&[], &program.body);
|
||||||
|
ctx.captured_bindings = self.collect_captured_bindings(&program.body, &ctx.own_bindings);
|
||||||
|
|
||||||
// First sub-pass: emit FunctionDeclarations (hoisting)
|
// First sub-pass: emit FunctionDeclarations (hoisting)
|
||||||
// This ensures function objects are available before any code runs.
|
// This ensures function objects are available before any code runs.
|
||||||
for stmt in &program.body {
|
for stmt in &program.body {
|
||||||
@@ -1510,10 +1692,13 @@ impl Compiler {
|
|||||||
|
|
||||||
crate::compiler_debug!("compile: done, {} instructions, {} constants", ctx.insts.len(), ctx.constants.len());
|
crate::compiler_debug!("compile: done, {} instructions, {} constants", ctx.insts.len(), ctx.constants.len());
|
||||||
|
|
||||||
let ir = ctx.assemble_ir(crate::ir::ParamLayout {
|
let ir = ctx.assemble_ir(
|
||||||
|
crate::ir::ParamLayout {
|
||||||
base: ctx.scopes.builtin_reg_map.len() as u32,
|
base: ctx.scopes.builtin_reg_map.len() as u32,
|
||||||
count: 0,
|
count: 0,
|
||||||
});
|
},
|
||||||
|
None,
|
||||||
|
);
|
||||||
lower(&ir)
|
lower(&ir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ impl Compiler {
|
|||||||
return Ok(val_reg);
|
return Ok(val_reg);
|
||||||
}
|
}
|
||||||
// Check if target is a captured cell
|
// Check if target is a captured cell
|
||||||
if ctx.scopes.symbols.lookup_is_captured(name) {
|
if ctx.captured_bindings.contains(name) {
|
||||||
let cell_idx = ctx
|
let cell_idx = ctx
|
||||||
.scopes
|
.scopes
|
||||||
.cell_registry
|
.cell_registry
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ impl Compiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.scopes.symbols.lookup_is_captured(name) {
|
if ctx.captured_bindings.contains(name) {
|
||||||
let cell_idx = ctx
|
let cell_idx = ctx
|
||||||
.scopes
|
.scopes
|
||||||
.cell_registry
|
.cell_registry
|
||||||
|
|||||||
@@ -222,7 +222,7 @@ impl Compiler {
|
|||||||
let name = id.name.as_str();
|
let name = id.name.as_str();
|
||||||
// Check if this is an upvalue or captured cell reference
|
// Check if this is an upvalue or captured cell reference
|
||||||
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
|
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
|
||||||
let is_captured = ctx.scopes.symbols.lookup_is_captured(name);
|
let is_captured = ctx.captured_bindings.contains(name);
|
||||||
if let Some(uv) = uv_idx {
|
if let Some(uv) = uv_idx {
|
||||||
// Upvalue: LOAD_UPVALUE + CONST(1) + ADD/SUB + STORE_UPVALUE
|
// Upvalue: LOAD_UPVALUE + CONST(1) + ADD/SUB + STORE_UPVALUE
|
||||||
let val_reg = ctx.alloc_reg();
|
let val_reg = ctx.alloc_reg();
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ impl Compiler {
|
|||||||
ctx.declare(name, var_reg, kind, is_const)?;
|
ctx.declare(name, var_reg, kind, is_const)?;
|
||||||
var_reg
|
var_reg
|
||||||
};
|
};
|
||||||
if ctx.scopes.symbols.lookup_is_captured(name) {
|
if ctx.captured_bindings.contains(name) {
|
||||||
let cell_idx = ctx.scopes.cell_registry.len() as u8;
|
let cell_idx = ctx.scopes.cell_registry.len() as u8;
|
||||||
ctx.scopes.cell_registry.push((name.to_string(), cell_idx));
|
ctx.scopes.cell_registry.push((name.to_string(), cell_idx));
|
||||||
ctx.inst(Inst::new(OpCode::MAKE_CELL, Operand::Reg(src_reg as u32), Operand::Imm(cell_idx as u16), Operand::None));
|
ctx.inst(Inst::new(OpCode::MAKE_CELL, Operand::Reg(src_reg as u32), Operand::Imm(cell_idx as u16), Operand::None));
|
||||||
|
|||||||
@@ -46,11 +46,11 @@ impl Compiler {
|
|||||||
ctx.declare(bi.name.as_str(), var_reg, decl.kind, is_const)?;
|
ctx.declare(bi.name.as_str(), var_reg, decl.kind, is_const)?;
|
||||||
var_reg
|
var_reg
|
||||||
};
|
};
|
||||||
let is_captured = ctx.scopes.symbols.lookup_is_captured(bi.name.as_str());
|
let is_captured = ctx.captured_bindings.contains(bi.name.as_str());
|
||||||
if is_captured {
|
if is_captured {
|
||||||
let cell_idx = ctx.scopes.cell_registry.len() as u8;
|
let cell_idx = ctx.scopes.cell_registry.len() as u8;
|
||||||
ctx.scopes.cell_registry.push((bi.name.to_string(), cell_idx));
|
ctx.scopes.cell_registry.push((bi.name.to_string(), cell_idx));
|
||||||
ctx.inst(Inst::new(OpCode::MAKE_CELL, Operand::Reg(tmp as u32), Operand::Reg(cell_idx as u32), Operand::None));
|
ctx.inst(Inst::new(OpCode::MAKE_CELL, Operand::Reg(tmp as u32), Operand::Imm(cell_idx as u16), Operand::None));
|
||||||
} else {
|
} else {
|
||||||
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(target_reg as u32), Operand::Reg(tmp as u32), Operand::None));
|
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(target_reg as u32), Operand::Reg(tmp as u32), Operand::None));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use std::cell::Cell;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use oxide_parser::VariableDeclarationKind;
|
use oxide_parser::VariableDeclarationKind;
|
||||||
@@ -18,7 +17,6 @@ pub(crate) struct Binding {
|
|||||||
pub(crate) reg: u8,
|
pub(crate) reg: u8,
|
||||||
pub(crate) initialized: bool,
|
pub(crate) initialized: bool,
|
||||||
pub(crate) is_const: bool,
|
pub(crate) is_const: bool,
|
||||||
pub(crate) is_captured: Cell<bool>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SymbolTable {
|
pub struct SymbolTable {
|
||||||
@@ -86,7 +84,6 @@ impl SymbolTable {
|
|||||||
reg,
|
reg,
|
||||||
initialized: false,
|
initialized: false,
|
||||||
is_const: matches!(kind, VariableDeclarationKind::Const) || is_const,
|
is_const: matches!(kind, VariableDeclarationKind::Const) || is_const,
|
||||||
is_captured: Cell::new(false),
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -132,7 +129,6 @@ impl SymbolTable {
|
|||||||
reg: reg_for_new,
|
reg: reg_for_new,
|
||||||
initialized: true,
|
initialized: true,
|
||||||
is_const: false,
|
is_const: false,
|
||||||
is_captured: Cell::new(false),
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
reg_for_new
|
reg_for_new
|
||||||
@@ -150,15 +146,6 @@ impl SymbolTable {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lookup_is_captured(&self, name: &str) -> bool {
|
|
||||||
for scope in self.scopes.iter().rev() {
|
|
||||||
if let Some(b) = scope.bindings.get(name) {
|
|
||||||
return b.is_captured.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn declare_initialized(
|
pub fn declare_initialized(
|
||||||
&mut self, name: &str, reg: u8, kind: VariableDeclarationKind, is_const: bool,
|
&mut self, name: &str, reg: u8, kind: VariableDeclarationKind, is_const: bool,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
@@ -179,7 +166,6 @@ impl SymbolTable {
|
|||||||
reg,
|
reg,
|
||||||
initialized: true,
|
initialized: true,
|
||||||
is_const: matches!(kind, VariableDeclarationKind::Const) || is_const,
|
is_const: matches!(kind, VariableDeclarationKind::Const) || is_const,
|
||||||
is_captured: Cell::new(false),
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -190,7 +176,6 @@ impl SymbolTable {
|
|||||||
reg,
|
reg,
|
||||||
initialized: true,
|
initialized: true,
|
||||||
is_const: false,
|
is_const: false,
|
||||||
is_captured: Cell::new(false),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -366,6 +366,24 @@ fn compile_closure_capture_emits_upvalue_load() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compile_closure_capture_emits_make_cell_for_captured_var() {
|
||||||
|
let module = compile_source("function outer() { var x = 1; return function() { return x; }; }");
|
||||||
|
assert!(
|
||||||
|
has_opcode(&module, OpCode::MAKE_CELL),
|
||||||
|
"captured outer var should emit MAKE_CELL so the inner closure can read it"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compile_closure_capture_arrow_emits_make_cell() {
|
||||||
|
let module = compile_source("function outer() { let x = 1; return () => x; }");
|
||||||
|
assert!(
|
||||||
|
has_opcode(&module, OpCode::MAKE_CELL),
|
||||||
|
"captured let in arrow closure should emit MAKE_CELL"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn compile_compound_assignment_complement_ops() {
|
fn compile_compound_assignment_complement_ops() {
|
||||||
let module = compile_source("let x = 10; x -= 1; x *= 2; x /= 3; x %= 4;");
|
let module = compile_source("let x = 10; x -= 1; x *= 2; x /= 3; x %= 4;");
|
||||||
|
|||||||
Reference in New Issue
Block a user