@@ -555,6 +555,35 @@ function partialObjectSetEquiv(a, b, mode, set, memo) { |
555 | 555 | } |
556 | 556 | } |
557 | 557 |
|
| 558 | +function setObjectEquiv(a, b, mode, set, memo) { |
| 559 | + if (mode === kPartial) { |
| 560 | + return partialObjectSetEquiv(a, b, mode, set, memo); |
| 561 | + } |
| 562 | + // Fast path for objects only |
| 563 | + if (mode === kStrict && set.size === a.size) { |
| 564 | + for (const val of a) { |
| 565 | + if (!setHasEqualElement(set, val, mode, memo)) { |
| 566 | + return false; |
| 567 | + } |
| 568 | + } |
| 569 | + return true; |
| 570 | + } |
| 571 | + |
| 572 | + for (const val of a) { |
| 573 | + // Primitive values have already been handled above. |
| 574 | + if (typeof val === 'object') { |
| 575 | + if (!b.has(val) && !setHasEqualElement(set, val, mode, memo)) { |
| 576 | + return false; |
| 577 | + } |
| 578 | + } else if (mode === kLoose && |
| 579 | + !b.has(val) && |
| 580 | + !setHasEqualElement(set, val, mode, memo)) { |
| 581 | + return false; |
| 582 | + } |
| 583 | + } |
| 584 | + return set.size === 0; |
| 585 | +} |
| 586 | + |
558 | 587 | function setEquiv(a, b, mode, memo) { |
559 | 588 | // This is a lazily initiated Set of entries which have to be compared |
560 | 589 | // pairwise. |
@@ -580,22 +609,7 @@ function setEquiv(a, b, mode, memo) { |
580 | 609 | } |
581 | 610 |
|
582 | 611 | if (set !== null) { |
583 | | - if (mode === kPartial) { |
584 | | - return partialObjectSetEquiv(a, b, mode, set, memo); |
585 | | - } |
586 | | - for (const val of a) { |
587 | | - // Primitive values have already been handled above. |
588 | | - if (typeof val === 'object' && val !== null) { |
589 | | - if (!b.has(val) && !setHasEqualElement(set, val, mode, memo)) { |
590 | | - return false; |
591 | | - } |
592 | | - } else if (mode === kLoose && |
593 | | - !b.has(val) && |
594 | | - !setHasEqualElement(set, val, mode, memo)) { |
595 | | - return false; |
596 | | - } |
597 | | - } |
598 | | - return set.size === 0; |
| 612 | + return setObjectEquiv(a, b, mode, set, memo); |
599 | 613 | } |
600 | 614 |
|
601 | 615 | return true; |
@@ -636,6 +650,35 @@ function partialObjectMapEquiv(a, b, mode, set, memo) { |
636 | 650 | } |
637 | 651 | } |
638 | 652 |
|
| 653 | +function mapObjectEquivalence(a, b, mode, set, memo) { |
| 654 | + if (mode === kPartial) { |
| 655 | + return partialObjectMapEquiv(a, b, mode, set, memo); |
| 656 | + } |
| 657 | + // Fast path for objects only |
| 658 | + if (mode === kStrict && set.size === a.size) { |
| 659 | + for (const { 0: key1, 1: item1 } of a) { |
| 660 | + if (!mapHasEqualEntry(set, b, key1, item1, mode, memo)) { |
| 661 | + return false; |
| 662 | + } |
| 663 | + } |
| 664 | + return true; |
| 665 | + } |
| 666 | + for (const { 0: key1, 1: item1 } of a) { |
| 667 | + if (typeof key1 === 'object' && key1 !== null) { |
| 668 | + if (!mapHasEqualEntry(set, b, key1, item1, mode, memo)) |
| 669 | + return false; |
| 670 | + } else if (set.size === 0) { |
| 671 | + return true; |
| 672 | + } else if (mode === kLoose && |
| 673 | + (!b.has(key1) || |
| 674 | + !innerDeepEqual(item1, b.get(key1), mode, memo)) && |
| 675 | + !mapHasEqualEntry(set, b, key1, item1, mode, memo)) { |
| 676 | + return false; |
| 677 | + } |
| 678 | + } |
| 679 | + return set.size === 0; |
| 680 | +} |
| 681 | + |
639 | 682 | function mapEquiv(a, b, mode, memo) { |
640 | 683 | let set = null; |
641 | 684 |
|
@@ -671,21 +714,7 @@ function mapEquiv(a, b, mode, memo) { |
671 | 714 | } |
672 | 715 |
|
673 | 716 | if (set !== null) { |
674 | | - if (mode === kPartial) { |
675 | | - return partialObjectMapEquiv(a, b, mode, set, memo); |
676 | | - } |
677 | | - for (const { 0: key1, 1: item1 } of a) { |
678 | | - if (typeof key1 === 'object' && key1 !== null) { |
679 | | - if (!mapHasEqualEntry(set, b, key1, item1, mode, memo)) |
680 | | - return false; |
681 | | - } else if (mode === kLoose && |
682 | | - (!b.has(key1) || |
683 | | - !innerDeepEqual(item1, b.get(key1), mode, memo)) && |
684 | | - !mapHasEqualEntry(set, b, key1, item1, mode, memo)) { |
685 | | - return false; |
686 | | - } |
687 | | - } |
688 | | - return set.size === 0; |
| 717 | + return mapObjectEquivalence(a, b, mode, set, memo); |
689 | 718 | } |
690 | 719 |
|
691 | 720 | return true; |
@@ -733,6 +762,24 @@ function partialArrayEquiv(a, b, mode, memos) { |
733 | 762 | return true; |
734 | 763 | } |
735 | 764 |
|
| 765 | +function sparseArrayEquiv(a, b, mode, memos, i) { |
| 766 | + // TODO(BridgeAR): Use internal method to only get index properties. The |
| 767 | + // same applies to the partial implementation. |
| 768 | + const keysA = ObjectKeys(a); |
| 769 | + const keysB = ObjectKeys(b); |
| 770 | + if (keysA.length !== keysB.length) { |
| 771 | + return false; |
| 772 | + } |
| 773 | + for (; i < keysA.length; i++) { |
| 774 | + const key = keysA[i]; |
| 775 | + if (!ObjectPrototypeHasOwnProperty(b, key) || |
| 776 | + !innerDeepEqual(a[key], b[key], mode, memos)) { |
| 777 | + return false; |
| 778 | + } |
| 779 | + } |
| 780 | + return true; |
| 781 | +} |
| 782 | + |
736 | 783 | function objEquiv(a, b, mode, keys2, memos, iterationType) { |
737 | 784 | // The pair must have equivalent values for every corresponding key. |
738 | 785 | if (keys2.length > 0) { |
@@ -751,23 +798,13 @@ function objEquiv(a, b, mode, keys2, memos, iterationType) { |
751 | 798 | if (!innerDeepEqual(a[i], b[i], mode, memos)) { |
752 | 799 | return false; |
753 | 800 | } |
754 | | - const isOwnProperty = ObjectPrototypeHasOwnProperty(a, i); |
755 | | - if (isOwnProperty !== ObjectPrototypeHasOwnProperty(b, i)) { |
| 801 | + const isSparseA = a[i] === undefined && !ObjectPrototypeHasOwnProperty(a, i); |
| 802 | + const isSparseB = b[i] === undefined && !ObjectPrototypeHasOwnProperty(b, i); |
| 803 | + if (isSparseA !== isSparseB) { |
756 | 804 | return false; |
757 | 805 | } |
758 | | - if (!isOwnProperty) { |
759 | | - // Array is sparse. |
760 | | - // TODO(BridgeAR): Use internal method to only get index properties. The |
761 | | - // same applies to the partial implementation. |
762 | | - const keysA = ObjectKeys(a); |
763 | | - for (; i < keysA.length; i++) { |
764 | | - const key = keysA[i]; |
765 | | - if (!ObjectPrototypeHasOwnProperty(b, key) || |
766 | | - !innerDeepEqual(a[key], b[key], mode, memos)) { |
767 | | - return false; |
768 | | - } |
769 | | - } |
770 | | - return keysA.length === ObjectKeys(b).length; |
| 806 | + if (isSparseA) { |
| 807 | + return sparseArrayEquiv(a, b, mode, memos, i); |
771 | 808 | } |
772 | 809 | } |
773 | 810 | } else if (iterationType === kIsSet) { |
|