oxc-project / oxc-project/backlog
Surface source `is_ascii` (+ codegen ASCII output) to enable zero-copy V8 latin1 strings across the napi boundary
Nobody has claimed this yet.
- Dominant language
- No language data
- Stars
- 7
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
Context
While profiling a production vite build (rolldown-vite → local rolldown) with Instruments + V8 CPU profiler, a large slice of the napi → JS boundary is spent in napi_create_string_utf8 — copying each module's source into the V8 heap to hand to JS plugin hooks (transform/load). In one build this was ~75 ms (the single biggest marshaling cost), and it also feeds GC churn.
V8 has no UTF-8 string representation — internally a string is one-byte (latin1) or two-byte (UTF-16). So create_string_utf8 must scan + allocate + transcode + copy. For ASCII source we can instead create a V8 external one-byte (latin1) string that points at Rust-owned bytes → zero copy (V8 holds only a header + pointer; a finalizer releases the bytes at GC). For ASCII, latin1 is byte-identical to UTF-8 and .length/indexing semantics match exactly, so it's transparent to JS.
The fast path is only correct for ASCII (non-ASCII UTF-8 ≠ latin1 → mojibake; falls back to a copy). Measured ASCII coverage on a real app (Excalidraw source):
| ext | non-ASCII bytes |
|---|---|
.ts |
0.128% |
.tsx |
0.016% |
.scss |
0.002% |
i.e. ~99.9% of code bytes are ASCII (the non-ASCII-heavy locale JSON is handled by a native plugin and never crosses). So the zero-copy fast path covers essentially all boundary traffic — if the caller can cheaply decide "is this source ASCII?".
oxc's role
oxc already scans/validates every source byte during lexing. It can surface the ASCII bit (and a couple of related enablers) ~for free, so consumers (rolldown) don't pay a redundant O(n) rescan before every boundary crossing.
Asks
- Surface
is_asciifrom source validation — expose whether the source is pure ASCII as part of the parse result (or a cheapSource/oxc_spanhelper), fused with the existing UTF-8 validation pass. Lets a consumer branchASCII → external-latin1 (zero-copy)vsnon-ASCII → copy. Closely related to #201 (read source into arena / validate in lexer). - Codegen ASCII output tracking — have
oxc_codegenreport whether generated output is ASCII (it usually is, unless source carries non-ASCII identifiers/string literals). Enables zero-copy on the return/emit direction (renderChunk/output strings crossing back to JS). - (optional) Borrow-parse of returned code — allow the parser to accept a borrowed view of JS-returned transformed code so the JS→Rust read can also avoid a copy.
Scope / where the rest lives
This issue tracks only the oxc-side enablers. The companion work:
- napi-rs: an
ExternalLatin1ToNapiValue(external one-byte string + finalizer + thecopiedout-param contract + size threshold + ASCII fast-path/fallback), plus a zero-copy read helper. - rolldown:
BindingSharedString::to_napi_valuecurrently holds anArcStr/Arc<String>but copies viacreate_string_utf8; switch it to the external path using the held Arc as the finalizer keep-alive, and route thetransform/loadcodearg through it.
Net: the napi_create_string_utf8 cost → ~0 for ASCII source, with a second-order GC reduction, and zero behavior change. The is_ascii bit from oxc is what makes the fast-path check free.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in oxc's source validation and lexing path to find where source bytes are already scanned, then inspect the oxc_codegen output path. The issue is done when parse results or a source helper expose ASCII status and generated output reports whether it is ASCII, without implementing the napi-rs or rolldown companion work.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, rust
- Domain
- compilers, performance
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100