Skip to content
Merged
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
10 changes: 5 additions & 5 deletions src/bun.js/bindings/BunInjectedScriptHost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,16 +144,16 @@ JSValue BunInjectedScriptHost::getInternalProperties(VM& vm, JSGlobalObject* exe
return array;
}

if (auto* params = jsDynamicCast<JSURLSearchParams*>(value)) {
if (auto* formData = jsDynamicCast<JSDOMFormData*>(value)) {
auto* array = constructEmptyArray(exec, nullptr);
constructDataProperties(vm, exec, array, WebCore::getInternalProperties(vm, exec, params));
constructDataProperties(vm, exec, array, WebCore::getInternalProperties(vm, exec, formData));
RETURN_IF_EXCEPTION(scope, {});
return array;
}

if (auto* formData = jsDynamicCast<JSDOMFormData*>(value)) {
} else if (type == JSAsJSONType) {
if (auto* params = jsDynamicCast<JSURLSearchParams*>(value)) {
auto* array = constructEmptyArray(exec, nullptr);
constructDataProperties(vm, exec, array, WebCore::getInternalProperties(vm, exec, formData));
constructDataProperties(vm, exec, array, WebCore::getInternalProperties(vm, exec, params));
RETURN_IF_EXCEPTION(scope, {});
return array;
}
Expand Down
91 changes: 90 additions & 1 deletion src/bun.js/bindings/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -944,8 +944,10 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2,
return false;
}

case JSAsJSONType:
case JSDOMWrapperType: {
if (c2Type == JSDOMWrapperType) {
if (c2Type == c1Type) {

// https://github.com/oven-sh/bun/issues/4089
// https://github.com/oven-sh/bun/issues/6492
auto* url2 = jsDynamicCast<JSDOMURL*>(v2);
Expand All @@ -956,6 +958,10 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2,
if ((url2 == nullptr) != (url1 == nullptr)) {
return false;
}
} else {
if ((url1 == nullptr) != (url2 == nullptr)) {
goto compareAsNormalValue;
}
}

if (url2 && url1) {
Expand All @@ -966,8 +972,91 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2,
if (url1->wrapped().href() != url2->wrapped().href()) {
return false;
}

goto compareAsNormalValue;
}

// TODO: FormData.
// It's complicated because it involves Blob.

{
auto urlSearchParams1 = jsDynamicCast<JSURLSearchParams*>(v1);
auto urlSearchParams2 = jsDynamicCast<JSURLSearchParams*>(v2);
if (urlSearchParams1 && urlSearchParams2) {
auto& wrapped1 = urlSearchParams1->wrapped();
const auto& wrapped2 = urlSearchParams2->wrapped();
if (wrapped1.size() != wrapped2.size()) {
return false;
}

auto iter1 = wrapped1.createIterator();
while (const auto& maybePair = iter1.next()) {
const auto& key = maybePair->key;
const auto& value = maybePair->value;
const auto& maybeValue = wrapped2.get(key);
if (!maybeValue || maybeValue != value) {
return false;
}
}

goto compareAsNormalValue;
} else {
if constexpr (isStrict) {
// if one is a URLSearchParams and the other is not a URLSearchParams, toStrictEqual should return false.
if ((urlSearchParams2 == nullptr) != (urlSearchParams1 == nullptr)) {
return false;
}
} else {
if ((urlSearchParams1 == nullptr) != (urlSearchParams2 == nullptr)) {
goto compareAsNormalValue;
}
}
}
}

{
auto headers1 = jsDynamicCast<JSFetchHeaders*>(v1);
auto headers2 = jsDynamicCast<JSFetchHeaders*>(v2);
if (headers1 && headers2) {
auto& wrapped1 = headers1->wrapped();
const auto& wrapped2 = headers2->wrapped();
if (wrapped1.size() != wrapped2.size()) {
return false;
}

auto iter1 = wrapped1.createIterator();
while (const auto& maybePair = iter1.next()) {
const auto& key = maybePair->key;
const auto& value = maybePair->value;
const auto& maybeValue = wrapped2.get(key);
if (maybeValue.hasException()) {
return false;
}

if (maybeValue.returnValue() != value) {
return false;
}
}

goto compareAsNormalValue;
} else {
if constexpr (isStrict) {
// if one is a FetchHeaders and the other is not a FetchHeaders, toStrictEqual should return false.
if ((headers2 == nullptr) != (headers1 == nullptr)) {
return false;
}
} else {
if ((headers1 == nullptr) != (headers2 == nullptr)) {
goto compareAsNormalValue;
}
}
}
}
}

goto compareAsNormalValue;

compareAsNormalValue:
break;
}

Expand Down
4 changes: 2 additions & 2 deletions src/bun.js/bindings/webcore/FetchHeaders.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ class FetchHeaders : public RefCounted<FetchHeaders> {

size_t memoryCost() const;

inline uint32_t size()
inline uint32_t size() const
{
return m_headers.size();
}

inline uint32_t sizeAfterJoiningSetCookieHeader()
inline uint32_t sizeAfterJoiningSetCookieHeader() const
{
return m_headers.commonHeaders().size() + m_headers.uncommonHeaders().size() + (m_headers.getSetCookieHeaders().size() > 0);
}
Expand Down
2 changes: 1 addition & 1 deletion src/bun.js/test/expect.zig
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ pub const Expect = struct {

const signature = comptime getSignature("toBe", "<green>expected<r>", false);
if (left.deepEquals(right, globalThis) or left.strictDeepEquals(right, globalThis)) {
const fmt = signature ++
const fmt =
(if (!has_custom_label) "\n\n<d>If this test should pass, replace \"toBe\" with \"toEqual\" or \"toStrictEqual\"<r>" else "") ++
"\n\nExpected: <green>{any}<r>\n" ++
"Received: serializes to the same string\n";
Expand Down
25 changes: 25 additions & 0 deletions test/js/bun/test/expect.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,31 @@ describe("expect()", () => {
expect([, 1]).toEqual([undefined, 1]);
});

describe("toEqual() with DOM types", () => {
test("URLSearchParams", () => {
expect(new URLSearchParams("a=1")).not.toEqual(new URLSearchParams("b=1"));
expect(new URLSearchParams("a=1")).toEqual(new URLSearchParams("a=1"));
expect(new URLSearchParams("a=1&b=2")).not.toEqual(new URLSearchParams("a=1&"));
});

if (isBun) {
test("URL", () => {
expect(new URL("https://example.com")).toEqual(new URL("https://example.com"));
expect(new URL("http://wat")).not.toStrictEqual(new URL("http://huh"));
});
}

test("Headers", () => {
expect(new Headers({ "a": "1" })).toEqual(new Headers({ "a": "1" }));
expect(new Headers({ "a": "1" })).not.toEqual(new Headers({ "b": "1" }));
expect(new Headers({ "a": "1" })).not.toEqual(new Headers({ "a": "2" }));
expect(new Headers({ "a": "1" })).not.toEqual(new Headers({ "a": "1", "b": "2" }));
});

// TODO: FormData
// It would need to compare Blob, which is tricky.
});

describe("BigInt", () => {
it("compares correctly (literal)", () => {
expect(42n).toBe(42n);
Expand Down