code-server/lib/vscode/src/bootstrap-fork.js

230 lines
6.3 KiB
JavaScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const performance = require('./vs/base/common/performance');
performance.mark('code/fork/start');
const bootstrap = require('./bootstrap');
const bootstrapNode = require('./bootstrap-node');
// Remove global paths from the node module lookup
bootstrapNode.removeGlobalNodeModuleLookupPaths();
// Enable ASAR in our forked processes
bootstrap.enableASARSupport(undefined);
if (process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']) {
bootstrapNode.injectNodeModuleLookupPath(process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']);
}
// Configure: pipe logging to parent process
if (!!process.send && process.env['VSCODE_PIPE_LOGGING'] === 'true') {
pipeLoggingToParent();
}
// Handle Exceptions
if (!process.env['VSCODE_HANDLES_UNCAUGHT_ERRORS']) {
handleExceptions();
}
// Terminate when parent terminates
if (process.env['VSCODE_PARENT_PID']) {
terminateWhenParentTerminates();
}
// Configure Crash Reporter
configureCrashReporter();
// Load AMD entry point
require('./bootstrap-amd').load(process.env['VSCODE_AMD_ENTRYPOINT']);
//#region Helpers
function pipeLoggingToParent() {
const MAX_LENGTH = 100000;
/**
* Prevent circular stringify and convert arguments to real array
*
* @param {IArguments} args
*/
function safeToArray(args) {
const seen = [];
const argsArray = [];
// Massage some arguments with special treatment
if (args.length) {
for (let i = 0; i < args.length; i++) {
// Any argument of type 'undefined' needs to be specially treated because
// JSON.stringify will simply ignore those. We replace them with the string
// 'undefined' which is not 100% right, but good enough to be logged to console
if (typeof args[i] === 'undefined') {
args[i] = 'undefined';
}
// Any argument that is an Error will be changed to be just the error stack/message
// itself because currently cannot serialize the error over entirely.
else if (args[i] instanceof Error) {
const errorObj = args[i];
if (errorObj.stack) {
args[i] = errorObj.stack;
} else {
args[i] = errorObj.toString();
}
}
argsArray.push(args[i]);
}
}
// Add the stack trace as payload if we are told so. We remove the message and the 2 top frames
// to start the stacktrace where the console message was being written
if (process.env['VSCODE_LOG_STACK'] === 'true') {
const stack = new Error().stack;
if (stack) {
argsArray.push({ __$stack: stack.split('\n').slice(3).join('\n') });
}
}
try {
const res = JSON.stringify(argsArray, function (key, value) {
// Objects get special treatment to prevent circles
if (isObject(value) || Array.isArray(value)) {
if (seen.indexOf(value) !== -1) {
return '[Circular]';
}
seen.push(value);
}
return value;
});
if (res.length > MAX_LENGTH) {
return 'Output omitted for a large object that exceeds the limits';
}
return res;
} catch (error) {
return `Output omitted for an object that cannot be inspected ('${error.toString()}')`;
}
}
/**
* @param {{ type: string; severity: string; arguments: string; }} arg
*/
function safeSend(arg) {
try {
if (process.send) {
process.send(arg);
}
} catch (error) {
// Can happen if the parent channel is closed meanwhile
}
}
/**
* @param {unknown} obj
*/
function isObject(obj) {
return typeof obj === 'object'
&& obj !== null
&& !Array.isArray(obj)
&& !(obj instanceof RegExp)
&& !(obj instanceof Date);
}
/**
*
* @param {'log' | 'warn' | 'error'} severity
* @param {string} args
*/
function safeSendConsoleMessage(severity, args) {
safeSend({ type: '__$console', severity, arguments: args });
}
/**
* @param {'log' | 'info' | 'warn' | 'error'} method
* @param {'log' | 'warn' | 'error'} severity
*/
function wrapConsoleMethod(method, severity) {
if (process.env['VSCODE_LOG_NATIVE'] === 'true') {
const original = console[method];
console[method] = function () {
safeSendConsoleMessage(severity, safeToArray(arguments));
const stream = method === 'error' || method === 'warn' ? process.stderr : process.stdout;
stream.write('\nSTART_NATIVE_LOG\n');
original.apply(console, arguments);
stream.write('\nEND_NATIVE_LOG\n');
};
} else {
console[method] = function () { safeSendConsoleMessage(severity, safeToArray(arguments)); };
}
}
// Pass console logging to the outside so that we have it in the main side if told so
if (process.env['VSCODE_VERBOSE_LOGGING'] === 'true') {
wrapConsoleMethod('info', 'log');
wrapConsoleMethod('log', 'log');
wrapConsoleMethod('warn', 'warn');
wrapConsoleMethod('error', 'error');
} else if (process.env['VSCODE_LOG_NATIVE'] !== 'true') {
console.log = function () { /* ignore */ };
console.warn = function () { /* ignore */ };
console.info = function () { /* ignore */ };
wrapConsoleMethod('error', 'error');
}
}
function handleExceptions() {
// Handle uncaught exceptions
process.on('uncaughtException', function (err) {
console.error('Uncaught Exception: ', err);
});
// Handle unhandled promise rejections
process.on('unhandledRejection', function (reason) {
console.error('Unhandled Promise Rejection: ', reason);
});
}
function terminateWhenParentTerminates() {
const parentPid = Number(process.env['VSCODE_PARENT_PID']);
if (typeof parentPid === 'number' && !isNaN(parentPid)) {
setInterval(function () {
try {
process.kill(parentPid, 0); // throws an exception if the main process doesn't exist anymore.
} catch (e) {
process.exit();
}
}, 5000);
}
}
function configureCrashReporter() {
const crashReporterOptionsRaw = process.env['VSCODE_CRASH_REPORTER_START_OPTIONS'];
if (typeof crashReporterOptionsRaw === 'string') {
try {
const crashReporterOptions = JSON.parse(crashReporterOptionsRaw);
if (crashReporterOptions && process['crashReporter'] /* Electron only */) {
process['crashReporter'].start(crashReporterOptions);
}
} catch (error) {
console.error(error);
}
}
}
//#endregion