Add react + rescript example - #493
tom-sherman wants to merge 2 commits into
Conversation
| "web-vitals": "^1.0.1" | ||
| }, | ||
| "scripts": { | ||
| "build": "rescript", |
There was a problem hiding this comment.
this is using rescript, which launches a Node.js process and not a Bun.js runtime process
|
Thank you for the example. Unfortunately, this example is running in Node.js and not in Bun.
if your script runs node, you're still using node. Just, with if you pass I'm closing this, but if |
|
Noted! 99% of ReScript is a native binary (that's why it's so fast) it's just that the binary is bootstrapped using a node script: https://github.com/rescript-lang/rescript-compiler/blob/e30c806e184762703be6b59ba4a9d1a611756382/rescript I've raised an issue here: rescript-lang/rescript#5530 |
…-parsing every module on a second VM A cold run with module.enableCompileCache() used to spend its exit re-parsing every missed module on a dedicated "BunCompileCache" thread with its own JSC VM and eagerly compiling every function in it. The SourceProvider now gets the live top-level UnlinkedCodeBlock from JSC right after CodeCache generates it (oven-sh/WebKit#493) and keeps a Weak to it on the VM's client data. The block is encoded once with encodeCodeBlock(): either right before Bun's GC drops unlinked function code (JSC__VM__runGC → deleteAllUnlinkedCodeBlocks), or at persist time (exit, module.flushCompileCache(), a --watch reload), whichever comes first. That writes the top-level block plus the code blocks of every function that ran up to that point; functions that never ran stay lazy stubs. Nothing is rooted: a block the GC already collected is simply not cached this run. The file format is unchanged: one CachedBytecode, decoded by the same path as before. Workers encode what they compiled in their own on_exit and hand the bytes to the entry; the main thread's exit writes them. Entries carry an id so bytecode for a module that was rewritten and re-required in the same process is dropped in favor of the new version's. --watch reloads now go through the JS thread when the cache is enabled, like they already did with --watch-kill-signal listeners, so the modules get encoded before execve; the grace timer still forces the reload if the JS thread is stuck, persisting only what was already encoded. Pins WebKit to the PR preview build until #493 lands.
…-parsing every module on a second VM A cold run with module.enableCompileCache() used to spend its exit re-parsing every missed module on a dedicated "BunCompileCache" thread with its own JSC VM and eagerly compiling every function in it. The SourceProvider now gets the live top-level UnlinkedCodeBlock from JSC right after CodeCache generates it (oven-sh/WebKit#493) and keeps a Weak to it on the VM's client data. The block is encoded once with encodeCodeBlock(): either right before Bun's GC drops unlinked function code (JSC__VM__runGC → deleteAllUnlinkedCodeBlocks), or at persist time (exit, module.flushCompileCache(), a --watch reload), whichever comes first. That writes the top-level block plus the code blocks of every function that ran up to that point; functions that never ran stay lazy stubs. Nothing is rooted: a block the GC already collected is simply not cached this run. The file format is unchanged: one CachedBytecode, decoded by the same path as before. Workers encode what they compiled in their own on_exit and hand the bytes to the entry; the main thread's exit writes them. Entries carry an id so bytecode for a module that was rewritten and re-required in the same process is dropped in favor of the new version's. --watch reloads now go through the JS thread when the cache is enabled, like they already did with --watch-kill-signal listeners, so the modules get encoded before execve; the grace timer still forces the reload if the JS thread is stuck, persisting only what was already encoded. Pins WebKit to the PR preview build until #493 lands.
### Problem - Bun's WebKit pin `aea1f010b6` is 412 upstream commits behind `8c4fd56347`, 90 of them in JavaScriptCore, WTF or bmalloc. oven-sh/WebKit#503 merges that range into the fork. - Upstream removed the `@newPromiseCapability` private builtin (`38027ff0ec`). Six call sites in Bun's bundled modules use it, so `node:events`, `node:util`, `node:dgram` and the HTTP server fail to load (`Private symbol not found: newPromiseCapability`). - #40263 and oven-sh/WebKit#501 were a parallel attempt at `55d9d9007f`, one WebCore-only commit ahead. Both are closed in favor of this pair. ### Fix - oven-sh/WebKit#503 is merged. `WEBKIT_VERSION` is `cb61607f1a4bae79d7701965062634dee9efb349`, its merge commit on the fork's main (release `autobuild-cb61607f1a4bae79d7701965062634dee9efb349`, 42 tarballs). That commit is the preview build this PR was tested against (`d2654c3b`) plus oven-sh/WebKit `a0a80b2276` (an optional depth bound on `recursivelyGenerateUnlinkedCodeBlockForProgram/ForModuleProgram`). - The six call sites create their promise with `$newPromise()` and settle it with `$resolvePromise` / `$rejectPromise`, or the `...WithFirstResolvingFunctionCallCheck` variants where a second settle is possible. `builtins.d.ts` follows. - `EncodeURIComponent.cpp` includes `<wtf/HexNumber.h>` itself (upstream `314133b7a6` no longer does). - Verified: `test/js/bun/jsc/webkit-upgrade-8c4fd56347.test.ts` pins four JavaScript-visible engine changes that fail at the current pin. The events, util and dgram tests cover the ported settlement paths. ### Background - Bun links a prebuilt JavaScriptCore from oven-sh/WebKit releases. `scripts/build/deps/webkit.ts` names the release tag. - Built-in modules (`src/js/`) go through JavaScriptCore's builtin compiler. A `$name` call becomes the private name `@name`, which has to exist in the engine. - `$newPromise` creates a pending promise. `$resolvePromise` / `$rejectPromise` settle it and require it to be pending. The `...WithFirstResolvingFunctionCallCheck` variants ignore calls after the first, like a Promise executor's functions. <details><summary>Notes</summary> - Duplicate resolution: oven-sh/WebKit#501 and #503 have the same structure (main at `62f427b86f`, then #488's head `d0fae3b3c9`, then upstream/main) and the same `CachedTypes.cpp` resolution (the two files differ in comments and an unused alias). WTF and bmalloc are identical. The only upstream difference is WebCore's `55d9d9007f` (`MediaElementAudioSourceNode` use-after-free), which the JSCOnly port does not compile. #503 was kept because this PR's CI run was green (Build #104490). The test file `webkit-upgrade-8c4fd56347.test.ts` is carried over from #40263. - oven-sh/WebKit#488 (upstream `baf4a9a7ec0b`) stopped merging after the fork's bytecode cache rework (#490, #493, #494, #497). The per-commit review of the upstream range (API and ABI changes, behavior changes, performance) and the conflict resolutions are in oven-sh/WebKit#503. The new conflict in this round is `CachedTypes.cpp`: the fork's new code block record layout against upstream moving the global-only fields (`features`, `lineCount`, source URL directives) to `UnlinkedGlobalCodeBlock` and deleting `m_jumpTargets`. - The ported call sites (the changes of #40054, carried over): `node:events` (`once`), `node:util` (`aborted`), `node:dgram` (`Symbol.asyncDispose`) and the HTTP server (CONNECT, Upgrade, the per-request completion promise). `builtins.d.ts` declares `$newPromise`, `$resolvePromiseWithFirstResolvingFunctionCallCheck` and `$rejectPromiseWithFirstResolvingFunctionCallCheck` and drops `$newPromiseCapability`. `@newPromise` is a bytecode intrinsic and `@resolvePromise` / `@rejectPromise` are link-time constants, so they exist in every engine build. `util.aborted` registers and unregisters its `FinalizationRegistry` entry with the same token (the promise). - Behavior changes in the upstream range that are visible from JavaScript: `Promise.try` follows the updated spec (`PromiseResolve` instead of `NewPromiseCapability`); the module map no longer caches fetch failures, so a second `import()` of a specifier whose load failed re-runs Bun's module loader instead of rejecting with the cached error; `Uint8Array.prototype.setFromBase64` on a zero-length target returns `{ read: 0, written: 0 }` without validating the input; `WebAssembly.Module.imports()/exports()` descriptors drop the non-standard `type` field; re-exported imported Wasm globals and tags keep object identity; a DFG `++`/`--` on an `int32` that overflows with an unused result now deoptimizes instead of wrapping (`7711916200`). The first, third, fourth and last of these are pinned by `test/js/bun/jsc/webkit-upgrade-8c4fd56347.test.ts`. - Performance changes of note: `SymbolTableEntry` no longer allocates a `WatchpointSet` per watched variable until the DFG watches it (`cea233cede`); `Object.assign` with several sources clones the first one through `objectCloneFast` (`96ca975b2a`); `JSON.parse` allocates arrays once at their final size; `TypedArray.prototype.sort()` without a comparator uses a radix sort for 2/4/8-byte element types; `Map`/`Set` `forEach` is inlined in the DFG and FTL; `RegExp` cells shrink from 96 to 80 bytes; `UnlinkedFunctionCodeBlock` shrinks from 216 to 192 bytes. - `src/jsc/bindings/NodeVMSyntheticModule.cpp` calls `SymbolTable::set(NoLockingNecessary, ...)`. After `cea233cede` only the locked overload exists. `NoLockingNecessary` converts to a `ConcurrentJSLocker`, so the call compiles unchanged. - The upstream change to Linux thread scheduling (per-QOS `sched_setattr` on every WTF thread, `SCHED_BATCH` compiler threads on hosts with 4 or fewer cores) is gated off for Bun in the fork: Bun's threads keep inheriting the process scheduling attributes. - Suites run on a local debug + ASAN build against the merged WebKit (`bun run build:local`): `test/js/bun/jsc`, `bun/jsc-stress` (116/116), `node/events`, `node/util`, `node/dgram`, `node/vm`, `node/module`, `bun/resolve`, `node/worker_threads`, `bun/wasm`, `web/url`, `web/atomics`, `node/http/node-http-connect`, `node/async_hooks`, `node/string_decoder`, `bundler/bundler_compile`, `bundler/bun-build-api`: 3,548 pass. The failures are 5 s timeouts under debug + ASAN, this machine's IPv6 multicast `ENODEV`, and one test that fails the same way at the current pin. `bun build --bytecode` output from that build loads and runs. A debug + ASAN build against the `autobuild-preview-pr-503-311eab61` prebuilt runs `test/js/bun/jsc/webkit-upgrade-8c4fd56347.test.ts`, `test/js/bun/jsc/webkit-upgrade-3722912f.test.ts`, `node/events/event-emitter.test.ts` and `node/util/test-aborted.test.ts`: 106 pass. - Every push to oven-sh/WebKit#503 produces a new preview tag (`autobuild-preview-pr-503-<first 8 of the head sha>`), and this PR's `WEBKIT_VERSION` follows it. CI lanes that fetch the prebuilt fail on the download until that tag's Actions run has published the release. - Rebase over #40201: Bun main moved its pin to the fork's `c148a12dd82b` and calls the bytecode APIs that release added (`EncoderStringTable`, persistent payloads). The `311eab61` preview predates them, so the branch could not rebase until oven-sh/WebKit#503 merged the fork's main (head `d2654c3b`, 0 commits behind). The rebase itself conflicted only on the `WEBKIT_VERSION` line. A debug + ASAN build against `autobuild-preview-pr-503-d2654c3b` passes `webkit-upgrade-8c4fd56347.test.ts`, `node/events/event-emitter.test.ts`, `node/util/test-aborted.test.ts`, `node/dgram` (except the IPv6 multicast `ENODEV` of this machine), `node/http/node-http-connect.test.ts`, `web/atomics`, `web/url`, `node/string_decoder` and `test/js/bun/jsc`. The compiled-executable bytecode paths of #40201 work against it: the aliasing run keeps 12 MB of instruction streams out of anonymous memory and 45 internal modules load from embedded bytecode. The failures on this machine are the DOMJIT hot loops and two `bun-build-compile` tests that exceed their timeouts under debug + ASAN (the compile alone takes 5 to 47 s here), and the nested `node-http-connect.node.mts` run that takes 5.0 s against a 5 s limit. - The `$newPromiseCapability` call in `src/node-fallbacks/events.js` (the browser polyfill, not a JSC builtin) is a pre-existing bug and was reported separately by #40054. </details> <!-- robobun:evidence:begin --> --- **no test proof** · iteration 2 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/js/node/dgram/node-dgram.test.js <!-- robobun:evidence:end -->
oven-sh#494 merged) 12-commit upstream delta, clean merge, no regressions found against the 1.4.1 merge's documented failure classes. bun-webkit bumped to 2e2aa2290f first (PR oven-sh#493), then bun to 1.4.2 (PR oven-sh#494), both via GitHub CI (local build container's sshd was unreachable). Real-device smoke + watch.test.ts + the execve/pthread_create repro all clean on 1.4.2; the intermittent SIGSEGV from the previous EAGAIN-fix verification did not reproduce this run, but it's inherently flaky (3/8 historically) so stays open, not closed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019hTeaNqnsU8Qqm1eacUy2g
Upstreaming my example from here: https://github.com/tom-sherman/bun-react-rescript