• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Flags: --expose-internals
2
3'use strict';
4
5const common = require('../common');
6const { internalBinding } = require('internal/test/binding');
7const assert = require('assert');
8const v8 = require('v8');
9const os = require('os');
10
11const circular = {};
12circular.circular = circular;
13
14const objects = [
15  { foo: 'bar' },
16  { bar: 'baz' },
17  new Uint8Array([1, 2, 3, 4]),
18  new Uint32Array([1, 2, 3, 4]),
19  new DataView(new ArrayBuffer(42)),
20  Buffer.from([1, 2, 3, 4]),
21  undefined,
22  null,
23  42,
24  circular,
25];
26
27const hostObject = new (internalBinding('js_stream').JSStream)();
28
29{
30  const ser = new v8.DefaultSerializer();
31  ser.writeHeader();
32  for (const obj of objects) {
33    ser.writeValue(obj);
34  }
35
36  const des = new v8.DefaultDeserializer(ser.releaseBuffer());
37  des.readHeader();
38
39  for (const obj of objects) {
40    assert.deepStrictEqual(des.readValue(), obj);
41  }
42}
43
44{
45  for (const obj of objects) {
46    assert.deepStrictEqual(v8.deserialize(v8.serialize(obj)), obj);
47  }
48}
49
50{
51  const ser = new v8.DefaultSerializer();
52  ser._getDataCloneError = common.mustCall((message) => {
53    assert.strictEqual(message, '[object Object] could not be cloned.');
54    return new Error('foobar');
55  });
56
57  ser.writeHeader();
58
59  assert.throws(() => {
60    ser.writeValue(new Proxy({}, {}));
61  }, /foobar/);
62}
63
64{
65  const ser = new v8.DefaultSerializer();
66  ser._writeHostObject = common.mustCall((object) => {
67    assert.strictEqual(object, hostObject);
68    const buf = Buffer.from('hostObjectTag');
69
70    ser.writeUint32(buf.length);
71    ser.writeRawBytes(buf);
72
73    ser.writeUint64(1, 2);
74    ser.writeDouble(-0.25);
75  });
76
77  ser.writeHeader();
78  ser.writeValue({ val: hostObject });
79
80  const des = new v8.DefaultDeserializer(ser.releaseBuffer());
81  des._readHostObject = common.mustCall(() => {
82    const length = des.readUint32();
83    const buf = des.readRawBytes(length);
84
85    assert.strictEqual(buf.toString(), 'hostObjectTag');
86
87    assert.deepStrictEqual(des.readUint64(), [1, 2]);
88    assert.strictEqual(des.readDouble(), -0.25);
89    return hostObject;
90  });
91
92  des.readHeader();
93
94  assert.strictEqual(des.readValue().val, hostObject);
95}
96
97// This test ensures that `v8.Serializer.writeRawBytes()` support
98// `TypedArray` and `DataView`.
99{
100  const text = 'hostObjectTag';
101  const data = Buffer.from(text);
102  const arrayBufferViews = common.getArrayBufferViews(data);
103
104  // `buf` is one of `TypedArray` or `DataView`.
105  function testWriteRawBytes(buf) {
106    let writeHostObjectCalled = false;
107    const ser = new v8.DefaultSerializer();
108
109    ser._writeHostObject = common.mustCall((object) => {
110      writeHostObjectCalled = true;
111      ser.writeUint32(buf.byteLength);
112      ser.writeRawBytes(buf);
113    });
114
115    ser.writeHeader();
116    ser.writeValue({ val: hostObject });
117
118    const des = new v8.DefaultDeserializer(ser.releaseBuffer());
119    des._readHostObject = common.mustCall(() => {
120      assert.strictEqual(writeHostObjectCalled, true);
121      const length = des.readUint32();
122      const buf = des.readRawBytes(length);
123      assert.strictEqual(buf.toString(), text);
124
125      return hostObject;
126    });
127
128    des.readHeader();
129
130    assert.strictEqual(des.readValue().val, hostObject);
131  }
132
133  arrayBufferViews.forEach((buf) => {
134    testWriteRawBytes(buf);
135  });
136}
137
138{
139  const ser = new v8.DefaultSerializer();
140  ser._writeHostObject = common.mustCall((object) => {
141    throw new Error('foobar');
142  });
143
144  ser.writeHeader();
145  assert.throws(() => {
146    ser.writeValue({ val: hostObject });
147  }, /foobar/);
148}
149
150{
151  assert.throws(() => v8.serialize(hostObject), {
152    constructor: Error,
153    message: 'Unserializable host object: JSStream {}'
154  });
155}
156
157{
158  const buf = Buffer.from('ff0d6f2203666f6f5e007b01', 'hex');
159
160  const des = new v8.DefaultDeserializer(buf);
161  des.readHeader();
162
163  const ser = new v8.DefaultSerializer();
164  ser.writeHeader();
165
166  ser.writeValue(des.readValue());
167
168  assert.deepStrictEqual(buf, ser.releaseBuffer());
169  assert.strictEqual(des.getWireFormatVersion(), 0x0d);
170}
171
172{
173  // Unaligned Uint16Array read, with padding in the underlying array buffer.
174  let buf = Buffer.alloc(32 + 9);
175  buf.write('ff0d5c0404addeefbe', 32, 'hex');
176  buf = buf.slice(32);
177
178  const expectedResult = os.endianness() === 'LE' ?
179    new Uint16Array([0xdead, 0xbeef]) : new Uint16Array([0xadde, 0xefbe]);
180
181  assert.deepStrictEqual(v8.deserialize(buf), expectedResult);
182}
183
184{
185  assert.throws(() => v8.Serializer(), {
186    constructor: TypeError,
187    message: "Class constructor Serializer cannot be invoked without 'new'",
188    code: 'ERR_CONSTRUCT_CALL_REQUIRED'
189  });
190  assert.throws(() => v8.Deserializer(), {
191    constructor: TypeError,
192    message: "Class constructor Deserializer cannot be invoked without 'new'",
193    code: 'ERR_CONSTRUCT_CALL_REQUIRED'
194  });
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  // Regression test for https://github.com/nodejs/node/issues/37978
242  assert.throws(() => {
243    new v8.Deserializer(new v8.Serializer().releaseBuffer()).readDouble();
244  }, /ReadDouble\(\) failed/);
245}
246