11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ciconst {
41cb0ef41Sopenharmony_ci  JSONParse,
51cb0ef41Sopenharmony_ci  JSONStringify,
61cb0ef41Sopenharmony_ci  StringPrototypeSplit,
71cb0ef41Sopenharmony_ci  ArrayPrototypePush,
81cb0ef41Sopenharmony_ci  Symbol,
91cb0ef41Sopenharmony_ci  TypedArrayPrototypeSubarray,
101cb0ef41Sopenharmony_ci} = primordials;
111cb0ef41Sopenharmony_ciconst { Buffer } = require('buffer');
121cb0ef41Sopenharmony_ciconst { StringDecoder } = require('string_decoder');
131cb0ef41Sopenharmony_ciconst v8 = require('v8');
141cb0ef41Sopenharmony_ciconst { isArrayBufferView } = require('internal/util/types');
151cb0ef41Sopenharmony_ciconst assert = require('internal/assert');
161cb0ef41Sopenharmony_ciconst { streamBaseState, kLastWriteWasAsync } = internalBinding('stream_wrap');
171cb0ef41Sopenharmony_ci
181cb0ef41Sopenharmony_ciconst kMessageBuffer = Symbol('kMessageBuffer');
191cb0ef41Sopenharmony_ciconst kMessageBufferSize = Symbol('kMessageBufferSize');
201cb0ef41Sopenharmony_ciconst kJSONBuffer = Symbol('kJSONBuffer');
211cb0ef41Sopenharmony_ciconst kStringDecoder = Symbol('kStringDecoder');
221cb0ef41Sopenharmony_ci
231cb0ef41Sopenharmony_ci// Extend V8's serializer APIs to give more JSON-like behaviour in
241cb0ef41Sopenharmony_ci// some cases; in particular, for native objects this serializes them the same
251cb0ef41Sopenharmony_ci// way that JSON does rather than throwing an exception.
261cb0ef41Sopenharmony_ciconst kArrayBufferViewTag = 0;
271cb0ef41Sopenharmony_ciconst kNotArrayBufferViewTag = 1;
281cb0ef41Sopenharmony_ciclass ChildProcessSerializer extends v8.DefaultSerializer {
291cb0ef41Sopenharmony_ci  _writeHostObject(object) {
301cb0ef41Sopenharmony_ci    if (isArrayBufferView(object)) {
311cb0ef41Sopenharmony_ci      this.writeUint32(kArrayBufferViewTag);
321cb0ef41Sopenharmony_ci      return super._writeHostObject(object);
331cb0ef41Sopenharmony_ci    }
341cb0ef41Sopenharmony_ci    this.writeUint32(kNotArrayBufferViewTag);
351cb0ef41Sopenharmony_ci    this.writeValue({ ...object });
361cb0ef41Sopenharmony_ci  }
371cb0ef41Sopenharmony_ci}
381cb0ef41Sopenharmony_ci
391cb0ef41Sopenharmony_ciclass ChildProcessDeserializer extends v8.DefaultDeserializer {
401cb0ef41Sopenharmony_ci  _readHostObject() {
411cb0ef41Sopenharmony_ci    const tag = this.readUint32();
421cb0ef41Sopenharmony_ci    if (tag === kArrayBufferViewTag)
431cb0ef41Sopenharmony_ci      return super._readHostObject();
441cb0ef41Sopenharmony_ci
451cb0ef41Sopenharmony_ci    assert(tag === kNotArrayBufferViewTag);
461cb0ef41Sopenharmony_ci    return this.readValue();
471cb0ef41Sopenharmony_ci  }
481cb0ef41Sopenharmony_ci}
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_ci// Messages are parsed in either of the following formats:
511cb0ef41Sopenharmony_ci// - Newline-delimited JSON, or
521cb0ef41Sopenharmony_ci// - V8-serialized buffers, prefixed with their length as a big endian uint32
531cb0ef41Sopenharmony_ci//   (aka 'advanced')
541cb0ef41Sopenharmony_ciconst advanced = {
551cb0ef41Sopenharmony_ci  initMessageChannel(channel) {
561cb0ef41Sopenharmony_ci    channel[kMessageBuffer] = [];
571cb0ef41Sopenharmony_ci    channel[kMessageBufferSize] = 0;
581cb0ef41Sopenharmony_ci    channel.buffering = false;
591cb0ef41Sopenharmony_ci  },
601cb0ef41Sopenharmony_ci
611cb0ef41Sopenharmony_ci  *parseChannelMessages(channel, readData) {
621cb0ef41Sopenharmony_ci    if (readData.length === 0) return;
631cb0ef41Sopenharmony_ci
641cb0ef41Sopenharmony_ci    ArrayPrototypePush(channel[kMessageBuffer], readData);
651cb0ef41Sopenharmony_ci    channel[kMessageBufferSize] += readData.length;
661cb0ef41Sopenharmony_ci
671cb0ef41Sopenharmony_ci    // Index 0 should always be present because we just pushed data into it.
681cb0ef41Sopenharmony_ci    let messageBufferHead = channel[kMessageBuffer][0];
691cb0ef41Sopenharmony_ci    while (messageBufferHead.length >= 4) {
701cb0ef41Sopenharmony_ci      // We call `readUInt32BE` manually here, because this is faster than first converting
711cb0ef41Sopenharmony_ci      // it to a buffer and using `readUInt32BE` on that.
721cb0ef41Sopenharmony_ci      const fullMessageSize = (
731cb0ef41Sopenharmony_ci        messageBufferHead[0] << 24 |
741cb0ef41Sopenharmony_ci        messageBufferHead[1] << 16 |
751cb0ef41Sopenharmony_ci        messageBufferHead[2] << 8 |
761cb0ef41Sopenharmony_ci        messageBufferHead[3]
771cb0ef41Sopenharmony_ci      ) + 4;
781cb0ef41Sopenharmony_ci
791cb0ef41Sopenharmony_ci      if (channel[kMessageBufferSize] < fullMessageSize) break;
801cb0ef41Sopenharmony_ci
811cb0ef41Sopenharmony_ci      const concatenatedBuffer = channel[kMessageBuffer].length === 1 ?
821cb0ef41Sopenharmony_ci        channel[kMessageBuffer][0] :
831cb0ef41Sopenharmony_ci        Buffer.concat(
841cb0ef41Sopenharmony_ci          channel[kMessageBuffer],
851cb0ef41Sopenharmony_ci          channel[kMessageBufferSize],
861cb0ef41Sopenharmony_ci        );
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_ci      const deserializer = new ChildProcessDeserializer(
891cb0ef41Sopenharmony_ci        TypedArrayPrototypeSubarray(concatenatedBuffer, 4, fullMessageSize),
901cb0ef41Sopenharmony_ci      );
911cb0ef41Sopenharmony_ci
921cb0ef41Sopenharmony_ci      messageBufferHead = TypedArrayPrototypeSubarray(concatenatedBuffer, fullMessageSize);
931cb0ef41Sopenharmony_ci      channel[kMessageBufferSize] = messageBufferHead.length;
941cb0ef41Sopenharmony_ci      channel[kMessageBuffer] =
951cb0ef41Sopenharmony_ci        channel[kMessageBufferSize] !== 0 ? [messageBufferHead] : [];
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ci      deserializer.readHeader();
981cb0ef41Sopenharmony_ci      yield deserializer.readValue();
991cb0ef41Sopenharmony_ci    }
1001cb0ef41Sopenharmony_ci
1011cb0ef41Sopenharmony_ci    channel.buffering = channel[kMessageBufferSize] > 0;
1021cb0ef41Sopenharmony_ci  },
1031cb0ef41Sopenharmony_ci
1041cb0ef41Sopenharmony_ci  writeChannelMessage(channel, req, message, handle) {
1051cb0ef41Sopenharmony_ci    const ser = new ChildProcessSerializer();
1061cb0ef41Sopenharmony_ci    // Add 4 bytes, to later populate with message length
1071cb0ef41Sopenharmony_ci    ser.writeRawBytes(Buffer.allocUnsafe(4));
1081cb0ef41Sopenharmony_ci    ser.writeHeader();
1091cb0ef41Sopenharmony_ci    ser.writeValue(message);
1101cb0ef41Sopenharmony_ci
1111cb0ef41Sopenharmony_ci    const serializedMessage = ser.releaseBuffer();
1121cb0ef41Sopenharmony_ci    const serializedMessageLength = serializedMessage.length - 4;
1131cb0ef41Sopenharmony_ci
1141cb0ef41Sopenharmony_ci    serializedMessage.set([
1151cb0ef41Sopenharmony_ci      serializedMessageLength >> 24 & 0xFF,
1161cb0ef41Sopenharmony_ci      serializedMessageLength >> 16 & 0xFF,
1171cb0ef41Sopenharmony_ci      serializedMessageLength >> 8 & 0xFF,
1181cb0ef41Sopenharmony_ci      serializedMessageLength & 0xFF,
1191cb0ef41Sopenharmony_ci    ], 0);
1201cb0ef41Sopenharmony_ci
1211cb0ef41Sopenharmony_ci    const result = channel.writeBuffer(req, serializedMessage, handle);
1221cb0ef41Sopenharmony_ci
1231cb0ef41Sopenharmony_ci    // Mirror what stream_base_commons.js does for Buffer retention.
1241cb0ef41Sopenharmony_ci    if (streamBaseState[kLastWriteWasAsync])
1251cb0ef41Sopenharmony_ci      req.buffer = serializedMessage;
1261cb0ef41Sopenharmony_ci
1271cb0ef41Sopenharmony_ci    return result;
1281cb0ef41Sopenharmony_ci  },
1291cb0ef41Sopenharmony_ci};
1301cb0ef41Sopenharmony_ci
1311cb0ef41Sopenharmony_ciconst json = {
1321cb0ef41Sopenharmony_ci  initMessageChannel(channel) {
1331cb0ef41Sopenharmony_ci    channel[kJSONBuffer] = '';
1341cb0ef41Sopenharmony_ci    channel[kStringDecoder] = undefined;
1351cb0ef41Sopenharmony_ci  },
1361cb0ef41Sopenharmony_ci
1371cb0ef41Sopenharmony_ci  *parseChannelMessages(channel, readData) {
1381cb0ef41Sopenharmony_ci    if (readData.length === 0) return;
1391cb0ef41Sopenharmony_ci
1401cb0ef41Sopenharmony_ci    if (channel[kStringDecoder] === undefined)
1411cb0ef41Sopenharmony_ci      channel[kStringDecoder] = new StringDecoder('utf8');
1421cb0ef41Sopenharmony_ci    const chunks =
1431cb0ef41Sopenharmony_ci      StringPrototypeSplit(channel[kStringDecoder].write(readData), '\n');
1441cb0ef41Sopenharmony_ci    const numCompleteChunks = chunks.length - 1;
1451cb0ef41Sopenharmony_ci    // Last line does not have trailing linebreak
1461cb0ef41Sopenharmony_ci    const incompleteChunk = chunks[numCompleteChunks];
1471cb0ef41Sopenharmony_ci    if (numCompleteChunks === 0) {
1481cb0ef41Sopenharmony_ci      channel[kJSONBuffer] += incompleteChunk;
1491cb0ef41Sopenharmony_ci    } else {
1501cb0ef41Sopenharmony_ci      chunks[0] = channel[kJSONBuffer] + chunks[0];
1511cb0ef41Sopenharmony_ci      for (let i = 0; i < numCompleteChunks; i++)
1521cb0ef41Sopenharmony_ci        yield JSONParse(chunks[i]);
1531cb0ef41Sopenharmony_ci      channel[kJSONBuffer] = incompleteChunk;
1541cb0ef41Sopenharmony_ci    }
1551cb0ef41Sopenharmony_ci    channel.buffering = channel[kJSONBuffer].length !== 0;
1561cb0ef41Sopenharmony_ci  },
1571cb0ef41Sopenharmony_ci
1581cb0ef41Sopenharmony_ci  writeChannelMessage(channel, req, message, handle) {
1591cb0ef41Sopenharmony_ci    const string = JSONStringify(message) + '\n';
1601cb0ef41Sopenharmony_ci    return channel.writeUtf8String(req, string, handle);
1611cb0ef41Sopenharmony_ci  },
1621cb0ef41Sopenharmony_ci};
1631cb0ef41Sopenharmony_ci
1641cb0ef41Sopenharmony_cimodule.exports = { advanced, json };
165