• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Flags: --expose-internals
2// Copyright Joyent, Inc. and other Node contributors.
3//
4// Permission is hereby granted, free of charge, to any person obtaining a
5// copy of this software and associated documentation files (the
6// "Software"), to deal in the Software without restriction, including
7// without limitation the rights to use, copy, modify, merge, publish,
8// distribute, sublicense, and/or sell copies of the Software, and to permit
9// persons to whom the Software is furnished to do so, subject to the
10// following conditions:
11//
12// The above copyright notice and this permission notice shall be included
13// in all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
18// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
19// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21// USE OR OTHER DEALINGS IN THE SOFTWARE.
22'use strict';
23const common = require('../common');
24const assert = require('assert');
25const { internalBinding } = require('internal/test/binding');
26const JSStream = internalBinding('js_stream').JSStream;
27const util = require('util');
28const vm = require('vm');
29const v8 = require('v8');
30const { previewEntries } = internalBinding('util');
31const { inspect } = util;
32const { MessageChannel } = require('worker_threads');
33
34assert.strictEqual(util.inspect(1), '1');
35assert.strictEqual(util.inspect(false), 'false');
36assert.strictEqual(util.inspect(''), "''");
37assert.strictEqual(util.inspect('hello'), "'hello'");
38assert.strictEqual(util.inspect(function abc() {}), '[Function: abc]');
39assert.strictEqual(util.inspect(() => {}), '[Function (anonymous)]');
40assert.strictEqual(
41  util.inspect(async function() {}),
42  '[AsyncFunction (anonymous)]'
43);
44assert.strictEqual(util.inspect(async () => {}), '[AsyncFunction (anonymous)]');
45
46// Special function inspection.
47{
48  const fn = (() => function*() {})();
49  assert.strictEqual(
50    util.inspect(fn),
51    '[GeneratorFunction (anonymous)]'
52  );
53  assert.strictEqual(
54    util.inspect(async function* abc() {}),
55    '[AsyncGeneratorFunction: abc]'
56  );
57  Object.setPrototypeOf(fn, Object.getPrototypeOf(async () => {}));
58  assert.strictEqual(
59    util.inspect(fn),
60    '[GeneratorFunction (anonymous)] AsyncFunction'
61  );
62  Object.defineProperty(fn, 'name', { value: 5, configurable: true });
63  assert.strictEqual(
64    util.inspect(fn),
65    '[GeneratorFunction: 5] AsyncFunction'
66  );
67  Object.defineProperty(fn, Symbol.toStringTag, {
68    value: 'Foobar',
69    configurable: true
70  });
71  assert.strictEqual(
72    util.inspect({ ['5']: fn }),
73    "{ '5': [GeneratorFunction: 5] AsyncFunction [Foobar] }"
74  );
75  Object.defineProperty(fn, 'name', { value: '5', configurable: true });
76  Object.setPrototypeOf(fn, null);
77  assert.strictEqual(
78    util.inspect(fn),
79    '[GeneratorFunction (null prototype): 5] [Foobar]'
80  );
81  assert.strictEqual(
82    util.inspect({ ['5']: fn }),
83    "{ '5': [GeneratorFunction (null prototype): 5] [Foobar] }"
84  );
85}
86
87assert.strictEqual(util.inspect(undefined), 'undefined');
88assert.strictEqual(util.inspect(null), 'null');
89assert.strictEqual(util.inspect(/foo(bar\n)?/gi), '/foo(bar\\n)?/gi');
90assert.strictEqual(
91  util.inspect(new Date('Sun, 14 Feb 2010 11:48:40 GMT')),
92  new Date('2010-02-14T12:48:40+01:00').toISOString()
93);
94assert.strictEqual(util.inspect(new Date('')), (new Date('')).toString());
95assert.strictEqual(util.inspect('\n\x01'), "'\\n\\x01'");
96assert.strictEqual(
97  util.inspect(`${Array(75).fill(1)}'\n\x1d\n\x03\x85\x7f\x7e\x9f\xa0`),
98  // eslint-disable-next-line no-irregular-whitespace
99  `"${Array(75).fill(1)}'\\n" +\n  '\\x1D\\n' +\n  '\\x03\\x85\\x7F~\\x9F '`
100);
101assert.strictEqual(util.inspect([]), '[]');
102assert.strictEqual(util.inspect(Object.create([])), 'Array {}');
103assert.strictEqual(util.inspect([1, 2]), '[ 1, 2 ]');
104assert.strictEqual(util.inspect([1, [2, 3]]), '[ 1, [ 2, 3 ] ]');
105assert.strictEqual(util.inspect({}), '{}');
106assert.strictEqual(util.inspect({ a: 1 }), '{ a: 1 }');
107assert.strictEqual(util.inspect({ a: function() {} }), '{ a: [Function: a] }');
108assert.strictEqual(util.inspect({ a: () => {} }), '{ a: [Function: a] }');
109// eslint-disable-next-line func-name-matching
110assert.strictEqual(util.inspect({ a: async function abc() {} }),
111                   '{ a: [AsyncFunction: abc] }');
112assert.strictEqual(util.inspect({ a: async () => {} }),
113                   '{ a: [AsyncFunction: a] }');
114assert.strictEqual(util.inspect({ a: function*() {} }),
115                   '{ a: [GeneratorFunction: a] }');
116assert.strictEqual(util.inspect({ a: 1, b: 2 }), '{ a: 1, b: 2 }');
117assert.strictEqual(util.inspect({ 'a': {} }), '{ a: {} }');
118assert.strictEqual(util.inspect({ 'a': { 'b': 2 } }), '{ a: { b: 2 } }');
119assert.strictEqual(util.inspect({ 'a': { 'b': { 'c': { 'd': 2 } } } }),
120                   '{ a: { b: { c: [Object] } } }');
121assert.strictEqual(
122  util.inspect({ 'a': { 'b': { 'c': { 'd': 2 } } } }, false, null),
123  '{\n  a: { b: { c: { d: 2 } } }\n}');
124assert.strictEqual(util.inspect([1, 2, 3], true), '[ 1, 2, 3, [length]: 3 ]');
125assert.strictEqual(util.inspect({ 'a': { 'b': { 'c': 2 } } }, false, 0),
126                   '{ a: [Object] }');
127assert.strictEqual(util.inspect({ 'a': { 'b': { 'c': 2 } } }, false, 1),
128                   '{ a: { b: [Object] } }');
129assert.strictEqual(util.inspect({ 'a': { 'b': ['c'] } }, false, 1),
130                   '{ a: { b: [Array] } }');
131assert.strictEqual(util.inspect(new Uint8Array(0)), 'Uint8Array(0) []');
132assert(inspect(new Uint8Array(0), { showHidden: true }).includes('[buffer]'));
133assert.strictEqual(
134  util.inspect(
135    Object.create(
136      {},
137      { visible: { value: 1, enumerable: true }, hidden: { value: 2 } }
138    )
139  ),
140  '{ visible: 1 }'
141);
142assert.strictEqual(
143  util.inspect(
144    Object.assign(new String('hello'), { [Symbol('foo')]: 123 }),
145    { showHidden: true }
146  ),
147  "[String: 'hello'] { [length]: 5, [Symbol(foo)]: 123 }"
148);
149
150assert.match(util.inspect((new JSStream())._externalStream),
151             /^\[External: [0-9a-f]+\]$/);
152
153{
154  const regexp = /regexp/;
155  regexp.aprop = 42;
156  assert.strictEqual(util.inspect({ a: regexp }, false, 0), '{ a: /regexp/ }');
157}
158
159assert(/Object/.test(
160  util.inspect({ a: { a: { a: { a: {} } } } }, undefined, undefined, true)
161));
162assert(!/Object/.test(
163  util.inspect({ a: { a: { a: { a: {} } } } }, undefined, null, true)
164));
165
166{
167  const showHidden = true;
168  const ab = new Uint8Array([1, 2, 3, 4]).buffer;
169  const dv = new DataView(ab, 1, 2);
170  assert.strictEqual(
171    util.inspect(ab, showHidden),
172    'ArrayBuffer { [Uint8Contents]: <01 02 03 04>, byteLength: 4 }'
173  );
174  assert.strictEqual(util.inspect(new DataView(ab, 1, 2), showHidden),
175                     'DataView {\n' +
176                     '  byteLength: 2,\n' +
177                     '  byteOffset: 1,\n' +
178                     '  buffer: ArrayBuffer {' +
179                      ' [Uint8Contents]: <01 02 03 04>, byteLength: 4 }\n}');
180  assert.strictEqual(
181    util.inspect(ab, showHidden),
182    'ArrayBuffer { [Uint8Contents]: <01 02 03 04>, byteLength: 4 }'
183  );
184  assert.strictEqual(util.inspect(dv, showHidden),
185                     'DataView {\n' +
186                     '  byteLength: 2,\n' +
187                     '  byteOffset: 1,\n' +
188                     '  buffer: ArrayBuffer { [Uint8Contents]: ' +
189                       '<01 02 03 04>, byteLength: 4 }\n}');
190  ab.x = 42;
191  dv.y = 1337;
192  assert.strictEqual(util.inspect(ab, showHidden),
193                     'ArrayBuffer { [Uint8Contents]: <01 02 03 04>, ' +
194                       'byteLength: 4, x: 42 }');
195  assert.strictEqual(util.inspect(dv, showHidden),
196                     'DataView {\n' +
197                     '  byteLength: 2,\n' +
198                     '  byteOffset: 1,\n' +
199                     '  buffer: ArrayBuffer { [Uint8Contents]: <01 02 03 04>,' +
200                       ' byteLength: 4, x: 42 },\n' +
201                     '  y: 1337\n}');
202}
203
204{
205  const ab = new ArrayBuffer(42);
206  assert.strictEqual(ab.byteLength, 42);
207  new MessageChannel().port1.postMessage(ab, [ ab ]);
208  assert.strictEqual(ab.byteLength, 0);
209  assert.strictEqual(util.inspect(ab),
210                     'ArrayBuffer { (detached), byteLength: 0 }');
211}
212
213// Truncate output for ArrayBuffers using plural or singular bytes
214{
215  const ab = new ArrayBuffer(3);
216  assert.strictEqual(util.inspect(ab, { showHidden: true, maxArrayLength: 2 }),
217                     'ArrayBuffer { [Uint8Contents]' +
218                      ': <00 00 ... 1 more byte>, byteLength: 3 }');
219  assert.strictEqual(util.inspect(ab, { showHidden: true, maxArrayLength: 1 }),
220                     'ArrayBuffer { [Uint8Contents]' +
221                      ': <00 ... 2 more bytes>, byteLength: 3 }');
222}
223
224// Now do the same checks but from a different context.
225{
226  const showHidden = false;
227  const ab = vm.runInNewContext('new ArrayBuffer(4)');
228  const dv = vm.runInNewContext('new DataView(ab, 1, 2)', { ab });
229  assert.strictEqual(
230    util.inspect(ab, showHidden),
231    'ArrayBuffer { [Uint8Contents]: <00 00 00 00>, byteLength: 4 }'
232  );
233  assert.strictEqual(util.inspect(new DataView(ab, 1, 2), showHidden),
234                     'DataView {\n' +
235                     '  byteLength: 2,\n' +
236                     '  byteOffset: 1,\n' +
237                     '  buffer: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' +
238                       ' byteLength: 4 }\n}');
239  assert.strictEqual(
240    util.inspect(ab, showHidden),
241    'ArrayBuffer { [Uint8Contents]: <00 00 00 00>, byteLength: 4 }'
242  );
243  assert.strictEqual(util.inspect(dv, showHidden),
244                     'DataView {\n' +
245                     '  byteLength: 2,\n' +
246                     '  byteOffset: 1,\n' +
247                     '  buffer: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' +
248                       ' byteLength: 4 }\n}');
249  ab.x = 42;
250  dv.y = 1337;
251  assert.strictEqual(util.inspect(ab, showHidden),
252                     'ArrayBuffer { [Uint8Contents]: <00 00 00 00>, ' +
253                       'byteLength: 4, x: 42 }');
254  assert.strictEqual(util.inspect(dv, showHidden),
255                     'DataView {\n' +
256                     '  byteLength: 2,\n' +
257                     '  byteOffset: 1,\n' +
258                     '  buffer: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' +
259                       ' byteLength: 4, x: 42 },\n' +
260                     '  y: 1337\n}');
261}
262
263[ Float32Array,
264  Float64Array,
265  Int16Array,
266  Int32Array,
267  Int8Array,
268  Uint16Array,
269  Uint32Array,
270  Uint8Array,
271  Uint8ClampedArray ].forEach((constructor) => {
272  const length = 2;
273  const byteLength = length * constructor.BYTES_PER_ELEMENT;
274  const array = new constructor(new ArrayBuffer(byteLength), 0, length);
275  array[0] = 65;
276  array[1] = 97;
277  assert.strictEqual(
278    util.inspect(array, { showHidden: true }),
279    `${constructor.name}(${length}) [\n` +
280      '  65,\n' +
281      '  97,\n' +
282      `  [BYTES_PER_ELEMENT]: ${constructor.BYTES_PER_ELEMENT},\n` +
283      `  [length]: ${length},\n` +
284      `  [byteLength]: ${byteLength},\n` +
285      '  [byteOffset]: 0,\n' +
286      `  [buffer]: ArrayBuffer { byteLength: ${byteLength} }\n]`);
287  assert.strictEqual(
288    util.inspect(array, false),
289    `${constructor.name}(${length}) [ 65, 97 ]`
290  );
291});
292
293// Now check that declaring a TypedArray in a different context works the same.
294[ Float32Array,
295  Float64Array,
296  Int16Array,
297  Int32Array,
298  Int8Array,
299  Uint16Array,
300  Uint32Array,
301  Uint8Array,
302  Uint8ClampedArray ].forEach((constructor) => {
303  const length = 2;
304  const byteLength = length * constructor.BYTES_PER_ELEMENT;
305  const array = vm.runInNewContext(
306    'new constructor(new ArrayBuffer(byteLength), 0, length)',
307    { constructor, byteLength, length }
308  );
309  array[0] = 65;
310  array[1] = 97;
311  assert.strictEqual(
312    util.inspect(array, true),
313    `${constructor.name}(${length}) [\n` +
314      '  65,\n' +
315      '  97,\n' +
316      `  [BYTES_PER_ELEMENT]: ${constructor.BYTES_PER_ELEMENT},\n` +
317      `  [length]: ${length},\n` +
318      `  [byteLength]: ${byteLength},\n` +
319      '  [byteOffset]: 0,\n' +
320      `  [buffer]: ArrayBuffer { byteLength: ${byteLength} }\n]`);
321  assert.strictEqual(
322    util.inspect(array, false),
323    `${constructor.name}(${length}) [ 65, 97 ]`
324  );
325});
326
327{
328  const brokenLength = new Float32Array(2);
329  Object.defineProperty(brokenLength, 'length', { value: -1 });
330  assert.strictEqual(inspect(brokenLength), 'Float32Array(2) [ 0n, 0n ]');
331}
332
333assert.strictEqual(
334  util.inspect(Object.create({}, {
335    visible: { value: 1, enumerable: true },
336    hidden: { value: 2 }
337  }), { showHidden: true }),
338  '{ visible: 1, [hidden]: 2 }'
339);
340// Objects without prototype.
341assert.strictEqual(
342  util.inspect(Object.create(null, {
343    name: { value: 'Tim', enumerable: true },
344    hidden: { value: 'secret' }
345  }), { showHidden: true }),
346  "[Object: null prototype] { name: 'Tim', [hidden]: 'secret' }"
347);
348
349assert.strictEqual(
350  util.inspect(Object.create(null, {
351    name: { value: 'Tim', enumerable: true },
352    hidden: { value: 'secret' }
353  })),
354  "[Object: null prototype] { name: 'Tim' }"
355);
356
357// Dynamic properties.
358{
359  assert.strictEqual(
360    util.inspect({ get readonly() { return 1; } }),
361    '{ readonly: [Getter] }');
362
363  assert.strictEqual(
364    util.inspect({ get readwrite() { return 1; }, set readwrite(val) {} }),
365    '{ readwrite: [Getter/Setter] }');
366
367  assert.strictEqual(
368    // eslint-disable-next-line accessor-pairs
369    util.inspect({ set writeonly(val) {} }),
370    '{ writeonly: [Setter] }');
371
372  const value = {};
373  value.a = value;
374  assert.strictEqual(util.inspect(value), '<ref *1> { a: [Circular *1] }');
375  const getterFn = {
376    get one() {
377      return null;
378    }
379  };
380  assert.strictEqual(
381    util.inspect(getterFn, { getters: true }),
382    '{ one: [Getter: null] }'
383  );
384}
385
386// Array with dynamic properties.
387{
388  const value = [1, 2, 3];
389  Object.defineProperty(
390    value,
391    'growingLength',
392    {
393      enumerable: true,
394      get: function() { this.push(true); return this.length; }
395    }
396  );
397  Object.defineProperty(
398    value,
399    '-1',
400    {
401      enumerable: true,
402      value: -1
403    }
404  );
405  assert.strictEqual(util.inspect(value),
406                     "[ 1, 2, 3, growingLength: [Getter], '-1': -1 ]");
407}
408
409// Array with inherited number properties.
410{
411  class CustomArray extends Array {}
412  CustomArray.prototype[5] = 'foo';
413  CustomArray.prototype[49] = 'bar';
414  CustomArray.prototype.foo = true;
415  const arr = new CustomArray(50);
416  arr[49] = 'I win';
417  assert.strictEqual(
418    util.inspect(arr),
419    "CustomArray(50) [ <49 empty items>, 'I win' ]"
420  );
421  assert.strictEqual(
422    util.inspect(arr, { showHidden: true }),
423    'CustomArray(50) [\n' +
424    '  <49 empty items>,\n' +
425    "  'I win',\n" +
426    '  [length]: 50,\n' +
427    "  '5': 'foo',\n" +
428    '  foo: true\n' +
429    ']'
430  );
431}
432
433// Array with extra properties.
434{
435  const arr = [1, 2, 3, , ];
436  arr.foo = 'bar';
437  assert.strictEqual(util.inspect(arr),
438                     "[ 1, 2, 3, <1 empty item>, foo: 'bar' ]");
439
440  const arr2 = [];
441  assert.strictEqual(util.inspect([], { showHidden: true }), '[ [length]: 0 ]');
442  arr2['00'] = 1;
443  assert.strictEqual(util.inspect(arr2), "[ '00': 1 ]");
444  assert.strictEqual(util.inspect(arr2, { showHidden: true }),
445                     "[ [length]: 0, '00': 1 ]");
446  arr2[1] = 0;
447  assert.strictEqual(util.inspect(arr2), "[ <1 empty item>, 0, '00': 1 ]");
448  assert.strictEqual(util.inspect(arr2, { showHidden: true }),
449                     "[ <1 empty item>, 0, [length]: 2, '00': 1 ]");
450  delete arr2[1];
451  assert.strictEqual(util.inspect(arr2), "[ <2 empty items>, '00': 1 ]");
452  assert.strictEqual(util.inspect(arr2, { showHidden: true }),
453                     "[ <2 empty items>, [length]: 2, '00': 1 ]");
454  arr2['01'] = 2;
455  assert.strictEqual(util.inspect(arr2),
456                     "[ <2 empty items>, '00': 1, '01': 2 ]");
457  assert.strictEqual(util.inspect(arr2, { showHidden: true }),
458                     "[ <2 empty items>, [length]: 2, '00': 1, '01': 2 ]");
459  delete arr2['00'];
460  arr2[0] = 0;
461  assert.strictEqual(util.inspect(arr2),
462                     "[ 0, <1 empty item>, '01': 2 ]");
463  assert.strictEqual(util.inspect(arr2, { showHidden: true }),
464                     "[ 0, <1 empty item>, [length]: 2, '01': 2 ]");
465  delete arr2['01'];
466  arr2[2 ** 32 - 2] = 'max';
467  arr2[2 ** 32 - 1] = 'too far';
468  assert.strictEqual(
469    util.inspect(arr2),
470    "[ 0, <4294967293 empty items>, 'max', '4294967295': 'too far' ]"
471  );
472
473  const arr3 = [];
474  arr3[-1] = -1;
475  assert.strictEqual(util.inspect(arr3), "[ '-1': -1 ]");
476}
477
478// Indices out of bounds.
479{
480  const arr = [];
481  arr[2 ** 32] = true; // Not a valid array index.
482  assert.strictEqual(util.inspect(arr), "[ '4294967296': true ]");
483  arr[0] = true;
484  arr[10] = true;
485  assert.strictEqual(util.inspect(arr),
486                     "[ true, <9 empty items>, true, '4294967296': true ]");
487  arr[2 ** 32 - 2] = true;
488  arr[2 ** 32 - 1] = true;
489  arr[2 ** 32 + 1] = true;
490  delete arr[0];
491  delete arr[10];
492  assert.strictEqual(util.inspect(arr),
493                     ['[',
494                      '<4294967294 empty items>,',
495                      'true,',
496                      "'4294967296': true,",
497                      "'4294967295': true,",
498                      "'4294967297': true\n]",
499                     ].join('\n  '));
500}
501
502// Function with properties.
503{
504  const value = () => {};
505  value.aprop = 42;
506  assert.strictEqual(util.inspect(value), '[Function: value] { aprop: 42 }');
507}
508
509// Anonymous function with properties.
510{
511  const value = (() => function() {})();
512  value.aprop = 42;
513  assert.strictEqual(
514    util.inspect(value),
515    '[Function (anonymous)] { aprop: 42 }'
516  );
517}
518
519// Regular expressions with properties.
520{
521  const value = /123/ig;
522  value.aprop = 42;
523  assert.strictEqual(util.inspect(value), '/123/gi { aprop: 42 }');
524}
525
526// Dates with properties.
527{
528  const value = new Date('Sun, 14 Feb 2010 11:48:40 GMT');
529  value.aprop = 42;
530  assert.strictEqual(util.inspect(value),
531                     '2010-02-14T11:48:40.000Z { aprop: 42 }');
532}
533
534// Test the internal isDate implementation.
535{
536  const Date2 = vm.runInNewContext('Date');
537  const d = new Date2();
538  const orig = util.inspect(d);
539  Date2.prototype.foo = 'bar';
540  const after = util.inspect(d);
541  assert.strictEqual(orig, after);
542}
543
544// Test positive/negative zero.
545assert.strictEqual(util.inspect(0), '0');
546assert.strictEqual(util.inspect(-0), '-0');
547// Edge case from check.
548assert.strictEqual(util.inspect(-5e-324), '-5e-324');
549
550// Test for sparse array.
551{
552  const a = ['foo', 'bar', 'baz'];
553  assert.strictEqual(util.inspect(a), "[ 'foo', 'bar', 'baz' ]");
554  delete a[1];
555  assert.strictEqual(util.inspect(a), "[ 'foo', <1 empty item>, 'baz' ]");
556  assert.strictEqual(
557    util.inspect(a, true),
558    "[ 'foo', <1 empty item>, 'baz', [length]: 3 ]"
559  );
560  assert.strictEqual(util.inspect(new Array(5)), '[ <5 empty items> ]');
561  a[3] = 'bar';
562  a[100] = 'qux';
563  assert.strictEqual(
564    util.inspect(a, { breakLength: Infinity }),
565    "[ 'foo', <1 empty item>, 'baz', 'bar', <96 empty items>, 'qux' ]"
566  );
567  delete a[3];
568  assert.strictEqual(
569    util.inspect(a, { maxArrayLength: 4 }),
570    "[ 'foo', <1 empty item>, 'baz', <97 empty items>, ... 1 more item ]"
571  );
572  // test 4 special case
573  assert.strictEqual(util.inspect(a, {
574    maxArrayLength: 2
575  }), "[ 'foo', <1 empty item>, ... 99 more items ]");
576}
577
578// Test for Array constructor in different context.
579{
580  const map = new Map();
581  map.set(1, 2);
582  // Passing only a single argument to indicate a set iterator.
583  const valsSetIterator = previewEntries(map.entries());
584  // Passing through true to indicate a map iterator.
585  const valsMapIterEntries = previewEntries(map.entries(), true);
586  const valsMapIterKeys = previewEntries(map.keys(), true);
587
588  assert.strictEqual(util.inspect(valsSetIterator), '[ 1, 2 ]');
589  assert.strictEqual(util.inspect(valsMapIterEntries), '[ [ 1, 2 ], true ]');
590  assert.strictEqual(util.inspect(valsMapIterKeys), '[ [ 1 ], false ]');
591}
592
593// Test for other constructors in different context.
594{
595  let obj = vm.runInNewContext('(function(){return {}})()', {});
596  assert.strictEqual(util.inspect(obj), '{}');
597  obj = vm.runInNewContext('const m=new Map();m.set(1,2);m', {});
598  assert.strictEqual(util.inspect(obj), 'Map(1) { 1 => 2 }');
599  obj = vm.runInNewContext('const s=new Set();s.add(1);s.add(2);s', {});
600  assert.strictEqual(util.inspect(obj), 'Set(2) { 1, 2 }');
601  obj = vm.runInNewContext('fn=function(){};new Promise(fn,fn)', {});
602  assert.strictEqual(util.inspect(obj), 'Promise { <pending> }');
603}
604
605// Test for property descriptors.
606{
607  const getter = Object.create(null, {
608    a: {
609      get: function() { return 'aaa'; }
610    }
611  });
612  const setter = Object.create(null, {
613    b: { // eslint-disable-line accessor-pairs
614      set: function() {}
615    }
616  });
617  const getterAndSetter = Object.create(null, {
618    c: {
619      get: function() { return 'ccc'; },
620      set: function() {}
621    }
622  });
623  assert.strictEqual(
624    util.inspect(getter, true),
625    '[Object: null prototype] { [a]: [Getter] }'
626  );
627  assert.strictEqual(
628    util.inspect(setter, true),
629    '[Object: null prototype] { [b]: [Setter] }'
630  );
631  assert.strictEqual(
632    util.inspect(getterAndSetter, true),
633    '[Object: null prototype] { [c]: [Getter/Setter] }'
634  );
635}
636
637// Exceptions should print the error message, not '{}'.
638{
639  [
640    new Error(),
641    new Error('FAIL'),
642    new TypeError('FAIL'),
643    new SyntaxError('FAIL'),
644  ].forEach((err) => {
645    assert.strictEqual(util.inspect(err), err.stack);
646  });
647  assert.throws(
648    () => undef(), // eslint-disable-line no-undef
649    (e) => {
650      assert.strictEqual(util.inspect(e), e.stack);
651      return true;
652    }
653  );
654
655  const ex = util.inspect(new Error('FAIL'), true);
656  assert(ex.includes('Error: FAIL'));
657  assert(ex.includes('[stack]'));
658  assert(ex.includes('[message]'));
659}
660
661{
662  const tmp = Error.stackTraceLimit;
663  Error.stackTraceLimit = 0;
664  const err = new Error('foo');
665  const err2 = new Error('foo\nbar');
666  assert.strictEqual(util.inspect(err, { compact: true }), '[Error: foo]');
667  assert(err.stack);
668  delete err.stack;
669  assert(!err.stack);
670  assert.strictEqual(util.inspect(err, { compact: true }), '[Error: foo]');
671  assert.strictEqual(
672    util.inspect(err2, { compact: true }),
673    '[Error: foo\nbar]'
674  );
675
676  err.bar = true;
677  err2.bar = true;
678
679  assert.strictEqual(
680    util.inspect(err, { compact: true }),
681    '{ [Error: foo] bar: true }'
682  );
683  assert.strictEqual(
684    util.inspect(err2, { compact: true }),
685    '{ [Error: foo\nbar]\n  bar: true }'
686  );
687  assert.strictEqual(
688    util.inspect(err, { compact: true, breakLength: 5 }),
689    '{ [Error: foo]\n  bar: true }'
690  );
691  assert.strictEqual(
692    util.inspect(err, { compact: true, breakLength: 1 }),
693    '{ [Error: foo]\n  bar:\n   true }'
694  );
695  assert.strictEqual(
696    util.inspect(err2, { compact: true, breakLength: 5 }),
697    '{ [Error: foo\nbar]\n  bar: true }'
698  );
699  assert.strictEqual(
700    util.inspect(err, { compact: false }),
701    '[Error: foo] {\n  bar: true\n}'
702  );
703  assert.strictEqual(
704    util.inspect(err2, { compact: false }),
705    '[Error: foo\nbar] {\n  bar: true\n}'
706  );
707
708  Error.stackTraceLimit = tmp;
709}
710
711// Prevent enumerable error properties from being printed.
712{
713  let err = new Error();
714  err.message = 'foobar';
715  let out = util.inspect(err).split('\n');
716  assert.strictEqual(out[0], 'Error: foobar');
717  assert(out[out.length - 1].startsWith('    at '));
718  // Reset the error, the stack is otherwise not recreated.
719  err = new Error();
720  err.message = 'foobar';
721  err.name = 'Unique';
722  Object.defineProperty(err, 'stack', { value: err.stack, enumerable: true });
723  out = util.inspect(err).split('\n');
724  assert.strictEqual(out[0], 'Unique: foobar');
725  assert(out[out.length - 1].startsWith('    at '));
726  err.name = 'Baz';
727  out = util.inspect(err).split('\n');
728  assert.strictEqual(out[0], 'Unique: foobar');
729  assert.strictEqual(out[out.length - 2], "  name: 'Baz'");
730  assert.strictEqual(out[out.length - 1], '}');
731}
732
733// Doesn't capture stack trace.
734{
735  function BadCustomError(msg) {
736    Error.call(this);
737    Object.defineProperty(this, 'message',
738                          { value: msg, enumerable: false });
739    Object.defineProperty(this, 'name',
740                          { value: 'BadCustomError', enumerable: false });
741  }
742  Object.setPrototypeOf(BadCustomError.prototype, Error.prototype);
743  Object.setPrototypeOf(BadCustomError, Error);
744  assert.strictEqual(
745    util.inspect(new BadCustomError('foo')),
746    '[BadCustomError: foo]'
747  );
748}
749
750// Tampered error stack or name property (different type than string).
751// Note: Symbols are not supported by `Error#toString()` which is called by
752// accessing the `stack` property.
753[
754  [404, '404: foo', '[404]'],
755  [0, '0: foo', '[RangeError: foo]'],
756  [0n, '0: foo', '[RangeError: foo]'],
757  [null, 'null: foo', '[RangeError: foo]'],
758  [undefined, 'RangeError: foo', '[RangeError: foo]'],
759  [false, 'false: foo', '[RangeError: foo]'],
760  ['', 'foo', '[RangeError: foo]'],
761  [[1, 2, 3], '1,2,3: foo', '[1,2,3]'],
762].forEach(([value, outputStart, stack]) => {
763  let err = new RangeError('foo');
764  err.name = value;
765  assert(
766    util.inspect(err).startsWith(outputStart),
767    util.format(
768      'The name set to %o did not result in the expected output "%s"',
769      value,
770      outputStart
771    )
772  );
773
774  err = new RangeError('foo');
775  err.stack = value;
776  assert.strictEqual(util.inspect(err), stack);
777});
778
779// https://github.com/nodejs/node-v0.x-archive/issues/1941
780assert.strictEqual(util.inspect(Object.create(Date.prototype)), 'Date {}');
781
782// https://github.com/nodejs/node-v0.x-archive/issues/1944
783{
784  const d = new Date();
785  d.toUTCString = null;
786  util.inspect(d);
787}
788
789// Should not throw.
790{
791  const d = new Date();
792  d.toISOString = null;
793  util.inspect(d);
794}
795
796// Should not throw.
797{
798  const r = /regexp/;
799  r.toString = null;
800  util.inspect(r);
801}
802
803// See https://github.com/nodejs/node-v0.x-archive/issues/2225
804{
805  const x = { [util.inspect.custom]: util.inspect };
806  assert(util.inspect(x).includes(
807    '[Symbol(nodejs.util.inspect.custom)]: [Function: inspect] {\n'));
808}
809
810// `util.inspect` should display the escaped value of a key.
811{
812  const w = {
813    '\\': 1,
814    '\\\\': 2,
815    '\\\\\\': 3,
816    '\\\\\\\\': 4,
817    '\n': 5,
818    '\r': 6
819  };
820
821  const y = ['a', 'b', 'c'];
822  y['\\\\'] = 'd';
823  y['\n'] = 'e';
824  y['\r'] = 'f';
825
826  assert.strictEqual(
827    util.inspect(w),
828    "{ '\\\\': 1, '\\\\\\\\': 2, '\\\\\\\\\\\\': 3, " +
829    "'\\\\\\\\\\\\\\\\': 4, '\\n': 5, '\\r': 6 }"
830  );
831  assert.strictEqual(
832    util.inspect(y),
833    "[ 'a', 'b', 'c', '\\\\\\\\': 'd', " +
834    "'\\n': 'e', '\\r': 'f' ]"
835  );
836}
837
838// Test util.inspect.styles and util.inspect.colors.
839{
840  function testColorStyle(style, input, implicit) {
841    const colorName = util.inspect.styles[style];
842    let color = ['', ''];
843    if (util.inspect.colors[colorName])
844      color = util.inspect.colors[colorName];
845
846    const withoutColor = util.inspect(input, false, 0, false);
847    const withColor = util.inspect(input, false, 0, true);
848    const expect = `\u001b[${color[0]}m${withoutColor}\u001b[${color[1]}m`;
849    assert.strictEqual(
850      withColor,
851      expect,
852      `util.inspect color for style ${style}`);
853  }
854
855  testColorStyle('special', function() {});
856  testColorStyle('number', 123.456);
857  testColorStyle('boolean', true);
858  testColorStyle('undefined', undefined);
859  testColorStyle('null', null);
860  testColorStyle('string', 'test string');
861  testColorStyle('date', new Date());
862  testColorStyle('regexp', /regexp/);
863}
864
865// An object with "hasOwnProperty" overwritten should not throw.
866util.inspect({ hasOwnProperty: null });
867
868// New API, accepts an "options" object.
869{
870  const subject = { foo: 'bar', hello: 31, a: { b: { c: { d: 0 } } } };
871  Object.defineProperty(subject, 'hidden', { enumerable: false, value: null });
872
873  assert.strictEqual(
874    util.inspect(subject, { showHidden: false }).includes('hidden'),
875    false
876  );
877  assert.strictEqual(
878    util.inspect(subject, { showHidden: true }).includes('hidden'),
879    true
880  );
881  assert.strictEqual(
882    util.inspect(subject, { colors: false }).includes('\u001b[32m'),
883    false
884  );
885  assert.strictEqual(
886    util.inspect(subject, { colors: true }).includes('\u001b[32m'),
887    true
888  );
889  assert.strictEqual(
890    util.inspect(subject, { depth: 2 }).includes('c: [Object]'),
891    true
892  );
893  assert.strictEqual(
894    util.inspect(subject, { depth: 0 }).includes('a: [Object]'),
895    true
896  );
897  assert.strictEqual(
898    util.inspect(subject, { depth: null }).includes('{ d: 0 }'),
899    true
900  );
901  assert.strictEqual(
902    util.inspect(subject, { depth: undefined }).includes('{ d: 0 }'),
903    true
904  );
905}
906
907{
908  // "customInspect" option can enable/disable calling [util.inspect.custom]().
909  const subject = { [util.inspect.custom]: () => 123 };
910
911  assert.strictEqual(
912    util.inspect(subject, { customInspect: true }).includes('123'),
913    true
914  );
915  assert.strictEqual(
916    util.inspect(subject, { customInspect: true }).includes('inspect'),
917    false
918  );
919  assert.strictEqual(
920    util.inspect(subject, { customInspect: false }).includes('123'),
921    false
922  );
923  assert.strictEqual(
924    util.inspect(subject, { customInspect: false }).includes('inspect'),
925    true
926  );
927
928  // A custom [util.inspect.custom]() should be able to return other Objects.
929  subject[util.inspect.custom] = () => ({ foo: 'bar' });
930
931  assert.strictEqual(util.inspect(subject), "{ foo: 'bar' }");
932
933  subject[util.inspect.custom] = common.mustCall((depth, opts) => {
934    const clone = { ...opts };
935    // This might change at some point but for now we keep the stylize function.
936    // The function should either be documented or an alternative should be
937    // implemented.
938    assert.strictEqual(typeof opts.stylize, 'function');
939    assert.strictEqual(opts.seen, undefined);
940    assert.strictEqual(opts.budget, undefined);
941    assert.strictEqual(opts.indentationLvl, undefined);
942    assert.strictEqual(opts.showHidden, false);
943    assert.deepStrictEqual(
944      new Set(Object.keys(util.inspect.defaultOptions).concat(['stylize'])),
945      new Set(Object.keys(opts))
946    );
947    opts.showHidden = true;
948    return { [util.inspect.custom]: common.mustCall((depth, opts2) => {
949      assert.deepStrictEqual(clone, opts2);
950    }) };
951  });
952
953  util.inspect(subject);
954
955  // util.inspect.custom is a shared symbol which can be accessed as
956  // Symbol.for("nodejs.util.inspect.custom").
957  const inspect = Symbol.for('nodejs.util.inspect.custom');
958
959  subject[inspect] = () => ({ baz: 'quux' });
960
961  assert.strictEqual(util.inspect(subject), '{ baz: \'quux\' }');
962
963  subject[inspect] = (depth, opts) => {
964    assert.strictEqual(opts.customInspectOptions, true);
965    assert.strictEqual(opts.seen, null);
966    return {};
967  };
968
969  util.inspect(subject, { customInspectOptions: true, seen: null });
970}
971
972{
973  const subject = { [util.inspect.custom]: common.mustCall((depth, opts) => {
974    assert.strictEqual(depth, null);
975    assert.strictEqual(opts.compact, true);
976  }) };
977  util.inspect(subject, { depth: null, compact: true });
978}
979
980{
981  // Returning `this` from a custom inspection function works.
982  const subject = { a: 123, [util.inspect.custom]() { return this; } };
983  const UIC = 'nodejs.util.inspect.custom';
984  assert.strictEqual(
985    util.inspect(subject),
986    `{\n  a: 123,\n  [Symbol(${UIC})]: [Function: [${UIC}]]\n}`
987  );
988}
989
990// Verify that it's possible to use the stylize function to manipulate input.
991assert.strictEqual(
992  util.inspect([1, 2, 3], { stylize() { return 'x'; } }),
993  '[ x, x, x ]'
994);
995
996// Using `util.inspect` with "colors" option should produce as many lines as
997// without it.
998{
999  function testLines(input) {
1000    const countLines = (str) => (str.match(/\n/g) || []).length;
1001    const withoutColor = util.inspect(input);
1002    const withColor = util.inspect(input, { colors: true });
1003    assert.strictEqual(countLines(withoutColor), countLines(withColor));
1004  }
1005
1006  const bigArray = new Array(100).fill().map((value, index) => index);
1007
1008  testLines([1, 2, 3, 4, 5, 6, 7]);
1009  testLines(bigArray);
1010  testLines({ foo: 'bar', baz: 35, b: { a: 35 } });
1011  testLines({ a: { a: 3, b: 1, c: 1, d: 1, e: 1, f: 1, g: 1, h: 1 }, b: 1 });
1012  testLines({
1013    foo: 'bar',
1014    baz: 35,
1015    b: { a: 35 },
1016    veryLongKey: 'very long value',
1017    evenLongerKey: ['with even longer value in array']
1018  });
1019}
1020
1021// Test boxed primitives output the correct values.
1022assert.strictEqual(util.inspect(new String('test')), "[String: 'test']");
1023assert.strictEqual(
1024  util.inspect(new String('test'), { colors: true }),
1025  "\u001b[32m[String: 'test']\u001b[39m"
1026);
1027assert.strictEqual(
1028  util.inspect(Object(Symbol('test'))),
1029  '[Symbol: Symbol(test)]'
1030);
1031assert.strictEqual(util.inspect(new Boolean(false)), '[Boolean: false]');
1032assert.strictEqual(
1033  util.inspect(Object.setPrototypeOf(new Boolean(true), null)),
1034  '[Boolean (null prototype): true]'
1035);
1036assert.strictEqual(util.inspect(new Number(0)), '[Number: 0]');
1037assert.strictEqual(
1038  util.inspect(
1039    Object.defineProperty(
1040      Object.setPrototypeOf(new Number(-0), Array.prototype),
1041      Symbol.toStringTag,
1042      { value: 'Foobar' }
1043    )
1044  ),
1045  '[Number (Array): -0] [Foobar]'
1046);
1047assert.strictEqual(util.inspect(new Number(-1.1)), '[Number: -1.1]');
1048assert.strictEqual(util.inspect(new Number(13.37)), '[Number: 13.37]');
1049
1050// Test boxed primitives with own properties.
1051{
1052  const str = new String('baz');
1053  str.foo = 'bar';
1054  assert.strictEqual(util.inspect(str), "[String: 'baz'] { foo: 'bar' }");
1055
1056  const bool = new Boolean(true);
1057  bool.foo = 'bar';
1058  assert.strictEqual(util.inspect(bool), "[Boolean: true] { foo: 'bar' }");
1059
1060  const num = new Number(13.37);
1061  num.foo = 'bar';
1062  assert.strictEqual(util.inspect(num), "[Number: 13.37] { foo: 'bar' }");
1063
1064  const sym = Object(Symbol('foo'));
1065  sym.foo = 'bar';
1066  assert.strictEqual(util.inspect(sym), "[Symbol: Symbol(foo)] { foo: 'bar' }");
1067
1068  const big = Object(BigInt(55));
1069  big.foo = 'bar';
1070  assert.strictEqual(util.inspect(big), "[BigInt: 55n] { foo: 'bar' }");
1071}
1072
1073// Test es6 Symbol.
1074if (typeof Symbol !== 'undefined') {
1075  assert.strictEqual(util.inspect(Symbol()), 'Symbol()');
1076  assert.strictEqual(util.inspect(Symbol(123)), 'Symbol(123)');
1077  assert.strictEqual(util.inspect(Symbol('hi')), 'Symbol(hi)');
1078  assert.strictEqual(util.inspect([Symbol()]), '[ Symbol() ]');
1079  assert.strictEqual(util.inspect({ foo: Symbol() }), '{ foo: Symbol() }');
1080
1081  const options = { showHidden: true };
1082  let subject = {};
1083
1084  subject[Symbol('sym\nbol')] = 42;
1085
1086  assert.strictEqual(util.inspect(subject), '{ [Symbol(sym\\nbol)]: 42 }');
1087  assert.strictEqual(
1088    util.inspect(subject, options),
1089    '{ [Symbol(sym\\nbol)]: 42 }'
1090  );
1091
1092  Object.defineProperty(
1093    subject,
1094    Symbol(),
1095    { enumerable: false, value: 'non-enum' });
1096  assert.strictEqual(util.inspect(subject), '{ [Symbol(sym\\nbol)]: 42 }');
1097  assert.strictEqual(
1098    util.inspect(subject, options),
1099    "{ [Symbol(sym\\nbol)]: 42, [Symbol()]: 'non-enum' }"
1100  );
1101
1102  subject = [1, 2, 3];
1103  subject[Symbol('symbol')] = 42;
1104
1105  assert.strictEqual(util.inspect(subject),
1106                     '[ 1, 2, 3, [Symbol(symbol)]: 42 ]');
1107}
1108
1109// Test Set.
1110{
1111  assert.strictEqual(util.inspect(new Set()), 'Set(0) {}');
1112  assert.strictEqual(util.inspect(new Set([1, 2, 3])), 'Set(3) { 1, 2, 3 }');
1113  const set = new Set(['foo']);
1114  set.bar = 42;
1115  assert.strictEqual(
1116    util.inspect(set, { showHidden: true }),
1117    "Set(1) { 'foo', bar: 42 }"
1118  );
1119}
1120
1121// Test circular Set.
1122{
1123  const set = new Set();
1124  set.add(set);
1125  assert.strictEqual(util.inspect(set), '<ref *1> Set(1) { [Circular *1] }');
1126}
1127
1128// Test Map.
1129{
1130  assert.strictEqual(util.inspect(new Map()), 'Map(0) {}');
1131  assert.strictEqual(util.inspect(new Map([[1, 'a'], [2, 'b'], [3, 'c']])),
1132                     "Map(3) { 1 => 'a', 2 => 'b', 3 => 'c' }");
1133  const map = new Map([['foo', null]]);
1134  map.bar = 42;
1135  assert.strictEqual(util.inspect(map, true),
1136                     "Map(1) { 'foo' => null, bar: 42 }");
1137}
1138
1139// Test circular Map.
1140{
1141  const map = new Map();
1142  map.set(map, 'map');
1143  assert.strictEqual(
1144    inspect(map),
1145    "<ref *1> Map(1) { [Circular *1] => 'map' }"
1146  );
1147  map.set(map, map);
1148  assert.strictEqual(
1149    inspect(map),
1150    '<ref *1> Map(1) { [Circular *1] => [Circular *1] }'
1151  );
1152  map.delete(map);
1153  map.set('map', map);
1154  assert.strictEqual(
1155    inspect(map),
1156    "<ref *1> Map(1) { 'map' => [Circular *1] }"
1157  );
1158}
1159
1160// Test multiple circular references.
1161{
1162  const obj = {};
1163  obj.a = [obj];
1164  obj.b = {};
1165  obj.b.inner = obj.b;
1166  obj.b.obj = obj;
1167
1168  assert.strictEqual(
1169    inspect(obj),
1170    '<ref *1> {\n' +
1171    '  a: [ [Circular *1] ],\n' +
1172    '  b: <ref *2> { inner: [Circular *2], obj: [Circular *1] }\n' +
1173    '}'
1174  );
1175}
1176
1177// Test Promise.
1178{
1179  const resolved = Promise.resolve(3);
1180  assert.strictEqual(util.inspect(resolved), 'Promise { 3 }');
1181
1182  const rejected = Promise.reject(3);
1183  assert.strictEqual(util.inspect(rejected), 'Promise { <rejected> 3 }');
1184  // Squelch UnhandledPromiseRejection.
1185  rejected.catch(() => {});
1186
1187  const pending = new Promise(() => {});
1188  assert.strictEqual(util.inspect(pending), 'Promise { <pending> }');
1189
1190  const promiseWithProperty = Promise.resolve('foo');
1191  promiseWithProperty.bar = 42;
1192  assert.strictEqual(util.inspect(promiseWithProperty),
1193                     "Promise { 'foo', bar: 42 }");
1194}
1195
1196// Make sure it doesn't choke on polyfills. Unlike Set/Map, there is no standard
1197// interface to synchronously inspect a Promise, so our techniques only work on
1198// a bonafide native Promise.
1199{
1200  const oldPromise = Promise;
1201  global.Promise = function() { this.bar = 42; };
1202  assert.strictEqual(util.inspect(new Promise()), '{ bar: 42 }');
1203  global.Promise = oldPromise;
1204}
1205
1206// Test Map iterators.
1207{
1208  const map = new Map([['foo', 'bar']]);
1209  assert.strictEqual(util.inspect(map.keys()), '[Map Iterator] { \'foo\' }');
1210  const mapValues = map.values();
1211  Object.defineProperty(mapValues, Symbol.toStringTag, { value: 'Foo' });
1212  assert.strictEqual(
1213    util.inspect(mapValues),
1214    '[Foo] [Map Iterator] { \'bar\' }'
1215  );
1216  map.set('A', 'B!');
1217  assert.strictEqual(util.inspect(map.entries(), { maxArrayLength: 1 }),
1218                     "[Map Entries] { [ 'foo', 'bar' ], ... 1 more item }");
1219  // Make sure the iterator doesn't get consumed.
1220  const keys = map.keys();
1221  assert.strictEqual(util.inspect(keys), "[Map Iterator] { 'foo', 'A' }");
1222  assert.strictEqual(util.inspect(keys), "[Map Iterator] { 'foo', 'A' }");
1223  keys.extra = true;
1224  assert.strictEqual(
1225    util.inspect(keys, { maxArrayLength: 0 }),
1226    '[Map Iterator] { ... 2 more items, extra: true }');
1227}
1228
1229// Test Set iterators.
1230{
1231  const aSet = new Set([1]);
1232  assert.strictEqual(util.inspect(aSet.entries(), { compact: false }),
1233                     '[Set Entries] {\n  [\n    1,\n    1\n  ]\n}');
1234  aSet.add(3);
1235  assert.strictEqual(util.inspect(aSet.keys()), '[Set Iterator] { 1, 3 }');
1236  assert.strictEqual(util.inspect(aSet.values()), '[Set Iterator] { 1, 3 }');
1237  const setEntries = aSet.entries();
1238  Object.defineProperty(setEntries, Symbol.toStringTag, { value: 'Foo' });
1239  assert.strictEqual(util.inspect(setEntries),
1240                     '[Foo] [Set Entries] { [ 1, 1 ], [ 3, 3 ] }');
1241  // Make sure the iterator doesn't get consumed.
1242  const keys = aSet.keys();
1243  Object.defineProperty(keys, Symbol.toStringTag, { value: null });
1244  assert.strictEqual(util.inspect(keys), '[Set Iterator] { 1, 3 }');
1245  assert.strictEqual(util.inspect(keys), '[Set Iterator] { 1, 3 }');
1246  keys.extra = true;
1247  assert.strictEqual(
1248    util.inspect(keys, { maxArrayLength: 1 }),
1249    '[Set Iterator] { 1, ... 1 more item, extra: true }');
1250}
1251
1252// Minimal inspection should still return as much information as possible about
1253// the constructor and Symbol.toStringTag.
1254{
1255  class Foo {
1256    get [Symbol.toStringTag]() {
1257      return 'ABC';
1258    }
1259  }
1260  const a = new Foo();
1261  assert.strictEqual(inspect(a, { depth: -1 }), 'Foo [ABC] {}');
1262  a.foo = true;
1263  assert.strictEqual(inspect(a, { depth: -1 }), '[Foo [ABC]]');
1264  Object.defineProperty(a, Symbol.toStringTag, {
1265    value: 'Foo',
1266    configurable: true,
1267    writable: true
1268  });
1269  assert.strictEqual(inspect(a, { depth: -1 }), '[Foo]');
1270  delete a[Symbol.toStringTag];
1271  Object.setPrototypeOf(a, null);
1272  assert.strictEqual(inspect(a, { depth: -1 }), '[Foo: null prototype]');
1273  delete a.foo;
1274  assert.strictEqual(inspect(a, { depth: -1 }), '[Foo: null prototype] {}');
1275  Object.defineProperty(a, Symbol.toStringTag, {
1276    value: 'ABC',
1277    configurable: true
1278  });
1279  assert.strictEqual(
1280    inspect(a, { depth: -1 }),
1281    '[Foo: null prototype] [ABC] {}'
1282  );
1283  Object.defineProperty(a, Symbol.toStringTag, {
1284    value: 'Foo',
1285    configurable: true
1286  });
1287  assert.strictEqual(
1288    inspect(a, { depth: -1 }),
1289    '[Object: null prototype] [Foo] {}'
1290  );
1291}
1292
1293// Test alignment of items in container.
1294// Assumes that the first numeric character is the start of an item.
1295{
1296  function checkAlignment(container, start, lineX, end) {
1297    const lines = util.inspect(container).split('\n');
1298    lines.forEach((line, i) => {
1299      if (i === 0) {
1300        assert.strictEqual(line, start);
1301      } else if (i === lines.length - 1) {
1302        assert.strictEqual(line, end);
1303      } else {
1304        let expected = lineX.replace('X', i - 1);
1305        if (i !== lines.length - 2)
1306          expected += ',';
1307        assert.strictEqual(line, expected);
1308      }
1309    });
1310  }
1311
1312  const bigArray = [];
1313  for (let i = 0; i < 100; i++) {
1314    bigArray.push(i);
1315  }
1316
1317  const obj = {};
1318  bigArray.forEach((prop) => {
1319    obj[prop] = null;
1320  });
1321
1322  checkAlignment(obj, '{', "  'X': null", '}');
1323  checkAlignment(new Set(bigArray), 'Set(100) {', '  X', '}');
1324  checkAlignment(
1325    new Map(bigArray.map((number) => [number, null])),
1326    'Map(100) {', '  X => null', '}'
1327  );
1328}
1329
1330
1331// Test display of constructors.
1332{
1333  class ObjectSubclass {}
1334  class ArraySubclass extends Array {}
1335  class SetSubclass extends Set {}
1336  class MapSubclass extends Map {}
1337  class PromiseSubclass extends Promise {}
1338
1339  const x = new ObjectSubclass();
1340  x.foo = 42;
1341  assert.strictEqual(util.inspect(x),
1342                     'ObjectSubclass { foo: 42 }');
1343  assert.strictEqual(util.inspect(new ArraySubclass(1, 2, 3)),
1344                     'ArraySubclass(3) [ 1, 2, 3 ]');
1345  assert.strictEqual(util.inspect(new SetSubclass([1, 2, 3])),
1346                     'SetSubclass(3) [Set] { 1, 2, 3 }');
1347  assert.strictEqual(util.inspect(new MapSubclass([['foo', 42]])),
1348                     "MapSubclass(1) [Map] { 'foo' => 42 }");
1349  assert.strictEqual(util.inspect(new PromiseSubclass(() => {})),
1350                     'PromiseSubclass [Promise] { <pending> }');
1351  assert.strictEqual(
1352    util.inspect({ a: { b: new ArraySubclass([1, [2], 3]) } }, { depth: 1 }),
1353    '{ a: { b: [ArraySubclass] } }'
1354  );
1355  assert.strictEqual(
1356    util.inspect(Object.setPrototypeOf(x, null)),
1357    '[ObjectSubclass: null prototype] { foo: 42 }'
1358  );
1359}
1360
1361// Empty and circular before depth.
1362{
1363  const arr = [[[[]]]];
1364  assert.strictEqual(util.inspect(arr), '[ [ [ [] ] ] ]');
1365  arr[0][0][0][0] = [];
1366  assert.strictEqual(util.inspect(arr), '[ [ [ [Array] ] ] ]');
1367  arr[0][0][0] = {};
1368  assert.strictEqual(util.inspect(arr), '[ [ [ {} ] ] ]');
1369  arr[0][0][0] = { a: 2 };
1370  assert.strictEqual(util.inspect(arr), '[ [ [ [Object] ] ] ]');
1371  arr[0][0][0] = arr;
1372  assert.strictEqual(util.inspect(arr), '<ref *1> [ [ [ [Circular *1] ] ] ]');
1373  arr[0][0][0] = arr[0][0];
1374  assert.strictEqual(util.inspect(arr), '[ [ <ref *1> [ [Circular *1] ] ] ]');
1375}
1376
1377// Corner cases.
1378{
1379  const x = { constructor: 42 };
1380  assert.strictEqual(util.inspect(x), '{ constructor: 42 }');
1381}
1382
1383{
1384  const x = {};
1385  Object.defineProperty(x, 'constructor', {
1386    get: function() {
1387      throw new Error('should not access constructor');
1388    },
1389    enumerable: true
1390  });
1391  assert.strictEqual(util.inspect(x), '{ constructor: [Getter] }');
1392}
1393
1394{
1395  const x = new function() {}; // eslint-disable-line new-parens
1396  assert.strictEqual(util.inspect(x), '{}');
1397}
1398
1399{
1400  const x = Object.create(null);
1401  assert.strictEqual(util.inspect(x), '[Object: null prototype] {}');
1402}
1403
1404{
1405  const x = [];
1406  x[''] = 1;
1407  assert.strictEqual(util.inspect(x), "[ '': 1 ]");
1408}
1409
1410// The following maxArrayLength tests were introduced after v6.0.0 was released.
1411// Do not backport to v5/v4 unless all of
1412// https://github.com/nodejs/node/pull/6334 is backported.
1413{
1414  const x = new Array(101).fill();
1415  assert(util.inspect(x).endsWith('1 more item\n]'));
1416  assert(!util.inspect(x, { maxArrayLength: 101 }).endsWith('1 more item\n]'));
1417  assert.strictEqual(
1418    util.inspect(x, { maxArrayLength: -1 }),
1419    '[ ... 101 more items ]'
1420  );
1421  assert.strictEqual(util.inspect(x, { maxArrayLength: 0 }),
1422                     '[ ... 101 more items ]');
1423}
1424
1425{
1426  const x = Array(101);
1427  assert.strictEqual(util.inspect(x, { maxArrayLength: 0 }),
1428                     '[ ... 101 more items ]');
1429  assert(!util.inspect(x, { maxArrayLength: null }).endsWith('1 more item\n]'));
1430  assert(!util.inspect(
1431    x, { maxArrayLength: Infinity }
1432  ).endsWith('1 more item ]'));
1433}
1434
1435{
1436  const x = new Uint8Array(101);
1437  assert(util.inspect(x).endsWith('1 more item\n]'));
1438  assert(!util.inspect(x, { maxArrayLength: 101 }).includes('1 more item'));
1439  assert.strictEqual(util.inspect(x, { maxArrayLength: 0 }),
1440                     'Uint8Array(101) [ ... 101 more items ]');
1441  assert(!util.inspect(x, { maxArrayLength: null }).includes('1 more item'));
1442  assert(util.inspect(x, { maxArrayLength: Infinity }).endsWith(' 0, 0\n]'));
1443}
1444
1445{
1446  const obj = { foo: 'abc', bar: 'xyz' };
1447  const oneLine = util.inspect(obj, { breakLength: Infinity });
1448  // Subtract four for the object's two curly braces and two spaces of padding.
1449  // Add one more to satisfy the strictly greater than condition in the code.
1450  const breakpoint = oneLine.length - 5;
1451  const twoLines = util.inspect(obj, { breakLength: breakpoint });
1452
1453  assert.strictEqual(oneLine, "{ foo: 'abc', bar: 'xyz' }");
1454  assert.strictEqual(
1455    util.inspect(obj, { breakLength: breakpoint + 1 }),
1456    twoLines
1457  );
1458  assert.strictEqual(twoLines, "{\n  foo: 'abc',\n  bar: 'xyz'\n}");
1459}
1460
1461// util.inspect.defaultOptions tests.
1462{
1463  const arr = new Array(101).fill();
1464  const obj = { a: { a: { a: { a: 1 } } } };
1465
1466  const oldOptions = { ...util.inspect.defaultOptions };
1467
1468  // Set single option through property assignment.
1469  util.inspect.defaultOptions.maxArrayLength = null;
1470  assert(!/1 more item/.test(util.inspect(arr)));
1471  util.inspect.defaultOptions.maxArrayLength = oldOptions.maxArrayLength;
1472  assert(/1 more item/.test(util.inspect(arr)));
1473  util.inspect.defaultOptions.depth = null;
1474  assert(!/Object/.test(util.inspect(obj)));
1475  util.inspect.defaultOptions.depth = oldOptions.depth;
1476  assert(/Object/.test(util.inspect(obj)));
1477  assert.strictEqual(
1478    JSON.stringify(util.inspect.defaultOptions),
1479    JSON.stringify(oldOptions)
1480  );
1481
1482  // Set multiple options through object assignment.
1483  util.inspect.defaultOptions = { maxArrayLength: null, depth: 2 };
1484  assert(!/1 more item/.test(util.inspect(arr)));
1485  assert(/Object/.test(util.inspect(obj)));
1486  util.inspect.defaultOptions = oldOptions;
1487  assert(/1 more item/.test(util.inspect(arr)));
1488  assert(/Object/.test(util.inspect(obj)));
1489  assert.strictEqual(
1490    JSON.stringify(util.inspect.defaultOptions),
1491    JSON.stringify(oldOptions)
1492  );
1493
1494  assert.throws(() => {
1495    util.inspect.defaultOptions = null;
1496  }, {
1497    code: 'ERR_INVALID_ARG_TYPE',
1498    name: 'TypeError',
1499    message: 'The "options" argument must be of type object. ' +
1500             'Received null'
1501  }
1502  );
1503
1504  assert.throws(() => {
1505    util.inspect.defaultOptions = 'bad';
1506  }, {
1507    code: 'ERR_INVALID_ARG_TYPE',
1508    name: 'TypeError',
1509    message: 'The "options" argument must be of type object. ' +
1510             "Received type string ('bad')"
1511  }
1512  );
1513}
1514
1515util.inspect(process);
1516
1517// Setting custom inspect property to a non-function should do nothing.
1518{
1519  const obj = { [util.inspect.custom]: 'fhqwhgads' };
1520  assert.strictEqual(
1521    util.inspect(obj),
1522    "{ [Symbol(nodejs.util.inspect.custom)]: 'fhqwhgads' }"
1523  );
1524}
1525
1526{
1527  // @@toStringTag
1528  const obj = { [Symbol.toStringTag]: 'a' };
1529  assert.strictEqual(
1530    util.inspect(obj),
1531    "{ [Symbol(Symbol.toStringTag)]: 'a' }"
1532  );
1533  Object.defineProperty(obj, Symbol.toStringTag, {
1534    value: 'a',
1535    enumerable: false
1536  });
1537  assert.strictEqual(util.inspect(obj), 'Object [a] {}');
1538  assert.strictEqual(
1539    util.inspect(obj, { showHidden: true }),
1540    "{ [Symbol(Symbol.toStringTag)]: 'a' }"
1541  );
1542
1543  class Foo {
1544    constructor() {
1545      this.foo = 'bar';
1546    }
1547
1548    get [Symbol.toStringTag]() {
1549      return this.foo;
1550    }
1551  }
1552
1553  assert.strictEqual(util.inspect(
1554    Object.create(null, { [Symbol.toStringTag]: { value: 'foo' } })),
1555                     '[Object: null prototype] [foo] {}');
1556
1557  assert.strictEqual(util.inspect(new Foo()), "Foo [bar] { foo: 'bar' }");
1558
1559  assert.strictEqual(
1560    util.inspect(new (class extends Foo {})()),
1561    "Foo [bar] { foo: 'bar' }");
1562
1563  assert.strictEqual(
1564    util.inspect(Object.create(Object.create(Foo.prototype), {
1565      foo: { value: 'bar', enumerable: true }
1566    })),
1567    "Foo [bar] { foo: 'bar' }");
1568
1569  class ThrowingClass {
1570    get [Symbol.toStringTag]() {
1571      throw new Error('toStringTag error');
1572    }
1573  }
1574
1575  assert.throws(() => util.inspect(new ThrowingClass()), /toStringTag error/);
1576
1577  class NotStringClass {
1578    get [Symbol.toStringTag]() {
1579      return null;
1580    }
1581  }
1582
1583  assert.strictEqual(util.inspect(new NotStringClass()),
1584                     'NotStringClass {}');
1585}
1586
1587{
1588  const o = {
1589    a: [1, 2, [[
1590      'Lorem ipsum dolor\nsit amet,\tconsectetur adipiscing elit, sed do ' +
1591        'eiusmod tempor incididunt ut labore et dolore magna aliqua.',
1592      'test',
1593      'foo']], 4],
1594    b: new Map([['za', 1], ['zb', 'test']])
1595  };
1596
1597  let out = util.inspect(o, { compact: true, depth: 5, breakLength: 80 });
1598  let expect = [
1599    '{ a:',
1600    '   [ 1,',
1601    '     2,',
1602    "     [ [ 'Lorem ipsum dolor\\nsit amet,\\tconsectetur adipiscing elit, " +
1603      "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',",
1604    "         'test',",
1605    "         'foo' ] ],",
1606    '     4 ],',
1607    "  b: Map(2) { 'za' => 1, 'zb' => 'test' } }",
1608  ].join('\n');
1609  assert.strictEqual(out, expect);
1610
1611  out = util.inspect(o, { compact: false, depth: 5, breakLength: 60 });
1612  expect = [
1613    '{',
1614    '  a: [',
1615    '    1,',
1616    '    2,',
1617    '    [',
1618    '      [',
1619    "        'Lorem ipsum dolor\\n' +",
1620    "          'sit amet,\\tconsectetur adipiscing elit, sed do eiusmod " +
1621      "tempor incididunt ut labore et dolore magna aliqua.',",
1622    "        'test',",
1623    "        'foo'",
1624    '      ]',
1625    '    ],',
1626    '    4',
1627    '  ],',
1628    '  b: Map(2) {',
1629    "    'za' => 1,",
1630    "    'zb' => 'test'",
1631    '  }',
1632    '}',
1633  ].join('\n');
1634  assert.strictEqual(out, expect);
1635
1636  out = util.inspect(o.a[2][0][0], { compact: false, breakLength: 30 });
1637  expect = [
1638    "'Lorem ipsum dolor\\n' +",
1639    "  'sit amet,\\tconsectetur adipiscing elit, sed do eiusmod tempor " +
1640      "incididunt ut labore et dolore magna aliqua.'",
1641  ].join('\n');
1642  assert.strictEqual(out, expect);
1643
1644  out = util.inspect(
1645    '12345678901234567890123456789012345678901234567890',
1646    { compact: false, breakLength: 3 });
1647  expect = "'12345678901234567890123456789012345678901234567890'";
1648  assert.strictEqual(out, expect);
1649
1650  out = util.inspect(
1651    '12 45 78 01 34 67 90 23 56 89 123456789012345678901234567890',
1652    { compact: false, breakLength: 3 });
1653  expect = [
1654    "'12 45 78 01 34 67 90 23 56 89 123456789012345678901234567890'",
1655  ].join('\n');
1656  assert.strictEqual(out, expect);
1657
1658  o.a = () => {};
1659  o.b = new Number(3);
1660  out = util.inspect(o, { compact: false, breakLength: 3 });
1661  expect = [
1662    '{',
1663    '  a: [Function (anonymous)],',
1664    '  b: [Number: 3]',
1665    '}',
1666  ].join('\n');
1667  assert.strictEqual(out, expect);
1668
1669  out = util.inspect(o, { compact: false, breakLength: 3, showHidden: true });
1670  expect = [
1671    '{',
1672    '  a: [Function (anonymous)] {',
1673    '    [length]: 0,',
1674    "    [name]: ''",
1675    '  },',
1676    '  b: [Number: 3]',
1677    '}',
1678  ].join('\n');
1679  assert.strictEqual(out, expect);
1680
1681  o[util.inspect.custom] = () => 42;
1682  out = util.inspect(o, { compact: false, breakLength: 3 });
1683  expect = '42';
1684  assert.strictEqual(out, expect);
1685
1686  o[util.inspect.custom] = () => '12 45 78 01 34 67 90 23';
1687  out = util.inspect(o, { compact: false, breakLength: 3 });
1688  expect = '12 45 78 01 34 67 90 23';
1689  assert.strictEqual(out, expect);
1690
1691  o[util.inspect.custom] = () => ({ a: '12 45 78 01 34 67 90 23' });
1692  out = util.inspect(o, { compact: false, breakLength: 3 });
1693  expect = "{\n  a: '12 45 78 01 34 67 90 23'\n}";
1694  assert.strictEqual(out, expect);
1695}
1696
1697// Check compact indentation.
1698{
1699  const typed = new Uint8Array();
1700  typed.buffer.foo = true;
1701  const set = new Set([[1, 2]]);
1702  const promise = Promise.resolve([[1, set]]);
1703  const map = new Map([[promise, typed]]);
1704  map.set(set.values(), map.values());
1705
1706  let out = util.inspect(map, { compact: false, showHidden: true, depth: 9 });
1707  let expected = [
1708    'Map(2) {',
1709    '  Promise {',
1710    '    [',
1711    '      [',
1712    '        1,',
1713    '        Set(1) {',
1714    '          [',
1715    '            1,',
1716    '            2,',
1717    '            [length]: 2',
1718    '          ]',
1719    '        },',
1720    '        [length]: 2',
1721    '      ],',
1722    '      [length]: 1',
1723    '    ]',
1724    '  } => Uint8Array(0) [',
1725    '    [BYTES_PER_ELEMENT]: 1,',
1726    '    [length]: 0,',
1727    '    [byteLength]: 0,',
1728    '    [byteOffset]: 0,',
1729    '    [buffer]: ArrayBuffer {',
1730    '      byteLength: 0,',
1731    '      foo: true',
1732    '    }',
1733    '  ],',
1734    '  [Set Iterator] {',
1735    '    [',
1736    '      1,',
1737    '      2,',
1738    '      [length]: 2',
1739    '    ],',
1740    "    [Symbol(Symbol.toStringTag)]: 'Set Iterator'",
1741    '  } => <ref *1> [Map Iterator] {',
1742    '    Uint8Array(0) [',
1743    '      [BYTES_PER_ELEMENT]: 1,',
1744    '      [length]: 0,',
1745    '      [byteLength]: 0,',
1746    '      [byteOffset]: 0,',
1747    '      [buffer]: ArrayBuffer {',
1748    '        byteLength: 0,',
1749    '        foo: true',
1750    '      }',
1751    '    ],',
1752    '    [Circular *1],',
1753    "    [Symbol(Symbol.toStringTag)]: 'Map Iterator'",
1754    '  }',
1755    '}',
1756  ].join('\n');
1757
1758  assert.strict.equal(out, expected);
1759
1760  out = util.inspect(map, { compact: 2, showHidden: true, depth: 9 });
1761
1762  expected = [
1763    'Map(2) {',
1764    '  Promise {',
1765    '    [',
1766    '      [',
1767    '        1,',
1768    '        Set(1) { [ 1, 2, [length]: 2 ] },',
1769    '        [length]: 2',
1770    '      ],',
1771    '      [length]: 1',
1772    '    ]',
1773    '  } => Uint8Array(0) [',
1774    '    [BYTES_PER_ELEMENT]: 1,',
1775    '    [length]: 0,',
1776    '    [byteLength]: 0,',
1777    '    [byteOffset]: 0,',
1778    '    [buffer]: ArrayBuffer { byteLength: 0, foo: true }',
1779    '  ],',
1780    '  [Set Iterator] {',
1781    '    [ 1, 2, [length]: 2 ],',
1782    "    [Symbol(Symbol.toStringTag)]: 'Set Iterator'",
1783    '  } => <ref *1> [Map Iterator] {',
1784    '    Uint8Array(0) [',
1785    '      [BYTES_PER_ELEMENT]: 1,',
1786    '      [length]: 0,',
1787    '      [byteLength]: 0,',
1788    '      [byteOffset]: 0,',
1789    '      [buffer]: ArrayBuffer { byteLength: 0, foo: true }',
1790    '    ],',
1791    '    [Circular *1],',
1792    "    [Symbol(Symbol.toStringTag)]: 'Map Iterator'",
1793    '  }',
1794    '}',
1795  ].join('\n');
1796
1797  assert.strict.equal(out, expected);
1798
1799  out = util.inspect(map, {
1800    showHidden: true, depth: 9, breakLength: 4, compact: true
1801  });
1802  expected = [
1803    'Map(2) {',
1804    '  Promise {',
1805    '    [ [ 1,',
1806    '        Set(1) {',
1807    '          [ 1,',
1808    '            2,',
1809    '            [length]: 2 ] },',
1810    '        [length]: 2 ],',
1811    '      [length]: 1 ] } => Uint8Array(0) [',
1812    '    [BYTES_PER_ELEMENT]: 1,',
1813    '    [length]: 0,',
1814    '    [byteLength]: 0,',
1815    '    [byteOffset]: 0,',
1816    '    [buffer]: ArrayBuffer {',
1817    '      byteLength: 0,',
1818    '      foo: true } ],',
1819    '  [Set Iterator] {',
1820    '    [ 1,',
1821    '      2,',
1822    '      [length]: 2 ],',
1823    '    [Symbol(Symbol.toStringTag)]:',
1824    "     'Set Iterator' } => <ref *1> [Map Iterator] {",
1825    '    Uint8Array(0) [',
1826    '      [BYTES_PER_ELEMENT]: 1,',
1827    '      [length]: 0,',
1828    '      [byteLength]: 0,',
1829    '      [byteOffset]: 0,',
1830    '      [buffer]: ArrayBuffer {',
1831    '        byteLength: 0,',
1832    '        foo: true } ],',
1833    '    [Circular *1],',
1834    '    [Symbol(Symbol.toStringTag)]:',
1835    "     'Map Iterator' } }",
1836  ].join('\n');
1837
1838  assert.strict.equal(out, expected);
1839}
1840
1841{ // Test WeakMap && WeakSet
1842  const obj = {};
1843  const arr = [];
1844  const weakMap = new WeakMap([[obj, arr], [arr, obj]]);
1845  let out = util.inspect(weakMap, { showHidden: true });
1846  let expect = 'WeakMap { [ [length]: 0 ] => {}, {} => [ [length]: 0 ] }';
1847  assert.strictEqual(out, expect);
1848
1849  out = util.inspect(weakMap);
1850  expect = 'WeakMap { <items unknown> }';
1851  assert.strictEqual(out, expect);
1852
1853  out = util.inspect(weakMap, { maxArrayLength: 0, showHidden: true });
1854  expect = 'WeakMap { ... 2 more items }';
1855  assert.strictEqual(out, expect);
1856
1857  weakMap.extra = true;
1858  out = util.inspect(weakMap, { maxArrayLength: 1, showHidden: true });
1859  // It is not possible to determine the output reliable.
1860  expect = 'WeakMap { [ [length]: 0 ] => {}, ... 1 more item, extra: true }';
1861  let expectAlt = 'WeakMap { {} => [ [length]: 0 ], ... 1 more item, ' +
1862                  'extra: true }';
1863  assert(out === expect || out === expectAlt,
1864         `Found: "${out}"\nrather than: "${expect}"\nor: "${expectAlt}"`);
1865
1866  // Test WeakSet
1867  arr.push(1);
1868  const weakSet = new WeakSet([obj, arr]);
1869  out = util.inspect(weakSet, { showHidden: true });
1870  expect = 'WeakSet { [ 1, [length]: 1 ], {} }';
1871  assert.strictEqual(out, expect);
1872
1873  out = util.inspect(weakSet);
1874  expect = 'WeakSet { <items unknown> }';
1875  assert.strictEqual(out, expect);
1876
1877  out = util.inspect(weakSet, { maxArrayLength: -2, showHidden: true });
1878  expect = 'WeakSet { ... 2 more items }';
1879  assert.strictEqual(out, expect);
1880
1881  weakSet.extra = true;
1882  out = util.inspect(weakSet, { maxArrayLength: 1, showHidden: true });
1883  // It is not possible to determine the output reliable.
1884  expect = 'WeakSet { {}, ... 1 more item, extra: true }';
1885  expectAlt = 'WeakSet { [ 1, [length]: 1 ], ... 1 more item, extra: true }';
1886  assert(out === expect || out === expectAlt,
1887         `Found: "${out}"\nrather than: "${expect}"\nor: "${expectAlt}"`);
1888  // Keep references to the WeakMap entries, otherwise they could be GCed too
1889  // early.
1890  assert(obj && arr);
1891}
1892
1893{ // Test argument objects.
1894  const args = (function() { return arguments; })('a');
1895  assert.strictEqual(util.inspect(args), "[Arguments] { '0': 'a' }");
1896}
1897
1898{
1899  // Test that a long linked list can be inspected without throwing an error.
1900  const list = {};
1901  let head = list;
1902  // A linked list of length 100k should be inspectable in some way, even though
1903  // the real cutoff value is much lower than 100k.
1904  for (let i = 0; i < 100000; i++)
1905    head = head.next = {};
1906  assert.strictEqual(
1907    util.inspect(list),
1908    '{ next: { next: { next: [Object] } } }'
1909  );
1910  const longList = util.inspect(list, { depth: Infinity });
1911  const match = longList.match(/next/g);
1912  assert(match.length > 500 && match.length < 10000);
1913  assert(longList.includes('[Object: Inspection interrupted ' +
1914    'prematurely. Maximum call stack size exceeded.]'));
1915}
1916
1917// Do not escape single quotes if no double quote or backtick is present.
1918assert.strictEqual(util.inspect("'"), '"\'"');
1919assert.strictEqual(util.inspect('"\''), '`"\'`');
1920// eslint-disable-next-line no-template-curly-in-string
1921assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'");
1922
1923// Errors should visualize as much information as possible.
1924// If the name is not included in the stack, visualize it as well.
1925[
1926  [class Foo extends TypeError {}, 'test'],
1927  [class Foo extends TypeError {}, undefined],
1928  [class BarError extends Error {}, 'test'],
1929  [class BazError extends Error {
1930    get name() {
1931      return 'BazError';
1932    }
1933  }, undefined],
1934].forEach(([Class, message], i) => {
1935  console.log('Test %i', i);
1936  const foo = new Class(message);
1937  const name = foo.name;
1938  const extra = Class.name.includes('Error') ? '' : ` [${foo.name}]`;
1939  assert(
1940    util.inspect(foo).startsWith(
1941      `${Class.name}${extra}${message ? `: ${message}` : '\n'}`),
1942    util.inspect(foo)
1943  );
1944  Object.defineProperty(foo, Symbol.toStringTag, {
1945    value: 'WOW',
1946    writable: true,
1947    configurable: true
1948  });
1949  const stack = foo.stack;
1950  foo.stack = 'This is a stack';
1951  assert.strictEqual(
1952    util.inspect(foo),
1953    '[This is a stack]'
1954  );
1955  foo.stack = stack;
1956  assert(
1957    util.inspect(foo).startsWith(
1958      `${Class.name} [WOW]${extra}${message ? `: ${message}` : '\n'}`),
1959    util.inspect(foo)
1960  );
1961  Object.setPrototypeOf(foo, null);
1962  assert(
1963    util.inspect(foo).startsWith(
1964      `[${name}: null prototype] [WOW]${message ? `: ${message}` : '\n'}`
1965    ),
1966    util.inspect(foo)
1967  );
1968  foo.bar = true;
1969  delete foo[Symbol.toStringTag];
1970  assert(
1971    util.inspect(foo).startsWith(
1972      `[${name}: null prototype]${message ? `: ${message}` : '\n'}`),
1973    util.inspect(foo)
1974  );
1975  foo.stack = 'This is a stack';
1976  assert.strictEqual(
1977    util.inspect(foo),
1978    '[[Error: null prototype]: This is a stack] { bar: true }'
1979  );
1980  foo.stack = stack.split('\n')[0];
1981  assert.strictEqual(
1982    util.inspect(foo),
1983    `[[${name}: null prototype]${message ? `: ${message}` : ''}] { bar: true }`
1984  );
1985});
1986
1987// Verify that classes are properly inspected.
1988[
1989  /* eslint-disable spaced-comment, no-multi-spaces, brace-style */
1990  // The whitespace is intentional.
1991  [class   { }, '[class (anonymous)]'],
1992  [class extends Error { log() {} }, '[class (anonymous) extends Error]'],
1993  [class A { constructor(a) { this.a = a; } log() { return this.a; } },
1994   '[class A]'],
1995  [class
1996  // Random { // comments /* */ are part of the toString() result
1997  /* eslint-disable-next-line space-before-blocks */
1998  äß/**/extends/*{*/TypeError{}, '[class äß extends TypeError]'],
1999  /* The whitespace and new line is intended! */
2000  // Foobar !!!
2001  [class X   extends /****/ Error
2002  // More comments
2003  {}, '[class X extends Error]'],
2004  /* eslint-enable spaced-comment, no-multi-spaces, brace-style */
2005].forEach(([clazz, string]) => {
2006  const inspected = util.inspect(clazz);
2007  assert.strictEqual(inspected, string);
2008  Object.defineProperty(clazz, Symbol.toStringTag, {
2009    value: 'Woohoo'
2010  });
2011  const parts = inspected.slice(0, -1).split(' ');
2012  const [, name, ...rest] = parts;
2013  rest.unshift('[Woohoo]');
2014  if (rest.length) {
2015    rest[rest.length - 1] += ']';
2016  }
2017  assert.strictEqual(
2018    util.inspect(clazz),
2019    ['[class', name, ...rest].join(' ')
2020  );
2021  if (rest.length) {
2022    rest[rest.length - 1] = rest[rest.length - 1].slice(0, -1);
2023    rest.length = 1;
2024  }
2025  Object.setPrototypeOf(clazz, Map.prototype);
2026  assert.strictEqual(
2027    util.inspect(clazz),
2028    ['[class', name, '[Map]', ...rest].join(' ') + ']'
2029  );
2030  Object.setPrototypeOf(clazz, null);
2031  assert.strictEqual(
2032    util.inspect(clazz),
2033    ['[class', name, ...rest, 'extends [null prototype]]'].join(' ')
2034  );
2035  Object.defineProperty(clazz, 'name', { value: 'Foo' });
2036  const res = ['[class', 'Foo', ...rest, 'extends [null prototype]]'].join(' ');
2037  assert.strictEqual(util.inspect(clazz), res);
2038  clazz.foo = true;
2039  assert.strictEqual(util.inspect(clazz), `${res} { foo: true }`);
2040});
2041
2042// "class" properties should not be detected as "class".
2043{
2044  // eslint-disable-next-line space-before-function-paren
2045  let obj = { class () {} };
2046  assert.strictEqual(
2047    util.inspect(obj),
2048    '{ class: [Function: class] }'
2049  );
2050  obj = { class: () => {} };
2051  assert.strictEqual(
2052    util.inspect(obj),
2053    '{ class: [Function: class] }'
2054  );
2055  obj = { ['class Foo {}']() {} };
2056  assert.strictEqual(
2057    util.inspect(obj),
2058    "{ 'class Foo {}': [Function: class Foo {}] }"
2059  );
2060  function Foo() {}
2061  Object.defineProperty(Foo, 'toString', { value: () => 'class Foo {}' });
2062  assert.strictEqual(
2063    util.inspect(Foo),
2064    '[Function: Foo]'
2065  );
2066  function fn() {}
2067  Object.defineProperty(fn, 'name', { value: 'class Foo {}' });
2068  assert.strictEqual(
2069    util.inspect(fn),
2070    '[Function: class Foo {}]'
2071  );
2072}
2073
2074// Verify that throwing in valueOf and toString still produces nice results.
2075[
2076  [new String(55), "[String: '55']"],
2077  [new Boolean(true), '[Boolean: true]'],
2078  [new Number(55), '[Number: 55]'],
2079  [Object(BigInt(55)), '[BigInt: 55n]'],
2080  [Object(Symbol('foo')), '[Symbol: Symbol(foo)]'],
2081  [function() {}, '[Function (anonymous)]'],
2082  [() => {}, '[Function (anonymous)]'],
2083  [[1, 2], '[ 1, 2 ]'],
2084  [[, , 5, , , , ], '[ <2 empty items>, 5, <3 empty items> ]'],
2085  [{ a: 5 }, '{ a: 5 }'],
2086  [new Set([1, 2]), 'Set(2) { 1, 2 }'],
2087  [new Map([[1, 2]]), 'Map(1) { 1 => 2 }'],
2088  [new Set([1, 2]).entries(), '[Set Entries] { [ 1, 1 ], [ 2, 2 ] }'],
2089  [new Map([[1, 2]]).keys(), '[Map Iterator] { 1 }'],
2090  [new Date(2000), '1970-01-01T00:00:02.000Z'],
2091  [new Uint8Array(2), 'Uint8Array(2) [ 0, 0 ]'],
2092  [new Promise((resolve) => setTimeout(resolve, 10)), 'Promise { <pending> }'],
2093  [new WeakSet(), 'WeakSet { <items unknown> }'],
2094  [new WeakMap(), 'WeakMap { <items unknown> }'],
2095  [/foobar/g, '/foobar/g'],
2096].forEach(([value, expected]) => {
2097  Object.defineProperty(value, 'valueOf', {
2098    get() {
2099      throw new Error('valueOf');
2100    }
2101  });
2102  Object.defineProperty(value, 'toString', {
2103    get() {
2104      throw new Error('toString');
2105    }
2106  });
2107  assert.strictEqual(util.inspect(value), expected);
2108  value.foo = 'bar';
2109  assert.notStrictEqual(util.inspect(value), expected);
2110  delete value.foo;
2111  value[Symbol('foo')] = 'yeah';
2112  assert.notStrictEqual(util.inspect(value), expected);
2113});
2114
2115// Verify that having no prototype still produces nice results.
2116[
2117  [[1, 3, 4], '[Array(3): null prototype] [ 1, 3, 4 ]'],
2118  [new Set([1, 2]), '[Set(2): null prototype] { 1, 2 }'],
2119  [new Map([[1, 2]]), '[Map(1): null prototype] { 1 => 2 }'],
2120  [new Promise((resolve) => setTimeout(resolve, 10)),
2121   '[Promise: null prototype] { <pending> }'],
2122  [new WeakSet(), '[WeakSet: null prototype] { <items unknown> }'],
2123  [new WeakMap(), '[WeakMap: null prototype] { <items unknown> }'],
2124  [new Uint8Array(2), '[Uint8Array(2): null prototype] [ 0, 0 ]'],
2125  [new Uint16Array(2), '[Uint16Array(2): null prototype] [ 0, 0 ]'],
2126  [new Uint32Array(2), '[Uint32Array(2): null prototype] [ 0, 0 ]'],
2127  [new Int8Array(2), '[Int8Array(2): null prototype] [ 0, 0 ]'],
2128  [new Int16Array(2), '[Int16Array(2): null prototype] [ 0, 0 ]'],
2129  [new Int32Array(2), '[Int32Array(2): null prototype] [ 0, 0 ]'],
2130  [new Float32Array(2), '[Float32Array(2): null prototype] [ 0, 0 ]'],
2131  [new Float64Array(2), '[Float64Array(2): null prototype] [ 0, 0 ]'],
2132  [new BigInt64Array(2), '[BigInt64Array(2): null prototype] [ 0n, 0n ]'],
2133  [new BigUint64Array(2), '[BigUint64Array(2): null prototype] [ 0n, 0n ]'],
2134  [new ArrayBuffer(16), '[ArrayBuffer: null prototype] {\n' +
2135     '  [Uint8Contents]: <00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>,\n' +
2136     '  byteLength: undefined\n}'],
2137  [new DataView(new ArrayBuffer(16)),
2138   '[DataView: null prototype] {\n  byteLength: undefined,\n  ' +
2139     'byteOffset: undefined,\n  buffer: undefined\n}'],
2140  [new SharedArrayBuffer(2), '[SharedArrayBuffer: null prototype] ' +
2141     '{\n  [Uint8Contents]: <00 00>,\n  byteLength: undefined\n}'],
2142  [/foobar/, '[RegExp: null prototype] /foobar/'],
2143  [new Date('Sun, 14 Feb 2010 11:48:40 GMT'),
2144   '[Date: null prototype] 2010-02-14T11:48:40.000Z'],
2145].forEach(([value, expected]) => {
2146  assert.strictEqual(
2147    util.inspect(Object.setPrototypeOf(value, null)),
2148    expected
2149  );
2150  value.foo = 'bar';
2151  assert.notStrictEqual(util.inspect(value), expected);
2152  delete value.foo;
2153  value[Symbol('foo')] = 'yeah';
2154  assert.notStrictEqual(util.inspect(value), expected);
2155});
2156
2157// Verify that subclasses with and without prototype produce nice results.
2158[
2159  [RegExp, ['foobar', 'g'], '/foobar/g'],
2160  [WeakSet, [[{}]], '{ <items unknown> }'],
2161  [WeakMap, [[[{}, {}]]], '{ <items unknown> }'],
2162  [BigInt64Array,
2163   [10],
2164   '[\n  0n, 0n, 0n, 0n, 0n,\n  0n, 0n, 0n, 0n, 0n\n]'],
2165  [Date, ['Sun, 14 Feb 2010 11:48:40 GMT'], '2010-02-14T11:48:40.000Z'],
2166  [Date, ['invalid_date'], 'Invalid Date'],
2167].forEach(([base, input, rawExpected]) => {
2168  class Foo extends base {}
2169  const value = new Foo(...input);
2170  const symbol = value[Symbol.toStringTag];
2171  const size = base.name.includes('Array') ? `(${input[0]})` : '';
2172  const expected = `Foo${size} ${symbol ? `[${symbol}] ` : ''}${rawExpected}`;
2173  const expectedWithoutProto =
2174    `[${base.name}${size}: null prototype] ${rawExpected}`;
2175  assert.strictEqual(util.inspect(value), expected);
2176  value.foo = 'bar';
2177  assert.notStrictEqual(util.inspect(value), expected);
2178  delete value.foo;
2179  assert.strictEqual(
2180    util.inspect(Object.setPrototypeOf(value, null)),
2181    expectedWithoutProto
2182  );
2183  value.foo = 'bar';
2184  let res = util.inspect(value);
2185  assert.notStrictEqual(res, expectedWithoutProto);
2186  assert(/foo: 'bar'/.test(res), res);
2187  delete value.foo;
2188  value[Symbol('foo')] = 'yeah';
2189  res = util.inspect(value);
2190  assert.notStrictEqual(res, expectedWithoutProto);
2191  assert(/\[Symbol\(foo\)]: 'yeah'/.test(res), res);
2192});
2193
2194assert.strictEqual(inspect(1n), '1n');
2195assert.strictEqual(inspect(Object(-1n)), '[BigInt: -1n]');
2196assert.strictEqual(inspect(Object(13n)), '[BigInt: 13n]');
2197assert.strictEqual(inspect(new BigInt64Array([0n])), 'BigInt64Array(1) [ 0n ]');
2198assert.strictEqual(
2199  inspect(new BigUint64Array([0n])), 'BigUint64Array(1) [ 0n ]');
2200
2201// Verify non-enumerable keys get escaped.
2202{
2203  const obj = {};
2204  Object.defineProperty(obj, 'Non\nenumerable\tkey', { value: true });
2205  assert.strictEqual(
2206    util.inspect(obj, { showHidden: true }),
2207    '{ [Non\\nenumerable\\tkey]: true }'
2208  );
2209}
2210
2211// Check for special colors.
2212{
2213  const special = inspect.colors[inspect.styles.special];
2214  const string = inspect.colors[inspect.styles.string];
2215
2216  assert.strictEqual(
2217    inspect(new WeakSet(), { colors: true }),
2218    `WeakSet { \u001b[${special[0]}m<items unknown>\u001b[${special[1]}m }`
2219  );
2220  assert.strictEqual(
2221    inspect(new WeakMap(), { colors: true }),
2222    `WeakMap { \u001b[${special[0]}m<items unknown>\u001b[${special[1]}m }`
2223  );
2224  assert.strictEqual(
2225    inspect(new Promise(() => {}), { colors: true }),
2226    `Promise { \u001b[${special[0]}m<pending>\u001b[${special[1]}m }`
2227  );
2228
2229  const rejection = Promise.reject('Oh no!');
2230  assert.strictEqual(
2231    inspect(rejection, { colors: true }),
2232    `Promise { \u001b[${special[0]}m<rejected>\u001b[${special[1]}m ` +
2233    `\u001b[${string[0]}m'Oh no!'\u001b[${string[1]}m }`
2234  );
2235  rejection.catch(() => {});
2236
2237  // Verify that aliases do not show up as key while checking `inspect.colors`.
2238  const colors = Object.keys(inspect.colors);
2239  const aliases = Object.getOwnPropertyNames(inspect.colors)
2240                  .filter((c) => !colors.includes(c));
2241  assert(!colors.includes('grey'));
2242  assert(colors.includes('gray'));
2243  // Verify that all aliases are correctly mapped.
2244  for (const alias of aliases) {
2245    assert(Array.isArray(inspect.colors[alias]));
2246  }
2247  // Check consistent naming.
2248  [
2249    'black',
2250    'red',
2251    'green',
2252    'yellow',
2253    'blue',
2254    'magenta',
2255    'cyan',
2256    'white',
2257  ].forEach((color, i) => {
2258    assert.deepStrictEqual(inspect.colors[color], [30 + i, 39]);
2259    assert.deepStrictEqual(inspect.colors[`${color}Bright`], [90 + i, 39]);
2260    const bgColor = `bg${color[0].toUpperCase()}${color.slice(1)}`;
2261    assert.deepStrictEqual(inspect.colors[bgColor], [40 + i, 49]);
2262    assert.deepStrictEqual(inspect.colors[`${bgColor}Bright`], [100 + i, 49]);
2263  });
2264
2265  // Unknown colors are handled gracefully:
2266  const stringStyle = inspect.styles.string;
2267  inspect.styles.string = 'UNKNOWN';
2268  assert.strictEqual(inspect('foobar', { colors: true }), "'foobar'");
2269  inspect.styles.string = stringStyle;
2270}
2271
2272assert.strictEqual(
2273  inspect([1, 3, 2], { sorted: true }),
2274  inspect([1, 3, 2])
2275);
2276assert.strictEqual(
2277  inspect({ c: 3, a: 1, b: 2 }, { sorted: true }),
2278  '{ a: 1, b: 2, c: 3 }'
2279);
2280assert.strictEqual(
2281  inspect(
2282    { a200: 4, a100: 1, a102: 3, a101: 2 },
2283    { sorted(a, b) { return b.localeCompare(a); } }
2284  ),
2285  '{ a200: 4, a102: 3, a101: 2, a100: 1 }'
2286);
2287
2288// Non-indices array properties are sorted as well.
2289{
2290  const arr = [3, 2, 1];
2291  arr.b = 2;
2292  arr.c = 3;
2293  arr.a = 1;
2294  arr[Symbol('b')] = true;
2295  arr[Symbol('a')] = false;
2296  assert.strictEqual(
2297    inspect(arr, { sorted: true }),
2298    '[ 3, 2, 1, [Symbol(a)]: false, [Symbol(b)]: true, a: 1, b: 2, c: 3 ]'
2299  );
2300}
2301
2302// Manipulate the prototype in weird ways.
2303{
2304  let obj = { a: true };
2305  let value = (function() { return function() {}; })();
2306  Object.setPrototypeOf(value, null);
2307  Object.setPrototypeOf(obj, value);
2308  assert.strictEqual(
2309    util.inspect(obj),
2310    'Object <[Function (null prototype) (anonymous)]> { a: true }'
2311  );
2312  assert.strictEqual(
2313    util.inspect(obj, { colors: true }),
2314    'Object <\u001b[36m[Function (null prototype) (anonymous)]\u001b[39m> ' +
2315      '{ a: \u001b[33mtrue\u001b[39m }'
2316  );
2317
2318  obj = { a: true };
2319  value = [];
2320  Object.setPrototypeOf(value, null);
2321  Object.setPrototypeOf(obj, value);
2322  assert.strictEqual(
2323    util.inspect(obj),
2324    'Object <[Array(0): null prototype] []> { a: true }'
2325  );
2326
2327  function StorageObject() {}
2328  StorageObject.prototype = Object.create(null);
2329  assert.strictEqual(
2330    util.inspect(new StorageObject()),
2331    'StorageObject <[Object: null prototype] {}> {}'
2332  );
2333
2334  obj = [1, 2, 3];
2335  Object.setPrototypeOf(obj, Number.prototype);
2336  assert.strictEqual(inspect(obj), "Number { '0': 1, '1': 2, '2': 3 }");
2337
2338  Object.setPrototypeOf(obj, Object.create(null));
2339  assert.strictEqual(
2340    inspect(obj),
2341    "Array <[Object: null prototype] {}> { '0': 1, '1': 2, '2': 3 }"
2342  );
2343
2344  StorageObject.prototype = Object.create(null);
2345  Object.setPrototypeOf(StorageObject.prototype, Object.create(null));
2346  Object.setPrototypeOf(
2347    Object.getPrototypeOf(StorageObject.prototype),
2348    Object.create(null)
2349  );
2350  assert.strictEqual(
2351    util.inspect(new StorageObject()),
2352    'StorageObject <Object <Object <[Object: null prototype] {}>>> {}'
2353  );
2354  assert.strictEqual(
2355    util.inspect(new StorageObject(), { depth: 1 }),
2356    'StorageObject <Object <Object <Complex prototype>>> {}'
2357  );
2358}
2359
2360// Check that the fallback always works.
2361{
2362  const obj = new Set([1, 2]);
2363  const iterator = obj[Symbol.iterator];
2364  Object.setPrototypeOf(obj, null);
2365  Object.defineProperty(obj, Symbol.iterator, {
2366    value: iterator,
2367    configurable: true
2368  });
2369  assert.strictEqual(util.inspect(obj), '[Set(2): null prototype] { 1, 2 }');
2370  Object.defineProperty(obj, Symbol.iterator, {
2371    value: true,
2372    configurable: true
2373  });
2374  Object.defineProperty(obj, 'size', {
2375    value: NaN,
2376    configurable: true,
2377    enumerable: true
2378  });
2379  assert.strictEqual(
2380    util.inspect(obj),
2381    '[Set(2): null prototype] { 1, 2, size: NaN }'
2382  );
2383}
2384
2385// Check the getter option.
2386{
2387  let foo = 1;
2388  const get = { get foo() { return foo; } };
2389  const getset = {
2390    get foo() { return foo; },
2391    set foo(val) { foo = val; },
2392    get inc() { return ++foo; }
2393  };
2394  const thrower = { get foo() { throw new Error('Oops'); } };
2395  assert.strictEqual(
2396    inspect(get, { getters: true, colors: true }),
2397    '{ foo: \u001b[36m[Getter:\u001b[39m ' +
2398      '\u001b[33m1\u001b[39m\u001b[36m]\u001b[39m }');
2399  assert.strictEqual(
2400    inspect(thrower, { getters: true }),
2401    '{ foo: [Getter: <Inspection threw (Oops)>] }');
2402  assert.strictEqual(
2403    inspect(getset, { getters: true }),
2404    '{ foo: [Getter/Setter: 1], inc: [Getter: 2] }');
2405  assert.strictEqual(
2406    inspect(getset, { getters: 'get' }),
2407    '{ foo: [Getter/Setter], inc: [Getter: 3] }');
2408  assert.strictEqual(
2409    inspect(getset, { getters: 'set' }),
2410    '{ foo: [Getter/Setter: 3], inc: [Getter] }');
2411  getset.foo = new Set([[{ a: true }, 2, {}], 'foobar', { x: 1 }]);
2412  assert.strictEqual(
2413    inspect(getset, { getters: true }),
2414    '{\n  foo: [Getter/Setter] Set(3) { [ [Object], 2, {} ], ' +
2415      "'foobar', { x: 1 } },\n  inc: [Getter: NaN]\n}");
2416}
2417
2418// Check compact number mode.
2419{
2420  let obj = {
2421    a: {
2422      b: {
2423        x: 5,
2424        c: {
2425          x: '10000000000000000 00000000000000000 '.repeat(1e1),
2426          d: 2,
2427          e: 3
2428        }
2429      }
2430    },
2431    b: [
2432      1,
2433      2,
2434      [ 1, 2, { a: 1, b: 2, c: 3 } ],
2435    ],
2436    c: ['foo', 4, 444444],
2437    d: Array.from({ length: 101 }).map((e, i) => {
2438      return i % 2 === 0 ? i * i : i;
2439    }),
2440    e: Array(6).fill('foobar'),
2441    f: Array(9).fill('foobar'),
2442    g: Array(21).fill('foobar baz'),
2443    h: [100].concat(Array.from({ length: 9 }).map((e, n) => (n))),
2444    long: Array(9).fill('This text is too long for grouping!')
2445  };
2446
2447  let out = util.inspect(obj, { compact: 3, depth: 10, breakLength: 60 });
2448  let expected = [
2449    '{',
2450    '  a: {',
2451    '    b: {',
2452    '      x: 5,',
2453    '      c: {',
2454    "        x: '10000000000000000 00000000000000000 10000000000000000 " +
2455      '00000000000000000 10000000000000000 00000000000000000 ' +
2456      '10000000000000000 00000000000000000 10000000000000000 ' +
2457      '00000000000000000 10000000000000000 00000000000000000 ' +
2458      '10000000000000000 00000000000000000 10000000000000000 ' +
2459      '00000000000000000 10000000000000000 00000000000000000 ' +
2460      "10000000000000000 00000000000000000 ',",
2461    '        d: 2,',
2462    '        e: 3',
2463    '      }',
2464    '    }',
2465    '  },',
2466    '  b: [ 1, 2, [ 1, 2, { a: 1, b: 2, c: 3 } ] ],',
2467    "  c: [ 'foo', 4, 444444 ],",
2468    '  d: [',
2469    '       0,    1,    4,    3,   16,    5,   36,    7,   64,',
2470    '       9,  100,   11,  144,   13,  196,   15,  256,   17,',
2471    '     324,   19,  400,   21,  484,   23,  576,   25,  676,',
2472    '      27,  784,   29,  900,   31, 1024,   33, 1156,   35,',
2473    '    1296,   37, 1444,   39, 1600,   41, 1764,   43, 1936,',
2474    '      45, 2116,   47, 2304,   49, 2500,   51, 2704,   53,',
2475    '    2916,   55, 3136,   57, 3364,   59, 3600,   61, 3844,',
2476    '      63, 4096,   65, 4356,   67, 4624,   69, 4900,   71,',
2477    '    5184,   73, 5476,   75, 5776,   77, 6084,   79, 6400,',
2478    '      81, 6724,   83, 7056,   85, 7396,   87, 7744,   89,',
2479    '    8100,   91, 8464,   93, 8836,   95, 9216,   97, 9604,',
2480    '      99,',
2481    '    ... 1 more item',
2482    '  ],',
2483    '  e: [',
2484    "    'foobar',",
2485    "    'foobar',",
2486    "    'foobar',",
2487    "    'foobar',",
2488    "    'foobar',",
2489    "    'foobar'",
2490    '  ],',
2491    '  f: [',
2492    "    'foobar', 'foobar',",
2493    "    'foobar', 'foobar',",
2494    "    'foobar', 'foobar',",
2495    "    'foobar', 'foobar',",
2496    "    'foobar'",
2497    '  ],',
2498    '  g: [',
2499    "    'foobar baz', 'foobar baz',",
2500    "    'foobar baz', 'foobar baz',",
2501    "    'foobar baz', 'foobar baz',",
2502    "    'foobar baz', 'foobar baz',",
2503    "    'foobar baz', 'foobar baz',",
2504    "    'foobar baz', 'foobar baz',",
2505    "    'foobar baz', 'foobar baz',",
2506    "    'foobar baz', 'foobar baz',",
2507    "    'foobar baz', 'foobar baz',",
2508    "    'foobar baz', 'foobar baz',",
2509    "    'foobar baz'",
2510    '  ],',
2511    '  h: [',
2512    '    100, 0, 1, 2, 3,',
2513    '      4, 5, 6, 7, 8',
2514    '  ],',
2515    '  long: [',
2516    "    'This text is too long for grouping!',",
2517    "    'This text is too long for grouping!',",
2518    "    'This text is too long for grouping!',",
2519    "    'This text is too long for grouping!',",
2520    "    'This text is too long for grouping!',",
2521    "    'This text is too long for grouping!',",
2522    "    'This text is too long for grouping!',",
2523    "    'This text is too long for grouping!',",
2524    "    'This text is too long for grouping!'",
2525    '  ]',
2526    '}',
2527  ].join('\n');
2528
2529  assert.strictEqual(out, expected);
2530
2531  obj = [
2532    1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2533    1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2534    1, 1, 1, 1, 1, 1, 123456789,
2535  ];
2536
2537  out = util.inspect(obj, { compact: 3 });
2538
2539  expected = [
2540    '[',
2541    '  1, 1,         1, 1,',
2542    '  1, 1,         1, 1,',
2543    '  1, 1,         1, 1,',
2544    '  1, 1,         1, 1,',
2545    '  1, 1,         1, 1,',
2546    '  1, 1,         1, 1,',
2547    '  1, 1, 123456789',
2548    ']',
2549  ].join('\n');
2550
2551  assert.strictEqual(out, expected);
2552
2553  // Unicode support. あ has a length of one and a width of two.
2554  obj = [
2555    '123', '123', '123', '123', 'あああ',
2556    '123', '123', '123', '123', 'あああ',
2557  ];
2558
2559  out = util.inspect(obj, { compact: 3 });
2560
2561  expected = [
2562    '[',
2563    "  '123',    '123',",
2564    "  '123',    '123',",
2565    "  'あああ', '123',",
2566    "  '123',    '123',",
2567    "  '123',    'あああ'",
2568    ']',
2569  ].join('\n');
2570
2571  assert.strictEqual(out, expected);
2572
2573  // Verify that array grouping and line consolidation does not happen together.
2574  obj = {
2575    a: {
2576      b: {
2577        x: 5,
2578        c: {
2579          d: 2,
2580          e: 3
2581        }
2582      }
2583    },
2584    b: Array.from({ length: 9 }).map((e, n) => {
2585      return n % 2 === 0 ? 'foobar' : 'baz';
2586    })
2587  };
2588
2589  out = util.inspect(obj, { compact: 1, breakLength: Infinity, colors: true });
2590
2591  expected = [
2592    '{',
2593    '  a: {',
2594    '    b: { x: \u001b[33m5\u001b[39m, c: \u001b[36m[Object]\u001b[39m }',
2595    '  },',
2596    '  b: [',
2597    "    \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,",
2598    "    \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,",
2599    "    \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,",
2600    "    \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,",
2601    "    \u001b[32m'foobar'\u001b[39m",
2602    '  ]',
2603    '}',
2604  ].join('\n');
2605
2606  assert.strictEqual(out, expected);
2607
2608  obj = Array.from({ length: 60 }).map((e, i) => i);
2609  out = util.inspect(obj, { compact: 1, breakLength: Infinity, colors: true });
2610
2611  expected = [
2612    '[',
2613    /* eslint-disable max-len */
2614    '   \u001b[33m0\u001b[39m,  \u001b[33m1\u001b[39m,  \u001b[33m2\u001b[39m,  \u001b[33m3\u001b[39m,',
2615    '   \u001b[33m4\u001b[39m,  \u001b[33m5\u001b[39m,  \u001b[33m6\u001b[39m,  \u001b[33m7\u001b[39m,',
2616    '   \u001b[33m8\u001b[39m,  \u001b[33m9\u001b[39m, \u001b[33m10\u001b[39m, \u001b[33m11\u001b[39m,',
2617    '  \u001b[33m12\u001b[39m, \u001b[33m13\u001b[39m, \u001b[33m14\u001b[39m, \u001b[33m15\u001b[39m,',
2618    '  \u001b[33m16\u001b[39m, \u001b[33m17\u001b[39m, \u001b[33m18\u001b[39m, \u001b[33m19\u001b[39m,',
2619    '  \u001b[33m20\u001b[39m, \u001b[33m21\u001b[39m, \u001b[33m22\u001b[39m, \u001b[33m23\u001b[39m,',
2620    '  \u001b[33m24\u001b[39m, \u001b[33m25\u001b[39m, \u001b[33m26\u001b[39m, \u001b[33m27\u001b[39m,',
2621    '  \u001b[33m28\u001b[39m, \u001b[33m29\u001b[39m, \u001b[33m30\u001b[39m, \u001b[33m31\u001b[39m,',
2622    '  \u001b[33m32\u001b[39m, \u001b[33m33\u001b[39m, \u001b[33m34\u001b[39m, \u001b[33m35\u001b[39m,',
2623    '  \u001b[33m36\u001b[39m, \u001b[33m37\u001b[39m, \u001b[33m38\u001b[39m, \u001b[33m39\u001b[39m,',
2624    '  \u001b[33m40\u001b[39m, \u001b[33m41\u001b[39m, \u001b[33m42\u001b[39m, \u001b[33m43\u001b[39m,',
2625    '  \u001b[33m44\u001b[39m, \u001b[33m45\u001b[39m, \u001b[33m46\u001b[39m, \u001b[33m47\u001b[39m,',
2626    '  \u001b[33m48\u001b[39m, \u001b[33m49\u001b[39m, \u001b[33m50\u001b[39m, \u001b[33m51\u001b[39m,',
2627    '  \u001b[33m52\u001b[39m, \u001b[33m53\u001b[39m, \u001b[33m54\u001b[39m, \u001b[33m55\u001b[39m,',
2628    '  \u001b[33m56\u001b[39m, \u001b[33m57\u001b[39m, \u001b[33m58\u001b[39m, \u001b[33m59\u001b[39m',
2629    /* eslint-enable max-len */
2630    ']',
2631  ].join('\n');
2632
2633  assert.strictEqual(out, expected);
2634
2635  out = util.inspect([1, 2, 3, 4], { compact: 1, colors: true });
2636  expected = '[ \u001b[33m1\u001b[39m, \u001b[33m2\u001b[39m, ' +
2637    '\u001b[33m3\u001b[39m, \u001b[33m4\u001b[39m ]';
2638
2639  assert.strictEqual(out, expected);
2640
2641  obj = [
2642    'Object', 'Function', 'Array',
2643    'Number', 'parseFloat', 'parseInt',
2644    'Infinity', 'NaN', 'undefined',
2645    'Boolean', 'String', 'Symbol',
2646    'Date', 'Promise', 'RegExp',
2647    'Error', 'EvalError', 'RangeError',
2648    'ReferenceError', 'SyntaxError', 'TypeError',
2649    'URIError', 'JSON', 'Math',
2650    'console', 'Intl', 'ArrayBuffer',
2651    'Uint8Array', 'Int8Array', 'Uint16Array',
2652    'Int16Array', 'Uint32Array', 'Int32Array',
2653    'Float32Array', 'Float64Array', 'Uint8ClampedArray',
2654    'BigUint64Array', 'BigInt64Array', 'DataView',
2655    'Map', 'BigInt', 'Set',
2656    'WeakMap', 'WeakSet', 'Proxy',
2657    'Reflect', 'decodeURI', 'decodeURIComponent',
2658    'encodeURI', 'encodeURIComponent', 'escape',
2659    'unescape', 'eval', 'isFinite',
2660    'isNaN', 'SharedArrayBuffer', 'Atomics',
2661    'globalThis', 'WebAssembly', 'global',
2662    'process', 'Buffer', 'URL',
2663    'URLSearchParams', 'TextEncoder', 'TextDecoder',
2664    'clearInterval', 'clearTimeout', 'setInterval',
2665    'setTimeout', 'queueMicrotask', 'clearImmediate',
2666    'setImmediate', 'module', 'require',
2667    'assert', 'async_hooks', 'buffer',
2668    'child_process', 'cluster', 'crypto',
2669    'dgram', 'dns', 'domain',
2670    'events', 'fs', 'http',
2671    'http2', 'https', 'inspector',
2672    'net', 'os', 'path',
2673    'perf_hooks', 'punycode', 'querystring',
2674    'readline', 'repl', 'stream',
2675    'string_decoder', 'tls', 'trace_events',
2676    'tty', 'url', 'v8',
2677    'vm', 'worker_threads', 'zlib',
2678    '_', '_error', 'util',
2679  ];
2680
2681  out = util.inspect(
2682    obj,
2683    { compact: 3, breakLength: 80, maxArrayLength: 250 }
2684  );
2685  expected = [
2686    '[',
2687    "  'Object',          'Function',           'Array',",
2688    "  'Number',          'parseFloat',         'parseInt',",
2689    "  'Infinity',        'NaN',                'undefined',",
2690    "  'Boolean',         'String',             'Symbol',",
2691    "  'Date',            'Promise',            'RegExp',",
2692    "  'Error',           'EvalError',          'RangeError',",
2693    "  'ReferenceError',  'SyntaxError',        'TypeError',",
2694    "  'URIError',        'JSON',               'Math',",
2695    "  'console',         'Intl',               'ArrayBuffer',",
2696    "  'Uint8Array',      'Int8Array',          'Uint16Array',",
2697    "  'Int16Array',      'Uint32Array',        'Int32Array',",
2698    "  'Float32Array',    'Float64Array',       'Uint8ClampedArray',",
2699    "  'BigUint64Array',  'BigInt64Array',      'DataView',",
2700    "  'Map',             'BigInt',             'Set',",
2701    "  'WeakMap',         'WeakSet',            'Proxy',",
2702    "  'Reflect',         'decodeURI',          'decodeURIComponent',",
2703    "  'encodeURI',       'encodeURIComponent', 'escape',",
2704    "  'unescape',        'eval',               'isFinite',",
2705    "  'isNaN',           'SharedArrayBuffer',  'Atomics',",
2706    "  'globalThis',      'WebAssembly',        'global',",
2707    "  'process',         'Buffer',             'URL',",
2708    "  'URLSearchParams', 'TextEncoder',        'TextDecoder',",
2709    "  'clearInterval',   'clearTimeout',       'setInterval',",
2710    "  'setTimeout',      'queueMicrotask',     'clearImmediate',",
2711    "  'setImmediate',    'module',             'require',",
2712    "  'assert',          'async_hooks',        'buffer',",
2713    "  'child_process',   'cluster',            'crypto',",
2714    "  'dgram',           'dns',                'domain',",
2715    "  'events',          'fs',                 'http',",
2716    "  'http2',           'https',              'inspector',",
2717    "  'net',             'os',                 'path',",
2718    "  'perf_hooks',      'punycode',           'querystring',",
2719    "  'readline',        'repl',               'stream',",
2720    "  'string_decoder',  'tls',                'trace_events',",
2721    "  'tty',             'url',                'v8',",
2722    "  'vm',              'worker_threads',     'zlib',",
2723    "  '_',               '_error',             'util'",
2724    ']',
2725  ].join('\n');
2726
2727  assert.strictEqual(out, expected);
2728}
2729
2730{
2731  // Use a fake stack to verify the expected colored outcome.
2732  const stack = [
2733    'TypedError: Wonderful message!',
2734    '    at A.<anonymous> (/test/node_modules/foo/node_modules/bar/baz.js:2:7)',
2735    '    at Module._compile (internal/modules/cjs/loader.js:827:30)',
2736    '    at Fancy (vm.js:697:32)',
2737    // This file is not an actual Node.js core file.
2738    '    at tryModuleLoad (internal/modules/cjs/foo.js:629:12)',
2739    '    at Function.Module._load (internal/modules/cjs/loader.js:621:3)',
2740    // This file is not an actual Node.js core file.
2741    '    at Module.require [as weird/name] (internal/aaaaaa/loader.js:735:19)',
2742    '    at require (internal/modules/cjs/helpers.js:14:16)',
2743    '    at /test/test-util-inspect.js:2239:9',
2744    '    at getActual (assert.js:592:5)',
2745  ];
2746  const isNodeCoreFile = [
2747    false, false, true, true, false, true, false, true, false, true,
2748  ];
2749  const err = new TypeError('Wonderful message!');
2750  err.stack = stack.join('\n');
2751  util.inspect(err, { colors: true }).split('\n').forEach((line, i) => {
2752    let actual = stack[i].replace(/node_modules\/([a-z]+)/g, (a, m) => {
2753      return `node_modules/\u001b[4m${m}\u001b[24m`;
2754    });
2755    if (isNodeCoreFile[i]) {
2756      actual = `\u001b[90m${actual}\u001b[39m`;
2757    }
2758    assert.strictEqual(actual, line);
2759  });
2760}
2761
2762{
2763  // Cross platform checks.
2764  const err = new Error('foo');
2765  util.inspect(err, { colors: true }).split('\n').forEach((line, i) => {
2766    assert(i < 2 || line.startsWith('\u001b[90m'));
2767  });
2768}
2769
2770{
2771  // Tracing class respects inspect depth.
2772  try {
2773    const trace = require('trace_events').createTracing({ categories: ['fo'] });
2774    const actualDepth0 = util.inspect({ trace }, { depth: 0 });
2775    assert.strictEqual(actualDepth0, '{ trace: [Tracing] }');
2776    const actualDepth1 = util.inspect({ trace }, { depth: 1 });
2777    assert.strictEqual(
2778      actualDepth1,
2779      "{ trace: Tracing { enabled: false, categories: 'fo' } }"
2780    );
2781  } catch (err) {
2782    if (err.code !== 'ERR_TRACE_EVENTS_UNAVAILABLE')
2783      throw err;
2784  }
2785}
2786
2787// Inspect prototype properties.
2788{
2789  class Foo extends Map {
2790    prop = false;
2791    prop2 = true;
2792    get abc() {
2793      return true;
2794    }
2795    get def() {
2796      return false;
2797    }
2798    set def(v) {}
2799    get xyz() {
2800      return 'Should be ignored';
2801    }
2802    func(a) {}
2803    [util.inspect.custom]() {
2804      return this;
2805    }
2806  }
2807
2808  class Bar extends Foo {
2809    abc = true;
2810    prop = true;
2811    get xyz() {
2812      return 'YES!';
2813    }
2814    [util.inspect.custom]() {
2815      return this;
2816    }
2817  }
2818
2819  const bar = new Bar();
2820
2821  assert.strictEqual(
2822    inspect(bar),
2823    'Bar(0) [Map] { prop: true, prop2: true, abc: true }'
2824  );
2825  assert.strictEqual(
2826    inspect(bar, { showHidden: true, getters: true, colors: false }),
2827    'Bar(0) [Map] {\n' +
2828    '  prop: true,\n' +
2829    '  prop2: true,\n' +
2830    '  abc: true,\n' +
2831    "  [xyz]: [Getter: 'YES!'],\n" +
2832    '  [def]: [Getter/Setter: false]\n' +
2833    '}'
2834  );
2835  assert.strictEqual(
2836    inspect(bar, { showHidden: true, getters: false, colors: true }),
2837    'Bar(0) [Map] {\n' +
2838    '  prop: \x1B[33mtrue\x1B[39m,\n' +
2839    '  prop2: \x1B[33mtrue\x1B[39m,\n' +
2840    '  abc: \x1B[33mtrue\x1B[39m,\n' +
2841    '  \x1B[2m[xyz]: \x1B[36m[Getter]\x1B[39m\x1B[22m,\n' +
2842    '  \x1B[2m[def]: \x1B[36m[Getter/Setter]\x1B[39m\x1B[22m\n' +
2843    '}'
2844  );
2845
2846  const obj = Object.create({ abc: true, def: 5, toString() {} });
2847  assert.strictEqual(
2848    inspect(obj, { showHidden: true, colors: true }),
2849    '{ \x1B[2mabc: \x1B[33mtrue\x1B[39m\x1B[22m, ' +
2850      '\x1B[2mdef: \x1B[33m5\x1B[39m\x1B[22m }'
2851  );
2852
2853  assert.strictEqual(
2854    inspect(Object.getPrototypeOf(bar), { showHidden: true, getters: true }),
2855    '<ref *1> Foo [Map] {\n' +
2856    '    [constructor]: [class Bar extends Foo] {\n' +
2857    '      [length]: 0,\n' +
2858    '      [prototype]: [Circular *1],\n' +
2859    "      [name]: 'Bar',\n" +
2860    '      [Symbol(Symbol.species)]: [Getter: <Inspection threw ' +
2861      "(Symbol.prototype.toString requires that 'this' be a Symbol)>]\n" +
2862    '    },\n' +
2863    "    [xyz]: [Getter: 'YES!'],\n" +
2864    '    [Symbol(nodejs.util.inspect.custom)]: ' +
2865      '[Function: [nodejs.util.inspect.custom]] {\n' +
2866    '      [length]: 0,\n' +
2867    "      [name]: '[nodejs.util.inspect.custom]'\n" +
2868    '    },\n' +
2869    '    [abc]: [Getter: true],\n' +
2870    '    [def]: [Getter/Setter: false]\n' +
2871    '  }'
2872  );
2873
2874  assert.strictEqual(
2875    inspect(Object.getPrototypeOf(bar)),
2876    'Foo [Map] {}'
2877  );
2878
2879  assert.strictEqual(
2880    inspect(Object.getPrototypeOf(new Foo())),
2881    'Map {}'
2882  );
2883}
2884
2885// Check that prototypes with a null prototype are inspectable.
2886// Regression test for https://github.com/nodejs/node/issues/35730
2887{
2888  function Func() {}
2889  Func.prototype = null;
2890  const object = {};
2891  object.constructor = Func;
2892
2893  assert.strictEqual(util.inspect(object), '{ constructor: [Function: Func] }');
2894}
2895
2896// Test changing util.inspect.colors colors and aliases.
2897{
2898  const colors = util.inspect.colors;
2899
2900  const originalValue = colors.gray;
2901
2902  // "grey" is reference-equal alias of "gray".
2903  assert.strictEqual(colors.grey, colors.gray);
2904
2905  // Assigninging one should assign the other. This tests that the alias setter
2906  // function keeps things reference-equal.
2907  colors.gray = [0, 0];
2908  assert.deepStrictEqual(colors.gray, [0, 0]);
2909  assert.strictEqual(colors.grey, colors.gray);
2910
2911  colors.grey = [1, 1];
2912  assert.deepStrictEqual(colors.grey, [1, 1]);
2913  assert.strictEqual(colors.grey, colors.gray);
2914
2915  // Restore original value to avoid side effects in other tests.
2916  colors.gray = originalValue;
2917  assert.deepStrictEqual(colors.gray, originalValue);
2918  assert.strictEqual(colors.grey, colors.gray);
2919}
2920
2921// https://github.com/nodejs/node/issues/31889
2922{
2923  v8.setFlagsFromString('--allow-natives-syntax');
2924  const undetectable = vm.runInThisContext('%GetUndetectable()');
2925  v8.setFlagsFromString('--no-allow-natives-syntax');
2926  assert.strictEqual(inspect(undetectable), '{}');
2927}
2928
2929// Truncate output for Primitives with 1 character left
2930{
2931  assert.strictEqual(util.inspect('bl', { maxStringLength: 1 }),
2932                     "'b'... 1 more character");
2933}
2934
2935{
2936  const x = 'a'.repeat(1e6);
2937  assert.strictEqual(
2938    util.inspect(x, { maxStringLength: 4 }),
2939    "'aaaa'... 999996 more characters"
2940  );
2941  assert.match(util.inspect(x, { maxStringLength: null }), /a'$/);
2942}
2943
2944{
2945  // Verify that util.inspect() invokes custom inspect functions on objects
2946  // from other vm.Contexts but does not pass data from its own Context to that
2947  // function.
2948  const target = vm.runInNewContext(`
2949    ({
2950      [Symbol.for('nodejs.util.inspect.custom')](depth, ctx) {
2951        this.depth = depth;
2952        this.ctx = ctx;
2953        try {
2954          this.stylized = ctx.stylize('��');
2955        } catch (e) {
2956          this.stylizeException = e;
2957        }
2958        return this.stylized;
2959      }
2960    })
2961  `, Object.create(null));
2962  assert.strictEqual(target.ctx, undefined);
2963
2964  {
2965    // Subtest 1: Just try to inspect the object with default options.
2966    assert.strictEqual(util.inspect(target), '��');
2967    assert.strictEqual(typeof target.ctx, 'object');
2968    const objectGraph = fullObjectGraph(target);
2969    assert(!objectGraph.has(Object));
2970    assert(!objectGraph.has(Function));
2971  }
2972
2973  {
2974    // Subtest 2: Use a stylize function that returns a non-primitive.
2975    const output = util.inspect(target, {
2976      stylize: common.mustCall((str) => {
2977        return {};
2978      })
2979    });
2980    assert.strictEqual(output, '[object Object]');
2981    assert.strictEqual(typeof target.ctx, 'object');
2982    const objectGraph = fullObjectGraph(target);
2983    assert(!objectGraph.has(Object));
2984    assert(!objectGraph.has(Function));
2985  }
2986
2987  {
2988    // Subtest 3: Use a stylize function that throws an exception.
2989    const output = util.inspect(target, {
2990      stylize: common.mustCall((str) => {
2991        throw new Error('oops');
2992      })
2993    });
2994    assert.strictEqual(output, '��');
2995    assert.strictEqual(typeof target.ctx, 'object');
2996    const objectGraph = fullObjectGraph(target);
2997    assert(!objectGraph.has(Object));
2998    assert(!objectGraph.has(Function));
2999  }
3000
3001  function fullObjectGraph(value) {
3002    const graph = new Set([value]);
3003
3004    for (const entry of graph) {
3005      if ((typeof entry !== 'object' && typeof entry !== 'function') ||
3006          entry === null) {
3007        continue;
3008      }
3009
3010      graph.add(Object.getPrototypeOf(entry));
3011      const descriptors = Object.values(
3012        Object.getOwnPropertyDescriptors(entry));
3013      for (const descriptor of descriptors) {
3014        graph.add(descriptor.value);
3015        graph.add(descriptor.set);
3016        graph.add(descriptor.get);
3017      }
3018    }
3019
3020    return graph;
3021  }
3022
3023  // Consistency check.
3024  assert(fullObjectGraph(global).has(Function.prototype));
3025}
3026
3027{
3028  // Confirm that own constructor value displays correctly.
3029
3030  function Fhqwhgads() {}
3031
3032  const sterrance = new Fhqwhgads();
3033  sterrance.constructor = Fhqwhgads;
3034
3035  assert.strictEqual(
3036    util.inspect(sterrance, { showHidden: true }),
3037    'Fhqwhgads {\n' +
3038      '  constructor: <ref *1> [Function: Fhqwhgads] {\n' +
3039      '    [length]: 0,\n' +
3040      "    [name]: 'Fhqwhgads',\n" +
3041      '    [prototype]: { [constructor]: [Circular *1] }\n' +
3042      '  }\n' +
3043      '}'
3044  );
3045}
3046
3047{
3048  // Confirm null prototype of generator prototype displays as expected.
3049
3050  function getProtoOfProto() {
3051    return Object.getPrototypeOf(Object.getPrototypeOf(function* () {}));
3052  }
3053
3054  function* generator() {}
3055
3056  const generatorPrototype = Object.getPrototypeOf(generator);
3057  const originalProtoOfProto = Object.getPrototypeOf(generatorPrototype);
3058  assert.strictEqual(getProtoOfProto(), originalProtoOfProto);
3059  Object.setPrototypeOf(generatorPrototype, null);
3060  assert.notStrictEqual(getProtoOfProto, originalProtoOfProto);
3061
3062  // This is the actual test. The other assertions in this block are about
3063  // making sure the test is set up correctly and isn't polluting other tests.
3064  assert.strictEqual(
3065    util.inspect(generator, { showHidden: true }),
3066    '[GeneratorFunction: generator] {\n' +
3067    '  [length]: 0,\n' +
3068    "  [name]: 'generator',\n" +
3069    "  [prototype]: Object [Generator] { [Symbol(Symbol.toStringTag)]: 'Generator' },\n" + // eslint-disable-line max-len
3070    "  [Symbol(Symbol.toStringTag)]: 'GeneratorFunction'\n" +
3071    '}'
3072  );
3073
3074  // Reset so we don't pollute other tests
3075  Object.setPrototypeOf(generatorPrototype, originalProtoOfProto);
3076  assert.strictEqual(getProtoOfProto(), originalProtoOfProto);
3077}
3078
3079{
3080  // Test for when breakLength results in a single column.
3081  const obj = Array(9).fill('fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf');
3082  assert.strictEqual(
3083    util.inspect(obj, { breakLength: 256 }),
3084    '[\n' +
3085    "  'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" +
3086    "  'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" +
3087    "  'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" +
3088    "  'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" +
3089    "  'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" +
3090    "  'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" +
3091    "  'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" +
3092    "  'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf',\n" +
3093    "  'fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf'\n" +
3094    ']'
3095  );
3096}
3097
3098{
3099  assert.strictEqual(
3100    util.inspect({ ['__proto__']: { a: 1 } }),
3101    "{ ['__proto__']: { a: 1 } }"
3102  );
3103}
3104