diff --git a/src/vs/workbench/api/browser/mainThreadExtensionService.ts b/src/vs/workbench/api/browser/mainThreadExtensionService.ts index e0ed0c4fdb6..efe0a38d024 100644 --- a/src/vs/workbench/api/browser/mainThreadExtensionService.ts +++ b/src/vs/workbench/api/browser/mainThreadExtensionService.ts @@ -5,9 +5,9 @@ import { SerializedError } from 'vs/base/common/errors'; import Severity from 'vs/base/common/severity'; -import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; -import { MainContext, MainThreadExtensionServiceShape } from 'vs/workbench/api/common/extHost.protocol'; -import { IExtensionService, ExtensionHostKind, MissingExtensionDependency, ExtensionActivationReason } from 'vs/workbench/services/extensions/common/extensions'; +import { extHostNamedCustomer, IExtHostContext, IInternalExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; +import { ExtHostContext, ExtHostExtensionServiceShape, MainContext, MainThreadExtensionServiceShape } from 'vs/workbench/api/common/extHost.protocol'; +import { IExtensionService, ExtensionHostKind, MissingExtensionDependency, ExtensionActivationReason, ActivationKind, IInternalExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { localize } from 'vs/nls'; @@ -21,11 +21,16 @@ import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensio import { ITimerService } from 'vs/workbench/services/timer/browser/timerService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IExtensionHostProxy, IResolveAuthorityResult } from 'vs/workbench/services/extensions/common/extensionHostProxy'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { URI } from 'vs/base/common/uri'; @extHostNamedCustomer(MainContext.MainThreadExtensionService) export class MainThreadExtensionService implements MainThreadExtensionServiceShape { private readonly _extensionHostKind: ExtensionHostKind; + private readonly _internalExtensionService: IInternalExtensionService; constructor( extHostContext: IExtHostContext, @@ -39,26 +44,30 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha @IWorkbenchEnvironmentService protected readonly _environmentService: IWorkbenchEnvironmentService, ) { this._extensionHostKind = extHostContext.extensionHostKind; + this._internalExtensionService = (extHostContext).internalExtensionService; + (extHostContext)._setExtensionHostProxy( + new ExtensionHostProxy(extHostContext.getProxy(ExtHostContext.ExtHostExtensionService)) + ); } public dispose(): void { } $activateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { - return this._extensionService._activateById(extensionId, reason); + return this._internalExtensionService._activateById(extensionId, reason); } async $onWillActivateExtension(extensionId: ExtensionIdentifier): Promise { - this._extensionService._onWillActivateExtension(extensionId); + this._internalExtensionService._onWillActivateExtension(extensionId); } $onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void { - this._extensionService._onDidActivateExtension(extensionId, codeLoadingTime, activateCallTime, activateResolvedTime, activationReason); + this._internalExtensionService._onDidActivateExtension(extensionId, codeLoadingTime, activateCallTime, activateResolvedTime, activationReason); } $onExtensionRuntimeError(extensionId: ExtensionIdentifier, data: SerializedError): void { const error = new Error(); error.name = data.name; error.message = data.message; error.stack = data.stack; - this._extensionService._onExtensionRuntimeError(extensionId, error); + this._internalExtensionService._onExtensionRuntimeError(extensionId, error); console.error(`[${extensionId}]${error.message}`); console.error(error.stack); } @@ -68,7 +77,7 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha error.message = data.message; error.stack = data.stack; - this._extensionService._onDidActivateExtensionError(extensionId, error); + this._internalExtensionService._onDidActivateExtensionError(extensionId, error); if (missingExtensionDependency) { const extension = await this._extensionService.getExtension(extensionId.value); @@ -171,3 +180,50 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha } } } + +class ExtensionHostProxy implements IExtensionHostProxy { + constructor( + private readonly _actual: ExtHostExtensionServiceShape + ) { } + + resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise { + return this._actual.$resolveAuthority(remoteAuthority, resolveAttempt); + } + async getCanonicalURI(remoteAuthority: string, uri: URI): Promise { + const uriComponents = await this._actual.$getCanonicalURI(remoteAuthority, uri); + return URI.revive(uriComponents); + } + startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise { + return this._actual.$startExtensionHost(enabledExtensionIds); + } + extensionTestsExecute(): Promise { + return this._actual.$extensionTestsExecute(); + } + extensionTestsExit(code: number): Promise { + return this._actual.$extensionTestsExit(code); + } + activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise { + return this._actual.$activateByEvent(activationEvent, activationKind); + } + activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { + return this._actual.$activate(extensionId, reason); + } + setRemoteEnvironment(env: { [key: string]: string | null; }): Promise { + return this._actual.$setRemoteEnvironment(env); + } + updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise { + return this._actual.$updateRemoteConnectionData(connectionData); + } + deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise { + return this._actual.$deltaExtensions(toAdd, toRemove); + } + test_latency(n: number): Promise { + return this._actual.$test_latency(n); + } + test_up(b: VSBuffer): Promise { + return this._actual.$test_up(b); + } + test_down(size: number): Promise { + return this._actual.$test_down(size); + } +} diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 417f5169191..c3df8c3e569 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -34,7 +34,7 @@ import { ILoggerOptions, LogLevel } from 'vs/platform/log/common/log'; import { IMarkerData } from 'vs/platform/markers/common/markers'; import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress'; import * as quickInput from 'vs/platform/quickinput/common/quickInput'; -import { IRemoteConnectionData, RemoteAuthorityResolverErrorCode, ResolverResult, TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { IRemoteConnectionData, TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ProvidedPortAttributes, TunnelCreationOptions, TunnelOptions, TunnelProviderFeatures } from 'vs/platform/tunnel/common/tunnel'; import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings'; import { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; @@ -70,6 +70,7 @@ import * as search from 'vs/workbench/services/search/common/search'; import { IWorkspaceSymbol } from 'vs/workbench/contrib/search/common/search'; import { ILineChange } from 'vs/editor/common/diff/diffComputer'; import { IStaticWorkspaceData } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; +import { IResolveAuthorityResult } from 'vs/workbench/services/extensions/common/extensionHostProxy'; export interface IWorkspaceData extends IStaticWorkspaceData { folders: { uri: UriComponents, name: string, index: number; }[]; @@ -1314,22 +1315,6 @@ export interface ExtHostSearchShape { $clearCache(cacheKey: string): Promise; } -export interface IResolveAuthorityErrorResult { - type: 'error'; - error: { - message: string | undefined; - code: RemoteAuthorityResolverErrorCode; - detail: any; - }; -} - -export interface IResolveAuthorityOKResult { - type: 'ok'; - value: ResolverResult; -} - -export type IResolveAuthorityResult = IResolveAuthorityErrorResult | IResolveAuthorityOKResult; - export interface ExtHostExtensionServiceShape { $resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise; $getCanonicalURI(remoteAuthority: string, uri: UriComponents): Promise; diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index dc3170c4d9d..28b4f7759ba 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -12,7 +12,7 @@ import { dispose, toDisposable, Disposable } from 'vs/base/common/lifecycle'; import { TernarySearchTree } from 'vs/base/common/map'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; -import { ExtHostExtensionServiceShape, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape, IResolveAuthorityResult } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostExtensionServiceShape, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape } from 'vs/workbench/api/common/extHost.protocol'; import { IExtensionHostInitData } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { ExtHostConfiguration, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; import { ActivatedExtension, EmptyExtension, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from 'vs/workbench/api/common/extHostExtensionActivator'; @@ -39,6 +39,7 @@ import { IExtensionActivationHost, checkActivateWorkspaceContainsExtension } fro import { ExtHostSecretState, IExtHostSecretState } from 'vs/workbench/api/common/exHostSecretState'; import { ExtensionSecrets } from 'vs/workbench/api/common/extHostSecrets'; import { Schemas } from 'vs/base/common/network'; +import { IResolveAuthorityResult } from 'vs/workbench/services/extensions/common/extensionHostProxy'; interface ITestRunner { /** Old test runner API, as exported from `vscode/lib/testrunner` */ diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index 891812f5a8c..032faf19ecc 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -15,7 +15,7 @@ import { IWebExtensionsScannerService, IWorkbenchExtensionEnablementService } fr import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension, IExtensionHost, ActivationKind, ExtensionHostKind, toExtensionDescription, ExtensionRunningLocation, extensionHostKindToString, ExtensionActivationReason } from 'vs/workbench/services/extensions/common/extensions'; +import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension, IExtensionHost, ActivationKind, ExtensionHostKind, toExtensionDescription, ExtensionRunningLocation, extensionHostKindToString, ExtensionActivationReason, IInternalExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; @@ -622,7 +622,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx private _startExtensionHosts(isInitialStart: boolean, initialActivationEvents: string[]): void { const extensionHosts = this._createExtensionHosts(isInitialStart); extensionHosts.forEach((extensionHost) => { - const processManager: IExtensionHostManager = createExtensionHostManager(this._instantiationService, extensionHost, isInitialStart, initialActivationEvents); + const processManager: IExtensionHostManager = createExtensionHostManager(this._instantiationService, extensionHost, isInitialStart, initialActivationEvents, this._acquireInternalAPI()); processManager.onDidExit(([code, signal]) => this._onExtensionHostCrashOrExit(processManager, code, signal)); processManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ isResponsive: responsiveState === ResponsiveState.Responsive }); }); this._extensionHostManagers.push(processManager); @@ -950,6 +950,26 @@ export abstract class AbstractExtensionService extends Disposable implements IEx } } + private _acquireInternalAPI(): IInternalExtensionService { + return { + _activateById: (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise => { + return this._activateById(extensionId, reason); + }, + _onWillActivateExtension: (extensionId: ExtensionIdentifier): void => { + return this._onWillActivateExtension(extensionId); + }, + _onDidActivateExtension: (extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void => { + return this._onDidActivateExtension(extensionId, codeLoadingTime, activateCallTime, activateResolvedTime, activationReason); + }, + _onDidActivateExtensionError: (extensionId: ExtensionIdentifier, error: Error): void => { + return this._onDidActivateExtensionError(extensionId, error); + }, + _onExtensionRuntimeError: (extensionId: ExtensionIdentifier, err: Error): void => { + return this._onExtensionRuntimeError(extensionId, err); + } + }; + } + public async _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { const results = await Promise.all( this._extensionHostManagers.map(manager => manager.activate(extensionId, reason)) @@ -960,16 +980,16 @@ export abstract class AbstractExtensionService extends Disposable implements IEx } } - public _onWillActivateExtension(extensionId: ExtensionIdentifier): void { + private _onWillActivateExtension(extensionId: ExtensionIdentifier): void { this._extensionHostActiveExtensions.set(ExtensionIdentifier.toKey(extensionId), extensionId); } - public _onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void { + private _onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void { this._extensionHostActivationTimes.set(ExtensionIdentifier.toKey(extensionId), new ActivationTimes(codeLoadingTime, activateCallTime, activateResolvedTime, activationReason)); this._onDidChangeExtensionsStatus.fire([extensionId]); } - public _onDidActivateExtensionError(extensionId: ExtensionIdentifier, error: Error): void { + private _onDidActivateExtensionError(extensionId: ExtensionIdentifier, error: Error): void { type ExtensionActivationErrorClassification = { extensionId: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; error: { classification: 'CallstackOrException', purpose: 'PerformanceAndHealth' }; @@ -984,7 +1004,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx }); } - public _onExtensionRuntimeError(extensionId: ExtensionIdentifier, err: Error): void { + private _onExtensionRuntimeError(extensionId: ExtensionIdentifier, err: Error): void { const extensionKey = ExtensionIdentifier.toKey(extensionId); if (!this._extensionHostExtensionRuntimeErrors.has(extensionKey)) { this._extensionHostExtensionRuntimeErrors.set(extensionKey, []); diff --git a/src/vs/workbench/services/extensions/common/extHostCustomers.ts b/src/vs/workbench/services/extensions/common/extHostCustomers.ts index 3b33d6712be..d33ba1a9ce8 100644 --- a/src/vs/workbench/services/extensions/common/extHostCustomers.ts +++ b/src/vs/workbench/services/extensions/common/extHostCustomers.ts @@ -5,7 +5,8 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { IConstructorSignature1, BrandedService } from 'vs/platform/instantiation/common/instantiation'; -import { ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionHostProxy } from 'vs/workbench/services/extensions/common/extensionHostProxy'; +import { ExtensionHostKind, IInternalExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IRPCProtocol, ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier'; export interface IExtHostContext extends IRPCProtocol { @@ -13,6 +14,11 @@ export interface IExtHostContext extends IRPCProtocol { readonly extensionHostKind: ExtensionHostKind; } +export interface IInternalExtHostContext extends IExtHostContext { + readonly internalExtensionService: IInternalExtensionService; + _setExtensionHostProxy(extensionHostProxy: IExtensionHostProxy): void; +} + export type IExtHostNamedCustomer = [ProxyIdentifier, IExtHostCustomerCtor]; export type IExtHostCustomerCtor = IConstructorSignature1; diff --git a/src/vs/workbench/services/extensions/common/extensionHostManager.ts b/src/vs/workbench/services/extensions/common/extensionHostManager.ts index 4ee7edef347..8e1664e9c99 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostManager.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostManager.ts @@ -9,8 +9,8 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { ExtHostCustomersRegistry, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; -import { ExtHostContext, ExtHostExtensionServiceShape, MainContext } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostCustomersRegistry, IInternalExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; +import { MainContext } from 'vs/workbench/api/common/extHost.protocol'; import { Proxied, ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import { IRPCProtocolLogger, RPCProtocol, RequestInitiator, ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; import { RemoteAuthorityResolverError, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver'; @@ -20,12 +20,13 @@ import { registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { StopWatch } from 'vs/base/common/stopwatch'; import { VSBuffer } from 'vs/base/common/buffer'; -import { IExtensionHost, ExtensionHostKind, ActivationKind, extensionHostKindToString, ExtensionActivationReason } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionHost, ExtensionHostKind, ActivationKind, extensionHostKindToString, ExtensionActivationReason, IInternalExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { CATEGORIES } from 'vs/workbench/common/actions'; import { Barrier, timeout } from 'vs/base/common/async'; import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IExtensionHostProxy } from 'vs/workbench/services/extensions/common/extensionHostProxy'; // Enable to see detailed message communication between window and extension host const LOG_EXTENSION_HOST_COMMUNICATION = false; @@ -49,11 +50,11 @@ export interface IExtensionHostManager { setRemoteEnvironment(env: { [key: string]: string | null }): Promise; } -export function createExtensionHostManager(instantiationService: IInstantiationService, extensionHost: IExtensionHost, isInitialStart: boolean, initialActivationEvents: string[]): IExtensionHostManager { +export function createExtensionHostManager(instantiationService: IInstantiationService, extensionHost: IExtensionHost, isInitialStart: boolean, initialActivationEvents: string[], internalExtensionService: IInternalExtensionService): IExtensionHostManager { if (extensionHost.lazyStart && isInitialStart && initialActivationEvents.length === 0) { - return instantiationService.createInstance(LazyStartExtensionHostManager, extensionHost); + return instantiationService.createInstance(LazyStartExtensionHostManager, extensionHost, internalExtensionService); } - return instantiationService.createInstance(ExtensionHostManager, extensionHost, initialActivationEvents); + return instantiationService.createInstance(ExtensionHostManager, extensionHost, initialActivationEvents, internalExtensionService); } export type ExtensionHostStartupClassification = { @@ -89,16 +90,14 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { private _rpcProtocol: RPCProtocol | null; private readonly _customers: IDisposable[]; private readonly _extensionHost: IExtensionHost; - /** - * winjs believes a proxy is a promise because it has a `then` method, so wrap the result in an object. - */ - private _proxy: Promise<{ value: ExtHostExtensionServiceShape; } | null> | null; + private _proxy: Promise | null; private _resolveAuthorityAttempt: number; private _hasStarted = false; constructor( extensionHost: IExtensionHost, initialActivationEvents: string[], + private readonly _internalExtensionService: IInternalExtensionService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @@ -132,7 +131,7 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { }; this._telemetryService.publicLog2('extensionHostStartup', successTelemetryEvent); - return { value: this._createExtensionHostCustomers(protocol) }; + return this._createExtensionHostCustomers(protocol); }, (err) => { this._logService.error(`Error received from starting extension host (kind: ${extensionHostKindToString(this.kind)})`); @@ -204,28 +203,21 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { }; } - private async _getProxy(): Promise { - if (!this._proxy) { - return null; - } - const p = await this._proxy; - if (!p) { - return null; - } - return p.value; + private async _getProxy(): Promise { + return this._proxy; } public async ready(): Promise { await this._getProxy(); } - private async _measureLatency(proxy: ExtHostExtensionServiceShape): Promise { + private async _measureLatency(proxy: IExtensionHostProxy): Promise { const COUNT = 10; let sum = 0; for (let i = 0; i < COUNT; i++) { const sw = StopWatch.create(true); - await proxy.$test_latency(i); + await proxy.test_latency(i); sw.stop(); sum += sw.elapsed(); } @@ -236,7 +228,7 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { return (byteCount * 1000 * 8) / elapsedMillis; } - private async _measureUp(proxy: ExtHostExtensionServiceShape): Promise { + private async _measureUp(proxy: IExtensionHostProxy): Promise { const SIZE = 10 * 1024 * 1024; // 10MB let buff = VSBuffer.alloc(SIZE); @@ -245,21 +237,21 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { buff.writeUInt8(i, value); } const sw = StopWatch.create(true); - await proxy.$test_up(buff); + await proxy.test_up(buff); sw.stop(); return ExtensionHostManager._convert(SIZE, sw.elapsed()); } - private async _measureDown(proxy: ExtHostExtensionServiceShape): Promise { + private async _measureDown(proxy: IExtensionHostProxy): Promise { const SIZE = 10 * 1024 * 1024; // 10MB const sw = StopWatch.create(true); - await proxy.$test_down(SIZE); + await proxy.test_down(SIZE); sw.stop(); return ExtensionHostManager._convert(SIZE, sw.elapsed()); } - private _createExtensionHostCustomers(protocol: IMessagePassingProtocol): ExtHostExtensionServiceShape { + private _createExtensionHostCustomers(protocol: IMessagePassingProtocol): IExtensionHostProxy { let logger: IRPCProtocolLogger | null = null; if (LOG_EXTENSION_HOST_COMMUNICATION || this._environmentService.logExtensionHostCommunication) { @@ -268,13 +260,21 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { this._rpcProtocol = new RPCProtocol(protocol, logger); this._register(this._rpcProtocol.onDidChangeResponsiveState((responsiveState: ResponsiveState) => this._onDidChangeResponsiveState.fire(responsiveState))); - const extHostContext: IExtHostContext = { + let extensionHostProxy: IExtensionHostProxy | null = null as IExtensionHostProxy | null; + const extHostContext: IInternalExtHostContext = { remoteAuthority: this._extensionHost.remoteAuthority, extensionHostKind: this.kind, getProxy: (identifier: ProxyIdentifier): Proxied => this._rpcProtocol!.getProxy(identifier), set: (identifier: ProxyIdentifier, instance: R): R => this._rpcProtocol!.set(identifier, instance), assertRegistered: (identifiers: ProxyIdentifier[]): void => this._rpcProtocol!.assertRegistered(identifiers), drain: (): Promise => this._rpcProtocol!.drain(), + + //#region internal + internalExtensionService: this._internalExtensionService, + _setExtensionHostProxy: (value: IExtensionHostProxy): void => { + extensionHostProxy = value; + }, + //#endregion }; // Named customers @@ -293,11 +293,15 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { this._customers.push(instance); } + if (!extensionHostProxy) { + throw new Error(`Missing IExtensionHostProxy!`); + } + // Check that no named customers are missing const expected: ProxyIdentifier[] = Object.keys(MainContext).map((key) => (MainContext)[key]); this._rpcProtocol.assertRegistered(expected); - return this._rpcProtocol.getProxy(ExtHostContext.ExtHostExtensionService); + return extensionHostProxy; } public async activate(extension: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { @@ -305,7 +309,7 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { if (!proxy) { return false; } - return proxy.$activate(extension, reason); + return proxy.activate(extension, reason); } public activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise { @@ -329,7 +333,7 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { // i.e. the extension host could not be started return; } - return proxy.value.$activateByEvent(activationEvent, activationKind); + return proxy.activateByEvent(activationEvent, activationKind); } public async getInspectPort(tryEnableInspector: boolean): Promise { @@ -364,7 +368,7 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { throw new Error(`Cannot resolve authority`); } this._resolveAuthorityAttempt++; - const result = await proxy.$resolveAuthority(remoteAuthority, this._resolveAuthorityAttempt); + const result = await proxy.resolveAuthority(remoteAuthority, this._resolveAuthorityAttempt); if (result.type === 'ok') { return result.value; } else { @@ -382,7 +386,7 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { if (!proxy) { throw new Error(`Cannot resolve canonical URI`); } - const result = await proxy.$getCanonicalURI(remoteAuthority, uri); + const result = await proxy.getCanonicalURI(remoteAuthority, uri); return URI.revive(result); } @@ -391,7 +395,7 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { if (!proxy) { return; } - return proxy.$startExtensionHost(enabledExtensionIds); + return proxy.startExtensionHost(enabledExtensionIds); } public async extensionTestsExecute(): Promise { @@ -399,7 +403,7 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { if (!proxy) { throw new Error('Could not obtain Extension Host Proxy'); } - return proxy.$extensionTestsExecute(); + return proxy.extensionTestsExecute(); } public async extensionTestsSendExit(exitCode: number): Promise { @@ -410,7 +414,7 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { // This method does not wait for the actual RPC to be confirmed // It waits for the socket to drain (i.e. the message has been sent) // It also times out after 5s in case drain takes too long - proxy.$extensionTestsExit(exitCode); + proxy.extensionTestsExit(exitCode); if (this._rpcProtocol) { await Promise.race([this._rpcProtocol.drain(), timeout(5000)]); } @@ -421,7 +425,7 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { if (!proxy) { return; } - return proxy.$deltaExtensions(toAdd, toRemove); + return proxy.deltaExtensions(toAdd, toRemove); } public async setRemoteEnvironment(env: { [key: string]: string | null }): Promise { @@ -430,7 +434,7 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { return; } - return proxy.$setRemoteEnvironment(env); + return proxy.setRemoteEnvironment(env); } } @@ -449,6 +453,7 @@ class LazyStartExtensionHostManager extends Disposable implements IExtensionHost constructor( extensionHost: IExtensionHost, + private readonly _internalExtensionService: IInternalExtensionService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @ILogService private readonly _logService: ILogService, ) { @@ -462,7 +467,7 @@ class LazyStartExtensionHostManager extends Disposable implements IExtensionHost private _createActual(reason: string): ExtensionHostManager { this._logService.info(`Creating lazy extension host: ${reason}`); - this._actual = this._register(this._instantiationService.createInstance(ExtensionHostManager, this._extensionHost, [])); + this._actual = this._register(this._instantiationService.createInstance(ExtensionHostManager, this._extensionHost, [], this._internalExtensionService)); this._register(this._actual.onDidChangeResponsiveState((e) => this._onDidChangeResponsiveState.fire(e))); return this._actual; } diff --git a/src/vs/workbench/services/extensions/common/extensionHostProxy.ts b/src/vs/workbench/services/extensions/common/extensionHostProxy.ts new file mode 100644 index 00000000000..890168f78fe --- /dev/null +++ b/src/vs/workbench/services/extensions/common/extensionHostProxy.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { VSBuffer } from 'vs/base/common/buffer'; +import { URI } from 'vs/base/common/uri'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { IRemoteConnectionData, RemoteAuthorityResolverErrorCode, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { ActivationKind, ExtensionActivationReason } from 'vs/workbench/services/extensions/common/extensions'; + +export interface IResolveAuthorityErrorResult { + type: 'error'; + error: { + message: string | undefined; + code: RemoteAuthorityResolverErrorCode; + detail: any; + }; +} + +export interface IResolveAuthorityOKResult { + type: 'ok'; + value: ResolverResult; +} + +export type IResolveAuthorityResult = IResolveAuthorityErrorResult | IResolveAuthorityOKResult; + +export interface IExtensionHostProxy { + resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise; + getCanonicalURI(remoteAuthority: string, uri: URI): Promise; + startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise; + extensionTestsExecute(): Promise; + extensionTestsExit(code: number): Promise; + activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise; + activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise; + setRemoteEnvironment(env: { [key: string]: string | null; }): Promise; + updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise; + deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise; + test_latency(n: number): Promise; + test_up(b: VSBuffer): Promise; + test_down(size: number): Promise; +} diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 93cc0998303..b25e53ea171 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -312,25 +312,13 @@ export interface IExtensionService { * (This is public such that the extension host process can coordinate with and call back in the IExtensionService) */ _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise; - /** - * Please do not use! - * (This is public such that the extension host process can coordinate with and call back in the IExtensionService) - */ +} + +export interface IInternalExtensionService { + _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise; _onWillActivateExtension(extensionId: ExtensionIdentifier): void; - /** - * Please do not use! - * (This is public such that the extension host process can coordinate with and call back in the IExtensionService) - */ _onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void; - /** - * Please do not use! - * (This is public such that the extension host process can coordinate with and call back in the IExtensionService) - */ _onDidActivateExtensionError(extensionId: ExtensionIdentifier, error: Error): void; - /** - * Please do not use! - * (This is public such that the extension host process can coordinate with and call back in the IExtensionService) - */ _onExtensionRuntimeError(extensionId: ExtensionIdentifier, err: Error): void; } @@ -382,9 +370,4 @@ export class NullExtensionService implements IExtensionService { canAddExtension(): boolean { return false; } canRemoveExtension(): boolean { return false; } _activateById(_extensionId: ExtensionIdentifier, _reason: ExtensionActivationReason): Promise { return Promise.resolve(); } - _onWillActivateExtension(_extensionId: ExtensionIdentifier): void { } - _onDidActivateExtension(_extensionId: ExtensionIdentifier, _codeLoadingTime: number, _activateCallTime: number, _activateResolvedTime: number, _activationReason: ExtensionActivationReason): void { } - _onDidActivateExtensionError(_extensionId: ExtensionIdentifier, _error: Error): void { } - _onExtensionRuntimeError(_extensionId: ExtensionIdentifier, _err: Error): void { } - _onExtensionHostExit(code: number): void { } }