Propagate JS exceptions instead of clearing them where the caller is fallible
What does this PR do?
A number of native paths cleared a pending JavaScript exception and carried on with a default value even though the function above them already returns JsResult / checks a ThrowScope. The clear defeated an existing error path, so the error was silently lost. Each site now leaves the exception pending and returns early. This is the "trivial" subset of a wider audit of exception-clearing in the runtime: only sites where no signature or FFI contract had to change.
User-visible effects:
-
mock(fn)/spyOn: a throwingname/lengthgetter on the implementation throws instead of being ignored. -
mockFn.withImplementation(impl, cb): a sync throw fromcbpropagates (was swallowed). Separately, the sync-return path now restores the queuedmockImplementationOncetail correctly (it restored the head, dropping queued once-impls). -
mock.module: a factory export getter that throws fails the import / themock.modulecall instead of exportingundefined; an already-imported namespace is left untouched (exports are read before any is overridden). -
Native
EventEmitterprototype methods (e.g.Object.getPrototypeOf(process).on) called on a foreignthiswhose_eventsgetter throws now propagate that error instead ofERR_INVALID_THIS. -
ESM import of a CJS module whose
module.exportscan't be enumerated (throwingownKeys) rejects instead of yielding an empty namespace. -
expect(fn).toThrow(/re/): if matching the message throws, that error propagates instead of being used as the match result. -
Bun.inspect: a throwingtoJSON(URLSearchParams/Headers/FormData) or ProxygetPrototypeOftrap propagates, matchingutil.inspect. -
ServerWebSocket.cork(cb): a returned Error is returned, not thrown. -
generateKeyPair('rsa-pss', { hash }): an error while emitting DEP0154 propagates. -
Module._findPath: an error thrown by aBun.pluginonResolvehook propagates (asrequire.resolvealready does) instead of the path being skipped. -
setTimeout/setIntervalwith an out-of-range delay: an error thrown while emitting the warning propagates (as in Node). -
X509Certificatewith an empty subject/issuer DN:toLegacyObject()(and the.subject/.issuerinternals) returnundefined/{}like Node instead of throwing "Crypto operation failed" — the getters used to throw-and-swallow it,toLegacyObject()threw it.
Also propagated (OOM/termination-only, no repro): bun:test/object-loader export enumeration, native ReadableStream cancel/error, napi_create_*error, util.types.isError tag read, addEventListener(type, null) warning, Windows IPC socket payload attach.
Deliberately not in this PR: the static-table PropertyCallback builders (process.stdout & co., Module._cache) keep their current clear+report. Propagating from those needs the builders to stop entering JS (checked custom getters) rather than teaching JSC's own-property lookup to throw; that is a follow-up.
How did you verify your code works?
Added tests next to existing coverage for each user-visible case; each fails with USE_SYSTEM_BUN=1 and passes on the debug build. Ran the repros under BUN_JSC_validateExceptionChecks=1.