Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/api/test/common/testRPCProtocol.ts
5242 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { isThenable } from '../../../../base/common/async.js';
7
import { CharCode } from '../../../../base/common/charCode.js';
8
import { IExtHostRpcService } from '../../common/extHostRpcService.js';
9
import { IExtHostContext } from '../../../services/extensions/common/extHostCustomers.js';
10
import { ExtensionHostKind } from '../../../services/extensions/common/extensionHostKind.js';
11
import { Proxied, ProxyIdentifier, SerializableObjectWithBuffers } from '../../../services/extensions/common/proxyIdentifier.js';
12
import { parseJsonAndRestoreBufferRefs, stringifyJsonWithBufferRefs } from '../../../services/extensions/common/rpcProtocol.js';
13
14
export function SingleProxyRPCProtocol(thing: any): IExtHostContext & IExtHostRpcService {
15
return {
16
_serviceBrand: undefined,
17
remoteAuthority: null!,
18
getProxy<T>(): T {
19
return thing;
20
},
21
set<T, R extends T>(identifier: ProxyIdentifier<T>, value: R): R {
22
return value;
23
},
24
dispose: undefined!,
25
assertRegistered: undefined!,
26
drain: undefined!,
27
extensionHostKind: ExtensionHostKind.LocalProcess
28
};
29
}
30
31
/** Makes a fake {@link SingleProxyRPCProtocol} on which any method can be called */
32
export function AnyCallRPCProtocol<T>(useCalls?: { [K in keyof T]: T[K] }) {
33
return SingleProxyRPCProtocol(new Proxy({}, {
34
get(_target, prop: string) {
35
if (useCalls && prop in useCalls) {
36
// eslint-disable-next-line local/code-no-any-casts
37
return (useCalls as any)[prop];
38
}
39
return () => Promise.resolve(undefined);
40
}
41
}));
42
}
43
44
export class TestRPCProtocol implements IExtHostContext, IExtHostRpcService {
45
46
public _serviceBrand: undefined;
47
public remoteAuthority = null!;
48
public extensionHostKind = ExtensionHostKind.LocalProcess;
49
50
private _callCountValue: number = 0;
51
private _idle?: Promise<any>;
52
private _completeIdle?: Function;
53
54
private readonly _locals: { [id: string]: any };
55
private readonly _proxies: { [id: string]: any };
56
57
constructor() {
58
this._locals = Object.create(null);
59
this._proxies = Object.create(null);
60
}
61
62
drain(): Promise<void> {
63
return Promise.resolve();
64
}
65
66
private get _callCount(): number {
67
return this._callCountValue;
68
}
69
70
private set _callCount(value: number) {
71
this._callCountValue = value;
72
if (this._callCountValue === 0) {
73
this._completeIdle?.();
74
this._idle = undefined;
75
}
76
}
77
78
sync(): Promise<any> {
79
return new Promise<any>((c) => {
80
setTimeout(c, 0);
81
}).then(() => {
82
if (this._callCount === 0) {
83
return undefined;
84
}
85
if (!this._idle) {
86
this._idle = new Promise<any>((c, e) => {
87
this._completeIdle = c;
88
});
89
}
90
return this._idle;
91
});
92
}
93
94
public getProxy<T>(identifier: ProxyIdentifier<T>): Proxied<T> {
95
if (!this._proxies[identifier.sid]) {
96
this._proxies[identifier.sid] = this._createProxy(identifier.sid);
97
}
98
return this._proxies[identifier.sid];
99
}
100
101
private _createProxy<T>(proxyId: string): T {
102
const handler = {
103
get: (target: any, name: PropertyKey) => {
104
if (typeof name === 'string' && !target[name] && name.charCodeAt(0) === CharCode.DollarSign) {
105
target[name] = (...myArgs: any[]) => {
106
return this._remoteCall(proxyId, name, myArgs);
107
};
108
}
109
110
return target[name];
111
}
112
};
113
return new Proxy(Object.create(null), handler);
114
}
115
116
public set<T, R extends T>(identifier: ProxyIdentifier<T>, value: R): R {
117
this._locals[identifier.sid] = value;
118
return value;
119
}
120
121
protected _remoteCall(proxyId: string, path: string, args: any[]): Promise<any> {
122
this._callCount++;
123
124
return new Promise<any>((c) => {
125
setTimeout(c, 0);
126
}).then(() => {
127
const instance = this._locals[proxyId];
128
// pretend the args went over the wire... (invoke .toJSON on objects...)
129
const wireArgs = simulateWireTransfer(args);
130
let p: Promise<any>;
131
try {
132
const result = (<Function>instance[path]).apply(instance, wireArgs);
133
p = isThenable(result) ? result : Promise.resolve(result);
134
} catch (err) {
135
p = Promise.reject(err);
136
}
137
138
return p.then(result => {
139
this._callCount--;
140
// pretend the result went over the wire... (invoke .toJSON on objects...)
141
const wireResult = simulateWireTransfer(result);
142
return wireResult;
143
}, err => {
144
this._callCount--;
145
return Promise.reject(err);
146
});
147
});
148
}
149
150
public dispose() { }
151
152
public assertRegistered(identifiers: ProxyIdentifier<any>[]): void {
153
throw new Error('Not implemented!');
154
}
155
}
156
157
function simulateWireTransfer<T>(obj: T): T {
158
if (!obj) {
159
return obj;
160
}
161
162
if (Array.isArray(obj)) {
163
// eslint-disable-next-line local/code-no-any-casts
164
return obj.map(simulateWireTransfer) as any;
165
}
166
167
if (obj instanceof SerializableObjectWithBuffers) {
168
const { jsonString, referencedBuffers } = stringifyJsonWithBufferRefs(obj);
169
return parseJsonAndRestoreBufferRefs(jsonString, referencedBuffers, null);
170
} else {
171
return JSON.parse(JSON.stringify(obj));
172
}
173
}
174
175