diff --git a/src/bun.js/ConsoleObject.zig b/src/bun.js/ConsoleObject.zig index fdb4ed923e..6954862623 100644 --- a/src/bun.js/ConsoleObject.zig +++ b/src/bun.js/ConsoleObject.zig @@ -1143,7 +1143,7 @@ pub const Formatter = struct { }; } - if (js_type.canGet()) { + if (js_type.canGet() and js_type != .ProxyObject) { // Attempt to get custom formatter if (value.fastGet(globalThis, .inspectCustom)) |callback_value| { if (callback_value.isCallable(globalThis.vm())) { @@ -1202,7 +1202,7 @@ pub const Formatter = struct { } // Is this a react element? - if (js_type.isObject()) { + if (js_type.isObject() and js_type != .ProxyObject) { if (value.getOwnTruthy(globalThis, "$$typeof")) |typeof_symbol| { var reactElement = ZigString.init("react.element"); var react_fragment = ZigString.init("react.fragment"); diff --git a/src/bun.js/bindings/ObjectBindings.cpp b/src/bun.js/bindings/ObjectBindings.cpp index d3ecb8e78a..852bcce4f2 100644 --- a/src/bun.js/bindings/ObjectBindings.cpp +++ b/src/bun.js/bindings/ObjectBindings.cpp @@ -55,7 +55,7 @@ static bool getNonIndexPropertySlotPrototypePollutionMitigation(JSC::VM& vm, JSO JSC::JSValue getIfPropertyExistsPrototypePollutionMitigation(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSObject* object, const JSC::PropertyName& name) { auto scope = DECLARE_THROW_SCOPE(vm); - auto propertySlot = PropertySlot(object, PropertySlot::InternalMethodType::HasProperty); + auto propertySlot = PropertySlot(object, PropertySlot::InternalMethodType::Get); auto isDefined = getNonIndexPropertySlotPrototypePollutionMitigation(vm, object, globalObject, name, propertySlot); if (!isDefined) { diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index 30288d4a18..51a7da1e23 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -5003,9 +5003,9 @@ enum class BuiltinNamesMap : uint8_t { encoding, }; -static const JSC::Identifier builtinNameMap(JSC::JSGlobalObject* globalObject, unsigned char name) +static inline const JSC::Identifier builtinNameMap(JSC::VM& vm, unsigned char name) { - auto& vm = globalObject->vm(); + auto clientData = WebCore::clientData(vm); switch (static_cast(name)) { case BuiltinNamesMap::method: { @@ -5076,14 +5076,20 @@ JSC__JSValue JSC__JSValue__fastGetDirect_(JSC__JSValue JSValue0, JSC__JSGlobalOb { JSC::JSValue value = JSC::JSValue::decode(JSValue0); ASSERT(value.isCell()); - return JSValue::encode(value.getObject()->getDirect(globalObject->vm(), PropertyName(builtinNameMap(globalObject, arg2)))); + return JSValue::encode(value.getObject()->getDirect(globalObject->vm(), PropertyName(builtinNameMap(globalObject->vm(), arg2)))); } JSC__JSValue JSC__JSValue__fastGet_(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, unsigned char arg2) { JSC::JSValue value = JSC::JSValue::decode(JSValue0); ASSERT(value.isCell()); - return JSValue::encode(value.getObject()->getIfPropertyExists(globalObject, builtinNameMap(globalObject, arg2))); + + JSC::JSObject* object = value.getObject(); + ASSERT_WITH_MESSAGE(object, "fastGet() called on non-object. Check that the JSValue is an object before calling fastGet()."); + auto& vm = globalObject->vm(); + const auto property = JSC::PropertyName(builtinNameMap(vm, arg2)); + + return JSC::JSValue::encode(Bun::getIfPropertyExistsPrototypePollutionMitigation(vm, globalObject, object, property)); } extern "C" JSC__JSValue JSC__JSValue__fastGetOwn(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, unsigned char arg2) @@ -5091,7 +5097,7 @@ extern "C" JSC__JSValue JSC__JSValue__fastGetOwn(JSC__JSValue JSValue0, JSC__JSG JSC::JSValue value = JSC::JSValue::decode(JSValue0); ASSERT(value.isCell()); PropertySlot slot = PropertySlot(value, PropertySlot::InternalMethodType::GetOwnProperty); - const Identifier name = builtinNameMap(globalObject, arg2); + const Identifier name = builtinNameMap(globalObject->vm(), arg2); auto* object = value.getObject(); if (object->getOwnPropertySlot(object, globalObject, name, slot)) { return JSValue::encode(slot.getValue(globalObject, name)); diff --git a/src/bun.js/test/pretty_format.zig b/src/bun.js/test/pretty_format.zig index c1a838ce1b..874158774f 100644 --- a/src/bun.js/test/pretty_format.zig +++ b/src/bun.js/test/pretty_format.zig @@ -442,7 +442,7 @@ pub const JestPrettyFormat = struct { } // Is this a react element? - if (js_type.isObject()) { + if (js_type.isObject() and js_type != .ProxyObject) { if (value.getOwnTruthy(globalThis, "$$typeof")) |typeof_symbol| { var reactElement = ZigString.init("react.element"); var react_fragment = ZigString.init("react.fragment"); diff --git a/test/js/bun/http/bun-request-fixture.js b/test/js/bun/http/bun-request-fixture.js new file mode 100644 index 0000000000..f1f9c15306 --- /dev/null +++ b/test/js/bun/http/bun-request-fixture.js @@ -0,0 +1,6 @@ +export const signal = undefined; + +export const method = "POST"; +export const body = JSON.stringify({ + hello: "world", +}); diff --git a/test/js/bun/http/bun-serve-exports-fixture.js b/test/js/bun/http/bun-serve-exports-fixture.js new file mode 100644 index 0000000000..63d070d51a --- /dev/null +++ b/test/js/bun/http/bun-serve-exports-fixture.js @@ -0,0 +1,5 @@ +export const port = 0; + +export function fetch() { + return new Response(); +} diff --git a/test/js/bun/http/getIfPropertyExists.test.ts b/test/js/bun/http/getIfPropertyExists.test.ts new file mode 100644 index 0000000000..2de06930f6 --- /dev/null +++ b/test/js/bun/http/getIfPropertyExists.test.ts @@ -0,0 +1,37 @@ +import { test, expect, describe } from "bun:test"; +import * as ServerOptions from "./bun-serve-exports-fixture.js"; +import * as RequestOptions from "./bun-request-fixture.js"; + +describe("getIfPropertyExists", () => { + test("Bun.serve()", async () => { + expect(() => Bun.serve(ServerOptions).stop(true)).not.toThrow(); + }); + + test("new Request()", async () => { + expect(await new Request("https://example.com/", RequestOptions).json()).toEqual({ + hello: "world", + }); + }); + + test("calls proxy getters", async () => { + expect( + await new Request( + "https://example.com/", + new Proxy( + {}, + { + get: (target, prop) => { + if (prop === "body") { + return JSON.stringify({ hello: "world" }); + } else if (prop === "method") { + return "POST"; + } + }, + }, + ), + ).json(), + ).toEqual({ + hello: "world", + }); + }); +});