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