1// Flags: --expose-internals 2'use strict'; 3require('../common'); 4const assert = require('assert'); 5const { inspect } = require('util'); 6const { ERR_INVALID_ARG_TYPE } = require('internal/errors').codes; 7const { serializeError, deserializeError } = require('internal/error_serdes'); 8 9function cycle(err) { 10 return deserializeError(serializeError(err)); 11} 12 13assert.strictEqual(cycle(0), 0); 14assert.strictEqual(cycle(-1), -1); 15assert.strictEqual(cycle(1.4), 1.4); 16assert.strictEqual(cycle(null), null); 17assert.strictEqual(cycle(undefined), undefined); 18assert.strictEqual(cycle('foo'), 'foo'); 19assert.strictEqual(cycle(Symbol.for('foo')), Symbol.for('foo')); 20assert.strictEqual(cycle(Symbol('foo')).toString(), Symbol('foo').toString()); 21 22 23let err = new Error('foo'); 24for (let i = 0; i < 10; i++) { 25 assert(err instanceof Error); 26 assert(Object.prototype.toString.call(err), '[object Error]'); 27 assert.strictEqual(err.name, 'Error'); 28 assert.strictEqual(err.message, 'foo'); 29 assert.match(err.stack, /^Error: foo\n/); 30 31 const prev = err; 32 err = cycle(err); 33 assert.deepStrictEqual(err, prev); 34} 35 36assert.strictEqual(cycle(new RangeError('foo')).name, 'RangeError'); 37assert.strictEqual(cycle(new TypeError('foo')).name, 'TypeError'); 38assert.strictEqual(cycle(new ReferenceError('foo')).name, 'ReferenceError'); 39assert.strictEqual(cycle(new URIError('foo')).name, 'URIError'); 40assert.strictEqual(cycle(new EvalError('foo')).name, 'EvalError'); 41assert.strictEqual(cycle(new SyntaxError('foo')).name, 'SyntaxError'); 42 43class SubError extends Error {} 44 45assert.strictEqual(cycle(new SubError('foo')).name, 'Error'); 46 47assert.deepStrictEqual(cycle({ message: 'foo' }), { message: 'foo' }); 48assert.strictEqual(cycle(Function), '[Function: Function]'); 49 50class ErrorWithCause extends Error { 51 get cause() { 52 return new Error('err'); 53 } 54} 55class ErrorWithThowingCause extends Error { 56 get cause() { 57 throw new Error('err'); 58 } 59} 60class ErrorWithCyclicCause extends Error { 61 get cause() { 62 return new ErrorWithCyclicCause(); 63 } 64} 65const errorWithCause = Object 66 .defineProperty(new Error('Error with cause'), 'cause', { get() { return { foo: 'bar' }; } }); 67const errorWithThrowingCause = Object 68 .defineProperty(new Error('Error with cause'), 'cause', { get() { throw new Error('err'); } }); 69const errorWithCyclicCause = Object 70 .defineProperty(new Error('Error with cause'), 'cause', { get() { return errorWithCyclicCause; } }); 71 72assert.strictEqual(cycle(new Error('Error with cause', { cause: 0 })).cause, 0); 73assert.strictEqual(cycle(new Error('Error with cause', { cause: -1 })).cause, -1); 74assert.strictEqual(cycle(new Error('Error with cause', { cause: 1.4 })).cause, 1.4); 75assert.strictEqual(cycle(new Error('Error with cause', { cause: null })).cause, null); 76assert.strictEqual(cycle(new Error('Error with cause', { cause: undefined })).cause, undefined); 77assert.strictEqual(Object.hasOwn(cycle(new Error('Error with cause', { cause: undefined })), 'cause'), true); 78assert.strictEqual(cycle(new Error('Error with cause', { cause: 'foo' })).cause, 'foo'); 79assert.deepStrictEqual(cycle(new Error('Error with cause', { cause: new Error('err') })).cause, new Error('err')); 80assert.deepStrictEqual(cycle(errorWithCause).cause, { foo: 'bar' }); 81assert.strictEqual(Object.hasOwn(cycle(errorWithThrowingCause), 'cause'), false); 82assert.strictEqual(Object.hasOwn(cycle(errorWithCyclicCause), 'cause'), true); 83assert.deepStrictEqual(cycle(new ErrorWithCause('Error with cause')).cause, new Error('err')); 84assert.strictEqual(cycle(new ErrorWithThowingCause('Error with cause')).cause, undefined); 85assert.strictEqual(Object.hasOwn(cycle(new ErrorWithThowingCause('Error with cause')), 'cause'), false); 86// When the cause is cyclic, it is serialized until Maxiumum call stack size is reached 87let depth = 0; 88let e = cycle(new ErrorWithCyclicCause('Error with cause')); 89while (e.cause) { 90 e = e.cause; 91 depth++; 92} 93assert(depth > 1); 94 95 96{ 97 const err = new ERR_INVALID_ARG_TYPE('object', 'Object', 42); 98 assert.match(String(err), /^TypeError \[ERR_INVALID_ARG_TYPE\]:/); 99 assert.strictEqual(err.name, 'TypeError'); 100 assert.strictEqual(err.code, 'ERR_INVALID_ARG_TYPE'); 101} 102 103{ 104 let called = false; 105 class DynamicError extends Error { 106 get type() { 107 called = true; 108 return 'dynamic'; 109 } 110 111 get shouldIgnoreError() { 112 throw new Error(); 113 } 114 } 115 116 serializeError(new DynamicError()); 117 assert.strictEqual(called, true); 118} 119 120 121const data = { 122 foo: 'bar', 123 [inspect.custom]() { 124 return 'barbaz'; 125 } 126}; 127assert.strictEqual(inspect(cycle(data)), 'barbaz'); 128 129const inheritedCustomInspect = new class { 130 foo = 'bar'; 131 [inspect.custom]() { 132 return 'barbaz'; 133 } 134}(); 135assert.strictEqual(inspect(cycle(inheritedCustomInspect)), 'barbaz'); 136