diff --git a/src/bun_core/env_var.rs b/src/bun_core/env_var.rs index 9ca6d375d233..eb14e27b6a99 100644 --- a/src/bun_core/env_var.rs +++ b/src/bun_core/env_var.rs @@ -252,6 +252,7 @@ pub mod feature_flag { new_feature_flag!(pub BUN_FEATURE_FLAG_DISABLE_SOURCE_MAPS, "BUN_FEATURE_FLAG_DISABLE_SOURCE_MAPS", {}); new_feature_flag!(pub BUN_FEATURE_FLAG_DISABLE_SPAWNSYNC_FAST_PATH, "BUN_FEATURE_FLAG_DISABLE_SPAWNSYNC_FAST_PATH", {}); new_feature_flag!(pub BUN_FEATURE_FLAG_DISABLE_SQL_AUTO_PIPELINING, "BUN_FEATURE_FLAG_DISABLE_SQL_AUTO_PIPELINING", {}); + new_feature_flag!(pub BUN_FEATURE_FLAG_DISABLE_STANDALONE_MADVISE, "BUN_FEATURE_FLAG_DISABLE_STANDALONE_MADVISE", {}); new_feature_flag!(pub BUN_DISABLE_TRANSPILED_SOURCE_CODE_PREVIEW, "BUN_DISABLE_TRANSPILED_SOURCE_CODE_PREVIEW", {}); new_feature_flag!(pub BUN_FEATURE_FLAG_DISABLE_UV_FS_COPYFILE, "BUN_FEATURE_FLAG_DISABLE_UV_FS_COPYFILE", {}); new_feature_flag!(pub BUN_DUMP_STATE_ON_CRASH, "BUN_DUMP_STATE_ON_CRASH", {}); diff --git a/src/standalone_graph/StandaloneModuleGraph.rs b/src/standalone_graph/StandaloneModuleGraph.rs index 6f427e1e48da..20e2851fcfa8 100644 --- a/src/standalone_graph/StandaloneModuleGraph.rs +++ b/src/standalone_graph/StandaloneModuleGraph.rs @@ -2193,7 +2193,8 @@ impl StandaloneModuleGraph { /// The pages are clean file-backed COW, so any later read (lazy require, /// stack-trace source lookup) faults back in transparently from the /// executable on disk. Only applies when running as a compiled - /// standalone binary. + /// standalone binary; `BUN_FEATURE_FLAG_DISABLE_STANDALONE_MADVISE=1` + /// skips the hint. pub fn hint_source_pages_dont_need() { #[cfg(windows)] { @@ -2225,7 +2226,11 @@ impl StandaloneModuleGraph { #[cfg(any(target_os = "macos", target_os = "linux", target_os = "android"))] { - if len == 0 { + if len == 0 + || bun_core::env_var::feature_flag::BUN_FEATURE_FLAG_DISABLE_STANDALONE_MADVISE + .get() + .unwrap_or(false) + { return; } diff --git a/test/js/bun/compile/standalone-madvise-tla.test.ts b/test/js/bun/compile/standalone-madvise-tla.test.ts index c3f499286dc4..fe3cfd8017b5 100644 --- a/test/js/bun/compile/standalone-madvise-tla.test.ts +++ b/test/js/bun/compile/standalone-madvise-tla.test.ts @@ -2,6 +2,8 @@ // entrypoint has top-level await. loadEntryPoint() returns a promise without // blocking, so the call site at bun.js.zig:466 is hit synchronously before the // main event loop spins — TLA resolution happens later in that loop. +// BUN_FEATURE_FLAG_DISABLE_STANDALONE_MADVISE, read by the compiled binary at +// runtime, skips the hint. import { expect, test } from "bun:test"; import { bunEnv, bunExe, isDebug, isWindows, tempDir } from "harness"; import path from "node:path"; @@ -29,10 +31,22 @@ test.skipIf(isWindows || !isDebug)( expect(build.stderr.toString()).not.toContain("error:"); expect(build.exitCode).toBe(0); - { + // BUN_FEATURE_FLAG_DISABLE_STANDALONE_MADVISE is read by the compiled + // executable at runtime (the binary above was built without it); a falsy + // value leaves the hint enabled. With the flag set, the function returns + // before logging anything, so the only evidence is the missing line. + for (const [flag, hinted] of [ + [undefined, true], + ["0", true], + ["1", false], + ] as const) { await using proc = Bun.spawn({ cmd: [out], - env: { ...bunEnv, BUN_DEBUG_StandaloneModuleGraph: "1" }, + env: { + ...bunEnv, + BUN_DEBUG_StandaloneModuleGraph: "1", + BUN_FEATURE_FLAG_DISABLE_STANDALONE_MADVISE: flag, + }, stdout: "pipe", stderr: "pipe", }); @@ -42,7 +56,11 @@ test.skipIf(isWindows || !isDebug)( expect(stdout).toContain("after-await"); // Scoped loggers write to the debug-writer stream (stdout by default). // Either the success or failure variant proves the call site is reached. - expect(stdout).toContain("hintSourcePagesDontNeed:"); + if (hinted) { + expect(stdout).toContain("hintSourcePagesDontNeed:"); + } else { + expect(stdout).not.toContain("hintSourcePagesDontNeed:"); + } expect(stderr).toBe(""); expect(exitCode).toBe(0); }