node/lib/internal/error_serdes.js
Antoine du Hamel a596af0819
tools: add lint rule to keep primordials in ASCII order
PR-URL: https://github.com/nodejs/node/pull/52592
Reviewed-By: Yagiz Nizipli <yagiz.nizipli@sentry.io>
Reviewed-By: Jacob Smith <jacob@frende.me>
Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
2024-04-21 16:53:08 +00:00

193 lines
5.6 KiB
JavaScript

'use strict';
const {
ArrayPrototypeForEach,
Error,
EvalError,
FunctionPrototypeCall,
ObjectAssign,
ObjectCreate,
ObjectDefineProperty,
ObjectGetOwnPropertyDescriptor,
ObjectGetOwnPropertyNames,
ObjectGetPrototypeOf,
ObjectKeys,
ObjectPrototypeToString,
RangeError,
ReferenceError,
SafeSet,
StringFromCharCode,
StringPrototypeSubstring,
SymbolFor,
SymbolToStringTag,
SyntaxError,
TypeError,
TypedArrayPrototypeGetBuffer,
TypedArrayPrototypeGetByteLength,
TypedArrayPrototypeGetByteOffset,
URIError,
} = primordials;
const { Buffer } = require('buffer');
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 = { __proto__: 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 };