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

This commit is contained in:
爱特每羊队
2026-06-30 21:52:18 +08:00
parent 575d3fdc08
commit b944aa2832
5 changed files with 50 additions and 27 deletions
+14 -8
View File
@@ -1089,10 +1089,13 @@ impl Compiler {
is_class_constructor: false, is_class_constructor: false,
is_derived_constructor: false, is_derived_constructor: false,
needs_home_object: false, needs_home_object: false,
upvalue_captures: Vec::new(), upvalue_captures: ctx.current_upvalue_captures.clone(),
cells_needed: ctx.scopes.symbols.scopes[0] cells_needed: ctx
.bindings .scopes
.values() .symbols
.scopes
.iter()
.flat_map(|s| s.bindings.values())
.filter(|b| b.is_captured.get()) .filter(|b| b.is_captured.get())
.count() as u8, .count() as u8,
}) })
@@ -1193,10 +1196,13 @@ impl Compiler {
is_class_constructor: false, is_class_constructor: false,
is_derived_constructor: false, is_derived_constructor: false,
needs_home_object: false, needs_home_object: false,
upvalue_captures: Vec::new(), upvalue_captures: ctx.current_upvalue_captures.clone(),
cells_needed: ctx.scopes.symbols.scopes[0] cells_needed: ctx
.bindings .scopes
.values() .symbols
.scopes
.iter()
.flat_map(|s| s.bindings.values())
.filter(|b| b.is_captured.get()) .filter(|b| b.is_captured.get())
.count() as u8, .count() as u8,
}) })
+7 -4
View File
@@ -356,9 +356,6 @@ impl Compiler {
) -> Result<(), String> { ) -> Result<(), String> {
let var_reg = ctx.alloc_reg(); let var_reg = ctx.alloc_reg();
let target_reg = if matches!(kind, VariableDeclarationKind::Var) { let target_reg = if matches!(kind, VariableDeclarationKind::Var) {
// `var` is hoisted, so the count pass already registered the name. A redeclare
// here reuses that existing register instead of erroring; the freshly allocated
// reg matches it (count/emit allocate in lock-step) so either resolves the same.
match ctx.declare(name, var_reg, kind, is_const) { match ctx.declare(name, var_reg, kind, is_const) {
Ok(()) => var_reg, Ok(()) => var_reg,
Err(_) => ctx.lookup(name).unwrap_or(var_reg), Err(_) => ctx.lookup(name).unwrap_or(var_reg),
@@ -367,7 +364,13 @@ impl Compiler {
ctx.declare(name, var_reg, kind, is_const)?; ctx.declare(name, var_reg, kind, is_const)?;
var_reg var_reg
}; };
ctx.emit(opcode::encode(OpCode::STORE_VAR, target_reg, src_reg, if is_const { 1 } else { 0 })); if ctx.scopes.symbols.lookup_is_captured(name) {
let cell_idx = ctx.scopes.cell_registry.len() as u8;
ctx.scopes.cell_registry.push((name.to_string(), cell_idx));
ctx.emit(opcode::encode(OpCode::MAKE_CELL, src_reg, cell_idx, 0));
} else {
ctx.emit(opcode::encode(OpCode::STORE_VAR, target_reg, src_reg, if is_const { 1 } else { 0 }));
}
ctx.init_var(name); ctx.init_var(name);
Ok(()) Ok(())
} }
@@ -37,7 +37,7 @@ impl Compiler {
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.emit(opcode::encode(OpCode::MAKE_CELL, var_reg, cell_idx, 0)); ctx.emit(opcode::encode(OpCode::MAKE_CELL, tmp, cell_idx, 0));
} else { } else {
ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, tmp, 0)); ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, tmp, 0));
} }
+28 -13
View File
@@ -96,7 +96,6 @@ impl Vm {
let is_derived_constructor = sub.is_derived_constructor; let is_derived_constructor = sub.is_derived_constructor;
let needs_home_object = sub.needs_home_object; let needs_home_object = sub.needs_home_object;
let upvalue_captures = sub.upvalue_captures.clone(); let upvalue_captures = sub.upvalue_captures.clone();
let cells_needed = sub.cells_needed;
let result = self.create_function_object( let result = self.create_function_object(
sub_idx, sub_idx,
is_arrow, is_arrow,
@@ -104,7 +103,7 @@ impl Vm {
is_derived_constructor, is_derived_constructor,
needs_home_object, needs_home_object,
); );
if !upvalue_captures.is_empty() && cells_needed > 0 { if !upvalue_captures.is_empty() {
if let Some(current_cells) = self.cell_stack.last() { if let Some(current_cells) = self.cell_stack.last() {
let mut upvals = Box::new(Vec::with_capacity(upvalue_captures.len())); let mut upvals = Box::new(Vec::with_capacity(upvalue_captures.len()));
for capture in &upvalue_captures { for capture in &upvalue_captures {
@@ -126,7 +125,7 @@ impl Vm {
pub(crate) fn dispatch_make_cell(&mut self, rd: usize, instr: u32) -> Result<(), String> { pub(crate) fn dispatch_make_cell(&mut self, rd: usize, instr: u32) -> Result<(), String> {
let cell_idx = opcode::imm16(instr) as usize; let cell_idx = opcode::imm16(instr) as usize;
let value = self.regs[rd]; let value = self.regs[rd];
let cell = self.gc_state.session_epoch.alloc(Cell::new(value, false)); let cell = self.gc_state.session_epoch.alloc(Cell::new(value, true));
let cell_ptr = cell as *mut Cell; let cell_ptr = cell as *mut Cell;
let current = self.cell_stack.last_mut().unwrap(); let current = self.cell_stack.last_mut().unwrap();
while current.len() <= cell_idx { while current.len() <= cell_idx {
@@ -137,19 +136,18 @@ impl Vm {
} }
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn dispatch_cell_get(&mut self, rd: usize, _a: usize, b: usize) -> Result<(), String> { pub(crate) fn dispatch_cell_get(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let cell_idx = b; let cell_idx = b;
let current = self.cell_stack.last().unwrap(); let current = self.cell_stack.last_mut().unwrap();
if cell_idx >= current.len() { while current.len() <= cell_idx {
self.regs[rd] = JsValue::undefined(); current.push(std::ptr::null_mut());
return Ok(());
} }
let cell_ptr = current[cell_idx]; if current[cell_idx].is_null() {
if cell_ptr.is_null() { let val = self.regs[a];
self.regs[rd] = JsValue::undefined(); let cell = self.gc_state.session_epoch.alloc(Cell::new(val, true));
return Ok(()); current[cell_idx] = cell as *mut Cell;
} }
self.regs[rd] = unsafe { (*cell_ptr).value }; self.regs[rd] = unsafe { (*current[cell_idx]).value };
Ok(()) Ok(())
} }
@@ -186,6 +184,23 @@ impl Vm {
} }
} }
} }
// Cell not yet created (hoisting order: CREATE_CLOSURE before MAKE_CELL).
// Create cell lazily with caller's register value.
self.lazy_create_upvalue_cell(rd, uv_idx)
}
fn lazy_create_upvalue_cell(&mut self, rd: usize, uv_idx: usize) -> Result<(), String> {
let callee = self.frames.last().unwrap().callee;
if callee.is_object() {
let obj = unsafe { &mut *callee.as_js_object_ptr() };
let upvals = obj.upvalues_slice_mut();
if uv_idx < upvals.len() {
let cell = self.gc_state.session_epoch.alloc(Cell::new(self.regs[rd], true));
upvals[uv_idx] = cell as *mut Cell;
self.regs[rd] = cell.value;
return Ok(());
}
}
self.regs[rd] = JsValue::undefined(); self.regs[rd] = JsValue::undefined();
Ok(()) Ok(())
} }
-1
View File
@@ -769,7 +769,6 @@ impl Vm {
self.sub_modules = Arc::new(callee_subs.clone()); self.sub_modules = Arc::new(callee_subs.clone());
self.immutables_cache = (0..=callee_subs.len()).map(|_| OnceLock::new()).collect(); 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;