Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 3b9360d

Browse filesBrowse 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
Copy full SHA for 3b9360d

File tree

Expand file treeCollapse file tree

2 files changed

+80
-92
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

2 files changed

+80
-92
lines changed
Open diff view settings
Collapse file

‎lib/internal/util/inspect.js‎

Copy file name to clipboardExpand all lines: lib/internal/util/inspect.js
+67-92Lines 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+
const setSizeGetter = uncurryThis(
119+
ObjectGetOwnPropertyDescriptor(SetPrototype, 'size').get);
120+
const mapSizeGetter = uncurryThis(
121+
ObjectGetOwnPropertyDescriptor(MapPrototype, 'size').get);
122+
118123
let hexSlice;
119124

120125
const builtInObjects = new Set(
@@ -648,51 +653,6 @@ function findTypedConstructor(value) {
648653
}
649654
}
650655

651-
let lazyNullPrototypeCache;
652-
// Creates a subclass and name
653-
// the constructor as `${clazz} : null prototype`
654-
function clazzWithNullPrototype(clazz, name) {
655-
if (lazyNullPrototypeCache === undefined) {
656-
lazyNullPrototypeCache = new Map();
657-
} else {
658-
const cachedClass = lazyNullPrototypeCache.get(clazz);
659-
if (cachedClass !== undefined) {
660-
return cachedClass;
661-
}
662-
}
663-
class NullPrototype extends clazz {
664-
get [SymbolToStringTag]() {
665-
return '';
666-
}
667-
}
668-
ObjectDefineProperty(NullPrototype.prototype.constructor, 'name',
669-
{ value: `[${name}: null prototype]` });
670-
lazyNullPrototypeCache.set(clazz, NullPrototype);
671-
return NullPrototype;
672-
}
673-
674-
function noPrototypeIterator(ctx, value, recurseTimes) {
675-
let newVal;
676-
if (isSet(value)) {
677-
const clazz = clazzWithNullPrototype(Set, 'Set');
678-
newVal = new clazz(SetPrototypeValues(value));
679-
} else if (isMap(value)) {
680-
const clazz = clazzWithNullPrototype(Map, 'Map');
681-
newVal = new clazz(MapPrototypeEntries(value));
682-
} else if (ArrayIsArray(value)) {
683-
const clazz = clazzWithNullPrototype(Array, 'Array');
684-
newVal = new clazz(value.length);
685-
} else if (isTypedArray(value)) {
686-
const constructor = findTypedConstructor(value);
687-
const clazz = clazzWithNullPrototype(constructor, constructor.name);
688-
newVal = new clazz(value);
689-
}
690-
if (newVal !== undefined) {
691-
ObjectDefineProperties(newVal, ObjectGetOwnPropertyDescriptors(value));
692-
return formatRaw(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
let extrasType = 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
} else if (isSet(value)) {
772+
const size = setSizeGetter(value);
810773
keys = getKeys(value, ctx.showHidden);
811-
const prefix = getPrefix(constructor, tag, 'Set');
812-
if (value.size === 0 && keys.length === 0 && protoProps === undefined)
774+
let prefix = '';
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
} else if (isMap(value)) {
788+
const size = mapSizeGetter(value);
817789
keys = getKeys(value, ctx.showHidden);
818-
const prefix = getPrefix(constructor, tag, 'Map');
819-
if (value.size === 0 && keys.length === 0 && protoProps === undefined)
790+
let prefix = '';
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
} else if (isTypedArray(value)) {
824804
keys = getOwnNonIndexProperties(value, filter);
825-
const prefix = constructor !== null ?
826-
getPrefix(constructor, tag) :
827-
getPrefix(constructor, tag, findTypedConstructor(value).name);
805+
let bound = value;
806+
let prefix = '';
807+
if (constructor === null) {
808+
const constr = findTypedConstructor(value);
809+
prefix = getPrefix(constructor, tag, constr.name);
810+
// Reconstruct the array information.
811+
bound = new constr(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
} else if (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
} else if (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
} else if (isModuleNamespaceObject(value)) {
917908
braces[0] = `[${tag}] {`;
918-
formatter = formatNamespaceObject;
909+
// Special handle keys for namespace objects.
910+
formatter = formatNamespaceObject.bind(null, keys);
919911
} else if (isBoxedPrimitive(value)) {
920912
base = getBoxedBase(value, ctx, keys, constructor, tag);
921913
if (keys.length === 0 && protoProps === undefined) {
922914
return base;
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-
const specialIterator = noPrototypeIterator(ctx, value, recurseTimes);
929-
if (specialIterator) {
930-
return specialIterator;
931-
}
932-
}
933-
if (isMapIterator(value)) {
934-
braces = getIteratorBraces('Map', tag);
935-
formatter = formatIterator;
936-
} else if (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-
return ctx.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+
return ctx.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
let output;
962937
const indentationLvl = 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
return fn(SymbolPrototypeToString(value), 'symbol');
13171292
}
13181293

1319-
function formatNamespaceObject(ctx, value, recurseTimes, keys) {
1294+
function formatNamespaceObject(keys, ctx, value, recurseTimes) {
13201295
const output = new Array(keys.length);
13211296
for (let i = 0; i < keys.length; i++) {
13221297
try {
@@ -1418,7 +1393,7 @@ function formatArray(ctx, value, recurseTimes) {
14181393
return output;
14191394
}
14201395

1421-
function formatTypedArray(ctx, value, recurseTimes) {
1396+
function formatTypedArray(value, ctx, ignored, recurseTimes) {
14221397
const maxLength = MathMin(MathMax(0, ctx.maxArrayLength), value.length);
14231398
const remaining = value.length - maxLength;
14241399
const output = new Array(maxLength);
@@ -1449,7 +1424,7 @@ function formatTypedArray(ctx, value, recurseTimes) {
14491424
return output;
14501425
}
14511426

1452-
function formatSet(ctx, value, recurseTimes) {
1427+
function formatSet(value, size, ctx, ignored, recurseTimes) {
14531428
const output = [];
14541429
ctx.indentationLvl += 2;
14551430
for (const v of value) {
@@ -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
return output;
14651440
}
14661441

1467-
function formatMap(ctx, value, recurseTimes) {
1442+
function formatMap(value, size, ctx, ignored, recurseTimes) {
14681443
const output = [];
14691444
ctx.indentationLvl += 2;
14701445
for (const [k, v] of value) {
@@ -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
return output;
14791454
}
14801455

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

1555-
function formatIterator(ctx, value, recurseTimes, keys, braces) {
1530+
function formatIterator(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-
const diff = (type !== kObjectType || ctx.compact !== true) ? 2 : 3;
1565+
const diff = (ctx.compact !== true || type !== kObjectType) ? 2 : 3;
15911566
ctx.indentationLvl += diff;
15921567
str = formatValue(ctx, desc.value, recurseTimes);
15931568
if (diff === 3) {
Collapse file

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

Copy file name to clipboardExpand all lines: test/parallel/test-util-inspect.js
+13Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.