Skip to content
Closed
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 @@ -320,9 +320,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 @@ -332,9 +329,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
12 changes: 9 additions & 3 deletions src/jsc/bindings/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5643,10 +5643,11 @@ static void JSC__JSValue__forEachPropertyImpl(JSC::EncodedJSValue JSValue0, JSC:
}

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

if ((slot.attributes() & PropertyAttribute::DontEnum) != 0) {
if (property == propertyNames->underscoreProto
Expand Down Expand Up @@ -5718,7 +5719,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)
break;
iterating = prototype.getObject();
}
}

Expand Down
62 changes: 62 additions & 0 deletions test/js/bun/util/inspect.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -928,3 +928,65 @@ describe.skipIf(!isASAN)("object mutated while being formatted", () => {
expect(exitCode).toBe(0);
});
});

describe("property lookup throws while formatting", () => {
async function run(fixture) {
await using proc = Bun.spawn({
cmd: [bunExe(), "-e", fixture],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, , exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
return { stdout, exitCode };
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

it.concurrent("get trap on a prototype Proxy throws", async () => {
const { stdout, exitCode } = await run(`
const proto = new Proxy(
{ a: 1, b: 2 },
{
get(target, key) {
if (key === "a" || key === "b") throw new Error("boom");
return Reflect.get(target, key);
},
},
);
console.log(Bun.inspect(Object.create(proto)));
`);
expect(stdout).toBe("{}\n");
expect(exitCode).toBe(0);
});

it.concurrent("getPrototypeOf trap on a prototype Proxy throws", async () => {
const { stdout, exitCode } = await run(`
const proto = new Proxy(
{ a: 1 },
{
getPrototypeOf() {
throw new Error("boom");
},
},
);
console.log(Bun.inspect(Object.create(proto)));
`);
expect(stdout).toBe("{\n a: 1,\n}\n");
expect(exitCode).toBe(0);
});

it.concurrent("lazy property initializer throws", async () => {
// Bun.$ is the first lazy property on the Bun object and its initializer
// calls Symbol(), so it throws here. The properties after it must still be
// printed. node:util is loaded up front because formatting a value with a
// custom inspect function (Bun.env on Windows) needs util.inspect, which
// can no longer be loaded once Symbol is gone.
const { stdout, exitCode } = await run(`
require("node:util");
globalThis.Symbol = {};
const text = Bun.inspect(Bun);
console.log(text.includes("Archive:"), text.includes("inspect:"));
`);
expect(stdout).toBe("true true\n");
expect(exitCode).toBe(0);
});
});