Path: blob/main/src/vs/workbench/api/common/extHostExtensionService.ts
3296 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,565postMessage: messagePort.postMessage.bind(messagePort) as any566};567}568569return messagePassingProtocol;570}571});572});573}574575private static _callActivate(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: vscode.ExtensionContext, extensionInternalStore: IDisposable, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<ActivatedExtension> {576// Make sure the extension's surface is not undefined577extensionModule = extensionModule || {578activate: undefined,579deactivate: undefined580};581582return this._callActivateOptional(logService, extensionId, extensionModule, context, activationTimesBuilder).then((extensionExports) => {583return new ActivatedExtension(false, null, activationTimesBuilder.build(), extensionModule, extensionExports, toDisposable(() => {584extensionInternalStore.dispose();585dispose(context.subscriptions);586}));587});588}589590private static _callActivateOptional(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: vscode.ExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<IExtensionAPI> {591if (typeof extensionModule.activate === 'function') {592try {593activationTimesBuilder.activateCallStart();594logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`);595const activateResult: Promise<IExtensionAPI> = extensionModule.activate.apply(globalThis, [context]);596activationTimesBuilder.activateCallStop();597598activationTimesBuilder.activateResolveStart();599return Promise.resolve(activateResult).then((value) => {600activationTimesBuilder.activateResolveStop();601return value;602});603} catch (err) {604return Promise.reject(err);605}606} else {607// No activate found => the module is the extension's exports608return Promise.resolve<IExtensionAPI>(extensionModule);609}610}611612// -- eager activation613614private _activateOneStartupFinished(desc: IExtensionDescription, activationEvent: string): void {615this._activateById(desc.identifier, {616startup: false,617extensionId: desc.identifier,618activationEvent: activationEvent619}).then(undefined, (err) => {620this._logService.error(err);621});622}623624private _activateAllStartupFinishedDeferred(extensions: IExtensionDescription[], start: number = 0): void {625const timeBudget = 50; // 50 milliseconds626const startTime = Date.now();627628setTimeout0(() => {629for (let i = start; i < extensions.length; i += 1) {630const desc = extensions[i];631for (const activationEvent of (desc.activationEvents ?? [])) {632if (activationEvent === 'onStartupFinished') {633if (Date.now() - startTime > timeBudget) {634// time budget for current task has been exceeded635// set a new task to activate current and remaining extensions636this._activateAllStartupFinishedDeferred(extensions, i);637break;638} else {639this._activateOneStartupFinished(desc, activationEvent);640}641}642}643}644});645}646647private _activateAllStartupFinished(): void {648// startup is considered finished649this._mainThreadExtensionsProxy.$setPerformanceMarks(performance.getMarks());650651this._extHostConfiguration.getConfigProvider().then((configProvider) => {652const shouldDeferActivation = configProvider.getConfiguration('extensions.experimental').get<boolean>('deferredStartupFinishedActivation');653const allExtensionDescriptions = this._myRegistry.getAllExtensionDescriptions();654if (shouldDeferActivation) {655this._activateAllStartupFinishedDeferred(allExtensionDescriptions);656} else {657for (const desc of allExtensionDescriptions) {658if (desc.activationEvents) {659for (const activationEvent of desc.activationEvents) {660if (activationEvent === 'onStartupFinished') {661this._activateOneStartupFinished(desc, activationEvent);662}663}664}665}666}667});668}669670// Handle "eager" activation extensions671private _handleEagerExtensions(): Promise<void> {672const starActivation = this._activateByEvent('*', true).then(undefined, (err) => {673this._logService.error(err);674});675676this._register(this._extHostWorkspace.onDidChangeWorkspace((e) => this._handleWorkspaceContainsEagerExtensions(e.added)));677const folders = this._extHostWorkspace.workspace ? this._extHostWorkspace.workspace.folders : [];678const workspaceContainsActivation = this._handleWorkspaceContainsEagerExtensions(folders);679const remoteResolverActivation = this._handleRemoteResolverEagerExtensions();680const eagerExtensionsActivation = Promise.all([remoteResolverActivation, starActivation, workspaceContainsActivation]).then(() => { });681682Promise.race([eagerExtensionsActivation, timeout(10000)]).then(() => {683this._activateAllStartupFinished();684});685686return eagerExtensionsActivation;687}688689private _handleWorkspaceContainsEagerExtensions(folders: ReadonlyArray<vscode.WorkspaceFolder>): Promise<void> {690if (folders.length === 0) {691return Promise.resolve(undefined);692}693694return Promise.all(695this._myRegistry.getAllExtensionDescriptions().map((desc) => {696return this._handleWorkspaceContainsEagerExtension(folders, desc);697})698).then(() => { });699}700701private async _handleWorkspaceContainsEagerExtension(folders: ReadonlyArray<vscode.WorkspaceFolder>, desc: IExtensionDescription): Promise<void> {702if (this.isActivated(desc.identifier)) {703return;704}705706const localWithRemote = !this._initData.remote.isRemote && !!this._initData.remote.authority;707const host: IExtensionActivationHost = {708logService: this._logService,709folders: folders.map(folder => folder.uri),710forceUsingSearch: localWithRemote || !this._hostUtils.fsExists,711exists: (uri) => this._hostUtils.fsExists!(uri.fsPath),712checkExists: (folders, includes, token) => this._mainThreadWorkspaceProxy.$checkExists(folders, includes, token)713};714715const result = await checkActivateWorkspaceContainsExtension(host, desc);716if (!result) {717return;718}719720return (721this._activateById(desc.identifier, { startup: true, extensionId: desc.identifier, activationEvent: result.activationEvent })722.then(undefined, err => this._logService.error(err))723);724}725726private async _handleRemoteResolverEagerExtensions(): Promise<void> {727if (this._initData.remote.authority) {728return this._activateByEvent(`onResolveRemoteAuthority:${this._initData.remote.authority}`, false);729}730}731732public async $extensionTestsExecute(): Promise<number> {733await this._eagerExtensionsActivated.wait();734try {735return await this._doHandleExtensionTests();736} catch (error) {737console.error(error); // ensure any error message makes it onto the console738throw error;739}740}741742private async _doHandleExtensionTests(): Promise<number> {743const { extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment;744if (!extensionDevelopmentLocationURI || !extensionTestsLocationURI) {745throw new Error(nls.localize('extensionTestError1', "Cannot load test runner."));746}747748const extensionDescription = (await this.getExtensionPathIndex()).findSubstr(extensionTestsLocationURI);749const isESM = this._isESM(extensionDescription, extensionTestsLocationURI.path);750751// Require the test runner via node require from the provided path752const testRunner = await (isESM753? this._loadESMModule<ITestRunner | INewTestRunner | undefined>(null, extensionTestsLocationURI, new ExtensionActivationTimesBuilder(false))754: this._loadCommonJSModule<ITestRunner | INewTestRunner | undefined>(null, extensionTestsLocationURI, new ExtensionActivationTimesBuilder(false)));755756if (!testRunner || typeof testRunner.run !== 'function') {757throw new Error(nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsLocationURI.toString()));758}759760// Execute the runner if it follows the old `run` spec761return new Promise<number>((resolve, reject) => {762const oldTestRunnerCallback = (error: Error, failures: number | undefined) => {763if (error) {764if (isCI) {765this._logService.error(`Test runner called back with error`, error);766}767reject(error);768} else {769if (isCI) {770if (failures) {771this._logService.info(`Test runner called back with ${failures} failures.`);772} else {773this._logService.info(`Test runner called back with successful outcome.`);774}775}776resolve((typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);777}778};779780const extensionTestsPath = originalFSPath(extensionTestsLocationURI); // for the old test runner API781782const runResult = testRunner.run(extensionTestsPath, oldTestRunnerCallback);783784// Using the new API `run(): Promise<void>`785if (runResult && runResult.then) {786runResult787.then(() => {788if (isCI) {789this._logService.info(`Test runner finished successfully.`);790}791resolve(0);792})793.catch((err: unknown) => {794if (isCI) {795this._logService.error(`Test runner finished with error`, err);796}797reject(err instanceof Error && err.stack ? err.stack : String(err));798});799}800});801}802803private _startExtensionHost(): Promise<void> {804if (this._started) {805throw new Error(`Extension host is already started!`);806}807this._started = true;808809return this._readyToStartExtensionHost.wait()810.then(() => this._readyToRunExtensions.open())811.then(() => {812// wait for all activation events that came in during workbench startup, but at maximum 1s813return Promise.race([this._activator.waitForActivatingExtensions(), timeout(1000)]);814})815.then(() => this._handleEagerExtensions())816.then(() => {817this._eagerExtensionsActivated.open();818this._logService.info(`Eager extensions activated`);819});820}821822// -- called by extensions823824public registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable {825this._resolvers[authorityPrefix] = resolver;826return toDisposable(() => {827delete this._resolvers[authorityPrefix];828});829}830831public async getRemoteExecServer(remoteAuthority: string): Promise<vscode.ExecServer | undefined> {832const { resolver } = await this._activateAndGetResolver(remoteAuthority);833return resolver?.resolveExecServer?.(remoteAuthority, { resolveAttempt: 0 });834}835836// -- called by main thread837838private async _activateAndGetResolver(remoteAuthority: string): Promise<{ authorityPrefix: string; resolver: vscode.RemoteAuthorityResolver | undefined }> {839const authorityPlusIndex = remoteAuthority.indexOf('+');840if (authorityPlusIndex === -1) {841throw new RemoteAuthorityResolverError(`Not an authority that can be resolved!`, RemoteAuthorityResolverErrorCode.InvalidAuthority);842}843const authorityPrefix = remoteAuthority.substr(0, authorityPlusIndex);844845await this._almostReadyToRunExtensions.wait();846await this._activateByEvent(`onResolveRemoteAuthority:${authorityPrefix}`, false);847848return { authorityPrefix, resolver: this._resolvers[authorityPrefix] };849}850851public async $resolveAuthority(remoteAuthorityChain: string, resolveAttempt: number): Promise<Dto<IResolveAuthorityResult>> {852const sw = StopWatch.create(false);853const prefix = () => `[resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthorityChain)},${resolveAttempt})][${sw.elapsed()}ms] `;854const logInfo = (msg: string) => this._logService.info(`${prefix()}${msg}`);855const logWarning = (msg: string) => this._logService.warn(`${prefix()}${msg}`);856const logError = (msg: string, err: any = undefined) => this._logService.error(`${prefix()}${msg}`, err);857const normalizeError = (err: unknown) => {858if (err instanceof RemoteAuthorityResolverError) {859return {860type: 'error' as const,861error: {862code: err._code,863message: err._message,864detail: err._detail865}866};867}868throw err;869};870871const getResolver = async (remoteAuthority: string) => {872logInfo(`activating resolver for ${remoteAuthority}...`);873const { resolver, authorityPrefix } = await this._activateAndGetResolver(remoteAuthority);874if (!resolver) {875logError(`no resolver for ${authorityPrefix}`);876throw new RemoteAuthorityResolverError(`No remote extension installed to resolve ${authorityPrefix}.`, RemoteAuthorityResolverErrorCode.NoResolverFound);877}878return { resolver, authorityPrefix, remoteAuthority };879};880881const chain = remoteAuthorityChain.split(/@|%40/g).reverse();882logInfo(`activating remote resolvers ${chain.join(' -> ')}`);883884let resolvers;885try {886resolvers = await Promise.all(chain.map(getResolver)).catch(async (e: Error) => {887if (!(e instanceof RemoteAuthorityResolverError) || e._code !== RemoteAuthorityResolverErrorCode.InvalidAuthority) { throw e; }888logWarning(`resolving nested authorities failed: ${e.message}`);889return [await getResolver(remoteAuthorityChain)];890});891} catch (e) {892return normalizeError(e);893}894895const intervalLogger = new IntervalTimer();896intervalLogger.cancelAndSet(() => logInfo('waiting...'), 1000);897898let result!: vscode.ResolverResult;899let execServer: vscode.ExecServer | undefined;900for (const [i, { authorityPrefix, resolver, remoteAuthority }] of resolvers.entries()) {901try {902if (i === resolvers.length - 1) {903logInfo(`invoking final resolve()...`);904performance.mark(`code/extHost/willResolveAuthority/${authorityPrefix}`);905result = await resolver.resolve(remoteAuthority, { resolveAttempt, execServer });906performance.mark(`code/extHost/didResolveAuthorityOK/${authorityPrefix}`);907logInfo(`setting tunnel factory...`);908this._register(await this._extHostTunnelService.setTunnelFactory(909resolver,910ExtHostManagedResolvedAuthority.isManagedResolvedAuthority(result) ? result : undefined911));912} else {913logInfo(`invoking resolveExecServer() for ${remoteAuthority}`);914performance.mark(`code/extHost/willResolveExecServer/${authorityPrefix}`);915execServer = await resolver.resolveExecServer?.(remoteAuthority, { resolveAttempt, execServer });916if (!execServer) {917throw new RemoteAuthorityResolverError(`Exec server was not available for ${remoteAuthority}`, RemoteAuthorityResolverErrorCode.NoResolverFound); // we did, in fact, break the chain :(918}919performance.mark(`code/extHost/didResolveExecServerOK/${authorityPrefix}`);920}921} catch (e) {922performance.mark(`code/extHost/didResolveAuthorityError/${authorityPrefix}`);923logError(`returned an error`, e);924intervalLogger.dispose();925return normalizeError(e);926}927}928929intervalLogger.dispose();930931const tunnelInformation: TunnelInformation = {932environmentTunnels: result.environmentTunnels,933features: result.tunnelFeatures ? {934elevation: result.tunnelFeatures.elevation,935privacyOptions: result.tunnelFeatures.privacyOptions,936protocol: result.tunnelFeatures.protocol === undefined ? true : result.tunnelFeatures.protocol,937} : undefined938};939940// Split merged API result into separate authority/options941const options: ResolvedOptions = {942extensionHostEnv: result.extensionHostEnv,943isTrusted: result.isTrusted,944authenticationSession: result.authenticationSessionForInitializingExtensions ? { id: result.authenticationSessionForInitializingExtensions.id, providerId: result.authenticationSessionForInitializingExtensions.providerId } : undefined945};946947// extension are not required to return an instance of ResolvedAuthority or ManagedResolvedAuthority, so don't use `instanceof`948logInfo(`returned ${ExtHostManagedResolvedAuthority.isManagedResolvedAuthority(result) ? 'managed authority' : `${result.host}:${result.port}`}`);949950let authority: ResolvedAuthority;951if (ExtHostManagedResolvedAuthority.isManagedResolvedAuthority(result)) {952// The socket factory is identified by the `resolveAttempt`, since that is a number which953// always increments and is unique over all resolve() calls in a workbench session.954const socketFactoryId = resolveAttempt;955956// There is only on managed socket factory at a time, so we can just overwrite the old one.957this._extHostManagedSockets.setFactory(socketFactoryId, result.makeConnection);958959authority = {960authority: remoteAuthorityChain,961connectTo: new ManagedRemoteConnection(socketFactoryId),962connectionToken: result.connectionToken963};964} else {965authority = {966authority: remoteAuthorityChain,967connectTo: new WebSocketRemoteConnection(result.host, result.port),968connectionToken: result.connectionToken969};970}971972return {973type: 'ok',974value: {975authority: authority as Dto<ResolvedAuthority>,976options,977tunnelInformation,978}979};980}981982public async $getCanonicalURI(remoteAuthority: string, uriComponents: UriComponents): Promise<UriComponents | null> {983this._logService.info(`$getCanonicalURI invoked for authority (${getRemoteAuthorityPrefix(remoteAuthority)})`);984985const { resolver } = await this._activateAndGetResolver(remoteAuthority);986if (!resolver) {987// Return `null` if no resolver for `remoteAuthority` is found.988return null;989}990991const uri = URI.revive(uriComponents);992993if (typeof resolver.getCanonicalURI === 'undefined') {994// resolver cannot compute canonical URI995return uri;996}997998const result = await asPromise(() => resolver.getCanonicalURI!(uri));999if (!result) {1000return uri;1001}10021003return result;1004}10051006public async $startExtensionHost(extensionsDelta: IExtensionDescriptionDelta): Promise<void> {1007extensionsDelta.toAdd.forEach((extension) => (<any>extension).extensionLocation = URI.revive(extension.extensionLocation));10081009const { globalRegistry, myExtensions } = applyExtensionsDelta(this._activationEventsReader, this._globalRegistry, this._myRegistry, extensionsDelta);1010const newSearchTree = await this._createExtensionPathIndex(myExtensions);1011const extensionsPaths = await this.getExtensionPathIndex();1012extensionsPaths.setSearchTree(newSearchTree);1013this._globalRegistry.set(globalRegistry.getAllExtensionDescriptions());1014this._myRegistry.set(myExtensions);10151016if (isCI) {1017this._logService.info(`$startExtensionHost: global extensions: ${printExtIds(this._globalRegistry)}`);1018this._logService.info(`$startExtensionHost: local extensions: ${printExtIds(this._myRegistry)}`);1019}10201021return this._startExtensionHost();1022}10231024public $activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void> {1025if (activationKind === ActivationKind.Immediate) {1026return this._almostReadyToRunExtensions.wait()1027.then(_ => this._activateByEvent(activationEvent, false));1028}10291030return (1031this._readyToRunExtensions.wait()1032.then(_ => this._activateByEvent(activationEvent, false))1033);1034}10351036public async $activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean> {1037await this._readyToRunExtensions.wait();1038if (!this._myRegistry.getExtensionDescription(extensionId)) {1039// unknown extension => ignore1040return false;1041}1042await this._activateById(extensionId, reason);1043return true;1044}10451046public async $deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise<void> {1047extensionsDelta.toAdd.forEach((extension) => (<any>extension).extensionLocation = URI.revive(extension.extensionLocation));10481049// First build up and update the trie and only afterwards apply the delta1050const { globalRegistry, myExtensions } = applyExtensionsDelta(this._activationEventsReader, this._globalRegistry, this._myRegistry, extensionsDelta);1051const newSearchTree = await this._createExtensionPathIndex(myExtensions);1052const extensionsPaths = await this.getExtensionPathIndex();1053extensionsPaths.setSearchTree(newSearchTree);1054this._globalRegistry.set(globalRegistry.getAllExtensionDescriptions());1055this._myRegistry.set(myExtensions);10561057if (isCI) {1058this._logService.info(`$deltaExtensions: global extensions: ${printExtIds(this._globalRegistry)}`);1059this._logService.info(`$deltaExtensions: local extensions: ${printExtIds(this._myRegistry)}`);1060}10611062return Promise.resolve(undefined);1063}10641065public async $test_latency(n: number): Promise<number> {1066return n;1067}10681069public async $test_up(b: VSBuffer): Promise<number> {1070return b.byteLength;1071}10721073public async $test_down(size: number): Promise<VSBuffer> {1074const buff = VSBuffer.alloc(size);1075const value = Math.random() % 256;1076for (let i = 0; i < size; i++) {1077buff.writeUInt8(value, i);1078}1079return buff;1080}10811082public async $updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise<void> {1083this._remoteConnectionData = connectionData;1084this._onDidChangeRemoteConnectionData.fire();1085}10861087protected _isESM(extensionDescription: IExtensionDescription | undefined, modulePath?: string): boolean {1088modulePath ??= extensionDescription ? this._getEntryPoint(extensionDescription) : modulePath;1089return modulePath?.endsWith('.mjs') || (extensionDescription?.type === 'module' && !modulePath?.endsWith('.cjs'));1090}10911092protected abstract _beforeAlmostReadyToRunExtensions(): Promise<void>;1093protected abstract _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined;1094protected abstract _loadCommonJSModule<T extends object | undefined>(extensionId: IExtensionDescription | null, module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T>;1095protected abstract _loadESMModule<T>(extension: IExtensionDescription | null, module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T>;1096public abstract $setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void>;1097}10981099function applyExtensionsDelta(activationEventsReader: SyncedActivationEventsReader, oldGlobalRegistry: ExtensionDescriptionRegistry, oldMyRegistry: ExtensionDescriptionRegistry, extensionsDelta: IExtensionDescriptionDelta) {1100activationEventsReader.addActivationEvents(extensionsDelta.addActivationEvents);1101const globalRegistry = new ExtensionDescriptionRegistry(activationEventsReader, oldGlobalRegistry.getAllExtensionDescriptions());1102globalRegistry.deltaExtensions(extensionsDelta.toAdd, extensionsDelta.toRemove);11031104const myExtensionsSet = new ExtensionIdentifierSet(oldMyRegistry.getAllExtensionDescriptions().map(extension => extension.identifier));1105for (const extensionId of extensionsDelta.myToRemove) {1106myExtensionsSet.delete(extensionId);1107}1108for (const extensionId of extensionsDelta.myToAdd) {1109myExtensionsSet.add(extensionId);1110}1111const myExtensions = filterExtensions(globalRegistry, myExtensionsSet);11121113return { globalRegistry, myExtensions };1114}11151116type TelemetryActivationEvent = {1117id: string;1118name: string;1119extensionVersion: string;1120publisherDisplayName: string;1121activationEvents: string | null;1122isBuiltin: boolean;1123reason: string;1124reasonId: string;1125};11261127function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TelemetryActivationEvent {1128const event = {1129id: extensionDescription.identifier.value,1130name: extensionDescription.name,1131extensionVersion: extensionDescription.version,1132publisherDisplayName: extensionDescription.publisher,1133activationEvents: extensionDescription.activationEvents ? extensionDescription.activationEvents.join(',') : null,1134isBuiltin: extensionDescription.isBuiltin,1135reason: reason.activationEvent,1136reasonId: reason.extensionId.value,1137};11381139return event;1140}11411142function printExtIds(registry: ExtensionDescriptionRegistry) {1143return registry.getAllExtensionDescriptions().map(ext => ext.identifier.value).join(',');1144}11451146export const IExtHostExtensionService = createDecorator<IExtHostExtensionService>('IExtHostExtensionService');11471148export interface IExtHostExtensionService extends AbstractExtHostExtensionService {1149readonly _serviceBrand: undefined;1150initialize(): Promise<void>;1151terminate(reason: string): void;1152getExtension(extensionId: string): Promise<IExtensionDescription | undefined>;1153isActivated(extensionId: ExtensionIdentifier): boolean;1154activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;1155getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined;1156getExtensionRegistry(): Promise<ExtensionDescriptionRegistry>;1157getExtensionPathIndex(): Promise<ExtensionPaths>;1158registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable;1159getRemoteExecServer(authority: string): Promise<vscode.ExecServer | undefined>;11601161onDidChangeRemoteConnectionData: Event<void>;1162getRemoteConnectionData(): IRemoteConnectionData | null;1163}11641165export class Extension<T extends object | null | undefined> implements vscode.Extension<T> {11661167#extensionService: IExtHostExtensionService;1168#originExtensionId: ExtensionIdentifier;1169#identifier: ExtensionIdentifier;11701171readonly id: string;1172readonly extensionUri: URI;1173readonly extensionPath: string;1174readonly packageJSON: IExtensionDescription;1175readonly extensionKind: vscode.ExtensionKind;1176readonly isFromDifferentExtensionHost: boolean;11771178constructor(extensionService: IExtHostExtensionService, originExtensionId: ExtensionIdentifier, description: IExtensionDescription, kind: ExtensionKind, isFromDifferentExtensionHost: boolean) {1179this.#extensionService = extensionService;1180this.#originExtensionId = originExtensionId;1181this.#identifier = description.identifier;1182this.id = description.identifier.value;1183this.extensionUri = description.extensionLocation;1184this.extensionPath = path.normalize(originalFSPath(description.extensionLocation));1185this.packageJSON = description;1186this.extensionKind = kind;1187this.isFromDifferentExtensionHost = isFromDifferentExtensionHost;1188}11891190get isActive(): boolean {1191// TODO@alexdima support this1192return this.#extensionService.isActivated(this.#identifier);1193}11941195get exports(): T {1196if (this.packageJSON.api === 'none' || this.isFromDifferentExtensionHost) {1197return undefined!; // Strict nulloverride - Public api1198}1199return <T>this.#extensionService.getExtensionExports(this.#identifier);1200}12011202async activate(): Promise<T> {1203if (this.isFromDifferentExtensionHost) {1204throw new Error('Cannot activate foreign extension'); // TODO@alexdima support this1205}1206await this.#extensionService.activateByIdWithErrors(this.#identifier, { startup: false, extensionId: this.#originExtensionId, activationEvent: 'api' });1207return this.exports;1208}1209}12101211function filterExtensions(globalRegistry: ExtensionDescriptionRegistry, desiredExtensions: ExtensionIdentifierSet): IExtensionDescription[] {1212return globalRegistry.getAllExtensionDescriptions().filter(1213extension => desiredExtensions.has(extension.identifier)1214);1215}12161217export class ExtensionPaths {12181219constructor(1220private _searchTree: TernarySearchTree<URI, IExtensionDescription>1221) { }12221223setSearchTree(searchTree: TernarySearchTree<URI, IExtensionDescription>): void {1224this._searchTree = searchTree;1225}12261227findSubstr(key: URI): IExtensionDescription | undefined {1228return this._searchTree.findSubstr(key);1229}12301231forEach(callback: (value: IExtensionDescription, index: URI) => any): void {1232return this._searchTree.forEach(callback);1233}1234}12351236/**1237* This mirrors the activation events as seen by the renderer. The renderer1238* is the only one which can have a reliable view of activation events because1239* implicit activation events are generated via extension points, and they1240* are registered only on the renderer side.1241*/1242class SyncedActivationEventsReader implements IActivationEventsReader {12431244private readonly _map = new ExtensionIdentifierMap<string[]>();12451246constructor(activationEvents: { [extensionId: string]: string[] }) {1247this.addActivationEvents(activationEvents);1248}12491250public readActivationEvents(extensionDescription: IExtensionDescription): string[] {1251return this._map.get(extensionDescription.identifier) ?? [];1252}12531254public addActivationEvents(activationEvents: { [extensionId: string]: string[] }): void {1255for (const extensionId of Object.keys(activationEvents)) {1256this._map.set(extensionId, activationEvents[extensionId]);1257}1258}1259}126012611262