aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2023-08-21 16:25:23 -0700
committerGravatar GitHub <noreply@github.com> 2023-08-21 16:25:23 -0700
commit752e59f23c78937558618f30c27abf3eafd8d5d5 (patch)
treefb3d5f8fc2eba8993b77859bac48c0897d5aaf94
parentdef5a85d90e5102ab52e6960e8caab3c3f8ab3e8 (diff)
downloadbun-752e59f23c78937558618f30c27abf3eafd8d5d5.tar.gz
bun-752e59f23c78937558618f30c27abf3eafd8d5d5.tar.zst
bun-752e59f23c78937558618f30c27abf3eafd8d5d5.zip
Fixes #4089 (#4105)
* Fixes #4089 * Update bindings.cpp * address PR feedback --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
-rw-r--r--src/bun.js/bindings/bindings.cpp142
-rw-r--r--src/bun.js/bindings/webcore/JSDOMURL.h2
-rw-r--r--test/js/bun/test/expect.test.js28
3 files changed, 136 insertions, 36 deletions
diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp
index afa1e254c..37594288d 100644
--- a/src/bun.js/bindings/bindings.cpp
+++ b/src/bun.js/bindings/bindings.cpp
@@ -362,8 +362,11 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2,
JSCell* c2 = v2.asCell();
JSObject* o1 = v1.getObject();
JSObject* o2 = v2.getObject();
- JSC::JSType c1Type = c1->type();
- JSC::JSType c2Type = c2->type();
+
+ // We use additional values outside the enum
+ // so the warning here is unnecessary
+ uint8_t c1Type = c1->type();
+ uint8_t c2Type = c2->type();
switch (c1Type) {
case JSSetType: {
@@ -573,7 +576,7 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2,
case Float64ArrayType:
case BigInt64ArrayType:
case BigUint64ArrayType: {
- if (!isTypedArrayType(c2Type)) {
+ if (!isTypedArrayType(static_cast<JSC::JSType>(c2Type))) {
return false;
}
@@ -620,6 +623,37 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2,
case JSFunctionType: {
return false;
}
+
+ case JSDOMWrapperType: {
+ if (c2Type == JSDOMWrapperType) {
+ // https://github.com/oven-sh/bun/issues/4089
+ auto* url2 = jsDynamicCast<JSDOMURL*>(v2);
+ auto* url1 = jsDynamicCast<JSDOMURL*>(v1);
+
+ if constexpr (isStrict) {
+ // if one is a URL and the other is not a URL, toStrictEqual returns false.
+ if ((url2 == nullptr) != (url1 == nullptr)) {
+ return false;
+ }
+
+ if (url2 && url1) {
+ return url1->wrapped().href() != url2->wrapped().href();
+ }
+ } else {
+ if (url2 && url1) {
+ // toEqual should return false when the URLs' href is not equal
+ // But you could have added additional properties onto the
+ // url object itself, so we must check those as well
+ // But it's definitely not equal if the href() is not the same
+ if (url1->wrapped().href() != url2->wrapped().href()) {
+ return false;
+ }
+ }
+ }
+ }
+ break;
+ }
+
default: {
break;
}
@@ -745,9 +779,9 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2,
}
JSC::Structure* o1Structure = o1->structure();
- if (canPerformFastPropertyEnumerationForIterationBun(o1Structure)) {
+ if (!o1Structure->hasNonReifiedStaticProperties() && o1Structure->canPerformFastPropertyEnumeration()) {
JSC::Structure* o2Structure = o2->structure();
- if (canPerformFastPropertyEnumerationForIterationBun(o2Structure)) {
+ if (!o2Structure->hasNonReifiedStaticProperties() && o2Structure->canPerformFastPropertyEnumeration()) {
size_t count1 = 0;
@@ -758,59 +792,95 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2,
}
}
- o1Structure->forEachProperty(vm, [&](const PropertyTableEntry& entry) -> bool {
- if (entry.attributes() & PropertyAttribute::DontEnum || PropertyName(entry.key()).isPrivateName()) {
- return true;
- }
- count1++;
+ bool sameStructure = o2Structure->id() == o1Structure->id();
- JSValue left = o1->getDirect(entry.offset());
- JSValue right = o2->getDirect(vm, JSC::PropertyName(entry.key()));
-
- if constexpr (!isStrict) {
- if (left.isUndefined() && right.isEmpty()) {
+ if (sameStructure) {
+ o1Structure->forEachProperty(vm, [&](const PropertyTableEntry& entry) -> bool {
+ if (entry.attributes() & PropertyAttribute::DontEnum || PropertyName(entry.key()).isPrivateName()) {
return true;
}
- }
+ count1++;
- if (!right) {
- result = false;
- return false;
- }
+ JSValue left = o1->getDirect(entry.offset());
+ JSValue right = o2->getDirect(entry.offset());
- if (left == right || JSC::sameValue(globalObject, left, right)) {
- return true;
- }
+ if constexpr (!isStrict) {
+ if (left.isUndefined() && right.isEmpty()) {
+ return true;
+ }
+ }
- if (!Bun__deepEquals<isStrict, enableAsymmetricMatchers>(globalObject, left, right, stack, scope, true)) {
- result = false;
- return false;
- }
+ if (!right) {
+ result = false;
+ return false;
+ }
- return true;
- });
+ if (left == right || JSC::sameValue(globalObject, left, right)) {
+ return true;
+ }
+
+ if (!Bun__deepEquals<isStrict, enableAsymmetricMatchers>(globalObject, left, right, stack, scope, true)) {
+ result = false;
+ return false;
+ }
- if (result && o2Structure->id() != o1Structure->id()) {
- size_t remain = count1;
- o2Structure->forEachProperty(vm, [&](const PropertyTableEntry& entry) -> bool {
+ return true;
+ });
+ } else {
+ o1Structure->forEachProperty(vm, [&](const PropertyTableEntry& entry) -> bool {
if (entry.attributes() & PropertyAttribute::DontEnum || PropertyName(entry.key()).isPrivateName()) {
return true;
}
+ count1++;
+
+ JSValue left = o1->getDirect(entry.offset());
+ JSValue right = o2->getDirect(vm, JSC::PropertyName(entry.key()));
if constexpr (!isStrict) {
- if (o2->getDirect(entry.offset()).isUndefined()) {
+ if (left.isUndefined() && right.isEmpty()) {
return true;
}
}
- if (remain == 0) {
+ if (!right) {
+ result = false;
+ return false;
+ }
+
+ if (left == right || JSC::sameValue(globalObject, left, right)) {
+ return true;
+ }
+
+ if (!Bun__deepEquals<isStrict, enableAsymmetricMatchers>(globalObject, left, right, stack, scope, true)) {
result = false;
return false;
}
- remain--;
return true;
});
+
+ if (result) {
+ size_t remain = count1;
+ o2Structure->forEachProperty(vm, [&](const PropertyTableEntry& entry) -> bool {
+ if (entry.attributes() & PropertyAttribute::DontEnum || PropertyName(entry.key()).isPrivateName()) {
+ return true;
+ }
+
+ if constexpr (!isStrict) {
+ if (o2->getDirect(entry.offset()).isUndefined()) {
+ return true;
+ }
+ }
+
+ if (remain == 0) {
+ result = false;
+ return false;
+ }
+
+ remain--;
+ return true;
+ });
+ }
}
if (addToStack) {
@@ -824,7 +894,9 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2,
JSC::PropertyNameArray a1(vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
JSC::PropertyNameArray a2(vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
o1->getPropertyNames(globalObject, a1, DontEnumPropertiesMode::Exclude);
+ RETURN_IF_EXCEPTION(*scope, false);
o2->getPropertyNames(globalObject, a2, DontEnumPropertiesMode::Exclude);
+ RETURN_IF_EXCEPTION(*scope, false);
const size_t propertyArrayLength = a1.size();
if (propertyArrayLength != a2.size()) {
diff --git a/src/bun.js/bindings/webcore/JSDOMURL.h b/src/bun.js/bindings/webcore/JSDOMURL.h
index e89add235..32d721912 100644
--- a/src/bun.js/bindings/webcore/JSDOMURL.h
+++ b/src/bun.js/bindings/webcore/JSDOMURL.h
@@ -45,7 +45,7 @@ public:
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
{
- return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), JSC::NonArray);
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(static_cast<JSC::JSType>(0b11101110), StructureFlags), info(), JSC::NonArray);
}
static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*);
diff --git a/test/js/bun/test/expect.test.js b/test/js/bun/test/expect.test.js
index 397afd0d9..804a5339f 100644
--- a/test/js/bun/test/expect.test.js
+++ b/test/js/bun/test/expect.test.js
@@ -955,6 +955,34 @@ describe("expect()", () => {
}).toThrow();
});
+ test("deepEquals URLs", () => {
+ const equals = [
+ [
+ [new URL("https://example.com"), new URL("https://example.com")],
+ [new URL("https://example.com"), new URL("https://example.com/")],
+ [Object.fromEntries(Object.entries(new URL("https://example.com"))), new URL("https://example.com/")],
+ ],
+ ];
+ const not = [
+ [new URL("https://example.com"), new URL("https://example.com/1")],
+ [new URL("https://example.com"), new URL("https://example.com/1")],
+ ];
+
+ for (let [first, second] of equals) {
+ expect(first).toEqual(second);
+ expect(second).toEqual(first);
+ }
+
+ for (let [first, second] of not) {
+ expect(first).not.toEqual(second);
+ expect(second).not.toEqual(first);
+ }
+
+ expect(Object.fromEntries(Object.entries(new URL("https://example.com")))).not.toStrictEqual(
+ new URL("https://example.com/"),
+ );
+ });
+
test("toEqual objects and arrays", () => {
{
let obj = { 0: 4, 1: 3, length: 2 };