fix(vm): 链式 upvalue 捕获支持多层嵌套函数引用深层变量
This commit is contained in:
@@ -23,13 +23,17 @@ pub enum Constant {
|
|||||||
|
|
||||||
/// 闭包对上层作用域一个变量的捕获描述。
|
/// 闭包对上层作用域一个变量的捕获描述。
|
||||||
///
|
///
|
||||||
/// `enclosing_reg` 是外层函数中该变量的寄存器位;若外层变量本身就是 upvalue
|
/// `enclosing_reg` 是外层函数中该变量的寄存器位;`cell_idx` 是父函数 own cell
|
||||||
/// (多级闭包),`cell_idx` 指向链式捕获的 cell。
|
/// 表下标(`parent_uv_idx` 为 None 时)。多级闭包(外层变量本身就是父函数从更
|
||||||
|
/// 外层捕获的 upvalue)时 `parent_uv_idx` 给出父闭包 `upvalues` 数组下标。
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct UpvalueCapture {
|
pub struct UpvalueCapture {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub enclosing_reg: u32,
|
pub enclosing_reg: u32,
|
||||||
pub cell_idx: u8,
|
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。
|
/// 分析子函数 body:引用父级被捕获绑定的名字 → upvalue_captures。
|
||||||
/// cell_idx 直接取父 captured_bindings 映射(父 emit 前已确定,索引一致);
|
/// 名字若在父 `captured_bindings`(父 own cell)则 `cell_idx` 索引定义方表;
|
||||||
/// enclosing_reg 由父 emit 完成后填充(assemble_ir)。
|
/// 若在父 `upvalue_captures`(父自身从更外层捕获)则链式标记 `parent_uv_idx`,
|
||||||
|
/// 运行时从父闭包 upvalues 取 cell。enclosing_reg 由父 emit 完成后填充。
|
||||||
pub(crate) fn collect_upvalue_names(
|
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> {
|
) -> 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();
|
let mut names = HashSet::new();
|
||||||
self.collect_capture_names_shadowed(body_stmts, &parent_names, sub_own, &mut names);
|
self.collect_capture_names_shadowed(body_stmts, &parent_names, sub_own, &mut names);
|
||||||
// HashSet 迭代序带随机种子(进程级非确定),必须排序使 upvalue_captures 的顺序与
|
// HashSet 迭代序带随机种子(进程级非确定),必须排序使 upvalue_captures 的顺序与
|
||||||
@@ -572,11 +577,13 @@ impl Emitter {
|
|||||||
names
|
names
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|name| {
|
.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);
|
let cell_idx = parent_captured.get(&name).copied().unwrap_or(0);
|
||||||
UpvalueCapture {
|
UpvalueCapture {
|
||||||
name,
|
name,
|
||||||
enclosing_reg: 0, // assemble_ir 时从父符号表填充
|
enclosing_reg: 0, // assemble_ir 时从父符号表填充
|
||||||
cell_idx,
|
cell_idx,
|
||||||
|
parent_uv_idx,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
|||||||
@@ -473,6 +473,7 @@ impl CompileCtx {
|
|||||||
name: u.name.clone(),
|
name: u.name.clone(),
|
||||||
enclosing_reg,
|
enclosing_reg,
|
||||||
cell_idx: u.cell_idx,
|
cell_idx: u.cell_idx,
|
||||||
|
parent_uv_idx: u.parent_uv_idx,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@@ -757,8 +758,12 @@ impl Emitter {
|
|||||||
|
|
||||||
// 自由变量分析(仅普通/箭头函数):收集 upvalue 捕获。
|
// 自由变量分析(仅普通/箭头函数):收集 upvalue 捕获。
|
||||||
if matches!(body_context, FunctionBodyContext::Ordinary | FunctionBodyContext::Arrow) {
|
if matches!(body_context, FunctionBodyContext::Ordinary | FunctionBodyContext::Arrow) {
|
||||||
ctx.current_upvalue_captures =
|
ctx.current_upvalue_captures = self.collect_upvalue_names(
|
||||||
self.collect_upvalue_names(body_stmts, &parent_ctx.captured_bindings, &ctx.own_bindings);
|
body_stmts,
|
||||||
|
&parent_ctx.captured_bindings,
|
||||||
|
&parent_ctx.current_upvalue_captures,
|
||||||
|
&ctx.own_bindings,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(param_base)
|
Ok(param_base)
|
||||||
|
|||||||
@@ -184,6 +184,7 @@ fn module_fields_are_copied_through() {
|
|||||||
name: "x".to_string(),
|
name: "x".to_string(),
|
||||||
enclosing_reg: 5,
|
enclosing_reg: 5,
|
||||||
cell_idx: 0,
|
cell_idx: 0,
|
||||||
|
parent_uv_idx: None,
|
||||||
});
|
});
|
||||||
f.cells_needed = 1;
|
f.cells_needed = 1;
|
||||||
let m = lower_ok(&f);
|
let m = lower_ok(&f);
|
||||||
@@ -274,6 +275,7 @@ fn ir_function_domain_assemble_default_clone() {
|
|||||||
name: "x".to_string(),
|
name: "x".to_string(),
|
||||||
enclosing_reg: 0,
|
enclosing_reg: 0,
|
||||||
cell_idx: 0,
|
cell_idx: 0,
|
||||||
|
parent_uv_idx: None,
|
||||||
});
|
});
|
||||||
f.cells_needed = 1;
|
f.cells_needed = 1;
|
||||||
f.function_name = Some("f".to_string());
|
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::This, Operand::This);
|
||||||
assert_eq!(Operand::NewTarget, Operand::NewTarget);
|
assert_eq!(Operand::NewTarget, Operand::NewTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ mod tests {
|
|||||||
name: "x".to_string(),
|
name: "x".to_string(),
|
||||||
enclosing_reg: 7,
|
enclosing_reg: 7,
|
||||||
cell_idx: 0,
|
cell_idx: 0,
|
||||||
|
parent_uv_idx: None,
|
||||||
});
|
});
|
||||||
let map = AllocMap::new();
|
let map = AllocMap::new();
|
||||||
run(&mut f, &map);
|
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(""));
|
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);
|
self.set_or_create_prop_value(func_obj, name_si, name_val);
|
||||||
if !upvalue_captures.is_empty() {
|
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() {
|
if let Some(current_cells) = self.cell_stack.last_mut() {
|
||||||
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 {
|
||||||
let cell_idx = capture.cell_idx as usize;
|
let cell_ptr = if let Some(puv) = capture.parent_uv_idx {
|
||||||
if cell_idx >= current_cells.len() {
|
parent_upvalues.get(puv as usize).copied().unwrap_or(std::ptr::null_mut())
|
||||||
current_cells.resize(cell_idx + 1, std::ptr::null_mut());
|
} else {
|
||||||
}
|
let cell_idx = capture.cell_idx as usize;
|
||||||
// cell 可能尚未 MAKE_CELL(hoisting 顺序:CREATE_CLOSURE 先于
|
if cell_idx >= current_cells.len() {
|
||||||
// MAKE_CELL)——建占位 Cell,后续 MAKE_CELL 更新同一 Cell 的值,
|
current_cells.resize(cell_idx + 1, std::ptr::null_mut());
|
||||||
// 保证 upvalue 始终指向定义方表里的稳定 cell(而非调用方表)。
|
}
|
||||||
if current_cells[cell_idx].is_null() {
|
// cell 可能尚未 MAKE_CELL(hoisting 顺序:CREATE_CLOSURE 先于
|
||||||
let cell = self.gc_state.session_epoch.alloc(Cell::new(JsValue::undefined(), true));
|
// MAKE_CELL)——建占位 Cell,后续 MAKE_CELL 更新同一 Cell 的值,
|
||||||
current_cells[cell_idx] = cell as *mut Cell;
|
// 保证 upvalue 始终指向定义方表里的稳定 cell(而非调用方表)。
|
||||||
}
|
if current_cells[cell_idx].is_null() {
|
||||||
upvals.push(current_cells[cell_idx]);
|
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() };
|
let func_obj = unsafe { &mut *result.as_js_object_ptr() };
|
||||||
func_obj.set_upvalues(upvals);
|
func_obj.set_upvalues(upvals);
|
||||||
|
|||||||
Reference in New Issue
Block a user