diff options
author | 2023-04-06 14:49:07 -0700 | |
---|---|---|
committer | 2023-04-06 14:49:07 -0700 | |
commit | 2b170c9d135930f80fa86dc2f92120a70c2720d9 (patch) | |
tree | e1a47e325df71a08fd936e2c0603684101f6269e | |
parent | 1d138057cb861fe540cfe5ef49905225cee40ae8 (diff) | |
download | bun-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.cpp | 34 | ||||
-rw-r--r-- | src/bun.js/ws.exports.js | 11 | ||||
-rw-r--r-- | test/js/bun/test/test-test.test.ts | 49 |
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); |