Skip to content

Commit 5b14066

Browse files
BridgeARtargos
authored andcommittedSep 3, 2018
util: restore all information in inspect
The former implementation lacked symbols on the iterator objects without prototype. This is now fixed. The special handling for overriding `Symbol.iterator` was removed as it's very difficult to deal with this properly. Manipulating the symbols is just not supported. PR-URL: #22437 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent fcf059a commit 5b14066

File tree

2 files changed

+45
-59
lines changed

2 files changed

+45
-59
lines changed
 

‎lib/util.js

+42-41
Original file line numberDiff line numberDiff line change
@@ -462,13 +462,6 @@ function getPrefix(constructor, tag, fallback) {
462462
return '';
463463
}
464464

465-
function addExtraKeys(source, target, keys) {
466-
for (const key of keys) {
467-
target[key] = source[key];
468-
}
469-
return target;
470-
}
471-
472465
function findTypedConstructor(value) {
473466
for (const [check, clazz] of [
474467
[isUint8Array, Uint8Array],
@@ -484,14 +477,36 @@ function findTypedConstructor(value) {
484477
[isBigUint64Array, BigUint64Array]
485478
]) {
486479
if (check(value)) {
487-
return new clazz(value);
480+
return clazz;
488481
}
489482
}
490-
return value;
491483
}
492484

493485
const getBoxedValue = formatPrimitive.bind(null, stylizeNoColor);
494486

487+
function noPrototypeIterator(ctx, value, recurseTimes) {
488+
let newVal;
489+
// TODO: Create a Subclass in case there's no prototype and show
490+
// `null-prototype`.
491+
if (isSet(value)) {
492+
const clazz = Object.getPrototypeOf(value) || Set;
493+
newVal = new clazz(setValues(value));
494+
} else if (isMap(value)) {
495+
const clazz = Object.getPrototypeOf(value) || Map;
496+
newVal = new clazz(mapEntries(value));
497+
} else if (Array.isArray(value)) {
498+
const clazz = Object.getPrototypeOf(value) || Array;
499+
newVal = new clazz(value.length || 0);
500+
} else if (isTypedArray(value)) {
501+
const clazz = findTypedConstructor(value) || Uint8Array;
502+
newVal = new clazz(value);
503+
}
504+
if (newVal) {
505+
Object.defineProperties(newVal, Object.getOwnPropertyDescriptors(value));
506+
return formatValue(ctx, newVal, recurseTimes);
507+
}
508+
}
509+
495510
// Note: using `formatValue` directly requires the indentation level to be
496511
// corrected by setting `ctx.indentationLvL += diff` and then to decrease the
497512
// value afterwards again.
@@ -757,39 +772,25 @@ function formatValue(ctx, value, recurseTimes) {
757772
braces = ['{', '}'];
758773
// The input prototype got manipulated. Special handle these.
759774
// We have to rebuild the information so we are able to display everything.
760-
} else if (isSet(value)) {
761-
const newVal = addExtraKeys(value, new Set(setValues(value)), keys);
762-
return formatValue(ctx, newVal, recurseTimes);
763-
} else if (isMap(value)) {
764-
const newVal = addExtraKeys(value, new Map(mapEntries(value)), keys);
765-
return formatValue(ctx, newVal, recurseTimes);
766-
} else if (Array.isArray(value)) {
767-
// The prefix is not always possible to fully reconstruct.
768-
const prefix = getPrefix(constructor, tag);
769-
braces = [`${prefix === 'Array ' ? '' : prefix}[`, ']'];
770-
formatter = formatArray;
771-
const newValue = [];
772-
newValue.length = value.length;
773-
value = addExtraKeys(value, newValue, keys);
774-
} else if (isTypedArray(value)) {
775-
const newValue = findTypedConstructor(value);
776-
value = addExtraKeys(value, newValue, keys.slice(newValue.length));
777-
// The prefix is not always possible to fully reconstruct.
778-
braces = [`${getPrefix(getConstructorName(value), tag)}[`, ']'];
779-
formatter = formatTypedArray;
780-
} else if (isMapIterator(value)) {
781-
braces = [`[${tag || 'Map Iterator'}] {`, '}'];
782-
formatter = formatMapIterator;
783-
} else if (isSetIterator(value)) {
784-
braces = [`[${tag || 'Set Iterator'}] {`, '}'];
785-
formatter = formatSetIterator;
786-
// Handle other regular objects again.
787-
} else if (keyLength === 0) {
788-
if (isExternal(value))
789-
return ctx.stylize('[External]', 'special');
790-
return `${getPrefix(constructor, tag)}{}`;
791775
} else {
792-
braces[0] = `${getPrefix(constructor, tag)}{`;
776+
const specialIterator = noPrototypeIterator(ctx, value, recurseTimes);
777+
if (specialIterator) {
778+
return specialIterator;
779+
}
780+
if (isMapIterator(value)) {
781+
braces = [`[${tag || 'Map Iterator'}] {`, '}'];
782+
formatter = formatMapIterator;
783+
} else if (isSetIterator(value)) {
784+
braces = [`[${tag || 'Set Iterator'}] {`, '}'];
785+
formatter = formatSetIterator;
786+
// Handle other regular objects again.
787+
} else if (keyLength === 0) {
788+
if (isExternal(value))
789+
return ctx.stylize('[External]', 'special');
790+
return `${getPrefix(constructor, tag)}{}`;
791+
} else {
792+
braces[0] = `${getPrefix(constructor, tag)}{`;
793+
}
793794
}
794795
}
795796

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

+3-18
Original file line numberDiff line numberDiff line change
@@ -1600,24 +1600,6 @@ util.inspect(process);
16001600
'prematurely. Maximum call stack size exceeded.]'));
16011601
}
16021602

1603-
// Manipulating the Symbol.iterator should still produce nice results.
1604-
[
1605-
[[1, 2], '[ 1, 2 ]'],
1606-
[[, , 5, , , , ], '[ <2 empty items>, 5, <3 empty items> ]'],
1607-
[new Set([1, 2]), 'Set { 1, 2 }'],
1608-
[new Map([[1, 2]]), 'Map { 1 => 2 }'],
1609-
[new Uint8Array(2), 'Uint8Array [ 0, 0 ]'],
1610-
// It seems like the following can not be fully restored :(
1611-
[new Set([1, 2]).entries(), 'Object [Set Iterator] {}'],
1612-
[new Map([[1, 2]]).keys(), 'Object [Map Iterator] {}'],
1613-
].forEach(([value, expected]) => {
1614-
// "Remove the Symbol.iterator"
1615-
Object.defineProperty(value, Symbol.iterator, {
1616-
value: false
1617-
});
1618-
assert.strictEqual(util.inspect(value), expected);
1619-
});
1620-
16211603
// Verify the output in case the value has no prototype.
16221604
// Sadly, these cases can not be fully inspected :(
16231605
[
@@ -1673,6 +1655,9 @@ util.inspect(process);
16731655
);
16741656
value.foo = 'bar';
16751657
assert.notStrictEqual(util.inspect(value), expected);
1658+
delete value.foo;
1659+
value[Symbol('foo')] = 'yeah';
1660+
assert.notStrictEqual(util.inspect(value), expected);
16761661
});
16771662

16781663
assert.strictEqual(inspect(1n), '1n');

0 commit comments

Comments
 (0)
Please sign in to comment.