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