aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Dylan Conway <35280289+dylan-conway@users.noreply.github.com> 2023-04-06 14:49:07 -0700
committerGravatar GitHub <noreply@github.com> 2023-04-06 14:49:07 -0700
commit2b170c9d135930f80fa86dc2f92120a70c2720d9 (patch)
treee1a47e325df71a08fd936e2c0603684101f6269e
parent1d138057cb861fe540cfe5ef49905225cee40ae8 (diff)
downloadbun-2b170c9d135930f80fa86dc2f92120a70c2720d9.tar.gz
bun-2b170c9d135930f80fa86dc2f92120a70c2720d9.tar.zst
bun-2b170c9d135930f80fa86dc2f92120a70c2720d9.zip
Fix `toEqual` when the second array has extra array holes (#2580)
* iterate through remaining indexes, keep prop identifier * tests * format
-rw-r--r--src/bun.js/bindings/bindings.cpp34
-rw-r--r--src/bun.js/ws.exports.js11
-rw-r--r--test/js/bun/test/test-test.test.ts49
3 files changed, 76 insertions, 18 deletions
diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp
index 8c1712338..450c9bc28 100644
--- a/src/bun.js/bindings/bindings.cpp
+++ b/src/bun.js/bindings/bindings.cpp
@@ -488,12 +488,16 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2,
JSC::JSArray* array1 = JSC::jsCast<JSC::JSArray*>(v1);
JSC::JSArray* array2 = JSC::jsCast<JSC::JSArray*>(v2);
- size_t length = array1->length();
- if (length != array2->length()) {
- return false;
+ size_t array1Length = array1->length();
+ size_t array2Length = array2->length();
+ if constexpr (isStrict) {
+ if (array1Length != array2Length) {
+ return false;
+ }
}
- for (uint64_t i = 0; i < length; i++) {
+ uint64_t i = 0;
+ for (; i < array1Length; i++) {
JSValue left = getIndexWithoutAccessors(globalObject, o1, i);
RETURN_IF_EXCEPTION(*scope, false);
JSValue right = getIndexWithoutAccessors(globalObject, o2, i);
@@ -521,6 +525,17 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2,
RETURN_IF_EXCEPTION(*scope, false);
}
+ for (; i < array2Length; i++) {
+ JSValue right = getIndexWithoutAccessors(globalObject, o2, i);
+ RETURN_IF_EXCEPTION(*scope, false);
+
+ if (((right.isEmpty() || right.isUndefined()))) {
+ continue;
+ }
+
+ return false;
+ }
+
JSC::PropertyNameArray a1(vm, PropertyNameMode::Symbols, PrivateSymbolMode::Include);
JSC::PropertyNameArray a2(vm, PropertyNameMode::Symbols, PrivateSymbolMode::Include);
JSObject::getOwnPropertyNames(o1, globalObject, a1, DontEnumPropertiesMode::Exclude);
@@ -3844,7 +3859,7 @@ restart:
}
}
-inline bool propertyCompare(const std::pair<String, JSValue>& a, const std::pair<String, JSValue>& b)
+inline bool propertyCompare(const std::pair<String, std::pair<Identifier, JSValue>>& a, const std::pair<String, std::pair<Identifier, JSValue>>& b)
{
return codePointCompare(a.first.impl(), b.first.impl()) < 0;
}
@@ -3862,19 +3877,20 @@ void JSC__JSValue__forEachPropertyOrdered(JSC__JSValue JSValue0, JSC__JSGlobalOb
JSC::PropertyNameArray properties(vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
JSC::JSObject::getOwnPropertyNames(object, globalObject, properties, DontEnumPropertiesMode::Include);
- Vector<std::pair<String, JSValue>> ordered_properties;
+ Vector<std::pair<String, std::pair<Identifier, JSValue>>> ordered_properties;
for (auto property : properties) {
JSValue propertyValue = object->getDirect(vm, property);
- ordered_properties.append(std::pair<String, JSValue>(property.isSymbol() && !property.isPrivateName() ? property.impl() : property.string(), propertyValue));
+ ordered_properties.append(std::pair<String, std::pair<Identifier, JSValue>>(property.isSymbol() && !property.isPrivateName() ? property.impl() : property.string(), std::pair<Identifier, JSValue>(property, propertyValue)));
}
std::sort(ordered_properties.begin(), ordered_properties.end(), propertyCompare);
for (auto item : ordered_properties) {
ZigString key = toZigString(item.first);
- JSValue propertyValue = item.second;
+ Identifier property = item.second.first;
+ JSValue propertyValue = item.second.second;
JSC::EnsureStillAliveScope ensureStillAliveScope(propertyValue);
- iter(globalObject, arg2, &key, JSC::JSValue::encode(propertyValue), propertyValue.isSymbol());
+ iter(globalObject, arg2, &key, JSC::JSValue::encode(propertyValue), property.isSymbol());
}
}
diff --git a/src/bun.js/ws.exports.js b/src/bun.js/ws.exports.js
index 4ecb0205d..864ac20d3 100644
--- a/src/bun.js/ws.exports.js
+++ b/src/bun.js/ws.exports.js
@@ -88,7 +88,7 @@ class Receiver {
BunWebSocket.Receiver = Receiver;
-var createWebSocketStream = (ws) => {
+var createWebSocketStream = ws => {
throw new Error("Not supported yet in Bun");
};
@@ -96,11 +96,4 @@ BunWebSocket.createWebSocketStream = createWebSocketStream;
export default BunWebSocket;
-export {
- createWebSocketStream,
- Server,
- Receiver,
- Sender,
- BunWebSocket as WebSocket,
- Server as WebSocketServer,
-};
+export { createWebSocketStream, Server, Receiver, Sender, BunWebSocket as WebSocket, Server as WebSocketServer };
diff --git a/test/js/bun/test/test-test.test.ts b/test/js/bun/test/test-test.test.ts
index 2777b35fa..5154950de 100644
--- a/test/js/bun/test/test-test.test.ts
+++ b/test/js/bun/test/test-test.test.ts
@@ -777,8 +777,57 @@ test("deepEquals - symbols", () => {
expect(o).toEqual(k);
});
+test("deepEquals should not segfault", () => {
+ const obj = { ...Object.fromEntries(Object.entries([1, 2, 3, 4])), length: 4 };
+ expect(() => {
+ expect(obj).toEqual([1, 2, 3, 4]);
+ }).toThrow();
+ expect(() => {
+ expect([1, 2, 3, 4]).toEqual(obj);
+ }).toThrow();
+});
+
test("toEqual objects and arrays", () => {
{
+ let obj = { 0: 4, 1: 3, length: 2 };
+ expect(Array.from(obj)).toEqual([4, 3]);
+ expect(Array.from(obj)).toStrictEqual([4, 3]);
+ }
+ {
+ let obj = { 0: 4, 1: 3, length: 4 };
+ expect(Array.from(obj)).toEqual([4, 3]);
+ expect(Array.from(obj)).not.toStrictEqual([4, 3]);
+ expect(Array.from(obj)).toEqual([4, 3, undefined, undefined]);
+ expect(Array.from(obj)).toStrictEqual([4, 3, undefined, undefined]);
+ expect(Array.from(obj)).toEqual([4, 3, , ,]);
+ expect(Array.from(obj)).not.toStrictEqual([4, 3, , ,]);
+ }
+ {
+ let a1 = [1, undefined, 3, , 4, null];
+ let a2 = [1, undefined, 3, , 4, null, , ,];
+ expect(a1).toEqual(a2);
+ expect(a1).not.toStrictEqual(a2);
+ expect(a2).toEqual(a1);
+ expect(a2).not.toStrictEqual(a1);
+ }
+ {
+ let a1 = [, , , , , , , , , , , ,];
+ let a2 = [undefined];
+ expect(a1).toEqual(a2);
+ expect(a1).not.toStrictEqual(a2);
+ expect(a2).toEqual(a1);
+ expect(a2).not.toStrictEqual(a1);
+ }
+ {
+ const a = [1];
+ const b = [1];
+ expect(a).toEqual(b);
+ Object.preventExtensions(b);
+ expect(a).toEqual(b);
+ Object.preventExtensions(a);
+ expect(a).toEqual(b);
+ }
+ {
let o1 = { 1: 4, 6: 3 };
let o2 = { 1: 4, 6: 3 };
expect(o1).toEqual(o2);