Repository navigation
AOT: front end, test runs and tests for the rewritten compiler - #47942
Conversation
- src/aot: the lowering writes the program that the engine's new scan, link and walk read. Scopes are made where they are first needed, captured values are copied into closures where nothing stores to them later, straight-line blocks are joined, and CommonJS and ES module graphs are resolved at build time, cycles included. - bun test --aot: module mocks, spies, hoisted factories, re-evaluated modules and --rerun-each behave as they do without --aot in many more cases. Names that tests add to built-in objects are tracked file by file. - bun:ffi: no limit on the number of open callbacks in compiled programs. - Built-in prototypes other than Object.prototype accept new names in compiled programs. Replacing an existing member is still refused. - test/aot: about 29,000 more programs, each compared with the same program run without --aot.
|
Updated 12:04 AM PT - Nov 7th, 2026
✅ @Jarred-Sumner, your commit 1bbb52e2112015fcd3614402dcce061a0376f5d0 passed in 🧪 To try this PR locally: bunx bun-pr 47942That installs a local version of the PR into your bun-47942 --bun |
It stands for a function that Bun's tables declare. Also names the engine's new preview build.
There was a problem hiding this comment.
Additional findings (no inline location, or outside the current diff):
-
🔴
scripts/build/compile.ts— Maintainers running the build-script type check get an error after this change, and a dead script stays in the tree. scripts/build/compile.ts:589 still readsopts.embedsCompiledLibraryandcfg.compiledLibrary, both of which this diff removes fromLinkOptsandConfig, andembedsLibraryis no longer used. Fix: delete line 589, and delete scripts/build/embed-compiled-library.ts, whose last caller this diff removed, so the removal of the compiledLibrary option is complete. [also at: scripts/build/aot-source-lists.ts:88 - Anyone who runs scripts/build/embed-compiled-library.ts after this merge gets a bun test that exits 1 on an unknown flag, which is a different failure from the TypeScript error already filed at scripts/build/compile.ts:589.]Why this was flagged
This diff removes
compiledLibraryfromConfig(scripts/build/config.ts) andembedsCompiledLibraryfromLinkOpts(scripts/build/compile.ts:558-563), and removes every use ofembedsLibraryinlink(). scripts/build/compile.ts:589 keepsconst embedsLibrary = opts.embedsCompiledLibrary === true && cfg.compiledLibrary;, which references two properties that no longer exist on their types and is never read. Under Bun's type stripping the line evaluates tofalseat run time, so ninja generation still works, but a TypeScript check of scripts/ reports unknown properties, and the base branch had no such error. scripts/build/embed-compiled-library.ts has no remaining caller after this diff (only its own usage string mentions it), which the repo review rules treat as dead code to delete in the same PR.Verification: Triggers on every pull request, because
.github/workflows/lint.yml:35runsbun x tsc --noEmit -p scripts/build/tsconfig.json. The diff removesembedsCompiledLibraryfromLinkOptsandcompiledLibraryfromConfig, yet scripts/build/compile.ts:589 still reads both; under strict tsc these are TS2339 errors, so the Lint typecheck step fails on this PR, whereas it passes on the base commit. -
🔴
src/runtime/cli/test/parallel/runner.rs:691— Anyone building this merge gets compile errors in several Rust crates and the C++ bindings, beyond the already-filed path_watcher.rs:849 site. Conflict hunks kept both parents' definitions, or kept one parent's callers of a symbol the other parent deleted. Fix: re-resolve every conflict region of merge 1bf73f0 so each item is defined once and every reference resolves, which covers the 10 sites listed. Same pattern at 10 sites (src/runtime/cli/test/parallel/runner.rs:682, src/runtime/cli/test/Timings.rs:213, src/runtime/cli/Arguments.rs:2362, src/resolver/resolver.rs:4047, src/runtime/webcore/Blob.rs:1886, src/runtime/webcore/Response.rs:912, +10 more). [also at: src/jsc/bindings/webcrypto/JsonWebKey.h:77 - Anyone building this branch gets a failed C++ and Rust compile: the merge with main left both sides of several…; src/spawn/process.rs:1408 - Anyone building this merge gets a Rust compile failure: the merge of main into the branch restored pre-#47893/#47895…; +2 more]Why this was flagged
HEAD 1bf73f0 merges 7f49a19 into main b9f2ec3;
cargo buildor the CMake C++ build is the entry point. runner.rs:658 and :682 both definestruct RunningFile, :667 and :691 both defineWorkerCommands(E0428); runner.rs no longer definesworker_ends_at_bail, which ParallelRunner.rs:19 re-exports and test_command.rs:2314 calls (base defines it at runner.rs:1153). Timings.rs:180-258 definesgrowth_of,is_heavy,largest_growth,growth_of_average_files,record_growthtwice and :220 readsself.heavy_over, a field the merged struct replaced withworkers(:40). Arguments.rs:2362 callscrate::cli::test::memory_of_job::parsebut src/runtime/cli/mod.rs:84-86 no longer declaresmod memory_of_job(base does). ZigGlobalObject.cpp:589-592 leavescheckIfNextTickWasCalledDuringMicrotaskwithout its closing brace and :594/:602 defineBun::updateOnEachMicrotaskTicktwice. On the base branch every one of these compiles. Fixing path_watcher.rs:849 alone leaves all of these; each needs its own resolution.Verification: Triggers on any
cargo buildof HEAD 1bf73f0 (merge of 7f49a19 into b9f2ec3); the Rust crate cannot compile. In src/runtime/cli/test/parallel/runner.rs,struct RunningFileis defined at line 658 and again at line 682;pub(crate) struct WorkerCommandsis defined at line 667 and again at line 691, in the same module (E0428). This is a hard build break the base branch does not have. -
🔴
src/runtime/cli/Arguments.rs:2361— Everybun testinvocation will panic while parsing its arguments once the crate compiles, instead of running tests as on the base branch. The diff removes--aot-memory-limitfrom TEST_ONLY_PARAMS but Arguments.rs:2361 still callsargs.option(b"--aot-memory-limit")unconditionally, and the clap table lookup hitsunreachable!("{} is not a parameter.")for an undeclared name. Fix: delete the parse block at Arguments.rs:2361-2370 together with the unusedaot_memory_limitfield andmemory_of_job::parse, or re-declare the parameter, so that no runtime lookup names a parameter absent from the command's table.Why this was flagged
bun testwith any arguments reaches parse_test_command_options (Arguments.rs:1321-1322, unconditional for CommandTag::TestCommand). Arguments.rs:2360-2361 runsargs.flag(b"--update-timings")thenargs.option(b"--aot-memory-limit")outside anyif. The diff deleted the only declaration of that option,parse_param!("--aot-memory-limit <STR> ..."), from TEST_ONLY_PARAMS (base Arguments.rs:1030).clap::Args::option(src/clap/lib.rs:486) callsself.table.find(name)(src/clap/comptime.rs:625-637);find(comptime.rs:441-470) returns only when a long name matches and otherwise ends inunreachable!("{} is not a parameter."), and the const-table fallbackfind_param_index(comptime.rs:148-181) ends inpanic!("clap: no such parameter"). Neither is debug-only, so release builds panic too and the crash handler reports a crash before any test file is read. On the base branch the parameter is declared andbun testproceeds.Verification: Triggers on every
bun testinvocation. The diff deletes the only declarationparse_param!("--aot-memory-limit <STR> ...")from TEST_ONLY_PARAMS, yet Arguments.rs:2361 still runsargs.option(b"--aot-memory-limit")outside any guard.ConvertedTable::findends inunreachable!("{} is not a parameter.", ...)at comptime.rs:469.unreachable!is not debug-only, so a releasebun testwould abort during argument parsing. -
🔴
test/js/node/crypto/node-crypto.test.js:1836— Users of node:crypto and webcrypto lose Node-compat fixes that main already shipped, and the tests that proved them are deleted without replacement. The merge dropped 413 lines from test/js/node/crypto/node-crypto.test.js and 155 from test/js/web/crypto/web-crypto.test.ts and reverted the matching source. Fix: restore main's versions of the crypto sources and the deleted describe blocks, so that once the build compiles the behavior of main (#47671, #47836) is kept rather than the branch's older code.Why this was flagged
Main commit 01ec085 (#47671) added Node-compatible digest aliases, JWK checks and tests; the merge 1bf73f0 reverted them while keeping the branch's older code. src/jsc/bindings/ncrypto.cpp:1623 findDigestByName no longer consults the removed digestNames table, so names like "RSA-SHA256", "sha-256" or "sha512-256" that main accepts in createHash/pbkdf2/hkdf stop resolving. src/jsc/bindings/node/crypto/KeyObject.cpp:1316 drops the check that threw "JWK does not contain private key material" when a private key is created from a public JWK. src/jsc/bindings/node/crypto/JSSign.cpp:229 and JSVerify.cpp:273 drop the
m_mdCtxnull guard that returned ERR_CRYPTO_INVALID_STATE. The describe blocks "%s is taken" (ArrayBuffer/SharedArrayBuffer inputs), "encodings of DiffieHellman and ECDH" and "key_ops of a JWK" that asserted these behaviors are deleted outright, so no test will flag the regression once the compile errors from the same merge are patched over.Verification: normal — triggers whenever node:crypto/webcrypto is built from this merge: the merge commit 1bf73f0 resolved the crypto conflicts by dropping main's #47671 changes while keeping their callers. HEAD's
findDigestByName(ncrypto.cpp:1623-1652) now only checks dss1, the sixotherNames. 413 lines deleted from test/js/node/crypto/node-crypto.test.js with no replacement. -
🔴
test/cli/compile/compile.test.ts:7— Thirty-eight test files fail to load after merging: they importdescribeandtestfrom "../aot/listed" (or "../../aot/listed"), but no test/aot/listed.ts exists in the tree. Both merge parents lack the file and nothing generates it, so every importer, including test/cli/compile/compile.test.ts:7 and test/cli/aot-check-and-build-flags.test.ts, errors with a module-not-found before any test runs. Fix: add the test/aot/listed module the new imports expect (exporting describe and test), or revert the 38 files to importing from "bun:test".Why this was flagged
test/cli/compile/compile.test.ts:7 reads
import { describe, test } from "../../aot/listed";, added by this diff (the base file imported them from "bun:test").git ls-tree HEAD test/aot/has no listed.ts, listed/index.ts or similar,git check-ignoredoes not ignore such a path, and neither 1bf73f0^1 nor 1bf73f0^2 contains test/aot/listed.ts. A grep for aot/listed finds 39 test files with the import, including test/cli/aot-check-and-build-flags.test.ts, test/bundler/compile-trim-runtime-refused-options.test.ts, test/bundler/compile-aot-size.test.ts and test/js/node/http/probes/node-http-probes.test.ts. Each fails at load with a resolve error, so none of their assertions run on CI, whereas the base branch ran them from bun:test.Verification: normal — triggered whenever any of the 38 importing test files is run. The diff adds test/cli/compile/compile.test.ts:7
import { describe, test } from "../../aot/listed";. No module satisfies that specifier: Globtest/aot/listed*returns nothing. Each of the 38 files will fail at module resolution before any test registers. Base branch imported these names from "bun:test" and worked. -
🔴
src/js/internal/util/deprecate.ts:12— Maintainers runningbun run typecheckget errors in src/js after this merge: the merge kept main'sfunction (arg: Arg)body while dropping the<Arg>generic from the signature. At src/js/internal/util/deprecate.ts:12Argis an unknown name, line 13 calls the zero-parameter defaultshouldEmitWarningwith one argument, and src/js/node/crypto.ts:238 passesundefinedwheredeprecatedis now non-optional. Fix: restore the base signature (<Arg = void>,deprecated: ... | undefined,shouldEmitWarning: (arg: Arg) => boolean) so the crypto.ts DEP0198 caller still type-checks, and give src/js/builtins/CommonJS.ts:286 a key or makekeyoptional again in builtins.d.ts.Why this was flagged
The trigger is
tsc --noEmitfrom the repo root (package.json:58typecheck), whose tsconfig.json references ./src/js with strict mode on (tsconfig.base.json:22). On the base branch deprecate.ts declaresgetDeprecationWarningEmitter<Arg = void>(..., deprecated: ((...args: unknown[]) => unknown) | undefined, shouldEmitWarning: (arg: Arg) => boolean = () => true)and returnsfunction (arg: Arg). The merge commit 1bf73f0 took the branch's signature (no generic,deprecatednon-optional,shouldEmitWarning = () => true) but main's body, so HEAD deprecate.ts:12 references the undeclared typeArg(TS2304), deprecate.ts:13 callsshouldEmitWarning(arg)on a function typed with zero parameters (TS2554), and src/js/node/crypto.ts:235-244 passesundefinedasdeprecatedand a one-parameter predicate, both rejected under strict. Runtime behaviour is unchanged because types are stripped, but the typecheck step fails where it passed on the base.Verification: Merge commit 1bf73f0 produced src/js/internal/util/deprecate.ts with the branch's signature and main's body.
Argis an undeclared name → TS2304 at line 12;shouldEmitWarning(arg)at line 13 → TS2554; src/js/node/crypto.ts:238 passesundefinedfor the now non-optionaldeprecatedparameter → TS2345. lint.yml:51-57 runs src/codegen/check-types-of-modules.ts on every pull_request, and this check passes on the base. -
🔴
scripts/build/deps/webkit.ts:6— Every build after this merges will stop downloading WebKit once the oven-sh/WebKit PR behind the preview tag merges or closes. WEBKIT_VERSION at scripts/build/deps/webkit.ts:6 is set to "autobuild-preview-pr-997-40597149", a preview release that GitHub deletes when its PR ends, as scripts/build/download.ts:319-336 documents. Fix: before merging, point WEBKIT_VERSION at the merged main sha or itsautobuild-<sha>release so the prebuilt tarball stays downloadable; the base pins the 40-hex sha 4d45755fed4aeef19e95beb90d3d0c884a2812bd.Why this was flagged
The pin at scripts/build/deps/webkit.ts:6 changes from a 40-hex commit sha to "autobuild-preview-pr-997-40597149". scripts/build/config.ts:64 passes it as webkitVersion, and the prebuilt download builds a release URL containing /autobuild-preview-pr-. scripts/build/download.ts:318-322 states that GitHub deletes the preview release when the WebKit PR merges or closes, and prebuiltDownloadError at download.ts:328-337 exists only to explain that 404 and tell the maintainer to edit this line. .claude/commands/upgrade-webkit.md:34 says the bump to the merge commit's
autobuild-<sha>must be done before merging the bun PR. After this merges, every CI and local build that fetches the prebuilt WebKit fails with "WebKit preview release is gone" as soon as that WebKit PR ends, where the base pin keeps resolving indefinitely.Verification: Triggers once the oven-sh/WebKit PR #997 behind the preview tag merges or closes; after that every prebuilt-mode build 404s. The diff changes scripts/build/deps/webkit.ts:6 from the base 40-hex sha to "autobuild-preview-pr-997-40597149". Nothing in the diff repoints the pin, and the source-lint test (test/internal/source-lints/webkit-prebuilt-url.test.ts:183-185) accepts any
autobuild-*tag, so no check stops the merge.
|
Don't merge this yet, and don't review the shared files at this head. The merge with The first commit is parented on
CI: every job that builds Bun also fails until oven-sh/WebKit#997's preview build is published. |
There was a problem hiding this comment.
Findings marked 🟡 are optional suggestions and need no follow-up push.
Still open from earlier reviews (3):
- 🔴
src/js/ahead-of-time/node/_http_agent.ts:2—Compiled (--aot) programs lose the keep-alive fix that plain bun run has: an idle pooled socket with unread bytes is ha… - 🔴
src/js/node/net.ts:2604—Users of node:net and node:http lose the 'timeout' event for a write that waits on a connecting socket, which main alre… - 🔴
src/runtime/cli/Arguments.rs:750—Test files still pass --aot flags this diff deletes from Arguments.rs, so those suites fail after merging while they pa…
If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.
Additional findings (outside the current diff — GitHub can't attach inline comments there):
-
🟡
scripts/build/binary-expectations.ts— nit: dead code left by the merge.symbolsOfIRTextat scripts/build/binary-expectations.ts:413 has no caller: binaryExpectations() at line 519 now calls the newtestOnlySymbols(line 367) that supersedes it, and the Windows-shim doc comment at lines 404-408 is now stranded above the dead function instead of above shimExpectations. Fix: delete symbolsOfIRText and its doc comment so the shim comment again sits on shimExpectations.Why this was flagged
The diff adds testOnlySymbols at scripts/build/binary-expectations.ts:367-403 and changes line 519 from
symbolsOfIRText(cfg)totestOnlySymbols(cfg). A repo-wide grep forsymbolsOfIRTextfinds only its definition at line 413, so it is unreachable. The build-script tsconfig has noUnusedLocals false, so nothing flags it. The doc comment for the Windows.bin/shim at lines 404-408 now precedes the dead function rather than shimExpectations() at line 442. No runtime effect; a maintainer reading the file sees two overlapping forbidden-symbol lists and a misplaced comment.Verification: nit. Dead code introduced by this diff, which the repository's REVIEW.md explicitly asks to be deleted in the same PR. The diff for scripts/build/binary-expectations.ts rewires the only call site at line 519 from
symbolsOfIRText(cfg)totestOnlySymbols(cfg), while leavingfunction symbolsOfIRText(cfg: Config)at line 413 untouched. No runtime effect. -
🔴
src/sql_jsc/shared/RowDecoder.rs:1— Anyone building this merge gets a compile error in the bun_sql_jsc crate: the file is deleted but still declared and used. src/sql_jsc/lib.rs:33 keeps#[path = "RowDecoder.rs"] pub mod row_decoder;, and src/sql_jsc/postgres/PostgresSQLStatement.rs:9 and PostgresSQLQuery.rs:6 still importRowDecoderfrom it. Fix: restore src/sql_jsc/shared/RowDecoder.rs (the base's version, blob c01609af7) or remove the module declaration and move its users to whatever replaced it, so that everyrow_decoderreference in src/sql_jsc resolves. The base branch has the file and builds.Why this was flagged
The diff deletes src/sql_jsc/shared/RowDecoder.rs (present at base 05a0ec9 as blob c01609af7fb1baed97c5432cd6039a1076f95601, absent in HEAD's tree and working directory). No other file under src/sql_jsc changes in the diff. src/sql_jsc/lib.rs:33-34 still reads
#[path = "RowDecoder.rs"] pub mod row_decoder;, so rustc fails with a missing-file error for the module. src/sql_jsc/postgres/PostgresSQLStatement.rs:9 (use crate::shared::row_decoder::{RowColumn, RowDecoder, WireFormat};, used at :30, :47, :146) and src/sql_jsc/postgres/PostgresSQLQuery.rs:6 and :454 also depend on it. The base branch compiles bun_sql_jsc; after the merge anycargo buildof the workspace stops at this crate. This is a deleted-file breakage, distinct from the already-filed conflict-hunk duplicates in runner.rs and path_watcher.rs: the fix is to restore the file or delete the module and its users, not to resolve a kept-both-sides hunk.Verification: normal — triggers on any build of the merged tree. src/sql_jsc/shared/RowDecoder.rs is deleted. Nothing replaces it. Yet src/sql_jsc/lib.rs:33-34 still has
#[path = "RowDecoder.rs"] pub mod row_decoder;, and the module is actively used: PostgresSQLStatement.rs:9 and PostgresSQLQuery.rs:6. So rustc fails with a missing module file error (E0583) and the whole Bun build breaks at HEAD. -
🔴
src/bun_alloc/lib.rs:1560— Debug builds of the bundler now carry a data race in the resolver's directory cache: a thread answering a known directory reads overflow-list counters that another thread is appending to. Since this change read_dir_info at src/resolver/resolver.rs:4632 calls at_index with no resolver lock, and the debug_assert! in OverflowList::at_index_mut at src/bun_alloc/lib.rs:1557-1560 readslist.usedand the block'sused, which put_unpublished (lib.rs:2363, under only the map's own mutex) writes at the same time. Fix: make every counter a lock-free reader touches atomic, as backing_buf_used was made here, or drop the counter reads from at_index_mut; the same read happens at lib.rs:1635 under the shared entries lock.Why this was flagged
A project whose resolver has cached more than 2048 directories (DirInfo::HashMap is BSSMapInner<DirInfo, 2048, true>, so later slots go to overflow_list) is bundled by several worker threads in a debug build. Thread A resolves under a directory already published and takes the new lock-free path in read_dir_info: src/resolver/resolver.rs:4626-4633 calls dir_cache_mut().at_index(known.index) without RESOLVER_MUTEX or DIRECTORY_MUTEXES. at_index (src/bun_alloc/lib.rs:2280) goes to OverflowList::at_index_mut, whose debug_assert! at lib.rs:1557-1560 reads self.list.used and ptrs[block_id].used with plain loads. Thread B at the same time reads a new directory and calls put_unpublished (lib.rs:2324) -> overflow_list.append (lib.rs:2363), which writes block.used at lib.rs:1491 and list.used at lib.rs:1447 under the map's mutex alone, which the reader does not take. Before this change every at_index ran under the global RESOLVER_MUTEX, so no such unsynchronized read existed. Release builds read no counter in at_index_mut, so the consequence is confined to debug builds.
Verification: Base held
let _unlock = self.mutex.lock_guard();at the top ofread_dir_info(old resolver.rs:4438). The PR removes it; src/resolver/resolver.rs:4626-4633 now callsself.dir_cache_mut().at_index(known.index)with no RESOLVER_MUTEX.OverflowList::at_index_mut(src/bun_alloc/lib.rs:1552-1560) does plain reads ofusedthatoverflow_list.append(2363) writes under only the map'smutex.
The earlier merges of main into this branch were made from a merge base older than what the branch's tree really holds, and took this branch's side wherever the two differed. That dropped recent changes of main in files this branch has no reason to touch, among them fixes and tests in node:crypto and WebCrypto. This redoes the merge over the commit of main that the tree stands on (2b0ace8), up to main at 05a0ec9. Outside the compiler's own directories every file is main's again, byte for byte, except for the 347 that this branch changes on purpose. Of the crypto, TLS and X.509 files that is one, node_crypto_binding.bind.ts, which gains a declaration of what becomes of randomInt's callback. WEBKIT_VERSION names the preview build of oven-sh/WebKit#997 at 1a9ffc0a. The tree is formatted: cargo fmt, bun fmt, clang-format.
|
The merge of What was wrong: the earlier merges used a merge base older than what the branch's tree really holds and took this branch's side wherever the two differed. That dropped recent changes of What holds now, checked on the pushed tree:
Expected to fail in this run, fixed locally, coming with the next push: three source lints, and 35 test files under Still not ready to merge. |
The redone merge was made from this branch's first commit and so left out the one line that c47b364 had added since. Without it the table of built-ins is not generated, and nothing builds.
There was a problem hiding this comment.
The last 3 reviews of this pull request each found new blocking issues. Where they share a root cause, one fix may close them together.
Findings marked 🟡 are optional suggestions and need no follow-up push.
Still open from earlier reviews (3):
- 🔴
src/runtime/cli/Arguments.rs:750—Test files still pass --aot flags this diff deletes from Arguments.rs, so those suites fail after merging while they pa… - 🔴
src/runtime/cli/compile_options.rs:26—Maintainers running the --explain tests get assertion failures after this merge: the allowed-values message changed but… - Also unresolved: 1 minor or pre-existing.
If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.
Additional findings (outside the current diff — GitHub can't attach inline comments there):
-
🔴
test/parallel-allowlist.json:715— Maintainers running test/internal/parallel-allowlist.test.ts get a failure after this merge: its "excludeFiles are real files inside listed dirs" test finds entries for test files this diff deletes. test/parallel-allowlist.json:715 still lists aot/bun-build-takes-aot-disable-and-aot-heat.test.ts, which the diff removes from test/aot, and at least five more deleted files stay in excludeFiles (lines 671, 861, 881, 1431, 1439). Fix: drop every excludeFiles entry whose file no longer exists in test/aot, or regenerate the list with scripts/update-parallel-allowlist.mjs, so the existsSync check at parallel-allowlist.test.ts:22 passes.Why this was flagged
Running bun test test/internal/parallel-allowlist.test.ts on the merged tree. The test at test/internal/parallel-allowlist.test.ts:20-24 filters table.excludeFiles by existsSync(join(testDir, f)) and expects the bad list toEqual([]). test/parallel-allowlist.json:715 (inside "excludeFiles", which starts at line 285) names aot/bun-build-takes-aot-disable-and-aot-heat.test.ts, and the diff deletes test/aot/bun-build-takes-aot-disable-and-aot-heat.test.ts (present as a blob in base 05a0ec9). The same holds for the entries at lines 671, 861, 881, 1431 and 1439 (a-function-constructor-kept-under-a-computed-key-costs-the-analysis-a-fixed-amount, a-file-that-is-saved-while-the-layers-of-a-test-run-are-built, a-layered-test-run-goes-by-the-flag-cwd, a-build-that-holds-too-much-memory-is-asked-what-it-compiles, a-field-that-holds-undefined-outside-the-cell-fails-no-test), none of which exist under test/aot at HEAD. On the base branch those files existed and the assertion held. Nothing else in the runner prunes missing entries, so the test fails on every lane.
Verification: normal — triggers whenever
test/internal/parallel-allowlist.test.tsruns on the merged tree.test/internal/parallel-allowlist.test.ts:20-23assertsexpect(bad).toEqual([]).test/parallel-allowlist.json:715still listsaot/bun-build-takes-aot-disable-and-aot-heat.test.ts;existsSyncalone makes these entries land inbad, so the test fails. Result: a repository self-check test that passes on base fails after merge.
Points WEBKIT_VERSION at autobuild-preview-pr-997-73da0369.
Tests. test/aot loses 1.5 million lines. A family of generated programs
now keeps a few witnesses in the tree, each of which has failed at least
once or is the only program that reaches some code; the generator that
makes the whole family stays beside them. test/expected-durations.json and
test/parallel-allowlist.json are regenerated for the test files that are
left.
Lints.
- cargo clippy: 22 errors fixed, and PROGRAM_FILE_FLAG is behind the cfg
that its only user is behind, which also fixes the hardened x64 build.
- tsc -p src: seven type errors.
- unused_pub: 42 items that nothing used are deleted, 7 move under
cfg(test). One stays until the engine's generator changes with it.
- ThreadPool.rs's test code compiles against main again, and miri finds
the module it could not find.
- parts.rs, MemoryOfJob.rs, two environment variables nobody reads and
three design notes are removed.
AddressSanitizer. test/harness.ts and test/aot/run-parallel.ts set Malloc=1
again, as main does. The branch had dropped both lines, so the ASAN lane
did not see WTF's fastMalloc memory.
test/internal/address-sanitizer-sees-wtf-allocations.test.ts is main's.
Compiler. The front end of the same tree as the engine. Together they fix,
among others:
- `if (path.startsWith(dir + "/")) .. else if (path.startsWith(dir))` took
the first test's answer for the second
- `Bun.sleep(0).finally(() => { throw "late" }).catch(e => e)` lost the
reason of the rejection
- `const { shown } = await import("./crate.cjs")` was undefined for a
CommonJS file marked __esModule that also sets exports.default
- `require("./levels.ts").Level.High` folded the enum member and dropped
the require, so the file never ran
- a `const` read in its dead zone across an ES module cycle and a CommonJS
file read garbage instead of throwing a ReferenceError
- `for (; i < 2; i++) { if (..) break; .. }` was unrolled with the wrong
final value of `i`
- a function reached through `Object.values(namespace)` and called before
a `const` that it reads is initialized
- `require` shadowed by a parameter, or replaced through `given || require`
The conflicts are all in src/aot and all come from main's removal of unsafe blocks: - statistics.rs: mi_process_info takes Option<&mut usize>. - engine.rs: Bun__AOT__initializeOptions is a safe fn that takes &mut CompileOptions. The fake that a test build calls in its place takes the same. - engine/testing.rs, engine/tests.rs: bytes_of is gone; bun_core::slice_as_bytes, with the element type written out where it was inferred from bytes_of's parameter. - compile.rs: the code that main touched is no longer in this file.
test/internal/source-lints/byte-search.test.ts refuses a scalar loop over bytes where bun_core::strings has the search: - load/modules_with_queries.rs: index_of_any for '#', '%' and '\' - load/react_compiler/module_items.rs: index_of_char_usize for ':' - lower/commonjs/variables.rs: contains_char for '?'
There was a problem hiding this comment.
The last 4 reviews of this pull request each found new blocking issues. Where they share a root cause, one fix may close them together.
Findings marked 🟡 are optional suggestions and need no follow-up push.
Still open from earlier reviews (1):
- Unresolved: 1 minor or pre-existing.
Additional findings (outside the current diff — GitHub can't attach inline comments there):
-
🟡
src/install/PackageManager/CommandLineArguments.rs:89— nit: two stale mentions of the deleted--aot-log-type-mismatchesflag stay in src after this change removes it from AUTO_PARAMS. sweep:aot-log-type-mismatchesThe ignored-flags table at src/install/PackageManager/CommandLineArguments.rs:89 says it mirrors AUTO_PARAMS of Arguments.rs but still lists the flag, sobun install --aot-log-type-mismatchesis silently accepted whilebun runnow rejects it; src/bun_core/output.rs:664 still matches it in compiles_in_place. Fix: drop the name from both lists, as the diff did for completions and docs. [also at: src/bun_core/output.rs:664 - nit: maintainers keep a reference to a CLI flag this change deletes. compiles_in_place at src/bun_core/output.rs:664 still matches b"--aot-log-type-mismatches", which the diff removes from Arguments.rs, compile_options.rs, the completions and the types.]Why this was flagged
The diff deletes
--aot-log-type-mismatchesfrom RUNTIME_PARAMS_, AUTO_REQUIREMENT_ROWS and BUILD_ONLY_PARAMS in src/runtime/cli/Arguments.rs and from completions/bun.bash, bun.fish, bun.zsh and bun-cli.json. A repo-wide grep (no path filter) still finds the name at src/install/PackageManager/CommandLineArguments.rs:89, in RUNTIME_FLAGS_PARAM whose comment at :86-87 says it lists the no-value flags of AUTO_PARAMS, and at src/bun_core/output.rs:664 in compiles_in_place(), which keeps inherited file descriptors open for a flag nobody can pass any more. A user runningbun install --aot-log-type-mismatchesgets the flag swallowed instead of theunknown flagerror thatbun rungives; on the base branch both commands accepted it. Nothing else breaks; this is a dead-code cleanup the repository guidance asks for in the same PR.Verification: nit. Triggering condition: whenever anyone passes
--aot-log-type-mismatchesto an install-family command. src/install/PackageManager/CommandLineArguments.rs:89 insideRUNTIME_FLAGS_PARAMstill lists a flag AUTO_PARAMS no longer has, so install-family commands silently accept it while run/compile reject it; src/bun_core/output.rs:661-665compiles_in_placestill matches it.
There was a problem hiding this comment.
Findings marked 🟡 are optional suggestions and need no follow-up push.
The last 5 reviews of this pull request each found new blocking issues. Where they share a root cause, one fix may close them together.
Still open from earlier reviews (2):
- 🔴
src/crash_handler/report_file.rs:46—A compiled program that assigns process.env.BUN_CRASH_REPORT_TAG or BUN_CRASH_REPORT_DIR more than once leaks the previ… - Also unresolved: 1 minor or pre-existing.
If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.
…dead code Lints: - Every finding of mordant on the last push has its fix: 25 small refactors, three types that said one state in two or three fields (BunTypes, Getter, ImporterCopy), one enum in place of two bare bools, and `pub` items that nobody used, with two dead chains behind them (-184 lines). - Four unit tests of bun_sema did work that grew as the square of their rounds. Miri ran one of them for over an hour. They ask the same at 100 rounds. Tests of plain Bun that the last push turned red: - Fourteen tests start programs stored in test/aot/parallel, some by names that they put together. Thinning that folder had taken 35 of them away. Back to main's text, because the compiler does not need the change: - How an ELF executable is written (clones, shared blocks, copies in the kernel, punched holes). `bun build --compile` without --aot wrote by it too. - The vectorized kernels of node:path, the file sink's close, sleepSync, eleven sort calls, the JIT for the filters of plugins, src/bundler/options.rs, the opt-level of the React Compiler. - The tests that set Malloc=1: an ASAN build takes WTF's memory from the system allocator by its build. Fixed: - `bun test --aot --shard=i/n` crashed where the system refused a thread. That share of the deal is now done on the calling thread, and the deal is the same. Deleted: - The crash report file (--aot-crash-report-dir, two environment variables). - 2 MB pages for the memory of a build. - The positions and names of Bun's own modules, which nothing read (-1,171 lines of TypeScript). - --aot-log-type-mismatches in two tables, five entries of runner.node.ts and 76 lines of test/expectations.txt that named files that are gone. - About 82,000 lines of stored test programs. - Seven files of tests that held only the frames of a stack trace, or a feature that is gone. test/expected-durations.json is main's text with a measured time for each new test file. test/js/node/http/node-http-fast-path-coverage-fixture.ts no longer replaces process.nextTick: it tells a finish within one tick by its frame.
CI starts ./scripts/runner.node.ts directly. The last commit stored it as 100644, so every test step ended with "Permission denied" (exit 126) before a test ran. test/aot/tools/generate-turns-that-start-over.py lost its bit the same way.
What this does
The Bun side of the rewritten ahead-of-time compiler. The engine side is oven-sh/WebKit#997, which has the design and the numbers.
src/aot: the lowering writes the program that the engine's new scan, link and walk read. Captured values are copied into closures where nothing stores to them later, straight-line blocks are joined, and CommonJS and ES module graphs are resolved at build time, cycles included.bun test --aot: module mocks, spies, hoisted factories, re-evaluated modules and--rerun-eachbehave as they do without--aotin many more cases.--aotbuild, in TypeScript only:bun compileandbun build --compile --aotstop at an error thattscwould report under the project's own tsconfig;bun test --aotprints it as a warning and runs. JavaScript,node_modulesand.d.tsfiles are never checked. In a project that does not list"bun"intypesthis first form still misses ordinary mistakes and has known false stops; the newest commit's message lists both.docs/compiler/compatibility.mdxsays under "Dynamic imports" what a path that isn't a constant embeds.bun:ffi: no limit on the number of open callbacks in compiled programs (it was 256).--aotdoes otherwise thanmain" below.src/semais shared withbun check,bun lint,bun fmt,--checkandbun build --dts, which run it without the compiler.Size
Against the
mainit is merged with (b1305dd, November 5): 35,326 files, +523,094 −1,761,628. Most of both numbers is tests.test/aottest/The deleted files under
test/aotare the old compiler's: tests of its passes and its counters. The three deleted elsewhere are this project's own (test/internal/builtins-table). Each stored program undertest/aotis compared with the same program run without--aot.What Bun without
--aotdoes otherwise thanmainThis PR is to change nothing that Bun does without
--aot. It still does. A reading of every hunk frommain(b1007f11) tod08cc055outsidetest/,docs/,src/aot/andsrc/js/ahead-of-time/, 627 files, +52,509 -21,855, 4,555 hunks, nothing built for it, counts: 334 hunks take out code of the previous compiler, 1,715 add what only an--aotbuild, an--aottest run or a compiled executable reaches, 914 are comments, declarations and unit tests, and 1,592 change code that runs without--aot: 1,238 insrc/sema, 354 elsewhere. In the 354 it finds no changed answer ofbun file.js,bun test,bun installorbun buildbeyond those that are marked below. The lines ared08cc055's. "By reading" holds for all of it.The engine.
42067fceon WebKit'smain, and since1bbb52e2scripts/build/deps/webkit.ts:6names that commit, no preview build. Its tree is the tree of the last preview (3c42aae2) to the byte.aot/: 157 hunks that a plain program passes through, listed there by file; among them a second mode in BBQ, the baseline WebAssembly JIT.mainthat Bun'smain, which pins4d45755f, never shipped: React typescript #967, Cloudflare worker deploy support #971, refactor: create a high-level property iterator #972, fix: prop iterator cstring variant #974, Asynchronous SQLite API #978, Ignore browser disable paths to support more node packages #979, Update README for development help #982, bun as superuser #983, all work on the previous compiler: 99 files outsideaot/, +4,320 -469, 268 hunks, 56 of which a plain program passes through. 37 shipped files of B3 and Air change (+2,191 -407), the back end of FTL and of WebAssembly's OMG, for a compiler that this PR removes; the new one does not use B3. Most of it is kept from the JIT byB3::Policy. 34 hunks are not, in phases that every FTL and OMG compile runs:fixSSA, the greedy register allocator, the stack allocator,optimizeBlockOrder,Value::returnsBool(). None changes the emitted code, by reading. One cannot be vouched for by reading:b3/B3FixSSA.cpp:361to:496, a new way to find where a variable is live, which decides where Phis go;testb3'stestFixSSAMatchesNaivePhiPlacementand the WebAssembly tests with OMG forced were not run for this page. Also:JSObject::hasDirectProperty()answers at once without compiled code for four classes; everyMapandSethas 4 bytes more, everyHeapone pointer.bun check,bun lint,bun fmt,--check,bun build --dtsrunsrc/sema(304 files, +35,826 -8,769), which earlier forms of this text counted among the compiler's folders and never read with this question. Of its 1,238 hunks 1,087 are judged harmless, 44 can change what is printed, 38 could not be judged by reading, 69 cost memory or time.function (this)is typed astsctypes it (check/expr.rs:3004); several quadratic and worse cases end in seconds (check/unions.rs,check/flow.rs,check/shape.rs,check/union_property.rs,check/context.rs: new caches and shortcuts for large unions); files may nest deeper before they are refused (bind/binder.rs:4986,parser/parser/expr.rs:435); a type-onlyimport x = require(..)keeps the directive prologue open and a type-only statement in a function body ends it (hir/runtime_syntax.rs:89,parser/parser/runtime_syntax.rs:117); fourteen errors of the parser are labeled early errors (parser/parser/ty.rs:822).ce10cc29: with@types/buninstalled, a question that compile builds ask of every file's tree before their workers start was asked bybun checkwhile they run (check/aot/program_symbols.rs:96): it could endbun check. It is asked in compile builds alone.check/unions.rs); TS2563 struck in a very large function wheremainpasses (check/flow.rs).test/cli/check/answers-that-no-shortcut-may-change.test.tsholds both: 1 case passes and 4 fail without the change, 5 pass with it.bun lintno longer closes a ring through a written type (check/expr.rs:4858).check/commit.rs:191), a pass over all files for each round (driver/lib.rs:4978); a file with a TS2339, TS2305 or TS2694 on its own declarations keeps its tree to the end (program/retire.rs:436); every file's node lists lie in shared chunks of 4 MiB (hir.rs:638,src/bun_alloc/MimallocArena.rs:408); memo tables that are never emptied (check/flow.rs:4489,:5266,check/instantiate.rs:702,types.rs:1310); every checker is about 6 KB larger (check/mod.rs:1046). The peak memory ofbun checkwas not measured.unsafeon their road:hir::Fixed,ListBlock,Listed::lent_by, the thread-local of recent arenas inSession::arena(), and a new crate,src/sort_by_number, which moves elements as bytes.Bun.build, flags, types.aotHeator anaotDisableof the wrong type is no TypeError any more". It is the other way round:Bun.build({ compile: { aotHeat: anything } })always throws now (compile.aotHeat is not a valid option): every key ofcompilethat begins withaotand is not known is refused before any is read, andaotHeat,aotDisable,aotValidate,aotLogTypeMismatchesleft the table (src/runtime/api/JSBundler.rs:346,src/runtime/cli/compile_options.rs:28). At the top level:Bun.build({ aotHeat })threw "goes inside compile" onmainand is passed over now;({ aotOut })and({ aotHarden })throw it now.main's compiler that this one does not have are not known any more; three are new:--aot-harden,--aot-cpu,--aot-stored-strings, the last of which works without--aot.bun checkloses--aot-reportand--aot-report-detail. The help of--explainand--obfuscateis reworded (src/runtime/cli/Arguments.rs,completions/).bun install --aot-log-type-mismatchesis refused wheremainpasses it over (src/install/PackageManager/CommandLineArguments.rs:88).packages/bun-types/bun.d.ts:3862,:4047,:4056: the types ofBun.build({ compile })follow, but foraotOut, which is read and not declared;aotExplainloses"missed-optimizations"and gains"forms"and"statistics".--aotbuild that imports a macro (with { type: "macro" }), whichmain's compiler ran, stops with TS100053 (docs/compiler/compatibility.mdx).--trim, the resolver, executables, the bundler.src/trim/features/engine.def:121:bun build --compile --trimwithout--aotremoves 38 more groups of engine code, on the word that only compiled code enters them: by reading, never run. The engine's removable features go from 56 to 94, and their tables are in everybun.src/trim/apply.rs:17: removed code is filled withhlt(x64, but Mach-O) and zeros (arm64), not breakpoints; on FreeBSD x64 the crash handler may not know the new byte.src/trim/decide.rs:88.src/resolver/resolver.rs:6215: every file that is found by its exact name passes one more test;:1602,:1705,:1769: every resolve sets and tests a flag, every success looks at the graph once more;:2220,:2365:require.resolve(name, { paths })makes three more calls for each folder. EveryResolveris 16 bytes larger.import styles from "./a.module.css"under plainbunruns about 230 lines that moved fromsrc/runtime/jsc_hooks.rs:3620,:3648tosrc/bundler/css_module_graph.rs; one error thatmainthrows to JavaScript ended the process; at the head it is an error again (by reading: no run reaches it on either build).src/standalone_graph/executable_file.rs:470(21 hunks):bun build --compilewithout--aotwrites its executable through a new type.StandaloneModuleGraph.rs:212: such an executable passes new tests in its lookups of files and folders while it runs;src/runtime/jsc_hooks.rs:3891,cli/startup_options.rs:566: one more filter for each module.src/js_parser/lower/import_meta_glob.rs:1044: two thread-local cells are read for eachimport.meta.glob().src/bundler/ParseTask.rs:2498.src/exe_format/mapped_file.rs:226: one moremmapfor each moved range, on a Linux host.Start, modules, exit.
buncompares its first argument with the prefix--aot-program-file;bun <file>andbun runtest for an environment file once more,bun runtwice (src/runtime/cli/run_command.rs:1042,:2415); the getter ofprocess.execPathhas one more test (node/node_process.rs:33); on Linux two flags are compared, not three (src/bun_core/output.rs:662).src/runtime/jsc_hooks.rs:4431,:4439: the check that everyimportandrequire()passes before a file is transpiled is reordered; an executable without the run-time transpiler loads an embedded.nodefile now.src/jsc/bindings/ZigGlobalObject.cpp:4165: three to four tests for each module that is resolved.JSCommonJSModule.cpp:428: one for each firstrequire()of a CommonJS module and one for each module object that is destroyed.BunProcess.cpp:385: one at exit.src/jsc/VirtualMachine.rs:5999,web_worker.rs:1684,bindings/NodeAsyncHooks.cpp:112,BunSignalActions.cpp:149: a few instructions at a failed resolve, at the start of a Worker, where tracking of the async context is turned on, where a SIGILL action is set.BUN_REQUIRE_INTERNAL_MODULE(.., "name")gives the export, not the module (src/jsc/bindings/InternalModuleRegistry.h:62); ten plain roads are rewritten to match (Bun.sql, SQL errors,util.inspectfrom native code, the timers'promisify.custom, the inspector, fake timers).src/bun_alloc/MimallocArena.rs:76), every global object (about +80),import.meta(+8),process(+8), each module mock (about +48), each covered file under--coverage(+24,src/sourcemap_jsc/CodeCoverage.rs:634).bun test.bun testthe runner's own load of a test file, which has no importer, keeps its key while a mock's factory has not settled. Three lines of this branch inkeyOfImportWhileModuleMocksRun()(src/jsc/bindings/BunPlugin.cpp). By intent.src/runtime/jsc_hooks.rs:780,:789,:894,test_runner/Execution.rs:652,:1233); a call at every failed test and more work at the end of a run (cli/test_command.rs:2205,:3619); one native call before each test file (src/jsc/VirtualMachine.rs:6198); one more for each function thatexpector a snapshot prints (test_runner/pretty_format.rs:520,:1431,dom_format.rs:176);toEqual(),toMatchObject(),Bun.deepEquals(),assert.deepStrictEqual()test for compiled objects (src/jsc/bindings/bindings.cpp:703); bookkeeping ofmock.module(),vi.mock(),jest.mock()that runs on data which only a compiled run reads (BunPlugin.cpp:528,:1724); howtest.extend()reads the parameter of a callback (test_runner/test_context_parameter.rs:43).--parallel: a worker that waits for its next file sleeps instead of polling (cli/test/parallel/runner.rs:713); the two messages between coordinator and worker have another shape (Coordinator.rs:750,Worker.rs:379,runner.rs:646); a worker reads an environment variable behind each file (runner.rs:608).--timingsreads a new key,"workers", and a plain--update-timingsdrops it (cli/test/Timings.rs:37,:129).--isolate=modulesand the environment files ask one more question (cli/test_command.rs:2734).Native functions and the library.
Bun.serve,Response,Blob,expect;src/codegen/bindgen-emit.ts:832,src/jsc/bindgen.rs:1374,:1437) test a flag before they return: 3 to 8 instructions a call in plain Bun, measured (Blob.text+3.0 of 1,777;Blob.slice+6 of 926;Blob.type+8.0 of 342, where the test behind the call takes the thunk's tail call away); about 55 KB of thunks. In a release build the flag is false unless compiled code runs, and a native function that answers outside its declared type answers as before (run:bun <file>, a plain executable). A build that asserts (debug,cargo test, ASan, or assertions with canary; CI publishes none of them) starts with the flag set: there such an answer throws a TypeError, and a value cached outside its type panics. Measured on Linux x64, a build ofmainagainst two of the working tree; only the +8.0 is laid to the test by the disassembly, the other two are differences of whole builds. Corrected: the messages said "a load and a branch". Corrected: "no released build has it" holds only as long as no release-safe canary is published: the switch isEnvironment::CI_ASSERT(src/bun_core/env.rs:49).main's:src/js/node/worker_threads.ts:771,node/events.ts:271,internal/streams/utils.ts:207,end-of-stream.ts:147,internal/net/symbols.ts:25,internal/streams/load_all.ts(new),internal-for-testing.ts:235,:917,builtins/CommonJS.ts:27,:270,builtins/ProcessObjectInternals.ts:706,builtins/BunBuiltinNames.h. No answer changes by reading; names and lines in stack traces do. 12 other changed library files,internal/streams/readable.tsamong them, come out byte for byte asmain's frombun buildwith the shipped definitions: the one thing that was run.console.logandBun.inspectask one more question of native constructors (src/jsc/ConsoleObject.rs:2104). Threads that read a large file or write an executable are named "Bun Parallel" (src/bun_core/util.rs:2787).buncarries: 38 more records of removable features, the first text of 16 library modules for a testing option (417 KB before compression,src/codegen/builtins-manifest.ts:275), natives for testing alone in release builds (src/jsc/declared_results_testing.rs,src/runtime/api/crash_handler_jsc.rs:98), one more internal module.src/crash_handler/lib.rs:1692,:1702);build_id()reads two more words (build_id.rs:80); a Rust panic asks of up to 20 addresses whether each lies in compiled code.For who builds Bun. The link of every released binary goes through
scripts/build/trim/*, which changes: symbols of size zero give no edges (elf-graph.ts:167), weak aliases no longer force the fallback on Windows (linker-coff.ts:220,coff-graph.ts:105), inputs outside the build folder are linked through symbolic links (linker-elf.ts:69,:104). The build fails on= (0, function () {})insrc/js, and where astatic const HashTableValue X[]undersrc/jsc/bindingsis described to nobody (src/codegen/builtins/collect-webcore-tables.ts:673).scripts/build/flags.ts:93,rust.ts:603: flags of two configurations that nothing ships.binary-expectations.ts:374,scripts/rust-test.ts:58,rust-miri.ts:42: CI's checks.Cargo.toml,Cargo.lock: the new cratesrc/sort_by_number, linked throughbun_sema.What it no longer pays. 38 hunks in
src/jscput a test of a flag in line wheremaincalls into the engine's ahead-of-time run time: at every load of a built-in module, every property read by name from Rust, every walk ofconsole.logover properties, every strict deep comparison, the making ofprocess.Response.json(),.bytes(),blob.json()and Postgres inBun.sqllose hooks of the previous compiler (src/runtime/webcore/Response.rs:821,Body.rs:1949,Blob.rs:2456,src/sql_jsc/postgres/). Since4954cae0Bun.jest()ismain's function again.State
src/aot,src/sema,src/bundler/aot,src/js/ahead-of-time,src/codegen/builtins,docs/compilerandtest/aotare this branch's, whole.scripts/build/deps/webkit.tsnames42067fce, the engine PR as it is merged.ce10cc29(63 minutes, no red file). The head1bbb52e2changes the pin's line and the limit of one test case and nothing else; its run is the first with the released engine. Each commit's message lists what is known to be wrong. The way there: Atc367dfe5two files were red and nothing else:test/aot/compiler/names-compared-as-text.test.tson ten lanes (the test reads the engine's C++; the zip of the build holds noaot-sources, because no tarball of the engine's release holds its C++) andvendor/elysia/test/response/stream.test.tsunder ASAN (the test races a timer of 5 ms, and a sanitizer's build, which takes the system's allocator on this branch, loses it).d0482105put main's text back where this branch had changed Bun without--aot, and turned nine more checks red by it: tests of this branch that still pinned the old behavior, places where the compiled side did not follow, one assertion of the compiler under ASAN, one lint.9fb4aa95mends eight of them and turned one sweep of the library red on the lane ofcargo test;eee70d05mends that: there the two files were red and nothing else, the first on nine lanes; the case on Linux arm64 that its message foretold red was green.0a0d53cdended at 34 red files: 31 on the x64 ASAN lane alone by one assertion, elysia's stream test there as before,names-compared-as-texton ten lanes, andtest/aot/test-runs-in-folders-3.test.tson three (Debian 13 x64, arm64, ASAN). The last is one stored test run: the fix that calls a mock's factory afterimport()stood on an older change of the working tree that did not come with it, so two loads in one turn of a module whose mock's factory throws came in the other order than underbun test. The files on the ASAN lane are nearly every one that builds a program: the compiler of the pinned engine left exceptions unlooked at while it works out the texts of regular expressions.39c6f344pins the engine'se8d13866, which looks at them, and brought a red of its own on the ASAN lane: two stored test runs oftest/aot/test-runs-in-folders-1.test.ts, by the line inBunPlugin.cppthat is named below.186ac75apinsa6c0a9a0, takes the fix on factories back and puts that line back.186ac75aended with three red files:names-compared-as-texton nine lanes, elysia's stream test under ASAN, andtest/aot/compiler/independence.test.tson Alpine arm64;test-runs-in-folders-1and-3were green on every lane. The third is a race in the compiler that the engine'se8d13866brought: it ended now and then without a message, at build time: in 7 of the 23 tries ofindependence.test.tsanddeterminism.test.tsat39c6f344, on 5 lanes, where the retries passed and hid all but one; in 4 of 18 at186ac75awhen they were last counted; in none of 18 at0a0d53cd.89e8b687pins the engine's461dc671, which fixes it: with 30 microseconds of sleep laid where the race is, 38 of 48 runs end so, and 0 of 48 with the fix; whether it is the only cause is not known. The engine PR's body has the whole. Since that pin the two files ran 26 times on eight lanes, in the builds at89e8b687andba47224d, and passed each time at the first try; at39c6f344and186ac75athe compiler exited in 11 of 43 tries. The build atbb7d7922ended with three red files and nothing else:names-compared-as-texton nine lanes,test/aot/ci-lists.test.tson three (new with that commit, ours;61b3e18fmends it) and elysia's stream test under ASAN. At241642deci-listswas green again, andtest/aot/a-require-that-goes-through-the-host-keeps-no-module-object.test.tswas red on x64 ASAN;c3231d2amends it. The build atbcb9df46has one red file more, ours:test/aot/runtime-use-of-built-in-code.test.ts, one case of twelve, on Debian 13 x64 and arm64: for a program that requires the other built-in modules the report namesglobalThis,processandBunas escaped, where the test expects none. It came with the engine's42121540, by its fix ofBuffer.concat([], null): the part of a call of a native function of the host that fits no row of the table goes out of sight, with its receiver and its arguments, and such calls stand in the library's own code. No answer of a program changes by it. What a user can see:--trimkeeps more. The global object goes out of sight for 13 more built-in modules, all that load node:net; forconsole.log(typeof require("net"))the report of--explain=trimgrows from 7,004 to 8,497 lines, all that hangs off those three. The bytes under--trimwere not measured. A two-line node:http server shows nothing (the same report, the same size), because its report was open before. The build atbc4394e9ran to its end with three red files:names-compared-as-text, elysia's stream test under ASAN and this one; no test thatbc4394e9brought is among them.9d2ddacffixes it by its pin: 12 pass; a build ofconsole.log(typeof require("net"))with--explain=trimprints 7,551 lines on the engine'se8d187c4, 8,488 onbd8bd487and 7,551 here (all that the build prints). The same pin turns two other files red, no answer wrong, too much is kept:test/aot/runtime-use-prototypes.test.ts(25 pass on the last pin; 24 pass and 1 fails) andtest/aot/asking-about-a-class-keeps-none-of-its-code.test.ts(14 pass; 12 pass and 2 fail): the engine'se75efb3bhas the engine's half of a fix for a crash (below), that half lets go of too much, and it alone turns the two red: found after the engine's push, so the front end's half is held back. What a user sees: for a program that stores the prototype object of a built-in class as the prototype of a function of its own, the report namesglobalThis,processandBunas escaped; for one that asks whether an object made over such a prototype object is an instance of the class, 13 lines of the report say what blocks--trimwhere 3 did. The bytes under--trimwere not measured. The build at9d2ddacfshowed a third file red by the same half, which nobody had foretold:test/aot/subclasses-of-collections-stay-tracked.test.ts(5 pass, 1 fails:Map.prototype.setescapes where a subclass of Map overridesset). The engine'sdacd2d81takes that half back, and900ca033pins it: 25, 14 and 6 cases pass. New in the build at9d2ddacf, under ASAN alone:test/js/bun/http/serve-body-leak.test.ts, one case of 15 (a body streamed and echoed over HTTP/2): the memory between start and peak read 262 to 278 MiB at four tries on one machine where the bound is 256; the five builds before read 49 to 125. Nothing that9d2ddacfor its pin changes lies on that road, by reading. Eleven runs of one ASAN build of this branch's text read 24 to 254 MiB, in two heaps, and pass: the number hangs on the run. Not fixed at900ca033. Atd08cc055the case oftest/aot/compiler/names-compared-as-text.test.tsthat read the engine's C++ is deleted with all that it alone needed,scripts/build/ci.ts's 14 lines among it, which is main's text again: CI never had the engine's C++. The case on the front end's Rust stays. Withbf18bbfcthe engine takes out the 13 lines by which a build with AddressSanitizer took the system's allocator;test/harness.tsandtest/internal/address-sanitizer-sees-wtf-allocations.test.tsare main's again. Ten rounds on two ASAN builds of one tree:vendor/elysia/test/response/stream.test.tspasses 0 of 10 with the lines and 7 of 10 without, and a build of main wavers alike;serve-body-leakpasses 10 of 10 on both. At4954cae0: Six of the twelve test files that this branch had added under test/internal go (-552 lines). They tested tools of the build on made-up inputs, or read sources by regular expressions, and no program's answer hangs on them: source-lints/stand-ins-without-the-compiler.test.ts (a reading of Bun's C++ by regular expressions; what it looked for is a compile error in a build with ENABLE_AOT=0); builtins-table/native-loads.test.ts, tables-in-cpp.test.ts and values-of-properties.test.ts (parts of the generator of the table of built-ins, which runs over the real table in every build); builtins-table/a-method-that-a-schema-gains-needs-no-other-prebuilt-engine.test.ts and the-table-that-a-prebuilt-engine-was-linked-with-is-not-compared.test.ts (two of four whole runs of that generator against a changed copy of an engine's files). What stays of the twelve: that a prebuilt engine with another position for a built-in is refused and one with this table accepted, with their helper; the two checks that refuse an unsound row written by hand; and thatSymbolandBigIntare constructors, which asks Windows' linker. No test ran after the six went.--aot. This PR is to change nothing thatbun <file>,bun test,bun buildand the rest do without--aot. Readings of the whole diff with that question found places where it did.d0482105took most of them back (21 files are main's text again and leave the diff; in a dozen more the lines that plain Bun runs are main's again);eee70d05takes back more:bun:ffiis main's text; flags and options that begin withaotin another case are passed over as on main; an executable built without--aotis handed every word of its command line;--update-timingswrites main's columns; two declarations inbun-types; the object of a CommonJS module is as large as it was; only the threads of a build get a stack for signals; two tables of the compiler's run time lie no longer in every Bun;39c6f344: one test that only compiled code needs (src/bun_core/debug.rs) is made only where the process has compiled code. It did the same to a second one, insrc/jsc/bindings/BunPlugin.cpp. That was wrong: two stored test runs, in which a mock's factory never settles while the next test file loads, were red on the ASAN lane on their side without--aot;186ac75aputs that line back as it was at0a0d53cd.9d2ddacf: the loop ofBun__deepMatch(expect().toMatchObject()and its kin) is main's again, word for word (found by the review on this PR), and a folder given torequire.resolve(name, { paths })is borrowed, not copied, where the process has no compiled program (no answer changes; by reading, no time was measured).bc4394e9: the chainable methods of a timer hand on what Rust answered, as on main, andcreateRequire()asks first whether the process has compiled modules; with the engine'sbd8bd487the generic road ofObject.assign()asks the target's own bit first andString.prototype.replace()with a function copies a replacement of 8-bit characters by the 8-bit road again (no answer changes; by reading, no time was measured).bcb9df46: four roads of every process (a resolution through the plugins' table, each resolved import, each load of a module of code, each lookup of a file in an executable built without--aot) ask first whether the process has compiled modules; and with the engine's42121540one answer goes back to main's,const m = new Map(); const it = m.keys(); Array.from(it); m.set("a", 1); it.next(), with what eight files of the engine cost a program that is not compiled. What is still otherwise has a section of its own below. The list that stood here, and stands in the commits' messages up to4954cae0under "What plain Bun still does otherwise than main", had ten items; it was offered as all, and it was not: a reading of every hunk finds 1,592 that a run without--aotpasses through. Corrected: until now this text, and the messages ofd0482105,eee70d05,0a0d53cd,39c6f344,186ac75a,89e8b687,ba47224dandbb7d7922, named two differences that the branch does not have: a shorter road forconsole.logof one string, andBun.inspectof whattoJSONgives asglobalThis. Both print as on main:src/jsc/ConsoleObject.rsdiffers from main by nine lines, which ask a question that only a process with compiled code answers with yes. The two changes were made in a working tree and are in no pushed commit. The same messages said of the threads ofbun test --parallelwhat is true of the threads named here.ce10cc29, with the engine's3c42aae2: three test files that were red in CI at4954cae0, no answer wrong:test/aot/no-leak-records-at-exit.test.tsunder ASAN (leak records that 13 lines of bmalloc had hidden until the engine'sbf18bbfc; 2 pass and 4 fail, 6 pass with the engine's change),test/aot/builtins/differential.test.ts(1 case of 6,875: the test called a helper that only compiled code can run) andtest/aot/mocks-are-hoisted-above-the-imports.test.ts(a known difference that4954cae0had ended was still listed). And the question that plainbun checkasked while its workers run, above. Corrected: the messages of900ca033,d08cc055and4954cae0said of some test files that they fail because the machine on which they are run before a push lacks packages or files. That was a hole in our own list: 23 of the 1,044 test files belowtest/aotran blind there. It holds the whole oftest/and the packages now. Corrected too: where those messages count test files that pass there, they count 39 of the 1,044 that run no case on that machine's build, which is not trimmable, not hardened and no sanitizer's: "ran no case" is not "passed". For the head the 27 test files that need a trimmablebunwere run on a second build of the same sources: 1,346 cases pass, none fails, none is skipped. The 7 on hardened executables ran on no build of these sources before the push.4954cae0, with the engine'sbf18bbfc, each seen red on a build ofd08cc055withdacd2d81and asbun <file>on a build of the head's text (38 new stored programs). New withd08cc055: nine right programs stopped: a program that findsaddEventListeneranddispatchEventby walking the keys of a prototype, calls them without a receiver and handsEventTarget.prototypeon in one of its lines (19 programs of another hand: 11 answer and 8 stop at900ca033, 2 and 17 atd08cc055, 11 and 8 at the head). Silent: fake timers in an executable (0 0for1 0; one executable did not end); a module mock behind twoexport *(both=undefined,kept=10forboth=file,kept=10);(E.B as any) = 9on an enum in the form that tsc emits: acase E.B:kept the old number;[import.meta.path].map(Bun.jest)answered a number. Loud: exit 134 on an async generator that relays a sync one and is resumed by a throw that nobody writes;spyOn(holder, 0, "get")on an object of compiled code threw.bun test --aotpointed at another line thanbun testfor athrowover lines. Aconstof a file read in a loop of a function of the file no longer stays in every turn (new with the engine's42121540).d08cc055, on the same preview of the engine: new with900ca033, no answer wrong, too much was kept, red in CI on three lanes: a program that printsperformanceand names no event kept all that the global object leads to (test/aot/parallel/host-objects/performance-printed.js:ImageFunctionsEmitted <= 8, but the build counted 3306).performanceinheritsEventTarget.prototype, and since900ca033such a holder counted as handed on wherever it was an operand of a call; it takes the method out only at a built-in that hands out what lies under keys. No bound moves. 58 stored programs: 56 pass and 2 fail on a build of900ca033, 58 pass on a build of the head's text. Of the 21 programs of another hand the same nine still stop by name.900ca033, with the engine'sdacd2d81, each seen red on a build ofe75efb3bwith9d2ddacfand asbun <file>on a build of the head's text (57 stored programs: 43 fail and 14 pass before, 57 pass here). Most forms, not all: a right program ended with exit 134 where one of the two methods that take the global object for their target comes out without a receiver:addEventListener("ping", event => { console.log(typeof event.target.process.finalization); }); const { dispatchEvent: send } = new EventTarget(); send(new Event("ping"));; soEventTarget.prototype.dispatchEvent.call(undefined, event), the method bound to nothing, read off a signal, under a constant or a name that is written as a string anywhere in the program, behind a comma, handed out byReflect.get()or a describer, or called throughsuperwherethisis nothing (30 stored programs: 22 fail and 8 pass before; 21 more of another hand, each built alone: 21 end with exit 134 before, 12 answer asbun <file>here and 9 still end so: all of the kinds thatdocs/compiler/compatibility.mdxdeclares since this commit: a name that is put together while the program runs or comes out offor .. in,Object.keys()or thenameof a function, and the method found without any name, out ofObject.values()of the prototype;void dispatchEventonce anywhere keeps all that the global object leads to). Silent: a directevalin a method of a class whose text is evaluated more than once told private names apart by their spelling alone:function make() { return class { #m() {} has(o) { return eval("#m in o"); } }; }, asked of an instance of the class that another call ofmake()gave, answered true for false (13 stored programs: 10 fail and 3 pass before). Silent: a key added behinddelete made[4294967295]by the number took the deleted key's place in the order of the keys. The build of a right generator stopped with "the walk is over and an edge is open". A build could hang on a program that lays a built-in function where the engine calls it: more forms (7 stored programs: 4 fail before, by their limits of time). The text of a message: a call that fails in the value of a field of a class was quoted at the constructor (In 'callee()'forIn 'callee(a, b)'; found by the review on this PR). node:http in an executable:http.OutgoingMessage.call(res)on the response of a request that is being answered threw out of the library or lost the body, ashttp.ServerResponse.call(res, req)did before9d2ddacf.9d2ddacf, with the engine'se75efb3b, each seen red on a build without the change and asbun <file>on a build of the head's text (18 stored programs: 12 fail and 6 pass, the controls and those of changes that move no answer, on a build ofbd8bd487; 18 pass here). Silent: a wrong value was stored: a whole number that is boxed as a fraction, stored into a list of whole numbers behind a comparison that says that the number is whole:const parsed = JSON.parse("[0.5,1,2,3,4]"); parsed.push("text"); function fill(list, value) { switch (value) { case 3: for (let i = 0; i < list.length; i++) list[i] = value; } return list; } const list = fill([9, 9, 9, 9, 9, 9, 9].slice(), parsed[3]);: a loop read 3 out of the list;list.join()andJSON.stringify(list)said 0,list.indexOf(3)-1,list.includes(3)false; so behind(x | 0) === x. On the engine's branch sincea1cb2d06, by the history of the line that makes such a store. With--aot-harden=proof-checksalone: the false alarm that ended a build with exit 134 (reported by a user of the last commits). A build could hang on a program that lays a built-in function where the engine calls it (46 small programs, 15 s given: 22 builds do not end before, all 46 build here; one of them builds slower than before: 0.99 s and 544 MB for 0.21 s and 123 MB). In an executable built with--aot,require.resolve(name, { paths: [module.path] })with a relative name made at run time answered a path under the working directory where a file of that name lay there; so for a package asked for from a folder of the executable. node:http in an executable:http.ServerResponse.call(res, req)on the response of a request that is being answered threw out of the library or lost the body; what a request class of the program's is handed, passed on as the options ofnew http.ServerResponse(req, kept), left the client without an answer. A closure that a directevalin a computed key of a class makes, and that reads a private method which stands behind the key, threw a TypeError wherebun <file>answers.docs/compiler/compatibility.mdxsays that a directevalin a computed key or theextendsclause of a class is strict code, and which built-ins have no frame of their own in a stack trace.bc4394e9, with the engine'sbd8bd487, each seen red on a build of42121540withbcb9df46and asbun <file>on a build of the head's text (28 stored programs: 23 fail and the 5 controls pass before, 28 pass here). A right program crashed: a function that stands in a computed key of a class, or in the expression of a decorator, calls a private method of that class in a loop while the class is still being defined (12 stored programs). A directevalin theextendsclause of a class was offered the private names of that class:falsefor a SyntaxError, and with a static private method a right program crashed (4 stored programs). Silent:enum Level { Warn = Base.Warn, Error }whereBaseis an enum of another file:warning: diskforerror: disk(reported by a user of the last commits). Exit 134: an index laid on a Map, a Set, a Date, a RegExp, an Error or a Promise, then a walk over its values:const cache = new Map([["a", 1]]); cache[0] = "x"; cache[1] = { v: 5 }; for (const value of Object.values(cache)) kinds += typeof value;(5 stored programs; 13 more built alone: 6 exit 134 before, 13 answer alike here). Exit 134: one form that was left of the crash on a read in front of a copy by a spread: a class that is never made bears the names of the literals (reported by a user of the last commits). New with42121540, loud, no wrong answer: the build of a program with a class that has private fields, a method of which is called in a loop, printedAOT: TypeTest, operand 0: OperandTypeMismatch ..and compiled the loop in its slower form. node:http in an executable, silent: the body of a request that nobody has read was dumped though the program says underreq._readableState.resumeScheduledthat a resume is scheduled;nullorundefinedthat a handler lays over the abort function onreq.socketwas handed on wherebun <file>lays the function anew. In an executable built with--aot, a file above the entry point's folder that climbs withpath.join(import.meta.dirname, "../../..", name)past everything the executable holds no longer gets a same-named file that lies beside the entry point's folder;/$bunfs//root/xand/$bunfs/./root/xname what/$bunfs/root/xnames. Found by the review on this PR, with a patch: in an executable built with--no-compile-function-sourcethe name of a callee in a message lost the round brackets that end an optional chain (o?.W is not a functionfor(o?.W) is not a function).bcb9df46, with the engine's42121540, each seen red on a build ofe8d187c4withc3231d2aand asbun <file>on a build of the head's text. A right program crashed: a function that reads a variable of its file is called, in a loop of another function, before the variable is filled:function go(turns, record) { let sum = 0; for (let turn = 0; turn < turns; turn++) { JSON.parse("1"); sum += record.read(turn); } return sum; } console.log(go(Number("0"), null)); const held = { width: Number("5") }; function read(turn) { return held.width + turn; } console.log(go(Number("3"), { read }));(13 stored programs, 2 more built alone, 2 IR tests). Exit 134:Buffer.concat([], null)andBuffer.copyBytesFrom(new Uint8Array(0), null), which return a Buffer underbun <file>(5 stored programs: 4 fail and the control passes before; 24 more built alone: 6 exit 134 before, 24 answer alike here). Exit 134: the two forms that were left of the crash on a copy by a spread (reported by a user of the last commits). node:http in an executable:stream.Stream = Mineorstream.Duplex = Minein front ofrequire("node:http")went on with Bun's own classes in silence: the load throws a TypeError that says what to do instead, anddocs/compiler/compatibility.mdxhas the row;stream.Readable = undefined; require("node:http")threw at the load wherebun <file>loads. New with this branch, found by the review on this PR: in the metafile of a build with--aotthe import of an HTML page had another kind than without.c3231d2a, with the engine'se8d187c4, each seen red on a build of7016427awith61b3e18fand asbun <file>on a build of the head's text; all three old. A right program ended with exit 134 where an event hands out the global object:addEventListener("ping", e => console.log(typeof e.target.process.finalization)); dispatchEvent(new Event("ping"))("process.finalization was left out of the executable, because nothing seemed to use it"); socurrentTarget,srcElement,composedPath(), an object withhandleEvent, and a worker'sonmessage: the load, which decides what goes into the executable, did not read the rows that say which built-ins hand out the global object (12 stored programs: 9 fail and the 3 controls pass before, 12 pass here). Silent:const k = {}, v = new Float64Array(1); v[Symbol.iterator] = function* () { yield* [[k, 0.5]]; }; new WeakMap(v).get(k) + 1answered NaN for 1.5 (2 stored programs). Exit 134:function T() {} new T().constructor.a = 1(2 stored programs, 2 more built alone; reported by a user of the last commits). One test that was red on x64 ASAN at241642de, because its inner run passedbun test's five seconds, gets the time.61b3e18f, with the engine's7016427a, seen red on a build of779e86aawithbb7d7922and asbun <file>on a build of the head's text. A right, everyday program ended with exit 134: a read of a field in front of a copy by a spread, where the objects that come differ in their fields:const k = []; function f(o) { if (!o.a) return; k.push({ ...o }); } f({ a: 1 }); f({ a: "x", b: 3 });; so a reducer that returns early on oneaction.typeand copies the action behind it. 9 stored programs: 8 fail and the control passes before, 9 pass here. Reported by a user of the last commits.bb7d7922had turnedtest/aot/ci-lists.test.tsred (four new test files were in neither of CI's lists): they are entered. Three test files that were green only at a later try get the time or the memory that they lacked. The list of what plain Bun does otherwise is corrected (above).241642de, a commit of the repository's formatting bot, stands on it: one test file laid otherwise.bb7d7922, with the engine's779e86aa, each seen red on a build of461dc671with89e8b687and asbun <file>on a build of the head's text. A right, everyday program ended with exit 134: a spread over an object whose membersnullandundefinedcame by stores,const o: Record<string, number | null | undefined> = {}; o.a = null; o.b = undefined; ({ z: 1, ...o, a: 0.5 }). The build stopped onconst K = "kk"; class A { [K] = 1; } const { [K]: v } = new A();. node:http in an executable, silent: what an own accessor_httpMessageonreq.socketthrows behind the end of a response was swallowed (stored null | okforescaped: made up | ok). 9 stored programs: the 6 witnesses fail before and pass here, the 3 controls pass on both. Two notes of the review; the programs that tell their answers run in eight test files for four.ba47224d, on the same preview of the engine: new with the engine's714174b0,bun compilestopped withinternal compiler error (Load): the program enters functions of Bun's library that the load has left out: Promise of PromiseConstructoron a program that writesconstructoroutside a class:function constructor() {}as the whole program,{ constructor() { return 1; } },Object.keys({ constructor: 1 }), a JSON file that holds the text, an HTML import whose page names an asset so. Three changes of the front end go with that commit of the engine and had not come with it. 5 stored programs: 5 fail at89e8b687, 5 pass here. Reported by a user of the last commits.89e8b687, with the engine's461dc671: the race in the compiler that is named above;while (map.size) map.delete(map.keys().next().value)on a Map or a Set whose type the compiler does not know took time that grew quadratically with the size (2 stored programs: the one that bounds the growth fails on a build of the last commit and passes here). Old, silent, node:http in an executable:res.write(new String(..))whosetoStringends the response, throws, or destroys the response or the connection amid the call answeredfalsewherebun <file>throws the RangeError, gives ERR_STREAM_WRITE_AFTER_END or answerstrue(7 stored programs: 4 fail without the line and pass with it, 3 pass on both). Tests: a tool of the engine's tests that is ended by a signal now says who ended it;determinism.test.tscompared nothing in one of its halves since November 4 (its call oftestaot factslacked a word, the tool printed "bad argument" and exited 0, and three copies of that line were held against each other): it compares facts now, and a tool that refuses its arguments fails the test that started it.39c6f344, with the engine'se8d13866: new with the engine'sf87bb065, the key of an object literal and the name of a function did not count as names that the program writes:for (const key of Object.keys({ eval: true, label: "made" })) { const found = holder[key]; if (typeof found === "function") found(text); }, with the global object forholder, threw an EvalError wherebun <file>answers (5 stored programs: 1 passes, 4 fail | 5 pass); afor awaitat the top level of a module over an async generator went on in the module's context ofAsyncLocalStorage(26 stored programs: 11 pass, 15 fail | 26 pass).0a0d53cd, with the engine's1607a183, each run on a build of the last commit and on a build of this one:bun compileended with an abort where subclasses of one class are held in the fields of an object, one is picked at run time and made withnew, and one has a member that nothing calls (const v = { k: K, l: L }; new (i & 1 ? v.k : v.l)(); 25 stored programs fail | pass); an enum whose first member is a constant from outside it, inside a namespace or a function, counted on from a wrong value in silence (100 undefined NaNfor100 101 102), and so an enum that is declared again and names a member of the earlier declaration; underbun test --aotthe factory ofvi.mock(),vi.doMock()ormock.module()ran inside theimport()of the mocked module, wherebun testcalls it in a later job (TAKEN BACK at186ac75a, see above); what a program lays on a request of node:http while its body arrives was passed over; two builds grew quadratically (many calls of a member ofrequire, many directevals), one could hang (a function made out of a text; a regular expression with a redefined property handed on byapply()), one never ended (classes that a statement which never runs would join into a ring). Faster, the answers the same:await it.next(), andfor awaitover an object whose method makes the async generator, where the piece arrives later.eee70d05, with the engine'sc563e0de: of two test files underbun test --aotthat each mock one module partly by spreading the real one, the second lost what its mock adds (new withd0482105; a family of stored test runs: 3 pass, 4 fail | 7 pass);for awaitover an object whose method makes an async generator callsnext()and awaits in its own context, as the language says (5 stored programs fail | pass; their known-failure mark is gone); underbun test --aota test that lays a class of its own in the place ofMaporSetcould not load node:stream; an executable built with--aotsays nothing of apreloadin a bunfig.toml that nobody named, and no longer ends with exit 1 where--confignames such a file. Taken back: the labels of aswitchare heard as every text that the program writes again (case "eval":threw an EvalError where=== "eval"answered).d0482105, with the engine'sf87bb065, each run on a build of the last pushed pair and on a build of this one (140 stored programs: 43 pass, 96 fail, 1 is skipped | 139 pass, the same one is skipped; 38 projects of stored test runs: 37 pass, 31 fail | 68 pass):bun test --aotover two test files that each mock one module partly by spreading the real one crashed;AsyncLocalStorage: a store entered behindmock.module()ornew Bun.ModuleGraph()was seen by the next callback, underbun test --aotby the next test, the consumer of an async generator did not always go on in Bun's context, and under--isolatea context that a test entered was seen by the next test (all silent); node:http: withcork,writeanduncorkcalled by hand what one response held back was sent by theuncork()of another (silent), and a status message that is an object is checked at every response; nine families of module mocks underbun test --aot; a loader of the program that laysmodule.exportsitself; a function assigned to a name in parentheses lost its name (silent); a name such as "constructor" that comes as a key of constant data, a label or a function's name is spelled by the program (an EvalError before);bun:osout of a list or a variable; names of built-in modules in an imported.jsonfile.c367dfe5, with the engine's2e069424, each run on a build of the last pushed pair and on a build of this one, every program alone (51 stored programs: 38 answered otherwise, 6 ended the process, the builds of 3 did not end, 4 were right | all 51 are right): the key of afor .. inover a list was a number, not a text, in acatchorfinallyaround the loop or behind ayield, andlist[key] = keystored 0 for -0; a module that exportsthenand is awaited, or is an entry point, got a namespace without the exports that nobody else reads; node:http asked what a program lays on the prototypes otherwise than Bun's other text of the library does (all silent);superin a method laid over a member ofprocessended the process; the build of a program that hands a regular expression with a redefined property of its own toreplace()ormatch(), called as written, did not end; a computed import with a written type left.sqland.jsoncfiles out, and TS100089 named a file that the executable held; a computed path written in an installed package left that package's data files out.10965b95, with the engine's2ca46e05: where a call of a built-in is refused for one operand, code of the program that another operand runs in front of the refusal runs (silent, old); a build could hang on a program that makes a function out of a text; a name out of a list, a record or a template of constants is spelled by the program (an EvalError before). The four stored programs on Workers that were red on both Windows lanes are green sincec61532e5.c61532e5, each with the program that showed it in the commit's message: with the engine'safaf7209, a loop that stores into a list of integers and reads the same list again in the same turn could get the element from before the store (silent); an import that only dropped decorators name was loaded, a decorated abstract or declared field was never initialized, a.cjsfile that nobody requires was taken for CommonJS (all silent, old);import("./" + name, { with: { type: "json" } }),createRequire()with a written folder and"@scope/" + nameembed their.jsonfiles again, and the build warns of the data files that a computed import leaves out (TS100089); the build warns where it loses sight of a Worker ofnode:worker_threads.d08cc055and not fixed atce10cc29: a program that finds the two methods by walkingperformanceitself (both(performance),Object.getPrototypeOf(performance),performance.__proto__, through a constructor) and calls them without a receiver ends with exit 134: 8 programs of another hand answer at900ca033and stop since.void dispatchEventonce anywhere keeps all that the global object leads to.ce10cc29, each seen on a build of the head's text, each program built alone and held againstbun <file>. The build does not end in 40 s: a type alias that grows where it names itself,type F<T> = { inner: T } & { or?: F<{ inner: T }> }(6 programs). Type errors thattscdoes not report stop the build: TS2322 atkeyof (typeof rows)[number]over rows out of a .js file, TS7006 in a literal of typeCrypto, TS2416 in subclasses ofBlobandHeaders(4 programs).bb7d7922, each run on a build of a pushed pair or of the head's text. New with this branch: in an executablenative.dlopenofbun:ffiis an accessor where plain Bun has a data member (stored as a known failure); one script that checks byBun.build({ check: true })and then compiles lowers on 32 MB of stack a thread, not 64. Silent: underbun test --aotthe factory ofvi.mock(),vi.doMock()ormock.module()runs inside theimport()of the mocked module, wherebun testruns it in a later job;module.exports = function () { return typeof this.children }; module.exports()answersundefinedin an executable, with or without--aot, wherebun <file>answersobject; under"useDefineForClassFields": falseone computed key that has to be folded makes all fields of the class defined; one data file imported under severaltypes, one of them by a name made at run time, can get what anothertypemade of it; node:http: code of the program that runs while a header's value is looked at comes too late, another object stored asreq._readableStatebetween a chunk and its event is not looked at, accessors above a server, a response or a request are called another number of times; streams and EventEmitter: an accessor underemit,write,pushor__proto__is called twice for once; underbun test --aota module that one test file mocks partly hands its state on to a later file that loads it without a mock. Underbun test --aot, on mocks, in no text of this PR until now (114 small projects in eight families on a build of the pinned engine, againstbun testby the same binary: 84 answer otherwise): old: the original of a mock that takes it (importOriginal, a mock without a factory, a spy) runs before the modules that its importer imports in front of it:import "./setup.js"; import { part } from "./part.js", where part.js reads what setup.js sets:mock real undefinedformock real true; old: once a mock of a CommonJS file has ended, a namespace that passes the file's names on byexport *shows the names that the mock left out, with the file's values, and the file's body runs:kept,leftOut 1 2forkept 10 undefined; a factory that imports another mocked module whose factory still waits gets the mock wherebun testhands it the file (mock 1 < mock 2formock 1 < real 2); a mock laid over a mock whose factory has not answered reaches the load that began under the old one; old:vi.doMock("./part.js", () => new Promise((resolve, reject) => { fail = reject; })); try { require("./part.js") } catch {}; fail(new RangeError("late")):bun testreports the error and the test fails, compiled it passes. node:http, old: a listener that a program writes intores._eventsis not heard (close/1forclose/1,close/2,close/3). Loud: old: beside a mock whose factory never answers, a load that does not lead to the mocked module waits for ever underbun test --aot((await import("./side.js")).ytimes out wherebun testprintsreal side); in a folder without a tsconfig.json a build stops with TS2339 atrequest.body.text()where the request has a written typeRequest; the text of a function that holds JSX, an experimental decorator orimport.meta.glob(), evaluated again, is a SyntaxError;require()of a conditional name with one written arm like "bun:os" and one unknown arm is refused;process instanceofa class whoseSymbol.hasInstancelists the keys ofholder.stdoutends the process ("was left out of the executable");require()of a made name where the file is a symbolic link beside a folder of the same name throws MODULE_NOT_FOUND; underbun test --aot97 more stored test runs on mocks fail wherebun testpasses (two factories that import each other's module,vi.importActual()inside a running factory, a mock over the mock of a preload, and others). Large, the answers right: a loop that fills a list up to a bound held in a variable asks for the room of the whole bound though it ends early (1,195 MB for 23 MB).b08c1788: a false build stop on a sloppy generator or async function in a CommonJS file that handsargumentson; a computedimport()orrequire()no longer embeds Markdown, HTML or shell files that its written name does not ask for; five tests that were red on the head before.649a797d, each with the program that showed it in the commit's message: computed imports and data files; an executable built again in one folder, which doubled or grew at every build; Workers started from files above the entry point; a test run with a preload that mocks a module, and mocks over a file whose first load threw; a UDP socket's handler that hands its chunk toJSON.stringify(); the kept text ofstatic readonly [key]; an arithmetic overflow in the checker.--trimremoves, 15 are on how builds grow with their programs, 51 are others. They aretest.failingwith the measured number and the bound in the name, orTodo:behind a stored program's bound, so each one runs every time and turns red the day it passes.test/aot/goals-not-met-yet.mdlists them all;bun test/aot/tools/list-goals-not-met-yet.tswrites that page from the marks in the tree.bun test --aot(test/aot/mock-file-url.test.ts,test/aot/unmock-while-loading.test.ts); the fourth is thenameof the error for a file whose import does not parse.test.todoand never run. 5 stored programs are skipped on one platform.