Skip to content

Commit 3b9360d

Browse files
BridgeARMylesBorins
authored andcommitted
util: refactor inspect code for constistency
This removes the special handling to inspect iterable objects with a null prototype. It is now handled together with the regular prototype. Backport-PR-URL: #31431 PR-URL: #30225 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
1 parent b0e3aec commit 3b9360d

2 files changed

Lines changed: 80 additions & 92 deletions

File tree

‎lib/internal/util/inspect.js‎

Lines changed: 67 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const {
1111
ErrorPrototypeToString,
1212
JSONStringify,
1313
Map,
14+
MapPrototype,
1415
MapPrototypeEntries,
1516
MathFloor,
1617
MathMax,
@@ -22,10 +23,8 @@ const {
2223
NumberPrototypeValueOf,
2324
ObjectAssign,
2425
ObjectCreate,
25-
ObjectDefineProperties,
2626
ObjectDefineProperty,
2727
ObjectGetOwnPropertyDescriptor,
28-
ObjectGetOwnPropertyDescriptors,
2928
ObjectGetOwnPropertyNames,
3029
ObjectGetOwnPropertySymbols,
3130
ObjectGetPrototypeOf,
@@ -36,6 +35,7 @@ const {
3635
ObjectSeal,
3736
RegExpPrototypeToString,
3837
Set,
38+
SetPrototype,
3939
SetPrototypeValues,
4040
StringPrototypeValueOf,
4141
SymbolPrototypeToString,
@@ -115,6 +115,11 @@ const assert = require('internal/assert');
115115

116116
const{ NativeModule }=require('internal/bootstrap/loaders');
117117

118+
constsetSizeGetter=uncurryThis(
119+
ObjectGetOwnPropertyDescriptor(SetPrototype,'size').get);
120+
constmapSizeGetter=uncurryThis(
121+
ObjectGetOwnPropertyDescriptor(MapPrototype,'size').get);
122+
118123
lethexSlice;
119124

120125
constbuiltInObjects=newSet(
@@ -648,51 +653,6 @@ function findTypedConstructor(value) {
648653
}
649654
}
650655

651-
letlazyNullPrototypeCache;
652-
// Creates a subclass and name
653-
// the constructor as `${clazz} : null prototype`
654-
functionclazzWithNullPrototype(clazz,name){
655-
if(lazyNullPrototypeCache===undefined){
656-
lazyNullPrototypeCache=newMap();
657-
}else{
658-
constcachedClass=lazyNullPrototypeCache.get(clazz);
659-
if(cachedClass!==undefined){
660-
returncachedClass;
661-
}
662-
}
663-
classNullPrototypeextendsclazz{
664-
get[SymbolToStringTag](){
665-
return'';
666-
}
667-
}
668-
ObjectDefineProperty(NullPrototype.prototype.constructor,'name',
669-
{value: `[${name}: null prototype]`});
670-
lazyNullPrototypeCache.set(clazz,NullPrototype);
671-
returnNullPrototype;
672-
}
673-
674-
functionnoPrototypeIterator(ctx,value,recurseTimes){
675-
letnewVal;
676-
if(isSet(value)){
677-
constclazz=clazzWithNullPrototype(Set,'Set');
678-
newVal=newclazz(SetPrototypeValues(value));
679-
}elseif(isMap(value)){
680-
constclazz=clazzWithNullPrototype(Map,'Map');
681-
newVal=newclazz(MapPrototypeEntries(value));
682-
}elseif(ArrayIsArray(value)){
683-
constclazz=clazzWithNullPrototype(Array,'Array');
684-
newVal=newclazz(value.length);
685-
}elseif(isTypedArray(value)){
686-
constconstructor=findTypedConstructor(value);
687-
constclazz=clazzWithNullPrototype(constructor,constructor.name);
688-
newVal=newclazz(value);
689-
}
690-
if(newVal!==undefined){
691-
ObjectDefineProperties(newVal,ObjectGetOwnPropertyDescriptors(value));
692-
returnformatRaw(ctx,newVal,recurseTimes);
693-
}
694-
}
695-
696656
// Note: using `formatValue` directly requires the indentation level to be
697657
// corrected by setting `ctx.indentationLvL += diff` and then to decrease the
698658
// value afterwards again.
@@ -795,7 +755,9 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
795755
letextrasType=kObjectType;
796756

797757
// Iterators and the rest are split to reduce checks.
798-
if(value[SymbolIterator]){
758+
// We have to check all values in case the constructor is set to null.
759+
// Otherwise it would not possible to identify all types properly.
760+
if(value[SymbolIterator]||constructor===null){
799761
noIterator=false;
800762
if(ArrayIsArray(value)){
801763
keys=getOwnNonIndexProperties(value,filter);
@@ -807,37 +769,66 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
807769
extrasType=kArrayExtrasType;
808770
formatter=formatArray;
809771
}elseif(isSet(value)){
772+
constsize=setSizeGetter(value);
810773
keys=getKeys(value,ctx.showHidden);
811-
constprefix=getPrefix(constructor,tag,'Set');
812-
if(value.size===0&&keys.length===0&&protoProps===undefined)
774+
letprefix='';
775+
if(constructor!==null){
776+
if(constructor===tag)
777+
tag='';
778+
prefix=getPrefix(`${constructor}`,tag,'');
779+
formatter=formatSet.bind(null,value,size);
780+
}else{
781+
prefix=getPrefix(constructor,tag,'Set');
782+
formatter=formatSet.bind(null,SetPrototypeValues(value),size);
783+
}
784+
if(size===0&&keys.length===0&&protoProps===undefined)
813785
return`${prefix}{}`;
814786
braces=[`${prefix}{`,'}'];
815-
formatter=formatSet;
816787
}elseif(isMap(value)){
788+
constsize=mapSizeGetter(value);
817789
keys=getKeys(value,ctx.showHidden);
818-
constprefix=getPrefix(constructor,tag,'Map');
819-
if(value.size===0&&keys.length===0&&protoProps===undefined)
790+
letprefix='';
791+
if(constructor!==null){
792+
if(constructor===tag)
793+
tag='';
794+
prefix=getPrefix(`${constructor}`,tag,'');
795+
formatter=formatMap.bind(null,value,size);
796+
}else{
797+
prefix=getPrefix(constructor,tag,'Map');
798+
formatter=formatMap.bind(null,MapPrototypeEntries(value),size);
799+
}
800+
if(size===0&&keys.length===0&&protoProps===undefined)
820801
return`${prefix}{}`;
821802
braces=[`${prefix}{`,'}'];
822-
formatter=formatMap;
823803
}elseif(isTypedArray(value)){
824804
keys=getOwnNonIndexProperties(value,filter);
825-
constprefix=constructor!==null ?
826-
getPrefix(constructor,tag) :
827-
getPrefix(constructor,tag,findTypedConstructor(value).name);
805+
letbound=value;
806+
letprefix='';
807+
if(constructor===null){
808+
constconstr=findTypedConstructor(value);
809+
prefix=getPrefix(constructor,tag,constr.name);
810+
// Reconstruct the array information.
811+
bound=newconstr(value);
812+
}else{
813+
prefix=getPrefix(constructor,tag);
814+
}
828815
braces=[`${prefix}[`,']'];
829816
if(value.length===0&&keys.length===0&&!ctx.showHidden)
830817
return`${braces[0]}]`;
831-
formatter=formatTypedArray;
818+
// Special handle the value. The original value is required below. The
819+
// bound function is required to reconstruct missing information.
820+
formatter=formatTypedArray.bind(null,bound);
832821
extrasType=kArrayExtrasType;
833822
}elseif(isMapIterator(value)){
834823
keys=getKeys(value,ctx.showHidden);
835824
braces=getIteratorBraces('Map',tag);
836-
formatter=formatIterator;
825+
// Add braces to the formatter parameters.
826+
formatter=formatIterator.bind(null,braces);
837827
}elseif(isSetIterator(value)){
838828
keys=getKeys(value,ctx.showHidden);
839829
braces=getIteratorBraces('Set',tag);
840-
formatter=formatIterator;
830+
// Add braces to the formatter parameters.
831+
formatter=formatIterator.bind(null,braces);
841832
}else{
842833
noIterator=true;
843834
}
@@ -915,36 +906,20 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
915906
formatter=ctx.showHidden ? formatWeakMap : formatWeakCollection;
916907
}elseif(isModuleNamespaceObject(value)){
917908
braces[0]=`[${tag}] {`;
918-
formatter=formatNamespaceObject;
909+
// Special handle keys for namespace objects.
910+
formatter=formatNamespaceObject.bind(null,keys);
919911
}elseif(isBoxedPrimitive(value)){
920912
base=getBoxedBase(value,ctx,keys,constructor,tag);
921913
if(keys.length===0&&protoProps===undefined){
922914
returnbase;
923915
}
924916
}else{
925-
// The input prototype got manipulated. Special handle these. We have to
926-
// rebuild the information so we are able to display everything.
927-
if(constructor===null){
928-
constspecialIterator=noPrototypeIterator(ctx,value,recurseTimes);
929-
if(specialIterator){
930-
returnspecialIterator;
931-
}
932-
}
933-
if(isMapIterator(value)){
934-
braces=getIteratorBraces('Map',tag);
935-
formatter=formatIterator;
936-
}elseif(isSetIterator(value)){
937-
braces=getIteratorBraces('Set',tag);
938-
formatter=formatIterator;
939-
// Handle other regular objects again.
940-
}else{
941-
if(keys.length===0&&protoProps===undefined){
942-
if(isExternal(value))
943-
returnctx.stylize('[External]','special');
944-
return`${getCtxStyle(value,constructor,tag)}{}`;
945-
}
946-
braces[0]=`${getCtxStyle(value,constructor,tag)}{`;
917+
if(keys.length===0&&protoProps===undefined){
918+
if(isExternal(value))
919+
returnctx.stylize('[External]','special');
920+
return`${getCtxStyle(value,constructor,tag)}{}`;
947921
}
922+
braces[0]=`${getCtxStyle(value,constructor,tag)}{`;
948923
}
949924
}
950925

@@ -961,7 +936,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
961936
letoutput;
962937
constindentationLvl=ctx.indentationLvl;
963938
try{
964-
output=formatter(ctx,value,recurseTimes,keys,braces);
939+
output=formatter(ctx,value,recurseTimes);
965940
for(i=0;i<keys.length;i++){
966941
output.push(
967942
formatProperty(ctx,value,recurseTimes,keys[i],extrasType));
@@ -1316,7 +1291,7 @@ function formatPrimitive(fn, value, ctx) {
13161291
returnfn(SymbolPrototypeToString(value),'symbol');
13171292
}
13181293

1319-
functionformatNamespaceObject(ctx,value,recurseTimes,keys){
1294+
functionformatNamespaceObject(keys,ctx,value,recurseTimes){
13201295
constoutput=newArray(keys.length);
13211296
for(leti=0;i<keys.length;i++){
13221297
try{
@@ -1418,7 +1393,7 @@ function formatArray(ctx, value, recurseTimes) {
14181393
returnoutput;
14191394
}
14201395

1421-
functionformatTypedArray(ctx,value,recurseTimes){
1396+
functionformatTypedArray(value,ctx,ignored,recurseTimes){
14221397
constmaxLength=MathMin(MathMax(0,ctx.maxArrayLength),value.length);
14231398
constremaining=value.length-maxLength;
14241399
constoutput=newArray(maxLength);
@@ -1449,7 +1424,7 @@ function formatTypedArray(ctx, value, recurseTimes) {
14491424
returnoutput;
14501425
}
14511426

1452-
functionformatSet(ctx,value,recurseTimes){
1427+
functionformatSet(value,size,ctx,ignored,recurseTimes){
14531428
constoutput=[];
14541429
ctx.indentationLvl+=2;
14551430
for(constvofvalue){
@@ -1460,11 +1435,11 @@ function formatSet(ctx, value, recurseTimes) {
14601435
// arrays. For consistency's sake, do the same for `size`, even though this
14611436
// property isn't selected by ObjectGetOwnPropertyNames().
14621437
if(ctx.showHidden)
1463-
output.push(`[size]: ${ctx.stylize(`${value.size}`,'number')}`);
1438+
output.push(`[size]: ${ctx.stylize(`${size}`,'number')}`);
14641439
returnoutput;
14651440
}
14661441

1467-
functionformatMap(ctx,value,recurseTimes){
1442+
functionformatMap(value,size,ctx,ignored,recurseTimes){
14681443
constoutput=[];
14691444
ctx.indentationLvl+=2;
14701445
for(const[k,v]ofvalue){
@@ -1474,7 +1449,7 @@ function formatMap(ctx, value, recurseTimes) {
14741449
ctx.indentationLvl-=2;
14751450
// See comment in formatSet
14761451
if(ctx.showHidden)
1477-
output.push(`[size]: ${ctx.stylize(`${value.size}`,'number')}`);
1452+
output.push(`[size]: ${ctx.stylize(`${size}`,'number')}`);
14781453
returnoutput;
14791454
}
14801455

@@ -1552,7 +1527,7 @@ function formatWeakMap(ctx, value, recurseTimes) {
15521527
returnformatMapIterInner(ctx,recurseTimes,entries,kWeak);
15531528
}
15541529

1555-
functionformatIterator(ctx,value,recurseTimes,keys,braces){
1530+
functionformatIterator(braces,ctx,value,recurseTimes){
15561531
const[entries,isKeyValue]=previewEntries(value,true);
15571532
if(isKeyValue){
15581533
// Mark entry iterators as such.
@@ -1587,7 +1562,7 @@ function formatProperty(ctx, value, recurseTimes, key, type, desc) {
15871562
desc=desc||ObjectGetOwnPropertyDescriptor(value,key)||
15881563
{value: value[key],enumerable: true};
15891564
if(desc.value!==undefined){
1590-
constdiff=(type!==kObjectType||ctx.compact!==true) ? 2 : 3;
1565+
constdiff=(ctx.compact!==true||type!==kObjectType) ? 2 : 3;
15911566
ctx.indentationLvl+=diff;
15921567
str=formatValue(ctx,desc.value,recurseTimes);
15931568
if(diff===3){

‎test/parallel/test-util-inspect.js‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2216,6 +2216,19 @@ assert.strictEqual(
22162216
configurable: true
22172217
});
22182218
assert.strictEqual(util.inspect(obj),'[Set: null prototype] { 1, 2 }');
2219+
Object.defineProperty(obj,Symbol.iterator,{
2220+
value: true,
2221+
configurable: true
2222+
});
2223+
Object.defineProperty(obj,'size',{
2224+
value: NaN,
2225+
configurable: true,
2226+
enumerable: true
2227+
});
2228+
assert.strictEqual(
2229+
util.inspect(obj),
2230+
'[Set: null prototype] { 1, 2, size: NaN }'
2231+
);
22192232
}
22202233

22212234
// Check the getter option.

0 commit comments

Comments
 (0)