mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-20 23:29:57 +01:00
Spawn the extension host process from the shared process
This commit is contained in:
@@ -1071,12 +1071,19 @@ export namespace ProxyChannel {
|
||||
|
||||
return new class implements IServerChannel {
|
||||
|
||||
listen<T>(_: unknown, event: string): Event<T> {
|
||||
listen<T>(_: unknown, event: string, arg: any): Event<T> {
|
||||
const eventImpl = mapEventNameToEvent.get(event);
|
||||
if (eventImpl) {
|
||||
return eventImpl as Event<T>;
|
||||
}
|
||||
|
||||
if (propertyIsDynamicEvent(event)) {
|
||||
const target = handler[event];
|
||||
if (typeof target === 'function') {
|
||||
return target.call(handler, arg);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Event not found: ${event}`);
|
||||
}
|
||||
|
||||
@@ -1126,6 +1133,13 @@ export namespace ProxyChannel {
|
||||
return options.properties.get(propKey);
|
||||
}
|
||||
|
||||
// Dynamic Event
|
||||
if (propertyIsDynamicEvent(propKey)) {
|
||||
return function (arg: any) {
|
||||
return channel.listen(propKey, arg);
|
||||
};
|
||||
}
|
||||
|
||||
// Event
|
||||
if (propertyIsEvent(propKey)) {
|
||||
return channel.listen(propKey);
|
||||
@@ -1162,6 +1176,11 @@ export namespace ProxyChannel {
|
||||
// Assume a property is an event if it has a form of "onSomething"
|
||||
return name[0] === 'o' && name[1] === 'n' && strings.isUpperAsciiLetter(name.charCodeAt(2));
|
||||
}
|
||||
|
||||
function propertyIsDynamicEvent(name: string): boolean {
|
||||
// Assume a property is a dynamic event (a method that returns an event) if it has a form of "onScopedSomething"
|
||||
return /^onScoped/.test(name) && strings.isUpperAsciiLetter(name.charCodeAt(8));
|
||||
}
|
||||
}
|
||||
|
||||
const colorTables = [
|
||||
|
||||
@@ -86,6 +86,8 @@ import { UserDataSyncChannel } from 'vs/platform/userDataSync/common/userDataSyn
|
||||
import { UserDataSyncStoreManagementService, UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
|
||||
import { UserDataAutoSyncService } from 'vs/platform/userDataSync/electron-sandbox/userDataAutoSyncService';
|
||||
import { ActiveWindowManager } from 'vs/platform/windows/node/windowTracker';
|
||||
import { IExtensionHostStarter, ipcExtensionHostStarterChannelName } from 'vs/platform/extensions/common/extensionHostStarter';
|
||||
import { ExtensionHostStarter } from 'vs/platform/extensions/node/extensionHostStarter';
|
||||
|
||||
class SharedProcessMain extends Disposable {
|
||||
|
||||
@@ -289,6 +291,9 @@ class SharedProcessMain extends Disposable {
|
||||
)
|
||||
);
|
||||
|
||||
// Extension Host
|
||||
services.set(IExtensionHostStarter, this._register(new ExtensionHostStarter()));
|
||||
|
||||
return new InstantiationService(services);
|
||||
}
|
||||
|
||||
@@ -339,6 +344,10 @@ class SharedProcessMain extends Disposable {
|
||||
const localPtyService = accessor.get(ILocalPtyService);
|
||||
const localPtyChannel = ProxyChannel.fromService(localPtyService);
|
||||
this.server.registerChannel(TerminalIpcChannels.LocalPty, localPtyChannel);
|
||||
|
||||
// Extension Host
|
||||
const extensionHostStarterChannel = ProxyChannel.fromService(accessor.get(IExtensionHostStarter));
|
||||
this.server.registerChannel(ipcExtensionHostStarterChannelName, extensionHostStarterChannel);
|
||||
}
|
||||
|
||||
private registerErrorHandler(logService: ILogService): void {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { SerializedError } from 'vs/base/common/errors';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export const IExtensionHostStarter = createDecorator<IExtensionHostStarter>('extensionHostStarter');
|
||||
|
||||
export const ipcExtensionHostStarterChannelName = 'extensionHostStarter';
|
||||
|
||||
export interface IExtensionHostProcessOptions {
|
||||
env: { [key: string]: string | undefined; };
|
||||
detached: boolean;
|
||||
execArgv: string[] | undefined;
|
||||
silent: boolean;
|
||||
}
|
||||
|
||||
export interface IExtensionHostStarter {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
onScopedStdout(id: string): Event<string>;
|
||||
onScopedStderr(id: string): Event<string>;
|
||||
onScopedMessage(id: string): Event<any>;
|
||||
onScopedError(id: string): Event<{ error: SerializedError; }>;
|
||||
onScopedExit(id: string): Event<{ code: number; signal: string }>;
|
||||
|
||||
createExtensionHost(): Promise<{ id: string; }>;
|
||||
start(id: string, opts: IExtensionHostProcessOptions): Promise<{ pid: number; }>;
|
||||
enableInspectPort(id: string): Promise<boolean>;
|
||||
kill(id: string): Promise<void>;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { SerializedError, transformErrorForSerialization } from 'vs/base/common/errors';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IExtensionHostProcessOptions, IExtensionHostStarter } from 'vs/platform/extensions/common/extensionHostStarter';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { ChildProcess, fork } from 'child_process';
|
||||
import { FileAccess } from 'vs/base/common/network';
|
||||
import { StringDecoder } from 'string_decoder';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
|
||||
class ExtensionHostProcess extends Disposable {
|
||||
|
||||
readonly _onStdout = this._register(new Emitter<string>());
|
||||
readonly onStdout = this._onStdout.event;
|
||||
|
||||
readonly _onStderr = this._register(new Emitter<string>());
|
||||
readonly onStderr = this._onStderr.event;
|
||||
|
||||
readonly _onMessage = this._register(new Emitter<any>());
|
||||
readonly onMessage = this._onMessage.event;
|
||||
|
||||
readonly _onError = this._register(new Emitter<{ error: SerializedError; }>());
|
||||
readonly onError = this._onError.event;
|
||||
|
||||
readonly _onExit = this._register(new Emitter<{ code: number; signal: string }>());
|
||||
readonly onExit = this._onExit.event;
|
||||
|
||||
private _process: ChildProcess | null = null;
|
||||
|
||||
constructor(
|
||||
public readonly id: string
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
register(disposable: IDisposable) {
|
||||
this._register(disposable);
|
||||
}
|
||||
|
||||
start(opts: IExtensionHostProcessOptions): { pid: number; } {
|
||||
this._process = fork(FileAccess.asFileUri('bootstrap-fork', require).fsPath, ['--type=extensionHost', '--skipWorkspaceStorageLock'], opts);
|
||||
|
||||
const stdoutDecoder = new StringDecoder('utf-8');
|
||||
this._process.stdout?.on('data', (chunk) => {
|
||||
const strChunk = typeof chunk === 'string' ? chunk : stdoutDecoder.write(chunk);
|
||||
this._onStdout.fire(strChunk);
|
||||
});
|
||||
|
||||
const stderrDecoder = new StringDecoder('utf-8');
|
||||
this._process.stderr?.on('data', (chunk) => {
|
||||
const strChunk = typeof chunk === 'string' ? chunk : stderrDecoder.write(chunk);
|
||||
this._onStderr.fire(strChunk);
|
||||
});
|
||||
|
||||
this._process.on('message', msg => {
|
||||
this._onMessage.fire(msg);
|
||||
});
|
||||
|
||||
this._process.on('error', (err) => {
|
||||
this._onError.fire({ error: transformErrorForSerialization(err) });
|
||||
});
|
||||
|
||||
this._process.on('exit', (code: number, signal: string) => {
|
||||
this._onExit.fire({ code, signal });
|
||||
});
|
||||
|
||||
return { pid: this._process.pid };
|
||||
}
|
||||
|
||||
enableInspectPort(): boolean {
|
||||
if (!this._process) {
|
||||
return false;
|
||||
}
|
||||
|
||||
interface ProcessExt {
|
||||
_debugProcess?(n: number): any;
|
||||
}
|
||||
|
||||
if (typeof (<ProcessExt>process)._debugProcess === 'function') {
|
||||
// use (undocumented) _debugProcess feature of node
|
||||
(<ProcessExt>process)._debugProcess!(this._process.pid);
|
||||
return true;
|
||||
} else if (!platform.isWindows) {
|
||||
// use KILL USR1 on non-windows platforms (fallback)
|
||||
this._process.kill('SIGUSR1');
|
||||
return true;
|
||||
} else {
|
||||
// not supported...
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
kill(): void {
|
||||
this._process!.kill();
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private static _lastId: number = 0;
|
||||
|
||||
private readonly _extHosts: Map<string, ExtensionHostProcess>;
|
||||
|
||||
constructor() {
|
||||
this._extHosts = new Map<string, ExtensionHostProcess>();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._extHosts.forEach((extHost) => {
|
||||
extHost.kill();
|
||||
});
|
||||
}
|
||||
|
||||
private _getExtHost(id: string): ExtensionHostProcess {
|
||||
const extHostProcess = this._extHosts.get(id);
|
||||
if (!extHostProcess) {
|
||||
throw new Error(`Unknown extension host!`);
|
||||
}
|
||||
return extHostProcess;
|
||||
}
|
||||
|
||||
onScopedStdout(id: string): Event<string> {
|
||||
return this._getExtHost(id).onStdout;
|
||||
}
|
||||
|
||||
onScopedStderr(id: string): Event<string> {
|
||||
return this._getExtHost(id).onStderr;
|
||||
}
|
||||
|
||||
onScopedMessage(id: string): Event<any> {
|
||||
return this._getExtHost(id).onMessage;
|
||||
}
|
||||
|
||||
onScopedError(id: string): Event<{ error: SerializedError; }> {
|
||||
return this._getExtHost(id).onError;
|
||||
}
|
||||
|
||||
onScopedExit(id: string): Event<{ code: number; signal: string; }> {
|
||||
return this._getExtHost(id).onExit;
|
||||
}
|
||||
|
||||
async createExtensionHost(): Promise<{ id: string; }> {
|
||||
const id = String(++ExtensionHostStarter._lastId);
|
||||
const extHost = new ExtensionHostProcess(id);
|
||||
this._extHosts.set(id, extHost);
|
||||
extHost.onExit(() => {
|
||||
setTimeout(() => {
|
||||
extHost.dispose();
|
||||
this._extHosts.delete(id);
|
||||
});
|
||||
});
|
||||
return { id };
|
||||
}
|
||||
|
||||
async start(id: string, opts: IExtensionHostProcessOptions): Promise<{ pid: number; }> {
|
||||
return this._getExtHost(id).start(opts);
|
||||
}
|
||||
|
||||
async enableInspectPort(id: string): Promise<boolean> {
|
||||
return this._getExtHost(id).enableInspectPort();
|
||||
}
|
||||
|
||||
async kill(id: string): Promise<void> {
|
||||
this._getExtHost(id).kill();
|
||||
}
|
||||
}
|
||||
+109
-71
@@ -4,10 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { ChildProcess, fork } from 'child_process';
|
||||
import { Server, Socket, createServer } from 'net';
|
||||
import { CrashReporterStartOptions } from 'vs/base/parts/sandbox/electron-sandbox/electronTypes';
|
||||
import { FileAccess } from 'vs/base/common/network';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
@@ -17,10 +14,8 @@ import * as platform from 'vs/base/common/platform';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IRemoteConsoleLog, log } from 'vs/base/common/console';
|
||||
import { logRemoteEntry } from 'vs/workbench/services/extensions/common/remoteConsoleUtil';
|
||||
import { findFreePort } from 'vs/base/node/ports';
|
||||
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net';
|
||||
import { createRandomIPCHandle, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net';
|
||||
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { ILifecycleService, WillShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
@@ -45,9 +40,13 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output';
|
||||
import { isUUID } from 'vs/base/common/uuid';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { Readable, Writable } from 'stream';
|
||||
import { StringDecoder } from 'string_decoder';
|
||||
import { IShellEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/shellEnvironmentService';
|
||||
import { IExtensionHostProcessOptions, IExtensionHostStarter } from 'vs/platform/extensions/common/extensionHostStarter';
|
||||
|
||||
import { Server, Socket, createServer } from 'net';
|
||||
import { findFreePort } from 'vs/base/node/ports';
|
||||
import { createRandomIPCHandle, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net';
|
||||
import { SerializedError } from 'vs/base/common/errors';
|
||||
|
||||
export interface ILocalProcessExtensionHostInitData {
|
||||
readonly autoStart: boolean;
|
||||
@@ -63,6 +62,50 @@ const enum NativeLogMarkers {
|
||||
End = 'END_NATIVE_LOG',
|
||||
}
|
||||
|
||||
class ExtensionHostProcess {
|
||||
|
||||
private readonly _id: string;
|
||||
|
||||
public get onStdout(): Event<string> {
|
||||
return this._extensionHostStarter.onScopedStdout(this._id);
|
||||
}
|
||||
|
||||
public get onStderr(): Event<string> {
|
||||
return this._extensionHostStarter.onScopedStderr(this._id);
|
||||
}
|
||||
|
||||
public get onMessage(): Event<any> {
|
||||
return this._extensionHostStarter.onScopedMessage(this._id);
|
||||
}
|
||||
|
||||
public get onError(): Event<{ error: SerializedError; }> {
|
||||
return this._extensionHostStarter.onScopedError(this._id);
|
||||
}
|
||||
|
||||
public get onExit(): Event<{ code: number; signal: string }> {
|
||||
return this._extensionHostStarter.onScopedExit(this._id);
|
||||
}
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
private readonly _extensionHostStarter: IExtensionHostStarter,
|
||||
) {
|
||||
this._id = id;
|
||||
}
|
||||
|
||||
public start(opts: IExtensionHostProcessOptions): Promise<{ pid: number; }> {
|
||||
return this._extensionHostStarter.start(this._id, opts);
|
||||
}
|
||||
|
||||
public enableInspectPort(): Promise<boolean> {
|
||||
return this._extensionHostStarter.enableInspectPort(this._id);
|
||||
}
|
||||
|
||||
public kill(): Promise<void> {
|
||||
return this._extensionHostStarter.kill(this._id);
|
||||
}
|
||||
}
|
||||
|
||||
export class LocalProcessExtensionHost implements IExtensionHost {
|
||||
|
||||
public readonly kind = ExtensionHostKind.LocalProcess;
|
||||
@@ -88,7 +131,7 @@ export class LocalProcessExtensionHost implements IExtensionHost {
|
||||
// Resources, in order they get acquired/created when .start() is called:
|
||||
private _namedPipeServer: Server | null;
|
||||
private _inspectPort: number | null;
|
||||
private _extensionHostProcess: ChildProcess | null;
|
||||
private _extensionHostProcess: ExtensionHostProcess | null;
|
||||
private _extensionHostConnection: Socket | null;
|
||||
private _messageProtocol: Promise<PersistentProtocol> | null;
|
||||
|
||||
@@ -107,7 +150,8 @@ export class LocalProcessExtensionHost implements IExtensionHost {
|
||||
@IExtensionHostDebugService private readonly _extensionHostDebugService: IExtensionHostDebugService,
|
||||
@IHostService private readonly _hostService: IHostService,
|
||||
@IProductService private readonly _productService: IProductService,
|
||||
@IShellEnvironmentService private readonly _shellEnvironmentService: IShellEnvironmentService
|
||||
@IShellEnvironmentService private readonly _shellEnvironmentService: IShellEnvironmentService,
|
||||
@IExtensionHostStarter private readonly _extensionHostStarter: IExtensionHostStarter,
|
||||
) {
|
||||
const devOpts = parseExtensionDevOptions(this._environmentService);
|
||||
this._isExtensionDevHost = devOpts.isExtensionDevHost;
|
||||
@@ -159,10 +203,14 @@ export class LocalProcessExtensionHost implements IExtensionHost {
|
||||
|
||||
if (!this._messageProtocol) {
|
||||
this._messageProtocol = Promise.all([
|
||||
this._extensionHostStarter.createExtensionHost(),
|
||||
this._tryListenOnPipe(),
|
||||
this._tryFindDebugPort(),
|
||||
this._shellEnvironmentService.getShellEnv()
|
||||
]).then(([pipeName, portNumber, processEnv]) => {
|
||||
this._shellEnvironmentService.getShellEnv(),
|
||||
]).then(([extensionHostCreationResult, pipeName, portNumber, processEnv]) => {
|
||||
|
||||
this._extensionHostProcess = new ExtensionHostProcess(extensionHostCreationResult.id, this._extensionHostStarter);
|
||||
|
||||
const env = objects.mixin(processEnv, {
|
||||
VSCODE_AMD_ENTRYPOINT: 'vs/workbench/services/extensions/node/extensionHostProcess',
|
||||
VSCODE_PIPE_LOGGING: 'true',
|
||||
@@ -238,13 +286,10 @@ export class LocalProcessExtensionHost implements IExtensionHost {
|
||||
opts.env.VSCODE_CRASH_REPORTER_START_OPTIONS = JSON.stringify(crashReporterStartOptions);
|
||||
}
|
||||
|
||||
// Run Extension Host as fork of current process
|
||||
this._extensionHostProcess = fork(FileAccess.asFileUri('bootstrap-fork', require).fsPath, ['--type=extensionHost', '--skipWorkspaceStorageLock'], opts);
|
||||
|
||||
// Catch all output coming from the extension host process
|
||||
type Output = { data: string, format: string[] };
|
||||
const onStdout = this._handleProcessOutputStream(this._extensionHostProcess.stdout!);
|
||||
const onStderr = this._handleProcessOutputStream(this._extensionHostProcess.stderr!);
|
||||
const onStdout = this._handleProcessOutputStream(this._extensionHostProcess.onStdout);
|
||||
const onStderr = this._handleProcessOutputStream(this._extensionHostProcess.onStderr);
|
||||
const onOutput = Event.any(
|
||||
Event.map(onStdout.event, o => ({ data: `%c${o}`, format: [''] })),
|
||||
Event.map(onStderr.event, o => ({ data: `%c${o}`, format: ['color: red'] }))
|
||||
@@ -278,15 +323,16 @@ export class LocalProcessExtensionHost implements IExtensionHost {
|
||||
});
|
||||
|
||||
// Support logging from extension host
|
||||
this._extensionHostProcess.on('message', msg => {
|
||||
this._extensionHostProcess.onMessage(msg => {
|
||||
if (msg && (<IRemoteConsoleLog>msg).type === '__$console') {
|
||||
this._logExtensionHostMessage(<IRemoteConsoleLog>msg);
|
||||
}
|
||||
});
|
||||
|
||||
// Lifecycle
|
||||
this._extensionHostProcess.on('error', (err) => this._onExtHostProcessError(err));
|
||||
this._extensionHostProcess.on('exit', (code: number, signal: string) => this._onExtHostProcessExit(code, signal));
|
||||
|
||||
this._extensionHostProcess.onError((e) => this._onExtHostProcessError(e.error));
|
||||
this._extensionHostProcess.onExit(({ code, signal }) => this._onExtHostProcessExit(code, signal));
|
||||
|
||||
// Notify debugger that we are ready to attach to the process if we run a development extension
|
||||
if (portNumber) {
|
||||
@@ -315,10 +361,12 @@ export class LocalProcessExtensionHost implements IExtensionHost {
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
// Initialize extension host process with hand shakes
|
||||
return this._tryExtHostHandshake().then((protocol) => {
|
||||
clearTimeout(startupTimeoutHandle);
|
||||
return protocol;
|
||||
return this._extensionHostProcess.start(opts).then(() => {
|
||||
// Initialize extension host process with hand shakes
|
||||
return this._tryExtHostHandshake().then((protocol) => {
|
||||
clearTimeout(startupTimeoutHandle);
|
||||
return protocol;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -515,7 +563,15 @@ export class LocalProcessExtensionHost implements IExtensionHost {
|
||||
}
|
||||
}
|
||||
|
||||
private _onExtHostProcessError(err: any): void {
|
||||
private _onExtHostProcessError(_err: SerializedError): void {
|
||||
let err: any = _err;
|
||||
if (_err && _err.$isError) {
|
||||
err = new Error();
|
||||
err.name = _err.name;
|
||||
err.message = _err.message;
|
||||
err.stack = _err.stack;
|
||||
}
|
||||
|
||||
let errorMessage = toErrorMessage(err);
|
||||
if (errorMessage === this._lastExtensionHostError) {
|
||||
return; // prevent error spam
|
||||
@@ -535,40 +591,35 @@ export class LocalProcessExtensionHost implements IExtensionHost {
|
||||
this._onExit.fire([code, signal]);
|
||||
}
|
||||
|
||||
private _handleProcessOutputStream(stream: Readable) {
|
||||
private _handleProcessOutputStream(stream: Event<string>) {
|
||||
let last = '';
|
||||
let isOmitting = false;
|
||||
const event = new Emitter<string>();
|
||||
const decoder = new StringDecoder('utf-8');
|
||||
stream.pipe(new Writable({
|
||||
write(chunk, _encoding, callback) {
|
||||
// not a fancy approach, but this is the same approach used by the split2
|
||||
// module which is well-optimized (https://github.com/mcollina/split2)
|
||||
last += typeof chunk === 'string' ? chunk : decoder.write(chunk);
|
||||
let lines = last.split(/\r?\n/g);
|
||||
last = lines.pop()!;
|
||||
stream((chunk) => {
|
||||
// not a fancy approach, but this is the same approach used by the split2
|
||||
// module which is well-optimized (https://github.com/mcollina/split2)
|
||||
last += chunk;
|
||||
let lines = last.split(/\r?\n/g);
|
||||
last = lines.pop()!;
|
||||
|
||||
// protected against an extension spamming and leaking memory if no new line is written.
|
||||
if (last.length > 10_000) {
|
||||
lines.push(last);
|
||||
last = '';
|
||||
}
|
||||
|
||||
for (const line of lines) {
|
||||
if (isOmitting) {
|
||||
if (line === NativeLogMarkers.End) {
|
||||
isOmitting = false;
|
||||
}
|
||||
} else if (line === NativeLogMarkers.Start) {
|
||||
isOmitting = true;
|
||||
} else if (line.length) {
|
||||
event.fire(line + '\n');
|
||||
}
|
||||
}
|
||||
|
||||
callback();
|
||||
// protected against an extension spamming and leaking memory if no new line is written.
|
||||
if (last.length > 10_000) {
|
||||
lines.push(last);
|
||||
last = '';
|
||||
}
|
||||
}));
|
||||
|
||||
for (const line of lines) {
|
||||
if (isOmitting) {
|
||||
if (line === NativeLogMarkers.End) {
|
||||
isOmitting = false;
|
||||
}
|
||||
} else if (line === NativeLogMarkers.Start) {
|
||||
isOmitting = true;
|
||||
} else if (line.length) {
|
||||
event.fire(line + '\n');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return event;
|
||||
}
|
||||
@@ -582,26 +633,13 @@ export class LocalProcessExtensionHost implements IExtensionHost {
|
||||
return false;
|
||||
}
|
||||
|
||||
interface ProcessExt {
|
||||
_debugProcess?(n: number): any;
|
||||
}
|
||||
|
||||
if (typeof (<ProcessExt>process)._debugProcess === 'function') {
|
||||
// use (undocumented) _debugProcess feature of node
|
||||
(<ProcessExt>process)._debugProcess!(this._extensionHostProcess.pid);
|
||||
await Promise.race([Event.toPromise(this._onDidSetInspectPort.event), timeout(1000)]);
|
||||
return typeof this._inspectPort === 'number';
|
||||
|
||||
} else if (!platform.isWindows) {
|
||||
// use KILL USR1 on non-windows platforms (fallback)
|
||||
this._extensionHostProcess.kill('SIGUSR1');
|
||||
await Promise.race([Event.toPromise(this._onDidSetInspectPort.event), timeout(1000)]);
|
||||
return typeof this._inspectPort === 'number';
|
||||
|
||||
} else {
|
||||
// not supported...
|
||||
const result = await this._extensionHostProcess.enableInspectPort();
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await Promise.race([Event.toPromise(this._onDidSetInspectPort.event), timeout(1000)]);
|
||||
return typeof this._inspectPort === 'number';
|
||||
}
|
||||
|
||||
public getInspectPort(): number | undefined {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { registerSharedProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services';
|
||||
import { IExtensionHostStarter, ipcExtensionHostStarterChannelName } from 'vs/platform/extensions/common/extensionHostStarter';
|
||||
|
||||
registerSharedProcessRemoteService(IExtensionHostStarter, ipcExtensionHostStarterChannelName, { supportsDelayedInstantiation: true });
|
||||
@@ -52,6 +52,7 @@ import 'vs/workbench/services/credentials/electron-sandbox/credentialsService';
|
||||
import 'vs/workbench/services/encryption/electron-sandbox/encryptionService';
|
||||
import 'vs/workbench/services/localizations/electron-sandbox/localizationsService';
|
||||
import 'vs/workbench/services/telemetry/electron-sandbox/telemetryService';
|
||||
import 'vs/workbench/services/extensions/electron-sandbox/extensionHostStarter';
|
||||
import 'vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService';
|
||||
import 'vs/workbench/services/extensionManagement/electron-sandbox/extensionTipsService';
|
||||
import 'vs/workbench/services/userDataSync/electron-sandbox/userDataSyncMachinesService';
|
||||
|
||||
Reference in New Issue
Block a user