-
Notifications
You must be signed in to change notification settings - Fork 5k
module loader: mark has_loaded before transpiler-cache early return #35482
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -223,6 +223,42 @@ describe("transpiler cache", () => { | |
| expect(b.stdout == "production 5"); | ||
| expect(newCacheCount()).toBe(0); | ||
| }); | ||
| test("cache hit on the entry point does not change loader selection for unknown-extension imports", () => { | ||
| // The runtime-transpiler-cache early return used to skip marking the VM's | ||
| // `has_loaded` flag, so on a warm run (>= MINIMUM_CACHE_SIZE entry file | ||
| // served from cache) an ESM `import x from "./file.c"` fell into the | ||
| // "potentially the main module" loader fallback and tried to parse the | ||
| // C source as TSX instead of using the file loader. | ||
| writeFileSync(join(temp_dir, "impl.c"), "#include <stdio.h>\nint main() { return 0; }\n"); | ||
| // Entry file large enough to be cached, importing an unknown-extension file. | ||
| const code = | ||
| 'import src from "./impl.c";\nconsole.log(typeof src === "string" && src.endsWith("impl.c") ? "file-loader-ok" : src);\n'; | ||
| writeFileSync(join(temp_dir, "entry.ts"), code + "//" + Buffer.alloc(50 * 1024, "x").toString() + "\n"); | ||
|
|
||
| const run = (label: string) => { | ||
| const result = Bun.spawnSync({ | ||
| cmd: [bunExe(), "entry.ts"], | ||
| cwd: temp_dir, | ||
| env, | ||
| }); | ||
| const stderr = result.stderr.toString(); | ||
| const stdout = result.stdout.toString().trim(); | ||
| if (!result.success) throw new Error(`${label}: ${stderr}\n${stdout}`); | ||
| return { stdout, stderr }; | ||
| }; | ||
|
|
||
| const cold = run("cold run"); | ||
| expect(cold.stdout).toBe("file-loader-ok"); | ||
| expect(existsSync(cache_dir)).toBeTrue(); | ||
| expect(newCacheCount()).toBe(1); | ||
|
|
||
| // On the warm run the entry point is restored from cache; the `.c` import | ||
| // must still go through the file loader. | ||
| const warm = run("warm run (cache hit)"); | ||
| expect(warm.stderr).not.toContain("Unexpected #include"); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 This assertion can never fail: if the warm run printed Extended reasoning...What the issue isThe assertion Why it can't failThe local if (!result.success) throw new Error(`${label}: ${stderr}\n${stdout}`);
return { stdout, stderr };So the only way execution reaches the Step-by-step proof
Therefore in every reachable state the assertion passes; it contributes nothing to the test's ability to catch the regression. ImpactLow. The test as a whole is not vacuous — on regression it still fails via the throw inside How to fixTwo options:
|
||
| expect(warm.stdout).toBe("file-loader-ok"); | ||
| expect(newCacheCount()).toBe(0); | ||
| }); | ||
| test("--feature flag invalidates cache", () => { | ||
| // feature() can only appear in an if/ternary, so wrap it | ||
| const code = `import { feature } from "bun:bundle";\nif (feature("SUPER_SECRET")) console.log("enabled"); else console.log("disabled");`; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 The trailing clause of this comment ("leaving it unset on a cache hit made a second run of
bun entry.tsparseimport x from \"./a.c\"as JS") is past-tense bug history — REVIEW.md's Comments section says that belongs in the PR description, not the source. Keep the first five lines (the placement invariant and the cross-file dependency ontranspile_file'ssynchronous_loaderblock) and drop just the trailing repro clause. Same applies to the 5-line block at test/cli/run/transpiler-cache.test.ts:227-231, which narrates the historical failure the test name already states as an invariant.Extended reasoning...
What the finding is
Two of the new comments in this PR mix durable invariant documentation with past-tense bug narration, which REVIEW.md's Comments section explicitly separates:
Location 1 —
src/runtime/jsc_hooks.rs:2655-2661The first ~5 lines are exactly what the rule wants kept: they state the placement invariant ("before any of the early
Ok(...)returns below") and the non-local, cross-file dependency ("has_loadedis what flips the loader fallback for unknown extensions —transpile_file'ssynchronous_loaderblock — fromTsxtoFile"). That's durable, non-obvious, and prevents a future refactor from re-hoisting the assignment.The trailing clause, however, is bug history:
That's a past-tense description of the pre-fix failure mode — precisely what the PR description's Repro and Cause sections already record verbatim. Dropping it loses nothing; the invariant half of the comment already tells a future reader everything they need to keep the assignment where it is.
Location 2 —
test/cli/run/transpiler-cache.test.ts:227-231The 5-line block:
is entirely past-tense narration of what was broken. The test name — "cache hit on the entry point does not change loader selection for unknown-extension imports" — already states the invariant, and the two surviving inline comments (line 233 "Entry file large enough to be cached, importing an unknown-extension file" and the pre-warm-run comment "the
.cimport must still go through the file loader") already cover the non-obvious setup constraints (why 50 KiB of filler, why a.cimport, why two runs).Addressing the counter-argument ("local convention allows explanatory comments")
One reading holds that this file's convention permits multi-line explanatory blocks — the
ignores files under the minimum cache sizetest explains the 4 KiB threshold, the--feature flagtest annotates each stage, and therejects cached module recordstest documents the on-disk cache-entry byte layout. But each of those is present-tense reference material (a threshold's rationale, a serialized layout, per-stage expected state) — durable content a reader needs to modify the test. None of them narrates a historical bug in the past tense. The distinction REVIEW.md draws is not "long comments vs short comments" but "invariants vs bug history", and the new block falls on the bug-history side ("used to skip", "fell into", "tried to parse").The counter-argument also notes there is no GitHub issue URL to substitute. That's fine — the rule doesn't require one; it says regression tests get at most the issue URL. With no issue, the setup comments already present at line 233 and before the warm run are sufficient, or the block can be rephrased as a one-line present-tense invariant if the author prefers.
Suggested edit
jsc_hooks.rs — end the comment after "…from
TsxtoFile.":transpiler-cache.test.ts — drop lines 227-231; the test name plus the two remaining inline comments already carry the durable content.
Severity
Nit. This is comment hygiene enforced by an explicit repo rule, but merging as-is causes no behavioral, correctness, or maintenance failure — it's a one-clause trim in the source and a block deletion in the test.