'use strict'; const Buffer = require('buffer').Buffer; const { ArrayPrototypeForEach, Error, EvalError, FunctionPrototypeCall, ObjectAssign, ObjectCreate, ObjectDefineProperty, ObjectGetOwnPropertyDescriptor, ObjectGetOwnPropertyNames, ObjectGetPrototypeOf, ObjectKeys, ObjectPrototypeToString, RangeError, ReferenceError, SafeSet, StringFromCharCode, StringPrototypeSubstring, SymbolToStringTag, SyntaxError, SymbolFor, TypeError, TypedArrayPrototypeGetBuffer, TypedArrayPrototypeGetByteOffset, TypedArrayPrototypeGetByteLength, URIError, } = primordials; const { inspect: { custom: customInspectSymbol } } = require('util'); const kSerializedError = 0; const kSerializedObject = 1; const kInspectedError = 2; const kInspectedSymbol = 3; const kCustomInspectedObject = 4; const kSymbolStringLength = 'Symbol('.length; const errors = { Error, TypeError, RangeError, URIError, SyntaxError, ReferenceError, EvalError, }; const errorConstructorNames = new SafeSet(ObjectKeys(errors)); function TryGetAllProperties(object, target = object) { const all = ObjectCreate(null); if (object === null) return all; ObjectAssign(all, TryGetAllProperties(ObjectGetPrototypeOf(object), target)); const keys = ObjectGetOwnPropertyNames(object); ArrayPrototypeForEach(keys, (key) => { let descriptor; try { // TODO: create a null-prototype descriptor with needed properties only descriptor = ObjectGetOwnPropertyDescriptor(object, key); } catch { return; } const getter = descriptor.get; if (getter && key !== '__proto__') { try { descriptor.value = FunctionPrototypeCall(getter, target); delete descriptor.get; delete descriptor.set; } catch { // Continue regardless of error. } } if (key === 'cause') { descriptor.value = serializeError(descriptor.value); all[key] = descriptor; } else if ('value' in descriptor && typeof descriptor.value !== 'function' && typeof descriptor.value !== 'symbol') { all[key] = descriptor; } }); return all; } function GetConstructors(object) { const constructors = []; for (let current = object; current !== null; current = ObjectGetPrototypeOf(current)) { const desc = ObjectGetOwnPropertyDescriptor(current, 'constructor'); if (desc && desc.value) { ObjectDefineProperty(constructors, constructors.length, { __proto__: null, value: desc.value, enumerable: true, }); } } return constructors; } function GetName(object) { const desc = ObjectGetOwnPropertyDescriptor(object, 'name'); return desc && desc.value; } let internalUtilInspect; function inspect(...args) { if (!internalUtilInspect) { internalUtilInspect = require('internal/util/inspect'); } return internalUtilInspect.inspect(...args); } let serialize; function serializeError(error) { if (!serialize) serialize = require('v8').serialize; if (typeof error === 'symbol') { return Buffer.from(StringFromCharCode(kInspectedSymbol) + inspect(error), 'utf8'); } try { if (typeof error === 'object' && ObjectPrototypeToString(error) === '[object Error]') { const constructors = GetConstructors(error); for (let i = 0; i < constructors.length; i++) { const name = GetName(constructors[i]); if (errorConstructorNames.has(name)) { const serialized = serialize({ constructor: name, properties: TryGetAllProperties(error), }); return Buffer.concat([Buffer.from([kSerializedError]), serialized]); } } } } catch { // Continue regardless of error. } try { if (error != null && customInspectSymbol in error) { return Buffer.from(StringFromCharCode(kCustomInspectedObject) + inspect(error), 'utf8'); } } catch { // Continue regardless of error. } try { const serialized = serialize(error); return Buffer.concat([Buffer.from([kSerializedObject]), serialized]); } catch { // Continue regardless of error. } return Buffer.from(StringFromCharCode(kInspectedError) + inspect(error), 'utf8'); } function fromBuffer(error) { return Buffer.from(TypedArrayPrototypeGetBuffer(error), TypedArrayPrototypeGetByteOffset(error) + 1, TypedArrayPrototypeGetByteLength(error) - 1); } let deserialize; function deserializeError(error) { if (!deserialize) deserialize = require('v8').deserialize; switch (error[0]) { case kSerializedError: { const { constructor, properties } = deserialize(error.subarray(1)); const ctor = errors[constructor]; ObjectDefineProperty(properties, SymbolToStringTag, { __proto__: null, value: { __proto__: null, value: 'Error', configurable: true }, enumerable: true, }); if ('cause' in properties && 'value' in properties.cause) { properties.cause.value = deserializeError(properties.cause.value); } return ObjectCreate(ctor.prototype, properties); } case kSerializedObject: return deserialize(error.subarray(1)); case kInspectedError: return fromBuffer(error).toString('utf8'); case kInspectedSymbol: { const buf = fromBuffer(error); return SymbolFor(StringPrototypeSubstring(buf.toString('utf8'), kSymbolStringLength, buf.length - 1)); } case kCustomInspectedObject: return { __proto__: null, [customInspectSymbol]: () => fromBuffer(error).toString('utf8'), }; } require('assert').fail('This should not happen'); } module.exports = { serializeError, deserializeError };