fix(vm): 链式 upvalue 捕获支持多层嵌套函数引用深层变量
This commit is contained in:
@@ -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<u8>,
|
||||
}
|
||||
|
||||
/// 一个函数单元(或顶层脚本)的编译产物。
|
||||
|
||||
@@ -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<String, u8>, sub_own: &HashSet<String>,
|
||||
&self, body_stmts: &[Statement], parent_captured: &BTreeMap<String, u8>,
|
||||
parent_upvalues: &[UpvalueCapture], sub_own: &HashSet<String>,
|
||||
) -> Vec<UpvalueCapture> {
|
||||
let parent_names: HashSet<String> = parent_captured.keys().cloned().collect();
|
||||
let mut parent_names: HashSet<String> = 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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user