fix: 绑定侧解构计算键参与闭包捕获遍历防静默错值
This commit is contained in:
@@ -306,6 +306,8 @@ impl Emitter {
|
||||
}
|
||||
Statement::VariableDeclaration(vd) => {
|
||||
for d in &vd.declarations {
|
||||
// 绑定 pattern 的计算键是运行时求值表达式,其引用须纳入捕获。
|
||||
self.collect_capture_names_binding_keys(&d.id, ref_set, shadow, out);
|
||||
if let Some(init) = &d.init {
|
||||
self.collect_capture_names_expr(init, ref_set, shadow, out);
|
||||
}
|
||||
@@ -342,6 +344,7 @@ impl Emitter {
|
||||
}
|
||||
if let oxide_parser::ForStatementInit::VariableDeclaration(vd) = init {
|
||||
for d in &vd.declarations {
|
||||
self.collect_capture_names_binding_keys(&d.id, ref_set, shadow, out);
|
||||
if let Some(i) = &d.init {
|
||||
self.collect_capture_names_expr(i, ref_set, shadow, out);
|
||||
}
|
||||
@@ -369,12 +372,22 @@ impl Emitter {
|
||||
// left 的 var 声明遮蔽外层同名绑定,body 内引用不视为捕获外层。
|
||||
let mut for_shadow = shadow.clone();
|
||||
self.collect_for_left_decl_names(&fi.left, &mut for_shadow);
|
||||
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fi.left {
|
||||
for d in &vd.declarations {
|
||||
self.collect_capture_names_binding_keys(&d.id, ref_set, shadow, out);
|
||||
}
|
||||
}
|
||||
self.collect_capture_names_shadowed(std::slice::from_ref(&fi.body), ref_set, &for_shadow, out);
|
||||
}
|
||||
Statement::ForOfStatement(fo) => {
|
||||
self.collect_capture_names_expr(&fo.right, ref_set, shadow, out);
|
||||
let mut for_shadow = shadow.clone();
|
||||
self.collect_for_left_decl_names(&fo.left, &mut for_shadow);
|
||||
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fo.left {
|
||||
for d in &vd.declarations {
|
||||
self.collect_capture_names_binding_keys(&d.id, ref_set, shadow, out);
|
||||
}
|
||||
}
|
||||
self.collect_capture_names_shadowed(std::slice::from_ref(&fo.body), ref_set, &for_shadow, out);
|
||||
}
|
||||
Statement::BlockStatement(b) => self.collect_capture_names_shadowed(&b.body, ref_set, shadow, out),
|
||||
@@ -410,6 +423,7 @@ impl Emitter {
|
||||
match decl {
|
||||
oxide_parser::Declaration::VariableDeclaration(vd) => {
|
||||
for d in &vd.declarations {
|
||||
self.collect_capture_names_binding_keys(&d.id, ref_set, shadow, out);
|
||||
if let Some(init) = &d.init {
|
||||
self.collect_capture_names_expr(init, ref_set, shadow, out);
|
||||
}
|
||||
@@ -526,6 +540,10 @@ impl Emitter {
|
||||
}
|
||||
oxide_parser::BindingPattern::ObjectPattern(op) => {
|
||||
for prop in &op.properties {
|
||||
// 计算键是运行时求值表达式,其引用须纳入捕获。
|
||||
if prop.computed {
|
||||
self.collect_capture_names_expr(prop.key.to_expression(), ref_set, shadow, out);
|
||||
}
|
||||
self.collect_capture_names_binding_pattern(&prop.value, ref_set, shadow, out, catch_shadow);
|
||||
}
|
||||
if let Some(rest) = &op.rest {
|
||||
@@ -539,6 +557,40 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
/// 遍历绑定 pattern 的计算键表达式(`{[k]: a}` 的键)中的引用:模式键是运行时
|
||||
/// 求值的表达式,嵌套函数引用外层绑定时须纳入捕获(与赋值侧/对象字面量/类字段
|
||||
/// 键遍历同口径)。
|
||||
fn collect_capture_names_binding_keys(
|
||||
&self, pattern: &oxide_parser::BindingPattern, ref_set: &HashSet<String>, shadow: &HashSet<String>,
|
||||
out: &mut HashSet<String>,
|
||||
) {
|
||||
match pattern {
|
||||
oxide_parser::BindingPattern::BindingIdentifier(_) => {}
|
||||
oxide_parser::BindingPattern::ArrayPattern(ap) => {
|
||||
for p in ap.elements.iter().flatten() {
|
||||
self.collect_capture_names_binding_keys(p, ref_set, shadow, out);
|
||||
}
|
||||
if let Some(rest) = &ap.rest {
|
||||
self.collect_capture_names_binding_keys(&rest.argument, ref_set, shadow, out);
|
||||
}
|
||||
}
|
||||
oxide_parser::BindingPattern::ObjectPattern(op) => {
|
||||
for prop in &op.properties {
|
||||
if prop.computed {
|
||||
self.collect_capture_names_expr(prop.key.to_expression(), ref_set, shadow, out);
|
||||
}
|
||||
self.collect_capture_names_binding_keys(&prop.value, ref_set, shadow, out);
|
||||
}
|
||||
if let Some(rest) = &op.rest {
|
||||
self.collect_capture_names_binding_keys(&rest.argument, ref_set, shadow, out);
|
||||
}
|
||||
}
|
||||
oxide_parser::BindingPattern::AssignmentPattern(ap) => {
|
||||
self.collect_capture_names_binding_keys(&ap.left, ref_set, shadow, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 扫描赋值目标中的引用(标识符写名,成员目标扫对象/键,解构目标递归元素)。
|
||||
fn collect_capture_names_assign_target(
|
||||
&self, target: &oxide_parser::AssignmentTarget, ref_set: &HashSet<String>, shadow: &HashSet<String>,
|
||||
@@ -868,11 +920,49 @@ impl Emitter {
|
||||
fn collect_fn_default_captured(
|
||||
&self, params: &oxide_parser::FormalParameters, own: &HashSet<String>, out: &mut HashSet<String>,
|
||||
) {
|
||||
let empty_shadow = HashSet::new();
|
||||
for p in ¶ms.items {
|
||||
if let Some(init) = &p.initializer {
|
||||
self.collect_captured_expr(init, own, out);
|
||||
self.collect_capture_names_expr(init, own, &empty_shadow, out);
|
||||
}
|
||||
// 形参模式的运行时求值表达式(计算键/内嵌默认值)在子作用域求值,
|
||||
// 其标识符引用须纳入父层 MAKE_CELL 判定。
|
||||
self.collect_captured_pattern_runtime_exprs(&p.pattern, own, out);
|
||||
}
|
||||
}
|
||||
|
||||
/// 遍历绑定 pattern 内全部运行时求值表达式(计算键与 AssignmentPattern 默认值):
|
||||
/// 这些表达式在子作用域(嵌套函数形参绑定时)求值,引用的父层绑定须建 cell 供
|
||||
/// 子函数 upvalue 读取——与 `collect_capture_names_expr` 同口径(标识符比对)。
|
||||
fn collect_captured_pattern_runtime_exprs(
|
||||
&self, pattern: &oxide_parser::BindingPattern, own: &HashSet<String>, out: &mut HashSet<String>,
|
||||
) {
|
||||
let empty_shadow = HashSet::new();
|
||||
match pattern {
|
||||
oxide_parser::BindingPattern::BindingIdentifier(_) => {}
|
||||
oxide_parser::BindingPattern::ArrayPattern(ap) => {
|
||||
for p in ap.elements.iter().flatten() {
|
||||
self.collect_captured_pattern_runtime_exprs(p, own, out);
|
||||
}
|
||||
if let Some(rest) = &ap.rest {
|
||||
self.collect_captured_pattern_runtime_exprs(&rest.argument, own, out);
|
||||
}
|
||||
}
|
||||
oxide_parser::BindingPattern::ObjectPattern(op) => {
|
||||
for prop in &op.properties {
|
||||
if prop.computed {
|
||||
self.collect_capture_names_expr(prop.key.to_expression(), own, &empty_shadow, out);
|
||||
}
|
||||
self.collect_captured_pattern_runtime_exprs(&prop.value, own, out);
|
||||
}
|
||||
if let Some(rest) = &op.rest {
|
||||
self.collect_captured_pattern_runtime_exprs(&rest.argument, own, out);
|
||||
}
|
||||
}
|
||||
oxide_parser::BindingPattern::AssignmentPattern(ap) => {
|
||||
self.collect_capture_names_expr(&ap.right, own, &empty_shadow, out);
|
||||
self.collect_captured_pattern_runtime_exprs(&ap.left, own, out);
|
||||
}
|
||||
self.collect_captured_binding_pattern(&p.pattern, own, out);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -904,6 +994,8 @@ impl Emitter {
|
||||
}
|
||||
Statement::VariableDeclaration(vd) => {
|
||||
for d in &vd.declarations {
|
||||
// 绑定 pattern 的计算键是运行时求值表达式,其引用须纳入捕获判定。
|
||||
self.collect_captured_binding_keys(&d.id, own, out);
|
||||
if let Some(init) = &d.init {
|
||||
self.collect_captured_expr(init, own, out);
|
||||
}
|
||||
@@ -921,6 +1013,14 @@ impl Emitter {
|
||||
if let Some(e) = init.as_expression() {
|
||||
self.collect_captured_expr(e, own, out);
|
||||
}
|
||||
if let oxide_parser::ForStatementInit::VariableDeclaration(vd) = init {
|
||||
for d in &vd.declarations {
|
||||
self.collect_captured_binding_keys(&d.id, own, out);
|
||||
if let Some(i) = &d.init {
|
||||
self.collect_captured_expr(i, own, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(t) = &fs.test {
|
||||
self.collect_captured_expr(t, own, out);
|
||||
@@ -940,10 +1040,20 @@ impl Emitter {
|
||||
}
|
||||
Statement::ForInStatement(fi) => {
|
||||
self.collect_captured_expr(&fi.right, own, out);
|
||||
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fi.left {
|
||||
for d in &vd.declarations {
|
||||
self.collect_captured_binding_keys(&d.id, own, out);
|
||||
}
|
||||
}
|
||||
self.collect_captured_stmt(&fi.body, own, out);
|
||||
}
|
||||
Statement::ForOfStatement(fo) => {
|
||||
self.collect_captured_expr(&fo.right, own, out);
|
||||
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fo.left {
|
||||
for d in &vd.declarations {
|
||||
self.collect_captured_binding_keys(&d.id, own, out);
|
||||
}
|
||||
}
|
||||
self.collect_captured_stmt(&fo.body, own, out);
|
||||
}
|
||||
Statement::BlockStatement(b) => {
|
||||
@@ -1003,6 +1113,11 @@ impl Emitter {
|
||||
}
|
||||
oxide_parser::BindingPattern::ObjectPattern(op) => {
|
||||
for prop in &op.properties {
|
||||
// 计算键表达式(本作用域 catch 参数求值,含 IIFE 等嵌套函数)引用
|
||||
// 本函数绑定走寄存器,只须扫描键内嵌套函数表达式。
|
||||
if prop.computed {
|
||||
self.collect_captured_expr(prop.key.to_expression(), own, out);
|
||||
}
|
||||
self.collect_captured_binding_pattern(&prop.value, own, out);
|
||||
}
|
||||
if let Some(rest) = &op.rest {
|
||||
@@ -1016,6 +1131,38 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
/// 遍历绑定 pattern 的计算键表达式(`{[k]: a}` 的键)中的引用,供父层捕获判定
|
||||
/// (声明语句/for 头的模式键与 catch/默认值路径同口径)。
|
||||
fn collect_captured_binding_keys(
|
||||
&self, pattern: &oxide_parser::BindingPattern, own: &HashSet<String>, out: &mut HashSet<String>,
|
||||
) {
|
||||
match pattern {
|
||||
oxide_parser::BindingPattern::BindingIdentifier(_) => {}
|
||||
oxide_parser::BindingPattern::ArrayPattern(ap) => {
|
||||
for p in ap.elements.iter().flatten() {
|
||||
self.collect_captured_binding_keys(p, own, out);
|
||||
}
|
||||
if let Some(rest) = &ap.rest {
|
||||
self.collect_captured_binding_keys(&rest.argument, own, out);
|
||||
}
|
||||
}
|
||||
oxide_parser::BindingPattern::ObjectPattern(op) => {
|
||||
for prop in &op.properties {
|
||||
if prop.computed {
|
||||
self.collect_captured_expr(prop.key.to_expression(), own, out);
|
||||
}
|
||||
self.collect_captured_binding_keys(&prop.value, own, out);
|
||||
}
|
||||
if let Some(rest) = &op.rest {
|
||||
self.collect_captured_binding_keys(&rest.argument, own, out);
|
||||
}
|
||||
}
|
||||
oxide_parser::BindingPattern::AssignmentPattern(ap) => {
|
||||
self.collect_captured_binding_keys(&ap.left, own, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_captured_expr(&self, expr: &Expression, own: &HashSet<String>, out: &mut HashSet<String>) {
|
||||
match expr {
|
||||
Expression::FunctionExpression(fe) => {
|
||||
|
||||
@@ -788,7 +788,9 @@ impl Emitter {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 遍历解构 pattern 收集内嵌默认值表达式(AssignmentPattern.right)。
|
||||
/// 遍历解构 pattern 收集内嵌默认值表达式(AssignmentPattern.right)与
|
||||
/// 计算键表达式(`{[k]: a}` 的键,运行时求值)——二者都会在参数绑定期被
|
||||
/// 内部闭包引用,须一并纳入捕获分析。
|
||||
fn collect_pattern_default_exprs<'a>(
|
||||
&self, pattern: &'a oxide_parser::BindingPattern<'a>, out: &mut Vec<&'a oxide_parser::Expression<'a>>,
|
||||
) {
|
||||
@@ -808,6 +810,9 @@ impl Emitter {
|
||||
}
|
||||
BindingPattern::ObjectPattern(op) => {
|
||||
for prop in &op.properties {
|
||||
if prop.computed {
|
||||
out.push(prop.key.to_expression());
|
||||
}
|
||||
self.collect_pattern_default_exprs(&prop.value, out);
|
||||
}
|
||||
if let Some(rest) = &op.rest {
|
||||
|
||||
@@ -22,6 +22,7 @@ impl Emitter {
|
||||
Statement::ExpressionStatement(es) => self.pre_scan_builtin_expr(&es.expression, ctx),
|
||||
Statement::VariableDeclaration(vd) => {
|
||||
for d in &vd.declarations {
|
||||
self.pre_scan_builtin_pattern(&d.id, ctx);
|
||||
if let Some(init) = &d.init {
|
||||
self.pre_scan_builtin_expr(init, ctx);
|
||||
}
|
||||
@@ -53,6 +54,7 @@ impl Emitter {
|
||||
self.pre_scan_builtin_expr(e, ctx);
|
||||
} else if let oxide_parser::ForStatementInit::VariableDeclaration(decl) = init {
|
||||
for d in &decl.declarations {
|
||||
self.pre_scan_builtin_pattern(&d.id, ctx);
|
||||
if let Some(init_expr) = &d.init {
|
||||
self.pre_scan_builtin_expr(init_expr, ctx);
|
||||
}
|
||||
@@ -68,10 +70,20 @@ impl Emitter {
|
||||
self.pre_scan_builtin_stmt(&fs.body, ctx);
|
||||
}
|
||||
Statement::ForInStatement(fi) => {
|
||||
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fi.left {
|
||||
for d in &vd.declarations {
|
||||
self.pre_scan_builtin_pattern(&d.id, ctx);
|
||||
}
|
||||
}
|
||||
self.pre_scan_builtin_expr(&fi.right, ctx);
|
||||
self.pre_scan_builtin_stmt(&fi.body, ctx);
|
||||
}
|
||||
Statement::ForOfStatement(fo) => {
|
||||
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fo.left {
|
||||
for d in &vd.declarations {
|
||||
self.pre_scan_builtin_pattern(&d.id, ctx);
|
||||
}
|
||||
}
|
||||
self.pre_scan_builtin_expr(&fo.right, ctx);
|
||||
self.pre_scan_builtin_stmt(&fo.body, ctx);
|
||||
}
|
||||
@@ -94,6 +106,9 @@ impl Emitter {
|
||||
self.pre_scan_builtin_stmt(s, ctx);
|
||||
}
|
||||
if let Some(handler) = &ts.handler {
|
||||
if let Some(param) = &handler.param {
|
||||
self.pre_scan_builtin_pattern(¶m.pattern, ctx);
|
||||
}
|
||||
for s in &handler.body.body {
|
||||
self.pre_scan_builtin_stmt(s, ctx);
|
||||
}
|
||||
@@ -122,6 +137,7 @@ impl Emitter {
|
||||
Statement::ExportNamedDeclaration(exp) => {
|
||||
if let Some(Declaration::VariableDeclaration(vd)) = &exp.declaration {
|
||||
for d in &vd.declarations {
|
||||
self.pre_scan_builtin_pattern(&d.id, ctx);
|
||||
if let Some(init) = &d.init {
|
||||
self.pre_scan_builtin_expr(init, ctx);
|
||||
}
|
||||
@@ -297,6 +313,37 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
/// 遍历绑定 pattern 的计算键表达式(`{[k]: a}` 的键)里的 builtin 引用:
|
||||
/// 模式键是运行时求值表达式,键内 builtin 标识符须预先登记固定槽位
|
||||
/// (与对象字面量计算键预扫描同口径)。
|
||||
fn pre_scan_builtin_pattern(&self, pattern: &BindingPattern, ctx: &mut CompileCtx) {
|
||||
match pattern {
|
||||
BindingPattern::BindingIdentifier(_) => {}
|
||||
BindingPattern::ArrayPattern(ap) => {
|
||||
for e in ap.elements.iter().flatten() {
|
||||
self.pre_scan_builtin_pattern(e, ctx);
|
||||
}
|
||||
if let Some(rest) = &ap.rest {
|
||||
self.pre_scan_builtin_pattern(&rest.argument, ctx);
|
||||
}
|
||||
}
|
||||
BindingPattern::ObjectPattern(op) => {
|
||||
for prop in &op.properties {
|
||||
if prop.computed {
|
||||
self.pre_scan_builtin_expr(prop.key.to_expression(), ctx);
|
||||
}
|
||||
self.pre_scan_builtin_pattern(&prop.value, ctx);
|
||||
}
|
||||
if let Some(rest) = &op.rest {
|
||||
self.pre_scan_builtin_pattern(&rest.argument, ctx);
|
||||
}
|
||||
}
|
||||
BindingPattern::AssignmentPattern(ap) => {
|
||||
self.pre_scan_builtin_pattern(&ap.left, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn predeclare_function_declarations(&self, statements: &[Statement], ctx: &mut CompileCtx) {
|
||||
for statement in statements {
|
||||
let function = match statement {
|
||||
|
||||
@@ -261,13 +261,11 @@ impl Emitter {
|
||||
|
||||
fn emit_property_key_expression(&self, key: &PropertyKey, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||
match key {
|
||||
PropertyKey::Identifier(ident) => {
|
||||
let name = ident.name.as_str();
|
||||
let var_reg = ctx.lookup_or_builtin(name)?;
|
||||
let key_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(key_reg), Operand::Reg(var_reg), Operand::None));
|
||||
Ok(key_reg)
|
||||
}
|
||||
// 标识符计算键(`{[k]: a}`)走通用表达式分发:经 emit_static_identifier_read
|
||||
// 正确解析 upvalue/被捕获 cell/with 动态与未声明标识符读(LOAD_GLOBAL 抛
|
||||
// ReferenceError),与对象字面量/类字段计算键同口径;直接 lookup_or_builtin
|
||||
// 只读局部符号表,嵌套函数引用外层绑定会静默回退读全局。
|
||||
PropertyKey::Identifier(_) => self.emit_expression(key.to_expression(), ctx),
|
||||
PropertyKey::StringLiteral(s) => {
|
||||
let key_idx = ctx.add_constant(Constant::String(s.value.to_string()));
|
||||
let key_reg = ctx.alloc_reg();
|
||||
|
||||
Reference in New Issue
Block a user