Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions src/jsc/bindings/BunObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,9 +319,6 @@ static JSValue defaultBunSQLObject(VM& vm, JSObject* bunObject)
auto scope = DECLARE_THROW_SCOPE(vm);
auto* globalObject = defaultGlobalObject(bunObject->globalObject());
JSValue sqlValue = globalObject->internalModuleRegistry()->requireId(globalObject, vm, InternalModuleRegistry::BunSql);
#if BUN_DEBUG
if (scope.exception()) globalObject->reportUncaughtExceptionAtEventLoop(globalObject, scope.exception());
#endif
RETURN_IF_EXCEPTION(scope, {});
RELEASE_AND_RETURN(scope, sqlValue.getObject()->get(globalObject, vm.propertyNames->defaultKeyword));
}
Expand All @@ -331,9 +328,6 @@ static JSValue constructBunSQLObject(VM& vm, JSObject* bunObject)
auto scope = DECLARE_THROW_SCOPE(vm);
auto* globalObject = defaultGlobalObject(bunObject->globalObject());
JSValue sqlValue = globalObject->internalModuleRegistry()->requireId(globalObject, vm, InternalModuleRegistry::BunSql);
#if BUN_DEBUG
if (scope.exception()) globalObject->reportUncaughtExceptionAtEventLoop(globalObject, scope.exception());
#endif
RETURN_IF_EXCEPTION(scope, {});
auto clientData = WebCore::clientData(vm);
RELEASE_AND_RETURN(scope, sqlValue.getObject()->get(globalObject, clientData->builtinNames().SQLPublicName()));
Expand Down
14 changes: 10 additions & 4 deletions src/jsc/bindings/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5596,10 +5596,11 @@ static void JSC__JSValue__forEachPropertyImpl(JSC::EncodedJSValue JSValue0, JSC:
}

JSC::PropertySlot slot(object, PropertySlot::InternalMethodType::Get);
if (!object->getPropertySlot(globalObject, property, slot))
continue;
// Ignore exceptions from "Get" proxy traps.
bool hasProperty = object->getPropertySlot(globalObject, property, slot);
// Ignore exceptions from "Get" proxy traps and lazy property initializers.
CLEAR_IF_EXCEPTION(scope);
if (!hasProperty)
continue;

if ((slot.attributes() & PropertyAttribute::DontEnum) != 0) {
if (property == propertyNames->underscoreProto
Expand Down Expand Up @@ -5671,7 +5672,12 @@ static void JSC__JSValue__forEachPropertyImpl(JSC::EncodedJSValue JSValue0, JSC:
break;
if (iterating == globalObject)
break;
iterating = iterating->getPrototype(globalObject).getObject();
JSValue prototype = iterating->getPrototype(globalObject);
// Ignore exceptions from "getPrototypeOf" proxy traps.
CLEAR_IF_EXCEPTION(scope);
if (!prototype) [[unlikely]]
break;
iterating = prototype.getObject();
Comment thread
claude[bot] marked this conversation as resolved.
}
}

Expand Down
79 changes: 79 additions & 0 deletions test/js/bun/util/inspect.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,85 @@ describe("crash testing", () => {
}
});

describe("exceptions thrown while walking properties", () => {
// Before the walk cleared these exceptions the process crashed (or, in the
// second case, a debug build asserted), and the second case replaces a
// global, so each case runs in a subprocess.
async function run(fixture) {
await using proc = Bun.spawn({
cmd: [bunExe(), "-e", fixture],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
return { stdout, stderr, exitCode };
}

it.concurrent("a throwing Proxy trap in the prototype chain skips only that property", async () => {
const { stdout, stderr, exitCode } = await run(`
{
const proto = new Proxy(
{ a: 1, b: 2, c: 3 },
{
get(target, key, receiver) {
if (key === "a") throw new Error("boom");
return Reflect.get(target, key, receiver);
},
},
);
console.log(Bun.inspect(Object.create(proto)));
}
{
const proto = new Proxy(
{ a: 1 },
{
getPrototypeOf() {
throw new Error("boom");
},
},
);
console.log(Bun.inspect(Object.create(proto)));
}
`);
expect(stdout).toMatchInlineSnapshot(`
"{
b: 2,
c: 3,
}
{
a: 1,
}
"
`);
expect(stderr).toBe("");
expect(exitCode).toBe(0);
});

it.concurrent("a throwing lazy property initializer skips only that property", async () => {
// The builtin behind Bun.$ calls the global Symbol("cwd"), so making that
// one call throw makes Bun.$ (and nothing else on Bun) fail to reify while
// Bun is formatted. The properties listed after it must still be printed.
const { stdout, stderr, exitCode } = await run(`
globalThis.Symbol = new Proxy(Symbol, {
apply(target, thisArg, args) {
if (args[0] === "cwd") throw new Error("boom");
return Reflect.apply(target, thisArg, args);
},
});
const inspected = Bun.inspect(Bun);
console.log(
inspected.includes("\\n $: "),
inspected.includes("\\n Archive: "),
inspected.includes("\\n version: "),
);
`);
expect(stdout).toBe("false true true\n");
expect(stderr).toBe("");
expect(exitCode).toBe(0);
});
});

it("possibly formatted emojis log", () => {
expect(Bun.inspect("✔")).toBe('"✔"');
});
Expand Down