extensionService.ts 29.5 KB
Newer Older
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

7
import * as nls from 'vs/nls';
A
Alex Dima 已提交
8
import * as errors from 'vs/base/common/errors';
9
import * as objects from 'vs/base/common/objects';
10 11 12 13
import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
import pkg from 'vs/platform/node/package';
import * as path from 'path';
14
import * as pfs from 'vs/base/node/pfs';
15
import URI from 'vs/base/common/uri';
16
import * as platform from 'vs/base/common/platform';
B
Benjamin Pasero 已提交
17
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
A
Alex Dima 已提交
18
import { IMessage, IExtensionDescription, IExtensionsStatus, IExtensionService, ExtensionPointContribution, ActivationTimes, IExtensionHostInformation, ProfileSession, USER_MANIFEST_CACHE_FILE, BUILTIN_MANIFEST_CACHE_FILE, MANIFEST_CACHE_FOLDER } from 'vs/platform/extensions/common/extensions';
S
Sandeep Somavarapu 已提交
19 20
import { IExtensionEnablementService, IExtensionIdentifier, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, BetterMergeId, BetterMergeDisabledNowKey } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
A
Alex Dima 已提交
21
import { ExtensionsRegistry, ExtensionPoint, IExtensionPointUser, ExtensionMessageCollector, IExtensionPoint } from 'vs/platform/extensions/common/extensionsRegistry';
22
import { ExtensionScanner, ILog, ExtensionScannerInput } from 'vs/workbench/services/extensions/electron-browser/extensionPoints';
23
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
24
import { ProxyIdentifier } from 'vs/workbench/services/thread/common/threadService';
25
import { ExtHostContext, ExtHostExtensionServiceShape, IExtHostContext, MainContext } from 'vs/workbench/api/node/extHost.protocol';
26 27 28
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IStorageService } from 'vs/platform/storage/common/storage';
B
Benjamin Pasero 已提交
29
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
30 31
import { ExtensionHostProcessWorker } from 'vs/workbench/services/extensions/electron-browser/extensionHost';
import { MainThreadService } from 'vs/workbench/services/thread/electron-browser/threadService';
B
Benjamin Pasero 已提交
32
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
33
import { ExtHostCustomersRegistry } from 'vs/workbench/api/electron-browser/extHostCustomers';
B
Benjamin Pasero 已提交
34 35
import { IWindowService } from 'vs/platform/windows/common/windows';
import { Action } from 'vs/base/common/actions';
A
Alex Dima 已提交
36
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
37
import { mark, time } from 'vs/base/common/performance';
38
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
J
Joao Moreno 已提交
39
import { Barrier } from 'vs/base/common/async';
40
import Event, { Emitter } from 'vs/base/common/event';
41
import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler';
42
import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions';
43
import product from 'vs/platform/node/product';
44 45 46

const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions'));

47
function messageWithSource(msg: IMessage): string {
48 49 50 51 52 53 54 55
	return messageWithSource2(msg.source, msg.message);
}

function messageWithSource2(source: string, message: string): string {
	if (source) {
		return `[${source}]: ${message}`;
	}
	return message;
56 57
}

58
const hasOwnProperty = Object.hasOwnProperty;
59
const NO_OP_VOID_PROMISE = TPromise.wrap<void>(void 0);
60

A
Alex Dima 已提交
61
export class ExtensionService extends Disposable implements IExtensionService {
62
	public _serviceBrand: any;
63

64 65
	private _onDidRegisterExtensions: Emitter<IExtensionDescription[]>;

66
	private _registry: ExtensionDescriptionRegistry;
67
	private readonly _installedExtensionsReady: Barrier;
68
	private readonly _isDev: boolean;
A
Alex Dima 已提交
69
	private readonly _extensionsMessages: { [id: string]: IMessage[] };
A
Alex Dima 已提交
70 71
	private _allRequestedActivateEvents: { [activationEvent: string]: boolean; };

A
Alex Dima 已提交
72 73
	private readonly _onDidChangeExtensionsStatus: Emitter<string[]> = this._register(new Emitter<string[]>());
	public readonly onDidChangeExtensionsStatus: Event<string[]> = this._onDidChangeExtensionsStatus.event;
A
Alex Dima 已提交
74 75 76

	// --- Members used per extension host process

77
	/**
78
	 * A map of already activated events to speed things up if the same activation event is triggered multiple times.
79
	 */
A
Alex Dima 已提交
80
	private _extensionHostProcessFinishedActivateEvents: { [activationEvent: string]: boolean; };
81
	private _extensionHostProcessActivationTimes: { [id: string]: ActivationTimes; };
82
	private _extensionHostExtensionRuntimeErrors: { [id: string]: Error[]; };
A
Alex Dima 已提交
83 84 85 86 87 88 89
	private _extensionHostProcessWorker: ExtensionHostProcessWorker;
	private _extensionHostProcessThreadService: MainThreadService;
	private _extensionHostProcessCustomers: IDisposable[];
	/**
	 * winjs believes a proxy is a promise because it has a `then` method, so wrap the result in an object.
	 */
	private _extensionHostProcessProxy: TPromise<{ value: ExtHostExtensionServiceShape; }>;
90

91 92 93
	constructor(
		@IInstantiationService private readonly _instantiationService: IInstantiationService,
		@IMessageService private readonly _messageService: IMessageService,
94
		@IEnvironmentService private readonly _environmentService: IEnvironmentService,
95
		@ITelemetryService private readonly _telemetryService: ITelemetryService,
96 97
		@IExtensionEnablementService private readonly _extensionEnablementService: IExtensionEnablementService,
		@IStorageService private readonly _storageService: IStorageService,
98 99
		@IWindowService private readonly _windowService: IWindowService,
		@ILifecycleService lifecycleService: ILifecycleService
100
	) {
A
Alex Dima 已提交
101
		super();
102
		this._registry = null;
103
		this._installedExtensionsReady = new Barrier();
104
		this._isDev = !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment;
A
Alex Dima 已提交
105
		this._extensionsMessages = {};
A
Alex Dima 已提交
106 107
		this._allRequestedActivateEvents = Object.create(null);

108 109
		this._onDidRegisterExtensions = new Emitter<IExtensionDescription[]>();

A
Alex Dima 已提交
110
		this._extensionHostProcessFinishedActivateEvents = Object.create(null);
111
		this._extensionHostProcessActivationTimes = Object.create(null);
112
		this._extensionHostExtensionRuntimeErrors = Object.create(null);
A
Alex Dima 已提交
113 114 115 116 117
		this._extensionHostProcessWorker = null;
		this._extensionHostProcessThreadService = null;
		this._extensionHostProcessCustomers = [];
		this._extensionHostProcessProxy = null;

118
		lifecycleService.when(LifecyclePhase.Running).then(() => {
119
			// delay extension host creation and extension scanning
120
			// until after workbench is running
121 122
			this._startExtensionHostProcess([]);
			this._scanAndHandleExtensions();
123
		});
A
Alex Dima 已提交
124 125
	}

A
Alex Dima 已提交
126 127 128 129
	public dispose(): void {
		super.dispose();
	}

130 131 132 133
	public get onDidRegisterExtensions(): Event<IExtensionDescription[]> {
		return this._onDidRegisterExtensions.event;
	}

134 135 136 137 138 139 140 141 142
	public getExtensionHostInformation(): IExtensionHostInformation {
		if (!this._extensionHostProcessWorker) {
			throw errors.illegalState();
		}
		return <IExtensionHostInformation>{
			inspectPort: this._extensionHostProcessWorker.getInspectPort()
		};
	}

143 144 145 146 147
	public restartExtensionHost(): void {
		this._stopExtensionHostProcess();
		this._startExtensionHostProcess(Object.keys(this._allRequestedActivateEvents));
	}

148 149 150 151 152 153 154 155
	public startExtensionHost(): void {
		this._startExtensionHostProcess(Object.keys(this._allRequestedActivateEvents));
	}

	public stopExtensionHost(): void {
		this._stopExtensionHostProcess();
	}

A
Alex Dima 已提交
156
	private _stopExtensionHostProcess(): void {
A
Alex Dima 已提交
157 158
		const previouslyActivatedExtensionIds = Object.keys(this._extensionHostProcessActivationTimes);

A
Alex Dima 已提交
159
		this._extensionHostProcessFinishedActivateEvents = Object.create(null);
160
		this._extensionHostProcessActivationTimes = Object.create(null);
161
		this._extensionHostExtensionRuntimeErrors = Object.create(null);
A
Alex Dima 已提交
162 163 164 165 166
		if (this._extensionHostProcessWorker) {
			this._extensionHostProcessWorker.dispose();
			this._extensionHostProcessWorker = null;
		}
		if (this._extensionHostProcessThreadService) {
167
			this._extensionHostProcessThreadService.dispose();
A
Alex Dima 已提交
168 169 170 171 172 173 174 175
			this._extensionHostProcessThreadService = null;
		}
		for (let i = 0, len = this._extensionHostProcessCustomers.length; i < len; i++) {
			const customer = this._extensionHostProcessCustomers[i];
			try {
				customer.dispose();
			} catch (err) {
				errors.onUnexpectedError(err);
176
			}
A
Alex Dima 已提交
177 178 179
		}
		this._extensionHostProcessCustomers = [];
		this._extensionHostProcessProxy = null;
A
Alex Dima 已提交
180 181

		this._onDidChangeExtensionsStatus.fire(previouslyActivatedExtensionIds);
A
Alex Dima 已提交
182 183 184 185 186 187 188 189
	}

	private _startExtensionHostProcess(initialActivationEvents: string[]): void {
		this._stopExtensionHostProcess();

		this._extensionHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, this);
		this._extensionHostProcessWorker.onCrashed(([code, signal]) => this._onExtensionHostCrashed(code, signal));
		this._extensionHostProcessProxy = this._extensionHostProcessWorker.start().then(
190
			(protocol) => {
A
Alex Dima 已提交
191
				return { value: this._createExtensionHostCustomers(protocol) };
192 193 194 195
			},
			(err) => {
				console.error('Error received from starting extension host');
				console.error(err);
A
Alex Dima 已提交
196
				return null;
197 198
			}
		);
A
Alex Dima 已提交
199 200 201 202
		this._extensionHostProcessProxy.then(() => {
			initialActivationEvents.forEach((activationEvent) => this.activateByEvent(activationEvent));
		});
	}
203

A
Alex Dima 已提交
204 205 206 207 208 209
	private _onExtensionHostCrashed(code: number, signal: string): void {
		const openDevTools = new Action('openDevTools', nls.localize('devTools', "Developer Tools"), '', true, (): TPromise<boolean> => {
			return this._windowService.openDevTools().then(() => false);
		});

		const restart = new Action('restart', nls.localize('restart', "Restart Extension Host"), '', true, (): TPromise<boolean> => {
A
Alex Dima 已提交
210
			this._messageService.hideAll();
A
Alex Dima 已提交
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
			this._startExtensionHostProcess(Object.keys(this._allRequestedActivateEvents));
			return TPromise.as(true);
		});

		console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal);
		this._stopExtensionHostProcess();

		let message = nls.localize('extensionHostProcess.crash', "Extension host terminated unexpectedly.");
		if (code === 87) {
			message = nls.localize('extensionHostProcess.unresponsiveCrash', "Extension host terminated because it was not responsive.");
		}
		this._messageService.show(Severity.Error, {
			message: message,
			actions: [
				openDevTools,
				restart
			]
		});
229 230
	}

A
Alex Dima 已提交
231
	private _createExtensionHostCustomers(protocol: IMessagePassingProtocol): ExtHostExtensionServiceShape {
232

A
Alex Dima 已提交
233 234
		this._extensionHostProcessThreadService = this._instantiationService.createInstance(MainThreadService, protocol);
		const extHostContext: IExtHostContext = this._extensionHostProcessThreadService;
235

236 237 238 239
		// Named customers
		const namedCustomers = ExtHostCustomersRegistry.getNamedCustomers();
		for (let i = 0, len = namedCustomers.length; i < len; i++) {
			const [id, ctor] = namedCustomers[i];
A
Alex Dima 已提交
240 241 242
			const instance = this._instantiationService.createInstance(ctor, extHostContext);
			this._extensionHostProcessCustomers.push(instance);
			this._extensionHostProcessThreadService.set(id, instance);
243
		}
244

245 246 247 248
		// Customers
		const customers = ExtHostCustomersRegistry.getCustomers();
		for (let i = 0, len = customers.length; i < len; i++) {
			const ctor = customers[i];
A
Alex Dima 已提交
249 250
			const instance = this._instantiationService.createInstance(ctor, extHostContext);
			this._extensionHostProcessCustomers.push(instance);
251
		}
252

253 254
		// Check that no named customers are missing
		const expected: ProxyIdentifier<any>[] = Object.keys(MainContext).map((key) => MainContext[key]);
A
Alex Dima 已提交
255
		this._extensionHostProcessThreadService.assertRegistered(expected);
256

A
Alex Dima 已提交
257
		return this._extensionHostProcessThreadService.get(ExtHostContext.ExtHostExtensionService);
258
	}
259

260
	// ---- begin IExtensionService
261

262
	public activateByEvent(activationEvent: string): TPromise<void> {
263
		if (this._installedExtensionsReady.isOpen()) {
A
Alex Dima 已提交
264 265 266 267 268 269 270 271 272 273
			// Extensions have been scanned and interpreted

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

			// Record the fact that this activationEvent was requested (in case of a restart)
			this._allRequestedActivateEvents[activationEvent] = true;

274 275
			return this._activateByEvent(activationEvent);
		} else {
A
Alex Dima 已提交
276 277 278 279 280
			// Extensions have not been scanned yet.

			// Record the fact that this activationEvent was requested (in case of a restart)
			this._allRequestedActivateEvents[activationEvent] = true;

281
			return this._installedExtensionsReady.wait().then(() => this._activateByEvent(activationEvent));
282 283
		}
	}
284

285
	protected _activateByEvent(activationEvent: string): TPromise<void> {
286
		if (this._extensionHostProcessFinishedActivateEvents[activationEvent] || !this._extensionHostProcessProxy) {
287
			return NO_OP_VOID_PROMISE;
288
		}
A
Alex Dima 已提交
289
		return this._extensionHostProcessProxy.then((proxy) => {
290 291
			return proxy.value.$activateByEvent(activationEvent);
		}).then(() => {
A
Alex Dima 已提交
292
			this._extensionHostProcessFinishedActivateEvents[activationEvent] = true;
293
		});
294 295
	}

296 297
	public whenInstalledExtensionsRegistered(): TPromise<boolean> {
		return this._installedExtensionsReady.wait();
298
	}
299

A
Alex Dima 已提交
300
	public getExtensions(): TPromise<IExtensionDescription[]> {
301
		return this._installedExtensionsReady.wait().then(() => {
A
Alex Dima 已提交
302 303 304 305 306
			return this._registry.getAllExtensionDescriptions();
		});
	}

	public readExtensionPointContributions<T>(extPoint: IExtensionPoint<T>): TPromise<ExtensionPointContribution<T>[]> {
307
		return this._installedExtensionsReady.wait().then(() => {
A
Alex Dima 已提交
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
			let availableExtensions = this._registry.getAllExtensionDescriptions();

			let result: ExtensionPointContribution<T>[] = [], resultLen = 0;
			for (let i = 0, len = availableExtensions.length; i < len; i++) {
				let desc = availableExtensions[i];

				if (desc.contributes && hasOwnProperty.call(desc.contributes, extPoint.name)) {
					result[resultLen++] = new ExtensionPointContribution<T>(desc, desc.contributes[extPoint.name]);
				}
			}

			return result;
		});
	}

323
	public getExtensionsStatus(): { [id: string]: IExtensionsStatus; } {
A
Alex Dima 已提交
324
		let result: { [id: string]: IExtensionsStatus; } = Object.create(null);
A
Alex Dima 已提交
325 326 327 328 329 330 331
		if (this._registry) {
			const extensions = this._registry.getAllExtensionDescriptions();
			for (let i = 0, len = extensions.length; i < len; i++) {
				const extension = extensions[i];
				const id = extension.id;
				result[id] = {
					messages: this._extensionsMessages[id],
332 333
					activationTimes: this._extensionHostProcessActivationTimes[id],
					runtimeErrors: this._extensionHostExtensionRuntimeErrors[id],
A
Alex Dima 已提交
334 335
				};
			}
A
Alex Dima 已提交
336 337
		}
		return result;
338 339
	}

J
Johannes Rieken 已提交
340 341 342 343
	public canProfileExtensionHost(): boolean {
		return this._extensionHostProcessWorker && Boolean(this._extensionHostProcessWorker.getInspectPort());
	}

344 345 346 347 348 349 350 351
	public startExtensionHostProfile(): TPromise<ProfileSession> {
		if (this._extensionHostProcessWorker) {
			let port = this._extensionHostProcessWorker.getInspectPort();
			if (port) {
				return this._instantiationService.createInstance(ExtensionHostProfiler, port).start();
			}
		}
		throw new Error('Extension host not running or no inspect port available');
352 353
	}

354
	// ---- end IExtensionService
355

356 357
	// --- impl

358
	private _scanAndHandleExtensions(): void {
359

S
Sandeep Somavarapu 已提交
360 361 362
		this._getRuntimeExtension()
			.then(runtimeExtensons => {
				this._registry = new ExtensionDescriptionRegistry(runtimeExtensons);
S
Sandeep Somavarapu 已提交
363 364 365 366 367 368 369 370 371 372 373 374 375

				let availableExtensions = this._registry.getAllExtensionDescriptions();
				let extensionPoints = ExtensionsRegistry.getExtensionPoints();

				let messageHandler = (msg: IMessage) => this._handleExtensionPointMessage(msg);

				for (let i = 0, len = extensionPoints.length; i < len; i++) {
					const clock = time(`handleExtensionPoint:${extensionPoints[i].name}`);
					try {
						ExtensionService._handleExtensionPoint(extensionPoints[i], availableExtensions, messageHandler);
					} finally {
						clock.stop();
					}
K
kieferrm 已提交
376
				}
377

S
Sandeep Somavarapu 已提交
378 379 380
				mark('extensionHostReady');
				this._installedExtensionsReady.open();
				this._onDidRegisterExtensions.fire(availableExtensions);
A
Alex Dima 已提交
381
				this._onDidChangeExtensionsStatus.fire(availableExtensions.map(e => e.id));
S
Sandeep Somavarapu 已提交
382 383
			});
	}
384

S
Sandeep Somavarapu 已提交
385 386 387 388 389
	private _getRuntimeExtension(): TPromise<IExtensionDescription[]> {
		const log = new Logger((severity, source, message) => {
			this._logOrShowMessage(severity, this._isDev ? messageWithSource2(source, message) : message);
		});

390
		return ExtensionService._scanInstalledExtensions(this._instantiationService, this._messageService, this._environmentService, log)
S
Sandeep Somavarapu 已提交
391 392 393 394 395 396 397 398 399 400 401
			.then(({ system, user, development }) => {
				this._extensionEnablementService.migrateToIdentifiers(user); // TODO: @sandy Remove it after couple of milestones
				return this._extensionEnablementService.getDisabledExtensions()
					.then(disabledExtensions => {
						let result: { [extensionId: string]: IExtensionDescription; } = {};
						let extensionsToDisable: IExtensionIdentifier[] = [];
						let userMigratedSystemExtensions: IExtensionIdentifier[] = [{ id: BetterMergeId }];

						system.forEach((systemExtension) => {
							// Disabling system extensions is not supported
							result[systemExtension.id] = systemExtension;
S
Sandeep Somavarapu 已提交
402
						});
S
Sandeep Somavarapu 已提交
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444

						user.forEach((userExtension) => {
							if (result.hasOwnProperty(userExtension.id)) {
								log.warn(userExtension.extensionFolderPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[userExtension.id].extensionFolderPath, userExtension.extensionFolderPath));
							}
							if (disabledExtensions.every(disabled => !areSameExtensions(disabled, userExtension))) {
								// Check if the extension is changed to system extension
								let userMigratedSystemExtension = userMigratedSystemExtensions.filter(userMigratedSystemExtension => areSameExtensions(userMigratedSystemExtension, { id: userExtension.id }))[0];
								if (userMigratedSystemExtension) {
									extensionsToDisable.push(userMigratedSystemExtension);
								} else {
									result[userExtension.id] = userExtension;
								}
							}
						});

						development.forEach(developedExtension => {
							log.info('', nls.localize('extensionUnderDevelopment', "Loading development extension at {0}", developedExtension.extensionFolderPath));
							if (result.hasOwnProperty(developedExtension.id)) {
								log.warn(developedExtension.extensionFolderPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[developedExtension.id].extensionFolderPath, developedExtension.extensionFolderPath));
							}
							// Do not disable extensions under development
							result[developedExtension.id] = developedExtension;
						});

						const runtimeExtensions = Object.keys(result).map(name => result[name]);

						this._telemetryService.publicLog('extensionsScanned', {
							totalCount: runtimeExtensions.length,
							disabledCount: disabledExtensions.length
						});

						if (extensionsToDisable.length) {
							return TPromise.join(extensionsToDisable.map(e => this._extensionEnablementService.setEnablement(e, EnablementState.Disabled)))
								.then(() => {
									this._storageService.store(BetterMergeDisabledNowKey, true);
									return runtimeExtensions;
								});
						} else {
							return runtimeExtensions;
						}
					});
S
Sandeep Somavarapu 已提交
445
			});
446 447
	}

448 449
	private _handleExtensionPointMessage(msg: IMessage) {

A
Alex Dima 已提交
450 451
		if (!this._extensionsMessages[msg.source]) {
			this._extensionsMessages[msg.source] = [];
452
		}
A
Alex Dima 已提交
453
		this._extensionsMessages[msg.source].push(msg);
454 455 456 457 458 459 460 461 462 463

		if (msg.source === this._environmentService.extensionDevelopmentPath) {
			// This message is about the extension currently being developed
			this._showMessageToUser(msg.type, messageWithSource(msg));
		} else {
			this._logMessageInConsole(msg.type, messageWithSource(msg));
		}

		if (!this._isDev && msg.extensionId) {
			const { type, extensionId, extensionPointId, message } = msg;
K
kieferrm 已提交
464
			/* __GDPR__
K
kieferrm 已提交
465 466 467 468 469 470 471
				"extensionsMessage" : {
					"type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
					"extensionId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
					"extensionPointId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
					"message": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
				}
			*/
472 473 474 475 476 477
			this._telemetryService.publicLog('extensionsMessage', {
				type, extensionId, extensionPointId, message
			});
		}
	}

478
	private static async _validateExtensionsCache(instantiationService: IInstantiationService, messageService: IMessageService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput): TPromise<void> {
A
Alex Dima 已提交
479
		const cacheFolder = path.join(environmentService.userDataPath, MANIFEST_CACHE_FOLDER);
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
		const cacheFile = path.join(cacheFolder, cacheKey);

		const expected = await ExtensionScanner.scanExtensions(input, new NullLogger());

		const cacheContents = await this._readExtensionCache(environmentService, cacheKey);
		const actual = cacheContents.result;

		if (objects.equals(expected, actual)) {
			// Cache is valid and running with it is perfectly fine...
			return;
		}

		try {
			await pfs.del(cacheFile);
		} catch (err) {
			errors.onUnexpectedError(err);
			console.error(err);
		}

		let message = nls.localize('extensionCache.invalid', "Extensions have been modified on disk. Please reload the window.");
		messageService.show(Severity.Info, {
			message: message,
			actions: [
				instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL),
				CloseAction
			]
		});
	}

	private static async _readExtensionCache(environmentService: IEnvironmentService, cacheKey: string): TPromise<IExtensionCacheData> {
A
Alex Dima 已提交
510
		const cacheFolder = path.join(environmentService.userDataPath, MANIFEST_CACHE_FOLDER);
511 512 513 514
		const cacheFile = path.join(cacheFolder, cacheKey);

		try {
			const cacheRawContents = await pfs.readFile(cacheFile, 'utf8');
515
			return JSON.parse(cacheRawContents);
516 517 518 519
		} catch (err) {
			// That's ok...
		}

520 521 522 523
		return null;
	}

	private static async _writeExtensionCache(environmentService: IEnvironmentService, cacheKey: string, cacheContents: IExtensionCacheData): TPromise<void> {
A
Alex Dima 已提交
524
		const cacheFolder = path.join(environmentService.userDataPath, MANIFEST_CACHE_FOLDER);
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
		const cacheFile = path.join(cacheFolder, cacheKey);

		try {
			await pfs.mkdirp(cacheFolder);
		} catch (err) {
			// That's ok...
		}

		try {
			await pfs.writeFile(cacheFile, JSON.stringify(cacheContents));
		} catch (err) {
			// That's ok...
		}
	}

	private static async _scanExtensionsWithCache(instantiationService: IInstantiationService, messageService: IMessageService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput, log: ILog): TPromise<IExtensionDescription[]> {
541 542 543 544 545
		if (input.devMode) {
			// Do not cache when running out of sources...
			return ExtensionScanner.scanExtensions(input, log);
		}

546 547 548 549 550 551 552
		try {
			const folderStat = await pfs.stat(input.absoluteFolderPath);
			input.mtime = folderStat.mtime.getTime();
		} catch (err) {
			// That's ok...
		}

553
		const cacheContents = await this._readExtensionCache(environmentService, cacheKey);
554
		if (cacheContents && cacheContents.input && ExtensionScannerInput.equals(cacheContents.input, input)) {
555 556 557 558 559 560 561 562 563 564 565
			// Validate the cache asynchronously after 5s
			setTimeout(async () => {
				try {
					await this._validateExtensionsCache(instantiationService, messageService, environmentService, cacheKey, input);
				} catch (err) {
					errors.onUnexpectedError(err);
				}
			}, 5000);
			return cacheContents.result;
		}

566 567
		const counterLogger = new CounterLogger(log);
		const result = await ExtensionScanner.scanExtensions(input, counterLogger);
A
Alex Dima 已提交
568
		if (counterLogger.errorCnt === 0) {
569
			// Nothing bad happened => cache the result
570 571 572 573 574
			const cacheContents: IExtensionCacheData = {
				input: input,
				result: result
			};
			await this._writeExtensionCache(environmentService, cacheKey, cacheContents);
575 576 577 578 579
		}

		return result;
	}

580
	private static _scanInstalledExtensions(instantiationService: IInstantiationService, messageService: IMessageService, environmentService: IEnvironmentService, log: ILog): TPromise<{ system: IExtensionDescription[], user: IExtensionDescription[], development: IExtensionDescription[] }> {
581
		const version = pkg.version;
582
		const commit = product.commit;
583 584 585
		const devMode = !!process.env['VSCODE_DEV'];
		const locale = platform.locale;

586 587 588
		const builtinExtensions = this._scanExtensionsWithCache(
			instantiationService,
			messageService,
589
			environmentService,
A
Alex Dima 已提交
590
			BUILTIN_MANIFEST_CACHE_FILE,
591
			new ExtensionScannerInput(version, commit, locale, devMode, SystemExtensionsRoot, true),
592
			log
593 594 595 596 597
		);

		const userExtensions = (
			environmentService.disableExtensions || !environmentService.extensionsPath
				? TPromise.as([])
598 599 600
				: this._scanExtensionsWithCache(
					instantiationService,
					messageService,
601
					environmentService,
A
Alex Dima 已提交
602
					USER_MANIFEST_CACHE_FILE,
603
					new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionsPath, false),
604
					log
605 606 607
				)
		);

S
Sandeep Somavarapu 已提交
608
		// Always load developed extensions while extensions development
609 610 611
		const developedExtensions = (
			environmentService.isExtensionDevelopment
				? ExtensionScanner.scanOneOrMultipleExtensions(
612
					new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionDevelopmentPath, false), log
613 614 615
				)
				: TPromise.as([])
		);
S
Sandeep Somavarapu 已提交
616 617 618 619 620 621 622 623 624 625

		return TPromise.join([builtinExtensions, userExtensions, developedExtensions])
			.then((extensionDescriptions: IExtensionDescription[][]) => {
				const system = extensionDescriptions[0];
				const user = extensionDescriptions[1];
				const development = extensionDescriptions[2];
				return { system, user, development };
			}).then(null, err => {
				log.error('', err);
				return { system: [], user: [], development: [] };
626 627
			});
	}
628

629
	private static _handleExtensionPoint<T>(extensionPoint: ExtensionPoint<T>, availableExtensions: IExtensionDescription[], messageHandler: (msg: IMessage) => void): void {
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
		let users: IExtensionPointUser<T>[] = [], usersLen = 0;
		for (let i = 0, len = availableExtensions.length; i < len; i++) {
			let desc = availableExtensions[i];

			if (desc.contributes && hasOwnProperty.call(desc.contributes, extensionPoint.name)) {
				users[usersLen++] = {
					description: desc,
					value: desc.contributes[extensionPoint.name],
					collector: new ExtensionMessageCollector(messageHandler, desc, extensionPoint.name)
				};
			}
		}

		extensionPoint.acceptUsers(users);
	}

646 647
	private _showMessageToUser(severity: Severity, msg: string): void {
		if (severity === Severity.Error || severity === Severity.Warning) {
648
			this._messageService.show(severity, msg);
649 650 651 652 653 654 655
		} else {
			this._logMessageInConsole(severity, msg);
		}
	}

	private _logMessageInConsole(severity: Severity, msg: string): void {
		if (severity === Severity.Error) {
656 657 658 659 660 661
			console.error(msg);
		} else if (severity === Severity.Warning) {
			console.warn(msg);
		} else {
			console.log(msg);
		}
662
	}
663 664 665 666 667 668 669 670 671 672

	// -- called by extension host

	public _logOrShowMessage(severity: Severity, msg: string): void {
		if (this._isDev) {
			this._showMessageToUser(severity, msg);
		} else {
			this._logMessageInConsole(severity, msg);
		}
	}
673

A
Alex Dima 已提交
674 675
	public _onExtensionActivated(extensionId: string, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void {
		this._extensionHostProcessActivationTimes[extensionId] = new ActivationTimes(startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent);
A
Alex Dima 已提交
676
		this._onDidChangeExtensionsStatus.fire([extensionId]);
677
	}
A
Alex Dima 已提交
678

679 680 681 682 683 684 685 686
	public _onExtensionRuntimeError(extensionId: string, err: Error): void {
		if (!this._extensionHostExtensionRuntimeErrors[extensionId]) {
			this._extensionHostExtensionRuntimeErrors[extensionId] = [];
		}
		this._extensionHostExtensionRuntimeErrors[extensionId].push(err);
		this._onDidChangeExtensionsStatus.fire([extensionId]);
	}

A
Alex Dima 已提交
687 688 689 690 691 692 693 694 695 696 697 698 699
	public _addMessage(extensionId: string, severity: Severity, message: string): void {
		if (!this._extensionsMessages[extensionId]) {
			this._extensionsMessages[extensionId] = [];
		}
		this._extensionsMessages[extensionId].push({
			type: severity,
			message: message,
			source: null,
			extensionId: null,
			extensionPointId: null
		});
		this._onDidChangeExtensionsStatus.fire([extensionId]);
	}
700 701
}

702 703 704 705 706
interface IExtensionCacheData {
	input: ExtensionScannerInput;
	result: IExtensionDescription[];
}

707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727
export class Logger implements ILog {

	private readonly _messageHandler: (severity: Severity, source: string, message: string) => void;

	constructor(
		messageHandler: (severity: Severity, source: string, message: string) => void
	) {
		this._messageHandler = messageHandler;
	}

	public error(source: string, message: string): void {
		this._messageHandler(Severity.Error, source, message);
	}

	public warn(source: string, message: string): void {
		this._messageHandler(Severity.Warning, source, message);
	}

	public info(source: string, message: string): void {
		this._messageHandler(Severity.Info, source, message);
	}
728
}
729

730
class CounterLogger implements ILog {
731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750

	public errorCnt = 0;
	public warnCnt = 0;
	public infoCnt = 0;

	constructor(private readonly _actual: ILog) {
	}

	public error(source: string, message: string): void {
		this._actual.error(source, message);
	}

	public warn(source: string, message: string): void {
		this._actual.warn(source, message);
	}

	public info(source: string, message: string): void {
		this._actual.info(source, message);
	}
}
751 752 753 754 755 756 757 758 759

class NullLogger implements ILog {
	public error(source: string, message: string): void {
	}
	public warn(source: string, message: string): void {
	}
	public info(source: string, message: string): void {
	}
}