未验证 提交 acfadb48 编写于 作者: A Alexandru Dima 提交者: GitHub

Merge pull request #57144 from Microsoft/alex/binary-rpc

Convert renderer <-> ext host communication to a binary format
......@@ -164,7 +164,7 @@ export class Protocol implements IDisposable, IMessagePassingProtocol {
}
send(buffer: Buffer): void {
const header = Buffer.alloc(Protocol._headerLen);
const header = Buffer.allocUnsafe(Protocol._headerLen);
header.writeUInt32BE(buffer.length, 0, true);
this._writeSoon(header, buffer);
}
......
......@@ -99,15 +99,13 @@ class RemoteFileSystemProvider implements IFileSystemProvider {
}
readFile(resource: URI): TPromise<Uint8Array> {
return this._proxy.$readFile(this._handle, resource).then(encoded => {
return Buffer.from(encoded, 'base64');
});
return this._proxy.$readFile(this._handle, resource);
}
writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): TPromise<void> {
let encoded = Buffer.isBuffer(content)
? content.toString('base64')
: Buffer.from(content.buffer, content.byteOffset, content.byteLength).toString('base64');
? content
: Buffer.from(content.buffer, content.byteOffset, content.byteLength);
return this._proxy.$writeFile(this._handle, resource, encoded, opts);
}
......
......@@ -680,8 +680,8 @@ export interface ExtHostWorkspaceShape {
export interface ExtHostFileSystemShape {
$stat(handle: number, resource: UriComponents): TPromise<IStat>;
$readdir(handle: number, resource: UriComponents): TPromise<[string, FileType][]>;
$readFile(handle: number, resource: UriComponents): TPromise<string>;
$writeFile(handle: number, resource: UriComponents, base64Encoded: string, opts: FileWriteOptions): TPromise<void>;
$readFile(handle: number, resource: UriComponents): TPromise<Buffer>;
$writeFile(handle: number, resource: UriComponents, content: Buffer, opts: FileWriteOptions): TPromise<void>;
$rename(handle: number, resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): TPromise<void>;
$copy(handle: number, resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): TPromise<void>;
$mkdir(handle: number, resource: UriComponents): TPromise<void>;
......
......@@ -159,16 +159,16 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
return asWinJsPromise(() => this._fsProvider.get(handle).readDirectory(URI.revive(resource)));
}
$readFile(handle: number, resource: UriComponents): TPromise<string> {
$readFile(handle: number, resource: UriComponents): TPromise<Buffer> {
return asWinJsPromise(() => {
return this._fsProvider.get(handle).readFile(URI.revive(resource));
}).then(data => {
return Buffer.isBuffer(data) ? data.toString('base64') : Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString('base64');
return Buffer.isBuffer(data) ? data : Buffer.from(data.buffer, data.byteOffset, data.byteLength);
});
}
$writeFile(handle: number, resource: UriComponents, base64Content: string, opts: files.FileWriteOptions): TPromise<void> {
return asWinJsPromise(() => this._fsProvider.get(handle).writeFile(URI.revive(resource), Buffer.from(base64Content, 'base64'), opts));
$writeFile(handle: number, resource: UriComponents, content: Buffer, opts: files.FileWriteOptions): TPromise<void> {
return asWinJsPromise(() => this._fsProvider.get(handle).writeFile(URI.revive(resource), content, opts));
}
$delete(handle: number, resource: UriComponents, opts: files.FileDeleteOptions): TPromise<void> {
......
......@@ -39,7 +39,7 @@ import { Event, Emitter } from 'vs/base/common/event';
import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler';
import product from 'vs/platform/node/product';
import * as strings from 'vs/base/common/strings';
import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol';
import { RPCProtocol, IRPCProtocolLogger } from 'vs/workbench/services/extensions/node/rpcProtocol';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { Schemas } from 'vs/base/common/network';
......@@ -180,11 +180,12 @@ export class ExtensionHostProcessManager extends Disposable {
private _createExtensionHostCustomers(protocol: IMessagePassingProtocol): ExtHostExtensionServiceShape {
let logger: IRPCProtocolLogger = null;
if (logExtensionHostCommunication || this._environmentService.logExtensionHostCommunication) {
protocol = asLoggingProtocol(protocol);
logger = new RPCLogger();
}
this._extensionHostProcessRPCProtocol = new RPCProtocol(protocol);
this._extensionHostProcessRPCProtocol = new RPCProtocol(protocol, logger);
const extHostContext: IExtHostContext = {
getProxy: <T>(identifier: ProxyIdentifier<T>): T => this._extensionHostProcessRPCProtocol.getProxy(identifier),
set: <T, R extends T>(identifier: ProxyIdentifier<T>, instance: R): R => this._extensionHostProcessRPCProtocol.set(identifier, instance),
......@@ -954,20 +955,20 @@ export class ExtensionService extends Disposable implements IExtensionService {
}
}
function asLoggingProtocol(protocol: IMessagePassingProtocol): IMessagePassingProtocol {
class RPCLogger implements IRPCProtocolLogger {
protocol.onMessage(msg => {
console.log('%c[Extension \u2192 Window]%c[len: ' + strings.pad(msg.length, 5, ' ') + ']', 'color: darkgreen', 'color: grey', msg);
});
private _totalIncoming = 0;
private _totalOutgoing = 0;
return {
onMessage: protocol.onMessage,
logIncoming(msgLength: number, str: string, data?: any): void {
this._totalIncoming += msgLength;
console.log(`%c[Extension \u2192 Window]%c[${strings.pad(this._totalIncoming, 7, ' ')}]%c[len: ${strings.pad(msgLength, 5, ' ')}]`, 'color: darkgreen', 'color: grey', 'color: grey', str, data);
}
send(msg: any) {
protocol.send(msg);
console.log('%c[Window \u2192 Extension]%c[len: ' + strings.pad(msg.length, 5, ' ') + ']', 'color: darkgreen', 'color: grey', msg);
}
};
logOutgoing(msgLength: number, str: string, data?: any): void {
this._totalOutgoing += msgLength;
console.log(`%c[Window \u2192 Extension]%c[${strings.pad(this._totalOutgoing, 7, ' ')}]%c[len: ${strings.pad(msgLength, 5, ' ')}]`, 'color: darkgreen', 'color: grey', 'color: grey', str, data);
}
}
interface IExtensionCacheData {
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol';
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc';
import { Event, Emitter } from 'vs/base/common/event';
import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import { TPromise } from 'vs/base/common/winjs.base';
suite('RPCProtocol', () => {
class MessagePassingProtocol implements IMessagePassingProtocol {
private _pair: MessagePassingProtocol;
private readonly _onMessage: Emitter<Buffer> = new Emitter<Buffer>();
public readonly onMessage: Event<Buffer> = this._onMessage.event;
public setPair(other: MessagePassingProtocol) {
this._pair = other;
}
public send(buffer: Buffer): void {
process.nextTick(() => {
this._pair._onMessage.fire(buffer);
});
}
}
let delegate: (a1: any, a2: any) => any;
let bProxy: BClass;
class BClass {
$m(a1: any, a2: any): TPromise<any> {
return TPromise.as(delegate.call(null, a1, a2));
}
}
setup(() => {
let a_protocol = new MessagePassingProtocol();
let b_protocol = new MessagePassingProtocol();
a_protocol.setPair(b_protocol);
b_protocol.setPair(a_protocol);
let A = new RPCProtocol(a_protocol);
let B = new RPCProtocol(b_protocol);
delegate = null;
const bIdentifier = new ProxyIdentifier<BClass>(false, 'bb');
const bInstance = new BClass();
B.set(bIdentifier, bInstance);
bProxy = A.getProxy(bIdentifier);
});
test('simple call', function (done) {
delegate = (a1: number, a2: number) => a1 + a2;
bProxy.$m(4, 1).then((res: number) => {
assert.equal(res, 5);
done(null);
}, done);
});
test('simple call without result', function (done) {
delegate = (a1: number, a2: number) => { };
bProxy.$m(4, 1).then((res: number) => {
assert.equal(res, undefined);
done(null);
}, done);
});
test('passing buffer as argument', function (done) {
delegate = (a1: Buffer, a2: number) => {
assert.ok(Buffer.isBuffer(a1));
return a1[a2];
};
let b = Buffer.allocUnsafe(4);
b[0] = 1;
b[1] = 2;
b[2] = 3;
b[3] = 4;
bProxy.$m(b, 2).then((res: number) => {
assert.equal(res, 3);
done(null);
}, done);
});
test('returning a buffer', function (done) {
delegate = (a1: number, a2: number) => {
let b = Buffer.allocUnsafe(4);
b[0] = 1;
b[1] = 2;
b[2] = 3;
b[3] = 4;
return b;
};
bProxy.$m(4, 1).then((res: Buffer) => {
assert.ok(Buffer.isBuffer(res));
assert.equal(res[0], 1);
assert.equal(res[1], 2);
assert.equal(res[2], 3);
assert.equal(res[3], 4);
done(null);
}, done);
});
test('cancelling a call', function () {
delegate = (a1: number, a2: number) => a1 + a2;
let p = bProxy.$m(4, 1);
p.then((res: number) => {
assert.fail('should not receive result');
});
p.cancel();
});
test('throwing an error', function (done) {
delegate = (a1: number, a2: number) => {
throw new Error(`nope`);
};
bProxy.$m(4, 1).then((res) => {
assert.fail('unexpected');
done(null);
}, (err) => {
assert.equal(err.message, 'nope');
done(null);
});
});
test('error promise', function (done) {
delegate = (a1: number, a2: number) => {
return TPromise.wrapError(undefined);
};
bProxy.$m(4, 1).then((res) => {
assert.fail('unexpected');
done(null);
}, (err) => {
assert.equal(err, undefined);
done(null);
});
});
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册