mirror of
https://github.com/oxc-project/oxc.git
synced 2026-09-14 19:36:11 +08:00
c1369a71ff
### Summary
Fix private-member mangling for class heritage expressions.
The code generator previously activated a class's private-member mapping before emitting that class's decorators and `extends` expression. A private identifier in those expressions is evaluated in the enclosing lexical class scope, not the class currently being declared.
This change:
- reserves each class's mapping ID in AST traversal order;
- activates that mapping only immediately before emitting its class body;
- adds a mangler/codegen regression test covering a nested class with identically named private members.
### Reproduction
```js
class HasOuterBrand {}
class MissingOuterBrand {}
class Outer {
#x;
create(o) {
return class Inner extends (
#x in o ? HasOuterBrand : MissingOuterBrand
) {
#x;
};
}
}
```
`#x` in `Inner`'s heritage expression refers to `Outer.#x`. `Inner.#x` is not in scope until the `Inner` class body.
### Scope model
```text
Outer class body: active private mapping
Outer.#x -> #e
create(o) {
class Inner extends (#x in o ? ... : ...)
│
└─ resolves in Outer scope: #e
Inner class body: active private mapping
Inner.#x -> #t
}
```
### Before
Codegen entered `Inner` before emitting the entire class header:
```rust
p.enter_class();
p.print_decorators(&self.decorators, ctx);
...
heritage.expression.print_expr(...);
...
self.body.print(p, ctx);
```
That made private-identifier lookup prefer `Inner`'s mapping while printing `extends`, producing the wrong brand:
```js
return class t extends (#t in e ? HasOuterBrand : MissingOuterBrand) {
#t;
};
```
The `#t` in the heritage expression is invalid because `Inner.#t` is not yet in scope.
### After
The class ID is reserved first, preserving mapping IDs assigned in traversal order, but the class mapping is pushed only for the body:
```rust
let class_id = p.reserve_class();
p.print_decorators(&self.decorators, ctx);
...
heritage.expression.print_expr(...);
p.enter_class(class_id);
self.body.print(p, ctx);
p.exit_class();
```
```text
Class-ID allocation Active private-name scope
──────────────────── ─────────────────────────────
reserve Outer ID enclosing class / none
reserve nested header IDs enclosing class / none
enter Outer ID Outer body
enter Inner ID Inner body
```
Separating reservation from activation matters: moving allocation until after heritage could shift IDs for nested classes encountered in a heritage expression and break the mangler's precomputed mapping association.
### Regression coverage
The existing minifier mangler integration harness now snapshots this case with private-member mappings enabled. It confirms that the generated heritage expression uses Outer’s mapping (`#e`) while Inner’s field uses its distinct mapping (`#t`):
```js
class Outer {
#e;
create(e) {
return class t extends (#e in e ? HasOuterBrand : MissingOuterBrand) {
#t;
};
}
}
```
Oxc Codegen
High-performance code generation from AST back to JavaScript/TypeScript source code.
Overview
This crate converts AST nodes back into source code strings, supporting JavaScript, TypeScript, and JSX. It's designed for speed and correctness, producing clean, readable output.
Key Features
- Fast code generation: Optimized for performance with minimal allocations
- Source map support: Generate accurate source maps during output
- Configurable formatting: Control whitespace, semicolons, and other formatting options
- Comment preservation: Maintain comments during code generation
- Binary expression optimization: Intelligent parentheses insertion
Architecture
Code Generation Pipeline
- AST Traversal: Walk through AST nodes systematically
- Token Generation: Convert nodes to appropriate tokens/strings
- Formatting: Apply whitespace, indentation, and style rules
- Source Mapping: Track original source positions if enabled
Design Principles
- Correctness: Generated code must be functionally equivalent to original
- Performance: Minimize string allocations and copying
- Readability: Produce clean, well-formatted output
- Fidelity: Preserve semantic meaning and behavior
The codegen is adapted from esbuild's approach, optimized for Rust and oxc's AST structure.