fix: 修复嵌套函数调用并接通闭包捕获链路

This commit is contained in:
爱特每羊队
2026-06-30 20:49:23 +08:00
parent 2d8e5d861d
commit 4271b7e348
7 changed files with 82 additions and 23 deletions
+21 -8
View File
@@ -1,3 +1,4 @@
use std::cell::Cell;
use std::collections::HashMap; use std::collections::HashMap;
use crate::emit_ctx::{LabelCtx, ScopeCtx}; use crate::emit_ctx::{LabelCtx, ScopeCtx};
@@ -894,7 +895,7 @@ 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, 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));
@@ -906,7 +907,7 @@ impl Compiler {
reg: *reg, reg: *reg,
initialized: true, initialized: true,
is_const: true, is_const: true,
is_captured: false, 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));
@@ -918,8 +919,6 @@ impl Compiler {
ctx.reset_regs(); ctx.reset_regs();
// Free variable analysis for upvalue capture (Ordinary + Arrow functions only) // Free variable analysis for upvalue capture (Ordinary + Arrow functions only)
// ponytail: temporarily disabled to isolate closure bug
/*
if matches!(body_context, FunctionBodyContext::Ordinary | FunctionBodyContext::Arrow) { if matches!(body_context, FunctionBodyContext::Ordinary | FunctionBodyContext::Arrow) {
let (captures, _cells) = self.analyze_upvalue_captures(body_stmts, parent_ctx, &ctx.scopes.symbols); let (captures, _cells) = self.analyze_upvalue_captures(body_stmts, parent_ctx, &ctx.scopes.symbols);
ctx.current_upvalue_captures = captures; ctx.current_upvalue_captures = captures;
@@ -932,7 +931,6 @@ impl Compiler {
} }
} }
} }
*/
// 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);
@@ -1010,9 +1008,20 @@ impl Compiler {
} }
// Emit body statements. // Emit body statements.
// Capture the last statement's expression result for expression-body arrows. // First sub-pass: emit function declarations (hoisting + marks parent captures)
let mut last_result_reg = None; let mut last_result_reg = None;
for stmt in body_stmts { for stmt in body_stmts {
if matches!(stmt, Statement::FunctionDeclaration(_)) {
if let Some(reg) = self.emit_statement(stmt, &mut ctx)? {
last_result_reg = Some(reg);
}
}
}
// Second sub-pass: emit all other statements
for stmt in body_stmts {
if matches!(stmt, Statement::FunctionDeclaration(_)) {
continue;
}
if let Some(reg) = self.emit_statement(stmt, &mut ctx)? { if let Some(reg) = self.emit_statement(stmt, &mut ctx)? {
last_result_reg = Some(reg); last_result_reg = Some(reg);
} }
@@ -1079,7 +1088,9 @@ impl Compiler {
is_derived_constructor: false, is_derived_constructor: false,
needs_home_object: false, needs_home_object: false,
upvalue_captures: Vec::new(), upvalue_captures: Vec::new(),
cells_needed: 0, cells_needed: ctx.scopes.symbols.scopes[0].bindings.values()
.filter(|b| b.is_captured.get())
.count() as u8,
}) })
} }
@@ -1179,7 +1190,9 @@ impl Compiler {
is_derived_constructor: false, is_derived_constructor: false,
needs_home_object: false, needs_home_object: false,
upvalue_captures: Vec::new(), upvalue_captures: Vec::new(),
cells_needed: 0, cells_needed: ctx.scopes.symbols.scopes[0].bindings.values()
.filter(|b| b.is_captured.get())
.count() as u8,
}) })
} }
} }
+7 -6
View File
@@ -1,3 +1,4 @@
use std::cell::Cell;
use std::collections::HashMap; use std::collections::HashMap;
use oxide_parser::VariableDeclarationKind; use oxide_parser::VariableDeclarationKind;
@@ -17,7 +18,7 @@ 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: bool, pub(crate) is_captured: Cell<bool>,
} }
pub struct SymbolTable { pub struct SymbolTable {
@@ -85,7 +86,7 @@ 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: false, is_captured: Cell::new(false),
}, },
); );
Ok(()) Ok(())
@@ -131,7 +132,7 @@ impl SymbolTable {
reg: reg_for_new, reg: reg_for_new,
initialized: true, initialized: true,
is_const: false, is_const: false,
is_captured: false, is_captured: Cell::new(false),
}, },
); );
reg_for_new reg_for_new
@@ -152,7 +153,7 @@ impl SymbolTable {
pub fn lookup_is_captured(&self, name: &str) -> bool { pub fn lookup_is_captured(&self, name: &str) -> bool {
for scope in self.scopes.iter().rev() { for scope in self.scopes.iter().rev() {
if let Some(b) = scope.bindings.get(name) { if let Some(b) = scope.bindings.get(name) {
return b.is_captured; return b.is_captured.get();
} }
} }
false false
@@ -178,7 +179,7 @@ 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: false, is_captured: Cell::new(false),
}, },
); );
Ok(()) Ok(())
@@ -189,7 +190,7 @@ impl SymbolTable {
reg, reg,
initialized: true, initialized: true,
is_const: false, is_const: false,
is_captured: false, is_captured: Cell::new(false),
}); });
} }
+19 -6
View File
@@ -1,3 +1,5 @@
use std::sync::OnceLock;
use crate::native::NativeFn; use crate::native::NativeFn;
use crate::vm::{native_fn_ptr_to_fn, CallFrame, FrameContinuation, Vm}; use crate::vm::{native_fn_ptr_to_fn, CallFrame, FrameContinuation, Vm};
use crate::{vm_debug, vm_trace}; use crate::{vm_debug, vm_trace};
@@ -346,13 +348,16 @@ impl Vm {
self.saved_bytecode_stack.push(std::mem::take(&mut self.bytecode)); self.saved_bytecode_stack.push(std::mem::take(&mut self.bytecode));
self.saved_immutables_stack.push(self.active_immutables); self.saved_immutables_stack.push(self.active_immutables);
let function_name = self.sub_modules[sub_idx]
.function_name
.as_deref()
.map(|name| self.kernel_core.perm_interner().intern(name).0)
.unwrap_or(0);
self.frames.push(CallFrame { self.frames.push(CallFrame {
return_addr: self.pc, return_addr: self.pc,
function_name: self.sub_modules[sub_idx] function_name,
.function_name
.as_deref()
.map(|name| self.kernel_core.perm_interner().intern(name).0)
.unwrap_or(0),
caller_reg_limit, caller_reg_limit,
saved_reg_offset, saved_reg_offset,
saved_this, saved_this,
@@ -367,13 +372,21 @@ impl Vm {
self.bytecode = sub_bytecode; self.bytecode = sub_bytecode;
let subs = Arc::clone(&self.sub_modules); let subs = Arc::clone(&self.sub_modules);
self.activate_immutables(sub_idx + 1, &subs[sub_idx].constants); self.activate_immutables(sub_idx + 1, &subs[sub_idx].constants);
for (name, reg) in &self.sub_modules[sub_idx].builtin_reg_map { self.cell_stack.push(Vec::with_capacity(subs[sub_idx].cells_needed as usize));
for (name, reg) in &self.sub_modules[sub_idx].builtin_reg_map.clone() {
let si = self.kernel_core.perm_interner().intern(name.as_str()).0; let si = self.kernel_core.perm_interner().intern(name.as_str()).0;
let global = self.session.global_object(); let global = self.session.global_object();
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) { if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) {
self.regs[*reg as usize] = global.get_prop_at(pos); self.regs[*reg as usize] = global.get_prop_at(pos);
} }
} }
self.sub_module_stack.push(Arc::clone(&self.sub_modules));
let callee_subs = &subs[sub_idx].sub_modules;
if !callee_subs.is_empty() {
self.sub_modules = Arc::new(callee_subs.clone());
self.immutables_cache = (0..=callee_subs.len()).map(|_| OnceLock::new()).collect();
}
self.active_reg_limit = sub_n_registers.max(1); self.active_reg_limit = sub_n_registers.max(1);
self.pc = 0; self.pc = 0;
return Ok(true); return Ok(true);
+27 -2
View File
@@ -140,6 +140,7 @@ pub(crate) struct InlineSyncState {
pub(crate) saved_bytecode_stack: Vec<Vec<opcode::Instr>>, pub(crate) saved_bytecode_stack: Vec<Vec<opcode::Instr>>,
pub(crate) saved_immutables_stack: Vec<*const [JsValue]>, pub(crate) saved_immutables_stack: Vec<*const [JsValue]>,
pub(crate) save_stack: Vec<JsValue>, pub(crate) save_stack: Vec<JsValue>,
pub(crate) cell_stack: Vec<Vec<*mut Cell>>,
} }
pub struct Vm { pub struct Vm {
@@ -189,6 +190,7 @@ pub struct Vm {
pub(crate) iters: IterState, pub(crate) iters: IterState,
/// Grouped inline-cache and instruction counters. /// Grouped inline-cache and instruction counters.
pub(crate) profiling: ProfilingState, pub(crate) profiling: ProfilingState,
pub(crate) sub_module_stack: Vec<Arc<Vec<CompiledModule>>>,
pub(crate) cell_stack: Vec<Vec<*mut Cell>>, pub(crate) cell_stack: Vec<Vec<*mut Cell>>,
/// Reusable string buffer for concatenation to avoid allocation per `+` op. /// Reusable string buffer for concatenation to avoid allocation per `+` op.
pub(crate) string_buf: String, pub(crate) string_buf: String,
@@ -721,8 +723,6 @@ impl Vm {
self.saved_bytecode_stack.push(std::mem::take(&mut self.bytecode)); self.saved_bytecode_stack.push(std::mem::take(&mut self.bytecode));
self.saved_immutables_stack.push(self.active_immutables); self.saved_immutables_stack.push(self.active_immutables);
// cell_stack push disabled for debug
// self.cell_stack.push(Vec::with_capacity(...));
let function_name = self.sub_modules[sub_idx] let function_name = self.sub_modules[sub_idx]
.function_name .function_name
@@ -744,9 +744,11 @@ impl Vm {
continuation, continuation,
}); });
self.pc = 0;
self.bytecode = sub_bytecode; self.bytecode = sub_bytecode;
let subs = Arc::clone(&self.sub_modules); let subs = Arc::clone(&self.sub_modules);
self.activate_immutables(sub_idx + 1, &subs[sub_idx].constants); self.activate_immutables(sub_idx + 1, &subs[sub_idx].constants);
self.cell_stack.push(Vec::with_capacity(subs[sub_idx].cells_needed as usize));
for (name, reg) in &self.sub_modules[sub_idx].builtin_reg_map.clone() { for (name, reg) in &self.sub_modules[sub_idx].builtin_reg_map.clone() {
let si = self.kernel_core.perm_interner().intern(name.as_str()).0; let si = self.kernel_core.perm_interner().intern(name.as_str()).0;
let global = self.session.global_object(); let global = self.session.global_object();
@@ -755,6 +757,14 @@ impl Vm {
} }
} }
self.sub_module_stack.push(Arc::clone(&self.sub_modules));
let callee_subs = &subs[sub_idx].sub_modules;
if !callee_subs.is_empty() {
self.sub_modules = Arc::new(callee_subs.clone());
self.immutables_cache = (0..=callee_subs.len()).map(|_| OnceLock::new()).collect();
}
self.cell_stack.push(Vec::with_capacity(subs[sub_idx].cells_needed as usize));
self.active_reg_limit = sub_n_registers.max(1); self.active_reg_limit = sub_n_registers.max(1);
self.pc = 0; self.pc = 0;
Ok(()) Ok(())
@@ -800,6 +810,21 @@ impl Vm {
OpCode::CREATE_CLOSURE => { OpCode::CREATE_CLOSURE => {
self.dispatch_create_closure(rd, instr); self.dispatch_create_closure(rd, instr);
} }
OpCode::MAKE_CELL => {
self.dispatch_make_cell(rd, instr)?;
}
OpCode::CELL_GET => {
self.dispatch_cell_get(rd, a, b)?;
}
OpCode::CELL_SET => {
self.dispatch_cell_set(a, b)?;
}
OpCode::LOAD_UPVALUE => {
self.dispatch_load_upvalue(rd, instr)?;
}
OpCode::STORE_UPVALUE => {
self.dispatch_store_upvalue(a, b)?;
}
OpCode::CREATE_REGEXP => match self.dispatch_create_regexp(rd, a, b) { OpCode::CREATE_REGEXP => match self.dispatch_create_regexp(rd, a, b) {
Ok(Some(result)) => return Ok(result), Ok(Some(result)) => return Ok(result),
Ok(None) => {} Ok(None) => {}
+1
View File
@@ -117,6 +117,7 @@ impl Vm {
self.frames.last().map(|f| f.return_addr).unwrap_or(0) self.frames.last().map(|f| f.return_addr).unwrap_or(0)
); );
if let Some(frame) = self.frames.pop() { if let Some(frame) = self.frames.pop() {
self.cell_stack.pop();
let construct_result_reg = frame.construct_result_reg; let construct_result_reg = frame.construct_result_reg;
let constructed_this = frame.constructed_this; let constructed_this = frame.constructed_this;
let is_derived_constructor = frame.is_derived_constructor; let is_derived_constructor = frame.is_derived_constructor;
+5 -1
View File
@@ -60,6 +60,7 @@ impl Vm {
saved_bytecode_stack: std::mem::take(&mut self.saved_bytecode_stack), saved_bytecode_stack: std::mem::take(&mut self.saved_bytecode_stack),
saved_immutables_stack: std::mem::take(&mut self.saved_immutables_stack), saved_immutables_stack: std::mem::take(&mut self.saved_immutables_stack),
save_stack: std::mem::take(&mut self.save_stack), save_stack: std::mem::take(&mut self.save_stack),
cell_stack: std::mem::take(&mut self.cell_stack),
}); });
self.regs = [JsValue::undefined(); 256]; self.regs = [JsValue::undefined(); 256];
@@ -110,6 +111,7 @@ impl Vm {
self.saved_bytecode_stack = saved.saved_bytecode_stack; self.saved_bytecode_stack = saved.saved_bytecode_stack;
self.saved_immutables_stack = saved.saved_immutables_stack; self.saved_immutables_stack = saved.saved_immutables_stack;
self.save_stack = saved.save_stack; self.save_stack = saved.save_stack;
self.cell_stack = saved.cell_stack;
result result
} }
@@ -126,6 +128,9 @@ impl Vm {
if let Some(saved_imm) = self.saved_immutables_stack.pop() { if let Some(saved_imm) = self.saved_immutables_stack.pop() {
self.active_immutables = saved_imm; self.active_immutables = saved_imm;
} }
if let Some(saved_subs) = self.sub_module_stack.pop() {
self.sub_modules = saved_subs;
}
let offset = frame.saved_reg_offset as usize; let offset = frame.saved_reg_offset as usize;
let len = frame.caller_reg_limit as usize; let len = frame.caller_reg_limit as usize;
self.regs[..len].copy_from_slice(&self.save_stack[offset..offset + len]); self.regs[..len].copy_from_slice(&self.save_stack[offset..offset + len]);
@@ -148,7 +153,6 @@ impl Vm {
vm_debug!("run: starting bytecode execution, {} instructions", module.bytecode.len()); vm_debug!("run: starting bytecode execution, {} instructions", module.bytecode.len());
self.clear_execution_state(); self.clear_execution_state();
self.sub_modules = Arc::new(module.sub_modules.clone()); self.sub_modules = Arc::new(module.sub_modules.clone());
// Per-run convert-once cache: slot 0 = top module, slot sub_idx+1 = sub_modules[sub_idx].
self.immutables_cache = (0..=self.sub_modules.len()).map(|_| OnceLock::new()).collect(); self.immutables_cache = (0..=self.sub_modules.len()).map(|_| OnceLock::new()).collect();
self.bytecode = module.bytecode.clone(); self.bytecode = module.bytecode.clone();
self.activate_immutables(0, &module.constants); self.activate_immutables(0, &module.constants);
+2
View File
@@ -70,6 +70,7 @@ impl Vm {
instruction_count: 0, instruction_count: 0,
}, },
string_buf: String::new(), string_buf: String::new(),
sub_module_stack: Vec::new(),
cell_stack: Vec::new(), cell_stack: Vec::new(),
}; };
vm_info!("Vm created"); vm_info!("Vm created");
@@ -130,6 +131,7 @@ impl Vm {
instruction_count: 0, instruction_count: 0,
}, },
string_buf: String::new(), string_buf: String::new(),
sub_module_stack: Vec::new(),
cell_stack: Vec::new(), cell_stack: Vec::new(),
}; };
vm_info!("Vm created (pool)"); vm_info!("Vm created (pool)");