Path: blob/main/src/vs/workbench/services/extensions/common/rpcProtocol.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { RunOnceScheduler } from '../../../../base/common/async.js';6import { VSBuffer } from '../../../../base/common/buffer.js';7import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js';8import { CharCode } from '../../../../base/common/charCode.js';9import * as errors from '../../../../base/common/errors.js';10import { Emitter, Event } from '../../../../base/common/event.js';11import { Disposable, DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js';12import { MarshalledObject } from '../../../../base/common/marshalling.js';13import { MarshalledId } from '../../../../base/common/marshallingIds.js';14import { IURITransformer, transformIncomingURIs } from '../../../../base/common/uriIpc.js';15import { IMessagePassingProtocol } from '../../../../base/parts/ipc/common/ipc.js';16import { CanceledLazyPromise, LazyPromise } from './lazyPromise.js';17import { getStringIdentifierForProxy, IRPCProtocol, Proxied, ProxyIdentifier, SerializableObjectWithBuffers } from './proxyIdentifier.js';1819export interface JSONStringifyReplacer {20(key: string, value: any): any;21}2223function safeStringify(obj: any, replacer: JSONStringifyReplacer | null): string {24try {25return JSON.stringify(obj, <(key: string, value: any) => any>replacer);26} catch (err) {27return 'null';28}29}3031const refSymbolName = '$$ref$$';32const undefinedRef = { [refSymbolName]: -1 } as const;3334class StringifiedJsonWithBufferRefs {35constructor(36public readonly jsonString: string,37public readonly referencedBuffers: readonly VSBuffer[],38) { }39}4041export function stringifyJsonWithBufferRefs<T>(obj: T, replacer: JSONStringifyReplacer | null = null, useSafeStringify = false): StringifiedJsonWithBufferRefs {42const foundBuffers: VSBuffer[] = [];43const serialized = (useSafeStringify ? safeStringify : JSON.stringify)(obj, (key, value) => {44if (typeof value === 'undefined') {45return undefinedRef; // JSON.stringify normally converts 'undefined' to 'null'46} else if (typeof value === 'object') {47if (value instanceof VSBuffer) {48const bufferIndex = foundBuffers.push(value) - 1;49return { [refSymbolName]: bufferIndex };50}51if (replacer) {52return replacer(key, value);53}54}55return value;56});57return {58jsonString: serialized,59referencedBuffers: foundBuffers60};61}6263export function parseJsonAndRestoreBufferRefs(jsonString: string, buffers: readonly VSBuffer[], uriTransformer: IURITransformer | null): any {64return JSON.parse(jsonString, (_key, value) => {65if (value) {66const ref = value[refSymbolName];67if (typeof ref === 'number') {68return buffers[ref];69}7071if (uriTransformer && (<MarshalledObject>value).$mid === MarshalledId.Uri) {72return uriTransformer.transformIncoming(value);73}74}75return value;76});77}787980function stringify(obj: any, replacer: JSONStringifyReplacer | null): string {81return JSON.stringify(obj, <(key: string, value: any) => any>replacer);82}8384function createURIReplacer(transformer: IURITransformer | null): JSONStringifyReplacer | null {85if (!transformer) {86return null;87}88return (key: string, value: any): any => {89if (value && value.$mid === MarshalledId.Uri) {90return transformer.transformOutgoing(value);91}92return value;93};94}9596export const enum RequestInitiator {97LocalSide = 0,98OtherSide = 199}100101export const enum ResponsiveState {102Responsive = 0,103Unresponsive = 1104}105106export interface IRPCProtocolLogger {107logIncoming(msgLength: number, req: number, initiator: RequestInitiator, str: string, data?: any): void;108logOutgoing(msgLength: number, req: number, initiator: RequestInitiator, str: string, data?: any): void;109}110111const noop = () => { };112113const _RPCProtocolSymbol = Symbol.for('rpcProtocol');114const _RPCProxySymbol = Symbol.for('rpcProxy');115116export class RPCProtocol extends Disposable implements IRPCProtocol {117118[_RPCProtocolSymbol] = true;119120private static readonly UNRESPONSIVE_TIME = 3 * 1000; // 3s121122private readonly _onDidChangeResponsiveState: Emitter<ResponsiveState> = this._register(new Emitter<ResponsiveState>());123public readonly onDidChangeResponsiveState: Event<ResponsiveState> = this._onDidChangeResponsiveState.event;124125private readonly _protocol: IMessagePassingProtocol;126private readonly _logger: IRPCProtocolLogger | null;127private readonly _uriTransformer: IURITransformer | null;128private readonly _uriReplacer: JSONStringifyReplacer | null;129private _isDisposed: boolean;130private readonly _locals: any[];131private readonly _proxies: any[];132private _lastMessageId: number;133private readonly _cancelInvokedHandlers: { [req: string]: () => void };134private readonly _pendingRPCReplies: { [msgId: string]: PendingRPCReply };135private _responsiveState: ResponsiveState;136private _unacknowledgedCount: number;137private _unresponsiveTime: number;138private _asyncCheckUresponsive: RunOnceScheduler;139140constructor(protocol: IMessagePassingProtocol, logger: IRPCProtocolLogger | null = null, transformer: IURITransformer | null = null) {141super();142this._protocol = protocol;143this._logger = logger;144this._uriTransformer = transformer;145this._uriReplacer = createURIReplacer(this._uriTransformer);146this._isDisposed = false;147this._locals = [];148this._proxies = [];149for (let i = 0, len = ProxyIdentifier.count; i < len; i++) {150this._locals[i] = null;151this._proxies[i] = null;152}153this._lastMessageId = 0;154this._cancelInvokedHandlers = Object.create(null);155this._pendingRPCReplies = {};156this._responsiveState = ResponsiveState.Responsive;157this._unacknowledgedCount = 0;158this._unresponsiveTime = 0;159this._asyncCheckUresponsive = this._register(new RunOnceScheduler(() => this._checkUnresponsive(), 1000));160this._register(this._protocol.onMessage((msg) => this._receiveOneMessage(msg)));161}162163public override dispose(): void {164this._isDisposed = true;165166// Release all outstanding promises with a canceled error167Object.keys(this._pendingRPCReplies).forEach((msgId) => {168const pending = this._pendingRPCReplies[msgId];169delete this._pendingRPCReplies[msgId];170pending.resolveErr(errors.canceled());171});172173super.dispose();174}175176public drain(): Promise<void> {177if (typeof this._protocol.drain === 'function') {178return this._protocol.drain();179}180return Promise.resolve();181}182183private _onWillSendRequest(req: number): void {184if (this._unacknowledgedCount === 0) {185// Since this is the first request we are sending in a while,186// mark this moment as the start for the countdown to unresponsive time187this._unresponsiveTime = Date.now() + RPCProtocol.UNRESPONSIVE_TIME;188}189this._unacknowledgedCount++;190if (!this._asyncCheckUresponsive.isScheduled()) {191this._asyncCheckUresponsive.schedule();192}193}194195private _onDidReceiveAcknowledge(req: number): void {196// The next possible unresponsive time is now + delta.197this._unresponsiveTime = Date.now() + RPCProtocol.UNRESPONSIVE_TIME;198this._unacknowledgedCount--;199if (this._unacknowledgedCount === 0) {200// No more need to check for unresponsive201this._asyncCheckUresponsive.cancel();202}203// The ext host is responsive!204this._setResponsiveState(ResponsiveState.Responsive);205}206207private _checkUnresponsive(): void {208if (this._unacknowledgedCount === 0) {209// Not waiting for anything => cannot say if it is responsive or not210return;211}212213if (Date.now() > this._unresponsiveTime) {214// Unresponsive!!215this._setResponsiveState(ResponsiveState.Unresponsive);216} else {217// Not (yet) unresponsive, be sure to check again soon218this._asyncCheckUresponsive.schedule();219}220}221222private _setResponsiveState(newResponsiveState: ResponsiveState): void {223if (this._responsiveState === newResponsiveState) {224// no change225return;226}227this._responsiveState = newResponsiveState;228this._onDidChangeResponsiveState.fire(this._responsiveState);229}230231public get responsiveState(): ResponsiveState {232return this._responsiveState;233}234235public transformIncomingURIs<T>(obj: T): T {236if (!this._uriTransformer) {237return obj;238}239return transformIncomingURIs(obj, this._uriTransformer);240}241242public getProxy<T>(identifier: ProxyIdentifier<T>): Proxied<T> {243const { nid: rpcId, sid } = identifier;244if (!this._proxies[rpcId]) {245this._proxies[rpcId] = this._createProxy(rpcId, sid);246}247return this._proxies[rpcId];248}249250private _createProxy<T>(rpcId: number, debugName: string): T {251const handler = {252get: (target: any, name: PropertyKey) => {253if (typeof name === 'string' && !target[name] && name.charCodeAt(0) === CharCode.DollarSign) {254target[name] = (...myArgs: any[]) => {255return this._remoteCall(rpcId, name, myArgs);256};257}258if (name === _RPCProxySymbol) {259return debugName;260}261return target[name];262}263};264return new Proxy(Object.create(null), handler);265}266267public set<T, R extends T>(identifier: ProxyIdentifier<T>, value: R): R {268this._locals[identifier.nid] = value;269return value;270}271272public assertRegistered(identifiers: ProxyIdentifier<any>[]): void {273for (let i = 0, len = identifiers.length; i < len; i++) {274const identifier = identifiers[i];275if (!this._locals[identifier.nid]) {276throw new Error(`Missing proxy instance ${identifier.sid}`);277}278}279}280281private _receiveOneMessage(rawmsg: VSBuffer): void {282if (this._isDisposed) {283return;284}285286const msgLength = rawmsg.byteLength;287const buff = MessageBuffer.read(rawmsg, 0);288const messageType = <MessageType>buff.readUInt8();289const req = buff.readUInt32();290291switch (messageType) {292case MessageType.RequestJSONArgs:293case MessageType.RequestJSONArgsWithCancellation: {294let { rpcId, method, args } = MessageIO.deserializeRequestJSONArgs(buff);295if (this._uriTransformer) {296args = transformIncomingURIs(args, this._uriTransformer);297}298this._receiveRequest(msgLength, req, rpcId, method, args, (messageType === MessageType.RequestJSONArgsWithCancellation));299break;300}301case MessageType.RequestMixedArgs:302case MessageType.RequestMixedArgsWithCancellation: {303let { rpcId, method, args } = MessageIO.deserializeRequestMixedArgs(buff);304if (this._uriTransformer) {305args = transformIncomingURIs(args, this._uriTransformer);306}307this._receiveRequest(msgLength, req, rpcId, method, args, (messageType === MessageType.RequestMixedArgsWithCancellation));308break;309}310case MessageType.Acknowledged: {311this._logger?.logIncoming(msgLength, req, RequestInitiator.LocalSide, `ack`);312this._onDidReceiveAcknowledge(req);313break;314}315case MessageType.Cancel: {316this._receiveCancel(msgLength, req);317break;318}319case MessageType.ReplyOKEmpty: {320this._receiveReply(msgLength, req, undefined);321break;322}323case MessageType.ReplyOKJSON: {324let value = MessageIO.deserializeReplyOKJSON(buff);325if (this._uriTransformer) {326value = transformIncomingURIs(value, this._uriTransformer);327}328this._receiveReply(msgLength, req, value);329break;330}331case MessageType.ReplyOKJSONWithBuffers: {332const value = MessageIO.deserializeReplyOKJSONWithBuffers(buff, this._uriTransformer);333this._receiveReply(msgLength, req, value);334break;335}336case MessageType.ReplyOKVSBuffer: {337const value = MessageIO.deserializeReplyOKVSBuffer(buff);338this._receiveReply(msgLength, req, value);339break;340}341case MessageType.ReplyErrError: {342let err = MessageIO.deserializeReplyErrError(buff);343if (this._uriTransformer) {344err = transformIncomingURIs(err, this._uriTransformer);345}346this._receiveReplyErr(msgLength, req, err);347break;348}349case MessageType.ReplyErrEmpty: {350this._receiveReplyErr(msgLength, req, undefined);351break;352}353default:354console.error(`received unexpected message`);355console.error(rawmsg);356}357}358359private _receiveRequest(msgLength: number, req: number, rpcId: number, method: string, args: any[], usesCancellationToken: boolean): void {360this._logger?.logIncoming(msgLength, req, RequestInitiator.OtherSide, `receiveRequest ${getStringIdentifierForProxy(rpcId)}.${method}(`, args);361const callId = String(req);362363let promise: Promise<any>;364let cancel: () => void;365if (usesCancellationToken) {366const cancellationTokenSource = new CancellationTokenSource();367args.push(cancellationTokenSource.token);368promise = this._invokeHandler(rpcId, method, args);369cancel = () => cancellationTokenSource.cancel();370} else {371// cannot be cancelled372promise = this._invokeHandler(rpcId, method, args);373cancel = noop;374}375376this._cancelInvokedHandlers[callId] = cancel;377378// Acknowledge the request379const msg = MessageIO.serializeAcknowledged(req);380this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `ack`);381this._protocol.send(msg);382383promise.then((r) => {384delete this._cancelInvokedHandlers[callId];385const msg = MessageIO.serializeReplyOK(req, r, this._uriReplacer);386this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `reply:`, r);387this._protocol.send(msg);388}, (err) => {389delete this._cancelInvokedHandlers[callId];390const msg = MessageIO.serializeReplyErr(req, err);391this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `replyErr:`, err);392this._protocol.send(msg);393});394}395396private _receiveCancel(msgLength: number, req: number): void {397this._logger?.logIncoming(msgLength, req, RequestInitiator.OtherSide, `receiveCancel`);398const callId = String(req);399this._cancelInvokedHandlers[callId]?.();400}401402private _receiveReply(msgLength: number, req: number, value: any): void {403this._logger?.logIncoming(msgLength, req, RequestInitiator.LocalSide, `receiveReply:`, value);404const callId = String(req);405if (!this._pendingRPCReplies.hasOwnProperty(callId)) {406return;407}408409const pendingReply = this._pendingRPCReplies[callId];410delete this._pendingRPCReplies[callId];411412pendingReply.resolveOk(value);413}414415private _receiveReplyErr(msgLength: number, req: number, value: any): void {416this._logger?.logIncoming(msgLength, req, RequestInitiator.LocalSide, `receiveReplyErr:`, value);417418const callId = String(req);419if (!this._pendingRPCReplies.hasOwnProperty(callId)) {420return;421}422423const pendingReply = this._pendingRPCReplies[callId];424delete this._pendingRPCReplies[callId];425426let err: any = undefined;427if (value) {428if (value.$isError) {429err = new Error();430err.name = value.name;431err.message = value.message;432err.stack = value.stack;433} else {434err = value;435}436}437pendingReply.resolveErr(err);438}439440private _invokeHandler(rpcId: number, methodName: string, args: any[]): Promise<any> {441try {442return Promise.resolve(this._doInvokeHandler(rpcId, methodName, args));443} catch (err) {444return Promise.reject(err);445}446}447448private _doInvokeHandler(rpcId: number, methodName: string, args: any[]): any {449const actor = this._locals[rpcId];450if (!actor) {451throw new Error('Unknown actor ' + getStringIdentifierForProxy(rpcId));452}453const method = actor[methodName];454if (typeof method !== 'function') {455throw new Error('Unknown method ' + methodName + ' on actor ' + getStringIdentifierForProxy(rpcId));456}457return method.apply(actor, args);458}459460private _remoteCall(rpcId: number, methodName: string, args: any[]): Promise<any> {461if (this._isDisposed) {462return new CanceledLazyPromise();463}464let cancellationToken: CancellationToken | null = null;465if (args.length > 0 && CancellationToken.isCancellationToken(args[args.length - 1])) {466cancellationToken = args.pop();467}468469if (cancellationToken && cancellationToken.isCancellationRequested) {470// No need to do anything...471return Promise.reject<any>(errors.canceled());472}473474const serializedRequestArguments = MessageIO.serializeRequestArguments(args, this._uriReplacer);475476const req = ++this._lastMessageId;477const callId = String(req);478const result = new LazyPromise();479480const disposable = new DisposableStore();481if (cancellationToken) {482disposable.add(cancellationToken.onCancellationRequested(() => {483const msg = MessageIO.serializeCancel(req);484this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.LocalSide, `cancel`);485this._protocol.send(msg);486}));487}488489this._pendingRPCReplies[callId] = new PendingRPCReply(result, disposable);490this._onWillSendRequest(req);491const msg = MessageIO.serializeRequest(req, rpcId, methodName, serializedRequestArguments, !!cancellationToken);492this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.LocalSide, `request: ${getStringIdentifierForProxy(rpcId)}.${methodName}(`, args);493this._protocol.send(msg);494return result;495}496}497498class PendingRPCReply {499constructor(500private readonly _promise: LazyPromise,501private readonly _disposable: IDisposable502) { }503504public resolveOk(value: any): void {505this._promise.resolveOk(value);506this._disposable.dispose();507}508509public resolveErr(err: any): void {510this._promise.resolveErr(err);511this._disposable.dispose();512}513}514515class MessageBuffer {516517public static alloc(type: MessageType, req: number, messageSize: number): MessageBuffer {518const result = new MessageBuffer(VSBuffer.alloc(messageSize + 1 /* type */ + 4 /* req */), 0);519result.writeUInt8(type);520result.writeUInt32(req);521return result;522}523524public static read(buff: VSBuffer, offset: number): MessageBuffer {525return new MessageBuffer(buff, offset);526}527528private _buff: VSBuffer;529private _offset: number;530531public get buffer(): VSBuffer {532return this._buff;533}534535private constructor(buff: VSBuffer, offset: number) {536this._buff = buff;537this._offset = offset;538}539540public static sizeUInt8(): number {541return 1;542}543544public static readonly sizeUInt32 = 4;545546public writeUInt8(n: number): void {547this._buff.writeUInt8(n, this._offset); this._offset += 1;548}549550public readUInt8(): number {551const n = this._buff.readUInt8(this._offset); this._offset += 1;552return n;553}554555public writeUInt32(n: number): void {556this._buff.writeUInt32BE(n, this._offset); this._offset += 4;557}558559public readUInt32(): number {560const n = this._buff.readUInt32BE(this._offset); this._offset += 4;561return n;562}563564public static sizeShortString(str: VSBuffer): number {565return 1 /* string length */ + str.byteLength /* actual string */;566}567568public writeShortString(str: VSBuffer): void {569this._buff.writeUInt8(str.byteLength, this._offset); this._offset += 1;570this._buff.set(str, this._offset); this._offset += str.byteLength;571}572573public readShortString(): string {574const strByteLength = this._buff.readUInt8(this._offset); this._offset += 1;575const strBuff = this._buff.slice(this._offset, this._offset + strByteLength);576const str = strBuff.toString(); this._offset += strByteLength;577return str;578}579580public static sizeLongString(str: VSBuffer): number {581return 4 /* string length */ + str.byteLength /* actual string */;582}583584public writeLongString(str: VSBuffer): void {585this._buff.writeUInt32BE(str.byteLength, this._offset); this._offset += 4;586this._buff.set(str, this._offset); this._offset += str.byteLength;587}588589public readLongString(): string {590const strByteLength = this._buff.readUInt32BE(this._offset); this._offset += 4;591const strBuff = this._buff.slice(this._offset, this._offset + strByteLength);592const str = strBuff.toString(); this._offset += strByteLength;593return str;594}595596public writeBuffer(buff: VSBuffer): void {597this._buff.writeUInt32BE(buff.byteLength, this._offset); this._offset += 4;598this._buff.set(buff, this._offset); this._offset += buff.byteLength;599}600601public static sizeVSBuffer(buff: VSBuffer): number {602return 4 /* buffer length */ + buff.byteLength /* actual buffer */;603}604605public writeVSBuffer(buff: VSBuffer): void {606this._buff.writeUInt32BE(buff.byteLength, this._offset); this._offset += 4;607this._buff.set(buff, this._offset); this._offset += buff.byteLength;608}609610public readVSBuffer(): VSBuffer {611const buffLength = this._buff.readUInt32BE(this._offset); this._offset += 4;612const buff = this._buff.slice(this._offset, this._offset + buffLength); this._offset += buffLength;613return buff;614}615616public static sizeMixedArray(arr: readonly MixedArg[]): number {617let size = 0;618size += 1; // arr length619for (let i = 0, len = arr.length; i < len; i++) {620const el = arr[i];621size += 1; // arg type622switch (el.type) {623case ArgType.String:624size += this.sizeLongString(el.value);625break;626case ArgType.VSBuffer:627size += this.sizeVSBuffer(el.value);628break;629case ArgType.SerializedObjectWithBuffers:630size += this.sizeUInt32; // buffer count631size += this.sizeLongString(el.value);632for (let i = 0; i < el.buffers.length; ++i) {633size += this.sizeVSBuffer(el.buffers[i]);634}635break;636case ArgType.Undefined:637// empty...638break;639}640}641return size;642}643644public writeMixedArray(arr: readonly MixedArg[]): void {645this._buff.writeUInt8(arr.length, this._offset); this._offset += 1;646for (let i = 0, len = arr.length; i < len; i++) {647const el = arr[i];648switch (el.type) {649case ArgType.String:650this.writeUInt8(ArgType.String);651this.writeLongString(el.value);652break;653case ArgType.VSBuffer:654this.writeUInt8(ArgType.VSBuffer);655this.writeVSBuffer(el.value);656break;657case ArgType.SerializedObjectWithBuffers:658this.writeUInt8(ArgType.SerializedObjectWithBuffers);659this.writeUInt32(el.buffers.length);660this.writeLongString(el.value);661for (let i = 0; i < el.buffers.length; ++i) {662this.writeBuffer(el.buffers[i]);663}664break;665case ArgType.Undefined:666this.writeUInt8(ArgType.Undefined);667break;668}669}670}671672public readMixedArray(): Array<string | VSBuffer | SerializableObjectWithBuffers<any> | undefined> {673const arrLen = this._buff.readUInt8(this._offset); this._offset += 1;674const arr: Array<string | VSBuffer | SerializableObjectWithBuffers<any> | undefined> = new Array(arrLen);675for (let i = 0; i < arrLen; i++) {676const argType = <ArgType>this.readUInt8();677switch (argType) {678case ArgType.String:679arr[i] = this.readLongString();680break;681case ArgType.VSBuffer:682arr[i] = this.readVSBuffer();683break;684case ArgType.SerializedObjectWithBuffers: {685const bufferCount = this.readUInt32();686const jsonString = this.readLongString();687const buffers: VSBuffer[] = [];688for (let i = 0; i < bufferCount; ++i) {689buffers.push(this.readVSBuffer());690}691arr[i] = new SerializableObjectWithBuffers(parseJsonAndRestoreBufferRefs(jsonString, buffers, null));692break;693}694case ArgType.Undefined:695arr[i] = undefined;696break;697}698}699return arr;700}701}702703const enum SerializedRequestArgumentType {704Simple,705Mixed,706}707708type SerializedRequestArguments =709| { readonly type: SerializedRequestArgumentType.Simple; args: string }710| { readonly type: SerializedRequestArgumentType.Mixed; args: MixedArg[] };711712713class MessageIO {714715private static _useMixedArgSerialization(arr: any[]): boolean {716for (let i = 0, len = arr.length; i < len; i++) {717if (arr[i] instanceof VSBuffer) {718return true;719}720if (arr[i] instanceof SerializableObjectWithBuffers) {721return true;722}723if (typeof arr[i] === 'undefined') {724return true;725}726}727return false;728}729730public static serializeRequestArguments(args: any[], replacer: JSONStringifyReplacer | null): SerializedRequestArguments {731if (this._useMixedArgSerialization(args)) {732const massagedArgs: MixedArg[] = [];733for (let i = 0, len = args.length; i < len; i++) {734const arg = args[i];735if (arg instanceof VSBuffer) {736massagedArgs[i] = { type: ArgType.VSBuffer, value: arg };737} else if (typeof arg === 'undefined') {738massagedArgs[i] = { type: ArgType.Undefined };739} else if (arg instanceof SerializableObjectWithBuffers) {740const { jsonString, referencedBuffers } = stringifyJsonWithBufferRefs(arg.value, replacer);741massagedArgs[i] = { type: ArgType.SerializedObjectWithBuffers, value: VSBuffer.fromString(jsonString), buffers: referencedBuffers };742} else {743massagedArgs[i] = { type: ArgType.String, value: VSBuffer.fromString(stringify(arg, replacer)) };744}745}746return {747type: SerializedRequestArgumentType.Mixed,748args: massagedArgs,749};750}751return {752type: SerializedRequestArgumentType.Simple,753args: stringify(args, replacer)754};755}756757public static serializeRequest(req: number, rpcId: number, method: string, serializedArgs: SerializedRequestArguments, usesCancellationToken: boolean): VSBuffer {758switch (serializedArgs.type) {759case SerializedRequestArgumentType.Simple:760return this._requestJSONArgs(req, rpcId, method, serializedArgs.args, usesCancellationToken);761case SerializedRequestArgumentType.Mixed:762return this._requestMixedArgs(req, rpcId, method, serializedArgs.args, usesCancellationToken);763}764}765766private static _requestJSONArgs(req: number, rpcId: number, method: string, args: string, usesCancellationToken: boolean): VSBuffer {767const methodBuff = VSBuffer.fromString(method);768const argsBuff = VSBuffer.fromString(args);769770let len = 0;771len += MessageBuffer.sizeUInt8();772len += MessageBuffer.sizeShortString(methodBuff);773len += MessageBuffer.sizeLongString(argsBuff);774775const result = MessageBuffer.alloc(usesCancellationToken ? MessageType.RequestJSONArgsWithCancellation : MessageType.RequestJSONArgs, req, len);776result.writeUInt8(rpcId);777result.writeShortString(methodBuff);778result.writeLongString(argsBuff);779return result.buffer;780}781782public static deserializeRequestJSONArgs(buff: MessageBuffer): { rpcId: number; method: string; args: any[] } {783const rpcId = buff.readUInt8();784const method = buff.readShortString();785const args = buff.readLongString();786return {787rpcId: rpcId,788method: method,789args: JSON.parse(args)790};791}792793private static _requestMixedArgs(req: number, rpcId: number, method: string, args: readonly MixedArg[], usesCancellationToken: boolean): VSBuffer {794const methodBuff = VSBuffer.fromString(method);795796let len = 0;797len += MessageBuffer.sizeUInt8();798len += MessageBuffer.sizeShortString(methodBuff);799len += MessageBuffer.sizeMixedArray(args);800801const result = MessageBuffer.alloc(usesCancellationToken ? MessageType.RequestMixedArgsWithCancellation : MessageType.RequestMixedArgs, req, len);802result.writeUInt8(rpcId);803result.writeShortString(methodBuff);804result.writeMixedArray(args);805return result.buffer;806}807808public static deserializeRequestMixedArgs(buff: MessageBuffer): { rpcId: number; method: string; args: any[] } {809const rpcId = buff.readUInt8();810const method = buff.readShortString();811const rawargs = buff.readMixedArray();812const args: any[] = new Array(rawargs.length);813for (let i = 0, len = rawargs.length; i < len; i++) {814const rawarg = rawargs[i];815if (typeof rawarg === 'string') {816args[i] = JSON.parse(rawarg);817} else {818args[i] = rawarg;819}820}821return {822rpcId: rpcId,823method: method,824args: args825};826}827828public static serializeAcknowledged(req: number): VSBuffer {829return MessageBuffer.alloc(MessageType.Acknowledged, req, 0).buffer;830}831832public static serializeCancel(req: number): VSBuffer {833return MessageBuffer.alloc(MessageType.Cancel, req, 0).buffer;834}835836public static serializeReplyOK(req: number, res: any, replacer: JSONStringifyReplacer | null): VSBuffer {837if (typeof res === 'undefined') {838return this._serializeReplyOKEmpty(req);839} else if (res instanceof VSBuffer) {840return this._serializeReplyOKVSBuffer(req, res);841} else if (res instanceof SerializableObjectWithBuffers) {842const { jsonString, referencedBuffers } = stringifyJsonWithBufferRefs(res.value, replacer, true);843return this._serializeReplyOKJSONWithBuffers(req, jsonString, referencedBuffers);844} else {845return this._serializeReplyOKJSON(req, safeStringify(res, replacer));846}847}848849private static _serializeReplyOKEmpty(req: number): VSBuffer {850return MessageBuffer.alloc(MessageType.ReplyOKEmpty, req, 0).buffer;851}852853private static _serializeReplyOKVSBuffer(req: number, res: VSBuffer): VSBuffer {854let len = 0;855len += MessageBuffer.sizeVSBuffer(res);856857const result = MessageBuffer.alloc(MessageType.ReplyOKVSBuffer, req, len);858result.writeVSBuffer(res);859return result.buffer;860}861862public static deserializeReplyOKVSBuffer(buff: MessageBuffer): VSBuffer {863return buff.readVSBuffer();864}865866private static _serializeReplyOKJSON(req: number, res: string): VSBuffer {867const resBuff = VSBuffer.fromString(res);868869let len = 0;870len += MessageBuffer.sizeLongString(resBuff);871872const result = MessageBuffer.alloc(MessageType.ReplyOKJSON, req, len);873result.writeLongString(resBuff);874return result.buffer;875}876877private static _serializeReplyOKJSONWithBuffers(req: number, res: string, buffers: readonly VSBuffer[]): VSBuffer {878const resBuff = VSBuffer.fromString(res);879880let len = 0;881len += MessageBuffer.sizeUInt32; // buffer count882len += MessageBuffer.sizeLongString(resBuff);883for (const buffer of buffers) {884len += MessageBuffer.sizeVSBuffer(buffer);885}886887const result = MessageBuffer.alloc(MessageType.ReplyOKJSONWithBuffers, req, len);888result.writeUInt32(buffers.length);889result.writeLongString(resBuff);890for (const buffer of buffers) {891result.writeBuffer(buffer);892}893894return result.buffer;895}896897public static deserializeReplyOKJSON(buff: MessageBuffer): any {898const res = buff.readLongString();899return JSON.parse(res);900}901902public static deserializeReplyOKJSONWithBuffers(buff: MessageBuffer, uriTransformer: IURITransformer | null): SerializableObjectWithBuffers<any> {903const bufferCount = buff.readUInt32();904const res = buff.readLongString();905906const buffers: VSBuffer[] = [];907for (let i = 0; i < bufferCount; ++i) {908buffers.push(buff.readVSBuffer());909}910911return new SerializableObjectWithBuffers(parseJsonAndRestoreBufferRefs(res, buffers, uriTransformer));912}913914public static serializeReplyErr(req: number, err: any): VSBuffer {915const errStr: string | undefined = (err ? safeStringify(errors.transformErrorForSerialization(err), null) : undefined);916if (typeof errStr !== 'string') {917return this._serializeReplyErrEmpty(req);918}919const errBuff = VSBuffer.fromString(errStr);920921let len = 0;922len += MessageBuffer.sizeLongString(errBuff);923924const result = MessageBuffer.alloc(MessageType.ReplyErrError, req, len);925result.writeLongString(errBuff);926return result.buffer;927}928929public static deserializeReplyErrError(buff: MessageBuffer): Error {930const err = buff.readLongString();931return JSON.parse(err);932}933934private static _serializeReplyErrEmpty(req: number): VSBuffer {935return MessageBuffer.alloc(MessageType.ReplyErrEmpty, req, 0).buffer;936}937}938939const enum MessageType {940RequestJSONArgs = 1,941RequestJSONArgsWithCancellation = 2,942RequestMixedArgs = 3,943RequestMixedArgsWithCancellation = 4,944Acknowledged = 5,945Cancel = 6,946ReplyOKEmpty = 7,947ReplyOKVSBuffer = 8,948ReplyOKJSON = 9,949ReplyOKJSONWithBuffers = 10,950ReplyErrError = 11,951ReplyErrEmpty = 12,952}953954const enum ArgType {955String = 1,956VSBuffer = 2,957SerializedObjectWithBuffers = 3,958Undefined = 4,959}960961962type MixedArg =963| { readonly type: ArgType.String; readonly value: VSBuffer }964| { readonly type: ArgType.VSBuffer; readonly value: VSBuffer }965| { readonly type: ArgType.SerializedObjectWithBuffers; readonly value: VSBuffer; readonly buffers: readonly VSBuffer[] }966| { readonly type: ArgType.Undefined }967;968969970