"Fossies" - the Fresh Open Source Software Archive

Member "vscode-1.49.1/src/vs/workbench/services/extensions/common/abstractExtensionService.ts" (16 Sep 2020, 22729 Bytes) of package /linux/misc/vscode-1.49.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) TypeScript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "abstractExtensionService.ts": 1.48.2_vs_1.49.0.

    1 /*---------------------------------------------------------------------------------------------
    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 { isNonEmptyArray } from 'vs/base/common/arrays';
    7 import { Barrier } from 'vs/base/common/async';
    8 import { Emitter, Event } from 'vs/base/common/event';
    9 import { Disposable } from 'vs/base/common/lifecycle';
   10 import * as perf from 'vs/base/common/performance';
   11 import { isEqualOrParent } from 'vs/base/common/resources';
   12 import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
   13 import { IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
   14 import { BetterMergeId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
   15 import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
   16 import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
   17 import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
   18 import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension, IExtensionHost, ActivationKind } from 'vs/workbench/services/extensions/common/extensions';
   19 import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
   20 import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
   21 import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol';
   22 import { ExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager';
   23 import { ExtensionIdentifier, IExtensionDescription, ExtensionType, ITranslatedScannedExtension } from 'vs/platform/extensions/common/extensions';
   24 import { IFileService } from 'vs/platform/files/common/files';
   25 import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions';
   26 import { IProductService } from 'vs/platform/product/common/productService';
   27 import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
   28 
   29 const hasOwnProperty = Object.hasOwnProperty;
   30 const NO_OP_VOID_PROMISE = Promise.resolve<void>(undefined);
   31 
   32 export function parseScannedExtension(extension: ITranslatedScannedExtension): IExtensionDescription {
   33     return {
   34         identifier: new ExtensionIdentifier(`${extension.packageJSON.publisher}.${extension.packageJSON.name}`),
   35         isBuiltin: extension.type === ExtensionType.System,
   36         isUnderDevelopment: false,
   37         extensionLocation: extension.location,
   38         ...extension.packageJSON,
   39     };
   40 }
   41 
   42 export abstract class AbstractExtensionService extends Disposable implements IExtensionService {
   43 
   44     public _serviceBrand: undefined;
   45 
   46     protected readonly _onDidRegisterExtensions: Emitter<void> = this._register(new Emitter<void>());
   47     public readonly onDidRegisterExtensions = this._onDidRegisterExtensions.event;
   48 
   49     protected readonly _onDidChangeExtensionsStatus: Emitter<ExtensionIdentifier[]> = this._register(new Emitter<ExtensionIdentifier[]>());
   50     public readonly onDidChangeExtensionsStatus: Event<ExtensionIdentifier[]> = this._onDidChangeExtensionsStatus.event;
   51 
   52     protected readonly _onDidChangeExtensions: Emitter<void> = this._register(new Emitter<void>());
   53     public readonly onDidChangeExtensions: Event<void> = this._onDidChangeExtensions.event;
   54 
   55     protected readonly _onWillActivateByEvent = this._register(new Emitter<IWillActivateEvent>());
   56     public readonly onWillActivateByEvent: Event<IWillActivateEvent> = this._onWillActivateByEvent.event;
   57 
   58     protected readonly _onDidChangeResponsiveChange = this._register(new Emitter<IResponsiveStateChangeEvent>());
   59     public readonly onDidChangeResponsiveChange: Event<IResponsiveStateChangeEvent> = this._onDidChangeResponsiveChange.event;
   60 
   61     protected readonly _registry: ExtensionDescriptionRegistry;
   62     private readonly _installedExtensionsReady: Barrier;
   63     protected readonly _isDev: boolean;
   64     private readonly _extensionsMessages: Map<string, IMessage[]>;
   65     protected readonly _allRequestedActivateEvents = new Set<string>();
   66     private readonly _proposedApiController: ProposedApiController;
   67     private readonly _isExtensionDevHost: boolean;
   68     protected readonly _isExtensionDevTestFromCli: boolean;
   69 
   70     // --- Members used per extension host process
   71     protected _extensionHostManagers: ExtensionHostManager[];
   72     protected _extensionHostActiveExtensions: Map<string, ExtensionIdentifier>;
   73     private _extensionHostActivationTimes: Map<string, ActivationTimes>;
   74     private _extensionHostExtensionRuntimeErrors: Map<string, Error[]>;
   75 
   76     constructor(
   77         @IInstantiationService protected readonly _instantiationService: IInstantiationService,
   78         @INotificationService protected readonly _notificationService: INotificationService,
   79         @IWorkbenchEnvironmentService protected readonly _environmentService: IWorkbenchEnvironmentService,
   80         @ITelemetryService protected readonly _telemetryService: ITelemetryService,
   81         @IWorkbenchExtensionEnablementService protected readonly _extensionEnablementService: IWorkbenchExtensionEnablementService,
   82         @IFileService protected readonly _fileService: IFileService,
   83         @IProductService protected readonly _productService: IProductService
   84     ) {
   85         super();
   86 
   87         // help the file service to activate providers by activating extensions by file system event
   88         this._register(this._fileService.onWillActivateFileSystemProvider(e => {
   89             e.join(this.activateByEvent(`onFileSystem:${e.scheme}`));
   90         }));
   91 
   92         this._registry = new ExtensionDescriptionRegistry([]);
   93         this._installedExtensionsReady = new Barrier();
   94         this._isDev = !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment;
   95         this._extensionsMessages = new Map<string, IMessage[]>();
   96         this._proposedApiController = new ProposedApiController(this._environmentService, this._productService);
   97 
   98         this._extensionHostManagers = [];
   99         this._extensionHostActiveExtensions = new Map<string, ExtensionIdentifier>();
  100         this._extensionHostActivationTimes = new Map<string, ActivationTimes>();
  101         this._extensionHostExtensionRuntimeErrors = new Map<string, Error[]>();
  102 
  103         const devOpts = parseExtensionDevOptions(this._environmentService);
  104         this._isExtensionDevHost = devOpts.isExtensionDevHost;
  105         this._isExtensionDevTestFromCli = devOpts.isExtensionDevTestFromCli;
  106     }
  107 
  108     protected async _initialize(): Promise<void> {
  109         perf.mark('willLoadExtensions');
  110         this._startExtensionHosts(true, []);
  111         this.whenInstalledExtensionsRegistered().then(() => perf.mark('didLoadExtensions'));
  112         await this._scanAndHandleExtensions();
  113         this._releaseBarrier();
  114     }
  115 
  116     private _releaseBarrier(): void {
  117         perf.mark('extensionHostReady');
  118         this._installedExtensionsReady.open();
  119         this._onDidRegisterExtensions.fire(undefined);
  120         this._onDidChangeExtensionsStatus.fire(this._registry.getAllExtensionDescriptions().map(e => e.identifier));
  121     }
  122 
  123     private _stopExtensionHosts(): void {
  124         let previouslyActivatedExtensionIds: ExtensionIdentifier[] = [];
  125         this._extensionHostActiveExtensions.forEach((value) => {
  126             previouslyActivatedExtensionIds.push(value);
  127         });
  128 
  129         for (const manager of this._extensionHostManagers) {
  130             manager.dispose();
  131         }
  132         this._extensionHostManagers = [];
  133         this._extensionHostActiveExtensions = new Map<string, ExtensionIdentifier>();
  134         this._extensionHostActivationTimes = new Map<string, ActivationTimes>();
  135         this._extensionHostExtensionRuntimeErrors = new Map<string, Error[]>();
  136 
  137         if (previouslyActivatedExtensionIds.length > 0) {
  138             this._onDidChangeExtensionsStatus.fire(previouslyActivatedExtensionIds);
  139         }
  140     }
  141 
  142     private _startExtensionHosts(isInitialStart: boolean, initialActivationEvents: string[]): void {
  143         this._stopExtensionHosts();
  144 
  145         const extensionHosts = this._createExtensionHosts(isInitialStart);
  146         extensionHosts.forEach((extensionHost) => {
  147             const processManager = this._instantiationService.createInstance(ExtensionHostManager, extensionHost, initialActivationEvents);
  148             processManager.onDidExit(([code, signal]) => this._onExtensionHostCrashOrExit(processManager, code, signal));
  149             processManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ isResponsive: responsiveState === ResponsiveState.Responsive }); });
  150             this._extensionHostManagers.push(processManager);
  151         });
  152     }
  153 
  154     private _onExtensionHostCrashOrExit(extensionHost: ExtensionHostManager, code: number, signal: string | null): void {
  155 
  156         // Unexpected termination
  157         if (!this._isExtensionDevHost) {
  158             this._onExtensionHostCrashed(extensionHost, code, signal);
  159             return;
  160         }
  161 
  162         this._onExtensionHostExit(code);
  163     }
  164 
  165     protected _onExtensionHostCrashed(extensionHost: ExtensionHostManager, code: number, signal: string | null): void {
  166         console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal);
  167         this._stopExtensionHosts();
  168     }
  169 
  170     //#region IExtensionService
  171 
  172     public canAddExtension(extension: IExtensionDescription): boolean {
  173         return false;
  174     }
  175 
  176     public canRemoveExtension(extension: IExtensionDescription): boolean {
  177         return false;
  178     }
  179 
  180     public restartExtensionHost(): void {
  181         this._stopExtensionHosts();
  182         this._startExtensionHosts(false, Array.from(this._allRequestedActivateEvents.keys()));
  183     }
  184 
  185     protected startExtensionHost(): void {
  186         this._startExtensionHosts(false, Array.from(this._allRequestedActivateEvents.keys()));
  187     }
  188 
  189     public activateByEvent(activationEvent: string, activationKind: ActivationKind = ActivationKind.Normal): Promise<void> {
  190         if (this._installedExtensionsReady.isOpen()) {
  191             // Extensions have been scanned and interpreted
  192 
  193             // Record the fact that this activationEvent was requested (in case of a restart)
  194             this._allRequestedActivateEvents.add(activationEvent);
  195 
  196             if (!this._registry.containsActivationEvent(activationEvent)) {
  197                 // There is no extension that is interested in this activation event
  198                 return NO_OP_VOID_PROMISE;
  199             }
  200 
  201             return this._activateByEvent(activationEvent, activationKind);
  202         } else {
  203             // Extensions have not been scanned yet.
  204 
  205             // Record the fact that this activationEvent was requested (in case of a restart)
  206             this._allRequestedActivateEvents.add(activationEvent);
  207 
  208             if (activationKind === ActivationKind.Immediate) {
  209                 // Do not wait for the normal start-up of the extension host(s)
  210                 return this._activateByEvent(activationEvent, activationKind);
  211             }
  212 
  213             return this._installedExtensionsReady.wait().then(() => this._activateByEvent(activationEvent, activationKind));
  214         }
  215     }
  216 
  217     private _activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void> {
  218         const result = Promise.all(
  219             this._extensionHostManagers.map(extHostManager => extHostManager.activateByEvent(activationEvent, activationKind))
  220         ).then(() => { });
  221         this._onWillActivateByEvent.fire({
  222             event: activationEvent,
  223             activation: result
  224         });
  225         return result;
  226     }
  227 
  228     public whenInstalledExtensionsRegistered(): Promise<boolean> {
  229         return this._installedExtensionsReady.wait();
  230     }
  231 
  232     public getExtensions(): Promise<IExtensionDescription[]> {
  233         return this._installedExtensionsReady.wait().then(() => {
  234             return this._registry.getAllExtensionDescriptions();
  235         });
  236     }
  237 
  238     public getExtension(id: string): Promise<IExtensionDescription | undefined> {
  239         return this._installedExtensionsReady.wait().then(() => {
  240             return this._registry.getExtensionDescription(id);
  241         });
  242     }
  243 
  244     public readExtensionPointContributions<T>(extPoint: IExtensionPoint<T>): Promise<ExtensionPointContribution<T>[]> {
  245         return this._installedExtensionsReady.wait().then(() => {
  246             const availableExtensions = this._registry.getAllExtensionDescriptions();
  247 
  248             const result: ExtensionPointContribution<T>[] = [];
  249             for (const desc of availableExtensions) {
  250                 if (desc.contributes && hasOwnProperty.call(desc.contributes, extPoint.name)) {
  251                     result.push(new ExtensionPointContribution<T>(desc, desc.contributes[extPoint.name as keyof typeof desc.contributes]));
  252                 }
  253             }
  254 
  255             return result;
  256         });
  257     }
  258 
  259     public getExtensionsStatus(): { [id: string]: IExtensionsStatus; } {
  260         let result: { [id: string]: IExtensionsStatus; } = Object.create(null);
  261         if (this._registry) {
  262             const extensions = this._registry.getAllExtensionDescriptions();
  263             for (const extension of extensions) {
  264                 const extensionKey = ExtensionIdentifier.toKey(extension.identifier);
  265                 result[extension.identifier.value] = {
  266                     messages: this._extensionsMessages.get(extensionKey) || [],
  267                     activationTimes: this._extensionHostActivationTimes.get(extensionKey),
  268                     runtimeErrors: this._extensionHostExtensionRuntimeErrors.get(extensionKey) || [],
  269                 };
  270             }
  271         }
  272         return result;
  273     }
  274 
  275     public getInspectPort(_tryEnableInspector: boolean): Promise<number> {
  276         return Promise.resolve(0);
  277     }
  278 
  279     public async setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void> {
  280         await this._extensionHostManagers
  281             .map(manager => manager.setRemoteEnvironment(env));
  282     }
  283 
  284     //#endregion
  285 
  286     // --- impl
  287 
  288     protected _checkEnableProposedApi(extensions: IExtensionDescription[]): void {
  289         for (let extension of extensions) {
  290             this._proposedApiController.updateEnableProposedApi(extension);
  291         }
  292     }
  293 
  294     protected _checkEnabledAndProposedAPI(extensions: IExtensionDescription[]): IExtensionDescription[] {
  295         // enable or disable proposed API per extension
  296         this._checkEnableProposedApi(extensions);
  297 
  298         // keep only enabled extensions
  299         return extensions.filter(extension => this._isEnabled(extension));
  300     }
  301 
  302     private _isExtensionUnderDevelopment(extension: IExtensionDescription): boolean {
  303         if (this._environmentService.isExtensionDevelopment) {
  304             const extDevLocs = this._environmentService.extensionDevelopmentLocationURI;
  305             if (extDevLocs) {
  306                 const extLocation = extension.extensionLocation;
  307                 for (let p of extDevLocs) {
  308                     if (isEqualOrParent(extLocation, p)) {
  309                         return true;
  310                     }
  311                 }
  312             }
  313         }
  314         return false;
  315     }
  316 
  317     protected _isEnabled(extension: IExtensionDescription): boolean {
  318         if (this._isExtensionUnderDevelopment(extension)) {
  319             // Never disable extensions under development
  320             return true;
  321         }
  322 
  323         if (ExtensionIdentifier.equals(extension.identifier, BetterMergeId)) {
  324             // Check if this is the better merge extension which was migrated to a built-in extension
  325             return false;
  326         }
  327 
  328         return this._extensionEnablementService.isEnabled(toExtension(extension));
  329     }
  330 
  331     protected _doHandleExtensionPoints(affectedExtensions: IExtensionDescription[]): void {
  332         const affectedExtensionPoints: { [extPointName: string]: boolean; } = Object.create(null);
  333         for (let extensionDescription of affectedExtensions) {
  334             if (extensionDescription.contributes) {
  335                 for (let extPointName in extensionDescription.contributes) {
  336                     if (hasOwnProperty.call(extensionDescription.contributes, extPointName)) {
  337                         affectedExtensionPoints[extPointName] = true;
  338                     }
  339                 }
  340             }
  341         }
  342 
  343         const messageHandler = (msg: IMessage) => this._handleExtensionPointMessage(msg);
  344         const availableExtensions = this._registry.getAllExtensionDescriptions();
  345         const extensionPoints = ExtensionsRegistry.getExtensionPoints();
  346         for (const extensionPoint of extensionPoints) {
  347             if (affectedExtensionPoints[extensionPoint.name]) {
  348                 AbstractExtensionService._handleExtensionPoint(extensionPoint, availableExtensions, messageHandler);
  349             }
  350         }
  351     }
  352 
  353     private _handleExtensionPointMessage(msg: IMessage) {
  354         const extensionKey = ExtensionIdentifier.toKey(msg.extensionId);
  355 
  356         if (!this._extensionsMessages.has(extensionKey)) {
  357             this._extensionsMessages.set(extensionKey, []);
  358         }
  359         this._extensionsMessages.get(extensionKey)!.push(msg);
  360 
  361         const extension = this._registry.getExtensionDescription(msg.extensionId);
  362         const strMsg = `[${msg.extensionId.value}]: ${msg.message}`;
  363         if (extension && extension.isUnderDevelopment) {
  364             // This message is about the extension currently being developed
  365             this._showMessageToUser(msg.type, strMsg);
  366         } else {
  367             this._logMessageInConsole(msg.type, strMsg);
  368         }
  369 
  370         if (!this._isDev && msg.extensionId) {
  371             const { type, extensionId, extensionPointId, message } = msg;
  372             type ExtensionsMessageClassification = {
  373                 type: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
  374                 extensionId: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
  375                 extensionPointId: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
  376                 message: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
  377             };
  378             type ExtensionsMessageEvent = {
  379                 type: Severity;
  380                 extensionId: string;
  381                 extensionPointId: string;
  382                 message: string;
  383             };
  384             this._telemetryService.publicLog2<ExtensionsMessageEvent, ExtensionsMessageClassification>('extensionsMessage', {
  385                 type, extensionId: extensionId.value, extensionPointId, message
  386             });
  387         }
  388     }
  389 
  390     private static _handleExtensionPoint<T>(extensionPoint: ExtensionPoint<T>, availableExtensions: IExtensionDescription[], messageHandler: (msg: IMessage) => void): void {
  391         const users: IExtensionPointUser<T>[] = [];
  392         for (const desc of availableExtensions) {
  393             if (desc.contributes && hasOwnProperty.call(desc.contributes, extensionPoint.name)) {
  394                 users.push({
  395                     description: desc,
  396                     value: desc.contributes[extensionPoint.name as keyof typeof desc.contributes],
  397                     collector: new ExtensionMessageCollector(messageHandler, desc, extensionPoint.name)
  398                 });
  399             }
  400         }
  401         perf.mark(`willHandleExtensionPoint/${extensionPoint.name}`);
  402         extensionPoint.acceptUsers(users);
  403         perf.mark(`didHandleExtensionPoint/${extensionPoint.name}`);
  404     }
  405 
  406     private _showMessageToUser(severity: Severity, msg: string): void {
  407         if (severity === Severity.Error || severity === Severity.Warning) {
  408             this._notificationService.notify({ severity, message: msg });
  409         } else {
  410             this._logMessageInConsole(severity, msg);
  411         }
  412     }
  413 
  414     private _logMessageInConsole(severity: Severity, msg: string): void {
  415         if (severity === Severity.Error) {
  416             console.error(msg);
  417         } else if (severity === Severity.Warning) {
  418             console.warn(msg);
  419         } else {
  420             console.log(msg);
  421         }
  422     }
  423 
  424     //#region Called by extension host
  425 
  426     public _logOrShowMessage(severity: Severity, msg: string): void {
  427         if (this._isDev) {
  428             this._showMessageToUser(severity, msg);
  429         } else {
  430             this._logMessageInConsole(severity, msg);
  431         }
  432     }
  433 
  434     public async _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
  435         const results = await Promise.all(
  436             this._extensionHostManagers.map(manager => manager.activate(extensionId, reason))
  437         );
  438         const activated = results.some(e => e);
  439         if (!activated) {
  440             throw new Error(`Unknown extension ${extensionId.value}`);
  441         }
  442     }
  443 
  444     public _onWillActivateExtension(extensionId: ExtensionIdentifier): void {
  445         this._extensionHostActiveExtensions.set(ExtensionIdentifier.toKey(extensionId), extensionId);
  446     }
  447 
  448     public _onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void {
  449         this._extensionHostActivationTimes.set(ExtensionIdentifier.toKey(extensionId), new ActivationTimes(codeLoadingTime, activateCallTime, activateResolvedTime, activationReason));
  450         this._onDidChangeExtensionsStatus.fire([extensionId]);
  451     }
  452 
  453     public _onExtensionRuntimeError(extensionId: ExtensionIdentifier, err: Error): void {
  454         const extensionKey = ExtensionIdentifier.toKey(extensionId);
  455         if (!this._extensionHostExtensionRuntimeErrors.has(extensionKey)) {
  456             this._extensionHostExtensionRuntimeErrors.set(extensionKey, []);
  457         }
  458         this._extensionHostExtensionRuntimeErrors.get(extensionKey)!.push(err);
  459         this._onDidChangeExtensionsStatus.fire([extensionId]);
  460     }
  461 
  462     //#endregion
  463 
  464     protected abstract _createExtensionHosts(isInitialStart: boolean): IExtensionHost[];
  465     protected abstract _scanAndHandleExtensions(): Promise<void>;
  466     public abstract _onExtensionHostExit(code: number): void;
  467 }
  468 
  469 class ProposedApiController {
  470 
  471     private readonly enableProposedApiFor: string[];
  472     private readonly enableProposedApiForAll: boolean;
  473     private readonly productAllowProposedApi: Set<string>;
  474 
  475     constructor(
  476         @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
  477         @IProductService productService: IProductService
  478     ) {
  479         // Make enabled proposed API be lowercase for case insensitive comparison
  480         this.enableProposedApiFor = (environmentService.extensionEnabledProposedApi || []).map(id => id.toLowerCase());
  481 
  482         this.enableProposedApiForAll =
  483             !environmentService.isBuilt || // always allow proposed API when running out of sources
  484             (!!environmentService.extensionDevelopmentLocationURI && productService.quality !== 'stable') || // do not allow proposed API against stable builds when developing an extension
  485             (this.enableProposedApiFor.length === 0 && Array.isArray(environmentService.extensionEnabledProposedApi)); // always allow proposed API if --enable-proposed-api is provided without extension ID
  486 
  487         this.productAllowProposedApi = new Set<string>();
  488         if (isNonEmptyArray(productService.extensionAllowedProposedApi)) {
  489             productService.extensionAllowedProposedApi.forEach((id) => this.productAllowProposedApi.add(ExtensionIdentifier.toKey(id)));
  490         }
  491     }
  492 
  493     public updateEnableProposedApi(extension: IExtensionDescription): void {
  494         if (this._allowProposedApiFromProduct(extension.identifier)) {
  495             // fast lane -> proposed api is available to all extensions
  496             // that are listed in product.json-files
  497             extension.enableProposedApi = true;
  498 
  499         } else if (extension.enableProposedApi && !extension.isBuiltin) {
  500             if (
  501                 !this.enableProposedApiForAll &&
  502                 this.enableProposedApiFor.indexOf(extension.identifier.value.toLowerCase()) < 0
  503             ) {
  504                 extension.enableProposedApi = false;
  505                 console.error(`Extension '${extension.identifier.value} cannot use PROPOSED API (must started out of dev or enabled via --enable-proposed-api)`);
  506 
  507             } else {
  508                 // proposed api is available when developing or when an extension was explicitly
  509                 // spelled out via a command line argument
  510                 console.warn(`Extension '${extension.identifier.value}' uses PROPOSED API which is subject to change and removal without notice.`);
  511             }
  512         }
  513     }
  514 
  515     private _allowProposedApiFromProduct(id: ExtensionIdentifier): boolean {
  516         return this.productAllowProposedApi.has(ExtensionIdentifier.toKey(id));
  517     }
  518 }