Skip to content

Commit 1c7396b

Browse files
BridgeARaduh95
authored andcommitted
assert,util: fix constructor lookup in deep equal comparison
The latest performance optimization did not take into account that an object may have a property called constructor. This is addressed in this PR by adding a new fast path and using fallbacks. PR-URL: #57876 Reviewed-By: Jordan Harband <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
1 parent 010dd91 commit 1c7396b

File tree

4 files changed

+335
-45
lines changed

4 files changed

+335
-45
lines changed

benchmark/assert/deepequal-prims-and-objs-big-loop.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const notCircular = {};
1010
notCircular.circular = {};
1111

1212
const primValues = {
13+
'null_prototype': { __proto__: null },
1314
'string': 'abcdef',
1415
'number': 1_000,
1516
'boolean': true,
@@ -24,6 +25,7 @@ const primValues = {
2425
};
2526

2627
const primValues2 = {
28+
'null_prototype': { __proto__: null },
2729
'object': { property: 'abcdef' },
2830
'array': [1, 2, 3],
2931
'set_object': new Set([[1]]),
@@ -35,6 +37,7 @@ const primValues2 = {
3537
};
3638

3739
const primValuesUnequal = {
40+
'null_prototype': { __proto__: { __proto__: null } },
3841
'string': 'abcdez',
3942
'number': 1_001,
4043
'boolean': false,

lib/internal/util/comparisons.js

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,31 @@
11
'use strict';
22

33
const {
4+
Array,
5+
ArrayBuffer,
46
ArrayIsArray,
57
ArrayPrototypeFilter,
68
ArrayPrototypePush,
9+
BigInt,
10+
BigInt64Array,
711
BigIntPrototypeValueOf,
12+
BigUint64Array,
13+
Boolean,
814
BooleanPrototypeValueOf,
15+
DataView,
16+
Date,
917
DatePrototypeGetTime,
1018
Error,
19+
Float32Array,
20+
Float64Array,
21+
Function,
22+
Int16Array,
23+
Int32Array,
24+
Int8Array,
25+
Map,
26+
Number,
1127
NumberPrototypeValueOf,
28+
Object,
1229
ObjectGetOwnPropertyDescriptor,
1330
ObjectGetOwnPropertySymbols: getOwnSymbols,
1431
ObjectGetPrototypeOf,
@@ -17,18 +34,67 @@ const {
1734
ObjectPrototypeHasOwnProperty: hasOwn,
1835
ObjectPrototypePropertyIsEnumerable: hasEnumerable,
1936
ObjectPrototypeToString,
37+
Promise,
38+
RegExp,
2039
SafeSet,
40+
Set,
41+
String,
2142
StringPrototypeValueOf,
43+
Symbol,
2244
SymbolPrototypeValueOf,
2345
TypedArrayPrototypeGetByteLength: getByteLength,
2446
TypedArrayPrototypeGetSymbolToStringTag,
47+
Uint16Array,
48+
Uint32Array,
2549
Uint8Array,
50+
Uint8ClampedArray,
51+
WeakMap,
52+
WeakSet,
53+
globalThis: { Float16Array },
2654
} = primordials;
2755

2856
const { compare } = internalBinding('buffer');
2957
const assert = require('internal/assert');
3058
const { isError } = require('internal/util');
3159
const { isURL } = require('internal/url');
60+
const { Buffer } = require('buffer');
61+
62+
const wellKnownConstructors = new SafeSet()
63+
.add(Array)
64+
.add(ArrayBuffer)
65+
.add(BigInt)
66+
.add(BigInt64Array)
67+
.add(BigUint64Array)
68+
.add(Boolean)
69+
.add(Buffer)
70+
.add(DataView)
71+
.add(Date)
72+
.add(Error)
73+
.add(Float32Array)
74+
.add(Float64Array)
75+
.add(Function)
76+
.add(Int16Array)
77+
.add(Int32Array)
78+
.add(Int8Array)
79+
.add(Map)
80+
.add(Number)
81+
.add(Object)
82+
.add(Promise)
83+
.add(RegExp)
84+
.add(Set)
85+
.add(String)
86+
.add(Symbol)
87+
.add(Uint16Array)
88+
.add(Uint32Array)
89+
.add(Uint8Array)
90+
.add(Uint8ClampedArray)
91+
.add(WeakMap)
92+
.add(WeakSet);
93+
94+
if (Float16Array) { // TODO(BridgeAR): Remove when regularly supported
95+
wellKnownConstructors.add(Float16Array);
96+
}
97+
3298
const types = require('internal/util/types');
3399
const {
34100
isAnyArrayBuffer,
@@ -199,11 +265,15 @@ function innerDeepEqual(val1, val2, mode, memos) {
199265
}
200266

201267
function objectComparisonStart(val1, val2, mode, memos) {
202-
if (mode === kStrict &&
203-
(val1.constructor !== val2.constructor ||
204-
(val1.constructor === undefined &&
205-
ObjectGetPrototypeOf(val1) !== ObjectGetPrototypeOf(val2)))) {
206-
return false;
268+
if (mode === kStrict) {
269+
if (wellKnownConstructors.has(val1.constructor) ||
270+
(val1.constructor !== undefined && !hasOwn(val1, 'constructor'))) {
271+
if (val1.constructor !== val2.constructor) {
272+
return false;
273+
}
274+
} else if (ObjectGetPrototypeOf(val1) !== ObjectGetPrototypeOf(val2)) {
275+
return false;
276+
}
207277
}
208278

209279
const val1Tag = ObjectPrototypeToString(val1);

lib/internal/util/inspect.js

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,18 @@ const {
2020
ArrayPrototypeSplice,
2121
ArrayPrototypeUnshift,
2222
BigIntPrototypeValueOf,
23+
Boolean,
24+
BooleanPrototype,
2325
BooleanPrototypeValueOf,
26+
DataView,
27+
DataViewPrototype,
28+
Date,
29+
DatePrototype,
2430
DatePrototypeGetTime,
2531
DatePrototypeToISOString,
2632
DatePrototypeToString,
33+
Error,
34+
ErrorPrototype,
2735
ErrorPrototypeToString,
2836
Function,
2937
FunctionPrototype,
@@ -47,6 +55,7 @@ const {
4755
NumberIsNaN,
4856
NumberParseFloat,
4957
NumberParseInt,
58+
NumberPrototype,
5059
NumberPrototypeToString,
5160
NumberPrototypeValueOf,
5261
Object,
@@ -63,9 +72,12 @@ const {
6372
ObjectPrototypePropertyIsEnumerable,
6473
ObjectSeal,
6574
ObjectSetPrototypeOf,
75+
Promise,
76+
PromisePrototype,
6677
ReflectApply,
6778
ReflectOwnKeys,
6879
RegExp,
80+
RegExpPrototype,
6981
RegExpPrototypeExec,
7082
RegExpPrototypeSymbolReplace,
7183
RegExpPrototypeSymbolSplit,
@@ -78,6 +90,7 @@ const {
7890
SetPrototypeGetSize,
7991
SetPrototypeValues,
8092
String,
93+
StringPrototype,
8194
StringPrototypeCharCodeAt,
8295
StringPrototypeCodePointAt,
8396
StringPrototypeEndsWith,
@@ -106,6 +119,10 @@ const {
106119
TypedArrayPrototypeGetLength,
107120
TypedArrayPrototypeGetSymbolToStringTag,
108121
Uint8Array,
122+
WeakMap,
123+
WeakMapPrototype,
124+
WeakSet,
125+
WeakSetPrototype,
109126
globalThis,
110127
uncurryThis,
111128
} = primordials;
@@ -608,21 +625,31 @@ function isInstanceof(object, proto) {
608625
}
609626

610627
// Special-case for some builtin prototypes in case their `constructor` property has been tampered.
611-
const wellKnownPrototypes = new SafeMap();
612-
wellKnownPrototypes.set(ArrayPrototype, { name: 'Array', constructor: Array });
613-
wellKnownPrototypes.set(ArrayBufferPrototype, { name: 'ArrayBuffer', constructor: ArrayBuffer });
614-
wellKnownPrototypes.set(FunctionPrototype, { name: 'Function', constructor: Function });
615-
wellKnownPrototypes.set(MapPrototype, { name: 'Map', constructor: Map });
616-
wellKnownPrototypes.set(ObjectPrototype, { name: 'Object', constructor: Object });
617-
wellKnownPrototypes.set(SetPrototype, { name: 'Set', constructor: Set });
618-
wellKnownPrototypes.set(TypedArrayPrototype, { name: 'TypedArray', constructor: TypedArray });
628+
const wellKnownPrototypes = new SafeMap()
629+
.set(ArrayPrototype, { name: 'Array', constructor: Array })
630+
.set(ArrayBufferPrototype, { name: 'ArrayBuffer', constructor: ArrayBuffer })
631+
.set(FunctionPrototype, { name: 'Function', constructor: Function })
632+
.set(MapPrototype, { name: 'Map', constructor: Map })
633+
.set(SetPrototype, { name: 'Set', constructor: Set })
634+
.set(ObjectPrototype, { name: 'Object', constructor: Object })
635+
.set(TypedArrayPrototype, { name: 'TypedArray', constructor: TypedArray })
636+
.set(RegExpPrototype, { name: 'RegExp', constructor: RegExp })
637+
.set(DatePrototype, { name: 'Date', constructor: Date })
638+
.set(DataViewPrototype, { name: 'DataView', constructor: DataView })
639+
.set(ErrorPrototype, { name: 'Error', constructor: Error })
640+
.set(BooleanPrototype, { name: 'Boolean', constructor: Boolean })
641+
.set(NumberPrototype, { name: 'Number', constructor: Number })
642+
.set(StringPrototype, { name: 'String', constructor: String })
643+
.set(PromisePrototype, { name: 'Promise', constructor: Promise })
644+
.set(WeakMapPrototype, { name: 'WeakMap', constructor: WeakMap })
645+
.set(WeakSetPrototype, { name: 'WeakSet', constructor: WeakSet });
619646

620647
function getConstructorName(obj, ctx, recurseTimes, protoProps) {
621648
let firstProto;
622649
const tmp = obj;
623650
while (obj || isUndetectableObject(obj)) {
624651
const wellKnownPrototypeNameAndConstructor = wellKnownPrototypes.get(obj);
625-
if (wellKnownPrototypeNameAndConstructor != null) {
652+
if (wellKnownPrototypeNameAndConstructor !== undefined) {
626653
const { name, constructor } = wellKnownPrototypeNameAndConstructor;
627654
if (FunctionPrototypeSymbolHasInstance(constructor, tmp)) {
628655
if (protoProps !== undefined && firstProto !== obj) {

0 commit comments

Comments
 (0)