diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts new file mode 100644 index 00000000000..f75da5a79a9 --- /dev/null +++ b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as vscode from 'vscode'; + +suite('vscode', function () { + + test('rpc protocol, proxies not reachable', function () { + + const symProxy = Symbol.for('rpcProxy'); + const symProtocol = Symbol.for('rpcProtocol'); + + const proxyPaths: string[] = []; + const rpcPaths: string[] = []; + + function walk(obj: any, path: string, seen: Set) { + if (!obj) { + return; + } + if (typeof obj !== 'object' && typeof obj !== 'function') { + return; + } + if (seen.has(obj)) { + return; + } + seen.add(obj); + + if (obj[symProtocol]) { + rpcPaths.push(`PROTOCOL via ${path}`); + } + if (obj[symProxy]) { + proxyPaths.push(`PROXY '${obj[symProxy]}' via ${path}`); + } + + for (const key in obj) { + walk(obj[key], `${path}.${String(key)}`, seen); + } + } + + try { + walk(vscode, 'vscode', new Set()); + } catch (err) { + assert.fail(err); + } + assert.strictEqual(rpcPaths.length, 0); + + // assert.strictEqual(proxyPaths.length, 0); // proxies are accessible... + }); + +}); diff --git a/src/vs/workbench/services/extensions/common/rpcProtocol.ts b/src/vs/workbench/services/extensions/common/rpcProtocol.ts index 199ea6e15ef..a9fef74ecec 100644 --- a/src/vs/workbench/services/extensions/common/rpcProtocol.ts +++ b/src/vs/workbench/services/extensions/common/rpcProtocol.ts @@ -60,8 +60,13 @@ export interface IRPCProtocolLogger { const noop = () => { }; +const _RPCProtocolSymbol = Symbol.for('rpcProtocol'); +const _RPCProxySymbol = Symbol.for('rpcProxy'); + export class RPCProtocol extends Disposable implements IRPCProtocol { + [_RPCProtocolSymbol] = true; + private static readonly UNRESPONSIVE_TIME = 3 * 1000; // 3s private readonly _onDidChangeResponsiveState: Emitter = this._register(new Emitter()); @@ -182,14 +187,14 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { } public getProxy(identifier: ProxyIdentifier): T { - const rpcId = identifier.nid; + const { nid: rpcId, sid } = identifier; if (!this._proxies[rpcId]) { - this._proxies[rpcId] = this._createProxy(rpcId); + this._proxies[rpcId] = this._createProxy(rpcId, sid); } return this._proxies[rpcId]; } - private _createProxy(rpcId: number): T { + private _createProxy(rpcId: number, debugName: string): T { let handler = { get: (target: any, name: PropertyKey) => { if (typeof name === 'string' && !target[name] && name.charCodeAt(0) === CharCode.DollarSign) { @@ -197,6 +202,9 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { return this._remoteCall(rpcId, name, myArgs); }; } + if (name === _RPCProxySymbol) { + return debugName; + } return target[name]; } };