extensionHostMain.ts 6.6 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

A
Alex Dima 已提交
6
import { timeout } from 'vs/base/common/async';
7
import * as errors from 'vs/base/common/errors';
J
Johannes Rieken 已提交
8
import { DisposableStore } from 'vs/base/common/lifecycle';
9
import { URI, setUriThrowOnMissingScheme } from 'vs/base/common/uri';
A
Alex Dima 已提交
10
import { IURITransformer } from 'vs/base/common/uriIpc';
A
Alex Dima 已提交
11
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
12
import { IInitData, MainContext, MainThreadConsoleShape } from 'vs/workbench/api/common/extHost.protocol';
J
Johannes Rieken 已提交
13
import { ExtHostExtensionService, IHostUtils } from 'vs/workbench/api/node/extHostExtensionService';
J
Johannes Rieken 已提交
14
import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService';
15
import { RPCProtocol } from 'vs/workbench/services/extensions/common/rpcProtocol';
16
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
17
import { ILogService } from 'vs/platform/log/common/log';
18 19 20 21 22
import { getSingletonServiceDescriptors } from 'vs/platform/instantiation/common/extensions';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IExtHostContextService, ExtHostContextService } from 'vs/workbench/api/common/extHostContextService';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
E
Erich Gamma 已提交
23

24 25
// we don't (yet) throw when extensions parse
// uris that have no scheme
26
setUriThrowOnMissingScheme(false);
27

28 29 30
export interface IExitFn {
	(code?: number): any;
}
B
Benjamin Pasero 已提交
31

32 33
export interface IConsolePatchFn {
	(mainThreadConsole: MainThreadConsoleShape): any;
34
}
B
Benjamin Pasero 已提交
35

36 37
export interface ILogServiceFn {
	(initData: IInitData): ILogService;
38 39
}

A
Alex Dima 已提交
40
export class ExtensionHostMain {
E
Erich Gamma 已提交
41

A
Alex Dima 已提交
42
	private _isTerminating: boolean;
J
Johannes Rieken 已提交
43
	private readonly _hostUtils: IHostUtils;
A
Alex Dima 已提交
44
	private readonly _extensionService: ExtHostExtensionService;
J
Johannes Rieken 已提交
45
	private readonly _disposables = new DisposableStore();
46

A
Alex Dima 已提交
47 48 49
	constructor(
		protocol: IMessagePassingProtocol,
		initData: IInitData,
J
Johannes Rieken 已提交
50
		hostUtils: IHostUtils,
A
Alex Dima 已提交
51 52
		consolePatchFn: IConsolePatchFn,
		logServiceFn: ILogServiceFn,
53
		uriTransformer: IURITransformer | null
A
Alex Dima 已提交
54
	) {
A
Alex Dima 已提交
55
		this._isTerminating = false;
J
Johannes Rieken 已提交
56
		this._hostUtils = hostUtils;
A
Alex Dima 已提交
57
		const rpcProtocol = new RPCProtocol(protocol, null, uriTransformer);
58 59

		// ensure URIs are transformed and revived
J
Johannes Rieken 已提交
60
		initData = ExtensionHostMain._transform(initData, rpcProtocol);
61

62 63
		// allow to patch console
		consolePatchFn(rpcProtocol.getProxy(MainContext.MainThreadConsole));
64

65
		// services
J
Johannes Rieken 已提交
66
		const extHostLogService = new ExtHostLogService(logServiceFn(initData), initData.logsLocation.fsPath);
J
Johannes Rieken 已提交
67
		this._disposables.add(extHostLogService);
68

69 70 71 72 73 74
		// bootstrap services
		const services = new ServiceCollection(...getSingletonServiceDescriptors());
		services.set(IExtHostContextService, new ExtHostContextService(rpcProtocol, initData));
		services.set(ILogService, extHostLogService);

		const instaService: IInstantiationService = new InstantiationService(services);
J
Joao Moreno 已提交
75

J
Johannes Rieken 已提交
76 77
		extHostLogService.info('extension host started');
		extHostLogService.trace('initData', initData);
J
Joao Moreno 已提交
78

79 80
		this._extensionService = instaService.createInstance(
			ExtHostExtensionService,
J
Johannes Rieken 已提交
81
			hostUtils,
82
			uriTransformer
A
Alex Dima 已提交
83
		);
84

85
		// error forwarding and stack trace scanning
B
Benjamin Pasero 已提交
86
		Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API)
87
		const extensionErrors = new WeakMap<Error, IExtensionDescription>();
88 89 90
		this._extensionService.getExtensionPathIndex().then(map => {
			(<any>Error).prepareStackTrace = (error: Error, stackTrace: errors.V8CallSite[]) => {
				let stackTraceMessage = '';
91
				let extension: IExtensionDescription | undefined;
J
Johannes Rieken 已提交
92
				let fileName: string;
93 94
				for (const call of stackTrace) {
					stackTraceMessage += `\n\tat ${call.toString()}`;
J
Johannes Rieken 已提交
95 96 97 98 99
					fileName = call.getFileName();
					if (!extension && fileName) {
						extension = map.findSubstr(fileName);
					}

100
				}
101 102
				extensionErrors.set(error, extension);
				return `${error.name || 'Error'}: ${error.message || ''}${stackTraceMessage}`;
103 104
			};
		});
B
Benjamin Pasero 已提交
105

106 107
		const mainThreadExtensions = rpcProtocol.getProxy(MainContext.MainThreadExtensionService);
		const mainThreadErrors = rpcProtocol.getProxy(MainContext.MainThreadErrors);
108 109
		errors.setUnexpectedErrorHandler(err => {
			const data = errors.transformErrorForSerialization(err);
110
			const extension = extensionErrors.get(err);
111
			if (extension) {
112
				mainThreadExtensions.$onExtensionRuntimeError(extension.identifier, data);
113 114 115
			} else {
				mainThreadErrors.$onUnexpectedError(data);
			}
116
		});
E
Erich Gamma 已提交
117 118
	}

B
Benjamin Pasero 已提交
119
	terminate(): void {
120 121 122 123 124 125
		if (this._isTerminating) {
			// we are already shutting down...
			return;
		}
		this._isTerminating = true;

J
Johannes Rieken 已提交
126
		this._disposables.dispose();
127

128 129 130 131
		errors.setUnexpectedErrorHandler((err) => {
			// TODO: write to log once we have one
		});

A
Alex Dima 已提交
132
		const extensionsDeactivated = this._extensionService.deactivateAll();
133

A
Alex Dima 已提交
134
		// Give extensions 1 second to wrap up any async dispose, then exit in at most 4 seconds
135
		setTimeout(() => {
J
Johannes Rieken 已提交
136
			Promise.race([timeout(4000), extensionsDeactivated]).finally(() => this._hostUtils.exit());
137 138 139
		}, 1000);
	}

J
Johannes Rieken 已提交
140
	private static _transform(initData: IInitData, rpcProtocol: RPCProtocol): IInitData {
A
Alex Dima 已提交
141 142 143
		initData.extensions.forEach((ext) => (<any>ext).extensionLocation = URI.revive(rpcProtocol.transformIncomingURIs(ext.extensionLocation)));
		initData.environment.appRoot = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appRoot));
		initData.environment.appSettingsHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appSettingsHome));
144 145 146 147
		const extDevLocs = initData.environment.extensionDevelopmentLocationURI;
		if (extDevLocs) {
			initData.environment.extensionDevelopmentLocationURI = extDevLocs.map(url => URI.revive(rpcProtocol.transformIncomingURIs(url)));
		}
148
		initData.environment.extensionTestsLocationURI = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.extensionTestsLocationURI));
A
Alex Dima 已提交
149
		initData.environment.globalStorageHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.globalStorageHome));
D
Daniel Imms 已提交
150
		initData.environment.userHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.userHome));
A
Alex Dima 已提交
151
		initData.logsLocation = URI.revive(rpcProtocol.transformIncomingURIs(initData.logsLocation));
152
		initData.workspace = rpcProtocol.transformIncomingURIs(initData.workspace);
153
		return initData;
E
Erich Gamma 已提交
154
	}
155
}