/* * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/licenses/publicdomain/ */ load(libdir + "asserts.js"); function roundtrip(error) { return deserialize(serialize(error, [])); } // Basic { let error = new Error("hello world"); let cloned = roundtrip(error); assertDeepEq(cloned, error); assertEq(cloned.name, "Error"); assertEq(cloned.message, "hello world"); assertEq(cloned.stack, error.stack); } let constructors = [Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError]; for (let constructor of constructors) { // With message let error = new constructor("hello"); let cloned = roundtrip(error); assertDeepEq(cloned, error); assertEq(cloned.hasOwnProperty('message'), true); assertEq(cloned instanceof constructor, true); // Without message error = new constructor(); cloned = roundtrip(error); assertDeepEq(cloned, error); assertEq(cloned.hasOwnProperty('message'), false); assertEq(cloned instanceof constructor, true); // Custom name error = new constructor("hello"); error.name = "MyError"; cloned = roundtrip(error); assertEq(cloned.name, "Error"); assertEq(cloned.message, "hello"); assertEq(cloned.stack, error.stack); if (constructor !== Error) { assertEq(cloned instanceof constructor, false); } // |cause| property error = new constructor("hello", { cause: new Error("foobar") }); cloned = roundtrip(error); assertDeepEq(cloned, error); assertEq(cloned.hasOwnProperty('message'), true); assertEq(cloned instanceof constructor, true); assertEq(cloned.stack, error.stack); assertEq(cloned.stack === undefined, false); // |cause| property, manually added after construction. error = new constructor("hello"); error.cause = new Error("foobar"); assertDeepEq(Object.getOwnPropertyDescriptor(error, "cause"), { value: error.cause, writable: true, enumerable: true, configurable: true, }); cloned = roundtrip(error); assertDeepEq(Object.getOwnPropertyDescriptor(cloned, "cause"), { value: cloned.cause, writable: true, enumerable: false, // Non-enumerable in the cloned object! configurable: true, }); assertEq(cloned.hasOwnProperty('message'), true); assertEq(cloned instanceof constructor, true); assertEq(cloned.stack, error.stack); assertEq(cloned.stack === undefined, false); // Subclassing error = new (class MyError extends constructor {}); cloned = roundtrip(error); assertEq(cloned.name, constructor.name); assertEq(cloned.hasOwnProperty('message'), false); assertEq(cloned.stack, error.stack); assertEq(cloned instanceof Error, true); // Cross-compartment error = evalcx(`new ${constructor.name}("hello")`); cloned = roundtrip(error); assertEq(cloned.name, constructor.name); assertEq(cloned.message, "hello"); assertEq(cloned.stack, error.stack); assertEq(cloned instanceof constructor, true); } // Non-string message { let error = new Error("hello world"); error.message = 123; let cloned = roundtrip(error); assertEq(cloned.message, "123"); assertEq(cloned.hasOwnProperty('message'), true); error = new Error(); Object.defineProperty(error, 'message', { get: () => {} }); cloned = roundtrip(error); assertEq(cloned.message, ""); assertEq(cloned.hasOwnProperty('message'), false); } // AggregateError { // With message let error = new AggregateError([{a: 1}, {b: 2}], "hello"); let cloned = roundtrip(error); assertDeepEq(cloned, error); assertEq(cloned.hasOwnProperty('message'), true); assertEq(cloned instanceof AggregateError, true); // Without message error = new AggregateError([{a: 1}, {b: 2}]); cloned = roundtrip(error); assertDeepEq(cloned, error); assertEq(cloned.hasOwnProperty('message'), false); assertEq(cloned instanceof AggregateError, true); // Custom name breaks this! error = new AggregateError([{a: 1}, {b: 2}]); error.name = "MyError"; cloned = roundtrip(error); assertEq(cloned.name, "Error"); assertEq(cloned.message, ""); assertEq(cloned.stack, error.stack); assertEq(cloned instanceof AggregateError, false); assertEq(cloned.errors, undefined); assertEq(cloned.hasOwnProperty('errors'), false); }