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