extensionHostMain.ts 6.7 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
import { getSingletonServiceDescriptors } from 'vs/platform/instantiation/common/extensions';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
20
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
21 22
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
23
import { IExtHostRpcService, ExtHostRpcService } from 'vs/workbench/api/common/rpcService';
E
Erich Gamma 已提交
24

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

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

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

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

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

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

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

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

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

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

70 71
		// bootstrap services
		const services = new ServiceCollection(...getSingletonServiceDescriptors());
72 73
		services.set(IExtHostInitDataService, { _serviceBrand: undefined, ...initData });
		services.set(IExtHostRpcService, new ExtHostRpcService(rpcProtocol));
74 75
		services.set(ILogService, extHostLogService);

76
		const instaService: IInstantiationService = new InstantiationService(services, true);
J
Joao Moreno 已提交
77

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

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

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

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

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

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

J
Johannes Rieken 已提交
129
		this._disposables.dispose();
130

131 132 133 134
		errors.setUnexpectedErrorHandler((err) => {
			// TODO: write to log once we have one
		});

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

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

J
Johannes Rieken 已提交
143
	private static _transform(initData: IInitData, rpcProtocol: RPCProtocol): IInitData {
A
Alex Dima 已提交
144 145 146
		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));
147 148 149 150
		const extDevLocs = initData.environment.extensionDevelopmentLocationURI;
		if (extDevLocs) {
			initData.environment.extensionDevelopmentLocationURI = extDevLocs.map(url => URI.revive(rpcProtocol.transformIncomingURIs(url)));
		}
151
		initData.environment.extensionTestsLocationURI = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.extensionTestsLocationURI));
A
Alex Dima 已提交
152
		initData.environment.globalStorageHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.globalStorageHome));
D
Daniel Imms 已提交
153
		initData.environment.userHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.userHome));
A
Alex Dima 已提交
154
		initData.logsLocation = URI.revive(rpcProtocol.transformIncomingURIs(initData.logsLocation));
155
		initData.workspace = rpcProtocol.transformIncomingURIs(initData.workspace);
156
		return initData;
E
Erich Gamma 已提交
157
	}
158
}