Repository navigation
Built-in modules: bind the engine's Date functions through @natives, not the Date found at first run - #47058
Built-in modules: bind the engine's Date functions through @natives, not the Date found at first run#47058robobun wants to merge 6 commits into
@natives, not the Date found at first run#47058Conversation
… `Date` found at first load node:v8, node:inspector, node:readline, the node:test mock timers and util.inspect read Date.now or a Date.prototype method from the global Date when the module first evaluates. A built-in module loads after user code. So a Date mock that is active at that moment stays inside the module after the test restores it. Each of the seven statements now binds a function that DatePrimordials.cpp makes from the static property table of JavaScriptCore's DateConstructor or DatePrototype. The copy has the native code and the intrinsic of the property, and a script cannot reach it.
|
Status: reproduced, fix pushed in this PR (#47058). Reproduced with the six-line script in the description. A plain function stands on the global As a test suite: CI for 6955356 is build #138236. It failed on tests that also fail on
|
|
No actionable comments were generated in the recent review. WalkthroughThe change adds native bindings for Date functions and uses them in Bun’s internal modules. The code generator checks for captured Date functions. New regression tests cover replaced Date APIs across module loading, runtime behavior, and AOT execution. ChangesDate primordial bindings
Merge Risk:
|
There was a problem hiding this comment.
Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.
Beyond the inline findings, I also checked: the uncurryThis(fn)(value) -> fn.$call(value) rewrite in inspect.js and DateNowFn.$call(Date) -> bare native in node-primordials.js are behavior-preserving (uncurryThis is Function.prototype.call.bind, and the native Date.now ignores this); the new C++ path in DatePrimordials.cpp only calls Identifier::fromString and JSFunction::create, neither of which enters JS, so there is no missing exception check; and a sweep of src/js for other module-scope Date.now / Date.prototype.* captures finds only _http_server.ts:123, which the PR says is removed by a separate change. The reading of JSC's static property hash table is a new mechanism in this tree, so a maintainer's sign-off on that design is still worthwhile.
Extended reasoning...
The change replaces seven module-load captures of Date functions in five built-in JS modules with four new $cpp bindings that construct fresh JSFunctions from JSC's static property tables for DateConstructor and DatePrototype, and adds five spawned-fixture tests that also run under Node when available. It touches no auth, crypto, or input-parsing surface; the only native code is a small table lookup guarded by RELEASE_ASSERTs. Two inline findings were already posted (duplicated test runner block, and the readline time-sentinel colliding with a mocked clock at 0). The decisive facts against approving are the two posted findings and that this is the first use of staticPropHashTable in src/jsc, which the author flags as needing maintainer acceptance.
runInBunAndNode() in test/harness.ts runs a fixture in a fresh process for each argument, under Bun and under Node.js when it is installed. The five new tests call it, so the runner is written once.
There was a problem hiding this comment.
Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.
The runInBunAndNode helper in 56e6bda addresses the duplication I noted on the first push. Beyond the inline note on that helper, I also checked that every remaining use of the rebound identifiers (DateNow in interface.js, readline.js, v8.ts and inspector.ts; DatePrototypeGetTime at mock_timers.ts:548 and :724; the three Date functions at inspect.js:1410-1412) is called with no receiver or via .$call, so no uncurryThis-style call survives the switch, and that the js2native codegen includes DatePrimordials.h from the $cpp filename, so the new file needs no extra registration. The read of JavaScriptCore's static property table in DatePrimordials.cpp is the design choice a maintainer should still weigh.
Extended reasoning...
The change adds src/jsc/bindings/DatePrimordials.{h,cpp}, which builds fresh JSFunctions from JSC's DateConstructor/DatePrototype static hash tables, rebinds seven Date captures in five built-in JS modules to those functions, and adds a shared test harness helper plus five spawned-fixture tests. It touches no security-sensitive surface. Not approved because the static-table read is a new mechanism with RELEASE_ASSERTs that fail at first load on a WebKit table change, which the author themselves flags as needing maintainer acceptance, and one inline nit is being posted.
It returned an empty stderr for a run that exits 0, so the `stderr: ""` that the five Date mock tests expect could not fail. The 13 fixture runs print nothing to stderr under Bun and under Node.js v26.3.0.
There was a problem hiding this comment.
Thanks, both earlier nits are addressed: the five fixtures now share runInBunAndNode, and it returns the real stderr of every run. This automated review ran on the latest push and found no bugs; since src/jsc/bindings/DatePrimordials.cpp is the first Bun binding to reify a function from a JSC-owned staticPropHashTable, a maintainer should still sign off on that mechanism.
What was reviewed:
createDateFunctionFromStaticTable: asserts the row exists and is a plain native (not Builtin/DOMJIT) before readingfunction()/intrinsic(); theJSFunction::createarguments matchreifyStaticProperty, and it runs once per module load, so the per-loadIdentifier::fromStringis off the hot path.- The seven JS sites:
DateNowis handed through as a bare function (readline calls it with no receiver, whichDate.nowignores); inspect.js calls the three prototype copies with.$call(value)on anisDate-checked value. - Sibling captures:
src/js/node/_http_server.ts:123is the only remainingconst DateNow = Date.nowin src/js and the PR names it as excluded. - Tests: the
toEqual(runs.map(...))shape pins stdout/stderr/exitCode to literals, so a differing run still fails; fixtures mock before the first require and assertcalls:0, reads:0.
Extended reasoning...
The change adds a 48-line C++ binding (DatePrimordials.cpp/.h) that builds fresh JSFunctions from JSC's DateConstructor/DatePrototype static property tables, swaps seven module-scope Date captures in five src/js modules to $cpp calls to it, adds a runInBunAndNode harness helper, and adds one spawned fixture test per module that also runs under Node 26.3.0+. It touches no auth, crypto, or input-parsing surface; the tamper-resistance direction is the point of the change. No CODEOWNERS entry covers the changed paths and both of my prior inline comments were addressed by commits 56e6bda and 9213a7b. Defer rather than approve because the PR introduces a new mechanism (first read of a JSC-owned staticPropHashTable, which the author also flags as needing maintainer acceptance) and the WebKit headers it includes (DatePrototype.h is not included anywhere else in src/) are not in this checkout, so I could not confirm the build locally.
Conflicts: main moved src/js/internal/repl/node-primordials.js and src/js/internal/util/inspect.js to TypeScript and dropped uncurryThis from inspect and node:v8. The seven Date bindings are the same statements in the new files.
…Date*` names Nine module-scope statements read `Date.now` or a member of `Date.prototype` when their module first runs, which is after user code. They now read `$DateNow`, `$DatePrototypeGetTime`, `$DatePrototypeToISOString` and `$DatePrototypeToString`. - `bun run`: a define turns each name into `@natives.<name>`, and `NativeGlobals` makes the function from the row of JavaScriptCore's own static table. - `bun build --compile --aot`: four rows of the table of built-ins bind the names to the engine's functions. - The bundler of the built-in modules rejects an uncalled `Date.now` and any `Date.prototype` in `src/js`. This replaces the `$cpp` bindings of `DatePrimordials.cpp`, which the compiler could not see through, and adds the two texts of `node:_http_server`.
…k shapes of a test run - `NativeGlobals` finds the row from the name itself (`DateNow` is `now` of `DateConstructor`, `DatePrototypeGetTime` is `getTime` of `DatePrototype`), so `dateFunctions` is the only list for `bun run`. - The build check says what to write and which members have no row. - `mock-fn.test.js`: a spy on `Date` in one test file, then a second file that prints and times dates. - `globals.test.js`: no built-in module reads the global `Date` when it first runs. - The two programs of `test/aot` also replace `Date.prototype.toString` and cover `util.inspect`, readline and the mock timers. The case of a compiled test run has a file of its own.
Date found at first load@natives, not the Date found at first run
There was a problem hiding this comment.
I reviewed the rewritten version of this PR (the two commits since my last review) and found no bugs; the earlier points about the duplicated spawn block and the blanked stderr are addressed by runInBunAndNode in test/harness.ts. Because the mechanism is now a fallback in NativeGlobals::getOwnPropertySlot that derives a row from JSC's DateConstructor/DatePrototype static hash tables plus a new build-time assertNoCapturedDateFunction check, a maintainer's look at that design is still worthwhile.
What was reviewed:
createDateFunctioninsrc/jsc/bindings/ZigGlobalObject.cpp: name derivation (DateNow->now,DatePrototypeGetTime->getTime), theRELEASE_ASSERTs, and that a non-Dateunknown name still returns false as before; the@ nativesobject is reachable only from bundled builtins, so the asserts are not user-reachable.- Codegen:
$DateNowand friends map to__intrinsic__natives.*forbun run, are excluded from the compiler's syntax-use set, and are declared asbuiltin("Date.now")-style rows alongside the existingObject.getPrototypeOf/Math.minentries; the remainingDate.nowuses insrc/jsare all immediate calls, so the new build check passes. - Tests: the per-module fixtures run under Bun and Node, assert exact
{stdout, stderr, exitCode}, andglobals.test.jscounts globalDatereads across every builtin module (Dateis not inglobalsToPrefix, so the oldconst DateNow = Date.nowcaptures would trip it). - One sweep item: the PR description still describes the earlier
DatePrimordials.cpp/$cpp(...)/ "4 more$lazyrows" design, which no longer exists in the diff.
Extended reasoning...
The change replaces seven module-scope captures of Date.now / Date.prototype methods in src/js with four new private intrinsics ($DateNow, $DatePrototypeGetTime, $DatePrototypeToISOString, $DatePrototypeToString) resolved lazily by a new fallback in NativeGlobals::getOwnPropertySlot that reads JSC's own DateConstructor/DatePrototype static property tables and creates a fresh JSFunction from the table row, plus a codegen assertion that rejects future captures and declarations in bun-syntax.builtins.ts and builtins.d.ts. It touches no auth, injection, or data-exposure surface; the hostile-userland concern it addresses (a mock active at a builtin module's first load) is the fix itself. The deciding facts for defer rather than approve: this is the first Bun read of a JSC-owned static table and the author explicitly asks a maintainer to accept it, the ahead-of-time compiler semantics of the new builtin("Date.now") rows live in vendored WebKit not present in this checkout, src/js/builtins.d.ts is covered by a CODEOWNERS entry, and the PR description still describes the superseded DatePrimordials.cpp design.
|
The description is current. I rewrote it for this version at 10:22 UTC, after the push and before this review was posted. It describes the |
Problem
Datemock that is on when they first run. After the restore,util.inspect(date)throwsTypeError: DatePrototypeGetTime.@call is not a function, and a keep-alivenode:httpserver dies withDateNow is not a function.const DateNow = Date.now(src/js/node/v8.ts:34). Built-in modules first run after user code. Node.js is not affected.util.inspectfirst runs at its first read, not whennode:utilloads. No user reported this: a fuzzer found it.Fix
$DateNowand three$DatePrototype*names.bun run: a define turns each name into@natives.<name>, a copy of JavaScriptCore's static table row, which a script cannot change.--aot: four rows of the built-ins table bind the names. A build check rejects new captures.test/js/bun/test/mock-fn.test.jsand ten more files (Notes). Self-reviewed: 27 concerns raised, 25 addressed.Background
@natives(bun test: Vitest compatibility #44833) is where built-in modules read globals that a script cannot patch.Date.nowfrom a compiled table row. A copy has the same native code and JIT intrinsic.$cppcopies per statement (the first version): the--aotcompiler cannot see their result.Downsides
@nativesnow also reads a JavaScriptCore-owned table, forDateonly. A changed row fails aRELEASE_ASSERTat first load.util.inspect(date): +15 instructions per call (+0.7%) in programs that read fewer than threeDate.prototypemembers..text: +1,536 B.Number.isNaN,Object.keysorJSON.stringifyreplaced around the firstutil.formatstill fail.Notes
Who hits it. A test run that puts a mock on
Date,Date.nowor a member ofDate.prototypewhile one of the six modules first runs.setSystemTime()andjest.useFakeTimers()do not. Each row isbun test ./a.test.ts ./b.test.ts, andb.test.tsmocks nothing. I ran the rows on the release binaries and on release builds ofmainb1305dd and of this branch.a.test.tsmainspyOn(Date.prototype, "toISOString")around a firstutil.format, thenmock.restore()jest.spyOn(globalThis, "Date"), thenjest.restoreAllMocks()spyOn(Date, "now")that no test restores, first use ofnode:v8On
mainthe failures are inb.test.ts:util.inspect({ createdAt: new Date(0) })throwsDatePrototypeGetTime.@call is not a function, orv8.GCProfilerreports no time. The third row is new onmain: since #44833 Bun restores a spy at the end of its test file, and the built-in modules stay shared between files.Short repro. Node.js v26.3.0 and Bun 1.4.0 print
1970-01-01T00:00:00.000Z. Bun 1.4.1 to 1.4.4 throwTypeError: undefined is not a function.mainthrowsTypeError: DatePrototypeGetTime.@call is not a function.Which moment decides for
util.inspect. It is the first run ofinternal/util/inspect. a5e30c9 (#39628) madeinspect,format,formatWithOptionsandstripVTControlCharacterslazy getters ofnode:util. The three statements are the same at the tags bun-v1.4.0 and bun-v1.4.1. Each row puts a plain function onglobalThis.Date, does the first use, restoresDateand callsutil.inspect(realDate).mainimport { format, inspect } from "node:util"(the import reads the getter)format("%s", 5)import util from "node:util"util.format("%s", 5)node:assertandnode:utilimportedassert.deepStrictEqual()console.log()of an object with a custom inspect functionimport util from "node:util"under the mock, first call after the restoreThe nine statements, and what Node.js does there.
mainsrc/js/node/v8.ts:34Date.nowv8.GCProfilerstartTime,endTimesrc/js/node/inspector.ts:12Date.nowtimestampofRuntime.consoleAPICalledsrc/js/internal/repl/node-primordials.ts:24Date.now, and the read of the globalDateon each call at:93DateNowprimordialsrc/js/internal/test_runner/mock_timers.ts:27Date.prototype.getTimemock.timers.enable({ now })DatePrototypeGetTimeprimordialsrc/js/internal/util/inspect.ts:59-61getTime,toISOString,toStringutil.inspect(date),util.formatsrc/js/node/_http_server.ts:123Date.nowand its second textsrc/js/ahead-of-time/node/_http_server.ts:228lib/_http_server.js:1229,lib/internal/timers.js:387in v26.3.0)The two
node:httpstatements are a stopgap. #46528 and #37974 replace them with the clock of the timers. Until then this PR keeps the clock they read today.How the two backings work.
src/codegen/replacements.tshas the listdateFunctions. A define turns$DateNowinto@natives.DateNow, as it turnsBlobinto@natives.Blob.NativeGlobals::getOwnPropertySlot(src/jsc/bindings/ZigGlobalObject.cpp) has one new arm behind a miss in Bun's own table. It takes the table from the name (DatePrototype...orDate...), finds the row withstaticPropHashTable->entry()and callsJSFunction::create(vm, globalObject, entry->functionLength(), name, entry->function(), ImplementationVisibility::Public, entry->intrinsic()). These are the arguments ofreifyStaticProperty()inLookup.h, and the same call that the function rows of@nativesalready make. The existingputDirectkeeps one function per realm.RELEASE_ASSERTs check what the accessors ofHashTableValueonlyASSERT: the row exists, it is a native function, and it is not a JS builtin or a DOMJIT function. So a name that JavaScriptCore does not have stops the first read. It does not leaveundefinedin a module.@nativesdo not reach the text that the--aotcompiler reads (src/codegen/builtins-manifest.ts:64). There the four names are rows ofsrc/codegen/builtins/host/bun-syntax.builtins.ts, next to$Arrayand$Promise.collect-syntax-uses.tscounts a name whose define reads@nativesas used, or the table step refuses the rows as unused.assertNoCapturedDateFunctionruns on the bundled output of each module and function, and on each second text. It rejects an uncalledDate.nowand anyDate.prototype. A call-timeDate.now()stays legal. With a capture put back into a first text or a second text, the bundle step fails with the message. It does not catchconst { now } = Date. The new test intest/js/bun/globals.test.jsdoes: it lists every module that reads the globalDatewhen it first runs.Dateonly.Array.prototype,Math,Object.prototypeandFunction.prototypehave no static table in the pinned WebKit, so it cannot serve the whole class. When engine constants land (internal/primordials: Node.js parity, generated and engine-backed #35567 with Node.js primordials: tamper-proof built-in references for builtin JS WebKit#341, which declare the same four names), four things go: the define loop ofdateFunctions,createDateFunctionwith its arm, the filter incollect-syntax-uses.ts, and the four lines ofbuiltins.d.ts. The statements, the tests, the compiled rows and the build check stay.Precedent.
REVIEW.mdasks built-in modules for "globals captured at module load". #45291 fixed the same first-load defect for two guards ofinspect. #40271 did it forMessagePort. ThenativeFunctionsof #44833 give built-in modules a copy ofsetTimeoutthat fake timers do not replace.Questions for a maintainer.
Date-only second source in@nativesacceptable until engine constants land? The other first-load captures are not in this PR, and a wider set of names in@nativeswaits for this answer. On fix(js): better error messages when makeSafe fails #16263 Jarred-Sumner wrote that JavaScriptCore should have private symbols for what built-in modules need. internal/primordials: Node.js parity, generated and engine-backed #35567 is that route.src/js/ahead-of-time,src/codegen/builtinsandtest/aot. Which lands first? If AOT: front end, test runs and tests for the rewritten compiler #47942 does, the compiled half of this PR is done again on it. AOT: front end, test runs and tests for the rewritten compiler #47942 also removes the loop inbundle-modules.tsthat holds the build check for second texts, so that call needs a new place.node:httpstatements stay until http: consolidate the open fetch, Bun.serve, node:http, node:http2 and HTTP/3 pull requests #46528?Measurements. Two release builds with LTO on the same machine, linux-x64:
mainb1305dd and this branch (6955356). Instructions are user-mode counts of the first thread from aperf_event_open(PERF_COUNT_HW_INSTRUCTIONS)program. A per-call number is the difference of a run of 2N and a run of N, divided by N, withBUN_JSC_useConcurrentJIT=0 BUN_JSC_useConcurrentGC=0 BUN_JSC_numberOfGCMarkers=1, median of 7 interleaved runs..text: +1,536 B,.rodata: +0 B.NativeGlobals::getOwnPropertySlot: 2,519 B -> 4,215 B (llvm-size,llvm-nm). The stripped binary has the same size (140,478,752 B).JS2NATIVE_COUNT: 327 onmain, 327 here. define keys: 152 -> 156.main3469, here 3469. When the program reads the four public functions first: 3469 and 3472 (four copies of 32 B, one arrow function less).NativeExecutable: +0 in every case.main/ here:GCProfilerstart and stop 1769.7 / 1769.6,console.debugunderRuntime.enable2497.1 / 2497.3, readline CR and LF chunk pair 3819.1 / 3823.7,mock.timers.enable({ now })andreset()24071 / 24137, one keep-alivenode:httprequest and response 279,956 / 279,298. The spread of one build is 0.2% to 1.2%.util.inspect(new Date(0)): median 2042.2 / 2057.2, mean 2035.4 / 2056.6, 40 interleaved runs, standard error 2.5. The difference follows the number of members that the publicDate.prototypehas made: the modules no longer read three of them. With three members read by the program first: 2038.7 / 2039.0. In the interpreter alone: 16701 / 16708.bun empty.js: 3,639,481 -> 3,633,880 instructions (spread 0.9%), heap cell counts identical.--compile --aotexecutable,main/ here (at 2c84d69, before the last commit, which changes only the first read of a name underbun run):util.inspect(date)2431.1 / 2433.3,GCProfiler1768.7 / 1753.1, readline 4510.4 / 4369.4, inspector 3962.0 / 3899.5, mock timers 34142 / 34364 (+0.65%),node:http82,762 / 82,864.test/aot/perf/compare-builds.tsat 2c84d69: 56 server rows (node, bun, hono, elysia, express, fastify, koa). One row,server bun json, was 2.2% slower in one run and 1.8% faster in the next: it costs one of two amounts (about 35,030 or 35,740 instructions) on both builds. No other row moved beyond the 2% threshold in both of its readings.test/aot/perf/plain-bun-costs.measure.tsat 2c84d69, 68 rows: -0.34% to +0.29%, start rows at most +0.13%.node:httpserver round (test/aot/servers/budgets-of-node.test.ts): 878 -> 878 accesses by name, 148 -> 148 runtime operations, 4,092,876 -> 4,092,876 code bytes.test/aot/tools/same-output.tsover 289 programs: in the programs that do not holdinternal/util/inspect, no function changes size. In one that holds it, compared by name (595 functions), only the module body ofinternal/util/inspectchanges: 4537 B -> 4522 B.aot_builtin_syntax.rs: 4 Date rows. Undescribed results: 2103 lines (main2103),native:Cpp: 19 (main19). With the$cppcopies of the first version the table listed four more, each asthe result of Call is anything.Date,Date.prototype), identical underbun runand--aot.globalThis.Date. The nine new tests of the eight plain test files fail on the release build ofmain.test/aot/run-parallel.tson the release build of this branch and fail on the release build ofmain.fs._toUnixTimestamp(-1)under aDate.nowspy that returns 5000:5with 1 call on both builds.perf,valgrindandbloatyare not installed.What stays. Each row is checked on this branch.
const X = <Builtin>[.prototype].<member>finds about 500 insrc/js, 72 of them ininspect.ts. Withnode:utilloaded, one member replaced around the firstutil.formatand restored:Number.isNaNmakesutil.inspect(date)throwRangeError: Invalid Date,Object.keysmakesutil.inspect({ a: 1 })print{},JSON.stringifymakes%jprint the mock's text. Bun 1.4.0 and Node.js are right in all three. internal/primordials: Node.js parity, generated and engine-backed #35567 works on the class. Stop reading user-mutable built-ins on the fs, child_process, dns, and CJS loader paths #40291 is about reads at call time, not these captures. This PR does not fix require('fs') evaluates Set prototype function #18890.process.nextTickis captured atsrc/js/internal/streams/destroy.ts:27,src/js/node/fs.ts:30,src/js/builtins/stdio.ts:23andsrc/js/ahead-of-time/node/_http_incoming.ts:58. Replaced while the first stream loads and restored,stream.destroy()emits no'close'. Bun 1.4.0 does the same.Date.now()lines (internal/http.ts:395,node/http2.ts,node/test.ts,node/fs.ts:1046,internal/inspector/cdp.ts) and about 20new Date()lines follow a mock while it is on. Node.js: 0 calls.test/aot/the-library-that-bun-holds-compiled-is-the-lowest-layer.test.tspins that the library sees aDate.nowspy at call time.console.log(date)calls a replacedtoJSON,valueOfandtoISOStringwhile they are on (3 calls, Node.js 0). Print Bun's snapshot format with theconsole.logformatter #46729 and the closed console: format Date from internal [[DateValue]], not JSON.stringify #35287 are about that path.spyOn()spy returnsundefinedthrough a kept reference, and a restoredvi.spyOn()spy calls the original. So the other captures still fail the spy-and-restore case.isDateand the other guards ofinspectcome from the writablenode:util/typesat first load. util.inspect: don't take the iterator guards from the writablenode:util/types#45291 left them.setSystemTime()ofbun:testthe nine sites follow the engine clock, as before.Other open PRs on these lines. #47942 (see the questions): a trial merge with its head 1bbb52e conflicts in
bundle-functions.ts,bundle-modules.tsandreplacements.ts, where it adds a build check of its own at the same place, and it deletes the key-order fixture that this PR edits. #46528 and #37974 delete the twonode:httpstatements. #35567 declares the same four names. #45274, #40291 and #36546 touch neighbouring lines.Tests.
test/js/node/v8/v8-module.test.ts,test/js/node/inspector/inspector.test.ts,test/js/node/readline/readline.node.test.ts,test/js/node/test_runner/node-test.test.ts,test/js/node/util/util.test.js,test/js/node/http/node-http.test.ts: one new test each. Each writes a fixture.runInBunAndNode(), new intest/harness.ts, runs it in a fresh process for each mock, under Bun and under Node.js 26.3.0 or later whennodeis installed. The fixture counts the calls of the mock and the reads of the globalDate.node:httpfixture setsresponse.sendDate = false. TheDateheader reads the clock anew for each response, which is a call-time read.test/js/bun/test/mock-fn.test.js: two rows. A spy onDatein one test file, then a second file that prints and times dates. Thejest.spyOn(globalThis, "Date")row also fails on the installed 1.4.3.test/js/bun/globals.test.js: no built-in module reads the globalDatewhen it first runs.mainlists 8 reads (_http_server,inspectorfour times,readline,v8, the mock timers). Node.js lists none.test/aot/a-module-that-first-runs-after-a-test-replaced-date-functions-uses-the-engines-own.test.ts: a test replacesgetTime,toISOStringandtoStringbeforeutil.inspectfirst runs. Plainbun testandbun test --debug-aotprint the same lines, with no call of a replacement.test/aot/parallel/library-modules/date-now-and-to-string-replaced-while-modules-first-run.cjsandglobal-date-replaced-while-modules-first-run.cjs: each holds its expected line and must print it underbun <file>and as a--compile --aotexecutable. Together they cover the four names innode:v8,node:http,util.inspect, readline and the mock timers.test/aot/parallel/library-modules/keys-of-built-in-objects-behind-modules-that-read-them.mjs: the stored orders ofDateandDate.prototypechange, because the modules no longer readnow,getTime,toISOStringandtoStringon the public objects.test/aottest file passes. Of 192 vendored Node.js tests (test/js/node/test/parallel/test-{util-inspect,util-format,runner-mock-timers,readline,v8-,inspector,repl}*andtest-http-*{keep*alive,timeout}*) 191 pass.test-http-client-timeout-option.js, which times a 1 ms timeout of the client, fails 2 of 5 runs on that build and passes on both release builds.test/internal/builtins-table,test/internal/source-lintsandtest/internal/second-texts-of-modules.test.tspass.test/aot/the-library-that-bun-holds-compiled-is-the-lowest-layer.test.tsfails here on the release build ofmainand on this branch in the same way: itsSEEN setTimeoutline isfalseunder plain Bun andtruein layers. ItsSEEN Date.now | 5,1line holds.Self-review. Of the 27 concerns, two are not done.
Date.nowrow in the compiled test run. With a write ofDate.nowandnode:v8the run of that one case took 3 min 20 s on the ASAN debug build, and the test file passed its limit of 270 s. The two executable programs pin$DateNowunder a replacedDate.nowand under a replaced globalDate. By hand, the compiled test run printed the same lines as plain Bun, with no call.The first version of this PR. Up to 9213a7b the seven statements of five modules called
$cpp("DatePrimordials.cpp", "Bun::createDateNow")and three more creators, which made the same copy from the same rows. On today'smainthe table step of the--aotcompiler refused the four bindings (runs native code nobody has classified), and the key-order fixture, which landed one day after that version's last CI run, failed. This version keeps the copy and the five tests, moves the copy into@natives, and addsnode:http.Credit. The four names are those of #35567. The mechanism is the
@nativesobject of #44833.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/http/node-http.test.ts