• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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