Archived
1
0

Make everything use active evals (#30)

* Add trace log level

* Use active eval to implement spdlog

* Split server/client active eval interfaces

Since all properties are *not* valid on both sides

* +200% fire resistance

* Implement exec using active evaluations

* Fully implement child process streams

* Watch impl, move child_process back to explicitly adding events

Automatically forwarding all events might be the right move, but wanna
think/discuss it a bit more because it didn't come out very cleanly.

* Would you like some args with that callback?

* Implement the rest of child_process using active evals

* Rampant memory leaks

Emit "kill" to active evaluations when client disconnects in order to
kill processes. Most likely won't be the final solution.

* Resolve some minor issues with output panel

* Implement node-pty with active evals

* Provide clearTimeout to vm sandbox

* Implement socket with active evals

* Extract some callback logic

Also remove some eval interfaces, need to re-think those.

* Implement net.Server and remainder of net.Socket using active evals

* Implement dispose for active evaluations

* Use trace for express requests

* Handle sending buffers through evaluation events

* Make event logging a bit more clear

* Fix some errors due to us not actually instantiating until connect/listen

* is this a commit message?

* We can just create the evaluator in the ctor

Not sure what I was thinking.

* memory leak for you, memory leak for everyone

* it's a ternary now

* Don't dispose automatically on close or error

The code may or may not be disposable at that point.

* Handle parsing buffers on the client side as well

* Remove unused protobuf

* Remove TypedValue

* Remove unused forkProvider and test

* Improve dispose pattern for active evals

* Socket calls close after error; no need to bind both

* Improve comment

* Comment is no longer wishy washy due to explicit boolean

* Simplify check for sendHandle and options

* Replace _require with __non_webpack_require__

Webpack will then replace this with `require` which we then provide to
the vm sandbox.

* Provide path.parse

* Prevent original-fs from loading

* Start with a pid of -1

vscode immediately checks the PID to see if the debug process launch
correctly, but of course we don't get the pid synchronously.

* Pass arguments to bootstrap-fork

* Fully implement streams

Was causing errors because internally the stream would set this.writing
to true and it would never become false, so subsequent messages would
never send.

* Fix serializing errors and streams emitting errors multiple times

* Was emitting close to data

* Fix missing path for spawned processes

* Move evaluation onDispose call

Now it's accurate and runs when the active evaluation has actually
disposed.

* Fix promisifying fs.exists

* Fix some active eval callback issues

* Patch existsSync in debug adapter
This commit is contained in:
Asher
2019-02-19 10:17:03 -06:00
committed by GitHub
parent 73762017c8
commit 4a80bcb42c
39 changed files with 1694 additions and 8731 deletions

View File

@ -27,6 +27,7 @@ export class Entry extends Command {
// Dev flags
"bootstrap-fork": flags.string({ hidden: true }),
env: flags.string({ hidden: true }),
args: flags.string({ hidden: true }),
};
public static args = [{
name: "workdir",
@ -57,10 +58,6 @@ export class Entry extends Command {
const { args, flags } = this.parse(Entry);
if (flags.env) {
Object.assign(process.env, JSON.parse(flags.env));
}
const builtInExtensionsDir = path.join(buildDir || path.join(__dirname, ".."), "build/extensions");
if (flags["bootstrap-fork"]) {
const modulePath = flags["bootstrap-fork"];
@ -69,6 +66,13 @@ export class Entry extends Command {
process.exit(1);
}
Object.assign(process.env, flags.env ? JSON.parse(flags.env) : {});
((flags.args ? JSON.parse(flags.args) : []) as string[]).forEach((arg, i) => {
// [0] contains the binary running the script (`node` for example) and
// [1] contains the script name, so the arguments come after that.
process.argv[i + 2] = arg;
});
return requireModule(modulePath, builtInExtensionsDir);
}
@ -110,7 +114,7 @@ export class Entry extends Command {
const app = createApp((app) => {
app.use((req, res, next) => {
res.on("finish", () => {
logger.debug(`\u001B[1m${req.method} ${res.statusCode} \u001B[0m${req.url}`, field("host", req.hostname), field("ip", req.ip));
logger.trace(`\u001B[1m${req.method} ${res.statusCode} \u001B[0m${req.url}`, field("host", req.hostname), field("ip", req.ip));
});
next();

View File

@ -1,8 +1,6 @@
import { logger } from "@coder/logger";
import { ReadWriteConnection } from "@coder/protocol";
import { Server, ServerOptions } from "@coder/protocol/src/node/server";
import { NewSessionMessage } from "@coder/protocol/src/proto";
import { ChildProcess } from "child_process";
import * as express from "express";
//@ts-ignore
import * as expressStaticGzip from "express-static-gzip";
@ -12,7 +10,6 @@ import * as mime from "mime-types";
import * as path from "path";
import * as util from "util";
import * as ws from "ws";
import { forkModule } from "./vscode/bootstrapFork";
import { isCli, buildDir } from "./constants";
export const createApp = (registerMiddleware?: (app: express.Application) => void, options?: ServerOptions): {
@ -51,23 +48,7 @@ export const createApp = (registerMiddleware?: (app: express.Application) => voi
onClose: (cb): void => ws.addEventListener("close", () => cb()),
};
const server = new Server(connection, options ? {
...options,
forkProvider: (message: NewSessionMessage): ChildProcess => {
let proc: ChildProcess;
if (message.getIsBootstrapFork()) {
const env: NodeJS.ProcessEnv = {};
message.getEnvMap().forEach((value, key) => {
env[key] = value;
});
proc = forkModule(message.getCommand(), env);
} else {
throw new Error("No support for non bootstrap-forking yet");
}
return proc;
},
} : undefined);
const server = new Server(connection, options);
});
const baseDir = buildDir || path.join(__dirname, "..");

View File

@ -16,10 +16,11 @@ export const requireModule = (modulePath: string, builtInExtensionsDir: string):
* Used for loading extensions. Using __non_webpack_require__ didn't work
* as it was not resolving to the FS.
*/
(global as any).nativeNodeRequire = (id: string) => {
(global as any).nativeNodeRequire = (id: string): any => {// tslint:disable-line no-any
const customMod = new mod.Module(id);
customMod.filename = id;
customMod.paths = (<any>mod)._nodeModulePaths(path.dirname(id));
// tslint:disable-next-line no-any
customMod.paths = (mod as any)._nodeModulePaths(path.dirname(id));
if (id.startsWith(builtInExtensionsDir)) {
customMod.loaded = true;
@ -28,6 +29,7 @@ export const requireModule = (modulePath: string, builtInExtensionsDir: string):
filename: id + ".js",
});
req(customMod.exports, customMod.require.bind(customMod), customMod, __filename, path.dirname(id));
return customMod.exports;
}
@ -54,20 +56,24 @@ export const requireModule = (modulePath: string, builtInExtensionsDir: string):
* cp.stderr.on("data", (data) => console.log(data.toString("utf8")));
* @param modulePath Path of the VS Code module to load.
*/
export const forkModule = (modulePath: string, env?: NodeJS.ProcessEnv): cp.ChildProcess => {
let proc: cp.ChildProcess | undefined;
const args = ["--bootstrap-fork", modulePath];
if (env) {
args.push("--env", JSON.stringify(env));
export const forkModule = (modulePath: string, args: string[], options: cp.ForkOptions): cp.ChildProcess => {
let proc: cp.ChildProcess;
const forkArgs = ["--bootstrap-fork", modulePath];
if (args) {
forkArgs.push("--args", JSON.stringify(args));
}
const options: cp.SpawnOptions = {
if (options.env) {
// This prevents vscode from trying to load original-fs from electron.
delete options.env.ELECTRON_RUN_AS_NODE;
forkArgs.push("--env", JSON.stringify(options.env));
}
const forkOptions: cp.ForkOptions = {
stdio: [null, null, null, "ipc"],
};
if (isCli) {
proc = cp.execFile(process.execPath, args, options);
proc = cp.execFile(process.execPath, forkArgs, forkOptions);
} else {
proc = cp.spawn(process.execPath, ["--require", "ts-node/register", "--require", "tsconfig-paths/register", process.argv[1], ...args], options);
proc = cp.spawn(process.execPath, ["--require", "ts-node/register", "--require", "tsconfig-paths/register", process.argv[1], ...forkArgs], forkOptions);
}
return proc;

View File

@ -65,9 +65,11 @@ export class SharedProcess {
state: SharedProcessState.Starting,
});
let resolved: boolean = false;
this.activeProcess = forkModule("vs/code/electron-browser/sharedProcess/sharedProcessMain", {
VSCODE_ALLOW_IO: "true",
VSCODE_LOGS: process.env.VSCODE_LOGS,
this.activeProcess = forkModule("vs/code/electron-browser/sharedProcess/sharedProcessMain", [], {
env: {
VSCODE_ALLOW_IO: "true",
VSCODE_LOGS: process.env.VSCODE_LOGS,
},
});
this.activeProcess.on("exit", (err) => {
if (this._state !== SharedProcessState.Stopped) {