From 6b2bd6abb856304bc56e6b352a5c8b3df3cb99b2 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Mon, 12 Oct 2026 03:56:18 +0000 Subject: [PATCH] =?UTF-8?q?perf:=20=E6=8D=95=E8=8E=B7=E5=88=86=E6=9E=90?= =?UTF-8?q?=E5=8A=A0=E6=97=A0=E5=B5=8C=E5=A5=97=E5=87=BD=E6=95=B0=E6=97=A9?= =?UTF-8?q?=E9=80=80=E5=AE=88=E5=8D=AB=EF=BC=8C=E5=85=8D=E6=8D=95=E8=8E=B7?= =?UTF-8?q?=E9=9B=86=E5=85=A8=20AST=20=E9=81=8D=E5=8E=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 40 ++-- Cargo.toml | 2 +- crates/oxide_emit/src/capture/guard.rs | 246 +++++++++++++++++++++++++ crates/oxide_emit/src/capture/mod.rs | 2 + crates/oxide_emit/src/function_body.rs | 16 +- 5 files changed, 280 insertions(+), 26 deletions(-) create mode 100644 crates/oxide_emit/src/capture/guard.rs diff --git a/Cargo.lock b/Cargo.lock index aaf900be..07af3306 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.16.2" +version = "0.16.3" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.16.2" +version = "0.16.3" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.16.2" +version = "0.16.3" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.16.2" +version = "0.16.3" dependencies = [ "ansi_term", "bumpalo", @@ -1228,7 +1228,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.16.2" +version = "0.16.3" dependencies = [ "lru", "oxide_bytecode", @@ -1237,7 +1237,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.16.2" +version = "0.16.3" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1257,7 +1257,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.16.2" +version = "0.16.3" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1271,7 +1271,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.16.2" +version = "0.16.3" dependencies = [ "num-bigint", "oxc_allocator", @@ -1286,7 +1286,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.16.2" +version = "0.16.3" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1298,7 +1298,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.16.2" +version = "0.16.3" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1307,7 +1307,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.16.2" +version = "0.16.3" dependencies = [ "bumpalo", "dashmap", @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.16.2" +version = "0.16.3" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1333,7 +1333,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.16.2" +version = "0.16.3" dependencies = [ "tracing", "tracing-appender", @@ -1342,11 +1342,11 @@ dependencies = [ [[package]] name = "oxide_net" -version = "0.16.2" +version = "0.16.3" [[package]] name = "oxide_parser" -version = "0.16.2" +version = "0.16.3" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1361,7 +1361,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.16.2" +version = "0.16.3" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1377,7 +1377,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.16.2" +version = "0.16.3" dependencies = [ "num-bigint", "num-traits", @@ -1390,7 +1390,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.16.2" +version = "0.16.3" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1410,7 +1410,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.16.2" +version = "0.16.3" dependencies = [ "bumpalo", "dashmap", @@ -1423,7 +1423,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.16.2" +version = "0.16.3" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index aa40675f..5674b86d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ members = [ ] [workspace.package] -version = "0.16.2" +version = "0.16.3" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_emit/src/capture/guard.rs b/crates/oxide_emit/src/capture/guard.rs new file mode 100644 index 00000000..26a365b8 --- /dev/null +++ b/crates/oxide_emit/src/capture/guard.rs @@ -0,0 +1,246 @@ +//! 闭包捕获分析早退守卫:扫描语句树与附加表达式,判定是否存在嵌套函数/类节点, +//! 无嵌套时使捕获集收集(collect_captured_bindings,仅从嵌套函数/类节点产出名字) +//! 整体跳过。 +//! +//! 关键约定:守卫臂集须与 `captured.rs` / `scanner.rs` 两收集器同步——新增 parser +//! 臂引入函数/类节点入口时三处同改;守卫漏任一入口臂即假阴,跳过捕获集收集导致 +//! 嵌套函数 upvalue 捕获丢失。 + +use oxide_parser::{ + Argument, AssignmentTarget, AssignmentTargetMaybeDefault, BindingPattern, ChainElement, Declaration, + ExportDefaultDeclarationKind, Expression, ForStatementLeft, SimpleAssignmentTarget, Statement, +}; + +/// 扫描语句树与附加表达式,判定是否存在嵌套函数/类节点(早退)。 +/// +/// 命中首个函数/类节点(函数声明/表达式、箭头函数、类声明/表达式)立即返回真, +/// 不分配任何集合、不克隆遮蔽集,单趟只读遍历显著轻于捕获集收集(每层嵌套函数 +/// 克隆遮蔽集并再遍历)。 +/// +/// # 边界与前提 +/// - `extra_exprs` 为参数默认值与类字段值表达式(位于函数体之外),与体同臂扫描。 +/// - 臂集与 `captured.rs` / `scanner.rs` 两收集器逐臂对齐(含 pattern 计算键、 +/// for 赋值头、catch 参数、export 包裹臂);新增 parser 臂引入函数/类节点入口 +/// 时三处同改。 +/// +/// # 注意事项 +/// - 守卫是跳过捕获集收集的必要条件:假阴(漏扫)丢 upvalue 捕获、读错 cell; +/// 假阳(多扫)仅使捕获集收集照跑,安全。 +pub(crate) fn has_nested_function(stmts: &[Statement], extra_exprs: &[&Expression]) -> bool { + for stmt in stmts { + if scan_stmt(stmt) { + return true; + } + } + for expr in extra_exprs { + if scan_expr(expr) { + return true; + } + } + false +} + +/// 扫描语句树:函数/类声明节点立即返回真,其余臂递归其表达式子节点,臂集与 +/// 两收集器一致。 +fn scan_stmt(stmt: &Statement) -> bool { + match stmt { + Statement::FunctionDeclaration(_) | Statement::ClassDeclaration(_) => true, + // export 包裹的声明:变量/函数/类声明体须下探。 + Statement::ExportNamedDeclaration(exp) => match &exp.declaration { + Some(Declaration::VariableDeclaration(vd)) => scan_var_decl(vd), + Some(Declaration::FunctionDeclaration(_)) | Some(Declaration::ClassDeclaration(_)) => true, + _ => false, + }, + Statement::ExportDefaultDeclaration(exp) => match &exp.declaration { + ExportDefaultDeclarationKind::FunctionDeclaration(_) + | ExportDefaultDeclarationKind::ClassDeclaration(_) => true, + other => other.as_expression().is_some_and(scan_expr), + }, + Statement::ExpressionStatement(es) => scan_expr(&es.expression), + Statement::ReturnStatement(rs) => rs.argument.as_ref().is_some_and(scan_expr), + Statement::VariableDeclaration(vd) => scan_var_decl(vd), + Statement::IfStatement(is) => { + scan_expr(&is.test) || scan_stmt(&is.consequent) || is.alternate.as_ref().is_some_and(scan_stmt) + } + Statement::ForStatement(fs) => { + let init = fs.init.as_ref().is_some_and(|init| { + if let Some(e) = init.as_expression() { + scan_expr(e) + } else if let oxide_parser::ForStatementInit::VariableDeclaration(vd) = init { + scan_var_decl(vd) + } else { + false + } + }); + init || fs.test.as_ref().is_some_and(scan_expr) + || fs.update.as_ref().is_some_and(scan_expr) + || scan_stmt(&fs.body) + } + Statement::WhileStatement(w) => scan_expr(&w.test) || scan_stmt(&w.body), + Statement::DoWhileStatement(d) => scan_stmt(&d.body) || scan_expr(&d.test), + Statement::ForInStatement(fi) => scan_expr(&fi.right) || scan_for_left(&fi.left) || scan_stmt(&fi.body), + Statement::ForOfStatement(fo) => scan_expr(&fo.right) || scan_for_left(&fo.left) || scan_stmt(&fo.body), + Statement::BlockStatement(b) => b.body.iter().any(scan_stmt), + Statement::TryStatement(ts) => { + ts.block.body.iter().any(scan_stmt) + || ts.handler.as_ref().is_some_and(|h| { + // catch 参数 pattern 的内嵌默认值可含嵌套函数。 + h.param.as_ref().is_some_and(|p| scan_binding_keys(&p.pattern)) || h.body.body.iter().any(scan_stmt) + }) + || ts.finalizer.as_ref().is_some_and(|f| f.body.iter().any(scan_stmt)) + } + Statement::ThrowStatement(ts) => scan_expr(&ts.argument), + Statement::SwitchStatement(sw) => { + scan_expr(&sw.discriminant) || sw.cases.iter().any(|c| c.consequent.iter().any(scan_stmt)) + } + Statement::LabeledStatement(ls) => scan_stmt(&ls.body), + Statement::WithStatement(ws) => scan_expr(&ws.object) || scan_stmt(&ws.body), + _ => false, + } +} + +/// 变量声明:绑定 pattern 的计算键与内嵌默认值是运行时求值表达式,与初始化式 +/// 同臂扫描。 +fn scan_var_decl(vd: &oxide_parser::VariableDeclaration) -> bool { + vd.declarations + .iter() + .any(|d| scan_binding_keys(&d.id) || d.init.as_ref().is_some_and(scan_expr)) +} + +/// 遍历绑定 pattern 内运行时求值表达式:计算键(`{[k]: a}` 的键)与内嵌默认值 +/// (`[x = expr]`),与 catch 参数 pattern 同形。 +fn scan_binding_keys(pattern: &BindingPattern) -> bool { + match pattern { + BindingPattern::BindingIdentifier(_) => false, + BindingPattern::ArrayPattern(ap) => { + ap.elements.iter().flatten().any(scan_binding_keys) + || ap.rest.as_ref().is_some_and(|rest| scan_binding_keys(&rest.argument)) + } + BindingPattern::ObjectPattern(op) => { + op.properties + .iter() + .any(|prop| (prop.computed && scan_expr(prop.key.to_expression())) || scan_binding_keys(&prop.value)) + || op.rest.as_ref().is_some_and(|rest| scan_binding_keys(&rest.argument)) + } + BindingPattern::AssignmentPattern(ap) => scan_expr(&ap.right) || scan_binding_keys(&ap.left), + } +} + +/// for-in/for-of 头:声明头扫 pattern 键,赋值头扫解构目标元素(含默认值)。 +fn scan_for_left(left: &ForStatementLeft) -> bool { + match left { + ForStatementLeft::VariableDeclaration(vd) => scan_var_decl(vd), + ForStatementLeft::ArrayAssignmentTarget(a) => scan_array_target(a), + ForStatementLeft::ObjectAssignmentTarget(o) => scan_object_target(o), + _ => false, + } +} + +/// 赋值目标:成员目标扫对象/键,解构目标扫元素(含默认值)。 +fn scan_assign_target(target: &AssignmentTarget) -> bool { + match target { + AssignmentTarget::StaticMemberExpression(m) => scan_expr(&m.object), + AssignmentTarget::ComputedMemberExpression(m) => scan_expr(&m.object) || scan_expr(&m.expression), + AssignmentTarget::ArrayAssignmentTarget(a) => scan_array_target(a), + AssignmentTarget::ObjectAssignmentTarget(o) => scan_object_target(o), + _ => false, + } +} + +/// 数组赋值目标:元素(含默认值)与 rest 目标。 +fn scan_array_target(a: &oxide_parser::ArrayAssignmentTarget) -> bool { + a.elements.iter().flatten().any(scan_maybe_default_target) + || a.rest.as_ref().is_some_and(|rest| scan_assign_target(&rest.target)) +} + +/// 对象赋值目标:属性标识符默认值、属性名表达式、绑定目标与 rest。 +fn scan_object_target(o: &oxide_parser::ObjectAssignmentTarget) -> bool { + o.properties.iter().any(|prop| match prop { + oxide_parser::AssignmentTargetProperty::AssignmentTargetPropertyIdentifier(id) => { + id.init.as_ref().is_some_and(scan_expr) + } + oxide_parser::AssignmentTargetProperty::AssignmentTargetPropertyProperty(p) => { + p.name.as_expression().is_some_and(scan_expr) || scan_maybe_default_target(&p.binding) + } + }) || o.rest.as_ref().is_some_and(|rest| scan_assign_target(&rest.target)) +} + +/// 解构赋值元素:带默认值的元素多一层(默认值表达式加绑定目标)。 +fn scan_maybe_default_target(target: &AssignmentTargetMaybeDefault) -> bool { + match target { + AssignmentTargetMaybeDefault::AssignmentTargetWithDefault(d) => { + scan_expr(&d.init) || scan_assign_target(&d.binding) + } + AssignmentTargetMaybeDefault::StaticMemberExpression(m) => scan_expr(&m.object), + AssignmentTargetMaybeDefault::ComputedMemberExpression(m) => scan_expr(&m.object) || scan_expr(&m.expression), + AssignmentTargetMaybeDefault::ArrayAssignmentTarget(a) => scan_array_target(a), + AssignmentTargetMaybeDefault::ObjectAssignmentTarget(o) => scan_object_target(o), + _ => false, + } +} + +/// 一元更新目标:成员目标扫对象/键。 +fn scan_simple_target(target: &SimpleAssignmentTarget) -> bool { + match target { + SimpleAssignmentTarget::StaticMemberExpression(m) => scan_expr(&m.object), + SimpleAssignmentTarget::ComputedMemberExpression(m) => scan_expr(&m.object) || scan_expr(&m.expression), + _ => false, + } +} + +/// 扫描表达式树:函数/类表达式节点立即返回真,其余臂递归,臂集与两收集器一致。 +fn scan_expr(expr: &Expression) -> bool { + match expr { + Expression::FunctionExpression(_) | Expression::ArrowFunctionExpression(_) | Expression::ClassExpression(_) => { + true + } + Expression::CallExpression(ce) => scan_expr(&ce.callee) || ce.arguments.iter().any(scan_arg), + Expression::NewExpression(ne) => scan_expr(&ne.callee) || ne.arguments.iter().any(scan_arg), + Expression::BinaryExpression(be) => scan_expr(&be.left) || scan_expr(&be.right), + Expression::UnaryExpression(ue) => scan_expr(&ue.argument), + Expression::LogicalExpression(le) => scan_expr(&le.left) || scan_expr(&le.right), + Expression::ConditionalExpression(ce) => { + scan_expr(&ce.test) || scan_expr(&ce.consequent) || scan_expr(&ce.alternate) + } + Expression::SequenceExpression(se) => se.expressions.iter().any(scan_expr), + // 赋值目标侧也可含嵌套函数(解构目标默认值),左右同扫。 + Expression::AssignmentExpression(ae) => scan_assign_target(&ae.left) || scan_expr(&ae.right), + Expression::UpdateExpression(ue) => scan_simple_target(&ue.argument), + Expression::ArrayExpression(ae) => ae.elements.iter().any(|e| e.as_expression().is_some_and(scan_expr)), + Expression::ObjectExpression(o) => o.properties.iter().any(|prop| match prop { + oxide_parser::ObjectPropertyKind::ObjectProperty(p) => { + (p.computed && scan_expr(p.key.to_expression())) || scan_expr(&p.value) + } + oxide_parser::ObjectPropertyKind::SpreadProperty(spread) => scan_expr(&spread.argument), + }), + Expression::ComputedMemberExpression(m) => scan_expr(&m.object) || scan_expr(&m.expression), + Expression::StaticMemberExpression(m) => scan_expr(&m.object), + Expression::PrivateFieldExpression(m) => scan_expr(&m.object), + Expression::TemplateLiteral(tl) => tl.expressions.iter().any(scan_expr), + Expression::TaggedTemplateExpression(tt) => scan_expr(&tt.tag) || tt.quasi.expressions.iter().any(scan_expr), + Expression::ParenthesizedExpression(p) => scan_expr(&p.expression), + Expression::YieldExpression(ye) => ye.argument.as_ref().is_some_and(scan_expr), + Expression::AwaitExpression(ae) => scan_expr(&ae.argument), + Expression::ChainExpression(c) => scan_chain(&c.expression), + _ => false, + } +} + +/// 遍历可选链元素:成员访问、调用与实参。 +fn scan_chain(element: &ChainElement) -> bool { + match element { + ChainElement::StaticMemberExpression(m) => scan_expr(&m.object), + ChainElement::ComputedMemberExpression(m) => scan_expr(&m.object) || scan_expr(&m.expression), + ChainElement::PrivateFieldExpression(m) => scan_expr(&m.object), + ChainElement::CallExpression(c) => scan_expr(&c.callee) || c.arguments.iter().any(scan_arg), + _ => false, + } +} + +/// 调用实参:静态实参与 spread 内部表达式。 +fn scan_arg(arg: &Argument) -> bool { + match arg { + Argument::SpreadElement(sp) => scan_expr(&sp.argument), + _ => arg.as_expression().is_some_and(scan_expr), + } +} diff --git a/crates/oxide_emit/src/capture/mod.rs b/crates/oxide_emit/src/capture/mod.rs index 944e3f81..fda56fdc 100644 --- a/crates/oxide_emit/src/capture/mod.rs +++ b/crates/oxide_emit/src/capture/mod.rs @@ -9,6 +9,7 @@ //! 跨区调用单向、无共享局部状态、无接收者状态。 mod captured; +mod guard; mod names; mod scanner; mod upvalues; @@ -16,6 +17,7 @@ mod upvalues; use std::collections::HashSet; pub(crate) use captured::collect_captured_bindings; +pub(crate) use guard::has_nested_function; pub(crate) use names::collect_block_level_decl_names; pub(crate) use names::collect_direct_lexical_names; pub(crate) use names::collect_own_binding_names; diff --git a/crates/oxide_emit/src/function_body.rs b/crates/oxide_emit/src/function_body.rs index f802f391..4a8a4672 100644 --- a/crates/oxide_emit/src/function_body.rs +++ b/crates/oxide_emit/src/function_body.rs @@ -15,7 +15,7 @@ use oxide_parser::{Expression, Statement, VariableDeclarationKind}; use crate::capture::{ collect_binding_pattern_names, collect_captured_bindings, collect_direct_lexical_names, collect_own_binding_names, - collect_upvalue_names, collect_var_binding_names, + collect_upvalue_names, collect_var_binding_names, has_nested_function, }; use crate::compile_ctx::{CompileCtx, FieldBuffer}; use crate::symbol_table::{Binding, ScopeKind}; @@ -689,6 +689,13 @@ impl Emitter { ParamSpec::Rest { .. } => {} } } + // 早退守卫:体与附加表达式无嵌套函数/类节点时,捕获集 + // (collect_captured_bindings,仅从嵌套函数/类节点产出名字)必为空,跳过该 + // 趟;own_bindings 是 upvalue 分析的遮蔽集、upvalue 捕获是本体对父的捕获, + // 二者独立于嵌套函数,不得跳过。 + let mut capture_exprs: Vec<&oxide_parser::Expression> = param_defaults.clone(); + capture_exprs.extend_from_slice(extra_capture_exprs); + let has_nested = has_nested_function(body_stmts, &capture_exprs); ctx.own_bindings = collect_own_binding_names(¶m_names, body_stmts); for spec in param_specs { if let ParamSpec::Pattern { pattern, .. } = spec { @@ -745,10 +752,9 @@ impl Emitter { } } - // 字段初始化表达式(值表达式)与参数默认值一并纳入捕获分析。 - let mut capture_exprs: Vec<&oxide_parser::Expression> = param_defaults.clone(); - capture_exprs.extend_from_slice(extra_capture_exprs); - ctx.set_captured_bindings(collect_captured_bindings(body_stmts, &capture_exprs, &ctx.own_bindings)); + if has_nested { + ctx.set_captured_bindings(collect_captured_bindings(body_stmts, &capture_exprs, &ctx.own_bindings)); + } // 自由变量分析:收集 upvalue 捕获(类方法也是普通函数,可捕获外层变量)。 if matches!( body_context,