Send all extension descriptions to all extension hosts (#145307)

This commit is contained in:
Alex Dima
2022-04-20 15:06:31 +03:00
parent 02a92ee13f
commit a045f3f48a
17 changed files with 311 additions and 132 deletions

View File

@@ -26,6 +26,7 @@ import { VSBuffer } from 'vs/base/common/buffer';
import { IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { URI, UriComponents } from 'vs/base/common/uri';
import { FileAccess } from 'vs/base/common/network';
import { IExtensionDescriptionDelta } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
@extHostNamedCustomer(MainContext.MainThreadExtensionService)
export class MainThreadExtensionService implements MainThreadExtensionServiceShape {
@@ -204,8 +205,8 @@ class ExtensionHostProxy implements IExtensionHostProxy {
const uriComponents = await this._actual.$getCanonicalURI(remoteAuthority, uri);
return (uriComponents ? URI.revive(uriComponents) : uriComponents);
}
startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void> {
return this._actual.$startExtensionHost(enabledExtensionIds);
startExtensionHost(extensionsDelta: IExtensionDescriptionDelta): Promise<void> {
return this._actual.$startExtensionHost(extensionsDelta);
}
extensionTestsExecute(): Promise<number> {
return this._actual.$extensionTestsExecute();
@@ -225,8 +226,8 @@ class ExtensionHostProxy implements IExtensionHostProxy {
updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise<void> {
return this._actual.$updateRemoteConnectionData(connectionData);
}
deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void> {
return this._actual.$deltaExtensions(toAdd, toRemove);
deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise<void> {
return this._actual.$deltaExtensions(extensionsDelta);
}
test_latency(n: number): Promise<number> {
return this._actual.$test_latency(n);

View File

@@ -62,7 +62,7 @@ import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescrip
import { TypeHierarchyItem } from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy';
import { AuthenticationProviderInformation, AuthenticationSession, AuthenticationSessionsChangeEvent } from 'vs/workbench/services/authentication/common/authentication';
import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn';
import { IStaticWorkspaceData } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
import { IExtensionDescriptionDelta, IStaticWorkspaceData } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
import { IResolveAuthorityResult } from 'vs/workbench/services/extensions/common/extensionHostProxy';
import { ActivationKind, ExtensionActivationReason, MissingExtensionDependency } from 'vs/workbench/services/extensions/common/extensions';
import { createProxyIdentifier, Dto, IRPCProtocol, SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier';
@@ -1434,7 +1434,7 @@ export interface ExtHostExtensionServiceShape {
* Returns `null` if no resolver for `remoteAuthority` is found.
*/
$getCanonicalURI(remoteAuthority: string, uri: UriComponents): Promise<UriComponents | null>;
$startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void>;
$startExtensionHost(extensionsDelta: IExtensionDescriptionDelta): Promise<void>;
$extensionTestsExecute(): Promise<number>;
$extensionTestsExit(code: number): Promise<void>;
$activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void>;
@@ -1442,7 +1442,7 @@ export interface ExtHostExtensionServiceShape {
$setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void>;
$updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise<void>;
$deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void>;
$deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise<void>;
$test_latency(n: number): Promise<number>;
$test_up(b: VSBuffer): Promise<number>;

View File

@@ -13,12 +13,12 @@ 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 } from 'vs/workbench/api/common/extHost.protocol';
import { IExtensionHostInitData } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
import { IExtensionDescriptionDelta, 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';
import { ExtHostStorage, IExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { MissingExtensionDependency, ActivationKind, checkProposedApiEnabled, isProposedApiEnabled, ExtensionActivationReason } from 'vs/workbench/services/extensions/common/extensions';
import { MissingExtensionDependency, ActivationKind, checkProposedApiEnabled, isProposedApiEnabled, ExtensionActivationReason, extensionIdentifiersArrayToSet } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import * as errors from 'vs/base/common/errors';
import type * as vscode from 'vscode';
@@ -100,7 +100,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
private readonly _readyToRunExtensions: Barrier;
private readonly _eagerExtensionsActivated: Barrier;
protected readonly _registry: ExtensionDescriptionRegistry;
protected readonly _myRegistry: ExtensionDescriptionRegistry;
protected readonly _globalRegistry: ExtensionDescriptionRegistry;
private readonly _storage: ExtHostStorage;
private readonly _secretState: ExtHostSecretState;
private readonly _storagePath: IExtensionStoragePaths;
@@ -144,7 +145,11 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
this._readyToStartExtensionHost = new Barrier();
this._readyToRunExtensions = new Barrier();
this._eagerExtensionsActivated = new Barrier();
this._registry = new ExtensionDescriptionRegistry(this._initData.extensions);
this._globalRegistry = new ExtensionDescriptionRegistry(this._initData.allExtensions);
const myExtensionsSet = extensionIdentifiersArrayToSet(this._initData.myExtensions);
this._myRegistry = new ExtensionDescriptionRegistry(
filterExtensions(this._globalRegistry, myExtensionsSet)
);
this._storage = new ExtHostStorage(this._extHostContext);
this._secretState = new ExtHostSecretState(this._extHostContext);
this._storagePath = storagePath;
@@ -154,24 +159,33 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
[IExtHostSecretState, this._secretState]
));
const hostExtensions = new Set<string>();
this._initData.hostExtensions.forEach((extensionId) => hostExtensions.add(ExtensionIdentifier.toKey(extensionId)));
let resolvedExtensions: ExtensionIdentifier[] = [];
let hostExtensions: ExtensionIdentifier[] = [];
if (this._initData.remote.isRemote) {
resolvedExtensions = this._initData.allExtensions.filter(extension => !extension.main && !extension.browser).map(extension => extension.identifier);
hostExtensions = (
this._initData.allExtensions
.filter(extension => !myExtensionsSet.has(ExtensionIdentifier.toKey(extension.identifier.value)))
.filter(extension => (extension.main || extension.browser) && extension.api === 'none').map(extension => extension.identifier)
);
}
const hostExtensionsSet = extensionIdentifiersArrayToSet(hostExtensions);
this._activator = this._register(new ExtensionsActivator(
this._registry,
this._initData.resolvedExtensions,
this._initData.hostExtensions,
this._myRegistry,
resolvedExtensions,
hostExtensions,
{
onExtensionActivationError: (extensionId: ExtensionIdentifier, error: Error, missingExtensionDependency: MissingExtensionDependency | null): void => {
this._mainThreadExtensionsProxy.$onExtensionActivationError(extensionId, errors.transformErrorForSerialization(error), missingExtensionDependency);
},
actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<ActivatedExtension> => {
if (hostExtensions.has(ExtensionIdentifier.toKey(extensionId))) {
if (hostExtensionsSet.has(ExtensionIdentifier.toKey(extensionId))) {
await this._mainThreadExtensionsProxy.$activateExtension(extensionId, reason);
return new HostExtension();
}
const extensionDescription = this._registry.getExtensionDescription(extensionId)!;
const extensionDescription = this._myRegistry.getExtensionDescription(extensionId)!;
return this._activateExtension(extensionDescription, reason);
}
},
@@ -210,7 +224,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
let allPromises: Promise<void>[] = [];
try {
const allExtensions = this._registry.getAllExtensionDescriptions();
const allExtensions = this._myRegistry.getAllExtensionDescriptions();
const allExtensionsIds = allExtensions.map(ext => ext.identifier);
const activatedExtensions = allExtensionsIds.filter(id => this.isActivated(id));
@@ -293,7 +307,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
}
public getExtensionRegistry(): Promise<ExtensionDescriptionRegistry> {
return this._readyToRunExtensions.wait().then(_ => this._registry);
return this._readyToRunExtensions.wait().then(_ => this._myRegistry);
}
public getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined {
@@ -326,7 +340,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
return extUriBiasedIgnorePathCase.ignorePathCasing(key);
});
// const tst = TernarySearchTree.forUris<IExtensionDescription>(key => true);
for (const ext of this._registry.getAllExtensionDescriptions()) {
for (const ext of this._myRegistry.getAllExtensionDescriptions()) {
if (this._getEntryPoint(ext)) {
const uri = await this._realPathExtensionUri(ext.extensionLocation);
tst.set(uri, ext);
@@ -572,7 +586,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
// startup is considered finished
this._mainThreadExtensionsProxy.$setPerformanceMarks(performance.getMarks());
for (const desc of this._registry.getAllExtensionDescriptions()) {
for (const desc of this._myRegistry.getAllExtensionDescriptions()) {
if (desc.activationEvents) {
for (const activationEvent of desc.activationEvents) {
if (activationEvent === 'onStartupFinished') {
@@ -607,7 +621,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
}
return Promise.all(
this._registry.getAllExtensionDescriptions().map((desc) => {
this._myRegistry.getAllExtensionDescriptions().map((desc) => {
return this._handleWorkspaceContainsEagerExtension(folders, desc);
})
).then(() => { });
@@ -816,8 +830,19 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
return result;
}
public $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void> {
this._registry.keepOnly(enabledExtensionIds);
public $startExtensionHost(extensionsDelta: IExtensionDescriptionDelta): Promise<void> {
extensionsDelta.toAdd.forEach((extension) => (<any>extension).extensionLocation = URI.revive(extension.extensionLocation));
this._globalRegistry.deltaExtensions(extensionsDelta.toAdd, extensionsDelta.toRemove);
const myExtensions = extensionIdentifiersArrayToSet(this._myRegistry.getAllExtensionDescriptions().map(extension => extension.identifier));
for (const extensionId of extensionsDelta.myToRemove) {
myExtensions.delete(ExtensionIdentifier.toKey(extensionId));
}
for (const extensionId of extensionsDelta.myToAdd) {
myExtensions.add(ExtensionIdentifier.toKey(extensionId));
}
this._myRegistry.set(filterExtensions(this._globalRegistry, myExtensions));
return this._startExtensionHost();
}
@@ -834,7 +859,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
public async $activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean> {
await this._readyToRunExtensions.wait();
if (!this._registry.getExtensionDescription(extensionId)) {
if (!this._myRegistry.getExtensionDescription(extensionId)) {
// unknown extension => ignore
return false;
}
@@ -842,24 +867,36 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
return true;
}
public async $deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void> {
toAdd.forEach((extension) => (<any>extension).extensionLocation = URI.revive(extension.extensionLocation));
public async $deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise<void> {
extensionsDelta.toAdd.forEach((extension) => (<any>extension).extensionLocation = URI.revive(extension.extensionLocation));
const trie = await this.getExtensionPathIndex();
this._globalRegistry.deltaExtensions(extensionsDelta.toAdd, extensionsDelta.toRemove);
await Promise.all(toRemove.map(async (extensionId) => {
const extensionDescription = this._registry.getExtensionDescription(extensionId);
if (extensionDescription) {
trie.delete(await this._realPathExtensionUri(extensionDescription.extensionLocation));
}
}));
const myExtensions = extensionIdentifiersArrayToSet(this._myRegistry.getAllExtensionDescriptions().map(extension => extension.identifier));
for (const extensionId of extensionsDelta.myToRemove) {
myExtensions.delete(ExtensionIdentifier.toKey(extensionId));
}
for (const extensionId of extensionsDelta.myToAdd) {
myExtensions.add(ExtensionIdentifier.toKey(extensionId));
}
this._myRegistry.set(filterExtensions(this._globalRegistry, myExtensions));
await Promise.all(toAdd.map(async (extensionDescription) => {
const realpathUri = await this._realPathExtensionUri(extensionDescription.extensionLocation);
trie.set(realpathUri, extensionDescription);
}));
console.log(`TODO: update the extension path trie!`);
// const trie = await this.getExtensionPathIndex();
// await Promise.all(toRemove.map(async (extensionId) => {
// const extensionDescription = this._myRegistry.getExtensionDescription(extensionId);
// if (extensionDescription) {
// trie.delete(await this._realPathExtensionUri(extensionDescription.extensionLocation));
// }
// }));
// await Promise.all(toAdd.map(async (extensionDescription) => {
// const realpathUri = await this._realPathExtensionUri(extensionDescription.extensionLocation);
// trie.set(realpathUri, extensionDescription);
// }));
this._registry.deltaExtensions(toAdd, toRemove);
return Promise.resolve(undefined);
}
@@ -976,6 +1013,12 @@ export class Extension<T extends object | null | undefined> implements vscode.Ex
}
}
function filterExtensions(globalRegistry: ExtensionDescriptionRegistry, desiredExtensions: Set<string>): IExtensionDescription[] {
return globalRegistry.getAllExtensionDescriptions().filter(
extension => desiredExtensions.has(ExtensionIdentifier.toKey(extension.identifier))
);
}
function getRemoteAuthorityPrefix(remoteAuthority: string): string {
const plusIndex = remoteAuthority.indexOf('+');
if (plusIndex === -1) {

View File

@@ -120,7 +120,7 @@ export class ExtensionHostMain {
}
private static _transform(initData: IExtensionHostInitData, rpcProtocol: RPCProtocol): IExtensionHostInitData {
initData.extensions.forEach((ext) => (<any>ext).extensionLocation = URI.revive(rpcProtocol.transformIncomingURIs(ext.extensionLocation)));
initData.allExtensions.forEach((ext) => (<any>ext).extensionLocation = URI.revive(rpcProtocol.transformIncomingURIs(ext.extensionLocation)));
initData.environment.appRoot = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appRoot));
const extDevLocs = initData.environment.extensionDevelopmentLocationURI;
if (extDevLocs) {

View File

@@ -72,7 +72,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
}
// Module loading tricks
const interceptor = this._instaService.createInstance(NodeModuleRequireInterceptor, extensionApiFactory, this._registry);
const interceptor = this._instaService.createInstance(NodeModuleRequireInterceptor, extensionApiFactory, this._myRegistry);
await interceptor.install();
performance.mark('code/extHost/didInitAPI');

View File

@@ -44,7 +44,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
// initialize API and register actors
const apiFactory = this._instaService.invokeFunction(createApiFactoryAndRegisterActors);
this._fakeModules = this._instaService.createInstance(WorkerRequireInterceptor, apiFactory, this._registry);
this._fakeModules = this._instaService.createInstance(WorkerRequireInterceptor, apiFactory, this._myRegistry);
await this._fakeModules.install();
performance.mark('code/extHost/didInitAPI');