This commit is contained in:
pfg
2025-05-12 18:57:46 -07:00
parent 5f05a8ba74
commit 5e864d6e47
2 changed files with 53 additions and 32 deletions

View File

@@ -804,12 +804,12 @@ bool Bun__deepEquals(JSC::JSGlobalObject* globalObject, JSValue v1, JSValue v2,
}
// Handle boxed primitives
auto v1IsPrimitive = v1.inherits<NumberObject>() || v1.inherits<StringObject>() || v1.inherits<BooleanObject>() || v1.inherits<BigIntObject>() || v1.inherits<SymbolObject>();
auto v2IsPrimitive = v2.inherits<NumberObject>() || v2.inherits<StringObject>() || v2.inherits<BooleanObject>() || v2.inherits<BigIntObject>() || v2.inherits<SymbolObject>();
if (v1IsPrimitive != v2IsPrimitive) {
auto v1IsBoxedPrimitive = v1.inherits<NumberObject>() || v1.inherits<StringObject>() || v1.inherits<BooleanObject>() || v1.inherits<BigIntObject>() || v1.inherits<SymbolObject>();
auto v2IsBoxedPrimitive = v2.inherits<NumberObject>() || v2.inherits<StringObject>() || v2.inherits<BooleanObject>() || v2.inherits<BigIntObject>() || v2.inherits<SymbolObject>();
if (v1IsBoxedPrimitive != v2IsBoxedPrimitive) {
return false; // one is a boxed primitive, the other is not
}
if (v1IsPrimitive && v2IsPrimitive) {
if (v1IsBoxedPrimitive && v2IsBoxedPrimitive) {
auto v1Wrapper = jsCast<JSC::JSWrapperObject*>(v1);
auto v2Wrapper = jsCast<JSC::JSWrapperObject*>(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()) {

View File

@@ -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
});
});
}