diff --git a/doc/api/v8.md b/doc/api/v8.md index 2d76eb3a0f70f8..3473fa786ea118 100644 --- a/doc/api/v8.md +++ b/doc/api/v8.md @@ -185,7 +185,7 @@ Uses a [`DefaultSerializer`][] to serialize `value` into a buffer. added: v8.0.0 --> -* `buffer` {Buffer|Uint8Array} A buffer returned by [`serialize()`][]. +* `buffer` {Buffer|TypedArray|DataView} A buffer returned by [`serialize()`][]. Uses a [`DefaultDeserializer`][] with default options to read a JS value from a buffer. @@ -252,7 +252,7 @@ For use inside of a custom [`serializer._writeHostObject()`][]. #### serializer.writeRawBytes(buffer) -* `buffer` {Buffer|Uint8Array} +* `buffer` {Buffer|TypedArray|DataView} Write raw bytes into the serializer’s internal buffer. The deserializer will require a way to compute the length of the buffer. @@ -308,7 +308,7 @@ added: v8.0.0 #### new Deserializer(buffer) -* `buffer` {Buffer|Uint8Array} A buffer returned by +* `buffer` {Buffer|TypedArray|DataView} A buffer returned by [`serializer.releaseBuffer()`][]. Creates a new `Deserializer` object. diff --git a/src/node_serdes.cc b/src/node_serdes.cc index a6f91d56c2b2bb..fbe9a3ecfc04fa 100644 --- a/src/node_serdes.cc +++ b/src/node_serdes.cc @@ -266,9 +266,9 @@ void SerializerContext::WriteRawBytes(const FunctionCallbackInfo& args) { SerializerContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); - if (!args[0]->IsUint8Array()) { + if (!args[0]->IsArrayBufferView()) { return node::THROW_ERR_INVALID_ARG_TYPE( - ctx->env(), "source must be a Uint8Array"); + ctx->env(), "source must be a TypedArray or a DataView"); } ctx->serializer_.WriteRawBytes(Buffer::Data(args[0]), @@ -317,9 +317,9 @@ MaybeLocal DeserializerContext::ReadHostObject(Isolate* isolate) { void DeserializerContext::New(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - if (!args[0]->IsUint8Array()) { + if (!args[0]->IsArrayBufferView()) { return node::THROW_ERR_INVALID_ARG_TYPE( - env, "buffer must be a Uint8Array"); + env, "buffer must be a TypedArray or a DataView"); } new DeserializerContext(env, args.This(), args[0]); diff --git a/test/parallel/test-v8-serdes.js b/test/parallel/test-v8-serdes.js index ad8666ee3ba877..32f627f1e8fdb1 100644 --- a/test/parallel/test-v8-serdes.js +++ b/test/parallel/test-v8-serdes.js @@ -95,6 +95,47 @@ const deserializerTypeError = assert.strictEqual(des.readValue().val, hostObject); } +// This test ensures that `v8.Serializer.writeRawBytes()` support +// `TypedArray` and `DataView`. +{ + const text = 'hostObjectTag'; + const data = Buffer.from(text); + const arrayBufferViews = common.getArrayBufferViews(data); + + // `buf` is one of `TypedArray` or `DataView`. + function testWriteRawBytes(buf) { + let writeHostObjectCalled = false; + const ser = new v8.DefaultSerializer(); + + ser._writeHostObject = common.mustCall((object) => { + writeHostObjectCalled = true; + ser.writeUint32(buf.byteLength); + ser.writeRawBytes(buf); + }); + + ser.writeHeader(); + ser.writeValue({ val: hostObject }); + + const des = new v8.DefaultDeserializer(ser.releaseBuffer()); + des._readHostObject = common.mustCall(() => { + assert.strictEqual(writeHostObjectCalled, true); + const length = des.readUint32(); + const buf = des.readRawBytes(length); + assert.strictEqual(buf.toString(), text); + + return hostObject; + }); + + des.readHeader(); + + assert.strictEqual(des.readValue().val, hostObject); + } + + arrayBufferViews.forEach((buf) => { + testWriteRawBytes(buf); + }); +} + { const ser = new v8.DefaultSerializer(); ser._writeHostObject = common.mustCall((object) => { @@ -143,3 +184,46 @@ const deserializerTypeError = assert.throws(v8.Serializer, serializerTypeError); assert.throws(v8.Deserializer, deserializerTypeError); } + + +// `v8.deserialize()` and `new v8.Deserializer()` should support both +// `TypedArray` and `DataView`. +{ + for (const obj of objects) { + const buf = v8.serialize(obj); + + for (const arrayBufferView of common.getArrayBufferViews(buf)) { + assert.deepStrictEqual(v8.deserialize(arrayBufferView), obj); + } + + for (const arrayBufferView of common.getArrayBufferViews(buf)) { + const deserializer = new v8.DefaultDeserializer(arrayBufferView); + deserializer.readHeader(); + const value = deserializer.readValue(); + assert.deepStrictEqual(value, obj); + + const serializer = new v8.DefaultSerializer(); + serializer.writeHeader(); + serializer.writeValue(value); + assert.deepStrictEqual(buf, serializer.releaseBuffer()); + } + } +} + +{ + const INVALID_SOURCE = 'INVALID_SOURCE_TYPE'; + const serializer = new v8.Serializer(); + serializer.writeHeader(); + assert.throws( + () => serializer.writeRawBytes(INVALID_SOURCE), + /^TypeError: source must be a TypedArray or a DataView$/, + ); + assert.throws( + () => v8.deserialize(INVALID_SOURCE), + /^TypeError: buffer must be a TypedArray or a DataView$/, + ); + assert.throws( + () => new v8.Deserializer(INVALID_SOURCE), + /^TypeError: buffer must be a TypedArray or a DataView$/, + ); +}