Skip to content

Commit

Permalink
v8: serialize BigInt64Array and BigUint64Array
Browse files Browse the repository at this point in the history
Teach the serializer about BigInt64Array and BigUint64Array.

I open-coded the type-to-index mapper to stay compatible with the
current wire format without undue code gymnastics.

PR-URL: #43571
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
bnoordhuis authored and danielleadams committed Jul 26, 2022
1 parent 0aa255a commit d7cfd0c
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 34 deletions.
80 changes: 46 additions & 34 deletions lib/v8.js
Expand Up @@ -16,9 +16,8 @@

const {
Array,
ArrayBuffer,
ArrayPrototypeForEach,
ArrayPrototypePush,
BigInt64Array,
BigUint64Array,
DataView,
Error,
Float32Array,
Expand All @@ -27,7 +26,6 @@ const {
Int32Array,
Int8Array,
ObjectPrototypeToString,
SafeMap,
Uint16Array,
Uint32Array,
Uint8Array,
Expand Down Expand Up @@ -247,29 +245,40 @@ Deserializer.prototype.readRawBytes = function readRawBytes(length) {
length);
};

/* Keep track of how to handle different ArrayBufferViews.
* The default Serializer for Node does not use the V8 methods for serializing
* those objects because Node's `Buffer` objects use pooled allocation in many
* cases, and their underlying `ArrayBuffer`s would show up in the
* serialization. Because a) those may contain sensitive data and the user
* may not be aware of that and b) they are often much larger than the `Buffer`
* itself, custom serialization is applied. */
const arrayBufferViewTypes = [Int8Array, Uint8Array, Uint8ClampedArray,
Int16Array, Uint16Array, Int32Array, Uint32Array,
Float32Array, Float64Array, DataView];

const arrayBufferViewTypeToIndex = new SafeMap();

{
const dummy = new ArrayBuffer();
ArrayPrototypeForEach(arrayBufferViewTypes, (ctor, i) => {
const tag = ObjectPrototypeToString(new ctor(dummy));
arrayBufferViewTypeToIndex.set(tag, i);
});
function arrayBufferViewTypeToIndex(abView) {
const type = ObjectPrototypeToString(abView);
if (type === '[object Int8Array]') return 0;
if (type === '[object Uint8Array]') return 1;
if (type === '[object Uint8ClampedArray]') return 2;
if (type === '[object Int16Array]') return 3;
if (type === '[object Uint16Array]') return 4;
if (type === '[object Int32Array]') return 5;
if (type === '[object Uint32Array]') return 6;
if (type === '[object Float32Array]') return 7;
if (type === '[object Float64Array]') return 8;
if (type === '[object DataView]') return 9;
// Index 10 is FastBuffer.
if (type === '[object BigInt64Array]') return 11;
if (type === '[object BigUint64Array]') return 12;
return -1;
}

const bufferConstructorIndex =
ArrayPrototypePush(arrayBufferViewTypes, FastBuffer) - 1;
function arrayBufferViewIndexToType(index) {
if (index === 0) return Int8Array;
if (index === 1) return Uint8Array;
if (index === 2) return Uint8ClampedArray;
if (index === 3) return Int16Array;
if (index === 4) return Uint16Array;
if (index === 5) return Int32Array;
if (index === 6) return Uint32Array;
if (index === 7) return Float32Array;
if (index === 8) return Float64Array;
if (index === 9) return DataView;
if (index === 10) return FastBuffer;
if (index === 11) return BigInt64Array;
if (index === 12) return BigUint64Array;
return undefined;
}

class DefaultSerializer extends Serializer {
constructor() {
Expand All @@ -285,14 +294,17 @@ class DefaultSerializer extends Serializer {
* @returns {void}
*/
_writeHostObject(abView) {
let i = 0;
if (abView.constructor === Buffer) {
i = bufferConstructorIndex;
} else {
const tag = ObjectPrototypeToString(abView);
i = arrayBufferViewTypeToIndex.get(tag);

if (i === undefined) {
// Keep track of how to handle different ArrayBufferViews. The default
// Serializer for Node does not use the V8 methods for serializing those
// objects because Node's `Buffer` objects use pooled allocation in many
// cases, and their underlying `ArrayBuffer`s would show up in the
// serialization. Because a) those may contain sensitive data and the user
// may not be aware of that and b) they are often much larger than the
// `Buffer` itself, custom serialization is applied.
let i = 10; // FastBuffer
if (abView.constructor !== Buffer) {
i = arrayBufferViewTypeToIndex(abView);
if (i === -1) {
throw new this._getDataCloneError(
`Unserializable host object: ${inspect(abView)}`);
}
Expand All @@ -313,7 +325,7 @@ class DefaultDeserializer extends Deserializer {
*/
_readHostObject() {
const typeIndex = this.readUint32();
const ctor = arrayBufferViewTypes[typeIndex];
const ctor = arrayBufferViewIndexToType(typeIndex);
const byteLength = this.readUint32();
const byteOffset = this._readRawBytes(byteLength);
const BYTES_PER_ELEMENT = ctor.BYTES_PER_ELEMENT || 1;
Expand Down
8 changes: 8 additions & 0 deletions test/parallel/test-v8-serdes.js
Expand Up @@ -14,10 +14,18 @@ circular.circular = circular;
const objects = [
{ foo: 'bar' },
{ bar: 'baz' },
new Int8Array([1, 2, 3, 4]),
new Uint8Array([1, 2, 3, 4]),
new Int16Array([1, 2, 3, 4]),
new Uint16Array([1, 2, 3, 4]),
new Int32Array([1, 2, 3, 4]),
new Uint32Array([1, 2, 3, 4]),
new Float32Array([1, 2, 3, 4]),
new Float64Array([1, 2, 3, 4]),
new DataView(new ArrayBuffer(42)),
Buffer.from([1, 2, 3, 4]),
new BigInt64Array([42n]),
new BigUint64Array([42n]),
undefined,
null,
42,
Expand Down

0 comments on commit d7cfd0c

Please sign in to comment.