Path: blob/main/src/vs/workbench/api/common/extHostExtensionService.ts
5227 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45/* eslint-disable local/code-no-native-private */67import * as nls from '../../../nls.js';8import * as path from '../../../base/common/path.js';9import * as performance from '../../../base/common/performance.js';10import { originalFSPath, joinPath, extUriBiasedIgnorePathCase } from '../../../base/common/resources.js';11import { asPromise, Barrier, IntervalTimer, timeout } from '../../../base/common/async.js';12import { dispose, toDisposable, Disposable, DisposableStore, IDisposable } from '../../../base/common/lifecycle.js';13import { TernarySearchTree } from '../../../base/common/ternarySearchTree.js';14import { URI, UriComponents } from '../../../base/common/uri.js';15import { ILogService } from '../../../platform/log/common/log.js';16import { ExtHostExtensionServiceShape, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape } from './extHost.protocol.js';17import { IExtensionDescriptionDelta, IExtensionHostInitData } from '../../services/extensions/common/extensionHostProtocol.js';18import { ExtHostConfiguration, IExtHostConfiguration } from './extHostConfiguration.js';19import { ActivatedExtension, EmptyExtension, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from './extHostExtensionActivator.js';20import { ExtHostStorage, IExtHostStorage } from './extHostStorage.js';21import { ExtHostWorkspace, IExtHostWorkspace } from './extHostWorkspace.js';22import { MissingExtensionDependency, ActivationKind, checkProposedApiEnabled, isProposedApiEnabled, ExtensionActivationReason } from '../../services/extensions/common/extensions.js';23import { ExtensionDescriptionRegistry, IActivationEventsReader } from '../../services/extensions/common/extensionDescriptionRegistry.js';24import * as errors from '../../../base/common/errors.js';25import type * as vscode from 'vscode';26import { ExtensionIdentifier, ExtensionIdentifierMap, ExtensionIdentifierSet, IExtensionDescription } from '../../../platform/extensions/common/extensions.js';27import { VSBuffer } from '../../../base/common/buffer.js';28import { ExtensionGlobalMemento, ExtensionMemento } from './extHostMemento.js';29import { RemoteAuthorityResolverError, ExtensionKind, ExtensionMode, ExtensionRuntime, ManagedResolvedAuthority as ExtHostManagedResolvedAuthority } from './extHostTypes.js';30import { ResolvedAuthority, ResolvedOptions, RemoteAuthorityResolverErrorCode, IRemoteConnectionData, getRemoteAuthorityPrefix, TunnelInformation, ManagedRemoteConnection, WebSocketRemoteConnection } from '../../../platform/remote/common/remoteAuthorityResolver.js';31import { IInstantiationService, createDecorator } from '../../../platform/instantiation/common/instantiation.js';32import { IExtHostInitDataService } from './extHostInitDataService.js';33import { IExtensionStoragePaths } from './extHostStoragePaths.js';34import { IExtHostRpcService } from './extHostRpcService.js';35import { ServiceCollection } from '../../../platform/instantiation/common/serviceCollection.js';36import { IExtHostTunnelService } from './extHostTunnelService.js';37import { IExtHostTerminalService } from './extHostTerminalService.js';38import { IExtHostLanguageModels } from './extHostLanguageModels.js';39import { Emitter, Event } from '../../../base/common/event.js';40import { IExtensionActivationHost, checkActivateWorkspaceContainsExtension } from '../../services/extensions/common/workspaceContains.js';41import { ExtHostSecretState, IExtHostSecretState } from './extHostSecretState.js';42import { ExtensionSecrets } from './extHostSecrets.js';43import { Schemas } from '../../../base/common/network.js';44import { IResolveAuthorityResult } from '../../services/extensions/common/extensionHostProxy.js';45import { IExtHostLocalizationService } from './extHostLocalizationService.js';46import { StopWatch } from '../../../base/common/stopwatch.js';47import { isCI, setTimeout0 } from '../../../base/common/platform.js';48import { IExtHostManagedSockets } from './extHostManagedSockets.js';49import { Dto } from '../../services/extensions/common/proxyIdentifier.js';5051interface ITestRunner {52/** Old test runner API, as exported from `vscode/lib/testrunner` */53run(testsRoot: string, clb: (error: Error, failures?: number) => void): void;54}5556interface INewTestRunner {57/** New test runner API, as explained in the extension test doc */58run(): Promise<void>;59}6061export const IHostUtils = createDecorator<IHostUtils>('IHostUtils');6263export interface IHostUtils {64readonly _serviceBrand: undefined;65readonly pid: number | undefined;66exit(code: number): void;67fsExists?(path: string): Promise<boolean>;68fsRealpath?(path: string): Promise<string>;69}7071type TelemetryActivationEventFragment = {72id: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'The identifier of an extension' };73name: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'The name of the extension' };74extensionVersion: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'The version of the extension' };75publisherDisplayName: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The publisher of the extension' };76activationEvents: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'All activation events of the extension' };77isBuiltin: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'If the extension is builtin or git installed' };78reason: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The activation event' };79reasonId: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'The identifier of the activation event' };80};8182export abstract class AbstractExtHostExtensionService extends Disposable implements ExtHostExtensionServiceShape {8384readonly _serviceBrand: undefined;8586abstract readonly extensionRuntime: ExtensionRuntime;8788private readonly _onDidChangeRemoteConnectionData = this._register(new Emitter<void>());89public readonly onDidChangeRemoteConnectionData = this._onDidChangeRemoteConnectionData.event;9091protected readonly _hostUtils: IHostUtils;92protected readonly _initData: IExtensionHostInitData;93protected readonly _extHostContext: IExtHostRpcService;94protected readonly _instaService: IInstantiationService;95protected readonly _extHostWorkspace: ExtHostWorkspace;96protected readonly _extHostConfiguration: ExtHostConfiguration;97protected readonly _logService: ILogService;98protected readonly _extHostTunnelService: IExtHostTunnelService;99protected readonly _extHostTerminalService: IExtHostTerminalService;100protected readonly _extHostLocalizationService: IExtHostLocalizationService;101102protected readonly _mainThreadWorkspaceProxy: MainThreadWorkspaceShape;103protected readonly _mainThreadTelemetryProxy: MainThreadTelemetryShape;104protected readonly _mainThreadExtensionsProxy: MainThreadExtensionServiceShape;105106private readonly _almostReadyToRunExtensions: Barrier;107private readonly _readyToStartExtensionHost: Barrier;108private readonly _readyToRunExtensions: Barrier;109private readonly _eagerExtensionsActivated: Barrier;110111private readonly _activationEventsReader: SyncedActivationEventsReader;112protected readonly _myRegistry: ExtensionDescriptionRegistry;113protected readonly _globalRegistry: ExtensionDescriptionRegistry;114private readonly _storage: ExtHostStorage;115private readonly _secretState: ExtHostSecretState;116private readonly _storagePath: IExtensionStoragePaths;117private readonly _activator: ExtensionsActivator;118private _extensionPathIndex: Promise<ExtensionPaths> | null;119private _realPathCache = new Map<string, Promise<string>>();120121private readonly _resolvers: { [authorityPrefix: string]: vscode.RemoteAuthorityResolver };122123private _started: boolean;124private _isTerminating: boolean = false;125private _remoteConnectionData: IRemoteConnectionData | null;126127constructor(128@IInstantiationService instaService: IInstantiationService,129@IHostUtils hostUtils: IHostUtils,130@IExtHostRpcService extHostContext: IExtHostRpcService,131@IExtHostWorkspace extHostWorkspace: IExtHostWorkspace,132@IExtHostConfiguration extHostConfiguration: IExtHostConfiguration,133@ILogService logService: ILogService,134@IExtHostInitDataService initData: IExtHostInitDataService,135@IExtensionStoragePaths storagePath: IExtensionStoragePaths,136@IExtHostTunnelService extHostTunnelService: IExtHostTunnelService,137@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService,138@IExtHostLocalizationService extHostLocalizationService: IExtHostLocalizationService,139@IExtHostManagedSockets private readonly _extHostManagedSockets: IExtHostManagedSockets,140@IExtHostLanguageModels private readonly _extHostLanguageModels: IExtHostLanguageModels,141) {142super();143this._hostUtils = hostUtils;144this._extHostContext = extHostContext;145this._initData = initData;146147this._extHostWorkspace = extHostWorkspace;148this._extHostConfiguration = extHostConfiguration;149this._logService = logService;150this._extHostTunnelService = extHostTunnelService;151this._extHostTerminalService = extHostTerminalService;152this._extHostLocalizationService = extHostLocalizationService;153154this._mainThreadWorkspaceProxy = this._extHostContext.getProxy(MainContext.MainThreadWorkspace);155this._mainThreadTelemetryProxy = this._extHostContext.getProxy(MainContext.MainThreadTelemetry);156this._mainThreadExtensionsProxy = this._extHostContext.getProxy(MainContext.MainThreadExtensionService);157158this._almostReadyToRunExtensions = new Barrier();159this._readyToStartExtensionHost = new Barrier();160this._readyToRunExtensions = new Barrier();161this._eagerExtensionsActivated = new Barrier();162this._activationEventsReader = new SyncedActivationEventsReader(this._initData.extensions.activationEvents);163this._globalRegistry = new ExtensionDescriptionRegistry(this._activationEventsReader, this._initData.extensions.allExtensions);164const myExtensionsSet = new ExtensionIdentifierSet(this._initData.extensions.myExtensions);165this._myRegistry = new ExtensionDescriptionRegistry(166this._activationEventsReader,167filterExtensions(this._globalRegistry, myExtensionsSet)168);169170if (isCI) {171this._logService.info(`Creating extension host with the following global extensions: ${printExtIds(this._globalRegistry)}`);172this._logService.info(`Creating extension host with the following local extensions: ${printExtIds(this._myRegistry)}`);173}174175this._storage = new ExtHostStorage(this._extHostContext, this._logService);176this._secretState = new ExtHostSecretState(this._extHostContext);177this._storagePath = storagePath;178179this._instaService = this._store.add(instaService.createChild(new ServiceCollection(180[IExtHostStorage, this._storage],181[IExtHostSecretState, this._secretState]182)));183184this._activator = this._register(new ExtensionsActivator(185this._myRegistry,186this._globalRegistry,187{188onExtensionActivationError: (extensionId: ExtensionIdentifier, error: Error, missingExtensionDependency: MissingExtensionDependency | null): void => {189this._mainThreadExtensionsProxy.$onExtensionActivationError(extensionId, errors.transformErrorForSerialization(error), missingExtensionDependency);190},191192actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<ActivatedExtension> => {193if (ExtensionDescriptionRegistry.isHostExtension(extensionId, this._myRegistry, this._globalRegistry)) {194await this._mainThreadExtensionsProxy.$activateExtension(extensionId, reason);195return new HostExtension();196}197const extensionDescription = this._myRegistry.getExtensionDescription(extensionId)!;198return this._activateExtension(extensionDescription, reason);199}200},201this._logService202));203this._extensionPathIndex = null;204this._resolvers = Object.create(null);205this._started = false;206this._remoteConnectionData = this._initData.remote.connectionData;207}208209public getRemoteConnectionData(): IRemoteConnectionData | null {210return this._remoteConnectionData;211}212213public async initialize(): Promise<void> {214try {215216await this._beforeAlmostReadyToRunExtensions();217this._almostReadyToRunExtensions.open();218219await this._extHostWorkspace.waitForInitializeCall();220performance.mark('code/extHost/ready');221this._readyToStartExtensionHost.open();222223if (this._initData.autoStart) {224this._startExtensionHost();225}226} catch (err) {227errors.onUnexpectedError(err);228}229}230231private async _deactivateAll(): Promise<void> {232this._storagePath.onWillDeactivateAll();233234let allPromises: Promise<void>[] = [];235try {236const allExtensions = this._myRegistry.getAllExtensionDescriptions();237const allExtensionsIds = allExtensions.map(ext => ext.identifier);238const activatedExtensions = allExtensionsIds.filter(id => this.isActivated(id));239240allPromises = activatedExtensions.map((extensionId) => {241return this._deactivate(extensionId);242});243} catch (err) {244// TODO: write to log once we have one245}246await Promise.all(allPromises);247}248249public terminate(reason: string, code: number = 0): void {250if (this._isTerminating) {251// we are already shutting down...252return;253}254this._isTerminating = true;255this._logService.info(`Extension host terminating: ${reason}`);256this._logService.flush();257258this._extHostTerminalService.dispose();259this._activator.dispose();260261errors.setUnexpectedErrorHandler((err) => {262this._logService.error(err);263});264265// Invalidate all proxies266this._extHostContext.dispose();267268const extensionsDeactivated = this._deactivateAll();269270// Give extensions at most 5 seconds to wrap up any async deactivate, then exit271Promise.race([timeout(5000), extensionsDeactivated]).finally(() => {272if (this._hostUtils.pid) {273this._logService.info(`Extension host with pid ${this._hostUtils.pid} exiting with code ${code}`);274} else {275this._logService.info(`Extension host exiting with code ${code}`);276}277this._logService.flush();278this._logService.dispose();279this._hostUtils.exit(code);280});281}282283public isActivated(extensionId: ExtensionIdentifier): boolean {284if (this._readyToRunExtensions.isOpen()) {285return this._activator.isActivated(extensionId);286}287return false;288}289290public async getExtension(extensionId: string): Promise<IExtensionDescription | undefined> {291const ext = await this._mainThreadExtensionsProxy.$getExtension(extensionId);292return ext && {293...ext,294identifier: new ExtensionIdentifier(ext.identifier.value),295extensionLocation: URI.revive(ext.extensionLocation)296};297}298299private _activateByEvent(activationEvent: string, startup: boolean): Promise<void> {300return this._activator.activateByEvent(activationEvent, startup);301}302303private _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {304return this._activator.activateById(extensionId, reason);305}306307public activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {308return this._activateById(extensionId, reason).then(() => {309const extension = this._activator.getActivatedExtension(extensionId);310if (extension.activationFailed) {311// activation failed => bubble up the error as the promise result312return Promise.reject(extension.activationFailedError);313}314return undefined;315});316}317318public getExtensionRegistry(): Promise<ExtensionDescriptionRegistry> {319return this._readyToRunExtensions.wait().then(_ => this._myRegistry);320}321322public getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined {323if (this._readyToRunExtensions.isOpen()) {324return this._activator.getActivatedExtension(extensionId).exports;325} else {326try {327return this._activator.getActivatedExtension(extensionId).exports;328} catch (err) {329return null;330}331}332}333334/**335* Applies realpath to file-uris and returns all others uris unmodified.336* The real path is cached for the lifetime of the extension host.337*/338private async _realPathExtensionUri(uri: URI): Promise<URI> {339if (uri.scheme === Schemas.file && this._hostUtils.fsRealpath) {340const fsPath = uri.fsPath;341if (!this._realPathCache.has(fsPath)) {342this._realPathCache.set(fsPath, this._hostUtils.fsRealpath(fsPath));343}344const realpathValue = await this._realPathCache.get(fsPath)!;345return URI.file(realpathValue);346}347return uri;348}349350// create trie to enable fast 'filename -> extension id' look up351public async getExtensionPathIndex(): Promise<ExtensionPaths> {352if (!this._extensionPathIndex) {353this._extensionPathIndex = this._createExtensionPathIndex(this._myRegistry.getAllExtensionDescriptions()).then((searchTree) => {354return new ExtensionPaths(searchTree);355});356}357return this._extensionPathIndex;358}359360/**361* create trie to enable fast 'filename -> extension id' look up362*/363private async _createExtensionPathIndex(extensions: IExtensionDescription[]): Promise<TernarySearchTree<URI, IExtensionDescription>> {364const tst = TernarySearchTree.forUris<IExtensionDescription>(key => {365// using the default/biased extUri-util because the IExtHostFileSystemInfo-service366// isn't ready to be used yet, e.g the knowledge about `file` protocol and others367// comes in while this code runs368return extUriBiasedIgnorePathCase.ignorePathCasing(key);369});370// const tst = TernarySearchTree.forUris<IExtensionDescription>(key => true);371await Promise.all(extensions.map(async (ext) => {372if (this._getEntryPoint(ext)) {373const uri = await this._realPathExtensionUri(ext.extensionLocation);374tst.set(uri, ext);375}376}));377return tst;378}379380private _deactivate(extensionId: ExtensionIdentifier): Promise<void> {381let result = Promise.resolve(undefined);382383if (!this._readyToRunExtensions.isOpen()) {384return result;385}386387if (!this._activator.isActivated(extensionId)) {388return result;389}390391const extension = this._activator.getActivatedExtension(extensionId);392if (!extension) {393return result;394}395396// call deactivate if available397try {398if (typeof extension.module.deactivate === 'function') {399result = Promise.resolve(extension.module.deactivate()).then(undefined, (err) => {400this._logService.error(err);401return Promise.resolve(undefined);402});403}404} catch (err) {405this._logService.error(`An error occurred when deactivating the extension '${extensionId.value}':`);406this._logService.error(err);407}408409// clean up subscriptions410try {411extension.disposable.dispose();412} catch (err) {413this._logService.error(`An error occurred when disposing the subscriptions for extension '${extensionId.value}':`);414this._logService.error(err);415}416417return result;418}419420// --- impl421422private async _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {423if (!this._initData.remote.isRemote) {424// local extension host process425await this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.identifier);426} else {427// remote extension host process428// do not wait for renderer confirmation429this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.identifier);430}431return this._doActivateExtension(extensionDescription, reason).then((activatedExtension) => {432const activationTimes = activatedExtension.activationTimes;433this._mainThreadExtensionsProxy.$onDidActivateExtension(extensionDescription.identifier, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, reason);434this._logExtensionActivationTimes(extensionDescription, reason, 'success', activationTimes);435return activatedExtension;436}, (err) => {437this._logExtensionActivationTimes(extensionDescription, reason, 'failure');438throw err;439});440}441442private _logExtensionActivationTimes(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason, outcome: string, activationTimes?: ExtensionActivationTimes) {443const event = getTelemetryActivationEvent(extensionDescription, reason);444type ExtensionActivationTimesClassification = {445owner: 'jrieken';446comment: 'Timestamps for extension activation';447outcome: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Did extension activation succeed or fail' };448} & TelemetryActivationEventFragment & ExtensionActivationTimesFragment;449450type ExtensionActivationTimesEvent = {451outcome: string;452} & ActivationTimesEvent & TelemetryActivationEvent;453454type ActivationTimesEvent = {455startup?: boolean;456codeLoadingTime?: number;457activateCallTime?: number;458activateResolvedTime?: number;459};460461this._mainThreadTelemetryProxy.$publicLog2<ExtensionActivationTimesEvent, ExtensionActivationTimesClassification>('extensionActivationTimes', {462...event,463...(activationTimes || {}),464outcome465});466}467468private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {469const event = getTelemetryActivationEvent(extensionDescription, reason);470type ActivatePluginClassification = {471owner: 'jrieken';472comment: 'Data about how/why an extension was activated';473} & TelemetryActivationEventFragment;474this._mainThreadTelemetryProxy.$publicLog2<TelemetryActivationEvent, ActivatePluginClassification>('activatePlugin', event);475const entryPoint = this._getEntryPoint(extensionDescription);476if (!entryPoint) {477// Treat the extension as being empty => NOT AN ERROR CASE478return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE));479}480481this._logService.info(`ExtensionService#_doActivateExtension ${extensionDescription.identifier.value}, startup: ${reason.startup}, activationEvent: '${reason.activationEvent}'${extensionDescription.identifier.value !== reason.extensionId.value ? `, root cause: ${reason.extensionId.value}` : ``}`);482this._logService.flush();483484const isESM = this._isESM(extensionDescription);485486const extensionInternalStore = new DisposableStore(); // disposables that follow the extension lifecycle487const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup);488return Promise.all([489isESM490? this._loadESMModule<IExtensionModule>(extensionDescription, joinPath(extensionDescription.extensionLocation, entryPoint), activationTimesBuilder)491: this._loadCommonJSModule<IExtensionModule>(extensionDescription, joinPath(extensionDescription.extensionLocation, entryPoint), activationTimesBuilder),492this._loadExtensionContext(extensionDescription, extensionInternalStore)493]).then(values => {494performance.mark(`code/extHost/willActivateExtension/${extensionDescription.identifier.value}`);495return AbstractExtHostExtensionService._callActivate(this._logService, extensionDescription.identifier, values[0], values[1], extensionInternalStore, activationTimesBuilder);496}).then((activatedExtension) => {497performance.mark(`code/extHost/didActivateExtension/${extensionDescription.identifier.value}`);498return activatedExtension;499});500}501502private _loadExtensionContext(extensionDescription: IExtensionDescription, extensionInternalStore: DisposableStore): Promise<vscode.ExtensionContext> {503504const languageModelAccessInformation = this._extHostLanguageModels.createLanguageModelAccessInformation(extensionDescription);505const globalState = extensionInternalStore.add(new ExtensionGlobalMemento(extensionDescription, this._storage));506const workspaceState = extensionInternalStore.add(new ExtensionMemento(extensionDescription.identifier.value, false, this._storage));507const secrets = extensionInternalStore.add(new ExtensionSecrets(extensionDescription, this._secretState));508const extensionMode = extensionDescription.isUnderDevelopment509? (this._initData.environment.extensionTestsLocationURI ? ExtensionMode.Test : ExtensionMode.Development)510: ExtensionMode.Production;511const extensionKind = this._initData.remote.isRemote ? ExtensionKind.Workspace : ExtensionKind.UI;512513this._logService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`);514515return Promise.all([516globalState.whenReady,517workspaceState.whenReady,518this._storagePath.whenReady519]).then(() => {520const that = this;521let extension: vscode.Extension<any> | undefined;522523let messagePassingProtocol: vscode.MessagePassingProtocol | undefined;524const messagePort = isProposedApiEnabled(extensionDescription, 'ipc')525? this._initData.messagePorts?.get(ExtensionIdentifier.toKey(extensionDescription.identifier))526: undefined;527528return Object.freeze<vscode.ExtensionContext>({529globalState,530workspaceState,531secrets,532subscriptions: [],533get languageModelAccessInformation() { return languageModelAccessInformation; },534get extensionUri() { return extensionDescription.extensionLocation; },535get extensionPath() { return extensionDescription.extensionLocation.fsPath; },536asAbsolutePath(relativePath: string) { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); },537get storagePath() { return that._storagePath.workspaceValue(extensionDescription)?.fsPath; },538get globalStoragePath() { return that._storagePath.globalValue(extensionDescription).fsPath; },539get logPath() { return path.join(that._initData.logsLocation.fsPath, extensionDescription.identifier.value); },540get logUri() { return URI.joinPath(that._initData.logsLocation, extensionDescription.identifier.value); },541get storageUri() { return that._storagePath.workspaceValue(extensionDescription); },542get globalStorageUri() { return that._storagePath.globalValue(extensionDescription); },543get extensionMode() { return extensionMode; },544get extension() {545if (extension === undefined) {546extension = new Extension(that, extensionDescription.identifier, extensionDescription, extensionKind, false);547}548return extension;549},550get extensionRuntime() {551checkProposedApiEnabled(extensionDescription, 'extensionRuntime');552return that.extensionRuntime;553},554get environmentVariableCollection() { return that._extHostTerminalService.getEnvironmentVariableCollection(extensionDescription); },555get messagePassingProtocol() {556if (!messagePassingProtocol) {557if (!messagePort) {558return undefined;559}560561const onDidReceiveMessage = Event.buffer(Event.fromDOMEventEmitter(messagePort, 'message', e => e.data));562messagePort.start();563messagePassingProtocol = {564onDidReceiveMessage,565// eslint-disable-next-line local/code-no-any-casts566postMessage: messagePort.postMessage.bind(messagePort) as any567};568}569570return messagePassingProtocol;571}572});573});574}575576private static _callActivate(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: vscode.ExtensionContext, extensionInternalStore: IDisposable, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<ActivatedExtension> {577// Make sure the extension's surface is not undefined578extensionModule = extensionModule || {579activate: undefined,580deactivate: undefined581};582583return this._callActivateOptional(logService, extensionId, extensionModule, context, activationTimesBuilder).then((extensionExports) => {584return new ActivatedExtension(false, null, activationTimesBuilder.build(), extensionModule, extensionExports, toDisposable(() => {585extensionInternalStore.dispose();586dispose(context.subscriptions);587}));588});589}590591private static _callActivateOptional(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: vscode.ExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<IExtensionAPI> {592if (typeof extensionModule.activate === 'function') {593try {594activationTimesBuilder.activateCallStart();595logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`);596const activateResult: Promise<IExtensionAPI> = extensionModule.activate.apply(globalThis, [context]);597activationTimesBuilder.activateCallStop();598599activationTimesBuilder.activateResolveStart();600return Promise.resolve(activateResult).then((value) => {601activationTimesBuilder.activateResolveStop();602return value;603});604} catch (err) {605return Promise.reject(err);606}607} else {608// No activate found => the module is the extension's exports609return Promise.resolve<IExtensionAPI>(extensionModule);610}611}612613// -- eager activation614615private _activateOneStartupFinished(desc: IExtensionDescription, activationEvent: string): void {616this._activateById(desc.identifier, {617startup: false,618extensionId: desc.identifier,619activationEvent: activationEvent620}).then(undefined, (err) => {621this._logService.error(err);622});623}624625private _activateAllStartupFinishedDeferred(extensions: IExtensionDescription[], start: number = 0): void {626const timeBudget = 50; // 50 milliseconds627const startTime = Date.now();628629setTimeout0(() => {630for (let i = start; i < extensions.length; i += 1) {631const desc = extensions[i];632for (const activationEvent of (desc.activationEvents ?? [])) {633if (activationEvent === 'onStartupFinished') {634if (Date.now() - startTime > timeBudget) {635// time budget for current task has been exceeded636// set a new task to activate current and remaining extensions637this._activateAllStartupFinishedDeferred(extensions, i);638break;639} else {640this._activateOneStartupFinished(desc, activationEvent);641}642}643}644}645});646}647648private _activateAllStartupFinished(): void {649// startup is considered finished650this._mainThreadExtensionsProxy.$setPerformanceMarks(performance.getMarks());651652this._extHostConfiguration.getConfigProvider().then((configProvider) => {653const shouldDeferActivation = configProvider.getConfiguration('extensions.experimental').get<boolean>('deferredStartupFinishedActivation');654const allExtensionDescriptions = this._myRegistry.getAllExtensionDescriptions();655if (shouldDeferActivation) {656this._activateAllStartupFinishedDeferred(allExtensionDescriptions);657} else {658for (const desc of allExtensionDescriptions) {659if (desc.activationEvents) {660for (const activationEvent of desc.activationEvents) {661if (activationEvent === 'onStartupFinished') {662this._activateOneStartupFinished(desc, activationEvent);663}664}665}666}667}668});669}670671// Handle "eager" activation extensions672private _handleEagerExtensions(): Promise<void> {673const starActivation = this._activateByEvent('*', true).then(undefined, (err) => {674this._logService.error(err);675});676677this._register(this._extHostWorkspace.onDidChangeWorkspace((e) => this._handleWorkspaceContainsEagerExtensions(e.added)));678const folders = this._extHostWorkspace.workspace ? this._extHostWorkspace.workspace.folders : [];679const workspaceContainsActivation = this._handleWorkspaceContainsEagerExtensions(folders);680const remoteResolverActivation = this._handleRemoteResolverEagerExtensions();681const eagerExtensionsActivation = Promise.all([remoteResolverActivation, starActivation, workspaceContainsActivation]).then(() => { });682683Promise.race([eagerExtensionsActivation, timeout(10000)]).then(() => {684this._activateAllStartupFinished();685});686687return eagerExtensionsActivation;688}689690private _handleWorkspaceContainsEagerExtensions(folders: ReadonlyArray<vscode.WorkspaceFolder>): Promise<void> {691if (folders.length === 0) {692return Promise.resolve(undefined);693}694695return Promise.all(696this._myRegistry.getAllExtensionDescriptions().map((desc) => {697return this._handleWorkspaceContainsEagerExtension(folders, desc);698})699).then(() => { });700}701702private async _handleWorkspaceContainsEagerExtension(folders: ReadonlyArray<vscode.WorkspaceFolder>, desc: IExtensionDescription): Promise<void> {703if (this.isActivated(desc.identifier)) {704return;705}706707const localWithRemote = !this._initData.remote.isRemote && !!this._initData.remote.authority;708const host: IExtensionActivationHost = {709logService: this._logService,710folders: folders.map(folder => folder.uri),711forceUsingSearch: localWithRemote || !this._hostUtils.fsExists,712exists: (uri) => this._hostUtils.fsExists!(uri.fsPath),713checkExists: (folders, includes, token) => this._mainThreadWorkspaceProxy.$checkExists(folders, includes, token)714};715716const result = await checkActivateWorkspaceContainsExtension(host, desc);717if (!result) {718return;719}720721return (722this._activateById(desc.identifier, { startup: true, extensionId: desc.identifier, activationEvent: result.activationEvent })723.then(undefined, err => this._logService.error(err))724);725}726727private async _handleRemoteResolverEagerExtensions(): Promise<void> {728if (this._initData.remote.authority) {729return this._activateByEvent(`onResolveRemoteAuthority:${this._initData.remote.authority}`, false);730}731}732733public async $extensionTestsExecute(): Promise<number> {734await this._eagerExtensionsActivated.wait();735try {736return await this._doHandleExtensionTests();737} catch (error) {738console.error(error); // ensure any error message makes it onto the console739throw error;740}741}742743private async _doHandleExtensionTests(): Promise<number> {744const { extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment;745if (!extensionDevelopmentLocationURI || !extensionTestsLocationURI) {746throw new Error(nls.localize('extensionTestError1', "Cannot load test runner."));747}748749const extensionDescription = (await this.getExtensionPathIndex()).findSubstr(extensionTestsLocationURI);750const isESM = this._isESM(extensionDescription, extensionTestsLocationURI.path);751752// Require the test runner via node require from the provided path753const testRunner = await (isESM754? this._loadESMModule<ITestRunner | INewTestRunner | undefined>(null, extensionTestsLocationURI, new ExtensionActivationTimesBuilder(false))755: this._loadCommonJSModule<ITestRunner | INewTestRunner | undefined>(null, extensionTestsLocationURI, new ExtensionActivationTimesBuilder(false)));756757if (!testRunner || typeof testRunner.run !== 'function') {758throw new Error(nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsLocationURI.toString()));759}760761// Execute the runner if it follows the old `run` spec762return new Promise<number>((resolve, reject) => {763const oldTestRunnerCallback = (error: Error, failures: number | undefined) => {764if (error) {765if (isCI) {766this._logService.error(`Test runner called back with error`, error);767}768reject(error);769} else {770if (isCI) {771if (failures) {772this._logService.info(`Test runner called back with ${failures} failures.`);773} else {774this._logService.info(`Test runner called back with successful outcome.`);775}776}777resolve((typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);778}779};780781const extensionTestsPath = originalFSPath(extensionTestsLocationURI); // for the old test runner API782783const runResult = testRunner.run(extensionTestsPath, oldTestRunnerCallback);784785// Using the new API `run(): Promise<void>`786if (runResult && runResult.then) {787runResult788.then(() => {789if (isCI) {790this._logService.info(`Test runner finished successfully.`);791}792resolve(0);793})794.catch((err: unknown) => {795if (isCI) {796this._logService.error(`Test runner finished with error`, err);797}798reject(err instanceof Error && err.stack ? err.stack : String(err));799});800}801});802}803804private _startExtensionHost(): Promise<void> {805if (this._started) {806throw new Error(`Extension host is already started!`);807}808this._started = true;809810return this._readyToStartExtensionHost.wait()811.then(() => this._readyToRunExtensions.open())812.then(() => {813// wait for all activation events that came in during workbench startup, but at maximum 1s814return Promise.race([this._activator.waitForActivatingExtensions(), timeout(1000)]);815})816.then(() => this._handleEagerExtensions())817.then(() => {818this._eagerExtensionsActivated.open();819this._logService.info(`Eager extensions activated`);820});821}822823// -- called by extensions824825public registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable {826this._resolvers[authorityPrefix] = resolver;827return toDisposable(() => {828delete this._resolvers[authorityPrefix];829});830}831832public async getRemoteExecServer(remoteAuthority: string): Promise<vscode.ExecServer | undefined> {833const { resolver } = await this._activateAndGetResolver(remoteAuthority);834return resolver?.resolveExecServer?.(remoteAuthority, { resolveAttempt: 0 });835}836837// -- called by main thread838839private async _activateAndGetResolver(remoteAuthority: string): Promise<{ authorityPrefix: string; resolver: vscode.RemoteAuthorityResolver | undefined }> {840const authorityPlusIndex = remoteAuthority.indexOf('+');841if (authorityPlusIndex === -1) {842throw new RemoteAuthorityResolverError(`Not an authority that can be resolved!`, RemoteAuthorityResolverErrorCode.InvalidAuthority);843}844const authorityPrefix = remoteAuthority.substr(0, authorityPlusIndex);845846await this._almostReadyToRunExtensions.wait();847await this._activateByEvent(`onResolveRemoteAuthority:${authorityPrefix}`, false);848849return { authorityPrefix, resolver: this._resolvers[authorityPrefix] };850}851852public async $resolveAuthority(remoteAuthorityChain: string, resolveAttempt: number): Promise<Dto<IResolveAuthorityResult>> {853const sw = StopWatch.create(false);854const prefix = () => `[resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthorityChain)},${resolveAttempt})][${sw.elapsed()}ms] `;855const logInfo = (msg: string) => this._logService.info(`${prefix()}${msg}`);856const logWarning = (msg: string) => this._logService.warn(`${prefix()}${msg}`);857const logError = (msg: string, err: any = undefined) => this._logService.error(`${prefix()}${msg}`, err);858const normalizeError = (err: unknown) => {859if (err instanceof RemoteAuthorityResolverError) {860return {861type: 'error' as const,862error: {863code: err._code,864message: err._message,865detail: err._detail866}867};868}869throw err;870};871872const getResolver = async (remoteAuthority: string) => {873logInfo(`activating resolver for ${remoteAuthority}...`);874const { resolver, authorityPrefix } = await this._activateAndGetResolver(remoteAuthority);875if (!resolver) {876logError(`no resolver for ${authorityPrefix}`);877throw new RemoteAuthorityResolverError(`No remote extension installed to resolve ${authorityPrefix}.`, RemoteAuthorityResolverErrorCode.NoResolverFound);878}879return { resolver, authorityPrefix, remoteAuthority };880};881882const chain = remoteAuthorityChain.split(/@|%40/g).reverse();883logInfo(`activating remote resolvers ${chain.join(' -> ')}`);884885let resolvers;886try {887resolvers = await Promise.all(chain.map(getResolver)).catch(async (e: Error) => {888if (!(e instanceof RemoteAuthorityResolverError) || e._code !== RemoteAuthorityResolverErrorCode.InvalidAuthority) { throw e; }889logWarning(`resolving nested authorities failed: ${e.message}`);890return [await getResolver(remoteAuthorityChain)];891});892} catch (e) {893return normalizeError(e);894}895896const intervalLogger = new IntervalTimer();897intervalLogger.cancelAndSet(() => logInfo('waiting...'), 1000);898899let result!: vscode.ResolverResult;900let execServer: vscode.ExecServer | undefined;901for (const [i, { authorityPrefix, resolver, remoteAuthority }] of resolvers.entries()) {902try {903if (i === resolvers.length - 1) {904logInfo(`invoking final resolve()...`);905performance.mark(`code/extHost/willResolveAuthority/${authorityPrefix}`);906result = await resolver.resolve(remoteAuthority, { resolveAttempt, execServer });907performance.mark(`code/extHost/didResolveAuthorityOK/${authorityPrefix}`);908logInfo(`setting tunnel factory...`);909this._register(await this._extHostTunnelService.setTunnelFactory(910resolver,911ExtHostManagedResolvedAuthority.isManagedResolvedAuthority(result) ? result : undefined912));913} else {914logInfo(`invoking resolveExecServer() for ${remoteAuthority}`);915performance.mark(`code/extHost/willResolveExecServer/${authorityPrefix}`);916execServer = await resolver.resolveExecServer?.(remoteAuthority, { resolveAttempt, execServer });917if (!execServer) {918throw new RemoteAuthorityResolverError(`Exec server was not available for ${remoteAuthority}`, RemoteAuthorityResolverErrorCode.NoResolverFound); // we did, in fact, break the chain :(919}920performance.mark(`code/extHost/didResolveExecServerOK/${authorityPrefix}`);921}922} catch (e) {923performance.mark(`code/extHost/didResolveAuthorityError/${authorityPrefix}`);924logError(`returned an error`, e);925intervalLogger.dispose();926return normalizeError(e);927}928}929930intervalLogger.dispose();931932const tunnelInformation: TunnelInformation = {933environmentTunnels: result.environmentTunnels,934features: result.tunnelFeatures ? {935elevation: result.tunnelFeatures.elevation,936privacyOptions: result.tunnelFeatures.privacyOptions,937protocol: result.tunnelFeatures.protocol === undefined ? true : result.tunnelFeatures.protocol,938} : undefined939};940941// Split merged API result into separate authority/options942const options: ResolvedOptions = {943extensionHostEnv: result.extensionHostEnv,944isTrusted: result.isTrusted,945authenticationSession: result.authenticationSessionForInitializingExtensions ? { id: result.authenticationSessionForInitializingExtensions.id, providerId: result.authenticationSessionForInitializingExtensions.providerId } : undefined946};947948// extension are not required to return an instance of ResolvedAuthority or ManagedResolvedAuthority, so don't use `instanceof`949logInfo(`returned ${ExtHostManagedResolvedAuthority.isManagedResolvedAuthority(result) ? 'managed authority' : `${result.host}:${result.port}`}`);950951let authority: ResolvedAuthority;952if (ExtHostManagedResolvedAuthority.isManagedResolvedAuthority(result)) {953// The socket factory is identified by the `resolveAttempt`, since that is a number which954// always increments and is unique over all resolve() calls in a workbench session.955const socketFactoryId = resolveAttempt;956957// There is only on managed socket factory at a time, so we can just overwrite the old one.958this._extHostManagedSockets.setFactory(socketFactoryId, result.makeConnection);959960authority = {961authority: remoteAuthorityChain,962connectTo: new ManagedRemoteConnection(socketFactoryId),963connectionToken: result.connectionToken964};965} else {966authority = {967authority: remoteAuthorityChain,968connectTo: new WebSocketRemoteConnection(result.host, result.port),969connectionToken: result.connectionToken970};971}972973return {974type: 'ok',975value: {976authority: authority as Dto<ResolvedAuthority>,977options,978tunnelInformation,979}980};981}982983public async $getCanonicalURI(remoteAuthority: string, uriComponents: UriComponents): Promise<UriComponents | null> {984this._logService.info(`$getCanonicalURI invoked for authority (${getRemoteAuthorityPrefix(remoteAuthority)})`);985986const { resolver } = await this._activateAndGetResolver(remoteAuthority);987if (!resolver) {988// Return `null` if no resolver for `remoteAuthority` is found.989return null;990}991992const uri = URI.revive(uriComponents);993994if (typeof resolver.getCanonicalURI === 'undefined') {995// resolver cannot compute canonical URI996return uri;997}998999const result = await asPromise(() => resolver.getCanonicalURI!(uri));1000if (!result) {1001return uri;1002}10031004return result;1005}10061007public async $startExtensionHost(extensionsDelta: IExtensionDescriptionDelta): Promise<void> {1008// eslint-disable-next-line local/code-no-any-casts1009extensionsDelta.toAdd.forEach((extension) => (<any>extension).extensionLocation = URI.revive(extension.extensionLocation));10101011const { globalRegistry, myExtensions } = applyExtensionsDelta(this._activationEventsReader, this._globalRegistry, this._myRegistry, extensionsDelta);1012const newSearchTree = await this._createExtensionPathIndex(myExtensions);1013const extensionsPaths = await this.getExtensionPathIndex();1014extensionsPaths.setSearchTree(newSearchTree);1015this._globalRegistry.set(globalRegistry.getAllExtensionDescriptions());1016this._myRegistry.set(myExtensions);10171018if (isCI) {1019this._logService.info(`$startExtensionHost: global extensions: ${printExtIds(this._globalRegistry)}`);1020this._logService.info(`$startExtensionHost: local extensions: ${printExtIds(this._myRegistry)}`);1021}10221023return this._startExtensionHost();1024}10251026public $activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void> {1027if (activationKind === ActivationKind.Immediate) {1028return this._almostReadyToRunExtensions.wait()1029.then(_ => this._activateByEvent(activationEvent, false));1030}10311032return (1033this._readyToRunExtensions.wait()1034.then(_ => this._activateByEvent(activationEvent, false))1035);1036}10371038public async $activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean> {1039await this._readyToRunExtensions.wait();1040if (!this._myRegistry.getExtensionDescription(extensionId)) {1041// unknown extension => ignore1042return false;1043}1044await this._activateById(extensionId, reason);1045return true;1046}10471048public async $deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise<void> {1049// eslint-disable-next-line local/code-no-any-casts1050extensionsDelta.toAdd.forEach((extension) => (<any>extension).extensionLocation = URI.revive(extension.extensionLocation));10511052// First build up and update the trie and only afterwards apply the delta1053const { globalRegistry, myExtensions } = applyExtensionsDelta(this._activationEventsReader, this._globalRegistry, this._myRegistry, extensionsDelta);1054const newSearchTree = await this._createExtensionPathIndex(myExtensions);1055const extensionsPaths = await this.getExtensionPathIndex();1056extensionsPaths.setSearchTree(newSearchTree);1057this._globalRegistry.set(globalRegistry.getAllExtensionDescriptions());1058this._myRegistry.set(myExtensions);10591060if (isCI) {1061this._logService.info(`$deltaExtensions: global extensions: ${printExtIds(this._globalRegistry)}`);1062this._logService.info(`$deltaExtensions: local extensions: ${printExtIds(this._myRegistry)}`);1063}10641065return Promise.resolve(undefined);1066}10671068public async $test_latency(n: number): Promise<number> {1069return n;1070}10711072public async $test_up(b: VSBuffer): Promise<number> {1073return b.byteLength;1074}10751076public async $test_down(size: number): Promise<VSBuffer> {1077const buff = VSBuffer.alloc(size);1078const value = Math.random() % 256;1079for (let i = 0; i < size; i++) {1080buff.writeUInt8(value, i);1081}1082return buff;1083}10841085public async $updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise<void> {1086this._remoteConnectionData = connectionData;1087this._onDidChangeRemoteConnectionData.fire();1088}10891090protected _isESM(extensionDescription: IExtensionDescription | undefined, modulePath?: string): boolean {1091modulePath ??= extensionDescription ? this._getEntryPoint(extensionDescription) : modulePath;1092return modulePath?.endsWith('.mjs') || (extensionDescription?.type === 'module' && !modulePath?.endsWith('.cjs'));1093}10941095protected abstract _beforeAlmostReadyToRunExtensions(): Promise<void>;1096protected abstract _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined;1097protected abstract _loadCommonJSModule<T extends object | undefined>(extensionId: IExtensionDescription | null, module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T>;1098protected abstract _loadESMModule<T>(extension: IExtensionDescription | null, module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T>;1099public abstract $setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void>;1100}11011102function applyExtensionsDelta(activationEventsReader: SyncedActivationEventsReader, oldGlobalRegistry: ExtensionDescriptionRegistry, oldMyRegistry: ExtensionDescriptionRegistry, extensionsDelta: IExtensionDescriptionDelta) {1103activationEventsReader.addActivationEvents(extensionsDelta.addActivationEvents);1104const globalRegistry = new ExtensionDescriptionRegistry(activationEventsReader, oldGlobalRegistry.getAllExtensionDescriptions());1105globalRegistry.deltaExtensions(extensionsDelta.toAdd, extensionsDelta.toRemove);11061107const myExtensionsSet = new ExtensionIdentifierSet(oldMyRegistry.getAllExtensionDescriptions().map(extension => extension.identifier));1108for (const extensionId of extensionsDelta.myToRemove) {1109myExtensionsSet.delete(extensionId);1110}1111for (const extensionId of extensionsDelta.myToAdd) {1112myExtensionsSet.add(extensionId);1113}1114const myExtensions = filterExtensions(globalRegistry, myExtensionsSet);11151116return { globalRegistry, myExtensions };1117}11181119type TelemetryActivationEvent = {1120id: string;1121name: string;1122extensionVersion: string;1123publisherDisplayName: string;1124activationEvents: string | null;1125isBuiltin: boolean;1126reason: string;1127reasonId: string;1128};11291130function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TelemetryActivationEvent {1131const event = {1132id: extensionDescription.identifier.value,1133name: extensionDescription.name,1134extensionVersion: extensionDescription.version,1135publisherDisplayName: extensionDescription.publisher,1136activationEvents: extensionDescription.activationEvents ? extensionDescription.activationEvents.join(',') : null,1137isBuiltin: extensionDescription.isBuiltin,1138reason: reason.activationEvent,1139reasonId: reason.extensionId.value,1140};11411142return event;1143}11441145function printExtIds(registry: ExtensionDescriptionRegistry) {1146return registry.getAllExtensionDescriptions().map(ext => ext.identifier.value).join(',');1147}11481149export const IExtHostExtensionService = createDecorator<IExtHostExtensionService>('IExtHostExtensionService');11501151export interface IExtHostExtensionService extends AbstractExtHostExtensionService {1152readonly _serviceBrand: undefined;1153initialize(): Promise<void>;1154terminate(reason: string): void;1155getExtension(extensionId: string): Promise<IExtensionDescription | undefined>;1156isActivated(extensionId: ExtensionIdentifier): boolean;1157activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;1158getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined;1159getExtensionRegistry(): Promise<ExtensionDescriptionRegistry>;1160getExtensionPathIndex(): Promise<ExtensionPaths>;1161registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable;1162getRemoteExecServer(authority: string): Promise<vscode.ExecServer | undefined>;11631164readonly onDidChangeRemoteConnectionData: Event<void>;1165getRemoteConnectionData(): IRemoteConnectionData | null;1166}11671168export class Extension<T extends object | null | undefined> implements vscode.Extension<T> {11691170#extensionService: IExtHostExtensionService;1171#originExtensionId: ExtensionIdentifier;1172#identifier: ExtensionIdentifier;11731174readonly id: string;1175readonly extensionUri: URI;1176readonly extensionPath: string;1177readonly packageJSON: IExtensionDescription;1178readonly extensionKind: vscode.ExtensionKind;1179readonly isFromDifferentExtensionHost: boolean;11801181constructor(extensionService: IExtHostExtensionService, originExtensionId: ExtensionIdentifier, description: IExtensionDescription, kind: ExtensionKind, isFromDifferentExtensionHost: boolean) {1182this.#extensionService = extensionService;1183this.#originExtensionId = originExtensionId;1184this.#identifier = description.identifier;1185this.id = description.identifier.value;1186this.extensionUri = description.extensionLocation;1187this.extensionPath = path.normalize(originalFSPath(description.extensionLocation));1188this.packageJSON = description;1189this.extensionKind = kind;1190this.isFromDifferentExtensionHost = isFromDifferentExtensionHost;1191}11921193get isActive(): boolean {1194// TODO@alexdima support this1195return this.#extensionService.isActivated(this.#identifier);1196}11971198get exports(): T {1199if (this.packageJSON.api === 'none' || this.isFromDifferentExtensionHost) {1200return undefined!; // Strict nulloverride - Public api1201}1202return <T>this.#extensionService.getExtensionExports(this.#identifier);1203}12041205async activate(): Promise<T> {1206if (this.isFromDifferentExtensionHost) {1207throw new Error('Cannot activate foreign extension'); // TODO@alexdima support this1208}1209await this.#extensionService.activateByIdWithErrors(this.#identifier, { startup: false, extensionId: this.#originExtensionId, activationEvent: 'api' });1210return this.exports;1211}1212}12131214function filterExtensions(globalRegistry: ExtensionDescriptionRegistry, desiredExtensions: ExtensionIdentifierSet): IExtensionDescription[] {1215return globalRegistry.getAllExtensionDescriptions().filter(1216extension => desiredExtensions.has(extension.identifier)1217);1218}12191220export class ExtensionPaths {12211222constructor(1223private _searchTree: TernarySearchTree<URI, IExtensionDescription>1224) { }12251226setSearchTree(searchTree: TernarySearchTree<URI, IExtensionDescription>): void {1227this._searchTree = searchTree;1228}12291230findSubstr(key: URI): IExtensionDescription | undefined {1231return this._searchTree.findSubstr(key);1232}12331234forEach(callback: (value: IExtensionDescription, index: URI) => any): void {1235return this._searchTree.forEach(callback);1236}1237}12381239/**1240* This mirrors the activation events as seen by the renderer. The renderer1241* is the only one which can have a reliable view of activation events because1242* implicit activation events are generated via extension points, and they1243* are registered only on the renderer side.1244*/1245class SyncedActivationEventsReader implements IActivationEventsReader {12461247private readonly _map = new ExtensionIdentifierMap<string[]>();12481249constructor(activationEvents: { [extensionId: string]: string[] }) {1250this.addActivationEvents(activationEvents);1251}12521253public readActivationEvents(extensionDescription: IExtensionDescription): string[] {1254return this._map.get(extensionDescription.identifier) ?? [];1255}12561257public addActivationEvents(activationEvents: { [extensionId: string]: string[] }): void {1258for (const extensionId of Object.keys(activationEvents)) {1259this._map.set(extensionId, activationEvents[extensionId]);1260}1261}1262}126312641265