• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Flags: --expose-internals
2
3'use strict';
4
5const common = require('../common');
6const fixtures = require('../common/fixtures');
7const { internalBinding } = require('internal/test/binding');
8const assert = require('assert');
9const v8 = require('v8');
10const os = require('os');
11
12const circular = {};
13circular.circular = circular;
14
15const wasmModule = new WebAssembly.Module(fixtures.readSync('simple.wasm'));
16
17const objects = [
18  { foo: 'bar' },
19  { bar: 'baz' },
20  new Uint8Array([1, 2, 3, 4]),
21  new Uint32Array([1, 2, 3, 4]),
22  Buffer.from([1, 2, 3, 4]),
23  undefined,
24  null,
25  42,
26  circular,
27  wasmModule
28];
29
30const hostObject = new (internalBinding('js_stream').JSStream)();
31
32const serializerTypeError =
33  /^TypeError: Class constructor Serializer cannot be invoked without 'new'$/;
34const deserializerTypeError =
35  /^TypeError: Class constructor Deserializer cannot be invoked without 'new'$/;
36
37{
38  const ser = new v8.DefaultSerializer();
39  ser.writeHeader();
40  for (const obj of objects) {
41    ser.writeValue(obj);
42  }
43
44  const des = new v8.DefaultDeserializer(ser.releaseBuffer());
45  des.readHeader();
46
47  for (const obj of objects) {
48    assert.deepStrictEqual(des.readValue(), obj);
49  }
50}
51
52{
53  for (const obj of objects) {
54    assert.deepStrictEqual(v8.deserialize(v8.serialize(obj)), obj);
55  }
56}
57
58{
59  const ser = new v8.DefaultSerializer();
60  ser._getDataCloneError = common.mustCall((message) => {
61    assert.strictEqual(message, '[object Object] could not be cloned.');
62    return new Error('foobar');
63  });
64
65  ser.writeHeader();
66
67  assert.throws(() => {
68    ser.writeValue(new Proxy({}, {}));
69  }, /foobar/);
70}
71
72{
73  const ser = new v8.DefaultSerializer();
74  ser._writeHostObject = common.mustCall((object) => {
75    assert.strictEqual(object, hostObject);
76    const buf = Buffer.from('hostObjectTag');
77
78    ser.writeUint32(buf.length);
79    ser.writeRawBytes(buf);
80
81    ser.writeUint64(1, 2);
82    ser.writeDouble(-0.25);
83  });
84
85  ser.writeHeader();
86  ser.writeValue({ val: hostObject });
87
88  const des = new v8.DefaultDeserializer(ser.releaseBuffer());
89  des._readHostObject = common.mustCall(() => {
90    const length = des.readUint32();
91    const buf = des.readRawBytes(length);
92
93    assert.strictEqual(buf.toString(), 'hostObjectTag');
94
95    assert.deepStrictEqual(des.readUint64(), [1, 2]);
96    assert.strictEqual(des.readDouble(), -0.25);
97    return hostObject;
98  });
99
100  des.readHeader();
101
102  assert.strictEqual(des.readValue().val, hostObject);
103}
104
105// This test ensures that `v8.Serializer.writeRawBytes()` support
106// `TypedArray` and `DataView`.
107{
108  const text = 'hostObjectTag';
109  const data = Buffer.from(text);
110  const arrayBufferViews = common.getArrayBufferViews(data);
111
112  // `buf` is one of `TypedArray` or `DataView`.
113  function testWriteRawBytes(buf) {
114    let writeHostObjectCalled = false;
115    const ser = new v8.DefaultSerializer();
116
117    ser._writeHostObject = common.mustCall((object) => {
118      writeHostObjectCalled = true;
119      ser.writeUint32(buf.byteLength);
120      ser.writeRawBytes(buf);
121    });
122
123    ser.writeHeader();
124    ser.writeValue({ val: hostObject });
125
126    const des = new v8.DefaultDeserializer(ser.releaseBuffer());
127    des._readHostObject = common.mustCall(() => {
128      assert.strictEqual(writeHostObjectCalled, true);
129      const length = des.readUint32();
130      const buf = des.readRawBytes(length);
131      assert.strictEqual(buf.toString(), text);
132
133      return hostObject;
134    });
135
136    des.readHeader();
137
138    assert.strictEqual(des.readValue().val, hostObject);
139  }
140
141  arrayBufferViews.forEach((buf) => {
142    testWriteRawBytes(buf);
143  });
144}
145
146{
147  const ser = new v8.DefaultSerializer();
148  ser._writeHostObject = common.mustCall((object) => {
149    throw new Error('foobar');
150  });
151
152  ser.writeHeader();
153  assert.throws(() => {
154    ser.writeValue({ val: hostObject });
155  }, /foobar/);
156}
157
158{
159  assert.throws(() => v8.serialize(hostObject), {
160    constructor: Error,
161    message: 'Unserializable host object: JSStream {}'
162  });
163}
164
165{
166  const buf = Buffer.from('ff0d6f2203666f6f5e007b01', 'hex');
167
168  const des = new v8.DefaultDeserializer(buf);
169  des.readHeader();
170
171  const ser = new v8.DefaultSerializer();
172  ser.writeHeader();
173
174  ser.writeValue(des.readValue());
175
176  assert.deepStrictEqual(buf, ser.releaseBuffer());
177  assert.strictEqual(des.getWireFormatVersion(), 0x0d);
178}
179
180{
181  // Unaligned Uint16Array read, with padding in the underlying array buffer.
182  let buf = Buffer.alloc(32 + 9);
183  buf.write('ff0d5c0404addeefbe', 32, 'hex');
184  buf = buf.slice(32);
185
186  const expectedResult = os.endianness() === 'LE' ?
187    new Uint16Array([0xdead, 0xbeef]) : new Uint16Array([0xadde, 0xefbe]);
188
189  assert.deepStrictEqual(v8.deserialize(buf), expectedResult);
190}
191
192{
193  assert.throws(v8.Serializer, serializerTypeError);
194  assert.throws(v8.Deserializer, deserializerTypeError);
195}
196
197
198// `v8.deserialize()` and `new v8.Deserializer()` should support both
199// `TypedArray` and `DataView`.
200{
201  for (const obj of objects) {
202    const buf = v8.serialize(obj);
203
204    for (const arrayBufferView of common.getArrayBufferViews(buf)) {
205      assert.deepStrictEqual(v8.deserialize(arrayBufferView), obj);
206    }
207
208    for (const arrayBufferView of common.getArrayBufferViews(buf)) {
209      const deserializer = new v8.DefaultDeserializer(arrayBufferView);
210      deserializer.readHeader();
211      const value = deserializer.readValue();
212      assert.deepStrictEqual(value, obj);
213
214      const serializer = new v8.DefaultSerializer();
215      serializer.writeHeader();
216      serializer.writeValue(value);
217      assert.deepStrictEqual(buf, serializer.releaseBuffer());
218    }
219  }
220}
221
222{
223  const INVALID_SOURCE = 'INVALID_SOURCE_TYPE';
224  const serializer = new v8.Serializer();
225  serializer.writeHeader();
226  assert.throws(
227    () => serializer.writeRawBytes(INVALID_SOURCE),
228    /^TypeError: source must be a TypedArray or a DataView$/,
229  );
230  assert.throws(
231    () => v8.deserialize(INVALID_SOURCE),
232    /^TypeError: buffer must be a TypedArray or a DataView$/,
233  );
234  assert.throws(
235    () => new v8.Deserializer(INVALID_SOURCE),
236    /^TypeError: buffer must be a TypedArray or a DataView$/,
237  );
238}
239
240{
241  const deserializedWasmModule = v8.deserialize(v8.serialize(wasmModule));
242  const instance = new WebAssembly.Instance(deserializedWasmModule);
243  assert.strictEqual(instance.exports.add(10, 20), 30);
244}
245