extHostExtensionActivator.ts ×33

Frontier kind: Code frontier

unlabeled · c_666882659cf0

42 tests · 14647 LOC · 61 files · introduces 0 tests · 176 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
33 ranges176 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
1907 ranges14647 lines · 61 files · Browse complete extent
All tests (intent)
42 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

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 native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

1 file ranked by introduced lines: 176 introduced LOC across 33 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/workbench/api/common/extHostExtensionActivator.ts 176 introduced LOC · 33 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostExtensionActivator.ts
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 type * as vscode from 'vscode';
7 > import * as errors from '../../../base/common/errors.js';
8 > import { Disposable, IDisposable } from '../../../base/common/lifecycle.js';
9 > import { ExtensionDescriptionRegistry } from '../../services/extensions/common/extensionDescriptionRegistry.js';
10 > import { ExtensionIdentifier, ExtensionIdentifierMap } from '../../../platform/extensions/common/extensions.js';
11 > import { ExtensionActivationReason, MissingExtensionDependency } from '../../services/extensions/common/extensions.js';
12 > import { ILogService } from '../../../platform/log/common/log.js';
13 > import { Barrier } from '../../../base/common/async.js';
14 >
15 > /**
16 > * Represents the source code (module) of an extension.
17 > */
18 > export interface IExtensionModule {
19 > activate?(ctx: vscode.ExtensionContext): Promise<IExtensionAPI>;
20 > deactivate?(): void;
21 > }
22 >
23 > /**
24 > * Represents the API of an extension (return value of `activate`).
25 > */
26 > export interface IExtensionAPI {
27 > // _extensionAPIBrand: any;
28 > }
29 >
30 > export type ExtensionActivationTimesFragment = {
31 > startup?: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Activation occurred during startup' };
32 > codeLoadingTime?: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Time it took to load the extension\'s code' };
33 > activateCallTime?: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Time it took to call activate' };
34 > activateResolvedTime?: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Time it took for async-activation to finish' };
35 > };
36 >
37 > export class ExtensionActivationTimes {
38 >
39 > public static readonly NONE = new ExtensionActivationTimes(false, -1, -1, -1);
40 >
41 > public readonly startup: boolean;
42 > public readonly codeLoadingTime: number;
43 > public readonly activateCallTime: number;
44 > public readonly activateResolvedTime: number;
45 >
46 > constructor(startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number) {
47 > this.startup = startup;
48 > this.codeLoadingTime = codeLoadingTime;
49 > this.activateCallTime = activateCallTime;
50 > this.activateResolvedTime = activateResolvedTime;
51 > }
52 > }
53 >
54 > export class ExtensionActivationTimesBuilder {
55 >
56 > private readonly _startup: boolean;
57 > private _codeLoadingStart: number;
58 > private _codeLoadingStop: number;
59 > private _activateCallStart: number;
60 > private _activateCallStop: number;
61 > private _activateResolveStart: number;
62 > private _activateResolveStop: number;
63 >
64 > constructor(startup: boolean) {
65 this._startup = startup;
66 this._codeLoadingStart = -1;
71 this._activateResolveStop = -1;
72 }
74 > private _delta(start: number, stop: number): number {
75 if (start === -1 || stop === -1) {
76 return -1;
78 return stop - start;
79 }
81 > public build(): ExtensionActivationTimes {
82 return new ExtensionActivationTimes(
83 this._startup,
87 );
88 }
90 > public codeLoadingStart(): void {
91 this._codeLoadingStart = Date.now();
92 }
94 > public codeLoadingStop(): void {
95 this._codeLoadingStop = Date.now();
96 }
98 > public activateCallStart(): void {
99 this._activateCallStart = Date.now();
100 }
102 > public activateCallStop(): void {
103 this._activateCallStop = Date.now();
104 }
106 > public activateResolveStart(): void {
107 this._activateResolveStart = Date.now();
108 }
110 > public activateResolveStop(): void {
111 this._activateResolveStop = Date.now();
112 }
114 >
115 > export class ActivatedExtension {
116 >
117 > public readonly activationFailed: boolean;
118 > public readonly activationFailedError: Error | null;
119 > public readonly activationTimes: ExtensionActivationTimes;
120 > public readonly module: IExtensionModule;
121 > public readonly exports: IExtensionAPI | undefined;
122 > public readonly disposable: IDisposable;
123 >
124 > constructor(
125 activationFailed: boolean,
126 activationFailedError: Error | null,
137 this.disposable = disposable;
138 }
140 >
141 > export class EmptyExtension extends ActivatedExtension {
142 > constructor(activationTimes: ExtensionActivationTimes) {
143 super(false, null, activationTimes, { activate: undefined, deactivate: undefined }, undefined, Disposable.None);
144 }
146 >
147 > export class HostExtension extends ActivatedExtension {
148 > constructor() {
149 super(false, null, ExtensionActivationTimes.NONE, { activate: undefined, deactivate: undefined }, undefined, Disposable.None);
150 }
152 >
153 > class FailedExtension extends ActivatedExtension {
154 > constructor(activationError: Error) {
155 super(true, activationError, ExtensionActivationTimes.NONE, { activate: undefined, deactivate: undefined }, undefined, Disposable.None);
156 }
158 >
159 > export interface IExtensionsActivatorHost {
160 > onExtensionActivationError(extensionId: ExtensionIdentifier, error: Error | null, missingExtensionDependency: MissingExtensionDependency | null): void;
161 > actualActivateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<ActivatedExtension>;
162 > }
163 >
164 > type ActivationIdAndReason = { id: ExtensionIdentifier; reason: ExtensionActivationReason };
165 >
166 > export class ExtensionsActivator implements IDisposable {
167 >
168 > private readonly _registry: ExtensionDescriptionRegistry;
169 > private readonly _globalRegistry: ExtensionDescriptionRegistry;
170 > private readonly _host: IExtensionsActivatorHost;
171 > private readonly _operations: ExtensionIdentifierMap<ActivationOperation>;
172 > /**
173 > * A map of already activated events to speed things up if the same activation event is triggered multiple times.
174 > */
175 > private readonly _alreadyActivatedEvents: { [activationEvent: string]: boolean };
176 >
177 > constructor(
178 registry: ExtensionDescriptionRegistry,
179 globalRegistry: ExtensionDescriptionRegistry,
187 this._alreadyActivatedEvents = Object.create(null);
188 }
190 > public dispose(): void {
191 for (const [_, op] of this._operations) {
192 op.dispose();
193 }
194 }
196 > public async waitForActivatingExtensions(): Promise<void> {
197 const res: Promise<boolean>[] = [];
198 for (const [_, op] of this._operations) {
201 await Promise.all(res);
202 }
204 > public isActivated(extensionId: ExtensionIdentifier): boolean {
205 const op = this._operations.get(extensionId);
206 return Boolean(op && op.value);
207 }
209 > public getActivatedExtension(extensionId: ExtensionIdentifier): ActivatedExtension {
210 const op = this._operations.get(extensionId);
211 if (!op || !op.value) {
214 return op.value;
215 }
217 > public async activateByEvent(activationEvent: string, startup: boolean): Promise<void> {
218 if (this._alreadyActivatedEvents[activationEvent]) {
219 return;
228 this._alreadyActivatedEvents[activationEvent] = true;
229 }
231 > public activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
232 const desc = this._registry.getExtensionDescription(extensionId);
233 if (!desc) {
236 return this._activateExtensions([{ id: desc.identifier, reason }]);
237 }
239 > private async _activateExtensions(extensions: ActivationIdAndReason[]): Promise<void> {
240 const operations = extensions
241 .filter((p) => !this.isActivated(p.id))
243 await Promise.all(operations.map(op => op.wait()));
244 }
246 > /**
247 > * Handle semantics related to dependencies for `currentExtension`.
248 > * We don't need to worry about dependency loops because they are handled by the registry.
249 > */
250 > private _handleActivationRequest(currentActivation: ActivationIdAndReason): ActivationOperation {
251 if (this._operations.has(currentActivation.id)) {
252 return this._operations.get(currentActivation.id)!;
323 return this._createAndSaveOperation(currentActivation, currentExtension.displayName, deps, null);
324 }
326 > private _createAndSaveOperation(activation: ActivationIdAndReason, displayName: string | null | undefined, deps: ActivationOperation[], value: ActivatedExtension | null): ActivationOperation {
327 const operation = new ActivationOperation(activation.id, displayName, activation.reason, deps, value, this._host, this._logService);
328 this._operations.set(activation.id, operation);
329 return operation;
330 }
332 > private _isHostExtension(extensionId: ExtensionIdentifier | string): boolean {
333 return ExtensionDescriptionRegistry.isHostExtension(extensionId, this._registry, this._globalRegistry);
334 }
336 > private _isResolvedExtension(extensionId: ExtensionIdentifier | string): boolean {
337 const extensionDescription = this._globalRegistry.getExtensionDescription(extensionId);
338 if (!extensionDescription) {
342 return (!extensionDescription.main && !extensionDescription.browser);
343 }
345 >
346 > class ActivationOperation {
347 >
348 > private readonly _barrier = new Barrier();
349 > private _isDisposed = false;
350 >
351 > public get value(): ActivatedExtension | null {
352 > return this._value;
353 > }
354 >
355 > public get friendlyName(): string {
356 return this._displayName || this._id.value;
357 }
359 > constructor(
360 private readonly _id: ExtensionIdentifier,
361 private readonly _displayName: string | null | undefined,
368 this._initialize();
369 }
371 > public dispose(): void {
372 this._isDisposed = true;
373 }
375 > public wait() {
376 return this._barrier.wait();
377 }
379 > private async _initialize(): Promise<void> {
380 await this._waitForDepsThenActivate();
381 this._barrier.open();
382 }
384 > private async _waitForDepsThenActivate(): Promise<void> {
385 if (this._value) {
386 // this operation is already finished
419 await this._activate();
420 }
422 > private async _activate(): Promise<void> {
423 try {
424 this._value = await this._host.actualActivateExtension(this._id, this._reason);