src/vs/workbench/api/common/extHostExtensionService.ts

1285 LOC · 326 covered · 959 uncovered · 65 ranges · 38 concepts · 2 introducers · 33 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the filesrc/vs/base/common/errors.ts · 357 LOCcommon/errors.tssrc/vs/workbench/api/common/extHostApiDeprecationService.ts · 77 LOCcommon/extHostApiDepreca…src/vs/workbench/api/common/extHostAuthentication.ts · 1040 LOCcommon/extHostAuthentica…src/vs/workbench/api/common/extHostFileSystemConsumer.ts · 260 LOCcommon/extHostFileSystem…src/vs/workbench/api/common/extHostLanguageModels.ts · 768 LOCcommon/extHostLanguageMo…src/vs/workbench/api/common/extHostLocalizationService.ts · 109 LOCcommon/extHostLocalizati…src/vs/workbench/api/common/extHostManagedSockets.ts · 121 LOCcommon/extHostManagedSoc…src/vs/workbench/api/common/extHostMemento.ts · 122 LOCcommon/extHostMemento.tssrc/vs/workbench/api/common/extHostProgress.ts · 107 LOCcommon/extHostProgress.t…src/vs/workbench/api/common/extHostSecretState.ts · 42 LOCcommon/extHostSecretStat…src/vs/workbench/api/common/extHostSecrets.ts · 51 LOCcommon/extHostSecrets.tssrc/vs/workbench/api/common/extHostStorage.ts · 75 LOCcommon/extHostStorage.tssrc/vs/workbench/api/common/extHostStoragePaths.ts · 95 LOCcommon/extHostStoragePat…src/vs/workbench/api/common/extHostUrls.ts · 70 LOCcommon/extHostUrls.tssrc/vs/workbench/api/common/extHostWindow.ts · 116 LOCcommon/extHostWindow.tssrc/vs/workbench/api/common/extHostXaaAuthProvider.ts · 402 LOCcommon/extHostXaaAuthPro…src/vs/workbench/api/common/extensionHostMain.ts · 239 LOCcommon/extensionHostMain…src/vs/workbench/services/extensions/common/workspaceContains.ts · 139 LOCcommon/workspaceContains…extHostMcp.test|title=ExtHostMcp IAuthMetadata properties should expose readonly properties|occurrence=1, extHostMcp.test|title=ExtHostMcp createAuthMetadata should create IAuthMetadata with fetched server metadata|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×1 · 3 introduced LOCextHostMcp.ts ×1extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should handle multiple Bearer challenges and use first scope|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are the same|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true and update scopes when WWW-Authenticate header contains new scopes|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are same but in different order|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from defined scopes to undefined (no scope in header)|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle non-401 status codes in update()|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should ignore non-Bearer schemes|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from undefined scopes to defined scopes|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×1 · 1 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×1 · 2 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×1 · 6 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×1 · 4 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×2 · 4 introduced LOCextHostMcp.ts ×2extHostMcp.ts ×1 · 1 introduced LOCextHostMcp.ts ×1extHostMcp.test|title=ExtHostMcp createAuthMetadata should fall back to default metadata when server metadata fetch fails|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×4 · 6 introduced LOCextHostMcp.ts ×4extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header when resource metadata has none|occurrence=1, extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header even when resource metadata has scopes_supported|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×1 · 2 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×1 · 6 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×4 · 13 introduced LOCextHostMcp.ts ×4extHostMcpNode.ts ×8 · 45 introduced LOCextHostMcpNode.ts ×8extHostMcp.test|title=ExtHostMcp createAuthMetadata should pass launch headers when fetching metadata from same origin|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×5 · 25 introduced LOCextHostMcp.ts ×5extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle invalid JSON in resource metadata response|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×3 · 23 introduced LOCextHostMcp.ts ×3extHostMcp.ts ×1 · 2 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×8 · 60 introduced LOCextHostMcp.ts ×8extHostMcp.ts ×43 · 874 introduced LOCextHostMcp.ts ×43extensionHostMain.ts ×1 · 2 introduced LOCextensionHostMain.ts ×1reset prepareStackTrace-callback|occurrence=1, extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 prevent rewrapping|occurrence=1 · 0 introduced LOCreset prepareStackTrace-…extensionHostMain.ts ×5 · 26 introduced LOCextensionHostMain.ts ×5f0f2e182082072efdaf0f8e1537d2cce Restored, too many uses before restoration|occurrence=1 · 0 introduced LOCf0f2e182082072efdaf0f8e1…extensionHostMain.ts ×1 · 3 introduced LOCextensionHostMain.ts ×1extensionHostMain.ts ×1 · 3 introduced LOCextensionHostMain.ts ×1extensionHostMain.ts ×11 · 164 introduced LOCextensionHostMain.ts ×11extHostExtensionService.ts ×64 · 1246 introduced LOCextHostExtensionService.…extHostMcp.test|title=ExtHostMcp IAuthMetadata properties should allow undefined scopes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata properties should allow undefined scopes|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata properties should expose readonly properties|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata properties should expose readonly properties|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should handle multiple Bearer challenges and use first scope|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should handle multiple Bearer challenges and use first scope|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should ignore non-Bearer schemes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should ignore non-Bearer schemes|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when no WWW-Authenticate header and scopes are already undefined|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when no WWW-Authenticate header and scopes are already undefined|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are same but in different order|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are same but in different order|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are the same|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are the same|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true and update scopes when WWW-Authenticate header contains new scopes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true and update scopes when WWW-Authenticate header contains new scopes|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from defined scopes to undefined (no scope in header)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from defined scopes to undefined (no scope in header)|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from undefined scopes to defined scopes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from undefined scopes to defined scopes|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should create IAuthMetadata with fetched server metadata|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should create IAuthMetadata with fetched server metadata|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should fall back to default metadata when server metadata fetch fails|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should fall back to default metadata when server metadata fetch fails|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle empty scope string in WWW-Authenticate header|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle empty scope string in WWW-Authenticate header|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle invalid JSON in resource metadata response|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle invalid JSON in resource metadata response|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle malformed WWW-Authenticate header gracefully|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle malformed WWW-Authenticate header gracefully|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle non-401 status codes in update()|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle non-401 status codes in update()|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should pass launch headers when fetching metadata from same origin|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should pass launch headers when fetching metadata from same origin|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should use resource_metadata challenge URL from WWW-Authenticate header|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should use resource_metadata challenge URL from WWW-Authenticate header|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header even when resource metadata has scopes_supported|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header even when resource metadata has scopes_supported|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header when resource metadata has none|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header when resource metadata has none|occurrence=1extHostMcp.test|title=Ex…extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 basics|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 basics|occurrence=1extensionHostMain.test|t…f0f2e182082072efdaf0f8e1537d2cce Never restored, separate operations|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 https://gist.github.com/thecrypticace/f0f2e182082072efdaf0f8e1537d2cce Never restored, separate operations|occurrence=1f0f2e182082072efdaf0f8e1…f0f2e182082072efdaf0f8e1537d2cce Restored, separate operations|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 https://gist.github.com/thecrypticace/f0f2e182082072efdaf0f8e1537d2cce Restored, separate operations|occurrence=1f0f2e182082072efdaf0f8e1…f0f2e182082072efdaf0f8e1537d2cce Restored, too many uses before restoration|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 https://gist.github.com/thecrypticace/f0f2e182082072efdaf0f8e1537d2cce Restored, too many uses before restoration|occurrence=1f0f2e182082072efdaf0f8e1…extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 prevent rewrapping|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 prevent rewrapping|occurrence=1extensionHostMain.test|t…reset prepareStackTrace-callback|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 set/reset prepareStackTrace-callback|occurrence=1reset prepareStackTrace-…extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 wrap prepareStackTrace-callback|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 wrap prepareStackTrace-callback|occurrence=1extensionHostMain.test|t…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg does not add stray ^ to argument values|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg does not add stray ^ to argument values|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg doubles embedded double quotes (cmd.exe convention)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg doubles embedded double quotes (cmd.exe convention)|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg neutralizes cmd.exe metacharacters inside quotes (CVE-2024-27980)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg neutralizes cmd.exe metacharacters inside quotes (CVE-2024-27980)|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg preserves paths with parentheses without injecting ^|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg preserves paths with parentheses without injecting ^|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg preserves paths with spaces|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg preserves paths with spaces|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg wraps simple values in double quotes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg wraps simple values in double quotes|occurrence=1extHostMcpNode.test|titl…Focused file · src/vs/workbench/api/common/extHostExtensionService.ts · 1285 LOCcommon/extHostExtensionS…

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- extHostExtensionService.ts ×64
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import * as nls from '../../../nls.js';
7 > import * as path from '../../../base/common/path.js';
8 > import * as performance from '../../../base/common/performance.js';
9 > import { originalFSPath, joinPath, extUriBiasedIgnorePathCase } from '../../../base/common/resources.js';
10 > import { asPromise, Barrier, IntervalTimer, timeout } from '../../../base/common/async.js';
11 > import { dispose, toDisposable, Disposable, DisposableStore, IDisposable } from '../../../base/common/lifecycle.js';
12 > import { TernarySearchTree } from '../../../base/common/ternarySearchTree.js';
13 > import { URI, UriComponents } from '../../../base/common/uri.js';
14 > import { ILogService } from '../../../platform/log/common/log.js';
15 > import { ExtHostExtensionServiceShape, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape } from './extHost.protocol.js';
16 > import { IExtensionDescriptionDelta, IExtensionHostInitData } from '../../services/extensions/common/extensionHostProtocol.js';
17 > import { ExtHostConfiguration, IExtHostConfiguration } from './extHostConfiguration.js';
18 > import { ActivatedExtension, EmptyExtension, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from './extHostExtensionActivator.js';
19 > import { ExtHostStorage, IExtHostStorage } from './extHostStorage.js';
20 > import { ExtHostWorkspace, IExtHostWorkspace } from './extHostWorkspace.js';
21 > import { MissingExtensionDependency, ActivationKind, checkProposedApiEnabled, isProposedApiEnabled, ExtensionActivationReason, IProposedApiUsage, setProposedApiUsageReporter, setEnabledApiProposalsFallbackExperiment } from '../../services/extensions/common/extensions.js';
22 > import { ExtensionDescriptionRegistry, IActivationEventsReader } from '../../services/extensions/common/extensionDescriptionRegistry.js';
23 > import * as errors from '../../../base/common/errors.js';
24 > import type * as vscode from 'vscode';
25 > import { ExtensionIdentifier, ExtensionIdentifierMap, ExtensionIdentifierSet, IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
26 > import { VSBuffer } from '../../../base/common/buffer.js';
27 > import { ExtensionGlobalMemento, ExtensionMemento } from './extHostMemento.js';
28 > import { RemoteAuthorityResolverError, ExtensionKind, ExtensionMode, ExtensionRuntime, ManagedResolvedAuthority as ExtHostManagedResolvedAuthority } from './extHostTypes.js';
29 > import { ResolvedAuthority, ResolvedOptions, RemoteAuthorityResolverErrorCode, IRemoteConnectionData, getRemoteAuthorityPrefix, TunnelInformation, ManagedRemoteConnection, WebSocketRemoteConnection } from '../../../platform/remote/common/remoteAuthorityResolver.js';
30 > import { IInstantiationService, createDecorator } from '../../../platform/instantiation/common/instantiation.js';
31 > import { IExtHostInitDataService } from './extHostInitDataService.js';
32 > import { IExtensionStoragePaths } from './extHostStoragePaths.js';
33 > import { IExtHostRpcService } from './extHostRpcService.js';
34 > import { ServiceCollection } from '../../../platform/instantiation/common/serviceCollection.js';
35 > import { IExtHostTunnelService } from './extHostTunnelService.js';
36 > import { IExtHostTerminalService } from './extHostTerminalService.js';
37 > import { IExtHostLanguageModels } from './extHostLanguageModels.js';
38 > import { Emitter, Event } from '../../../base/common/event.js';
39 > import { IExtensionActivationHost, checkActivateWorkspaceContainsExtension } from '../../services/extensions/common/workspaceContains.js';
40 > import { ExtHostSecretState, IExtHostSecretState } from './extHostSecretState.js';
41 > import { ExtensionSecrets } from './extHostSecrets.js';
42 > import { Schemas } from '../../../base/common/network.js';
43 > import { IResolveAuthorityResult } from '../../services/extensions/common/extensionHostProxy.js';
44 > import { IExtHostLocalizationService } from './extHostLocalizationService.js';
45 > import { StopWatch } from '../../../base/common/stopwatch.js';
46 > import { isCI, setTimeout0 } from '../../../base/common/platform.js';
47 > import { IExtHostManagedSockets } from './extHostManagedSockets.js';
48 > import { Dto } from '../../services/extensions/common/proxyIdentifier.js';
49 >
50 > interface ITestRunner {
51 > /** Old test runner API, as exported from `vscode/lib/testrunner` */
52 > run(testsRoot: string, clb: (error: Error, failures?: number) => void): void;
53 > }
54 >
55 > interface INewTestRunner {
56 > /** New test runner API, as explained in the extension test doc */
57 > run(): Promise<void>;
58 > }
59 >
60 > export const IHostUtils = createDecorator<IHostUtils>('IHostUtils');
61 >
62 > export interface IHostUtils {
63 > readonly _serviceBrand: undefined;
64 > readonly pid: number | undefined;
65 > exit(code: number): void;
66 > fsExists?(path: string): Promise<boolean>;
67 > fsRealpath?(path: string): Promise<string>;
68 > }
69 >
70 > type TelemetryActivationEventFragment = {
71 > id: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'The identifier of an extension' };
72 > name: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'The name of the extension' };
73 > extensionVersion: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'The version of the extension' };
74 > publisherDisplayName: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The publisher of the extension' };
75 > activationEvents: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'All activation events of the extension' };
76 > isBuiltin: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'If the extension is builtin or git installed' };
77 > reason: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The activation event' };
78 > reasonId: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'The identifier of the activation event' };
79 > };
80 >
81 > export abstract class AbstractExtHostExtensionService extends Disposable implements ExtHostExtensionServiceShape {
82 >
83 > readonly _serviceBrand: undefined;
84 >
85 > abstract readonly extensionRuntime: ExtensionRuntime;
86 >
87 > private readonly _onDidChangeRemoteConnectionData = this._register(new Emitter<void>());
88 > public readonly onDidChangeRemoteConnectionData = this._onDidChangeRemoteConnectionData.event;
89 >
90 > protected readonly _hostUtils: IHostUtils;
91 > protected readonly _initData: IExtensionHostInitData;
92 > protected readonly _extHostContext: IExtHostRpcService;
93 > protected readonly _instaService: IInstantiationService;
94 > protected readonly _extHostWorkspace: ExtHostWorkspace;
95 > protected readonly _extHostConfiguration: ExtHostConfiguration;
96 > protected readonly _logService: ILogService;
97 > protected readonly _extHostTunnelService: IExtHostTunnelService;
98 > protected readonly _extHostTerminalService: IExtHostTerminalService;
99 > protected readonly _extHostLocalizationService: IExtHostLocalizationService;
100 >
101 > protected readonly _mainThreadWorkspaceProxy: MainThreadWorkspaceShape;
102 > protected readonly _mainThreadTelemetryProxy: MainThreadTelemetryShape;
103 > protected readonly _mainThreadExtensionsProxy: MainThreadExtensionServiceShape;
104 >
105 > private readonly _almostReadyToRunExtensions: Barrier;
106 > private readonly _readyToStartExtensionHost: Barrier;
107 > private readonly _readyToRunExtensions: Barrier;
108 > private readonly _eagerExtensionsActivated: Barrier;
109 >
110 > private readonly _activationEventsReader: SyncedActivationEventsReader;
111 > protected readonly _myRegistry: ExtensionDescriptionRegistry;
112 > protected readonly _globalRegistry: ExtensionDescriptionRegistry;
113 > private readonly _storage: ExtHostStorage;
114 > private readonly _secretState: ExtHostSecretState;
115 > private readonly _storagePath: IExtensionStoragePaths;
116 > private readonly _activator: ExtensionsActivator;
117 > private _extensionPathIndex: Promise<ExtensionPaths> | null;
118 > private _realPathCache = new Map<string, Promise<string>>();
119 >
120 > private readonly _resolvers: { [authorityPrefix: string]: vscode.RemoteAuthorityResolver };
121 >
122 > private _started: boolean;
123 > private _isTerminating: boolean = false;
124 > private _remoteConnectionData: IRemoteConnectionData | null;
125 >
126 > constructor(
127 @IInstantiationService instaService: IInstantiationService,
128 @IHostUtils hostUtils: IHostUtils,
129 @IExtHostRpcService extHostContext: IExtHostRpcService,
130 @IExtHostWorkspace extHostWorkspace: IExtHostWorkspace,
131 @IExtHostConfiguration extHostConfiguration: IExtHostConfiguration,
132 @ILogService logService: ILogService,
133 @IExtHostInitDataService initData: IExtHostInitDataService,
134 @IExtensionStoragePaths storagePath: IExtensionStoragePaths,
135 @IExtHostTunnelService extHostTunnelService: IExtHostTunnelService,
136 @IExtHostTerminalService extHostTerminalService: IExtHostTerminalService,
137 @IExtHostLocalizationService extHostLocalizationService: IExtHostLocalizationService,
138 @IExtHostManagedSockets private readonly _extHostManagedSockets: IExtHostManagedSockets,
139 @IExtHostLanguageModels private readonly _extHostLanguageModels: IExtHostLanguageModels,
140 ) {
141 super();
142 this._hostUtils = hostUtils;
143 this._extHostContext = extHostContext;
144 this._initData = initData;
145
146 this._extHostWorkspace = extHostWorkspace;
147 this._extHostConfiguration = extHostConfiguration;
148 this._logService = logService;
149 this._extHostTunnelService = extHostTunnelService;
150 this._extHostTerminalService = extHostTerminalService;
151 this._extHostLocalizationService = extHostLocalizationService;
152
153 this._mainThreadWorkspaceProxy = this._extHostContext.getProxy(MainContext.MainThreadWorkspace);
154 this._mainThreadTelemetryProxy = this._extHostContext.getProxy(MainContext.MainThreadTelemetry);
155 this._mainThreadExtensionsProxy = this._extHostContext.getProxy(MainContext.MainThreadExtensionService);
156
157 this._almostReadyToRunExtensions = new Barrier();
158 this._readyToStartExtensionHost = new Barrier();
159 this._readyToRunExtensions = new Barrier();
160 this._eagerExtensionsActivated = new Barrier();
161 this._activationEventsReader = new SyncedActivationEventsReader(this._initData.extensions.activationEvents);
162 this._globalRegistry = new ExtensionDescriptionRegistry(this._activationEventsReader, this._initData.extensions.allExtensions);
163 const myExtensionsSet = new ExtensionIdentifierSet(this._initData.extensions.myExtensions);
164 this._myRegistry = new ExtensionDescriptionRegistry(
165 this._activationEventsReader,
166 filterExtensions(this._globalRegistry, myExtensionsSet)
167 );
168
169 if (isCI) {
170 this._logService.info(`Creating extension host with the following global extensions: ${printExtIds(this._globalRegistry)}`);
171 this._logService.info(`Creating extension host with the following local extensions: ${printExtIds(this._myRegistry)}`);
172 }
173
174 this._storage = new ExtHostStorage(this._extHostContext, this._logService);
175 this._secretState = new ExtHostSecretState(this._extHostContext);
176 this._storagePath = storagePath;
177
178 this._instaService = this._store.add(instaService.createChild(new ServiceCollection(
179 [IExtHostStorage, this._storage],
180 [IExtHostSecretState, this._secretState]
181 )));
182
183 this._activator = this._register(new ExtensionsActivator(
184 this._myRegistry,
185 this._globalRegistry,
186 {
187 onExtensionActivationError: (extensionId: ExtensionIdentifier, error: Error, missingExtensionDependency: MissingExtensionDependency | null): void => {
188 this._mainThreadExtensionsProxy.$onExtensionActivationError(extensionId, errors.transformErrorForSerialization(error), missingExtensionDependency);
189 },
190
191 actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<ActivatedExtension> => {
192 if (ExtensionDescriptionRegistry.isHostExtension(extensionId, this._myRegistry, this._globalRegistry)) {
193 await this._mainThreadExtensionsProxy.$activateExtension(extensionId, reason);
194 return new HostExtension();
195 }
196 const extensionDescription = this._myRegistry.getExtensionDescription(extensionId)!;
197 return this._activateExtension(extensionDescription, reason);
198 }
199 },
200 this._logService
201 ));
202 this._extensionPathIndex = null;
203 this._resolvers = Object.create(null);
204 this._started = false;
205 this._remoteConnectionData = this._initData.remote.connectionData;
206
207 // report telemetry when an extension attempts to use a proposed API it is not entitled to use
208 this._register(setProposedApiUsageReporter(usage => this._reportProposedApiUsage(usage)));
209
210 // experiment: grant proposed API access to extension/proposal combinations that have not
211 // declared the proposal themselves (only takes effect on `stable`)
212 this._register(setEnabledApiProposalsFallbackExperiment(this._initData.enabledApiProposalsFallback, this._initData.quality));
213 }
215 > private _reportProposedApiUsage(usage: IProposedApiUsage): void {
216 type ProposedApiUsageClassification = {
217 owner: 'alexr00';
218 comment: 'An extension attempted to use a proposed API it has not been allowlisted to use.';
219 extensionId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The identifier of the extension attempting to use the proposed API.' };
220 proposalName: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the proposed API the extension is not entitled to use.' };
221 };
222 type ProposedApiUsageEvent = {
223 extensionId: string;
224 proposalName: string;
225 };
226 this._mainThreadTelemetryProxy.$publicLog2<ProposedApiUsageEvent, ProposedApiUsageClassification>('extensionProposedApiNotEnabled', {
227 extensionId: usage.extensionId,
228 proposalName: usage.proposalName
229 });
230 }
232 > public getRemoteConnectionData(): IRemoteConnectionData | null {
233 return this._remoteConnectionData;
234 }
236 > public async initialize(): Promise<void> {
237 try {
238
239 await this._beforeAlmostReadyToRunExtensions();
240 this._almostReadyToRunExtensions.open();
241
242 await this._extHostWorkspace.waitForInitializeCall();
243 performance.mark('code/extHost/ready');
244 this._readyToStartExtensionHost.open();
245
246 if (this._initData.autoStart) {
247 this._startExtensionHost();
248 }
249 } catch (err) {
250 errors.onUnexpectedError(err);
251 }
252 }
254 > private async _deactivateAll(): Promise<void> {
255 this._storagePath.onWillDeactivateAll();
256
257 let allPromises: Promise<void>[] = [];
258 try {
259 const allExtensions = this._myRegistry.getAllExtensionDescriptions();
260 const allExtensionsIds = allExtensions.map(ext => ext.identifier);
261 const activatedExtensions = allExtensionsIds.filter(id => this.isActivated(id));
262
263 allPromises = activatedExtensions.map((extensionId) => {
264 return this._deactivate(extensionId);
265 });
266 } catch (err) {
267 // TODO: write to log once we have one
268 }
269 await Promise.all(allPromises);
270 }
272 > public terminate(reason: string, code: number = 0): void {
273 if (this._isTerminating) {
274 // we are already shutting down...
275 return;
276 }
277 this._isTerminating = true;
278 this._logService.info(`Extension host terminating: ${reason}`);
279 this._logService.flush();
280
281 this._extHostTerminalService.dispose();
282 this._activator.dispose();
283
284 errors.setUnexpectedErrorHandler((err) => {
285 this._logService.error(err);
286 });
287
288 // Invalidate all proxies
289 this._extHostContext.dispose();
290
291 const extensionsDeactivated = this._deactivateAll();
292
293 // Give extensions at most 5 seconds to wrap up any async deactivate, then exit
294 Promise.race([timeout(5000), extensionsDeactivated]).finally(() => {
295 if (this._hostUtils.pid) {
296 this._logService.info(`Extension host with pid ${this._hostUtils.pid} exiting with code ${code}`);
297 } else {
298 this._logService.info(`Extension host exiting with code ${code}`);
299 }
300 this._logService.flush();
301 this._logService.dispose();
302 this._hostUtils.exit(code);
303 });
304 }
306 > public isActivated(extensionId: ExtensionIdentifier): boolean {
307 if (this._readyToRunExtensions.isOpen()) {
308 return this._activator.isActivated(extensionId);
309 }
310 return false;
311 }
313 > public async getExtension(extensionId: string): Promise<IExtensionDescription | undefined> {
314 const ext = await this._mainThreadExtensionsProxy.$getExtension(extensionId);
315 return ext && {
316 ...ext,
317 identifier: new ExtensionIdentifier(ext.identifier.value),
318 extensionLocation: URI.revive(ext.extensionLocation)
319 };
320 }
322 > private _activateByEvent(activationEvent: string, startup: boolean): Promise<void> {
323 return this._activator.activateByEvent(activationEvent, startup);
324 }
326 > private _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
327 return this._activator.activateById(extensionId, reason);
328 }
330 > public activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
331 return this._activateById(extensionId, reason).then(() => {
332 const extension = this._activator.getActivatedExtension(extensionId);
333 if (extension.activationFailed) {
334 // activation failed => bubble up the error as the promise result
335 return Promise.reject(extension.activationFailedError);
336 }
337 return undefined;
338 });
339 }
341 > public getExtensionRegistry(): Promise<ExtensionDescriptionRegistry> {
342 return this._readyToRunExtensions.wait().then(_ => this._myRegistry);
343 }
345 > public getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined {
346 if (this._readyToRunExtensions.isOpen()) {
347 return this._activator.getActivatedExtension(extensionId).exports;
348 } else {
349 try {
350 return this._activator.getActivatedExtension(extensionId).exports;
351 } catch (err) {
352 return null;
353 }
354 }
355 }
357 > /**
358 > * Applies realpath to file-uris and returns all others uris unmodified.
359 > * The real path is cached for the lifetime of the extension host.
360 > */
361 > private async _realPathExtensionUri(uri: URI): Promise<URI> {
362 if (uri.scheme === Schemas.file && this._hostUtils.fsRealpath) {
363 const fsPath = uri.fsPath;
364 if (!this._realPathCache.has(fsPath)) {
365 this._realPathCache.set(fsPath, this._hostUtils.fsRealpath(fsPath));
366 }
367 const realpathValue = await this._realPathCache.get(fsPath)!;
368 return URI.file(realpathValue);
369 }
370 return uri;
371 }
373 > // create trie to enable fast 'filename -> extension id' look up
374 > public async getExtensionPathIndex(): Promise<ExtensionPaths> {
375 if (!this._extensionPathIndex) {
376 this._extensionPathIndex = this._createExtensionPathIndex(this._myRegistry.getAllExtensionDescriptions()).then((searchTree) => {
377 return new ExtensionPaths(searchTree);
378 });
379 }
380 return this._extensionPathIndex;
381 }
383 > /**
384 > * create trie to enable fast 'filename -> extension id' look up
385 > */
386 > private async _createExtensionPathIndex(extensions: IExtensionDescription[]): Promise<TernarySearchTree<URI, IExtensionDescription>> {
387 const tst = TernarySearchTree.forUris<IExtensionDescription>(key => {
388 // using the default/biased extUri-util because the IExtHostFileSystemInfo-service
389 // isn't ready to be used yet, e.g the knowledge about `file` protocol and others
390 // comes in while this code runs
391 return extUriBiasedIgnorePathCase.ignorePathCasing(key);
392 });
393 // const tst = TernarySearchTree.forUris<IExtensionDescription>(key => true);
394 await Promise.all(extensions.map(async (ext) => {
395 if (this._getEntryPoint(ext)) {
396 const uri = await this._realPathExtensionUri(ext.extensionLocation);
397 tst.set(uri, ext);
398 }
399 }));
400 return tst;
401 }
403 > private _deactivate(extensionId: ExtensionIdentifier): Promise<void> {
404 let result = Promise.resolve(undefined);
405
406 if (!this._readyToRunExtensions.isOpen()) {
407 return result;
408 }
409
410 if (!this._activator.isActivated(extensionId)) {
411 return result;
412 }
413
414 const extension = this._activator.getActivatedExtension(extensionId);
415 if (!extension) {
416 return result;
417 }
418
419 // call deactivate if available
420 try {
421 if (typeof extension.module.deactivate === 'function') {
422 result = Promise.resolve(extension.module.deactivate()).then(undefined, (err) => {
423 this._logService.error(err);
424 return Promise.resolve(undefined);
425 });
426 }
427 } catch (err) {
428 this._logService.error(`An error occurred when deactivating the extension '${extensionId.value}':`);
429 this._logService.error(err);
430 }
431
432 // clean up subscriptions
433 try {
434 extension.disposable.dispose();
435 } catch (err) {
436 this._logService.error(`An error occurred when disposing the subscriptions for extension '${extensionId.value}':`);
437 this._logService.error(err);
438 }
439
440 return result;
441 }
443 > // --- impl
444 >
445 > private async _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
446 if (!this._initData.remote.isRemote) {
447 // local extension host process
448 await this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.identifier);
449 } else {
450 // remote extension host process
451 // do not wait for renderer confirmation
452 this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.identifier);
453 }
454 return this._doActivateExtension(extensionDescription, reason).then((activatedExtension) => {
455 const activationTimes = activatedExtension.activationTimes;
456 this._mainThreadExtensionsProxy.$onDidActivateExtension(extensionDescription.identifier, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, reason);
457 this._logExtensionActivationTimes(extensionDescription, reason, 'success', activationTimes);
458 return activatedExtension;
459 }, (err) => {
460 this._logExtensionActivationTimes(extensionDescription, reason, 'failure');
461 throw err;
462 });
463 }
465 > private _logExtensionActivationTimes(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason, outcome: string, activationTimes?: ExtensionActivationTimes) {
466 const event = getTelemetryActivationEvent(extensionDescription, reason);
467 type ExtensionActivationTimesClassification = {
468 owner: 'jrieken';
469 comment: 'Timestamps for extension activation';
470 outcome: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Did extension activation succeed or fail' };
471 } & TelemetryActivationEventFragment & ExtensionActivationTimesFragment;
472
473 type ExtensionActivationTimesEvent = {
474 outcome: string;
475 } & ActivationTimesEvent & TelemetryActivationEvent;
476
477 type ActivationTimesEvent = {
478 startup?: boolean;
479 codeLoadingTime?: number;
480 activateCallTime?: number;
481 activateResolvedTime?: number;
482 };
483
484 this._mainThreadTelemetryProxy.$publicLog2<ExtensionActivationTimesEvent, ExtensionActivationTimesClassification>('extensionActivationTimes', {
485 ...event,
486 ...(activationTimes || {}),
487 outcome
488 });
489 }
491 > private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
492 const event = getTelemetryActivationEvent(extensionDescription, reason);
493 type ActivatePluginClassification = {
494 owner: 'jrieken';
495 comment: 'Data about how/why an extension was activated';
496 } & TelemetryActivationEventFragment;
497 this._mainThreadTelemetryProxy.$publicLog2<TelemetryActivationEvent, ActivatePluginClassification>('activatePlugin', event);
498 const entryPoint = this._getEntryPoint(extensionDescription);
499 if (!entryPoint) {
500 // Treat the extension as being empty => NOT AN ERROR CASE
501 return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE));
502 }
503
504 this._logService.info(`ExtensionService#_doActivateExtension ${extensionDescription.identifier.value}, startup: ${reason.startup}, activationEvent: '${reason.activationEvent}'${extensionDescription.identifier.value !== reason.extensionId.value ? `, root cause: ${reason.extensionId.value}` : ``}`);
505 this._logService.flush();
506
507 const isESM = this._isESM(extensionDescription);
508
509 const extensionInternalStore = new DisposableStore(); // disposables that follow the extension lifecycle
510 const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup);
511 return Promise.all([
512 isESM
513 ? this._loadESMModule<IExtensionModule>(extensionDescription, joinPath(extensionDescription.extensionLocation, entryPoint), activationTimesBuilder)
514 : this._loadCommonJSModule<IExtensionModule>(extensionDescription, joinPath(extensionDescription.extensionLocation, entryPoint), activationTimesBuilder),
515 this._loadExtensionContext(extensionDescription, extensionInternalStore)
516 ]).then(values => {
517 performance.mark(`code/extHost/willActivateExtension/${extensionDescription.identifier.value}`);
518 return AbstractExtHostExtensionService._callActivate(this._logService, extensionDescription.identifier, values[0], values[1], extensionInternalStore, activationTimesBuilder);
519 }).then((activatedExtension) => {
520 performance.mark(`code/extHost/didActivateExtension/${extensionDescription.identifier.value}`);
521 return activatedExtension;
522 });
523 }
525 > private _loadExtensionContext(extensionDescription: IExtensionDescription, extensionInternalStore: DisposableStore): Promise<vscode.ExtensionContext> {
526
527 const languageModelAccessInformation = this._extHostLanguageModels.createLanguageModelAccessInformation(extensionDescription);
528 const globalState = extensionInternalStore.add(new ExtensionGlobalMemento(extensionDescription, this._storage));
529 const workspaceState = extensionInternalStore.add(new ExtensionMemento(extensionDescription.identifier.value, false, this._storage));
530 const secrets = extensionInternalStore.add(new ExtensionSecrets(extensionDescription, this._secretState));
531 const extensionMode = extensionDescription.isUnderDevelopment
532 ? (this._initData.environment.extensionTestsLocationURI ? ExtensionMode.Test : ExtensionMode.Development)
533 : ExtensionMode.Production;
534 const extensionKind = this._initData.remote.isRemote ? ExtensionKind.Workspace : ExtensionKind.UI;
535
536 this._logService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`);
537
538 return Promise.all([
539 globalState.whenReady,
540 workspaceState.whenReady,
541 this._storagePath.whenReady
542 ]).then(() => {
543 const that = this;
544 let extension: vscode.Extension<any> | undefined;
545
546 let messagePassingProtocol: vscode.MessagePassingProtocol | undefined;
547 const messagePort = isProposedApiEnabled(extensionDescription, 'ipc')
548 ? this._initData.messagePorts?.get(ExtensionIdentifier.toKey(extensionDescription.identifier))
549 : undefined;
550
551 return Object.freeze<vscode.ExtensionContext>({
552 globalState,
553 workspaceState,
554 secrets,
555 subscriptions: [],
556 get languageModelAccessInformation() { return languageModelAccessInformation; },
557 get extensionUri() { return extensionDescription.extensionLocation; },
558 get extensionPath() { return extensionDescription.extensionLocation.fsPath; },
559 asAbsolutePath(relativePath: string) { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); },
560 get storagePath() { return that._storagePath.workspaceValue(extensionDescription)?.fsPath; },
561 get globalStoragePath() { return that._storagePath.globalValue(extensionDescription).fsPath; },
562 get logPath() { return path.join(that._initData.logsLocation.fsPath, extensionDescription.identifier.value); },
563 get logUri() { return URI.joinPath(that._initData.logsLocation, extensionDescription.identifier.value); },
564 get storageUri() { return that._storagePath.workspaceValue(extensionDescription); },
565 get globalStorageUri() { return that._storagePath.globalValue(extensionDescription); },
566 get extensionMode() { return extensionMode; },
567 get extension() {
568 if (extension === undefined) {
569 extension = new Extension(that, extensionDescription.identifier, extensionDescription, extensionKind, false);
570 }
571 return extension;
572 },
573 get extensionRuntime() {
574 checkProposedApiEnabled(extensionDescription, 'extensionRuntime');
575 return that.extensionRuntime;
576 },
577 get environmentVariableCollection() { return that._extHostTerminalService.getEnvironmentVariableCollection(extensionDescription); },
578 get messagePassingProtocol() {
579 if (!messagePassingProtocol) {
580 if (!messagePort) {
581 return undefined;
582 }
583
584 const onDidReceiveMessage = Event.buffer(Event.fromDOMEventEmitter(messagePort, 'message', e => e.data), 'onDidReceiveMessage');
585 messagePort.start();
586 messagePassingProtocol = {
587 onDidReceiveMessage,
588 // eslint-disable-next-line local/code-no-any-casts
589 postMessage: messagePort.postMessage.bind(messagePort) as any
590 };
591 }
592
593 return messagePassingProtocol;
594 }
595 });
596 });
597 }
599 > private static _callActivate(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: vscode.ExtensionContext, extensionInternalStore: IDisposable, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<ActivatedExtension> {
600 // Make sure the extension's surface is not undefined
601 extensionModule = extensionModule || {
602 activate: undefined,
603 deactivate: undefined
604 };
605
606 return this._callActivateOptional(logService, extensionId, extensionModule, context, activationTimesBuilder).then((extensionExports) => {
607 return new ActivatedExtension(false, null, activationTimesBuilder.build(), extensionModule, extensionExports, toDisposable(() => {
608 extensionInternalStore.dispose();
609 dispose(context.subscriptions);
610 }));
611 });
612 }
614 > private static _callActivateOptional(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: vscode.ExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<IExtensionAPI> {
615 if (typeof extensionModule.activate === 'function') {
616 try {
617 activationTimesBuilder.activateCallStart();
618 logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`);
619 const activateResult: Promise<IExtensionAPI> = extensionModule.activate.apply(globalThis, [context]);
620 activationTimesBuilder.activateCallStop();
621
622 activationTimesBuilder.activateResolveStart();
623 return Promise.resolve(activateResult).then((value) => {
624 activationTimesBuilder.activateResolveStop();
625 return value;
626 });
627 } catch (err) {
628 return Promise.reject(err);
629 }
630 } else {
631 // No activate found => the module is the extension's exports
632 return Promise.resolve<IExtensionAPI>(extensionModule);
633 }
634 }
636 > // -- eager activation
637 >
638 > private _activateOneStartupFinished(desc: IExtensionDescription, activationEvent: string): void {
639 this._activateById(desc.identifier, {
640 startup: false,
641 extensionId: desc.identifier,
642 activationEvent: activationEvent
643 }).then(undefined, (err) => {
644 this._logService.error(err);
645 });
646 }
648 > private _activateAllStartupFinishedDeferred(extensions: IExtensionDescription[], start: number = 0): void {
649 const timeBudget = 50; // 50 milliseconds
650 const startTime = Date.now();
651
652 setTimeout0(() => {
653 for (let i = start; i < extensions.length; i += 1) {
654 const desc = extensions[i];
655 for (const activationEvent of (desc.activationEvents ?? [])) {
656 if (activationEvent === 'onStartupFinished') {
657 if (Date.now() - startTime > timeBudget) {
658 // time budget for current task has been exceeded
659 // set a new task to activate current and remaining extensions
660 this._activateAllStartupFinishedDeferred(extensions, i);
661 break;
662 } else {
663 this._activateOneStartupFinished(desc, activationEvent);
664 }
665 }
666 }
667 }
668 });
669 }
671 > private _activateAllStartupFinished(): void {
672 // startup is considered finished
673 this._mainThreadExtensionsProxy.$setPerformanceMarks(performance.getMarks());
674
675 this._extHostConfiguration.getConfigProvider().then((configProvider) => {
676 const shouldDeferActivation = configProvider.getConfiguration('extensions.experimental').get<boolean>('deferredStartupFinishedActivation');
677 const allExtensionDescriptions = this._myRegistry.getAllExtensionDescriptions();
678 if (shouldDeferActivation) {
679 this._activateAllStartupFinishedDeferred(allExtensionDescriptions);
680 } else {
681 for (const desc of allExtensionDescriptions) {
682 if (desc.activationEvents) {
683 for (const activationEvent of desc.activationEvents) {
684 if (activationEvent === 'onStartupFinished') {
685 this._activateOneStartupFinished(desc, activationEvent);
686 }
687 }
688 }
689 }
690 }
691 });
692 }
694 > // Handle "eager" activation extensions
695 > private _handleEagerExtensions(): Promise<void> {
696 const starActivation = this._activateByEvent('*', true).then(undefined, (err) => {
697 this._logService.error(err);
698 });
699
700 this._register(this._extHostWorkspace.onDidChangeWorkspace((e) => this._handleWorkspaceContainsEagerExtensions(e.added)));
701 const folders = this._extHostWorkspace.workspace ? this._extHostWorkspace.workspace.folders : [];
702 const workspaceContainsActivation = this._handleWorkspaceContainsEagerExtensions(folders);
703 const remoteResolverActivation = this._handleRemoteResolverEagerExtensions();
704 const eagerExtensionsActivation = Promise.all([remoteResolverActivation, starActivation, workspaceContainsActivation]).then(() => { });
705
706 Promise.race([eagerExtensionsActivation, timeout(10000)]).then(() => {
707 this._activateAllStartupFinished();
708 });
709
710 return eagerExtensionsActivation;
711 }
713 > private _handleWorkspaceContainsEagerExtensions(folders: ReadonlyArray<vscode.WorkspaceFolder>): Promise<void> {
714 if (folders.length === 0) {
715 return Promise.resolve(undefined);
716 }
717
718 return Promise.all(
719 this._myRegistry.getAllExtensionDescriptions().map((desc) => {
720 return this._handleWorkspaceContainsEagerExtension(folders, desc);
721 })
722 ).then(() => { });
723 }
725 > private async _handleWorkspaceContainsEagerExtension(folders: ReadonlyArray<vscode.WorkspaceFolder>, desc: IExtensionDescription): Promise<void> {
726 if (this.isActivated(desc.identifier)) {
727 return;
728 }
729
730 const localWithRemote = !this._initData.remote.isRemote && !!this._initData.remote.authority;
731 const host: IExtensionActivationHost = {
732 logService: this._logService,
733 folders: folders.map(folder => folder.uri),
734 forceUsingSearch: localWithRemote || !this._hostUtils.fsExists,
735 exists: (uri) => this._hostUtils.fsExists!(uri.fsPath),
736 checkExists: (folders, includes, token) => this._mainThreadWorkspaceProxy.$checkExists(folders, includes, token)
737 };
738
739 const result = await checkActivateWorkspaceContainsExtension(host, desc);
740 if (!result) {
741 return;
742 }
743
744 return (
745 this._activateById(desc.identifier, { startup: true, extensionId: desc.identifier, activationEvent: result.activationEvent })
746 .then(undefined, err => this._logService.error(err))
747 );
748 }
750 > private async _handleRemoteResolverEagerExtensions(): Promise<void> {
751 if (this._initData.remote.authority) {
752 return this._activateByEvent(`onResolveRemoteAuthority:${this._initData.remote.authority}`, false);
753 }
754 }
756 > public async $extensionTestsExecute(): Promise<number> {
757 await this._eagerExtensionsActivated.wait();
758 try {
759 return await this._doHandleExtensionTests();
760 } catch (error) {
761 console.error(error); // ensure any error message makes it onto the console
762 throw error;
763 }
764 }
766 > private async _doHandleExtensionTests(): Promise<number> {
767 const { extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment;
768 if (!extensionDevelopmentLocationURI || !extensionTestsLocationURI) {
769 throw new Error(nls.localize('extensionTestError1', "Cannot load test runner."));
770 }
771
772 const extensionDescription = (await this.getExtensionPathIndex()).findSubstr(extensionTestsLocationURI);
773 const isESM = this._isESM(extensionDescription, extensionTestsLocationURI.path);
774
775 // Require the test runner via node require from the provided path
776 const testRunner = await (isESM
777 ? this._loadESMModule<ITestRunner | INewTestRunner | undefined>(null, extensionTestsLocationURI, new ExtensionActivationTimesBuilder(false))
778 : this._loadCommonJSModule<ITestRunner | INewTestRunner | undefined>(null, extensionTestsLocationURI, new ExtensionActivationTimesBuilder(false)));
779
780 if (!testRunner || typeof testRunner.run !== 'function') {
781 throw new Error(nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsLocationURI.toString()));
782 }
783
784 // Execute the runner if it follows the old `run` spec
785 return new Promise<number>((resolve, reject) => {
786 const oldTestRunnerCallback = (error: Error, failures: number | undefined) => {
787 if (error) {
788 if (isCI) {
789 this._logService.error(`Test runner called back with error`, error);
790 }
791 reject(error);
792 } else {
793 if (isCI) {
794 if (failures) {
795 this._logService.info(`Test runner called back with ${failures} failures.`);
796 } else {
797 this._logService.info(`Test runner called back with successful outcome.`);
798 }
799 }
800 resolve((typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);
801 }
802 };
803
804 const extensionTestsPath = originalFSPath(extensionTestsLocationURI); // for the old test runner API
805
806 const runResult = testRunner.run(extensionTestsPath, oldTestRunnerCallback);
807
808 // Using the new API `run(): Promise<void>`
809 if (runResult && runResult.then) {
810 runResult
811 .then(() => {
812 if (isCI) {
813 this._logService.info(`Test runner finished successfully.`);
814 }
815 resolve(0);
816 })
817 .catch((err: unknown) => {
818 if (isCI) {
819 this._logService.error(`Test runner finished with error`, err);
820 }
821 reject(err instanceof Error && err.stack ? err.stack : String(err));
822 });
823 }
824 });
825 }
827 > private _startExtensionHost(): Promise<void> {
828 if (this._started) {
829 throw new Error(`Extension host is already started!`);
830 }
831 this._started = true;
832
833 return this._readyToStartExtensionHost.wait()
834 .then(() => this._readyToRunExtensions.open())
835 .then(() => {
836 // wait for all activation events that came in during workbench startup, but at maximum 1s
837 return Promise.race([this._activator.waitForActivatingExtensions(), timeout(1000)]);
838 })
839 .then(() => this._handleEagerExtensions())
840 .then(() => {
841 this._eagerExtensionsActivated.open();
842 this._logService.info(`Eager extensions activated`);
843 });
844 }
846 > // -- called by extensions
847 >
848 > public registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable {
849 this._resolvers[authorityPrefix] = resolver;
850 return toDisposable(() => {
851 delete this._resolvers[authorityPrefix];
852 });
853 }
855 > public async getRemoteExecServer(remoteAuthority: string): Promise<vscode.ExecServer | undefined> {
856 const { resolver } = await this._activateAndGetResolver(remoteAuthority);
857 return resolver?.resolveExecServer?.(remoteAuthority, { resolveAttempt: 0 });
858 }
860 > // -- called by main thread
861 >
862 > private async _activateAndGetResolver(remoteAuthority: string): Promise<{ authorityPrefix: string; resolver: vscode.RemoteAuthorityResolver | undefined }> {
863 const authorityPlusIndex = remoteAuthority.indexOf('+');
864 if (authorityPlusIndex === -1) {
865 throw new RemoteAuthorityResolverError(`Not an authority that can be resolved!`, RemoteAuthorityResolverErrorCode.InvalidAuthority);
866 }
867 const authorityPrefix = remoteAuthority.substr(0, authorityPlusIndex);
868
869 await this._almostReadyToRunExtensions.wait();
870 await this._activateByEvent(`onResolveRemoteAuthority:${authorityPrefix}`, false);
871
872 return { authorityPrefix, resolver: this._resolvers[authorityPrefix] };
873 }
875 > public async $resolveAuthority(remoteAuthorityChain: string, resolveAttempt: number): Promise<Dto<IResolveAuthorityResult>> {
876 const sw = StopWatch.create(false);
877 const prefix = () => `[resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthorityChain)},${resolveAttempt})][${sw.elapsed()}ms] `;
878 const logInfo = (msg: string) => this._logService.info(`${prefix()}${msg}`);
879 const logWarning = (msg: string) => this._logService.warn(`${prefix()}${msg}`);
880 const logError = (msg: string, err: any = undefined) => this._logService.error(`${prefix()}${msg}`, err);
881 const normalizeError = (err: unknown) => {
882 if (err instanceof RemoteAuthorityResolverError) {
883 return {
884 type: 'error' as const,
885 error: {
886 code: err._code,
887 message: err._message,
888 detail: err._detail
889 }
890 };
891 }
892 throw err;
893 };
894
895 const getResolver = async (remoteAuthority: string) => {
896 logInfo(`activating resolver for ${remoteAuthority}...`);
897 const { resolver, authorityPrefix } = await this._activateAndGetResolver(remoteAuthority);
898 if (!resolver) {
899 logError(`no resolver for ${authorityPrefix}`);
900 throw new RemoteAuthorityResolverError(`No remote extension installed to resolve ${authorityPrefix}.`, RemoteAuthorityResolverErrorCode.NoResolverFound);
901 }
902 return { resolver, authorityPrefix, remoteAuthority };
903 };
904
905 const chain = remoteAuthorityChain.split(/@|%40/g).reverse();
906 logInfo(`activating remote resolvers ${chain.join(' -> ')}`);
907
908 let resolvers;
909 try {
910 resolvers = await Promise.all(chain.map(getResolver)).catch(async (e: Error) => {
911 if (!(e instanceof RemoteAuthorityResolverError) || e._code !== RemoteAuthorityResolverErrorCode.InvalidAuthority) { throw e; }
912 logWarning(`resolving nested authorities failed: ${e.message}`);
913 return [await getResolver(remoteAuthorityChain)];
914 });
915 } catch (e) {
916 return normalizeError(e);
917 }
918
919 const intervalLogger = new IntervalTimer();
920 intervalLogger.cancelAndSet(() => logInfo('waiting...'), 1000);
921
922 let result!: vscode.ResolverResult;
923 let execServer: vscode.ExecServer | undefined;
924 for (const [i, { authorityPrefix, resolver, remoteAuthority }] of resolvers.entries()) {
925 try {
926 if (i === resolvers.length - 1) {
927 logInfo(`invoking final resolve()...`);
928 performance.mark(`code/extHost/willResolveAuthority/${authorityPrefix}`);
929 result = await resolver.resolve(remoteAuthority, { resolveAttempt, execServer });
930 performance.mark(`code/extHost/didResolveAuthorityOK/${authorityPrefix}`);
931 logInfo(`setting tunnel factory...`);
932 this._register(await this._extHostTunnelService.setTunnelFactory(
933 resolver,
934 ExtHostManagedResolvedAuthority.isManagedResolvedAuthority(result) ? result : undefined
935 ));
936 } else {
937 logInfo(`invoking resolveExecServer() for ${remoteAuthority}`);
938 performance.mark(`code/extHost/willResolveExecServer/${authorityPrefix}`);
939 execServer = await resolver.resolveExecServer?.(remoteAuthority, { resolveAttempt, execServer });
940 if (!execServer) {
941 throw new RemoteAuthorityResolverError(`Exec server was not available for ${remoteAuthority}`, RemoteAuthorityResolverErrorCode.NoResolverFound); // we did, in fact, break the chain :(
942 }
943 performance.mark(`code/extHost/didResolveExecServerOK/${authorityPrefix}`);
944 }
945 } catch (e) {
946 performance.mark(`code/extHost/didResolveAuthorityError/${authorityPrefix}`);
947 logError(`returned an error`, e);
948 intervalLogger.dispose();
949 return normalizeError(e);
950 }
951 }
952
953 intervalLogger.dispose();
954
955 const tunnelInformation: TunnelInformation = {
956 environmentTunnels: result.environmentTunnels,
957 features: result.tunnelFeatures ? {
958 elevation: result.tunnelFeatures.elevation,
959 privacyOptions: result.tunnelFeatures.privacyOptions,
960 protocol: result.tunnelFeatures.protocol === undefined ? true : result.tunnelFeatures.protocol,
961 } : undefined
962 };
963
964 // Split merged API result into separate authority/options
965 const options: ResolvedOptions = {
966 extensionHostEnv: result.extensionHostEnv,
967 isTrusted: result.isTrusted,
968 authenticationSession: result.authenticationSessionForInitializingExtensions ? { id: result.authenticationSessionForInitializingExtensions.id, providerId: result.authenticationSessionForInitializingExtensions.providerId } : undefined
969 };
970
971 // extension are not required to return an instance of ResolvedAuthority or ManagedResolvedAuthority, so don't use `instanceof`
972 logInfo(`returned ${ExtHostManagedResolvedAuthority.isManagedResolvedAuthority(result) ? 'managed authority' : `${result.host}:${result.port}`}`);
973
974 let authority: ResolvedAuthority;
975 if (ExtHostManagedResolvedAuthority.isManagedResolvedAuthority(result)) {
976 // The socket factory is identified by the `resolveAttempt`, since that is a number which
977 // always increments and is unique over all resolve() calls in a workbench session.
978 const socketFactoryId = resolveAttempt;
979
980 // There is only on managed socket factory at a time, so we can just overwrite the old one.
981 this._extHostManagedSockets.setFactory(socketFactoryId, result.makeConnection);
982
983 authority = {
984 authority: remoteAuthorityChain,
985 connectTo: new ManagedRemoteConnection(socketFactoryId),
986 connectionToken: result.connectionToken
987 };
988 } else {
989 authority = {
990 authority: remoteAuthorityChain,
991 connectTo: new WebSocketRemoteConnection(result.host, result.port),
992 connectionToken: result.connectionToken
993 };
994 }
995
996 return {
997 type: 'ok',
998 value: {
999 authority: authority as Dto<ResolvedAuthority>,
1000 options,
1001 tunnelInformation,
1002 }
1003 };
1004 }
1006 > public async $getCanonicalURI(remoteAuthority: string, uriComponents: UriComponents): Promise<UriComponents | null> {
1007 this._logService.info(`$getCanonicalURI invoked for authority (${getRemoteAuthorityPrefix(remoteAuthority)})`);
1008
1009 const { resolver } = await this._activateAndGetResolver(remoteAuthority);
1010 if (!resolver) {
1011 // Return `null` if no resolver for `remoteAuthority` is found.
1012 return null;
1013 }
1014
1015 const uri = URI.revive(uriComponents);
1016
1017 if (typeof resolver.getCanonicalURI === 'undefined') {
1018 // resolver cannot compute canonical URI
1019 return uri;
1020 }
1021
1022 const result = await asPromise(() => resolver.getCanonicalURI!(uri));
1023 if (!result) {
1024 return uri;
1025 }
1026
1027 return result;
1028 }
1030 > public async $startExtensionHost(extensionsDelta: IExtensionDescriptionDelta): Promise<void> {
1031 // eslint-disable-next-line local/code-no-any-casts
1032 extensionsDelta.toAdd.forEach((extension) => (<any>extension).extensionLocation = URI.revive(extension.extensionLocation));
1033
1034 const { globalRegistry, myExtensions } = applyExtensionsDelta(this._activationEventsReader, this._globalRegistry, this._myRegistry, extensionsDelta);
1035 const newSearchTree = await this._createExtensionPathIndex(myExtensions);
1036 const extensionsPaths = await this.getExtensionPathIndex();
1037 extensionsPaths.setSearchTree(newSearchTree);
1038 this._globalRegistry.set(globalRegistry.getAllExtensionDescriptions());
1039 this._myRegistry.set(myExtensions);
1040
1041 if (isCI) {
1042 this._logService.info(`$startExtensionHost: global extensions: ${printExtIds(this._globalRegistry)}`);
1043 this._logService.info(`$startExtensionHost: local extensions: ${printExtIds(this._myRegistry)}`);
1044 }
1045
1046 return this._startExtensionHost();
1047 }
1049 > public $activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void> {
1050 if (activationKind === ActivationKind.Immediate) {
1051 return this._almostReadyToRunExtensions.wait()
1052 .then(_ => this._activateByEvent(activationEvent, false));
1053 }
1054
1055 return (
1056 this._readyToRunExtensions.wait()
1057 .then(_ => this._activateByEvent(activationEvent, false))
1058 );
1059 }
1061 > public async $activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean> {
1062 await this._readyToRunExtensions.wait();
1063 if (!this._myRegistry.getExtensionDescription(extensionId)) {
1064 // unknown extension => ignore
1065 return false;
1066 }
1067 await this._activateById(extensionId, reason);
1068 return true;
1069 }
1071 > public async $deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise<void> {
1072 // eslint-disable-next-line local/code-no-any-casts
1073 extensionsDelta.toAdd.forEach((extension) => (<any>extension).extensionLocation = URI.revive(extension.extensionLocation));
1074
1075 // First build up and update the trie and only afterwards apply the delta
1076 const { globalRegistry, myExtensions } = applyExtensionsDelta(this._activationEventsReader, this._globalRegistry, this._myRegistry, extensionsDelta);
1077 const newSearchTree = await this._createExtensionPathIndex(myExtensions);
1078 const extensionsPaths = await this.getExtensionPathIndex();
1079 extensionsPaths.setSearchTree(newSearchTree);
1080 this._globalRegistry.set(globalRegistry.getAllExtensionDescriptions());
1081 this._myRegistry.set(myExtensions);
1082
1083 if (isCI) {
1084 this._logService.info(`$deltaExtensions: global extensions: ${printExtIds(this._globalRegistry)}`);
1085 this._logService.info(`$deltaExtensions: local extensions: ${printExtIds(this._myRegistry)}`);
1086 }
1087
1088 return Promise.resolve(undefined);
1089 }
1091 > public async $test_latency(n: number): Promise<number> {
1092 return n;
1093 }
1095 > public async $test_up(b: VSBuffer): Promise<number> {
1096 return b.byteLength;
1097 }
1099 > public async $test_down(size: number): Promise<VSBuffer> {
1100 const buff = VSBuffer.alloc(size);
1101 const value = Math.random() % 256;
1102 for (let i = 0; i < size; i++) {
1103 buff.writeUInt8(value, i);
1104 }
1105 return buff;
1106 }
1108 > public async $updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise<void> {
1109 this._remoteConnectionData = connectionData;
1110 this._onDidChangeRemoteConnectionData.fire();
1111 }
1113 > protected _isESM(extensionDescription: IExtensionDescription | undefined, modulePath?: string): boolean {
1114 modulePath ??= extensionDescription ? this._getEntryPoint(extensionDescription) : modulePath;
1115 return modulePath?.endsWith('.mjs') || (extensionDescription?.type === 'module' && !modulePath?.endsWith('.cjs'));
1116 }
1118 > protected abstract _beforeAlmostReadyToRunExtensions(): Promise<void>;
1119 > protected abstract _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined;
1120 > protected abstract _loadCommonJSModule<T extends object | undefined>(extensionId: IExtensionDescription | null, module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T>;
1121 > protected abstract _loadESMModule<T>(extension: IExtensionDescription | null, module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T>;
1122 > public abstract $setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void>;
1123 > }
1124 >
1125 function applyExtensionsDelta(activationEventsReader: SyncedActivationEventsReader, oldGlobalRegistry: ExtensionDescriptionRegistry, oldMyRegistry: ExtensionDescriptionRegistry, extensionsDelta: IExtensionDescriptionDelta) {
1126 activationEventsReader.addActivationEvents(extensionsDelta.addActivationEvents);
1127 const globalRegistry = new ExtensionDescriptionRegistry(activationEventsReader, oldGlobalRegistry.getAllExtensionDescriptions());
1128 globalRegistry.deltaExtensions(extensionsDelta.toAdd, extensionsDelta.toRemove);
1129
1130 const myExtensionsSet = new ExtensionIdentifierSet(oldMyRegistry.getAllExtensionDescriptions().map(extension => extension.identifier));
1131 for (const extensionId of extensionsDelta.myToRemove) {
1132 myExtensionsSet.delete(extensionId);
1133 }
1134 for (const extensionId of extensionsDelta.myToAdd) {
1135 myExtensionsSet.add(extensionId);
1136 }
1137 const myExtensions = filterExtensions(globalRegistry, myExtensionsSet);
1138
1139 return { globalRegistry, myExtensions };
1140 }
1142 > type TelemetryActivationEvent = {
1143 > id: string;
1144 > name: string;
1145 > extensionVersion: string;
1146 > publisherDisplayName: string;
1147 > activationEvents: string | null;
1148 > isBuiltin: boolean;
1149 > reason: string;
1150 > reasonId: string;
1151 > };
1152 >
1153 function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TelemetryActivationEvent {
1154 const event = {
1155 id: extensionDescription.identifier.value,
1156 name: extensionDescription.name,
1157 extensionVersion: extensionDescription.version,
1158 publisherDisplayName: extensionDescription.publisher,
1159 activationEvents: extensionDescription.activationEvents ? extensionDescription.activationEvents.join(',') : null,
1160 isBuiltin: extensionDescription.isBuiltin,
1161 reason: reason.activationEvent,
1162 reasonId: reason.extensionId.value,
1163 };
1164
1165 return event;
1166 }
1168 function printExtIds(registry: ExtensionDescriptionRegistry) {
1169 return registry.getAllExtensionDescriptions().map(ext => ext.identifier.value).join(',');
1170 }
1172 > export const IExtHostExtensionService = createDecorator<IExtHostExtensionService>('IExtHostExtensionService');
1173 >
1174 > export interface IExtHostExtensionService extends AbstractExtHostExtensionService {
1175 > readonly _serviceBrand: undefined;
1176 > initialize(): Promise<void>;
1177 > terminate(reason: string): void;
1178 > getExtension(extensionId: string): Promise<IExtensionDescription | undefined>;
1179 > isActivated(extensionId: ExtensionIdentifier): boolean;
1180 > activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;
1181 > getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined;
1182 > getExtensionRegistry(): Promise<ExtensionDescriptionRegistry>;
1183 > getExtensionPathIndex(): Promise<ExtensionPaths>;
1184 > registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable;
1185 > getRemoteExecServer(authority: string): Promise<vscode.ExecServer | undefined>;
1186 >
1187 > readonly onDidChangeRemoteConnectionData: Event<void>;
1188 > getRemoteConnectionData(): IRemoteConnectionData | null;
1189 > }
1190 >
1191 > export class Extension<T extends object | null | undefined> implements vscode.Extension<T> {
1192 >
1193 > #extensionService: IExtHostExtensionService;
1194 #originExtensionId: ExtensionIdentifier;
1195 #identifier: ExtensionIdentifier;
1197 > readonly id: string;
1198 > readonly extensionUri: URI;
1199 > readonly extensionPath: string;
1200 > readonly packageJSON: IExtensionDescription;
1201 > readonly extensionKind: vscode.ExtensionKind;
1202 > readonly isFromDifferentExtensionHost: boolean;
1203 >
1204 > constructor(extensionService: IExtHostExtensionService, originExtensionId: ExtensionIdentifier, description: IExtensionDescription, kind: ExtensionKind, isFromDifferentExtensionHost: boolean) {
1205 this.#extensionService = extensionService;
1206 this.#originExtensionId = originExtensionId;
1207 this.#identifier = description.identifier;
1208 this.id = description.identifier.value;
1209 this.extensionUri = description.extensionLocation;
1210 this.extensionPath = path.normalize(originalFSPath(description.extensionLocation));
1211 this.packageJSON = description;
1212 this.extensionKind = kind;
1213 this.isFromDifferentExtensionHost = isFromDifferentExtensionHost;
1214 }
1216 > get isActive(): boolean {
1217 // TODO@alexdima support this
1218 return this.#extensionService.isActivated(this.#identifier);
1219 }
1221 > get exports(): T {
1222 if (this.packageJSON.api === 'none' || this.isFromDifferentExtensionHost) {
1223 return undefined!; // Strict nulloverride - Public api
1224 }
1225 return <T>this.#extensionService.getExtensionExports(this.#identifier);
1226 }
1228 > async activate(): Promise<T> {
1229 if (this.isFromDifferentExtensionHost) {
1230 throw new Error('Cannot activate foreign extension'); // TODO@alexdima support this
1231 }
1232 await this.#extensionService.activateByIdWithErrors(this.#identifier, { startup: false, extensionId: this.#originExtensionId, activationEvent: 'api' });
1233 return this.exports;
1234 }
1236 >
1237 function filterExtensions(globalRegistry: ExtensionDescriptionRegistry, desiredExtensions: ExtensionIdentifierSet): IExtensionDescription[] {
1238 return globalRegistry.getAllExtensionDescriptions().filter(
1239 extension => desiredExtensions.has(extension.identifier)
1240 );
1241 }
1243 > export class ExtensionPaths {
1244 >
1245 > constructor(
1246 > private _searchTree: TernarySearchTree<URI, IExtensionDescription> extensionHostMain.ts ×11
1247 > ) { }
1249 > setSearchTree(searchTree: TernarySearchTree<URI, IExtensionDescription>): void {
1250 this._searchTree = searchTree;
1251 }
1253 > findSubstr(key: URI): IExtensionDescription | undefined {
1254 return this._searchTree.findSubstr(key);
1255 }
1257 > forEach(callback: (value: IExtensionDescription, index: URI) => any): void {
1258 return this._searchTree.forEach(callback);
1259 }
1261 >
1262 > /**
1263 > * This mirrors the activation events as seen by the renderer. The renderer
1264 > * is the only one which can have a reliable view of activation events because
1265 > * implicit activation events are generated via extension points, and they
1266 > * are registered only on the renderer side.
1267 > */
1268 > class SyncedActivationEventsReader implements IActivationEventsReader {
1269 >
1270 > private readonly _map = new ExtensionIdentifierMap<string[]>();
1271 >
1272 > constructor(activationEvents: { [extensionId: string]: string[] }) {
1273 this.addActivationEvents(activationEvents);
1274 }
1276 > public readActivationEvents(extensionDescription: IExtensionDescription): string[] {
1277 return this._map.get(extensionDescription.identifier) ?? [];
1278 }
1280 > public addActivationEvents(activationEvents: { [extensionId: string]: string[] }): void {
1281 for (const extensionId of Object.keys(activationEvents)) {
1282 this._map.set(extensionId, activationEvents[extensionId]);
1283 }
1284 }