Skip to content

Commit 1186108

Browse files
BridgeARRafaelGSS
authored andcommitted
assert,util: improve comparison performance
This makes sure that the toStringTag symbol is used, if available instead of calculating the toString() value each time, if not needed (the type checks make a brand check, so there is no need to check the toStringTag, if non is defined on the object). PR-URL: #61176 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
1 parent bd47107 commit 1186108

1 file changed

Lines changed: 60 additions & 46 deletions

File tree

‎lib/internal/util/comparisons.js‎

Lines changed: 60 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const {
4141
StringPrototypeValueOf,
4242
Symbol,
4343
SymbolPrototypeValueOf,
44+
SymbolToStringTag,
4445
TypedArrayPrototypeGetByteLength: getByteLength,
4546
TypedArrayPrototypeGetSymbolToStringTag,
4647
Uint16Array,
@@ -264,6 +265,17 @@ function innerDeepEqual(val1, val2, mode, memos) {
264265
returnobjectComparisonStart(val1,val2,mode,memos);
265266
}
266267

268+
functionhasUnequalTag(val1,val2){
269+
returnval1[SymbolToStringTag]!==val2[SymbolToStringTag];
270+
}
271+
272+
functionslowHasUnequalTag(val1Tag,val1,val2){
273+
if(val1[SymbolToStringTag]!==undefined&&val2[SymbolToStringTag]!==undefined){
274+
returnval1[SymbolToStringTag]!==val2[SymbolToStringTag];
275+
}
276+
returnval1Tag!==ObjectPrototypeToString(val2);
277+
}
278+
267279
functionobjectComparisonStart(val1,val2,mode,memos){
268280
if(mode===kStrict){
269281
if(wellKnownConstructors.has(val1.constructor)||
@@ -276,16 +288,10 @@ function objectComparisonStart(val1, val2, mode, memos) {
276288
}
277289
}
278290

279-
constval1Tag=ObjectPrototypeToString(val1);
280-
constval2Tag=ObjectPrototypeToString(val2);
281-
282-
if(val1Tag!==val2Tag){
283-
returnfalse;
284-
}
285-
286291
if(ArrayIsArray(val1)){
287292
if(!ArrayIsArray(val2)||
288-
(val1.length!==val2.length&&(mode!==kPartial||val1.length<val2.length))){
293+
(val1.length!==val2.length&&(mode!==kPartial||val1.length<val2.length))||
294+
hasUnequalTag(val1,val2)){
289295
returnfalse;
290296
}
291297

@@ -296,22 +302,29 @@ function objectComparisonStart(val1, val2, mode, memos) {
296302
returnfalse;
297303
}
298304
returnkeyCheck(val1,val2,mode,memos,kIsArray,keys2);
299-
}elseif(val1Tag==='[object Object]'){
300-
returnkeyCheck(val1,val2,mode,memos,kNoIterator);
301-
}elseif(isDate(val1)){
302-
if(!isDate(val2)){
305+
}
306+
307+
letval1Tag;
308+
if(val1[SymbolToStringTag]===undefined&&
309+
(val1Tag=ObjectPrototypeToString(val1))==='[object Object]'){
310+
if(slowHasUnequalTag(val1Tag,val1,val2)){
303311
returnfalse;
304312
}
305-
consttime1=DatePrototypeGetTime(val1);
306-
consttime2=DatePrototypeGetTime(val2);
307-
// eslint-disable-next-line no-self-compare
308-
if(time1!==time2&&(time1===time1||time2===time2)){
313+
returnkeyCheck(val1,val2,mode,memos,kNoIterator);
314+
}elseif(isSet(val1)){
315+
if(!isSet(val2)||
316+
(val1.size!==val2.size&&(mode!==kPartial||val1.size<val2.size))||
317+
hasUnequalTag(val1,val2)){
309318
returnfalse;
310319
}
311-
}elseif(isRegExp(val1)){
312-
if(!isRegExp(val2)||!areSimilarRegExps(val1,val2)){
320+
returnkeyCheck(val1,val2,mode,memos,kIsSet);
321+
}elseif(isMap(val1)){
322+
if(!isMap(val2)||
323+
(val1.size!==val2.size&&(mode!==kPartial||val1.size<val2.size))||
324+
hasUnequalTag(val1,val2)){
313325
returnfalse;
314326
}
327+
returnkeyCheck(val1,val2,mode,memos,kIsMap);
315328
}elseif(isArrayBufferView(val1)){
316329
if(TypedArrayPrototypeGetSymbolToStringTag(val1)!==
317330
TypedArrayPrototypeGetSymbolToStringTag(val2)){
@@ -339,20 +352,22 @@ function objectComparisonStart(val1, val2, mode, memos) {
339352
returnfalse;
340353
}
341354
returnkeyCheck(val1,val2,mode,memos,kNoIterator,keys2);
342-
}elseif(isSet(val1)){
343-
if(!isSet(val2)||
344-
(val1.size!==val2.size&&(mode!==kPartial||val1.size<val2.size))){
355+
}elseif(isDate(val1)){
356+
if(!isDate(val2)||hasUnequalTag(val1,val2)){
345357
returnfalse;
346358
}
347-
returnkeyCheck(val1,val2,mode,memos,kIsSet);
348-
}elseif(isMap(val1)){
349-
if(!isMap(val2)||
350-
(val1.size!==val2.size&&(mode!==kPartial||val1.size<val2.size))){
359+
consttime1=DatePrototypeGetTime(val1);
360+
consttime2=DatePrototypeGetTime(val2);
361+
// eslint-disable-next-line no-self-compare
362+
if(time1!==time2&&(time1===time1||time2===time2)){
363+
returnfalse;
364+
}
365+
}elseif(isRegExp(val1)){
366+
if(!isRegExp(val2)||!areSimilarRegExps(val1,val2)||hasUnequalTag(val1,val2)){
351367
returnfalse;
352368
}
353-
returnkeyCheck(val1,val2,mode,memos,kIsMap);
354369
}elseif(isAnyArrayBuffer(val1)){
355-
if(!isAnyArrayBuffer(val2)){
370+
if(!isAnyArrayBuffer(val2)||hasUnequalTag(val1,val2)){
356371
returnfalse;
357372
}
358373
if(mode!==kPartial||val1.byteLength===val2.byteLength){
@@ -362,6 +377,15 @@ function objectComparisonStart(val1, val2, mode, memos) {
362377
}elseif(!isPartialUint8Array(newUint8Array(val1),newUint8Array(val2))){
363378
returnfalse;
364379
}
380+
}elseif(slowHasUnequalTag(val1Tag??ObjectPrototypeToString(val1),val1,val2)||
381+
ArrayIsArray(val2)||
382+
isArrayBufferView(val2)||
383+
isSet(val2)||
384+
isMap(val2)||
385+
isDate(val2)||
386+
isRegExp(val2)||
387+
isAnyArrayBuffer(val2)){
388+
returnfalse;
365389
}elseif(isError(val1)){
366390
// Do not compare the stack as it might differ even though the error itself
367391
// is otherwise identical.
@@ -380,17 +404,6 @@ function objectComparisonStart(val1, val2, mode, memos) {
380404
if(!isEqualBoxedPrimitive(val1,val2)){
381405
returnfalse;
382406
}
383-
}elseif(ArrayIsArray(val2)||
384-
isArrayBufferView(val2)||
385-
isSet(val2)||
386-
isMap(val2)||
387-
isDate(val2)||
388-
isRegExp(val2)||
389-
isAnyArrayBuffer(val2)||
390-
isBoxedPrimitive(val2)||
391-
isNativeError(val2)||
392-
val2instanceofError){
393-
returnfalse;
394407
}elseif(isURL(val1)){
395408
if(!isURL(val2)||val1.href!==val2.href){
396409
returnfalse;
@@ -412,7 +425,12 @@ function objectComparisonStart(val1, val2, mode, memos) {
412425
){
413426
returnfalse;
414427
}
415-
}elseif(isWeakMap(val1)||isWeakSet(val1)||isPromise(val1)){
428+
}elseif(isBoxedPrimitive(val2)||
429+
isNativeError(val2)||
430+
val2instanceofError||
431+
isWeakMap(val1)||
432+
isWeakSet(val1)||
433+
isPromise(val1)){
416434
returnfalse;
417435
}
418436

@@ -879,18 +897,16 @@ function partialSparseArrayEquiv(a, b, mode, memos, startA, startB) {
879897
letaPos=startA;
880898
constkeysA=ObjectKeys(a);
881899
constkeysB=ObjectKeys(b);
882-
constkeysBLength=keysB.length;
883-
constkeysALength=keysA.length;
884-
constlenA=keysALength-startA;
885-
constlenB=keysBLength-startB;
900+
constlenA=keysA.length-startA;
901+
constlenB=keysB.length-startB;
886902
if(lenA<lenB){
887903
returnfalse;
888904
}
889905
for(leti=0;i<lenB;i++){
890906
constkeyB=keysB[startB+i];
891907
while(!innerDeepEqual(a[keysA[aPos]],b[keyB],mode,memos)){
892908
aPos++;
893-
if(aPos>keysALength-lenB+i){
909+
if(aPos>keysA.length-lenB+i){
894910
returnfalse;
895911
}
896912
}
@@ -922,8 +938,6 @@ function partialArrayEquiv(a, b, mode, memos) {
922938
}
923939

924940
functionsparseArrayEquiv(a,b,mode,memos,i){
925-
// TODO(BridgeAR): Use internal method to only get index properties. The
926-
// same applies to the partial implementation.
927941
constkeysA=ObjectKeys(a);
928942
constkeysB=ObjectKeys(b);
929943
if(keysA.length!==keysB.length){

0 commit comments

Comments
 (0)