Skip to content

fix(turbopack): classify unbound transformed references as free variables - #206

Merged
fireairforce merged 2 commits into
utoofrom
zoomdong/fix-synthetic-free-variable-analysis
Oct 30, 2026
Merged

fireairforce merged 2 commits into
utoofrom
zoomdong/fix-synthetic-free-variable-analysis

Conversation

@fireairforce

@fireairforce fireairforce commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

Summary

Input transforms can introduce identifiers with the top-level syntax context even when no runtime declaration exists, or remove the TypeScript declaration that an identifier originally referred to. The analyzer treats these identifiers as local variables, so configured compile-time definitions and module provisions cannot resolve them. Classic JSX factories, including custom factories such as h, are one example.

Infer unbound references during the existing import analysis of the complete transformed program. Reuse program_decl_usage.decl_usages for references, collect surviving declarations in the existing visitor, and filter after the visit so forward declarations remain bound. This removes the previous two additional whole-AST traversals and separate reference set. Handle variable declarations directly, including var declarations in loop heads, and keep assignments distinct from declarations.

Carry the inferred globals through the existing force_free_values set. Module fragments inherit the complete program's classification. Reads and assignment tracking use the same free-variable predicate. There is no React, provider, or source-span special case, and the AST is not mutated.

Pack integration and browser/Node dummy-runtime fixtures: utooland/utoo#3431 .

Validation

  • Scoped formatting and Clippy with all targets / warnings denied passed; the consuming Utoo workspace also passed formatting and all-target Clippy.
  • Seven focused tests passed, covering transformed references, real declarations/imports/nested scopes, TypeScript erasure, JSX factories, assignment tracking, explicit globals, and module-fragment propagation. Declaration and assignment tables include destructuring, forward declarations, and for (var ...) loop heads.
  • The full crate library run passed 599/603 tests. The four failures remain the existing fork snapshots amphtml-document, mui-sys, node-fetch, and template-pages: they expect const for default-export temporaries although the fork's existing 9c5a174ec9 emits var. Unrelated snapshots were left unchanged.
  • Both Pack browser/Node dummy-runtime snapshots passed, including provider.h with no source import.
  • The final local native binding passed 52 development/production, browser/Node execution cases and Smallfish's two native artifact execution tests with no Babel JSX loader. Execution verified binding SHA256 10fb92d1284052712a87ea78a675bf0687712cc9bf4b2c34e3b746c0357a7a1c with Pack JS 1.5.28 and 1.5.35. No node_modules files were changed.

Performance

Compare the original behavior, the earlier two-pass implementation, and this implementation with the same release-local optimization and plugin feature set. Each workload uses one warmup per variant followed by 12 interleaved rounds, fresh compiler processes, persistent caching disabled, warm OS filesystem caches, no minification, and no provider. The old React-specific workaround is therefore inactive in the baseline.

Build-time medians:

Workload Baseline Two extra passes Reused import analysis
1,000 JS modules + entry 203.70 ms 201.42 ms 202.10 ms
1,000 JSX modules + entry 229.93 ms 227.51 ms 228.09 ms
One 1 MB JS module, 2,000 functions 217.22 ms 217.11 ms 215.78 ms

Paired median changes against the two-pass version are -0.93 / +1.27 / -1.98 ms, with paired median absolute deviations of 3.31 / 3.23 / 1.65 ms. Whole-process peak RSS differences are also within sampling variation. All three variants produced identical output SHA256 values in every workload. The changes are small compared with run-to-run variation; this does not establish an overall speedup or regression. These synthetic release-local measurements do not measure production-release optimization, application HMR, or incremental builds.

An equivalent patch is maintained against official canary fcd3fdebca on zoomdong/fix-synthetic-free-vars-upstream. That port is format-checked only: required nightly 2026-10-06 is unavailable here, and the installed older nightly fails in an unrelated turbo-tasks dependency before reaching this crate. The compiler and execution validation above applies to this fork's pinned source/toolchain.

Collect surviving declarations during import analysis and infer globals from its existing usage table. Remove the two additional full-program AST traversals while preserving module-fragment classification and declaration/assignment distinctions.

Co-authored-by: Sisyphus <sisyphus@opencode.ai>
@fireairforce
fireairforce marked this pull request as ready for review October 30, 2026 06:07
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-30T06:08:12.531952Z 8b4196d Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@fireairforce
fireairforce merged commit 7a29af1 into utoo Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant