Allow using Proxy and module namespace objects in APIs (#15043)

This commit is contained in:
Jarred Sumner
2024-11-07 19:45:29 -08:00
committed by GitHub
parent 1e932ff38b
commit 27067d2a6d
7 changed files with 63 additions and 9 deletions

View File

@@ -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");

View File

@@ -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) {

View File

@@ -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<BuiltinNamesMap>(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));

View File

@@ -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");

6
test/js/bun/http/bun-request-fixture.js generated Normal file
View File

@@ -0,0 +1,6 @@
export const signal = undefined;
export const method = "POST";
export const body = JSON.stringify({
hello: "world",
});

View File

@@ -0,0 +1,5 @@
export const port = 0;
export function fetch() {
return new Response();
}

View File

@@ -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",
});
});
});