oxc-project / oxc-project/backlog

Surface source `is_ascii` (+ codegen ASCII output) to enable zero-copy V8 latin1 strings across the napi boundary

Open
#205 1 comment 0 reactions 0 assignees View on GitHub

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
  1. Surface is_ascii from source validation — expose whether the source is pure ASCII as part of the parse result (or a cheap Source/oxc_span helper), fused with the existing UTF-8 validation pass. Lets a consumer branch ASCII → external-latin1 (zero-copy) vs non-ASCII → copy. Closely related to #201 (read source into arena / validate in lexer).
  2. Codegen ASCII output tracking — have oxc_codegen report 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).
  3. (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 ExternalLatin1 ToNapiValue (external one-byte string + finalizer + the copied out-param contract + size threshold + ASCII fast-path/fallback), plus a zero-copy read helper.
  • rolldown: BindingSharedString::to_napi_value currently holds an ArcStr/Arc<String> but copies via create_string_utf8; switch it to the external path using the held Arc as the finalizer keep-alive, and route the transform/load code arg 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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.