Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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: 3 additions & 3 deletions src/bun.js/ConsoleObject.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2037,7 +2037,7 @@ pub const Formatter = struct {
try value.getClassName(globalThis, &name_str);
if (!name_str.eqlComptime("Object")) {
return name_str;
} else if (value.getPrototype(globalThis).eqlValue(JSValue.null)) {
} else if ((try value.getPrototype(globalThis)) == .null) {
return ZigString.static("[Object: null prototype]").*;
}
return null;
Expand Down Expand Up @@ -2318,7 +2318,7 @@ pub const Formatter = struct {
try value.getClassName(this.globalThis, &printable);
this.addForNewLine(printable.len);

const proto = value.getPrototype(this.globalThis);
const proto = try value.getPrototype(this.globalThis);
var printable_proto = ZigString.init(&name_buf);
try proto.getClassName(this.globalThis, &printable_proto);
this.addForNewLine(printable_proto.len);
Expand All @@ -2341,7 +2341,7 @@ pub const Formatter = struct {
var printable = try value.getName(this.globalThis);
defer printable.deref();

const proto = value.getPrototype(this.globalThis);
const proto = try value.getPrototype(this.globalThis);
const func_name = try proto.getName(this.globalThis); // "Function" | "AsyncFunction" | "GeneratorFunction" | "AsyncGeneratorFunction"
defer func_name.deref();

Expand Down
5 changes: 2 additions & 3 deletions src/bun.js/bindings/JSValue.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1275,9 +1275,8 @@ pub const JSValue = enum(i64) {
}
extern fn JSC__JSValue__unwrapBoxedPrimitive(*JSGlobalObject, JSValue) JSValue;

extern fn JSC__JSValue__getPrototype(this: JSValue, globalObject: *JSGlobalObject) JSValue;
pub fn getPrototype(this: JSValue, globalObject: *JSGlobalObject) JSValue {
return JSC__JSValue__getPrototype(this, globalObject);
pub fn getPrototype(this: JSValue, globalObject: *JSGlobalObject) JSError!JSValue {
return bun.cpp.JSC__JSValue__getPrototype(this, globalObject);
}

extern fn JSC__JSValue__eqlValue(this: JSValue, other: JSValue) bool;
Expand Down
14 changes: 10 additions & 4 deletions src/bun.js/bindings/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3691,7 +3691,7 @@ bool JSC__JSValue__eqlValue(JSC::EncodedJSValue JSValue0, JSC::EncodedJSValue JS
{
return JSC::JSValue::decode(JSValue0) == JSC::JSValue::decode(JSValue1);
};
JSC::EncodedJSValue JSC__JSValue__getPrototype(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* arg1)
[[ZIG_EXPORT(zero_is_throw)]] JSC::EncodedJSValue JSC__JSValue__getPrototype(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* arg1)
{
auto value = JSC::JSValue::decode(JSValue0);
return JSC::JSValue::encode(value.getPrototype(arg1));
Expand Down Expand Up @@ -4970,6 +4970,8 @@ static void JSC__JSValue__forEachPropertyImpl(JSC::EncodedJSValue JSValue0, JSC:
fast = canPerformFastPropertyEnumerationForIterationBun(structure);
prototypeCount = 1;
}
} else {
RETURN_IF_EXCEPTION(scope, void());
}
}
}
Expand Down Expand Up @@ -5049,9 +5051,9 @@ static void JSC__JSValue__forEachPropertyImpl(JSC::EncodedJSValue JSValue0, JSC:
goto restart;
}
}
} else {
RETURN_IF_EXCEPTION(scope, void());
}
// Ignore exceptions from Proxy "getPrototype" trap.
CLEAR_IF_EXCEPTION(scope);
}
return;
}
Expand Down Expand Up @@ -5164,7 +5166,11 @@ static void JSC__JSValue__forEachPropertyImpl(JSC::EncodedJSValue JSValue0, JSC:
break;
if (iterating == globalObject)
break;
iterating = iterating->getPrototype(globalObject).getObject();

JSValue proto = iterating->getPrototype(globalObject);
RETURN_IF_EXCEPTION(scope, void());

iterating = proto.getObject();
}
}

Expand Down
1 change: 0 additions & 1 deletion src/bun.js/bindings/headers.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion src/bun.js/bindings/napi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1831,7 +1831,9 @@ extern "C" napi_status napi_get_all_property_names(
// Climb up the prototype chain to find inherited properties
JSObject* current_object = object;
while (!current_object->getOwnPropertyDescriptor(globalObject, key.toPropertyKey(globalObject), desc)) {
JSObject* proto = current_object->getPrototype(globalObject).getObject();
JSValue protoValue = current_object->getPrototype(globalObject);
NAPI_RETURN_IF_EXCEPTION(env);
JSObject* proto = protoValue.getObject();
if (!proto) {
break;
}
Expand Down
1 change: 1 addition & 0 deletions src/bun.js/modules/NodeUtilTypesModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionIsError,
}

JSValue proto = object->getPrototype(globalObject);
RETURN_IF_EXCEPTION(scope, {});
if (proto.isCell() && (proto.inherits<JSC::ErrorInstance>() || proto.asCell()->type() == ErrorInstanceType || proto.inherits<JSC::ErrorPrototype>()))
return JSValue::encode(jsBoolean(true));
}
Expand Down
2 changes: 1 addition & 1 deletion src/bun.js/test/ScopeFunctions.zig
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ pub fn createUnbound(globalThis: *JSGlobalObject, mode: Mode, each: jsc.JSValue,
pub fn bind(value: JSValue, globalThis: *JSGlobalObject, name: bun.String) bun.JSError!JSValue {
const callFn = jsc.JSFunction.create(globalThis, name, callAsFunction, 1, .{});
const bound = try callFn.bind(globalThis, value, &name, 1, &.{});
try bound.setPrototypeDirect(value.getPrototype(globalThis), globalThis);
try bound.setPrototypeDirect(try value.getPrototype(globalThis), globalThis);
return bound;
}

Expand Down
2 changes: 1 addition & 1 deletion src/bun.js/test/pretty_format.zig
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ pub const JestPrettyFormat = struct {
if (name_str.len > 0 and !name_str.eqlComptime("Object")) {
writer.print("{f} ", .{name_str});
} else {
try value.getPrototype(globalThis).getNameProperty(globalThis, &name_str);
try (try value.getPrototype(globalThis)).getNameProperty(globalThis, &name_str);
if (name_str.len > 0 and !name_str.eqlComptime("Object")) {
writer.print("{f} ", .{name_str});
}
Expand Down
24 changes: 24 additions & 0 deletions test/js/bun/util/inspect.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,30 @@ it("getter/setters", () => {
expect(Bun.inspect(obj)).toBe("{\n" + " foo: [Getter/Setter]," + "\n" + "}");
});

it("stack overflow exception checks", () => {
function probe(value) {
let originalPrototype, newPrototype;
let handler = {
set(target, key, value, receiver) {
return Reflect.set(target, key, value, receiver);
},
};
originalPrototype = Object.getPrototypeOf(value);
newPrototype = new Proxy(originalPrototype, handler);
Object.setPrototypeOf(value, newPrototype);
}
class Foo {
get bar() {
Bun.inspect(this);
}
}
const foo = new Foo();
probe(foo);
expect(() => {
foo.bar(Foo, foo);
}).toThrow("Maximum call stack size exceeded");
});
Comment on lines +85 to +107
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

New regression test for inspect stack overflow looks good; consider silencing the getter lint

The "stack overflow exception checks" test correctly exercises a recursive Bun.inspect path via a proxy‑wrapped prototype and asserts that it now surfaces as "Maximum call stack size exceeded" instead of crashing, which matches the PR goal.

Static analysis is flagging the get bar() accessor for not returning a value. While this is valid JS (it implicitly returns undefined), you might either:

  • make the intent explicit:
class Foo {
  get bar() {
    return Bun.inspect(this);
  }
}
  • or add a Biome ignore comment for this getter, if you prefer the current shape.

Both keep the test behavior while satisfying the linter.

🧰 Tools
🪛 Biome (2.1.2)

[error] 98-100: This getter should return a value.

(lint/suspicious/useGetterReturn)

🤖 Prompt for AI Agents
In test/js/bun/util/inspect.test.js around lines 85 to 107, the getter get bar()
is flagged by the linter for not returning a value; update the getter to
explicitly return Bun.inspect(this) (i.e., make the intent explicit) or add the
appropriate Biome/linter ignore comment above the getter to silence the warning
while preserving the test behavior.


it("Timeout", () => {
const id = setTimeout(() => {}, 0);
expect(Bun.inspect(id)).toBe(`Timeout (#${+id})`);
Expand Down