diff --git a/crates/oxide_bytecode/src/module.rs b/crates/oxide_bytecode/src/module.rs index b8de052..b830678 100644 --- a/crates/oxide_bytecode/src/module.rs +++ b/crates/oxide_bytecode/src/module.rs @@ -23,13 +23,17 @@ pub enum Constant { /// 闭包对上层作用域一个变量的捕获描述。 /// -/// `enclosing_reg` 是外层函数中该变量的寄存器位;若外层变量本身就是 upvalue -/// (多级闭包),`cell_idx` 指向链式捕获的 cell。 +/// `enclosing_reg` 是外层函数中该变量的寄存器位;`cell_idx` 是父函数 own cell +/// 表下标(`parent_uv_idx` 为 None 时)。多级闭包(外层变量本身就是父函数从更 +/// 外层捕获的 upvalue)时 `parent_uv_idx` 给出父闭包 `upvalues` 数组下标。 #[derive(Debug, Clone)] pub struct UpvalueCapture { pub name: String, pub enclosing_reg: u32, pub cell_idx: u8, + /// 链式捕获:None = 父 own cell(cell_idx 索引定义方 cell 表); + /// Some = 父函数自身 upvalue(运行时从父闭包 upvalues[parent_uv_idx] 取 cell)。 + pub parent_uv_idx: Option, } /// 一个函数单元(或顶层脚本)的编译产物。 diff --git a/crates/oxide_emit/src/closure.rs b/crates/oxide_emit/src/closure.rs index bddc5a4..b576b77 100644 --- a/crates/oxide_emit/src/closure.rs +++ b/crates/oxide_emit/src/closure.rs @@ -556,12 +556,17 @@ impl Emitter { } /// 分析子函数 body:引用父级被捕获绑定的名字 → upvalue_captures。 - /// cell_idx 直接取父 captured_bindings 映射(父 emit 前已确定,索引一致); - /// enclosing_reg 由父 emit 完成后填充(assemble_ir)。 + /// 名字若在父 `captured_bindings`(父 own cell)则 `cell_idx` 索引定义方表; + /// 若在父 `upvalue_captures`(父自身从更外层捕获)则链式标记 `parent_uv_idx`, + /// 运行时从父闭包 upvalues 取 cell。enclosing_reg 由父 emit 完成后填充。 pub(crate) fn collect_upvalue_names( - &self, body_stmts: &[Statement], parent_captured: &BTreeMap, sub_own: &HashSet, + &self, body_stmts: &[Statement], parent_captured: &BTreeMap, + parent_upvalues: &[UpvalueCapture], sub_own: &HashSet, ) -> Vec { - let parent_names: HashSet = parent_captured.keys().cloned().collect(); + let mut parent_names: HashSet = parent_captured.keys().cloned().collect(); + for u in parent_upvalues { + parent_names.insert(u.name.clone()); + } let mut names = HashSet::new(); self.collect_capture_names_shadowed(body_stmts, &parent_names, sub_own, &mut names); // HashSet 迭代序带随机种子(进程级非确定),必须排序使 upvalue_captures 的顺序与 @@ -572,11 +577,13 @@ impl Emitter { names .into_iter() .map(|name| { + let parent_uv_idx = parent_upvalues.iter().position(|u| u.name == name).map(|i| i as u8); let cell_idx = parent_captured.get(&name).copied().unwrap_or(0); UpvalueCapture { name, enclosing_reg: 0, // assemble_ir 时从父符号表填充 cell_idx, + parent_uv_idx, } }) .collect() diff --git a/crates/oxide_emit/src/emit.rs b/crates/oxide_emit/src/emit.rs index a6a43a7..4e52cfd 100644 --- a/crates/oxide_emit/src/emit.rs +++ b/crates/oxide_emit/src/emit.rs @@ -473,6 +473,7 @@ impl CompileCtx { name: u.name.clone(), enclosing_reg, cell_idx: u.cell_idx, + parent_uv_idx: u.parent_uv_idx, } }) .collect(); @@ -757,8 +758,12 @@ impl Emitter { // 自由变量分析(仅普通/箭头函数):收集 upvalue 捕获。 if matches!(body_context, FunctionBodyContext::Ordinary | FunctionBodyContext::Arrow) { - ctx.current_upvalue_captures = - self.collect_upvalue_names(body_stmts, &parent_ctx.captured_bindings, &ctx.own_bindings); + ctx.current_upvalue_captures = self.collect_upvalue_names( + body_stmts, + &parent_ctx.captured_bindings, + &parent_ctx.current_upvalue_captures, + &ctx.own_bindings, + ); } Ok(param_base) diff --git a/crates/oxide_ir/tests/lowering_tests.rs b/crates/oxide_ir/tests/lowering_tests.rs index 90338ad..6093b91 100644 --- a/crates/oxide_ir/tests/lowering_tests.rs +++ b/crates/oxide_ir/tests/lowering_tests.rs @@ -184,6 +184,7 @@ fn module_fields_are_copied_through() { name: "x".to_string(), enclosing_reg: 5, cell_idx: 0, + parent_uv_idx: None, }); f.cells_needed = 1; let m = lower_ok(&f); @@ -274,6 +275,7 @@ fn ir_function_domain_assemble_default_clone() { name: "x".to_string(), enclosing_reg: 0, cell_idx: 0, + parent_uv_idx: None, }); f.cells_needed = 1; f.function_name = Some("f".to_string()); @@ -305,3 +307,4 @@ fn operand_this_is_semantic_not_physical_index() { assert_eq!(Operand::This, Operand::This); assert_eq!(Operand::NewTarget, Operand::NewTarget); } + diff --git a/crates/oxide_regalloc/src/finish.rs b/crates/oxide_regalloc/src/finish.rs index 7c83f08..2f848e1 100644 --- a/crates/oxide_regalloc/src/finish.rs +++ b/crates/oxide_regalloc/src/finish.rs @@ -118,6 +118,7 @@ mod tests { name: "x".to_string(), enclosing_reg: 7, cell_idx: 0, + parent_uv_idx: None, }); let map = AllocMap::new(); run(&mut f, &map); diff --git a/crates/oxide_vm/src/dispatch/call.rs b/crates/oxide_vm/src/dispatch/call.rs index 6b4debb..7b06ddf 100644 --- a/crates/oxide_vm/src/dispatch/call.rs +++ b/crates/oxide_vm/src/dispatch/call.rs @@ -133,21 +133,34 @@ impl Vm { let name_val = function_name.as_deref().map(|n| self.new_string(n)).unwrap_or_else(|| self.new_string("")); self.set_or_create_prop_value(func_obj, name_si, name_val); if !upvalue_captures.is_empty() { + // 链式捕获(parent_uv_idx):从父闭包(创建者)的 upvalues 取 cell。 + let parent_upvalues: Vec<*mut Cell> = match self.current_callee() { + Some(callee) if callee.is_object() => { + unsafe { &*callee.as_js_object_ptr() }.upvalues_slice().to_vec() + } + _ => Vec::new(), + }; if let Some(current_cells) = self.cell_stack.last_mut() { let mut upvals = Box::new(Vec::with_capacity(upvalue_captures.len())); for capture in &upvalue_captures { - let cell_idx = capture.cell_idx as usize; - if cell_idx >= current_cells.len() { - current_cells.resize(cell_idx + 1, std::ptr::null_mut()); - } - // cell 可能尚未 MAKE_CELL(hoisting 顺序:CREATE_CLOSURE 先于 - // MAKE_CELL)——建占位 Cell,后续 MAKE_CELL 更新同一 Cell 的值, - // 保证 upvalue 始终指向定义方表里的稳定 cell(而非调用方表)。 - if current_cells[cell_idx].is_null() { - let cell = self.gc_state.session_epoch.alloc(Cell::new(JsValue::undefined(), true)); - current_cells[cell_idx] = cell as *mut Cell; - } - upvals.push(current_cells[cell_idx]); + let cell_ptr = if let Some(puv) = capture.parent_uv_idx { + parent_upvalues.get(puv as usize).copied().unwrap_or(std::ptr::null_mut()) + } else { + let cell_idx = capture.cell_idx as usize; + if cell_idx >= current_cells.len() { + current_cells.resize(cell_idx + 1, std::ptr::null_mut()); + } + // cell 可能尚未 MAKE_CELL(hoisting 顺序:CREATE_CLOSURE 先于 + // MAKE_CELL)——建占位 Cell,后续 MAKE_CELL 更新同一 Cell 的值, + // 保证 upvalue 始终指向定义方表里的稳定 cell(而非调用方表)。 + if current_cells[cell_idx].is_null() { + let cell = + self.gc_state.session_epoch.alloc(Cell::new(JsValue::undefined(), true)); + current_cells[cell_idx] = cell as *mut Cell; + } + current_cells[cell_idx] + }; + upvals.push(cell_ptr); } let func_obj = unsafe { &mut *result.as_js_object_ptr() }; func_obj.set_upvalues(upvals);