Skip to content

vscode 插件加载原理

多进程架构详解

alt text

1、主进程(Main),一个 Electron 应用只有一个主进程。创建 GUI 相关的接口只由主进程来调用。

2、渲染进程(Renderer),每一个工作区(workbench)对应一个进程,同时是BrowserWindow实例。一个Electron项目可以有多个渲染进程。

3、插件进程(Extension),fork了渲染进程,每个插件都运行在一个NodeJS宿主环境中,即插件间共享进程。VSCode规定,插件不允许直接访问UI,这和Atom不同。

4、Debug进程,一个特殊的插件进程。

5、Search进程,搜索是密集型任务,单独占用一个进程。

6、进程之间通过IPC、RPC进行通信,这个后面会介绍。

7、LSP和DAP像两座桥梁,连接起语言和调试服务,它们都运行在插件进程中。

因为VSCode基于Electron,Electron基于chromium,所以进程和浏览器架构十分相似。

进程间通信

IPC

ts
electron.ipcRenderer.send(
  "sendMessageFromRendererProcesses",
  "渲染进程向主进程发送异步消息"
);

electron.ipcMain.on("sendMessageFromRendererProcesses", (event, message) => {
  event.sender.send("sendMessageFromMainProcesses", "回应异步消息:" + message);
});

VSCode的IPC通信是基于Electron,进程间可以双向通信,并且支持同步异步通信。

RPC

ts
const { BrowserWindow } = require("electron").remote;
let win = new BrowserWindow({ width: 800, height: 600 });
win.loadURL("https://github.com");

这里是渲染进程直接调用Electron的远程模块,重新初始化一个界面BrowserWindow,并且打开一个页面,地址为https://github.com。RPC一般用于单向调用,如渲染进程调用主进程。

小结

1、多进程架构,实现了视图与逻辑分离。

2、基于接口编程(LSP、DAP),规范了扩展功能。

3、插件进程,单独开启一个进程。不影响启动速度,不影响主进程和渲染进程,不能直接改变UI样式。缺点,UI可扩展性差,优点,带来了统一的视觉效果和交互风格。

最简单的插件

插件目录

ini
helloworld-sample
  ├─.vscode
  │  ├─launch.json
  │  └─tasks.json
  ├─src
  │  └─extension.ts
  ├─.eslintrc.js
  ├─package-lock.json
  ├─package.json
  ├─README.md
  └─tsconfig.json

package.json

有两个关键字,engines 指VSCode兼容版本,activationEvents 表示触发事件。onLanguage 为语言为 java 时,输入命令 onCommand:java.show.references(通过 cmd + p 可进入输入命令界面),或者工作区中包含 pom.xml 文件,这些都会加载插件。插件的加载机制是懒加载,只有触发了指定事件才会加载。

json
{
  "engines": {
    "vscode": "^1.47.0"
  },
  "contributes": {
		"commands": [
			{
				"command": "extension.helloWorld",
				"title": "Hello World"
			}
		]
	},
  "activationEvents": [
    "onLanguage:java",
    "onCommand:java.show.references",
    "onCommand:java.show.implementations",
    "onCommand:java.open.output",
    "onCommand:java.open.serverLog",
    "onCommand:java.execute.workspaceCommand",
    "onCommand:java.projectConfiguration.update",
    "workspaceContains:pom.xml",
    "workspaceContains:build.gradle"
  ]
}

extension.ts

extension.ts 里导出一个 activate 函数,表示当插件被激活时执行函数内的内容。Demo里注册了一个命令到VSCode的context上下文中,且当执行hellworld这个命令时,会弹出一个提示语,我们将提示语由Hello World 改为了 Hello VS Code。

VSCode能自动实现插件项目,我们按 F5 即可进入调试模式,下面是一个输出提示语的视频。

ts
// src/extension.ts

const vscode = require('vscode');


/**
 * @param {vscode.ExtensionContext} context
 */
function activate(context) {
	console.log('Congratulations, your extension "helloworld-minimal-sample" is now active!');

	let disposable = vscode.commands.registerCommand('extension.helloWorld', () => {
		vscode.window.showInformationMessage('Hello World!');
	});

	context.subscriptions.push(disposable);
}

// this method is called when your extension is deactivated
function deactivate() {}

// eslint-disable-next-line no-undef
module.exports = {
	activate,
	deactivate
}

extension load steps

源码组织

workbench.desktop.main.ts before

  • main.ts -> ... -> workbench.desktop.main.ts 详见

workbench.desktop.main.ts

  • src/vs/workbench/workbench.desktop.main.ts
  • import './services/extensions/electron-sandbox/nativeExtensionService.js'

nativeExtensionService.ts

  • vs/workbench/services/extensions/electron-sandbox/nativeExtensionService.ts
    • NativeExtensionService -> NativeExtensionService -> extensionHostFactory -> super()
    • NativeExtensionService -> this._initialize()

abstractExtensionService.ts

  • vs/workbench/services/extensions/common/abstractExtensionService.ts
    • AbstractExtensionService -> _initialize() -> this._startExtensionHostsIfNecessary() -> extHostManager.start() -> ExtensionHostManager/LazyCreateExtensionHostManager
      • this._startExtensionHostsIfNecessary() ->this._createExtensionHostManager()
        • this._extensionHostFactory.createExtensionHost()
        • this._doCreateExtensionHostManager()
          • createInstance() -> ExtensionHostManager
          • createInstance() -> LazyCreateExtensionHostManager
    • AbstractExtensionService -> constructor()
      • 1

nativeExtensionService.ts

  • vs/workbench/services/extensions/electron-sandbox/nativeExtensionService.ts
    • this._extensionHostFactory.createExtensionHost()
      • createInstance() -> NativeLocalProcessExtensionHost
      • createInstance() -> WebWorkerExtensionHost
      • createInstance() -> RemoteExtensionHost

extensionHostManager.ts

  • vs/workbench/services/extensions/common/extensionHostManager.ts
    • ExtensionHostManager
      • this._proxy = this._extensionHost.start().then(this._createExtensionHostCustomers())
      • start() -> this._proxy.startExtensionHost()

lazyCreateExtensionHostManager.ts

  • vs/workbench/services/extensions/common/lazyCreateExtensionHostManager.ts
    • LazyCreateExtensionHostManager
      • start() -> _start() -> _establishProtocol()

localProcessExtensionHost.ts

  • vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts
    • NativeLocalProcessExtensionHost -> start() -> _start() -> _establishProtocol()

webWorkerExtensionHost.ts

  • vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts
    • WebWorkerExtensionHost -> start() -> _startInsideIframe() -> workerUrl -> vs/workbench/api/worker/extensionHostWorkerMain.js -> create() -> vs/workbench/api/worker/extensionHostWorker.ts -> ExtensionHostMain -> vs/workbench/api/common/extensionHostMain.ts

remoteExtensionHost.ts

  • vs/workbench/services/extensions/common/remoteExtensionHost.ts
    • RemoteExtensionHost -> start() -> connectRemoteAgentExtensionHost()

源码链路(精简)

workbench.desktop.main.ts before

  • main.ts -> ... -> workbench.desktop.main.ts 详见

workbench.desktop.main.ts

  • src/vs/workbench/workbench.desktop.main.ts
ts
//#region --- workbench services
import './services/extensionManagement/electron-sandbox/extensionManagementService.js';
import './services/extensions/electron-sandbox/extensionHostStarter.js';
import '../platform/extensionResourceLoader/common/extensionResourceLoaderService.js';
import './services/extensions/electron-sandbox/extensionsScannerService.js';
import './services/extensionManagement/electron-sandbox/extensionManagementServerService.js';
import './services/extensionManagement/electron-sandbox/extensionTipsService.js';
import './services/extensions/electron-sandbox/nativeExtensionService.js';  
import '../platform/extensionManagement/electron-sandbox/extensionsProfileScannerService.js';
//#endregion

//#region --- workbench contributions

// Debug
import './contrib/debug/electron-sandbox/extensionHostDebugService.js';

// Extensions Management
import './contrib/extensions/electron-sandbox/extensions.contribution.js';

//#endregion

nativeExtensionService.ts

ts
// vs/workbench/services/extensions/electron-sandbox/nativeExtensionService.ts

export class NativeExtensionService extends AbstractExtensionService implements IExtensionService {

  private readonly _extensionScanner: CachedExtensionScanner;
  private readonly _localCrashTracker = new ExtensionHostCrashTracker();

  constructor(
    // ...
  ) {
    const extensionsProposedApi = instantiationService.createInstance(ExtensionsProposedApi);  
    const extensionScanner = instantiationService.createInstance(CachedExtensionScanner);
    const extensionHostFactory = new NativeExtensionHostFactory(  
      extensionsProposedApi,
      extensionScanner,
      // ...
    );
    super(
      extensionsProposedApi,
      extensionHostFactory,  
      new NativeExtensionHostKindPicker(environmentService, configurationService, logService),
      instantiationService,
      // ...
    );

    this._extensionScanner = extensionScanner;

    // delay extension host creation and extension scanning
    // until the workbench is running. we cannot defer the
    // extension host more (LifecyclePhase.Restored) because
    // some editors require the extension host to restore
    // and this would result in a deadlock
    // see https://github.com/microsoft/vscode/issues/41322
    lifecycleService.when(LifecyclePhase.Ready).then(() => {
      // reschedule to ensure this runs after restoring viewlets, panels, and editors
      runWhenWindowIdle(mainWindow, () => {
        this._initialize();  
      }, 50 /*max delay*/);
    });
  }
}

class NativeExtensionHostFactory implements IExtensionHostFactory {
  constructor(
    private readonly _extensionsProposedApi: ExtensionsProposedApi,
    private readonly _extensionScanner: CachedExtensionScanner,
    // ...
  ) {
    this._webWorkerExtHostEnablement = determineLocalWebWorkerExtHostEnablement(environmentService, configurationService);
  }

  public createExtensionHost(runningLocations: ExtensionRunningLocationTracker, runningLocation: ExtensionRunningLocation, isInitialStart: boolean): IExtensionHost | null {
    switch (runningLocation.kind) {
      case ExtensionHostKind.LocalProcess: {
        const startup = (
          isInitialStart
            ? ExtensionHostStartup.EagerManualStart
            : ExtensionHostStartup.EagerAutoStart
        );
        return this._instantiationService.createInstance(NativeLocalProcessExtensionHost, runningLocation, startup, this._createLocalProcessExtensionHostDataProvider(runningLocations, isInitialStart, runningLocation));  
      }
      case ExtensionHostKind.LocalWebWorker: {
        if (this._webWorkerExtHostEnablement !== LocalWebWorkerExtHostEnablement.Disabled) {
          const startup = (
            isInitialStart
              ? (this._webWorkerExtHostEnablement === LocalWebWorkerExtHostEnablement.Lazy ? ExtensionHostStartup.Lazy : ExtensionHostStartup.EagerManualStart)
              : ExtensionHostStartup.EagerAutoStart
          );
          return this._instantiationService.createInstance(WebWorkerExtensionHost, runningLocation, startup, this._createWebWorkerExtensionHostDataProvider(runningLocations, runningLocation));  
        }
        return null;
      }
      case ExtensionHostKind.Remote: {
        const remoteAgentConnection = this._remoteAgentService.getConnection();
        if (remoteAgentConnection) {
          return this._instantiationService.createInstance(RemoteExtensionHost, runningLocation, this._createRemoteExtensionHostDataProvider(runningLocations, remoteAgentConnection.remoteAuthority));  
        }
        return null;
      }
    }
  }
}

abstractExtensionService.ts

ts
// vs/workbench/services/extensions\common\abstractExtensionService.ts
export abstract class AbstractExtensionService extends Disposable implements IExtensionService {
  public _serviceBrand: undefined;

  private readonly _onDidRegisterExtensions = this._register(new Emitter<void>());
  public readonly onDidRegisterExtensions = this._onDidRegisterExtensions.event;

  private readonly _onDidChangeExtensionsStatus = this._register(new Emitter<ExtensionIdentifier[]>());
  public readonly onDidChangeExtensionsStatus = this._onDidChangeExtensionsStatus.event;

  private readonly _onDidChangeExtensions = this._register(new Emitter<{ readonly added: ReadonlyArray<IExtensionDescription>; readonly removed: ReadonlyArray<IExtensionDescription> }>({ leakWarningThreshold: 400 }));
  public readonly onDidChangeExtensions = this._onDidChangeExtensions.event;

  private readonly _onWillActivateByEvent = this._register(new Emitter<IWillActivateEvent>());
  public readonly onWillActivateByEvent = this._onWillActivateByEvent.event;

  private readonly _onDidChangeResponsiveChange = this._register(new Emitter<IResponsiveStateChangeEvent>());
  public readonly onDidChangeResponsiveChange = this._onDidChangeResponsiveChange.event;

  private readonly _onWillStop = this._register(new Emitter<WillStopExtensionHostsEvent>());
  public readonly onWillStop = this._onWillStop.event;

  private readonly _activationEventReader = new ImplicitActivationAwareReader();
  private readonly _registry = new LockableExtensionDescriptionRegistry(this._activationEventReader);
  private readonly _installedExtensionsReady = new Barrier();
  private readonly _extensionStatus = new ExtensionIdentifierMap<ExtensionStatus>();
  private readonly _allRequestedActivateEvents = new Set<string>();
  private readonly _runningLocations: ExtensionRunningLocationTracker;
  private readonly _remoteCrashTracker = new ExtensionHostCrashTracker();

  private _deltaExtensionsQueue: DeltaExtensionsQueueItem[] = [];
  private _inHandleDeltaExtensions = false;

  private readonly _extensionHostManagers = this._register(new ExtensionHostCollection());

  private readonly _extensionHostManagers = this._register(new ExtensionHostCollection());  

  private _resolveAuthorityAttempt: number = 0;

  constructor(
    private readonly _extensionsProposedApi: ExtensionsProposedApi,
    private readonly _extensionHostFactory: IExtensionHostFactory,  
    private readonly _extensionHostKindPicker: IExtensionHostKindPicker,
    // ...
  ) {
    super();

    // help the file service to activate providers by activating extensions by file system event
    this._register(this._fileService.onWillActivateFileSystemProvider(e => {
      if (e.scheme !== Schemas.vscodeRemote) {
        e.join(this.activateByEvent(`onFileSystem:${e.scheme}`));  
      }
    }));

    this._runningLocations = new ExtensionRunningLocationTracker(
      this._registry,
      this._extensionHostKindPicker,
      this._environmentService,
      this._configurationService,
      this._logService,
      this._extensionManifestPropertiesService
    );

    this._register(this._extensionEnablementService.onEnablementChanged((extensions) => {
      // ...
      this._handleDeltaExtensions(new DeltaExtensionsQueueItem(toAdd, toRemove));  
    }));

    this._register(this._extensionManagementService.onDidChangeProfile(({ added, removed }) => {
      // ...
      this._handleDeltaExtensions(new DeltaExtensionsQueueItem(added, removed));  
      
    }));

    this._register(this._extensionManagementService.onDidEnableExtensions(extensions => {
      // ...
      this._handleDeltaExtensions(new DeltaExtensionsQueueItem(extensions, []));  
    }));

    this._register(this._extensionManagementService.onDidInstallExtensions((result) => {
      // ...
      this._handleDeltaExtensions(new DeltaExtensionsQueueItem(extensions, []));  
    }));

    this._register(this._extensionManagementService.onDidUninstallExtension((event) => {
      // ...
      this._handleDeltaExtensions(new DeltaExtensionsQueueItem([], [event.identifier.id]));  
    }));

    this._register(this._lifecycleService.onWillShutdown(event => {
      // ...
    }));
  }

  protected async _initialize(): Promise<void> {
    this._startExtensionHostsIfNecessary(true, []);  

    for (const extHostManager of this._extensionHostManagers) {
      if (extHostManager.startup !== ExtensionHostStartup.EagerAutoStart) {
        const extensions = this._runningLocations.filterByExtensionHostManager(snapshot.extensions, extHostManager);  

        extHostManager.start(snapshot.versionId, snapshot.extensions, extensions.map(extension => extension.identifier));  
      }
    }
  }

  private _startExtensionHostsIfNecessary(isInitialStart: boolean, initialActivationEvents: string[]): void {
    const locations: ExtensionRunningLocation[] = [];
    for (let affinity = 0; affinity <= this._runningLocations.maxLocalProcessAffinity; affinity++) {
      locations.push(new LocalProcessRunningLocation(affinity));
    }
    for (let affinity = 0; affinity <= this._runningLocations.maxLocalWebWorkerAffinity; affinity++) {
      locations.push(new LocalWebWorkerRunningLocation(affinity));
    }
    locations.push(new RemoteRunningLocation());
    for (const location of locations) {
      if (this._extensionHostManagers.getByRunningLocation(location)) {
        // already running
        continue;
      }
      const res = this._createExtensionHostManager(location, isInitialStart, initialActivationEvents);  
      if (res) {
        const [extHostManager, disposableStore] = res;
        this._extensionHostManagers.add(extHostManager, disposableStore);  
      }
    }
  }

  private _createExtensionHostManager(runningLocation: ExtensionRunningLocation, isInitialStart: boolean, initialActivationEvents: string[]): null | [IExtensionHostManager, DisposableStore] {
    const extensionHost = this._extensionHostFactory.createExtensionHost(this._runningLocations, runningLocation, isInitialStart);  
    if (!extensionHost) {
      return null;
    }

    const processManager: IExtensionHostManager = this._doCreateExtensionHostManager(extensionHost, initialActivationEvents);  
    const disposableStore = new DisposableStore();
    // ...
    return [processManager, disposableStore];  
  }

  protected _doCreateExtensionHostManager(extensionHost: IExtensionHost, initialActivationEvents: string[]): IExtensionHostManager {
    const internalExtensionService = this._acquireInternalAPI(extensionHost);
    if (extensionHost.startup === ExtensionHostStartup.Lazy && initialActivationEvents.length === 0) {
      return this._instantiationService.createInstance(LazyCreateExtensionHostManager, extensionHost, internalExtensionService);  
    }
    return this._instantiationService.createInstance(ExtensionHostManager, extensionHost, initialActivationEvents, internalExtensionService);  
  }

  //#region IExtensionService

  public activateByEvent(activationEvent: string, activationKind: ActivationKind = ActivationKind.Normal): Promise<void> {
    if (this._installedExtensionsReady.isOpen()) {
      // Extensions have been scanned and interpreted

      // Record the fact that this activationEvent was requested (in case of a restart)
      this._allRequestedActivateEvents.add(activationEvent);

      if (!this._registry.containsActivationEvent(activationEvent)) {
        // There is no extension that is interested in this activation event
        return NO_OP_VOID_PROMISE;
      }

      return this._activateByEvent(activationEvent, activationKind);  
    } else {
      // Extensions have not been scanned yet.

      // Record the fact that this activationEvent was requested (in case of a restart)
      this._allRequestedActivateEvents.add(activationEvent);

      if (activationKind === ActivationKind.Immediate) {
        // Do not wait for the normal start-up of the extension host(s)
        return this._activateByEvent(activationEvent, activationKind);
      }

      return this._installedExtensionsReady.wait().then(() => this._activateByEvent(activationEvent, activationKind));
    }
  }

  private _activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void> {
    const result = Promise.all(
      this._extensionHostManagers.map(extHostManager => extHostManager.activateByEvent(activationEvent, activationKind))
    ).then(() => { });
    this._onWillActivateByEvent.fire({
      event: activationEvent,
      activation: result
    });
    return result;
  }

  public activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
    return this._activateById(extensionId, reason);
  }

  public activationEventIsDone(activationEvent: string): boolean {
    if (!this._installedExtensionsReady.isOpen()) {
      return false;
    }
    if (!this._registry.containsActivationEvent(activationEvent)) {
      // There is no extension that is interested in this activation event
      return true;
    }
    return this._extensionHostManagers.every(manager => manager.activationEventIsDone(activationEvent));
  }

  public whenInstalledExtensionsRegistered(): Promise<boolean> {
    return this._installedExtensionsReady.wait();
  }

  get extensions(): IExtensionDescription[] {
    return this._registry.getAllExtensionDescriptions();
  }

  protected _getExtensionRegistrySnapshotWhenReady(): Promise<ExtensionDescriptionRegistrySnapshot> {
    return this._installedExtensionsReady.wait().then(() => this._registry.getSnapshot());
  }

  public getExtension(id: string): Promise<IExtensionDescription | undefined> {
    return this._installedExtensionsReady.wait().then(() => {
      return this._registry.getExtensionDescription(id);
    });
  }

  public readExtensionPointContributions<T extends IExtensionContributions[keyof IExtensionContributions]>(extPoint: IExtensionPoint<T>): Promise<ExtensionPointContribution<T>[]> {
    return this._installedExtensionsReady.wait().then(() => {
      const availableExtensions = this._registry.getAllExtensionDescriptions();

      const result: ExtensionPointContribution<T>[] = [];
      for (const desc of availableExtensions) {
        if (desc.contributes && hasOwnProperty.call(desc.contributes, extPoint.name)) {
          result.push(new ExtensionPointContribution<T>(desc, desc.contributes[extPoint.name as keyof typeof desc.contributes] as T));
        }
      }

      return result;
    });
  }

  public getExtensionsStatus(): { [id: string]: IExtensionsStatus } {
    const result: { [id: string]: IExtensionsStatus } = Object.create(null);
    if (this._registry) {
      const extensions = this._registry.getAllExtensionDescriptions();
      for (const extension of extensions) {
        const extensionStatus = this._extensionStatus.get(extension.identifier);
        result[extension.identifier.value] = {
          id: extension.identifier,
          messages: extensionStatus?.messages ?? [],
          activationStarted: extensionStatus?.activationStarted ?? false,
          activationTimes: extensionStatus?.activationTimes ?? undefined,
          runtimeErrors: extensionStatus?.runtimeErrors ?? [],
          runningLocation: this._runningLocations.getRunningLocation(extension.identifier),
        };
      }
    }
    return result;
  }

  public async getInspectPorts(extensionHostKind: ExtensionHostKind, tryEnableInspector: boolean): Promise<{ port: number; host: string }[]> {
    const result = await Promise.all(
      this._getExtensionHostManagers(extensionHostKind).map(extHost => extHost.getInspectPort(tryEnableInspector))
    );
    // remove 0s:
    return result.filter(isDefined);
  }

  public async setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void> {
    await this._extensionHostManagers
      .map(manager => manager.setRemoteEnvironment(env));
  }

  //#endregion
}

extensionHostManager.ts

ts
// vs\workbench\services\extensions\common\extensionHostManager.ts
export class ExtensionHostManager extends Disposable implements IExtensionHostManager {
  constructor(
    extensionHost: IExtensionHost,
    // ...
  ) {
    super();
    this._extensionHost = extensionHost;  
    // ...

    this._proxy = this._extensionHost.start().then(
      // ...
      return this._createExtensionHostCustomers(this.kind, protocol);  
    );

    this._proxy.then(() => {
      initialActivationEvents.forEach((activationEvent) => this.activateByEvent(activationEvent, ActivationKind.Normal));  
      this._register(registerLatencyTestProvider({
        measure: () => this.measure()
      }));
    });
  }

  public get startup(): ExtensionHostStartup {
    return this._extensionHost.startup;  
  }

  public async start(extensionRegistryVersionId: number, allExtensions: IExtensionDescription[], myExtensions: ExtensionIdentifier[]): Promise<void> {
    const proxy = await this._proxy;
    if (!proxy) {
      return;
    }
    const deltaExtensions = this._extensionHost.extensions!.set(extensionRegistryVersionId, allExtensions, myExtensions);
    return proxy.startExtensionHost(deltaExtensions);  
  }

  public async activate(extension: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean> {
    const proxy = await this._proxy;
    if (!proxy) {
      return false;
    }
    return proxy.activate(extension, reason);  
  }

  public activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void> {
    if (activationKind === ActivationKind.Immediate && !this._hasStarted) {
      return Promise.resolve();
    }

    if (!this._cachedActivationEvents.has(activationEvent)) {
      this._cachedActivationEvents.set(activationEvent, this._activateByEvent(activationEvent, activationKind));  
    }
    return this._cachedActivationEvents.get(activationEvent)!;
  }

  public activationEventIsDone(activationEvent: string): boolean {
    return this._resolvedActivationEvents.has(activationEvent);
  }

  private async _activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void> {
    if (!this._proxy) {
      return;
    }
    const proxy = await this._proxy;
    if (!proxy) {
      // this case is already covered above and logged.
      // i.e. the extension host could not be started
      return;
    }

    if (!this._extensionHost.extensions!.containsActivationEvent(activationEvent)) {
      this._resolvedActivationEvents.add(activationEvent);
      return;
    }

    await proxy.activateByEvent(activationEvent, activationKind);  
    this._resolvedActivationEvents.add(activationEvent);
  }

}

lazyCreateExtensionHostManager.ts

ts
// LazyCreateExtensionHostManager
export class LazyCreateExtensionHostManager extends Disposable implements IExtensionHostManager {
  constructor(
    extensionHost: IExtensionHost,
    // ...
  ) {
    super();
    this._extensionHost = extensionHost;  
    // ...
  }

  public get startup(): ExtensionHostStartup {
    return this._extensionHost.startup;  
  }

  public start(): Promise<IMessagePassingProtocol> {
    if (this._terminating) {
      // .terminate() was called
      throw new CancellationError();
    }

    if (!this._messageProtocol) {
      this._messageProtocol = this._start();  
    }

    return this._messageProtocol;
  }

  private async _start(): Promise<IMessagePassingProtocol> {
    const [extensionHostCreationResult, portNumber, processEnv] = await Promise.all([
      this._extensionHostStarter.createExtensionHost(),
      this._tryFindDebugPort(),
      this._shellEnvironmentService.getShellEnv(),
    ]);

    this._extensionHostProcess = new ExtensionHostProcess(extensionHostCreationResult.id, this._extensionHostStarter);

    const env = objects.mixin(processEnv, {
      VSCODE_ESM_ENTRYPOINT: 'vs/workbench/api/node/extensionHostProcess',  
      VSCODE_HANDLES_UNCAUGHT_ERRORS: true
    });

    if (this._environmentService.debugExtensionHost.env) {
      objects.mixin(env, this._environmentService.debugExtensionHost.env);
    }

    const opts: IExtensionHostProcessOptions = {
      responseWindowId: this._nativeHostService.windowId,
      responseChannel: 'vscode:startExtensionHostMessagePortResult',
      responseNonce: generateUuid(),
      env,
      // We only detach the extension host on windows. Linux and Mac orphan by default
      // and detach under Linux and Mac create another process group.
      // We detach because we have noticed that when the renderer exits, its child processes
      // (i.e. extension host) are taken down in a brutal fashion by the OS
      detached: !!platform.isWindows,
      execArgv: undefined as string[] | undefined,
      silent: true
    };
    // ...

    // Initialize extension host process with hand shakes
    const protocol = await this._establishProtocol(this._extensionHostProcess, opts);  
    await this._performHandshake(protocol);
    clearTimeout(startupTimeoutHandle);
    return protocol;  
  }

  private _establishProtocol(extensionHostProcess: ExtensionHostProcess, opts: IExtensionHostProcessOptions): Promise<IMessagePassingProtocol> {

    return new Promise<IMessagePassingProtocol>((resolve, reject) => {

      const handle = setTimeout(() => {
        reject('The local extension host took longer than 60s to connect.');
      }, 60 * 1000);

      // Now that the message port listener is installed, start the ext host process
      const sw = StopWatch.create(false);
      extensionHostProcess.start(opts).then(({ pid }) => {  
        // ...
      }, (err) => {
        // ...
      });
    });
  }

}

localProcessExtensionHost.ts

ts
// vs\workbench\services\extensions\electron-sandbox\localProcessExtensionHost.ts

export class NativeLocalProcessExtensionHost implements IExtensionHost {

  constructor(
    public readonly runningLocation: LocalProcessRunningLocation,
    public readonly startup: ExtensionHostStartup.EagerAutoStart | ExtensionHostStartup.EagerManualStart,
    private readonly _initDataProvider: ILocalProcessExtensionHostDataProvider,
    // ...
  ) {
    // ...
  }
  public start(): Promise<IMessagePassingProtocol> {
    if (this._terminating) {
      // .terminate() was called
      throw new CancellationError();
    }

    if (!this._messageProtocol) {
      this._messageProtocol = this._start();  
    }

    return this._messageProtocol;
  }

  private async _start(): Promise<IMessagePassingProtocol> {
    const [extensionHostCreationResult, portNumber, processEnv] = await Promise.all([
      this._extensionHostStarter.createExtensionHost(),
      this._tryFindDebugPort(),
      this._shellEnvironmentService.getShellEnv(),
    ]);

    this._extensionHostProcess = new ExtensionHostProcess(extensionHostCreationResult.id, this._extensionHostStarter);

    const env = objects.mixin(processEnv, {
      VSCODE_ESM_ENTRYPOINT: 'vs/workbench/api/node/extensionHostProcess', 
      VSCODE_HANDLES_UNCAUGHT_ERRORS: true
    });


    const opts: IExtensionHostProcessOptions = {
      env,
      // ...
    };

    // Initialize extension host process with hand shakes
    const protocol = await this._establishProtocol(this._extensionHostProcess, opts); 
    return protocol;
  }
}

export class ExtensionHostProcess {
  constructor(
    id: string,
    private readonly _extensionHostStarter: IExtensionHostStarter,
  ) {
    this._id = id;
  }

  public start(opts: IExtensionHostProcessOptions): Promise<{ pid: number | undefined }> {
    return this._extensionHostStarter.start(this._id, opts);  
  }
}

webWorkerExtensionHost.ts

ts
// vs\workbench\services\extensions\browser\webWorkerExtensionHost.ts
export class WebWorkerExtensionHost extends Disposable implements IExtensionHost {
  constructor(
    public readonly runningLocation: LocalWebWorkerRunningLocation,
    public readonly startup: ExtensionHostStartup,
    private readonly _initDataProvider: IWebWorkerExtensionHostDataProvider,
    // ...
  ) {
    // ...
  }
  public async start(): Promise<IMessagePassingProtocol> {
    if (!this._protocolPromise) {
      this._protocolPromise = this._startInsideIframe();  
      this._protocolPromise.then(protocol => this._protocol = protocol);  
    }
    return this._protocolPromise;
  }

  private async _startInsideIframe(): Promise<IMessagePassingProtocol> {
    const webWorkerExtensionHostIframeSrc = await this._getWebWorkerExtensionHostIframeSrc();
    const emitter = this._register(new Emitter<VSBuffer>());

    const iframe = document.createElement('iframe');  
    iframe.setAttribute('class', 'web-worker-ext-host-iframe');  
    iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');  
    iframe.setAttribute('allow', 'usb; serial; hid; cross-origin-isolated;');  
    iframe.setAttribute('aria-hidden', 'true');  
    iframe.style.display = 'none';  

    const vscodeWebWorkerExtHostId = generateUuid();
    iframe.setAttribute('src', `${webWorkerExtensionHostIframeSrc}&vscodeWebWorkerExtHostId=${vscodeWebWorkerExtHostId}`);  

    this._register(dom.addDisposableListener(mainWindow, 'message', (event) => {  
      if (event.data.type === 'vscode.bootstrap.nls') {  
        iframe.contentWindow!.postMessage({
          type: event.data.type,
          data: {
            workerUrl: FileAccess.asBrowserUri('vs/workbench/api/worker/extensionHostWorkerMain.js').toString(true),  
            fileRoot: globalThis._VSCODE_FILE_ROOT,
            nls: {
              messages: getNLSMessages(),
              language: getNLSLanguage()
            }
          }
        }, '*');
        return;
      }
    }));

    this._layoutService.mainContainer.appendChild(iframe);  
    this._register(toDisposable(() => iframe.remove()));

    // Send over message ports for extension API
    const messagePorts = this._environmentService.options?.messagePorts ?? new Map();
    iframe.contentWindow!.postMessage({ type: 'vscode.init', data: messagePorts }, '*', [...messagePorts.values()]);

    const protocol: IMessagePassingProtocol = {  
      onMessage: emitter.event,
      send: vsbuf => {
        const data = vsbuf.buffer.buffer.slice(vsbuf.buffer.byteOffset, vsbuf.buffer.byteOffset + vsbuf.buffer.byteLength);
        port.postMessage(data, [data]);
      }
    };

    return this._performHandshake(protocol);  
  }
}

remoteExtensionHost.ts

ts
// vs\workbench\services\extensions\common\remoteExtensionHost.ts

export class RemoteExtensionHost extends Disposable implements IExtensionHost {
  constructor(
    public readonly runningLocation: RemoteRunningLocation,
    private readonly _initDataProvider: IRemoteExtensionHostDataProvider,
    // ...
  ) {
    // ...
  }

  public start(): Promise<IMessagePassingProtocol> {
    const options: IConnectionOptions = {
      remoteSocketFactoryService: this.remoteSocketFactoryService,  
      // ...
    };
    return this.remoteAuthorityResolverService.resolveAuthority(this._initDataProvider.remoteAuthority).then((resolverResult) => {  
      // ...

      return connectRemoteAgentExtensionHost(options, startParams).then(result => {  
        // ...
      });
    });
  }
}

参考资料