diff --git a/src/jsc/modules/BunJSCModule.h b/src/jsc/modules/BunJSCModule.h index 253567844d19..89943e9966b8 100644 --- a/src/jsc/modules/BunJSCModule.h +++ b/src/jsc/modules/BunJSCModule.h @@ -816,7 +816,7 @@ JSC_DEFINE_HOST_FUNCTION(functionGenerateHeapSnapshotForDebugging, } scope.releaseAssertNoException(); - return JSValue::encode(JSONParse(globalObject, WTF::move(jsonString))); + RELEASE_AND_RETURN(scope, JSValue::encode(JSONParse(globalObject, WTF::move(jsonString)))); } JSC_DEFINE_HOST_FUNCTION(functionSerialize, diff --git a/test/js/bun/jsc/bun-jsc.test.ts b/test/js/bun/jsc/bun-jsc.test.ts index 993aaf6ab4c4..3e73cb8b02d1 100644 --- a/test/js/bun/jsc/bun-jsc.test.ts +++ b/test/js/bun/jsc/bun-jsc.test.ts @@ -567,3 +567,26 @@ it("deserialize applies the same nesting depth limit to arrays as to objects", a const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); expect({ stdout, exitCode }).toEqual({ stdout: "rejected\n65\n", exitCode: 0 }); }); + +it("generateHeapSnapshotForDebugging survives BUN_JSC_validateExceptionChecks", async () => { + // JSONParse (LiteralParser::parseRecursively) can throw, so the enclosing + // throw scope must release before returning. With validateExceptionChecks + // enabled the process aborts when the scope goes unchecked; on release + // builds the option is a no-op and this just exercises the snapshot path. + await using proc = Bun.spawn({ + cmd: [ + bunExe(), + "-e", + `const snap = require("bun:jsc").generateHeapSnapshotForDebugging(); console.log("ok", typeof snap);`, + ], + env: { ...bunEnv, BUN_JSC_validateExceptionChecks: "1" }, + stdout: "pipe", + stderr: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + const uncheckedScopes = stderr + .split("\n") + .map(line => line.trim()) + .filter(line => line.startsWith("This scope can throw") || line.startsWith("But the exception was unchecked")); + expect({ stdout, uncheckedScopes, exitCode }).toEqual({ stdout: "ok object\n", uncheckedScopes: [], exitCode: 0 }); +});