Skip to content

Commit 27067d2

Browse files
Allow using Proxy and module namespace objects in APIs (#15043)
1 parent 1e932ff commit 27067d2

7 files changed

Lines changed: 63 additions & 9 deletions

File tree

src/bun.js/ConsoleObject.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,7 +1143,7 @@ pub const Formatter = struct {
11431143
};
11441144
}
11451145

1146-
if (js_type.canGet()) {
1146+
if (js_type.canGet() and js_type != .ProxyObject) {
11471147
// Attempt to get custom formatter
11481148
if (value.fastGet(globalThis, .inspectCustom)) |callback_value| {
11491149
if (callback_value.isCallable(globalThis.vm())) {
@@ -1202,7 +1202,7 @@ pub const Formatter = struct {
12021202
}
12031203

12041204
// Is this a react element?
1205-
if (js_type.isObject()) {
1205+
if (js_type.isObject() and js_type != .ProxyObject) {
12061206
if (value.getOwnTruthy(globalThis, "$$typeof")) |typeof_symbol| {
12071207
var reactElement = ZigString.init("react.element");
12081208
var react_fragment = ZigString.init("react.fragment");

src/bun.js/bindings/ObjectBindings.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ static bool getNonIndexPropertySlotPrototypePollutionMitigation(JSC::VM& vm, JSO
5555
JSC::JSValue getIfPropertyExistsPrototypePollutionMitigation(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSObject* object, const JSC::PropertyName& name)
5656
{
5757
auto scope = DECLARE_THROW_SCOPE(vm);
58-
auto propertySlot = PropertySlot(object, PropertySlot::InternalMethodType::HasProperty);
58+
auto propertySlot = PropertySlot(object, PropertySlot::InternalMethodType::Get);
5959
auto isDefined = getNonIndexPropertySlotPrototypePollutionMitigation(vm, object, globalObject, name, propertySlot);
6060

6161
if (!isDefined) {

src/bun.js/bindings/bindings.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5003,9 +5003,9 @@ enum class BuiltinNamesMap : uint8_t {
50035003
encoding,
50045004
};
50055005

5006-
static const JSC::Identifier builtinNameMap(JSC::JSGlobalObject* globalObject, unsigned char name)
5006+
static inline const JSC::Identifier builtinNameMap(JSC::VM& vm, unsigned char name)
50075007
{
5008-
auto& vm = globalObject->vm();
5008+
50095009
auto clientData = WebCore::clientData(vm);
50105010
switch (static_cast<BuiltinNamesMap>(name)) {
50115011
case BuiltinNamesMap::method: {
@@ -5076,22 +5076,28 @@ JSC__JSValue JSC__JSValue__fastGetDirect_(JSC__JSValue JSValue0, JSC__JSGlobalOb
50765076
{
50775077
JSC::JSValue value = JSC::JSValue::decode(JSValue0);
50785078
ASSERT(value.isCell());
5079-
return JSValue::encode(value.getObject()->getDirect(globalObject->vm(), PropertyName(builtinNameMap(globalObject, arg2))));
5079+
return JSValue::encode(value.getObject()->getDirect(globalObject->vm(), PropertyName(builtinNameMap(globalObject->vm(), arg2))));
50805080
}
50815081

50825082
JSC__JSValue JSC__JSValue__fastGet_(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, unsigned char arg2)
50835083
{
50845084
JSC::JSValue value = JSC::JSValue::decode(JSValue0);
50855085
ASSERT(value.isCell());
5086-
return JSValue::encode(value.getObject()->getIfPropertyExists(globalObject, builtinNameMap(globalObject, arg2)));
5086+
5087+
JSC::JSObject* object = value.getObject();
5088+
ASSERT_WITH_MESSAGE(object, "fastGet() called on non-object. Check that the JSValue is an object before calling fastGet().");
5089+
auto& vm = globalObject->vm();
5090+
const auto property = JSC::PropertyName(builtinNameMap(vm, arg2));
5091+
5092+
return JSC::JSValue::encode(Bun::getIfPropertyExistsPrototypePollutionMitigation(vm, globalObject, object, property));
50875093
}
50885094

50895095
extern "C" JSC__JSValue JSC__JSValue__fastGetOwn(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, unsigned char arg2)
50905096
{
50915097
JSC::JSValue value = JSC::JSValue::decode(JSValue0);
50925098
ASSERT(value.isCell());
50935099
PropertySlot slot = PropertySlot(value, PropertySlot::InternalMethodType::GetOwnProperty);
5094-
const Identifier name = builtinNameMap(globalObject, arg2);
5100+
const Identifier name = builtinNameMap(globalObject->vm(), arg2);
50955101
auto* object = value.getObject();
50965102
if (object->getOwnPropertySlot(object, globalObject, name, slot)) {
50975103
return JSValue::encode(slot.getValue(globalObject, name));

src/bun.js/test/pretty_format.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ pub const JestPrettyFormat = struct {
442442
}
443443

444444
// Is this a react element?
445-
if (js_type.isObject()) {
445+
if (js_type.isObject() and js_type != .ProxyObject) {
446446
if (value.getOwnTruthy(globalThis, "$$typeof")) |typeof_symbol| {
447447
var reactElement = ZigString.init("react.element");
448448
var react_fragment = ZigString.init("react.fragment");

test/js/bun/http/bun-request-fixture.js

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/js/bun/http/bun-serve-exports-fixture.js

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { test, expect, describe } from "bun:test";
2+
import * as ServerOptions from "./bun-serve-exports-fixture.js";
3+
import * as RequestOptions from "./bun-request-fixture.js";
4+
5+
describe("getIfPropertyExists", () => {
6+
test("Bun.serve()", async () => {
7+
expect(() => Bun.serve(ServerOptions).stop(true)).not.toThrow();
8+
});
9+
10+
test("new Request()", async () => {
11+
expect(await new Request("https://example.com/", RequestOptions).json()).toEqual({
12+
hello: "world",
13+
});
14+
});
15+
16+
test("calls proxy getters", async () => {
17+
expect(
18+
await new Request(
19+
"https://example.com/",
20+
new Proxy(
21+
{},
22+
{
23+
get: (target, prop) => {
24+
if (prop === "body") {
25+
return JSON.stringify({ hello: "world" });
26+
} else if (prop === "method") {
27+
return "POST";
28+
}
29+
},
30+
},
31+
),
32+
).json(),
33+
).toEqual({
34+
hello: "world",
35+
});
36+
});
37+
});

0 commit comments

Comments
 (0)