1'use strict'; 2 3const Buffer = require('buffer').Buffer; 4const { 5 ArrayPrototypeForEach, 6 Error, 7 EvalError, 8 FunctionPrototypeCall, 9 ObjectAssign, 10 ObjectCreate, 11 ObjectDefineProperty, 12 ObjectGetOwnPropertyDescriptor, 13 ObjectGetOwnPropertyNames, 14 ObjectGetPrototypeOf, 15 ObjectKeys, 16 ObjectPrototypeToString, 17 RangeError, 18 ReferenceError, 19 SafeSet, 20 StringFromCharCode, 21 StringPrototypeSubstring, 22 SymbolToStringTag, 23 SyntaxError, 24 SymbolFor, 25 TypeError, 26 TypedArrayPrototypeGetBuffer, 27 TypedArrayPrototypeGetByteOffset, 28 TypedArrayPrototypeGetByteLength, 29 URIError, 30} = primordials; 31const { inspect: { custom: customInspectSymbol } } = require('util'); 32 33const kSerializedError = 0; 34const kSerializedObject = 1; 35const kInspectedError = 2; 36const kInspectedSymbol = 3; 37const kCustomInspectedObject = 4; 38 39const kSymbolStringLength = 'Symbol('.length; 40 41const errors = { 42 Error, TypeError, RangeError, URIError, SyntaxError, ReferenceError, EvalError, 43}; 44const errorConstructorNames = new SafeSet(ObjectKeys(errors)); 45 46function TryGetAllProperties(object, target = object) { 47 const all = ObjectCreate(null); 48 if (object === null) 49 return all; 50 ObjectAssign(all, 51 TryGetAllProperties(ObjectGetPrototypeOf(object), target)); 52 const keys = ObjectGetOwnPropertyNames(object); 53 ArrayPrototypeForEach(keys, (key) => { 54 let descriptor; 55 try { 56 // TODO: create a null-prototype descriptor with needed properties only 57 descriptor = ObjectGetOwnPropertyDescriptor(object, key); 58 } catch { return; } 59 const getter = descriptor.get; 60 if (getter && key !== '__proto__') { 61 try { 62 descriptor.value = FunctionPrototypeCall(getter, target); 63 delete descriptor.get; 64 delete descriptor.set; 65 } catch { 66 // Continue regardless of error. 67 } 68 } 69 if (key === 'cause') { 70 descriptor.value = serializeError(descriptor.value); 71 all[key] = descriptor; 72 } else if ('value' in descriptor && 73 typeof descriptor.value !== 'function' && typeof descriptor.value !== 'symbol') { 74 all[key] = descriptor; 75 } 76 }); 77 return all; 78} 79 80function GetConstructors(object) { 81 const constructors = []; 82 83 for (let current = object; 84 current !== null; 85 current = ObjectGetPrototypeOf(current)) { 86 const desc = ObjectGetOwnPropertyDescriptor(current, 'constructor'); 87 if (desc && desc.value) { 88 ObjectDefineProperty(constructors, constructors.length, { 89 __proto__: null, 90 value: desc.value, enumerable: true, 91 }); 92 } 93 } 94 95 return constructors; 96} 97 98function GetName(object) { 99 const desc = ObjectGetOwnPropertyDescriptor(object, 'name'); 100 return desc && desc.value; 101} 102 103let internalUtilInspect; 104function inspect(...args) { 105 if (!internalUtilInspect) { 106 internalUtilInspect = require('internal/util/inspect'); 107 } 108 return internalUtilInspect.inspect(...args); 109} 110 111let serialize; 112function serializeError(error) { 113 if (!serialize) serialize = require('v8').serialize; 114 if (typeof error === 'symbol') { 115 return Buffer.from(StringFromCharCode(kInspectedSymbol) + inspect(error), 'utf8'); 116 } 117 try { 118 if (typeof error === 'object' && 119 ObjectPrototypeToString(error) === '[object Error]') { 120 const constructors = GetConstructors(error); 121 for (let i = 0; i < constructors.length; i++) { 122 const name = GetName(constructors[i]); 123 if (errorConstructorNames.has(name)) { 124 const serialized = serialize({ 125 constructor: name, 126 properties: TryGetAllProperties(error), 127 }); 128 return Buffer.concat([Buffer.from([kSerializedError]), serialized]); 129 } 130 } 131 } 132 } catch { 133 // Continue regardless of error. 134 } 135 try { 136 if (error != null && customInspectSymbol in error) { 137 return Buffer.from(StringFromCharCode(kCustomInspectedObject) + inspect(error), 'utf8'); 138 } 139 } catch { 140 // Continue regardless of error. 141 } 142 try { 143 const serialized = serialize(error); 144 return Buffer.concat([Buffer.from([kSerializedObject]), serialized]); 145 } catch { 146 // Continue regardless of error. 147 } 148 return Buffer.from(StringFromCharCode(kInspectedError) + inspect(error), 'utf8'); 149} 150 151function fromBuffer(error) { 152 return Buffer.from(TypedArrayPrototypeGetBuffer(error), 153 TypedArrayPrototypeGetByteOffset(error) + 1, 154 TypedArrayPrototypeGetByteLength(error) - 1); 155} 156 157let deserialize; 158function deserializeError(error) { 159 if (!deserialize) deserialize = require('v8').deserialize; 160 switch (error[0]) { 161 case kSerializedError: { 162 const { constructor, properties } = deserialize(error.subarray(1)); 163 const ctor = errors[constructor]; 164 ObjectDefineProperty(properties, SymbolToStringTag, { 165 __proto__: null, 166 value: { __proto__: null, value: 'Error', configurable: true }, 167 enumerable: true, 168 }); 169 if ('cause' in properties && 'value' in properties.cause) { 170 properties.cause.value = deserializeError(properties.cause.value); 171 } 172 return ObjectCreate(ctor.prototype, properties); 173 } 174 case kSerializedObject: 175 return deserialize(error.subarray(1)); 176 case kInspectedError: 177 return fromBuffer(error).toString('utf8'); 178 case kInspectedSymbol: { 179 const buf = fromBuffer(error); 180 return SymbolFor(StringPrototypeSubstring(buf.toString('utf8'), kSymbolStringLength, buf.length - 1)); 181 } 182 case kCustomInspectedObject: 183 return { 184 __proto__: null, 185 [customInspectSymbol]: () => fromBuffer(error).toString('utf8'), 186 }; 187 } 188 require('assert').fail('This should not happen'); 189} 190 191module.exports = { serializeError, deserializeError }; 192