criptor.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?.value) {
      ObjectDefineProperty(constructors, constructors.length, {
        __proto__: null,
        value: desc.value, enumerable: true,
      });
    }
  }

  return constructors;
}

function GetName(object) {
  const desc = ObjectGetOwnPropertyDescriptor(object, 'name');
  return desc?.value;
}

let internalUtilInspect;
function inspect(...args) {
  internalUtilInspect ??= require('internal/util/inspect');
  return internalUtilInspect.inspect(...args);
}

let serialize;
function serializeError(error) {
  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) {
  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 };
