/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import { CharCode } from 'vs/base/common/charCode'; import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { isThenable } from 'vs/base/common/async'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; export function SingleProxyRPCProtocol(thing: any): IExtHostContext & IExtHostRpcService { return { _serviceBrand: undefined, remoteAuthority: null!, getProxy(): T { return thing; }, set(identifier: ProxyIdentifier, value: R): R { return value; }, assertRegistered: undefined! }; } export class TestRPCProtocol implements IExtHostContext, IExtHostRpcService { public _serviceBrand: undefined; public remoteAuthority = null!; private _callCountValue: number = 0; private _idle?: Promise; private _completeIdle?: Function; private readonly _locals: { [id: string]: any; }; private readonly _proxies: { [id: string]: any; }; constructor() { this._locals = Object.create(null); this._proxies = Object.create(null); } private get _callCount(): number { return this._callCountValue; } private set _callCount(value: number) { this._callCountValue = value; if (this._callCountValue === 0) { if (this._completeIdle) { this._completeIdle(); } this._idle = undefined; } } sync(): Promise { return new Promise((c) => { setTimeout(c, 0); }).then(() => { if (this._callCount === 0) { return undefined; } if (!this._idle) { this._idle = new Promise((c, e) => { this._completeIdle = c; }); } return this._idle; }); } public getProxy(identifier: ProxyIdentifier): T { if (!this._proxies[identifier.sid]) { this._proxies[identifier.sid] = this._createProxy(identifier.sid); } return this._proxies[identifier.sid]; } private _createProxy(proxyId: string): T { let handler = { get: (target: any, name: string) => { if (!target[name] && name.charCodeAt(0) === CharCode.DollarSign) { target[name] = (...myArgs: any[]) => { return this._remoteCall(proxyId, name, myArgs); }; } return target[name]; } }; return new Proxy(Object.create(null), handler); } public set(identifier: ProxyIdentifier, value: R): R { this._locals[identifier.sid] = value; return value; } protected _remoteCall(proxyId: string, path: string, args: any[]): Promise { this._callCount++; return new Promise((c) => { setTimeout(c, 0); }).then(() => { const instance = this._locals[proxyId]; // pretend the args went over the wire... (invoke .toJSON on objects...) const wireArgs = simulateWireTransfer(args); let p: Promise; try { let result = (instance[path]).apply(instance, wireArgs); p = isThenable(result) ? result : Promise.resolve(result); } catch (err) { p = Promise.reject(err); } return p.then(result => { this._callCount--; // pretend the result went over the wire... (invoke .toJSON on objects...) const wireResult = simulateWireTransfer(result); return wireResult; }, err => { this._callCount--; return Promise.reject(err); }); }); } public assertRegistered(identifiers: ProxyIdentifier[]): void { throw new Error('Not implemented!'); } } function simulateWireTransfer(obj: T): T { if (!obj) { return obj; } return JSON.parse(JSON.stringify(obj)); }