Path: blob/main/src/vs/workbench/api/test/common/testRPCProtocol.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 { 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) {35return (useCalls as any)[prop];36}37return () => Promise.resolve(undefined);38}39}));40}4142export class TestRPCProtocol implements IExtHostContext, IExtHostRpcService {4344public _serviceBrand: undefined;45public remoteAuthority = null!;46public extensionHostKind = ExtensionHostKind.LocalProcess;4748private _callCountValue: number = 0;49private _idle?: Promise<any>;50private _completeIdle?: Function;5152private readonly _locals: { [id: string]: any };53private readonly _proxies: { [id: string]: any };5455constructor() {56this._locals = Object.create(null);57this._proxies = Object.create(null);58}5960drain(): Promise<void> {61return Promise.resolve();62}6364private get _callCount(): number {65return this._callCountValue;66}6768private set _callCount(value: number) {69this._callCountValue = value;70if (this._callCountValue === 0) {71this._completeIdle?.();72this._idle = undefined;73}74}7576sync(): Promise<any> {77return new Promise<any>((c) => {78setTimeout(c, 0);79}).then(() => {80if (this._callCount === 0) {81return undefined;82}83if (!this._idle) {84this._idle = new Promise<any>((c, e) => {85this._completeIdle = c;86});87}88return this._idle;89});90}9192public getProxy<T>(identifier: ProxyIdentifier<T>): Proxied<T> {93if (!this._proxies[identifier.sid]) {94this._proxies[identifier.sid] = this._createProxy(identifier.sid);95}96return this._proxies[identifier.sid];97}9899private _createProxy<T>(proxyId: string): T {100const handler = {101get: (target: any, name: PropertyKey) => {102if (typeof name === 'string' && !target[name] && name.charCodeAt(0) === CharCode.DollarSign) {103target[name] = (...myArgs: any[]) => {104return this._remoteCall(proxyId, name, myArgs);105};106}107108return target[name];109}110};111return new Proxy(Object.create(null), handler);112}113114public set<T, R extends T>(identifier: ProxyIdentifier<T>, value: R): R {115this._locals[identifier.sid] = value;116return value;117}118119protected _remoteCall(proxyId: string, path: string, args: any[]): Promise<any> {120this._callCount++;121122return new Promise<any>((c) => {123setTimeout(c, 0);124}).then(() => {125const instance = this._locals[proxyId];126// pretend the args went over the wire... (invoke .toJSON on objects...)127const wireArgs = simulateWireTransfer(args);128let p: Promise<any>;129try {130const result = (<Function>instance[path]).apply(instance, wireArgs);131p = isThenable(result) ? result : Promise.resolve(result);132} catch (err) {133p = Promise.reject(err);134}135136return p.then(result => {137this._callCount--;138// pretend the result went over the wire... (invoke .toJSON on objects...)139const wireResult = simulateWireTransfer(result);140return wireResult;141}, err => {142this._callCount--;143return Promise.reject(err);144});145});146}147148public dispose() { }149150public assertRegistered(identifiers: ProxyIdentifier<any>[]): void {151throw new Error('Not implemented!');152}153}154155function simulateWireTransfer<T>(obj: T): T {156if (!obj) {157return obj;158}159160if (Array.isArray(obj)) {161return obj.map(simulateWireTransfer) as any;162}163164if (obj instanceof SerializableObjectWithBuffers) {165const { jsonString, referencedBuffers } = stringifyJsonWithBufferRefs(obj);166return parseJsonAndRestoreBufferRefs(jsonString, referencedBuffers, null);167} else {168return JSON.parse(JSON.stringify(obj));169}170}171172173