diff options
author | Moshe Atlow <moshe@atlow.co.il> | 2023-05-12 14:46:32 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-12 11:46:32 +0000 |
commit | 7984af69a090dd6d1f60ffe7e194d5e69bce0c20 (patch) | |
tree | 62a8d74d743c798b0a8d98b4db3a9172258b8a33 /lib | |
parent | 78123275c2561260368eeebaa84b805830d189f1 (diff) | |
download | node-new-7984af69a090dd6d1f60ffe7e194d5e69bce0c20.tar.gz |
worker: support more cases when (de)serializing errors
- error.cause is potentially an error, so is now handled recursively
- best effort to serialize thrown symbols
- handle thrown object with custom inspect
PR-URL: https://github.com/nodejs/node/pull/47925
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/internal/error_serdes.js | 63 |
1 files changed, 52 insertions, 11 deletions
diff --git a/lib/internal/error_serdes.js b/lib/internal/error_serdes.js index 13f3f8b35f..e4850c63aa 100644 --- a/lib/internal/error_serdes.js +++ b/lib/internal/error_serdes.js @@ -13,19 +13,31 @@ const { ObjectGetOwnPropertyNames, ObjectGetPrototypeOf, ObjectKeys, + ObjectPrototypeHasOwnProperty, 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, @@ -42,19 +54,24 @@ function TryGetAllProperties(object, target = 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 ('value' in descriptor && typeof descriptor.value !== 'function') { - delete descriptor.get; - delete descriptor.set; + 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; } }); @@ -95,6 +112,9 @@ function 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]') { @@ -114,13 +134,26 @@ function serializeError(error) { // Continue regardless of error. } try { + if (error != null && + ObjectPrototypeHasOwnProperty(error, customInspectSymbol)) { + 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.concat([Buffer.from([kInspectedError]), - Buffer.from(inspect(error), 'utf8')]); + return Buffer.from(StringFromCharCode(kInspectedError) + inspect(error), 'utf8'); +} + +function fromBuffer(error) { + return Buffer.from(TypedArrayPrototypeGetBuffer(error), + TypedArrayPrototypeGetByteOffset(error) + 1, + TypedArrayPrototypeGetByteLength(error) - 1); } let deserialize; @@ -132,19 +165,27 @@ function deserializeError(error) { const ctor = errors[constructor]; ObjectDefineProperty(properties, SymbolToStringTag, { __proto__: null, - value: { value: 'Error', configurable: true }, + 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: { - const buf = Buffer.from(error.buffer, - error.byteOffset + 1, - error.byteLength - 1); - return buf.toString('utf8'); + 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'); } |