fix: 修复嵌套函数调用并接通闭包捕获链路
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
use std::cell::Cell;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::emit_ctx::{LabelCtx, ScopeCtx};
|
||||
@@ -894,7 +895,7 @@ impl Compiler {
|
||||
reg: binding.reg,
|
||||
initialized: binding.initialized,
|
||||
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));
|
||||
@@ -906,7 +907,7 @@ impl Compiler {
|
||||
reg: *reg,
|
||||
initialized: true,
|
||||
is_const: true,
|
||||
is_captured: false,
|
||||
is_captured: Cell::new(false),
|
||||
},
|
||||
);
|
||||
inherited_reg_start = inherited_reg_start.max(reg.saturating_add(1));
|
||||
@@ -918,8 +919,6 @@ impl Compiler {
|
||||
ctx.reset_regs();
|
||||
|
||||
// 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) {
|
||||
let (captures, _cells) = self.analyze_upvalue_captures(body_stmts, parent_ctx, &ctx.scopes.symbols);
|
||||
ctx.current_upvalue_captures = captures;
|
||||
@@ -932,7 +931,6 @@ impl Compiler {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Function body scope - params and local vars
|
||||
ctx.push_scope_with_kind(ScopeKind::FunctionScope);
|
||||
@@ -1010,9 +1008,20 @@ impl Compiler {
|
||||
}
|
||||
|
||||
// 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;
|
||||
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)? {
|
||||
last_result_reg = Some(reg);
|
||||
}
|
||||
@@ -1079,7 +1088,9 @@ impl Compiler {
|
||||
is_derived_constructor: false,
|
||||
needs_home_object: false,
|
||||
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,
|
||||
needs_home_object: false,
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::cell::Cell;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use oxide_parser::VariableDeclarationKind;
|
||||
@@ -17,7 +18,7 @@ pub(crate) struct Binding {
|
||||
pub(crate) reg: u8,
|
||||
pub(crate) initialized: bool,
|
||||
pub(crate) is_const: bool,
|
||||
pub(crate) is_captured: bool,
|
||||
pub(crate) is_captured: Cell<bool>,
|
||||
}
|
||||
|
||||
pub struct SymbolTable {
|
||||
@@ -85,7 +86,7 @@ impl SymbolTable {
|
||||
reg,
|
||||
initialized: false,
|
||||
is_const: matches!(kind, VariableDeclarationKind::Const) || is_const,
|
||||
is_captured: false,
|
||||
is_captured: Cell::new(false),
|
||||
},
|
||||
);
|
||||
Ok(())
|
||||
@@ -131,7 +132,7 @@ impl SymbolTable {
|
||||
reg: reg_for_new,
|
||||
initialized: true,
|
||||
is_const: false,
|
||||
is_captured: false,
|
||||
is_captured: Cell::new(false),
|
||||
},
|
||||
);
|
||||
reg_for_new
|
||||
@@ -152,7 +153,7 @@ impl SymbolTable {
|
||||
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;
|
||||
return b.is_captured.get();
|
||||
}
|
||||
}
|
||||
false
|
||||
@@ -178,7 +179,7 @@ impl SymbolTable {
|
||||
reg,
|
||||
initialized: true,
|
||||
is_const: matches!(kind, VariableDeclarationKind::Const) || is_const,
|
||||
is_captured: false,
|
||||
is_captured: Cell::new(false),
|
||||
},
|
||||
);
|
||||
Ok(())
|
||||
@@ -189,7 +190,7 @@ impl SymbolTable {
|
||||
reg,
|
||||
initialized: true,
|
||||
is_const: false,
|
||||
is_captured: false,
|
||||
is_captured: Cell::new(false),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use crate::native::NativeFn;
|
||||
use crate::vm::{native_fn_ptr_to_fn, CallFrame, FrameContinuation, Vm};
|
||||
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_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 {
|
||||
return_addr: self.pc,
|
||||
function_name: self.sub_modules[sub_idx]
|
||||
.function_name
|
||||
.as_deref()
|
||||
.map(|name| self.kernel_core.perm_interner().intern(name).0)
|
||||
.unwrap_or(0),
|
||||
function_name,
|
||||
caller_reg_limit,
|
||||
saved_reg_offset,
|
||||
saved_this,
|
||||
@@ -367,13 +372,21 @@ impl Vm {
|
||||
self.bytecode = sub_bytecode;
|
||||
let subs = Arc::clone(&self.sub_modules);
|
||||
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 global = self.session.global_object();
|
||||
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.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.pc = 0;
|
||||
return Ok(true);
|
||||
|
||||
@@ -140,6 +140,7 @@ pub(crate) struct InlineSyncState {
|
||||
pub(crate) saved_bytecode_stack: Vec<Vec<opcode::Instr>>,
|
||||
pub(crate) saved_immutables_stack: Vec<*const [JsValue]>,
|
||||
pub(crate) save_stack: Vec<JsValue>,
|
||||
pub(crate) cell_stack: Vec<Vec<*mut Cell>>,
|
||||
}
|
||||
|
||||
pub struct Vm {
|
||||
@@ -189,6 +190,7 @@ pub struct Vm {
|
||||
pub(crate) iters: IterState,
|
||||
/// Grouped inline-cache and instruction counters.
|
||||
pub(crate) profiling: ProfilingState,
|
||||
pub(crate) sub_module_stack: Vec<Arc<Vec<CompiledModule>>>,
|
||||
pub(crate) cell_stack: Vec<Vec<*mut Cell>>,
|
||||
/// Reusable string buffer for concatenation to avoid allocation per `+` op.
|
||||
pub(crate) string_buf: String,
|
||||
@@ -721,8 +723,6 @@ impl Vm {
|
||||
|
||||
self.saved_bytecode_stack.push(std::mem::take(&mut self.bytecode));
|
||||
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]
|
||||
.function_name
|
||||
@@ -744,9 +744,11 @@ impl Vm {
|
||||
continuation,
|
||||
});
|
||||
|
||||
self.pc = 0;
|
||||
self.bytecode = sub_bytecode;
|
||||
let subs = Arc::clone(&self.sub_modules);
|
||||
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() {
|
||||
let si = self.kernel_core.perm_interner().intern(name.as_str()).0;
|
||||
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.pc = 0;
|
||||
Ok(())
|
||||
@@ -800,6 +810,21 @@ impl Vm {
|
||||
OpCode::CREATE_CLOSURE => {
|
||||
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) {
|
||||
Ok(Some(result)) => return Ok(result),
|
||||
Ok(None) => {}
|
||||
|
||||
@@ -117,6 +117,7 @@ impl Vm {
|
||||
self.frames.last().map(|f| f.return_addr).unwrap_or(0)
|
||||
);
|
||||
if let Some(frame) = self.frames.pop() {
|
||||
self.cell_stack.pop();
|
||||
let construct_result_reg = frame.construct_result_reg;
|
||||
let constructed_this = frame.constructed_this;
|
||||
let is_derived_constructor = frame.is_derived_constructor;
|
||||
|
||||
@@ -60,6 +60,7 @@ impl Vm {
|
||||
saved_bytecode_stack: std::mem::take(&mut self.saved_bytecode_stack),
|
||||
saved_immutables_stack: std::mem::take(&mut self.saved_immutables_stack),
|
||||
save_stack: std::mem::take(&mut self.save_stack),
|
||||
cell_stack: std::mem::take(&mut self.cell_stack),
|
||||
});
|
||||
|
||||
self.regs = [JsValue::undefined(); 256];
|
||||
@@ -110,6 +111,7 @@ impl Vm {
|
||||
self.saved_bytecode_stack = saved.saved_bytecode_stack;
|
||||
self.saved_immutables_stack = saved.saved_immutables_stack;
|
||||
self.save_stack = saved.save_stack;
|
||||
self.cell_stack = saved.cell_stack;
|
||||
|
||||
result
|
||||
}
|
||||
@@ -126,6 +128,9 @@ impl Vm {
|
||||
if let Some(saved_imm) = self.saved_immutables_stack.pop() {
|
||||
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 len = frame.caller_reg_limit as usize;
|
||||
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());
|
||||
self.clear_execution_state();
|
||||
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.bytecode = module.bytecode.clone();
|
||||
self.activate_immutables(0, &module.constants);
|
||||
|
||||
@@ -70,6 +70,7 @@ impl Vm {
|
||||
instruction_count: 0,
|
||||
},
|
||||
string_buf: String::new(),
|
||||
sub_module_stack: Vec::new(),
|
||||
cell_stack: Vec::new(),
|
||||
};
|
||||
vm_info!("Vm created");
|
||||
@@ -130,6 +131,7 @@ impl Vm {
|
||||
instruction_count: 0,
|
||||
},
|
||||
string_buf: String::new(),
|
||||
sub_module_stack: Vec::new(),
|
||||
cell_stack: Vec::new(),
|
||||
};
|
||||
vm_info!("Vm created (pool)");
|
||||
|
||||
Reference in New Issue
Block a user