Skip to content

ast: keep a class with a static accessor initializer in place - #40887

Open
robobun wants to merge 3 commits into
mainfrom
robobun/4c82f29f/fix-static-accessor-hoist
Open

ast: keep a class with a static accessor initializer in place#40887
robobun wants to merge 3 commits into
mainfrom
robobun/4c82f29f/fix-static-accessor-hoist

Conversation

@robobun

@robobun robobun commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

Problem

  • bun run moves a side-effect-free class statement to the top of the module (src/js_parser/parse/parse_entry.rs:1069, cyclic import relief). Class::can_be_moved (src/ast/g.rs:104) checks static field initializers for side effects but skips static accessor initializers. So class C { static accessor s = f() } is hoisted and f() runs before the bindings above the class exist: ReferenceError: Cannot access 'order' before initialization.
  • The bundler uses the same predicate to move a class out of a lazy __esm wrapper, so the initializer can run at the wrong time there too.

Fix

  • Class::can_be_moved treats PropertyKind::AutoAccessor like PropertyKind::Normal. A static auto-accessor initializer that is not a literal or a function keeps the class where it was written.
  • Verified: test/bundler/transpiler/es-decorators.test.ts (new "class statement placement" test, fails on 1.4.1 with the ReferenceError above). Also es-decorators-esbuild.test.ts, test/bundler/esbuild/ts.test.ts and default.test.ts.

Background

  • An auto-accessor (accessor x = v) is a field with a generated getter and setter. Its storage is initialized like a field: during class evaluation for a static one, during construction for an instance one.
  • The standard-decorator lowering turns the static initializer into __privateAdd(C, _s, v) after the class. That statement belongs to the class statement's part, so it moves with the class when the class is hoisted.
Notes

Found with a differential run of the standard-decorator lowering against tsc and esbuild. The other findings of that run are covered by #40833 (field initialization order) and are reported there.

Repro on 1.4.1:

let order = [];
order.push("top");
function sideEffect(name, value) { order.push(name); return value; }
class C {
  accessor m = sideEffect("m", 1);
  static accessor s = sideEffect("s", 2);
}
order.push("after class");
new C();
console.log(order.join(","));

bun run throws the ReferenceError inside sideEffect (the whole lowered class group runs before line 1). bun build --no-bundle output evaluated directly prints top,s,after class,m, because the hoist only happens in the runtime path.


[stamp-90s] gate passed · iteration 0 · 2 files touched

fails on main (without fix)
ASAN without fix: BUILD FAILED (no junit output)
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/mechgate.xml" test/bundler/transpiler/es-decorators.test.ts
ninja: Entering directory `/workspace/bun/build/debug'
[1/46] gen generated_host_exports.rs
generated_host_exports.rs: 122 exports (host=5, lazy=10, generic=107, rust=0); 244 extern-C blocks audited
[2/46] gen cpp.rs (cppbind)
[3/46] gen JS modules (bundle-modules)
Preprocess modules (7522ms)
Bundle modules (51ms)
Postprocesss modules (24ms)
Bundle Functions (499ms)
Generate Code (27ms)

[8.13s] Bundled "src/js" for development
  2787 kb
  197 internal modules
  13 native modules
  50 internal functions across 16 files
[3/30] cargo bun_runtime → libbun_runtime.a
[24/30] cc obj/packages/bun-usockets/src/quic.c.o
FAILED: obj/packages/bun-usockets/src/quic.c.o 
/usr/bin/ccache /usr/lib/llvm-21/bin/clang -march=nehalem -O0 -glldb -g3 -gz=zstd -fno-standalone-debug -fsanitize=address -fno-exceptions -fno-rtti -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -fvisibility=hidden -fvisibility-inlines-hidden -fno-unwind-tables -fno-asynchronous-unwind-tables -Wno-c23-extensions -ffunction-sect
... (truncated)

release without fix: 1 FAILED
bun test v1.4.1-canary.1 (d578a8c70)

test/bundler/transpiler/es-decorators.test.ts:
(pass) ES Decorators > class decorators > basic class decorator [7.92ms]
(pass) ES Decorators > class decorators > class decorator receives correct context [5.92ms]
(pass) ES Decorators > class decorators > class decorator can replace class [5.69ms]
(pass) ES Decorators > class decorators > multiple class decorators apply in reverse order [5.78ms]
(pass) ES Decorators > method decorators > instance method decorator [5.52ms]
(pass) ES Decorators > method decorators > static method decorator [5.30ms]
(pass) ES Decorators > method decorators > method decorator context has correct access [5.96ms]
(pass) ES Decorators > getter decorators > getter decorator [5.41ms]
(pass) ES Decorators > setter decorators > setter decorator [5.50ms]
(pass) ES Decorators > field decorators > field decorator receives undefined value [5.38ms]
(pass) ES Decorators > field decorators > multiple field decorators [5.83ms]
(pass) ES Decorators > field decorators > static field decorator [5.23ms]
(pass) ES Decorators > non-ASCII string-literal keys > Bun.Transpiler output preserves the key [0.50ms]
(pass) ES Deco
... (truncated)
passes on PR (with fix)
ASAN with fix: all passed
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/mechgate.xml" test/bundler/transpiler/es-decorators.test.ts
bun test v1.4.1 (d578a8c70)

test/bundler/transpiler/es-decorators.test.ts:
(pass) ES Decorators > class decorators > basic class decorator [402.09ms]
(pass) ES Decorators > class decorators > class decorator receives correct context [301.45ms]
(pass) ES Decorators > class decorators > class decorator can replace class [299.49ms]
(pass) ES Decorators > class decorators > multiple class decorators apply in reverse order [304.27ms]
(pass) ES Decorators > method decorators > instance method decorator [302.20ms]
(pass) ES Decorators > method decorators > static method decorator [300.75ms]
(pass) ES Decorators > method decorators > method decorator context has correct access [370.71ms]
(pass) ES Decorators > getter decorators > getter decorator [305.55ms]
(pass) ES Decorators > setter decorators > setter decorator [305.07ms]
(pass) ES Decorators > field decorators > field decorator receives undefined value [365.30ms]
(pass) ES Decorators > field decorators > multiple field decorators [295.03ms]

... (truncated)

release with fix: all passed
$ bun scripts/build.ts --profile=release
[configured] bun-profile → bun (stripped) in 797ms (unchanged)
ninja: Entering directory `/workspace/bun/build/release'
[1/40] gen generated_host_exports.rs
generated_host_exports.rs: 122 exports (host=5, lazy=10, generic=107, rust=0); 244 extern-C blocks audited
[2/40] gen cpp.rs (cppbind)
[3/40] gen JS modules (bundle-modules)
Preprocess modules (7532ms)
Bundle modules (58ms)
Postprocesss modules (23ms)
Bundle Functions (483ms)
Generate Code (26ms)

[8.13s] Bundled "src/js" for production
  2594 kb
  197 internal modules
  13 native modules
  50 internal functions across 16 files
[3/31] cargo bun_runtime → libbun_runtime.a
�[1m�[92m   Compiling�[0m bun_core v0.0.0 (/workspace/bun/src/bun_core)
�[1m�[92m   Compiling�[0m bun_errno v0.0.0 (/workspace/bun/src/errno)
�[1m�[92m   Compiling�[0m bun_ptr v0.0.0 (/workspace/bun/src/ptr)
�[1m�[92m   Compiling�[0m bun_boringssl_sys v0.0.0 (/workspace/bun/src/boringssl_sys)
�[1m�[92m   Compiling�[0m bun_safety v0.0.0 (/workspace/bun/src/safety)
�[1m�[92m   Compiling�[0m bun_base64 v0.0.0 (/workspace/bun/src/base64)
�[1m�[92m   Compiling�[0m bun_cares_sys v0.0.0 (/workspace/bun/src/c
... (truncated)
diff hotspot
src/ast/g.rs                                  |  5 ++++-
 test/bundler/transpiler/es-decorators.test.ts | 27 +++++++++++++++++++++++++++
 2 files changed, 31 insertions(+), 1 deletion(-)

gate history · 1 passed · 0 rejected · iteration 0

evidence per changed file
file                                           reads  edits  tests
src/ast/g.rs                                       1      1      0
test/bundler/transpiler/es-decorators.test.ts      2      1      0

@coderabbitai

coderabbitai Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7ec4336a-d1c8-4eda-91a5-d0ef6b8860f0

📥 Commits

Reviewing files that changed from the base of the PR and between cbe146a and bf226d8.

📒 Files selected for processing (5)
  • packages/bun-usockets/root_certs.der
  • packages/bun-usockets/root_certs.txt
  • src/runtime/socket/bundled_root_certs.rs
  • src/runtime/socket/cert_files.rs
  • src/runtime/socket/system_certs.rs

Included review availability: Your plan provides up to 10 included reviews per hour; 1 remains after this review.


Walkthrough

Changes

Static auto-accessor movement

Layer / File(s) Summary
Auto-accessor movability validation
src/ast/g.rs, test/bundler/transpiler/es-decorators.test.ts
Class::can_be_moved now validates static auto-accessor initializers. A regression test verifies static initializer order and instance initializer behavior.

System certificate loading

Layer / File(s) Summary
Certificate sources and FFI
packages/bun-usockets/root_certs.txt, src/runtime/socket/bundled_root_certs.rs, src/runtime/socket/cert_files.rs
The change adds bundled Mozilla root certificates and C-compatible helpers for reading certificate files and bundled DER certificates.
System certificate discovery and parsing
src/runtime/socket/system_certs.rs
The POSIX loader reads configured files and directories, applies platform fallbacks, parses PEM certificates, removes duplicates, and returns a lazy BoringSSL certificate set.

Suggested reviewers: jarred-sumner

Merge Risk: ⚪ Minimal · up to bf226

The change preserves class placement when static auto-accessor initializers may execute code; no actionable merge-blocking risk remains beyond normal checks and review.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the main change: keeping classes with static accessor initializers in their original position.
Description check ✅ Passed The description explains the problem, fix, impact, and verification results. It does not use the template headings exactly, but it provides the required information in equivalent sections.

Comment @coderabbitai help to get the list of available commands.

@robobun

robobun commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 2:17 AM PT - Aug 29th, 2026

@robobun, your commit 99d8634 has 1 failures in Build #108243 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 40887

That installs a local version of the PR into your bun-40887 executable, so you can run:

bun-40887 --bun

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no issues

No high-confidence issues detected in this change.

Comment thread src/runtime/socket/bundled_root_certs.rs Outdated
Comment thread src/runtime/socket/bundled_root_certs.rs Outdated
Comment thread src/runtime/socket/cert_files.rs Outdated
Comment thread src/runtime/socket/cert_files.rs Outdated
Comment thread src/runtime/socket/cert_files.rs Outdated
Comment thread src/runtime/socket/cert_files.rs Outdated
Comment thread src/runtime/socket/system_certs.rs Outdated
Comment thread src/runtime/socket/system_certs.rs Outdated
@robobun
robobun force-pushed the robobun/4c82f29f/fix-static-accessor-hoist branch from bf226d8 to 99d8634 Compare August 29, 2026 08:56
@robobun

robobun commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator Author

The retrigger commit bf226d8 accidentally carried five files from main (the root certificate loaders from #40862) that were sitting in the working tree. It is replaced by an empty commit, 99d8634. The PR is back to two files: src/ast/g.rs and test/bundler/transpiler/es-decorators.test.ts. The review threads on the certificate files were about code that is not part of this change and are resolved.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Comment thread src/runtime/socket/system_certs.rs Outdated
@robobun

robobun commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator Author

Status: the change is ready for review.

Reproduced with the test added in test/bundler/transpiler/es-decorators.test.ts ("static accessor initializer keeps the class in place"): on 1.4.1 it fails with ReferenceError: Cannot access 'order' before initialization, with this branch it passes.

CI on the latest head (build 108243) is red only on lanes this change does not touch: test/js/web/url/url.test.ts (also fails on main, reported to main-break triage) and tests that passed on retry or when run alone (bun-serve-file, tcp-server, h2-conformance, structuredClone-classes, bun-patch, and similar). No decorator or transpiler test failed on any lane.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants