mirror of
https://github.com/oxc-project/oxc.git
synced 2026-09-14 19:36:11 +08:00
codex/fix-semantic-parameter-resolution
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
53ff08068d |
perf(packages/codegen): remove lastWasPostfixClose (#25888)
Follow-on after #25585. Previously `printExpression` checked whether last character written was a `)` or `]` by consulting `lastWasPostfixClose`. Every write kept `lastWasPostfixClose` up to date by reading the last character that was written (`updatePostfixClose`). It's statically known everywhere that writes a `)` or `]`, so instead add a category `CAT_CLOSE_BRACKET`, and write that as `state.last` using the existing machinery. `printExpression` then does its check with `state.last === CAT_CLOSE_BRACKET`. This has a large effect on perf because checking the last character that was written in `updatePostfixClose` was expensive and on a hot path. Benchmark without sourcemap generation: | Fixture | Bytes | Before | After | Faster by | |:---| ---:| ---:| ---:| ---:| | `RadixUIAdoptionSection.jsx` | 2,518 | 0\.01221ms | 0\.00716ms | \+41.4% | | `react.development.js` | 72,141 | 0\.38326ms | 0\.25377ms | \+33.8% | | `binder.ts` | 193,077 | 0\.6824ms | 0\.6458ms | \+5.4% | | `App.tsx` | 415,340 | 1\.8782ms | 1\.7846ms | \+5.0% | | `lodash.js` | 544,096 | 1\.6802ms | 1\.0902ms | \+35.1% | | `kitchen-sink.tsx` | 732,222 | 5\.2654ms | 4\.7936ms | \+9.0% | | `antd.js` | 6,683,633 | 32\.0742ms | 22\.4061ms | \+30.1% | With sourcemap generation included: | Fixture | Bytes | Before | After | Faster by | |:---| ---:| ---:| ---:| ---:| | `RadixUIAdoptionSection.jsx` | 2,518 | 0\.02126ms | 0\.01616ms | \+24.0% | | `react.development.js` | 72,141 | 0\.52684ms | 0\.39368ms | \+25.3% | | `binder.ts` | 193,077 | 1\.0708ms | 1\.0335ms | \+3.5% | | `App.tsx` | 415,340 | 3\.8221ms | 3\.7476ms | \+2.0% | | `lodash.js` | 544,096 | 2\.9815ms | 2\.4294ms | \+18.5% | | `kitchen-sink.tsx` | 732,222 | 9\.8293ms | 9\.5644ms | \+2.7% | | `antd.js` | 6,683,633 | 54\.3088ms | 44\.3745ms | \+18.3% | The main reason for the large impact on JS/JSX fixtures is that the JS build gets poisoned by JSX text. Most strings that `updatePostfixClose` sees are interned strings (source literals like `"function "`) but in JSX files it was also seeing dynamic strings (JSX text) which turned it polymorphic. Once it's gone polymorphic, it never goes back, affecting all following JS fixtures (which use the JS build). Removing the string accesses removes this cost. |
||
|
|
300763f328 |
feat(packages/codegen): add full source map support (#25585)
## Summary
Align `oxc-codegen` source maps with Rust `oxc_codegen`, using the Rust implementation as the conformance oracle.
This PR:
- changes `printSync` to return `{ code, map? }`;
- returns a standard Source Map v3 object;
- exposes Rust’s generated source map through `codegen_conformance`;
- compares the complete JS and Rust source maps;
- supports Oxc `start`/`end` offsets and TS-ESTree `range` offsets;
- uses `sourceText` to convert UTF-16 offsets and recover original identifier spellings;
- validates both maps and no-maps builds in both `preserveParens` modes;
- aligns mapping placement across JavaScript, TypeScript, JSX, templates, imports, destructuring, delimiters, and postfix chains;
- handles astral characters and all ECMAScript line terminators.
`loc`-only ASTs are not supported for source maps.
## API change
```ts
const { code, map } = printSync(program, {
sourcemap: true,
sourceFileName: "input.js",
sourceText,
});
```
`map` is a Source Map v3 object:
```ts
interface SourceMap {
version: 3;
mappings: string;
names: string[];
sources: string[];
sourcesContent?: string[];
}
```
## Implementation
Mapped writes collect only:
- the generated UTF-16 offset;
- the original source offset;
- the optional original identifier name.
After printing, the generator builds line tables for the generated output and `sourceText`, converts the offsets to line/column positions, suppresses duplicate source positions, and encodes the result as base64 VLQ.
The no-maps builds continue to remove mapping-specific writes at build time.
## Validation flow
```mermaid
flowchart TB
fixture["Fixture<br/>filename + sourceText + parse options"]
fixture --> rustParse
fixture --> jsParse
subgraph rust["Rust oracle"]
direction TB
rustParse["Parse with oxc_parser"]
normalize["Normalize Rust-only AST information"]
rustPrint["Print with oxc_codegen<br/>source maps enabled"]
rustResult["Expected result<br/>{ code, map }"]
rustParse --> normalize
normalize --> rustPrint
rustPrint --> rustResult
end
subgraph js["JavaScript candidate"]
direction TB
jsParse["Parse ESTree with oxc-parser<br/>start/end or range offsets"]
plainPrint["Print with no-maps build"]
plainResult["Actual code"]
mappedPrint["Print with maps-enabled build"]
deferred["Record generated offsets,<br/>source offsets, and names"]
encode["Convert UTF-16 offsets to positions<br/>deduplicate and encode VLQ"]
mappedResult["Actual result<br/>{ code, map }"]
jsParse --> plainPrint
plainPrint --> plainResult
jsParse --> mappedPrint
mappedPrint --> deferred
deferred --> encode
encode --> mappedResult
end
rustParse --> parseGate
jsParse --> parseGate
rustResult --> codeGate
plainResult --> codeGate
mappedResult --> codeGate
rustResult --> mapGate
mappedResult --> mapGate
parseGate["Parse agreement<br/>Rust success requires no JS errors"]
codeGate["Code equality<br/>both JS builds equal Rust"]
mapGate["Source Map v3 equality<br/>mappings + names + sources + content"]
parseGate --> pass["Fixture passes"]
codeGate --> pass
mapGate --> pass
```
Each fixture runs with both `preserveParens: false` and `preserveParens: true`.
## AST normalization
Rust’s AST contains information that ESTree cannot represent. The Rust AST is normalized before printing so the comparison measures code generation rather than representation differences.
This includes:
- parenthesized function and arrow-expression flags;
- `import "m"` versus `import {} from "m"`;
- absent versus empty import-attribute clauses;
- `assert` versus `with` attribute keywords;
- the unavailable ESTree span for a complete `with` clause.
|
||
|
|
a4478e960a |
feat(codegen): add oxc-codegen package (#25488)
Add `oxc-codegen` package - a printer written in TS. ### Features - Prints JS, JSX, TS, TSX. - Matches `oxc_codegen` exactly on all Test262, TypeScript, Acorn-JSX fixtures. - Very fast. ### Deficiencies - Pretty-printing only (no compact/minified output). - No support for printing comments. - Source map support is semi-there, but largely untested, and poor API (design doc has some ideas on how to improve it). ### This PR adds - The printer code. - Conformance testing infra. - Further tests for edge cases. - README. - Design doc - how it differs from `oxc_codegen` and why, what produces the perf. ### This PR doesn't add - Not integrated into CI or release pipeline. ### Notes This started as a slop port of `oxc_codegen`. I've iterated on that base, cleaned it up a lot, and at least skimmed through all of it. I think it's in fairly decent shape. The design doc is I think worth a read. It explains the rationale for the choices, in particular the "1 codebase, 4 builds" approach. I've put it in a new top-level directory called `packages`. It's not an app, so didn't seem to me to fit alongside `apps/oxlint` and `apps/oxfmt`. It's pure JS, so not like e.g. `napi/parser`. And it felt too heavyweight to put into `npm` dir. But maybe there's a better place for it. Co-authored-by: Cameron <cameron.clark@hey.com> |