Path: blob/main/src/vs/workbench/api/test/common/testRPCProtocol.ts
5242 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 { isThenable } from '../../../../base/common/async.js';6import { CharCode } from '../../../../base/common/charCode.js';7import { IExtHostRpcService } from '../../common/extHostRpcService.js';8import { IExtHostContext } from '../../../services/extensions/common/extHostCustomers.js';9import { ExtensionHostKind } from '../../../services/extensions/common/extensionHostKind.js';10import { Proxied, ProxyIdentifier, SerializableObjectWithBuffers } from '../../../services/extensions/common/proxyIdentifier.js';11import { parseJsonAndRestoreBufferRefs, stringifyJsonWithBufferRefs } from '../../../services/extensions/common/rpcProtocol.js';1213export function SingleProxyRPCProtocol(thing: any): IExtHostContext & IExtHostRpcService {14return {15_serviceBrand: undefined,16remoteAuthority: null!,17getProxy<T>(): T {18return thing;19},20set<T, R extends T>(identifier: ProxyIdentifier<T>, value: R): R {21return value;22},23dispose: undefined!,24assertRegistered: undefined!,25drain: undefined!,26extensionHostKind: ExtensionHostKind.LocalProcess27};28}2930/** Makes a fake {@link SingleProxyRPCProtocol} on which any method can be called */31export function AnyCallRPCProtocol<T>(useCalls?: { [K in keyof T]: T[K] }) {32return SingleProxyRPCProtocol(new Proxy({}, {33get(_target, prop: string) {34if (useCalls && prop in useCalls) {35// eslint-disable-next-line local/code-no-any-casts36return (useCalls as any)[prop];37}38return () => Promise.resolve(undefined);39}40}));41}4243export class TestRPCProtocol implements IExtHostContext, IExtHostRpcService {4445public _serviceBrand: undefined;46public remoteAuthority = null!;47public extensionHostKind = ExtensionHostKind.LocalProcess;4849private _callCountValue: number = 0;50private _idle?: Promise<any>;51private _completeIdle?: Function;5253private readonly _locals: { [id: string]: any };54private readonly _proxies: { [id: string]: any };5556constructor() {57this._locals = Object.create(null);58this._proxies = Object.create(null);59}6061drain(): Promise<void> {62return Promise.resolve();63}6465private get _callCount(): number {66return this._callCountValue;67}6869private set _callCount(value: number) {70this._callCountValue = value;71if (this._callCountValue === 0) {72this._completeIdle?.();73this._idle = undefined;74}75}7677sync(): Promise<any> {78return new Promise<any>((c) => {79setTimeout(c, 0);80}).then(() => {81if (this._callCount === 0) {82return undefined;83}84if (!this._idle) {85this._idle = new Promise<any>((c, e) => {86this._completeIdle = c;87});88}89return this._idle;90});91}9293public getProxy<T>(identifier: ProxyIdentifier<T>): Proxied<T> {94if (!this._proxies[identifier.sid]) {95this._proxies[identifier.sid] = this._createProxy(identifier.sid);96}97return this._proxies[identifier.sid];98}99100private _createProxy<T>(proxyId: string): T {101const handler = {102get: (target: any, name: PropertyKey) => {103if (typeof name === 'string' && !target[name] && name.charCodeAt(0) === CharCode.DollarSign) {104target[name] = (...myArgs: any[]) => {105return this._remoteCall(proxyId, name, myArgs);106};107}108109return target[name];110}111};112return new Proxy(Object.create(null), handler);113}114115public set<T, R extends T>(identifier: ProxyIdentifier<T>, value: R): R {116this._locals[identifier.sid] = value;117return value;118}119120protected _remoteCall(proxyId: string, path: string, args: any[]): Promise<any> {121this._callCount++;122123return new Promise<any>((c) => {124setTimeout(c, 0);125}).then(() => {126const instance = this._locals[proxyId];127// pretend the args went over the wire... (invoke .toJSON on objects...)128const wireArgs = simulateWireTransfer(args);129let p: Promise<any>;130try {131const result = (<Function>instance[path]).apply(instance, wireArgs);132p = isThenable(result) ? result : Promise.resolve(result);133} catch (err) {134p = Promise.reject(err);135}136137return p.then(result => {138this._callCount--;139// pretend the result went over the wire... (invoke .toJSON on objects...)140const wireResult = simulateWireTransfer(result);141return wireResult;142}, err => {143this._callCount--;144return Promise.reject(err);145});146});147}148149public dispose() { }150151public assertRegistered(identifiers: ProxyIdentifier<any>[]): void {152throw new Error('Not implemented!');153}154}155156function simulateWireTransfer<T>(obj: T): T {157if (!obj) {158return obj;159}160161if (Array.isArray(obj)) {162// eslint-disable-next-line local/code-no-any-casts163return obj.map(simulateWireTransfer) as any;164}165166if (obj instanceof SerializableObjectWithBuffers) {167const { jsonString, referencedBuffers } = stringifyJsonWithBufferRefs(obj);168return parseJsonAndRestoreBufferRefs(jsonString, referencedBuffers, null);169} else {170return JSON.parse(JSON.stringify(obj));171}172}173174175