diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index 3772c4c2c3..d28c6f79e9 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -804,12 +804,12 @@ bool Bun__deepEquals(JSC::JSGlobalObject* globalObject, JSValue v1, JSValue v2, } // Handle boxed primitives - auto v1IsPrimitive = v1.inherits() || v1.inherits() || v1.inherits() || v1.inherits() || v1.inherits(); - auto v2IsPrimitive = v2.inherits() || v2.inherits() || v2.inherits() || v2.inherits() || v2.inherits(); - if (v1IsPrimitive != v2IsPrimitive) { + auto v1IsBoxedPrimitive = v1.inherits() || v1.inherits() || v1.inherits() || v1.inherits() || v1.inherits(); + auto v2IsBoxedPrimitive = v2.inherits() || v2.inherits() || v2.inherits() || v2.inherits() || v2.inherits(); + if (v1IsBoxedPrimitive != v2IsBoxedPrimitive) { return false; // one is a boxed primitive, the other is not } - if (v1IsPrimitive && v2IsPrimitive) { + if (v1IsBoxedPrimitive && v2IsBoxedPrimitive) { auto v1Wrapper = jsCast(v1); auto v2Wrapper = jsCast(v2); auto v1Value = v1Wrapper->internalValue(); @@ -872,7 +872,21 @@ bool Bun__deepEquals(JSC::JSGlobalObject* globalObject, JSValue v1, JSValue v2, count++; JSValue left = o1->getDirect(entry.offset()); - JSValue right = o2->getDirect(vm, JSC::PropertyName(entry.key())); + Structure* rightStructure = o2->structure(); + + PropertyOffset rightOffset = rightStructure->get(vm, JSC::PropertyName(entry.key())); + checkOffset(rightOffset, rightStructure->inlineCapacity()); + JSValue right = rightOffset != invalidOffset ? o2->getDirect(rightOffset) : JSValue(); + + PropertySlot slot(o2, PropertySlot::InternalMethodType::GetOwnProperty); + bool hasProperty = o2->getPropertySlot(globalObject, JSC::PropertyName(entry.key()), slot); + RETURN_IF_EXCEPTION(*scope, false); + if (!hasProperty) { + // TODO + } + bool isEnumerable = !(slot.attributes() & PropertyAttribute::DontEnum) || (slot.slotBase() && slot.slotBase()->structure()->typeInfo().getOwnPropertySlotMayBeWrongAboutDontEnum()); + JSValue property = slot.getValue(); + // have to get attributes and check for DontEnum; in that case, ignore. if constexpr (!isStrict) { if (left.isUndefined() && right.isEmpty()) { diff --git a/test/js/bun/test/expect-boxed.test.js b/test/js/bun/test/expect-boxed.test.js index 1510482b95..94f2ecedd1 100644 --- a/test/js/bun/test/expect-boxed.test.js +++ b/test/js/bun/test/expect-boxed.test.js @@ -5,30 +5,37 @@ test("boxed number", () => { test("boxed symbol", () => { expect(Object(Symbol())).not.toEqual(Object(Symbol())); }); -const util = require("util"); -const sym = Symbol(); -const obj1 = { [sym]: 1 }; -const obj4 = {}; -Object.defineProperty(obj4, sym, { value: 1 }); // non-enumerable -test("symbol key", () => { - expect(obj1).not.toEqual(obj4); - expect(util.isDeepStrictEqual(obj1, obj4)).toBe(false); - expect(Bun.deepEquals(obj1, obj4)).toBe(false); - expect(Bun.deepEquals(obj1, obj4, false)).toBe(false); - expect(Bun.deepEquals(obj1, obj4, true)).toBe(false); - expect(obj4).not.toEqual(obj1); - expect(util.isDeepStrictEqual(obj4, obj1)).toBe(false); - expect(Bun.deepEquals(obj4, obj1)).toBe(false); - expect(Bun.deepEquals(obj4, obj1, false)).toBe(false); - expect(Bun.deepEquals(obj4, obj1, true)).toBe(false); -}); -test("symbol key 2", () => { - const obj1 = {}; - const obj2 = {}; - Object.defineProperty(obj2, sym, { value: 1 }); - expect(util.isDeepStrictEqual(obj1, obj2)).toBe(true); - expect(util.isDeepStrictEqual(obj2, obj1)).toBe(true); - obj1[sym] = 1; - expect(util.isDeepStrictEqual(obj1, obj2)).toBe(false); - expect(util.isDeepStrictEqual(obj2, obj1)).toBe(false); -}); +for (const key of [Symbol(), "abc"]) { + describe(key === "abc" ? "string key" : "symbol key", () => { + const util = require("util"); + const sym = Symbol(); + const obj1 = {}; + const obj4 = {}; + Object.defineProperty(obj1, sym, { value: 1, enumerable: true }); + Object.defineProperty(obj4, sym, { value: 1, enumerable: false }); + test("enumerable 1", () => { + expect(obj1).not.toEqual(obj4); + expect(util.isDeepStrictEqual(obj1, obj4)).toBe(false); + expect(Bun.deepEquals(obj1, obj4)).toBe(false); + expect(Bun.deepEquals(obj1, obj4, false)).toBe(false); + expect(Bun.deepEquals(obj1, obj4, true)).toBe(false); + expect(obj4).not.toEqual(obj1); + expect(util.isDeepStrictEqual(obj4, obj1)).toBe(false); + expect(Bun.deepEquals(obj4, obj1)).toBe(false); + expect(Bun.deepEquals(obj4, obj1, false)).toBe(false); + expect(Bun.deepEquals(obj4, obj1, true)).toBe(false); + }); + test("enumerable 2", () => { + const obj1 = {}; + const obj2 = {}; + Object.defineProperty(obj2, sym, { value: 1 }); + expect(util.isDeepStrictEqual(obj1, obj2)).toBe(true); + expect(util.isDeepStrictEqual(obj2, obj1)).toBe(true); + obj1[sym] = 1; + expect(util.isDeepStrictEqual(obj1, obj2)).toBe(false); + expect(util.isDeepStrictEqual(obj2, obj1)).toBe(false); + + // handle check hasNonReifiedStaticProperties/!canPerformFastPropertyEnumeration case + }); + }); +}