From d7cfd0c5ba70312923035abc1f57e44360a3f40f Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 15 Jul 2022 09:56:04 +0200 Subject: [PATCH] v8: serialize BigInt64Array and BigUint64Array 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: https://github.com/nodejs/node/pull/43571 Reviewed-By: Ruben Bridgewater Reviewed-By: Colin Ihrig Reviewed-By: James M Snell --- lib/v8.js | 80 +++++++++++++++++++-------------- test/parallel/test-v8-serdes.js | 8 ++++ 2 files changed, 54 insertions(+), 34 deletions(-) diff --git a/lib/v8.js b/lib/v8.js index 1a8b4bce2fccd6..db3dba59565458 100644 --- a/lib/v8.js +++ b/lib/v8.js @@ -16,9 +16,8 @@ const { Array, - ArrayBuffer, - ArrayPrototypeForEach, - ArrayPrototypePush, + BigInt64Array, + BigUint64Array, DataView, Error, Float32Array, @@ -27,7 +26,6 @@ const { Int32Array, Int8Array, ObjectPrototypeToString, - SafeMap, Uint16Array, Uint32Array, Uint8Array, @@ -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() { @@ -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)}`); } @@ -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; diff --git a/test/parallel/test-v8-serdes.js b/test/parallel/test-v8-serdes.js index 3c079e661c7886..1b6638ac1a90bd 100644 --- a/test/parallel/test-v8-serdes.js +++ b/test/parallel/test-v8-serdes.js @@ -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,