Use arena for core compiler#5
Conversation
There was a problem hiding this comment.
Pull request overview
This PR refactors MiraScript’s core compiler pipeline to allocate AST nodes in a bumpalo arena (AstArena) instead of Box, threading a new arena lifetime through parser/AST types and updating all downstream consumers (emitter/formatter/wasm wrapper) accordingly.
Changes:
- Introduce
AstArenaand switch recursive AST ownership fromBox<T>tobumpalo::boxed::Box<'a, T>(ABox) across parser AST types. - Update parser entrypoints to accept an arena reference and allocate AST nodes via
arena.alloc(...). - Adapt emitter, formatter, and WASM Monaco integration to the new AST lifetimes and allocation model.
Reviewed changes
Copilot reviewed 47 out of 48 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/wasm/src/monaco.rs | Stores AstArena alongside parsed Script and passes arena into Compiler::parse for Monaco WASM integration. |
| crates/core/src/parser/statements.rs | Threads &AstArena through statement parsing and allocates nested nodes in the arena. |
| crates/core/src/parser/statement.rs | Updates Statement AST to use arena-backed boxes and adds arena lifetime parameter. |
| crates/core/src/parser/scripts.rs | Updates script parser to accept &AstArena and allocate trailing expression into the arena. |
| crates/core/src/parser/script.rs | Updates Script AST to carry arena-backed optional expression and arena lifetime parameter. |
| crates/core/src/parser/record_helper.rs | Threads arena into record parsing helpers and allocates record element payloads in arena. |
| crates/core/src/parser/record_element.rs | Updates record element AST types/aliases to use arena-backed boxes and arena lifetime parameter. |
| crates/core/src/parser/range.rs | Updates Range AST to use arena-backed boxes and arena lifetime parameter. |
| crates/core/src/parser/patterns.rs | Threads arena through pattern parsing and allocates recursive pattern nodes in arena. |
| crates/core/src/parser/pattern.rs | Updates Pattern AST to use arena-backed boxes and revises unknown-recovery handling accordingly. |
| crates/core/src/parser/parameter_list.rs | Threads arena into parameter list parsing and updates types to arena-backed patterns. |
| crates/core/src/parser/mod.rs | Adds ABox alias + AstArena to parser prelude and updates parse() signature to accept arena. |
| crates/core/src/parser/list_item.rs | Switches list item storage to arena-backed boxes and updates constructors to take &AstArena. |
| crates/core/src/parser/json_expressions.rs | Threads arena through JSON-expression parsing and allocates interpolated/values in arena. |
| crates/core/src/parser/iterable.rs | Updates Iterable AST to carry arena lifetime and updated inner types. |
| crates/core/src/parser/helper.rs | Threads arena through helpers and returns arena-backed optional expressions. |
| crates/core/src/parser/expressions.rs | Threads arena into expression parsing entrypoints and updates helper parsers accordingly. |
| crates/core/src/parser/expression.rs | Updates core Expression/Callable/ElseBlock/MatchCase AST to arena-backed boxes and arena lifetime. |
| crates/core/src/parser/block_expressions.rs | Threads arena through block-like expressions (if/loop/while/match/for) and allocates nested nodes in arena. |
| crates/core/src/parser/basic_expressions.rs | Threads arena through pratt parser, postfix/access parsing, interpolations, and allocates recursive nodes in arena. |
| crates/core/src/parser/ast_visitor.rs | Updates AstWalker impls to cover ABox instead of Box. |
| crates/core/src/parser/array_helper.rs | Threads arena into array parsing helpers and allocates elements/spreads in arena. |
| crates/core/src/parser/array_element.rs | Updates array element AST types/aliases to use arena-backed boxes and arena lifetime parameter. |
| crates/core/src/lib.rs | Exposes arena module and re-exports AstArena. |
| crates/core/src/formatter/statement.rs | Updates formatter impls to accept Statement<'_, '_>. |
| crates/core/src/formatter/record_element.rs | Updates formatter impls to accept RecordElementBase<'_, '_, ...>. |
| crates/core/src/formatter/range.rs | Updates formatter impls to accept Range<'_, '_>. |
| crates/core/src/formatter/pattern.rs | Updates formatter impls to accept Pattern<'_, '_>. |
| crates/core/src/formatter/parameter_list.rs | Updates formatter impls to accept ParameterList<'_, '_>. |
| crates/core/src/formatter/mod.rs | Updates formatter API to accept Script<'_, '_> and Statement<'_, '_>. |
| crates/core/src/formatter/manager/types.rs | Switches Formattable blanket impl from Box<T> to bumpalo::boxed::Box<'_, T>. |
| crates/core/src/formatter/manager/token.rs | Updates string-token formatting to accept Expression<'s, '_> slices. |
| crates/core/src/formatter/list_items.rs | Updates list item formatting to accept ListItem<'_, '_, T>. |
| crates/core/src/formatter/iterable.rs | Updates iterable formatting to accept Iterable<'_, '_>. |
| crates/core/src/formatter/expression.rs | Updates expression formatting to accept Expression<'_, '_>. |
| crates/core/src/formatter/else_block.rs | Updates else-block formatting to accept ElseBlock<'_, '_>. |
| crates/core/src/formatter/callable.rs | Updates callable formatting to accept Callable<'_, '_>. |
| crates/core/src/formatter/array_element.rs | Updates array element formatting to accept ArrayElementBase<'_, '_, ...>. |
| crates/core/src/emitter/utils.rs | Updates helper signatures to accept Expression<'_, '_>. |
| crates/core/src/emitter/mod.rs | Updates emitter entrypoint to accept Script<'s, 'a>. |
| crates/core/src/emitter/emitter_statement.rs | Threads arena lifetime through statement emission APIs. |
| crates/core/src/emitter/emitter_pattern.rs | Threads arena lifetime through pattern emission APIs. |
| crates/core/src/emitter/emitter_expression.rs | Threads arena lifetime through expression declare/emit APIs and callable/args types. |
| crates/core/src/emitter/emitter_closure.rs | Threads arena lifetime through closure/block emission APIs (block expr/args/body). |
| crates/core/src/compile/mod.rs | Creates an AstArena for compilation and updates Compiler::parse/emit signatures accordingly. |
| crates/core/src/arena.rs | Adds AstArena wrapper around bumpalo::Bump with alloc() and reset(). |
| crates/core/Cargo.toml | Adds bumpalo = { version = \"3\", features = [\"boxed\"] } dependency. |
| Cargo.lock | Locks bumpalo dependency in workspace lockfile. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Drop the old script before replacing the arena it was allocated in. | ||
| self.script = None; | ||
| if let Some(tokens) = compiler.lex() { | ||
| self.tokens = tokens.into(); | ||
| let tokens = |
There was a problem hiding this comment.
Fixed in the latest commit. self.arena = None is now set immediately after self.script = None at the top of parse(), and also after a parse failure (inside the if let Some(tokens) block) to clear any partially-allocated AST nodes.
cc4f6ec to
4f57f5c
Compare
Code Coverage OverviewLanguages: TypeScript TypeScript / npm/mirascriptThe overall coverage in commit 0923eb1 in the TypeScript / npm/typedThe overall coverage in commit 0923eb1 in the Updated |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Arena 编译器重构计划
1. 目标
用 Bump Allocation Arena(
bumpalo)替代编译器中大量Box堆分配,消除 AST 构造过程中数百次小对象malloc,预期提升编译吞吐量 10-30%,并改善 CPU 缓存局部性。2. 当前架构分析
2.1 编译器管线
2.2 AST 类型体系
所有 AST 类型位于
crates/core/src/parser/,共享一个源生命周期's(借用 token 流):Box)Expression<'s>expression.rsBox<Expression>/Box<Pattern>/Box<Iterable>Statement<'s>statement.rsBox<Expression>/Box<Pattern>Pattern<'s>pattern.rsBox<Pattern>Callable<'s>expression.rsBox<Expression>ElseBlock<'s>expression.rsBox<Expression>MatchCase<'s>expression.rsExpression、PatternRange<'s>range.rsBox<Expression>Iterable<'s>iterable.rsRange、ExpressionParameterList<'s>parameter_list.rsVec<ArrayPattern>ListItem<'s, T>list_item.rsBox<T>(泛型)RecordElementBase<'s, E, I>record_element.rsBox<E>、Box<I>(泛型)ArrayElementBase<'s, E, S>array_element.rsBox<E>、Box<S>(泛型)Script<'s>script.rsVec<Statement>、Option<Box<Expression>>2.3 关键消费者
basic_expressions.rs,block_expressions.rs,statements.rs,patterns.rs,scripts.rs等 15+ 文件emitter_expression.rs,emitter_statement.rs,emitter_pattern.rs等&'s Expression<'s>引用遍历formatter/expression.rs,formatter/statement.rs等&Expression<'_>引用遍历ast_visitor.rscompile/mod.rs3. 技术方案
3.1 选定方案:
bumpalo::boxed::Box<'a, T>+'a生命周期传播理由:
bumpalo::boxed::Box实现Deref、Clone、Debug、PartialEq——保留所有现有 deriveAstArena::alloc()分配,返回 arena 生命周期的 Box'a参数机械性传播到所有包含递归字段的类型3.2 已排除的方案
&'a mut T引用&mut T不实现Clone,导致所有 AST 类型的#[derive(Clone)]失败bumpalo::boxed::Box<'static, T>T内含TokenRef<'s>(非'static),编译失败ExprId)3.3 Arena 定义
依赖:
bumpalo = { version = "3", features = ["boxed"] }加入Cargo.toml3.4
TokenRef— 不做 Arena 化TokenRef<'s>保持原有Box<Token<'s>>/&'s Token<'s>双态设计。理由:TokenRef::Owned仅在错误恢复路径(如缺失 token 插入)使用,频率极低4. 变更清单
4.1 阶段 1: 基础设施(3 文件)
crates/core/Cargo.toml: 添加bumpalo = { version = "3", features = ["boxed"] }crates/core/src/arena.rs(新建):AstArena定义(见 3.3)crates/core/src/lib.rs: 添加pub mod arena;和pub use arena::AstArena;4.2 阶段 2: AST 类型重构(~12 文件)
所有修改遵循同一模式:类型增加
'a,递归Box<X<'s>>→bumpalo::boxed::Box<'a, X<'s, 'a>>。parser/expression.rsExpression<'s, 'a>,Callable<'s, 'a>,ElseBlock<'s, 'a>,MatchCase<'s, 'a>— 所有Box替换parser/statement.rsStatement<'s, 'a>— 所有Box替换parser/pattern.rsPattern<'s, 'a>— 所有Box替换parser/range.rsRange<'s, 'a>—Box替换parser/iterable.rsIterable<'s, 'a>— 内部类型更新parser/parameter_list.rsParameterList<'s, 'a>parser/script.rsScript<'s, 'a>parser/list_item.rsListItem<'s, 'a, T>— 泛型Box<T>替换,new()/new_with_comma()签名变更parser/record_element.rsRecordElementBase<'s, 'a, E, I>— 泛型Box替换parser/array_element.rsArrayElementBase<'s, 'a, E, S>— 泛型Box替换,类型别名更新parser/ast_visitor.rsAstWalker<'s, 'a>trait + blanket impls(Vec,Option,bumpalo::Box)parser/token_ref.rsAstWalkerimpl 签名更新(类型本身不变)4.3 阶段 3: Parser 适配(~15 文件)
'a:fn foo<'s>(...)→fn foo<'s, 'a>(...)'a:Result<Expression<'s>>→Result<Expression<'s, 'a>>Box::new(...)→arena.alloc(...)(arena 引用通过参数传入)ListItem::new()/ListItem::new_with_comma()调用适配新签名Call、AccessIndex、Function、JsonFieldName、JsonElement)添加'a关键文件:
basic_expressions.rs,block_expressions.rs,statements.rs,patterns.rs,scripts.rs,expressions.rs,helper.rs,json_expressions.rs,array_helper.rs,record_helper.rs,parameter_list.rs,mod.rs4.4 阶段 4: Emitter 适配(~6 文件)
Emitter<'s, 'c>→Emitter<'s, 'c, 'a>添加 arena 生命周期&'s Expression<'s>→&'s Expression<'s, 'a>等AstWalker<'s, 'a>trait bound 更新emit()入口函数签名更新关键文件:
emitter/mod.rs,emitter_struct.rs,emitter_closure.rs,emitter_expression.rs,emitter_statement.rs,emitter_pattern.rs4.5 阶段 5: Formatter 适配(~10 文件,feature gated)
FormatOptions和入口函数签名添加'aFormattabletrait 及所有 impl 签名更新关键文件:
formatter/mod.rs,formatter/expression.rs,formatter/statement.rs,formatter/pattern.rs等4.6 阶段 6: Compiler 入口 + 公共 API(~3 文件)
compile/mod.rs:Compiler::compile()中创建AstArena,传入 parser;更新parse()和emit()调用lib.rs: 公共导出类型签名更新benches/compile.rs): 适配新签名4.7 阶段 7: 验证
cargo check— 编译通过cargo test --all— 核心测试通过cargo test --features formatter— formatter 测试通过pnpm --filter @mirascript/mirascript... build && pnpm --filter @mirascript/mirascript test— e2e 测试通过cargo bench -p mira-core --bench main— benchmark 对比(记录重构前后数据)5. 风险与缓解
'a传播过于广泛,公共 API 破坏cargo check验证后再继续Clonederive 失败bumpalo::boxed::Box实现Clone,已验证可行6. 备忘
TokenRef— 保持Box<Token<'s>>&'a mut T—Clone不兼容bumpalo::boxed::Box<'static, T>—TokenRef<'s>不满足'staticAstArena在Compiler::compile()中创建,单次编译结束后自然 drop(已证伪,见下)bumpalo::boxed::Box的Clone实现会将 clone 分配到同一 arena7. 实施备忘(2026-07-20)
bumpalo::boxed::Box不实现Clone(bumpalo 3.x 实际无Clone/CloneIn,clone 需要 arena 而Clone::clone拿不到)。所有 AST 类型的 derive 从Debug, Clone, PartialEq改为Debug, PartialEq;仅有的 2 处 AST 深拷贝(parameter_list.rs、patterns.rsspread 错误恢复)改用std::mem::replace移出原值,语义等价。*box移出,改用ABox::into_inner()。<'s: 'a, 'a>(ABox<'a, T>要求T: 'a)。crates/wasm/src/monaco.rs已适配:MonacoCompiler新增arena: Option<AstArena>字段,沿用现有'staticunsafe 自引用模式。