perf: 捕获分析加无嵌套函数早退守卫,免捕获集全 AST 遍历

This commit is contained in:
2026-10-12 03:56:18 +00:00
parent 9084aff761
commit 6b2bd6abb8
5 changed files with 280 additions and 26 deletions
Generated
+20 -20
View File
@@ -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",
+1 -1
View File
@@ -24,7 +24,7 @@ members = [
]
[workspace.package]
version = "0.16.2"
version = "0.16.3"
edition = "2021"
license = "MIT"
rust-version = "1.80"
+246
View File
@@ -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),
}
}
+2
View File
@@ -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;
+11 -5
View File
@@ -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(&param_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,