fix: 修复嵌套函数调用并接通闭包捕获链路
This commit is contained in:
@@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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) => {}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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)");
|
||||||
|
|||||||
Reference in New Issue
Block a user