mirror of
https://github.com/facebookarchive/prepack.git
synced 2024-10-03 18:07:21 +03:00
Set up adapter communication channel with Prepack
Summary: Release note: none Issue: #907 - Set up AdapterChannel object to read and write to Prepack - Changed Prepack DebugChannel to read and write in the same format - Implement init, stopped, terminated events - Implement set breakpoints end to end The adapter will hold a queue to store requests to Prepack that came from the UI. When Prepack is ready to receive a request, the adapter will send a request from the queue through this AdapterChannel. Closes https://github.com/facebook/prepack/pull/1091 Differential Revision: D6174872 Pulled By: JWZ2018 fbshipit-source-id: 63c0ebf32bd76d7c6214c5a294b38e07c6be51f7
This commit is contained in:
parent
3ab3d3c133
commit
f7025fc2ed
@ -9,6 +9,8 @@
|
||||
|
||||
/* @flow */
|
||||
|
||||
// Generated using flowgen from vscode-debugadapter npm package and modified
|
||||
|
||||
import DebugProtocol from 'vscode-debugprotocol'
|
||||
import * as ee from 'events';
|
||||
declare module 'vscode-debugadapter' {
|
||||
@ -28,9 +30,31 @@ declare module 'vscode-debugadapter' {
|
||||
event: string;
|
||||
constructor(event: string, body?: any): this
|
||||
}
|
||||
declare export class StoppedEvent mixins Event, DebugProtocol.StoppedEvent {
|
||||
body: {
|
||||
reason: string,
|
||||
threadId: number
|
||||
};
|
||||
constructor(reason: string, threadId: number, exception_text?: string): this
|
||||
}
|
||||
declare export class InitializedEvent mixins Event, DebugProtocol.InitializedEvent {
|
||||
constructor(): this
|
||||
}
|
||||
declare export class TerminatedEvent mixins Event, DebugProtocol.TerminatedEvent {
|
||||
/** Protocol: A debug adapter may set 'restart' to true (or to an arbitrary object) to request that the front end restarts the session.
|
||||
The value is not interpreted by the client and passed unmodified as an attribute '__restart' to the launchRequest.
|
||||
*/
|
||||
constructor(restart?: any): this
|
||||
}
|
||||
declare export class OutputEvent mixins Event, DebugProtocol.OutputEvent {
|
||||
body: {
|
||||
category: string,
|
||||
output: string,
|
||||
// Protocol: this can be any optional data to report
|
||||
data?: any
|
||||
};
|
||||
constructor(output: string, category?: string, data?: any): this
|
||||
}
|
||||
declare export class ProtocolServer mixins ee.EventEmitter {
|
||||
constructor(): this;
|
||||
start(inStream: ee.EventEmitter.ReadableStream, outStream: ee.EventEmitter.WritableStream): void;
|
||||
|
@ -1,66 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
import path from "path";
|
||||
import type { DebuggerOptions } from "./options";
|
||||
|
||||
export class DebugChannel {
|
||||
constructor(fs: any, dbgOptions: DebuggerOptions) {
|
||||
this.inFilePath = path.join(__dirname, "../", dbgOptions.inFilePath);
|
||||
this.outFilePath = path.join(__dirname, "../", dbgOptions.outFilePath);
|
||||
this.fs = fs;
|
||||
this.requestReceived = false;
|
||||
}
|
||||
inFilePath: string;
|
||||
outFilePath: string;
|
||||
fs: any;
|
||||
requestReceived: boolean;
|
||||
|
||||
/*
|
||||
/* Only called in the beginning to check if a debugger is attached
|
||||
*/
|
||||
debuggerIsAttached(): boolean {
|
||||
let line = this.readIn();
|
||||
if (line === "Debugger Attached\n") {
|
||||
this.writeOut("Ready\n");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Reads in a request from the debug adapter
|
||||
/* The caller is responsible for sending a response with the appropriate
|
||||
/* contents at the right time.
|
||||
/* For now, it returns the request as a string. It will be made to return a
|
||||
/* Request object based on the protocol
|
||||
*/
|
||||
readIn(): string {
|
||||
let contents = "";
|
||||
while (contents.length === 0) {
|
||||
contents = this.fs.readFileSync(this.inFilePath, "utf8");
|
||||
}
|
||||
//clear the file
|
||||
this.fs.writeFileSync(this.inFilePath, "");
|
||||
this.requestReceived = true;
|
||||
return contents;
|
||||
}
|
||||
|
||||
/* Write out a response to the debug adapter
|
||||
/* For now, it writes the response as a string. It will be made to return
|
||||
/* a Response object based on the protocol
|
||||
*/
|
||||
writeOut(contents: string): void {
|
||||
//Prepack only writes back to the debug adapter in response to a request
|
||||
if (this.requestReceived) {
|
||||
this.fs.writeFileSync(this.outFilePath, contents);
|
||||
this.requestReceived = false;
|
||||
}
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ import * as partialEvaluators from "./partial-evaluators/index.js";
|
||||
import { NewGlobalEnvironment } from "./methods/index.js";
|
||||
import { ObjectValue } from "./values/index.js";
|
||||
import { DebugServer } from "./debugger/Debugger.js";
|
||||
import { DebugChannel } from "./DebugChannel.js";
|
||||
import type { DebugChannel } from "./debugger/channel/DebugChannel.js";
|
||||
import simplifyAndRefineAbstractValue from "./utils/simplifier.js";
|
||||
|
||||
export default function(opts: RealmOptions = {}, debugChannel: void | DebugChannel = undefined): Realm {
|
||||
|
@ -11,9 +11,12 @@
|
||||
|
||||
import type { BabelNode } from "babel-types";
|
||||
import { BreakpointCollection } from "./BreakpointCollection.js";
|
||||
import { Breakpoint } from "./Breakpoint.js";
|
||||
import type { BreakpointCommandArguments } from "./types.js";
|
||||
import invariant from "../invariant.js";
|
||||
import { DebugChannel } from "../DebugChannel.js";
|
||||
import type { DebugChannel } from "./channel/DebugChannel.js";
|
||||
import { DebugMessage } from "./channel/DebugMessage.js";
|
||||
import { DebuggerError } from "./DebuggerError.js";
|
||||
|
||||
export class DebugServer {
|
||||
constructor(channel: DebugChannel) {
|
||||
@ -21,9 +24,7 @@ export class DebugServer {
|
||||
this.previousExecutedLine = 0;
|
||||
this.previousExecutedCol = 0;
|
||||
this.channel = channel;
|
||||
this.waitForRun(function(line) {
|
||||
return line === "Run";
|
||||
});
|
||||
this.waitForRun();
|
||||
}
|
||||
// the collection of breakpoints
|
||||
breakpoints: BreakpointCollection;
|
||||
@ -37,30 +38,18 @@ export class DebugServer {
|
||||
/* runCondition: a function that determines whether the adapter has told
|
||||
/* Prepack to continue running
|
||||
*/
|
||||
waitForRun(runCondition: string => boolean) {
|
||||
let blocking = true;
|
||||
let line = "";
|
||||
while (blocking) {
|
||||
line = this.channel.readIn().toString();
|
||||
if (runCondition(line)) {
|
||||
//The adapter gave the command to continue running
|
||||
//The caller (or someone else later) needs to send a response
|
||||
//to the adapter
|
||||
//We cannot pass in the response too because it may not be ready
|
||||
//immediately after Prepack unblocks
|
||||
blocking = false;
|
||||
} else {
|
||||
//The adapter gave another command so Prepack still blocks
|
||||
//but can read in other commands and respond to them
|
||||
this.executeCommand(line);
|
||||
}
|
||||
waitForRun() {
|
||||
let keepRunning = false;
|
||||
let message = "";
|
||||
while (!keepRunning) {
|
||||
message = this.channel.readIn().toString();
|
||||
keepRunning = this.processDebuggerCommand(message);
|
||||
}
|
||||
}
|
||||
|
||||
// Checking if the debugger needs to take any action on reaching this ast node
|
||||
checkForActions(ast: BabelNode) {
|
||||
this.checkForBreakpoint(ast);
|
||||
|
||||
// last step: set the current location as the previously executed line
|
||||
if (ast.loc && ast.loc.source !== null) {
|
||||
this.previousExecutedFile = ast.loc.source;
|
||||
@ -69,7 +58,8 @@ export class DebugServer {
|
||||
}
|
||||
}
|
||||
|
||||
proceedBreakpoint(filePath: string, lineNum: number, colNum: number): boolean {
|
||||
// Try to find a breakpoint at the given location and check if we should stop on it
|
||||
findStoppableBreakpoint(filePath: string, lineNum: number, colNum: number): null | Breakpoint {
|
||||
let breakpoint = this.breakpoints.getBreakpoint(filePath, lineNum, colNum);
|
||||
if (breakpoint && breakpoint.enabled) {
|
||||
// checking if this is the same file and line we stopped at last time
|
||||
@ -78,16 +68,24 @@ export class DebugServer {
|
||||
// breakpoint consecutively (e.g. the statement is in a loop), some other
|
||||
// ast node (e.g. block, loop) must have been checked in between so
|
||||
// previousExecutedFile and previousExecutedLine will have changed
|
||||
if (
|
||||
filePath === this.previousExecutedFile &&
|
||||
lineNum === this.previousExecutedLine &&
|
||||
colNum === this.previousExecutedCol
|
||||
) {
|
||||
return false;
|
||||
if (breakpoint.column !== 0) {
|
||||
// this is a column breakpoint
|
||||
if (
|
||||
filePath === this.previousExecutedFile &&
|
||||
lineNum === this.previousExecutedLine &&
|
||||
colNum === this.previousExecutedCol
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// this is a line breakpoint
|
||||
if (filePath === this.previousExecutedFile && lineNum === this.previousExecutedLine) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return breakpoint;
|
||||
}
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
checkForBreakpoint(ast: BabelNode) {
|
||||
@ -97,56 +95,89 @@ export class DebugServer {
|
||||
if (filePath === null) return;
|
||||
let lineNum = location.start.line;
|
||||
let colNum = location.start.column;
|
||||
|
||||
// Check whether there is a breakpoint we need to stop on here
|
||||
if (!this.proceedBreakpoint(filePath, lineNum, colNum)) return;
|
||||
|
||||
let breakpoint = this.findStoppableBreakpoint(filePath, lineNum, colNum);
|
||||
if (breakpoint === null) return;
|
||||
// Tell the adapter that Prepack has stopped on this breakpoint
|
||||
this.channel.writeOut(`breakpoint stopped ${lineNum}:${colNum}`);
|
||||
this.channel.writeOut(
|
||||
`${DebugMessage.BREAKPOINT_STOPPED_RESPONSE} ${breakpoint.filePath} ${breakpoint.line}:${breakpoint.column}`
|
||||
);
|
||||
|
||||
// Wait for the adapter to tell us to run again
|
||||
this.waitForRun(function(line) {
|
||||
return line === "proceed";
|
||||
});
|
||||
this.waitForRun();
|
||||
}
|
||||
}
|
||||
|
||||
executeCommand(command: string) {
|
||||
// Process a command from a debugger. Returns whether Prepack should unblock
|
||||
// if it is blocked
|
||||
processDebuggerCommand(command: string) {
|
||||
if (command.length === 0) {
|
||||
return;
|
||||
}
|
||||
let parts = command.split(" ");
|
||||
if (parts[0] === "breakpoint") {
|
||||
this.executeBreakpointCommand(this._parseBreakpointArguments(parts));
|
||||
let prefix = parts[0];
|
||||
switch (prefix) {
|
||||
case DebugMessage.BREAKPOINT_ADD_COMMAND:
|
||||
let addArgs = this._parseBreakpointArguments(parts);
|
||||
this.breakpoints.addBreakpoint(addArgs.filePath, addArgs.lineNum, addArgs.columnNum);
|
||||
this._sendBreakpointAcknowledge(
|
||||
DebugMessage.BREAKPOINT_ADD_ACKNOWLEDGE,
|
||||
addArgs.filePath,
|
||||
addArgs.lineNum,
|
||||
addArgs.columnNum
|
||||
);
|
||||
break;
|
||||
case DebugMessage.BREAKPOINT_REMOVE_COMMAND:
|
||||
let removeArgs = this._parseBreakpointArguments(parts);
|
||||
this.breakpoints.removeBreakpoint(removeArgs.filePath, removeArgs.lineNum, removeArgs.columnNum);
|
||||
this._sendBreakpointAcknowledge(
|
||||
DebugMessage.BREAKPOINT_REMOVE_ACKNOWLEDGE,
|
||||
removeArgs.filePath,
|
||||
removeArgs.lineNum,
|
||||
removeArgs.columnNum
|
||||
);
|
||||
break;
|
||||
case DebugMessage.BREAKPOINT_ENABLE_COMMAND:
|
||||
let enableArgs = this._parseBreakpointArguments(parts);
|
||||
this.breakpoints.enableBreakpoint(enableArgs.filePath, enableArgs.lineNum, enableArgs.columnNum);
|
||||
this._sendBreakpointAcknowledge(
|
||||
DebugMessage.BREAKPOINT_ENABLE_ACKNOWLEDGE,
|
||||
enableArgs.filePath,
|
||||
enableArgs.lineNum,
|
||||
enableArgs.columnNum
|
||||
);
|
||||
break;
|
||||
case DebugMessage.BREAKPOINT_DISABLE_COMMAND:
|
||||
let disableArgs = this._parseBreakpointArguments(parts);
|
||||
this.breakpoints.disableBreakpoint(disableArgs.filePath, disableArgs.lineNum, disableArgs.columnNum);
|
||||
this._sendBreakpointAcknowledge(
|
||||
DebugMessage.BREAKPOINT_DISABLE_ACKNOWLEDGE,
|
||||
disableArgs.filePath,
|
||||
disableArgs.lineNum,
|
||||
disableArgs.columnNum
|
||||
);
|
||||
break;
|
||||
case DebugMessage.PREPACK_RUN_COMMAND:
|
||||
return true;
|
||||
default:
|
||||
throw new DebuggerError("Invalid command", "Invalid command from adapter: " + prefix);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
executeBreakpointCommand(args: BreakpointCommandArguments) {
|
||||
if (args.kind === "add") {
|
||||
this.breakpoints.addBreakpoint(args.filePath, args.lineNum, args.columnNum);
|
||||
this.channel.writeOut(`added breakpoint ${args.filePath} ${args.lineNum} ${args.columnNum}`);
|
||||
} else if (args.kind === "remove") {
|
||||
this.breakpoints.removeBreakpoint(args.filePath, args.lineNum, args.columnNum);
|
||||
this.channel.writeOut(`removed breakpoint ${args.filePath} ${args.lineNum} ${args.columnNum}`);
|
||||
} else if (args.kind === "enable") {
|
||||
this.breakpoints.enableBreakpoint(args.filePath, args.lineNum, args.columnNum);
|
||||
this.channel.writeOut(`enabled breakpoint ${args.filePath} ${args.lineNum} ${args.columnNum}`);
|
||||
} else if (args.kind === "disable") {
|
||||
this.breakpoints.disableBreakpoint(args.filePath, args.lineNum, args.columnNum);
|
||||
this.channel.writeOut(`disabled breakpoint ${args.filePath} ${args.lineNum} ${args.columnNum}`);
|
||||
}
|
||||
_sendBreakpointAcknowledge(responsePrefix: string, filePath: string, line: number, column: number) {
|
||||
this.channel.writeOut(`${responsePrefix} ${filePath} ${line} ${column}`);
|
||||
}
|
||||
|
||||
_parseBreakpointArguments(parts: Array<string>): BreakpointCommandArguments {
|
||||
invariant(parts[0] === "breakpoint");
|
||||
let kind = parts[1];
|
||||
let filePath = parts[2];
|
||||
let kind = parts[0];
|
||||
let filePath = parts[1];
|
||||
|
||||
let lineNum = parseInt(parts[3], 10);
|
||||
let lineNum = parseInt(parts[2], 10);
|
||||
invariant(!isNaN(lineNum));
|
||||
let columnNum = 0;
|
||||
if (parts.length === 5) {
|
||||
columnNum = parseInt(parts[4], 10);
|
||||
if (parts.length === 4) {
|
||||
columnNum = parseInt(parts[3], 10);
|
||||
invariant(!isNaN(columnNum));
|
||||
}
|
||||
|
||||
@ -162,6 +193,6 @@ export class DebugServer {
|
||||
|
||||
shutdown() {
|
||||
//let the adapter know Prepack is done running
|
||||
this.channel.writeOut("Finished");
|
||||
this.channel.writeOut(DebugMessage.PREPACK_FINISH_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
22
src/debugger/DebuggerError.js
Normal file
22
src/debugger/DebuggerError.js
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
|
||||
// More error types will be added as needed
|
||||
export type DebuggerErrorType = "Invalid command" | "Invalid response";
|
||||
|
||||
export class DebuggerError {
|
||||
constructor(errorType: DebuggerErrorType, message: string) {
|
||||
this.errorType = errorType;
|
||||
this.message = message;
|
||||
}
|
||||
errorType: DebuggerErrorType;
|
||||
message: string;
|
||||
}
|
@ -35,6 +35,11 @@ export class PerFileBreakpointMap {
|
||||
let key = this._getKey(line, column);
|
||||
if (key in this.breakpoints) {
|
||||
return this.breakpoints[key];
|
||||
} else {
|
||||
key = this._getKey(line, 0);
|
||||
if (key in this.breakpoints) {
|
||||
return this.breakpoints[key];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let key = this._getKey(line, 0);
|
||||
|
@ -9,10 +9,21 @@
|
||||
|
||||
/* @flow */
|
||||
|
||||
import { DebugSession, LoggingDebugSession, InitializedEvent } from "vscode-debugadapter";
|
||||
import {
|
||||
DebugSession,
|
||||
LoggingDebugSession,
|
||||
InitializedEvent,
|
||||
OutputEvent,
|
||||
TerminatedEvent,
|
||||
StoppedEvent,
|
||||
} from "vscode-debugadapter";
|
||||
import * as DebugProtocol from "vscode-debugprotocol";
|
||||
import child_process from "child_process";
|
||||
import Queue from "queue-fifo";
|
||||
import { AdapterChannel } from "./../channel/AdapterChannel.js";
|
||||
import type { DebuggerOptions } from "./../../options.js";
|
||||
import { getDebuggerOptions } from "./../../prepack-options.js";
|
||||
import { DebugMessage } from "./../channel/DebugMessage.js";
|
||||
|
||||
/* An implementation of an debugger adapter adhering to the VSCode Debug protocol
|
||||
* The adapter is responsible for communication between the UI and Prepack
|
||||
@ -26,29 +37,54 @@ class PrepackDebugSession extends LoggingDebugSession {
|
||||
super("prepack");
|
||||
this.setDebuggerLinesStartAt1(true);
|
||||
this.setDebuggerColumnsStartAt1(true);
|
||||
|
||||
this._prepackWaiting = false;
|
||||
this._readCLIParameters();
|
||||
this._startPrepack();
|
||||
}
|
||||
|
||||
_prepackCommand: string;
|
||||
_inFilePath: string;
|
||||
_outFilePath: string;
|
||||
_prepackProcess: child_process.ChildProcess;
|
||||
_messageQueue: Queue;
|
||||
_adapterChannel: AdapterChannel;
|
||||
_debuggerOptions: DebuggerOptions;
|
||||
_prepackWaiting: boolean;
|
||||
|
||||
_readCLIParameters() {
|
||||
let args = Array.from(process.argv);
|
||||
args.splice(0, 2);
|
||||
let inFilePath;
|
||||
let outFilePath;
|
||||
while (args.length > 0) {
|
||||
let arg = args.shift();
|
||||
if (arg.startsWith("--")) {
|
||||
arg = arg.slice(2);
|
||||
if (arg === "prepack") {
|
||||
this._prepackCommand = args.shift();
|
||||
} else if (arg === "inFilePath") {
|
||||
inFilePath = args.shift();
|
||||
} else if (arg === "outFilePath") {
|
||||
outFilePath = args.shift();
|
||||
}
|
||||
} else {
|
||||
console.error("Unknown parameter: " + arg);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
if (!inFilePath || inFilePath.length === 0) {
|
||||
console.error("No debugger input file given");
|
||||
process.exit(1);
|
||||
}
|
||||
if (!outFilePath || outFilePath.length === 0) {
|
||||
console.error("No debugger output file given");
|
||||
process.exit(1);
|
||||
}
|
||||
this._debuggerOptions = getDebuggerOptions({
|
||||
debugInFilePath: inFilePath,
|
||||
debugOutFilePath: outFilePath,
|
||||
});
|
||||
}
|
||||
|
||||
// Start Prepack in a child process
|
||||
@ -57,13 +93,26 @@ class PrepackDebugSession extends LoggingDebugSession {
|
||||
console.error("No command given to start Prepack in adapter");
|
||||
process.exit(1);
|
||||
}
|
||||
//set up message queue
|
||||
// set up message queue
|
||||
this._messageQueue = new Queue();
|
||||
// set up the communication channel
|
||||
this._adapterChannel = new AdapterChannel(this._debuggerOptions);
|
||||
this._adapterChannel.writeOut(DebugMessage.DEBUGGER_ATTACHED);
|
||||
this._adapterChannel.listenOnFile(this._handleFileReadError.bind(this), this._processPrepackMessage.bind(this));
|
||||
|
||||
this._prepackProcess = child_process.spawn("node", this._prepackCommand.split(" "));
|
||||
let prepackArgs = this._prepackCommand.split(" ");
|
||||
// Note: here the input file for the adapter is the output file for Prepack, and vice versa.
|
||||
prepackArgs = prepackArgs.concat([
|
||||
"--debugInFilePath",
|
||||
this._debuggerOptions.outFilePath,
|
||||
"--debugOutFilePath",
|
||||
this._debuggerOptions.inFilePath,
|
||||
]);
|
||||
this._prepackProcess = child_process.spawn("node", prepackArgs);
|
||||
|
||||
process.on("exit", () => {
|
||||
this._prepackProcess.kill();
|
||||
this._adapterChannel.clean();
|
||||
process.exit();
|
||||
});
|
||||
|
||||
@ -71,6 +120,58 @@ class PrepackDebugSession extends LoggingDebugSession {
|
||||
this._prepackProcess.kill();
|
||||
process.exit();
|
||||
});
|
||||
|
||||
this._prepackProcess.stdout.on("data", (data: Buffer) => {
|
||||
let outputEvent = new OutputEvent(data.toString(), "stdout");
|
||||
this.sendEvent(outputEvent);
|
||||
});
|
||||
|
||||
this._prepackProcess.on("exit", () => {
|
||||
this.sendEvent(new TerminatedEvent());
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
|
||||
_processPrepackMessage(message: string) {
|
||||
let parts = message.split(" ");
|
||||
let prefix = parts[0];
|
||||
if (prefix === DebugMessage.PREPACK_READY_RESPONSE) {
|
||||
this._prepackWaiting = true;
|
||||
// the second argument is the threadID required by the protocol, since
|
||||
// Prepack only has one thread, this argument will be ignored
|
||||
this.sendEvent(new StoppedEvent("entry", 1));
|
||||
this._trySendNextRequest();
|
||||
} else if (prefix === DebugMessage.BREAKPOINT_ADD_ACKNOWLEDGE) {
|
||||
// Prepack acknowledged adding a breakpoint
|
||||
this._prepackWaiting = true;
|
||||
this._trySendNextRequest();
|
||||
} else if (prefix === DebugMessage.BREAKPOINT_STOPPED_RESPONSE) {
|
||||
// Prepack stopped on a breakpoint
|
||||
this._prepackWaiting = true;
|
||||
// the second argument is the threadID required by the protocol, since
|
||||
// Prepack only has one thread, this argument will be ignored
|
||||
this.sendEvent(new StoppedEvent("breakpoint " + parts.slice(2).join(" "), 1));
|
||||
this._trySendNextRequest();
|
||||
}
|
||||
}
|
||||
|
||||
// Error handler for errors in files from the adapter channel
|
||||
_handleFileReadError(err: ?ErrnoError) {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Check to see if the next request to Prepack can be sent and send it if so
|
||||
_trySendNextRequest(): boolean {
|
||||
// check to see if Prepack is ready to accept another request
|
||||
if (!this._prepackWaiting) return false;
|
||||
// check that there is a message to send
|
||||
if (this._messageQueue.isEmpty()) return false;
|
||||
let request = this._messageQueue.dequeue();
|
||||
this._adapterChannel.listenOnFile(this._handleFileReadError.bind(this), this._processPrepackMessage.bind(this));
|
||||
this._adapterChannel.writeOut(request);
|
||||
this._prepackWaiting = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,6 +188,35 @@ class PrepackDebugSession extends LoggingDebugSession {
|
||||
// Respond back to the UI with the configurations. Will add more configurations gradually as needed.
|
||||
this.sendResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Request Prepack to continue running when it is stopped
|
||||
*/
|
||||
continueRequest(response: DebugProtocol.ContinueResponse, args: DebugProtocol.ContinueArguments): void {
|
||||
// queue a Run request to Prepack and try to send the next request in the queue
|
||||
this._messageQueue.enqueue(DebugMessage.PREPACK_RUN_COMMAND);
|
||||
this._trySendNextRequest();
|
||||
this.sendResponse(response);
|
||||
}
|
||||
|
||||
setBreakPointsRequest(
|
||||
response: DebugProtocol.SetBreakpointsResponse,
|
||||
args: DebugProtocol.SetBreakpointsArguments
|
||||
): void {
|
||||
if (!args.source.path || !args.breakpoints) return;
|
||||
let filePath = args.source.path;
|
||||
|
||||
for (const breakpoint of args.breakpoints) {
|
||||
let line = breakpoint.line;
|
||||
let column = 0;
|
||||
if (breakpoint.column) {
|
||||
column = breakpoint.column;
|
||||
}
|
||||
this._messageQueue.enqueue(`${DebugMessage.BREAKPOINT_ADD_COMMAND} ${filePath} ${line} ${column}`);
|
||||
}
|
||||
this._trySendNextRequest();
|
||||
this.sendResponse(response);
|
||||
}
|
||||
}
|
||||
|
||||
DebugSession.run(PrepackDebugSession);
|
||||
|
33
src/debugger/channel/AdapterChannel.js
Normal file
33
src/debugger/channel/AdapterChannel.js
Normal file
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
import type { DebuggerOptions } from "./../../options.js";
|
||||
import { FileIOWrapper } from "./FileIOWrapper.js";
|
||||
|
||||
//Channel used by the debug adapter to communicate with Prepack
|
||||
export class AdapterChannel {
|
||||
constructor(dbgOptions: DebuggerOptions) {
|
||||
this._ioWrapper = new FileIOWrapper(true, dbgOptions.inFilePath, dbgOptions.outFilePath);
|
||||
}
|
||||
_ioWrapper: FileIOWrapper;
|
||||
|
||||
writeOut(contents: string) {
|
||||
this._ioWrapper.writeOutSync(contents);
|
||||
}
|
||||
|
||||
listenOnFile(errorHandler: (err: ?ErrnoError) => void, messageProcessor: (message: string) => void) {
|
||||
this._ioWrapper.readIn(errorHandler, messageProcessor);
|
||||
}
|
||||
|
||||
clean() {
|
||||
this._ioWrapper.clearInFile();
|
||||
this._ioWrapper.clearOutFile();
|
||||
}
|
||||
}
|
56
src/debugger/channel/DebugChannel.js
Normal file
56
src/debugger/channel/DebugChannel.js
Normal file
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
import invariant from "./../../invariant.js";
|
||||
import { FileIOWrapper } from "./FileIOWrapper.js";
|
||||
import { DebugMessage } from "./DebugMessage.js";
|
||||
|
||||
//Channel used by the DebugServer in Prepack to communicate with the debug adapter
|
||||
export class DebugChannel {
|
||||
constructor(ioWrapper: FileIOWrapper) {
|
||||
this._requestReceived = false;
|
||||
this._ioWrapper = ioWrapper;
|
||||
}
|
||||
|
||||
_requestReceived: boolean;
|
||||
_ioWrapper: FileIOWrapper;
|
||||
|
||||
/*
|
||||
/* Only called in the beginning to check if a debugger is attached
|
||||
*/
|
||||
debuggerIsAttached(): boolean {
|
||||
let message = this._ioWrapper.readInSyncOnce();
|
||||
if (message === DebugMessage.DEBUGGER_ATTACHED) {
|
||||
this._requestReceived = true;
|
||||
this._ioWrapper.clearInFile();
|
||||
this.writeOut(DebugMessage.PREPACK_READY_RESPONSE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Reads in a request from the debug adapter
|
||||
/* The caller is responsible for sending a response with the appropriate
|
||||
/* contents at the right time.
|
||||
*/
|
||||
readIn(): string {
|
||||
let message = this._ioWrapper.readInSync();
|
||||
this._requestReceived = true;
|
||||
return message;
|
||||
}
|
||||
|
||||
// Write out a response to the debug adapter
|
||||
writeOut(contents: string): void {
|
||||
//Prepack only writes back to the debug adapter in response to a request
|
||||
invariant(this._requestReceived);
|
||||
this._ioWrapper.writeOutSync(contents);
|
||||
this._requestReceived = false;
|
||||
}
|
||||
}
|
43
src/debugger/channel/DebugMessage.js
Normal file
43
src/debugger/channel/DebugMessage.js
Normal file
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
|
||||
//A collection of messages used between Prepack and the debug adapter
|
||||
export class DebugMessage {
|
||||
/* Messages from adapter to Prepack */
|
||||
// Tell Prepack a debugger is present
|
||||
static DEBUGGER_ATTACHED: string = "DebuggerAttached";
|
||||
// Command Prepack to keep running
|
||||
static PREPACK_RUN_COMMAND: string = "PrepackRun";
|
||||
// Command to set a breakpoint
|
||||
static BREAKPOINT_ADD_COMMAND: string = "Breakpoint-add-command";
|
||||
// Command to remove a breakpoint
|
||||
static BREAKPOINT_REMOVE_COMMAND: string = "Breakpoint-remove-command";
|
||||
// Command to enable a breakpoint
|
||||
static BREAKPOINT_ENABLE_COMMAND: string = "Breakpoint-enable-command";
|
||||
// Command to disable a breakpoint
|
||||
static BREAKPOINT_DISABLE_COMMAND: string = "Breakpoint-disable-command";
|
||||
|
||||
/* Messages from Prepack to adapter */
|
||||
// Respond to the adapter that Prepack is ready
|
||||
static PREPACK_READY_RESPONSE: string = "PrepackReady";
|
||||
// Respond to the adapter that Prepack is finished
|
||||
static PREPACK_FINISH_RESPONSE: string = "PrepackFinish";
|
||||
// Respond to the adapter that Prepack has stopped on a breakpoint
|
||||
static BREAKPOINT_STOPPED_RESPONSE: string = "Breakpoint-stopped";
|
||||
// Acknowledgement for setting a breakpoint
|
||||
static BREAKPOINT_ADD_ACKNOWLEDGE: string = "Breakpoint-add-acknowledge";
|
||||
// Acknowledgement for removing a breakpoint
|
||||
static BREAKPOINT_REMOVE_ACKNOWLEDGE: string = "Breakpoint-remove-acknowledge";
|
||||
// Acknowledgement for enabling a breakpoint
|
||||
static BREAKPOINT_ENABLE_ACKNOWLEDGE: string = "Breakpoint-enable-acknowledge";
|
||||
// Acknoledgement for disabling a breakpoint
|
||||
static BREAKPOINT_DISABLE_ACKNOWLEDGE: string = "Breakpoint-disable-acknowledge";
|
||||
}
|
84
src/debugger/channel/FileIOWrapper.js
Normal file
84
src/debugger/channel/FileIOWrapper.js
Normal file
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { MessagePackager } from "./MessagePackager.js";
|
||||
import invariant from "../../invariant.js";
|
||||
|
||||
export class FileIOWrapper {
|
||||
constructor(isAdapter: boolean, inFilePath: string, outFilePath: string) {
|
||||
// the paths are expected to be relative to Prepack top level directory
|
||||
this._inFilePath = path.join(__dirname, "../../../", inFilePath);
|
||||
this._outFilePath = path.join(__dirname, "../../../", outFilePath);
|
||||
this._packager = new MessagePackager(isAdapter);
|
||||
this._isAdapter = isAdapter;
|
||||
}
|
||||
_inFilePath: string;
|
||||
_outFilePath: string;
|
||||
_packager: MessagePackager;
|
||||
_isAdapter: boolean;
|
||||
|
||||
// Read in a message from the input asynchronously
|
||||
readIn(errorHandler: (err: ?ErrnoError) => void, messageProcessor: (message: string) => void) {
|
||||
fs.readFile(this._inFilePath, { encoding: "utf8" }, (err: ?ErrnoError, contents: string) => {
|
||||
if (err) {
|
||||
errorHandler(err);
|
||||
return;
|
||||
}
|
||||
let message = this._packager.unpackage(contents);
|
||||
if (message === null) {
|
||||
this.readIn(errorHandler, messageProcessor);
|
||||
return;
|
||||
}
|
||||
//clear the file
|
||||
fs.writeFileSync(this._inFilePath, "");
|
||||
//process the message
|
||||
messageProcessor(message);
|
||||
});
|
||||
}
|
||||
|
||||
// Read in a message from the input synchronously
|
||||
readInSync(): string {
|
||||
let message: null | string = null;
|
||||
while (true) {
|
||||
let contents = fs.readFileSync(this._inFilePath, "utf8");
|
||||
message = this._packager.unpackage(contents);
|
||||
if (message === null) continue;
|
||||
break;
|
||||
}
|
||||
// loop should not break when message is still null
|
||||
invariant(message !== null);
|
||||
//clear the file
|
||||
fs.writeFileSync(this._inFilePath, "");
|
||||
return message;
|
||||
}
|
||||
|
||||
// Read in a message from the input synchronously only once
|
||||
readInSyncOnce(): null | string {
|
||||
let contents = fs.readFileSync(this._inFilePath, "utf8");
|
||||
let message = this._packager.unpackage(contents);
|
||||
return message;
|
||||
}
|
||||
|
||||
// Write out a message to the output synchronously
|
||||
writeOutSync(contents: string) {
|
||||
fs.writeFileSync(this._outFilePath, this._packager.package(contents));
|
||||
}
|
||||
|
||||
clearInFile() {
|
||||
fs.writeFileSync(this._inFilePath, "");
|
||||
}
|
||||
|
||||
clearOutFile() {
|
||||
fs.writeFileSync(this._outFilePath, "");
|
||||
}
|
||||
}
|
54
src/debugger/channel/MessagePackager.js
Normal file
54
src/debugger/channel/MessagePackager.js
Normal file
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
|
||||
import invariant from "../../invariant.js";
|
||||
|
||||
const LENGTH_SEPARATOR = "--";
|
||||
|
||||
// Package a message sent or unpackage a message received
|
||||
export class MessagePackager {
|
||||
constructor(isAdapter: boolean) {
|
||||
this._isAdapter = isAdapter;
|
||||
}
|
||||
_isAdapter: boolean;
|
||||
|
||||
// package a message to be sent
|
||||
package(contents: string): string {
|
||||
// format: <length>--<contents>
|
||||
return contents.length + LENGTH_SEPARATOR + contents;
|
||||
}
|
||||
|
||||
// unpackage a message received, verify it, and return it
|
||||
// returns null if no message or the message is only partially read
|
||||
// errors if the message violates the format
|
||||
unpackage(contents: string): null | string {
|
||||
// format: <length>--<contents>
|
||||
let separatorIndex = contents.indexOf(LENGTH_SEPARATOR);
|
||||
// if the separator is not written in yet --> partial read
|
||||
if (separatorIndex === -1) {
|
||||
return null;
|
||||
}
|
||||
let messageLength = parseInt(contents.slice(0, separatorIndex), 10);
|
||||
// if the part before the separator is not a valid length, it is a
|
||||
// violation of protocol
|
||||
invariant(!isNaN(messageLength));
|
||||
let startIndex = separatorIndex + LENGTH_SEPARATOR.length;
|
||||
let endIndex = startIndex + messageLength;
|
||||
// there should only be one message in the contents at a time
|
||||
invariant(contents.length <= startIndex + messageLength);
|
||||
// if we didn't read the whole message yet --> partial read
|
||||
if (contents.length < endIndex) {
|
||||
return null;
|
||||
}
|
||||
let message = contents.slice(startIndex, endIndex);
|
||||
return message;
|
||||
}
|
||||
}
|
@ -22,18 +22,25 @@ const TWO_CRLF = "\r\n\r\n";
|
||||
* sends the commands to the adapter and process any responses
|
||||
*/
|
||||
export class UISession {
|
||||
constructor(proc: Process, adapterPath: string, prepackCommand: string) {
|
||||
constructor(proc: Process, adapterPath: string, prepackCommand: string, inFilePath: string, outFilePath: string) {
|
||||
this._proc = proc;
|
||||
this._adapterPath = adapterPath;
|
||||
this._prepackCommand = prepackCommand;
|
||||
this._inFilePath = inFilePath;
|
||||
this._outFilePath = outFilePath;
|
||||
this._sequenceNum = 1;
|
||||
this._invalidCount = 0;
|
||||
this._dataHandler = new DataHandler();
|
||||
this._prepackWaiting = false;
|
||||
}
|
||||
// the parent (i.e. ui) process
|
||||
_proc: Process;
|
||||
//path to the debug adapter
|
||||
_adapterPath: string;
|
||||
// path to debugger input file
|
||||
_inFilePath: string;
|
||||
// path to debugger output file
|
||||
_outFilePath: string;
|
||||
// the child (i.e. adapter) process
|
||||
_adapterProcess: child_process.ChildProcess;
|
||||
|
||||
@ -47,9 +54,18 @@ export class UISession {
|
||||
_prepackCommand: string;
|
||||
// handler for any received messages
|
||||
_dataHandler: DataHandler;
|
||||
_prepackWaiting: boolean;
|
||||
|
||||
_startAdapter() {
|
||||
let adapterArgs = [this._adapterPath, "--prepack", this._prepackCommand];
|
||||
let adapterArgs = [
|
||||
this._adapterPath,
|
||||
"--prepack",
|
||||
this._prepackCommand,
|
||||
"--inFilePath",
|
||||
this._inFilePath,
|
||||
"--outFilePath",
|
||||
this._outFilePath,
|
||||
];
|
||||
this._adapterProcess = child_process.spawn("node", adapterArgs);
|
||||
this._proc.on("exit", () => {
|
||||
this.shutdown();
|
||||
@ -57,16 +73,15 @@ export class UISession {
|
||||
this._proc.on("SIGINT", () => {
|
||||
this.shutdown();
|
||||
});
|
||||
this._adapterProcess.on("exit", () => {
|
||||
this.shutdown();
|
||||
});
|
||||
this._adapterProcess.stdout.on("data", (data: Buffer) => {
|
||||
//handle the received data
|
||||
this._dataHandler.handleData(data, this._processMessage.bind(this));
|
||||
//ask the user for the next command
|
||||
this._reader.question("(dbg) ", (input: string) => {
|
||||
this._dispatch(input);
|
||||
});
|
||||
if (this._prepackWaiting) {
|
||||
this._reader.question("(dbg) ", (input: string) => {
|
||||
this._dispatch(input);
|
||||
});
|
||||
}
|
||||
});
|
||||
this._adapterProcess.stderr.on("data", (data: Buffer) => {
|
||||
console.error(data.toString());
|
||||
@ -90,8 +105,21 @@ export class UISession {
|
||||
}
|
||||
|
||||
_processEvent(event: DebugProtocol.Event) {
|
||||
// to be implemented
|
||||
console.log(event);
|
||||
if (event.event === "output") {
|
||||
this._uiOutput("Prepack output:\n" + event.body.output);
|
||||
} else if (event.event === "terminated") {
|
||||
this._uiOutput("Prepack exited! Shutting down...");
|
||||
this.shutdown();
|
||||
} else if (event.event === "stopped") {
|
||||
this._prepackWaiting = true;
|
||||
if (event.body) {
|
||||
if (event.body.reason === "entry") {
|
||||
this._uiOutput("Prepack is ready");
|
||||
} else if (event.body.reason.startsWith("breakpoint")) {
|
||||
this._uiOutput("Prepack stopped on: " + event.body.reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_processResponse(response: DebugProtocol.Response) {
|
||||
@ -132,6 +160,30 @@ export class UISession {
|
||||
let configDoneArgs: DebugProtocol.ConfigurationDoneArguments = {};
|
||||
this._sendConfigDoneRequest(configDoneArgs);
|
||||
break;
|
||||
case "run":
|
||||
// format: run
|
||||
if (parts.length !== 1) return false;
|
||||
let continueArgs: DebugProtocol.ContinueArguments = {
|
||||
// Prepack will only have 1 thread, this argument will be ignored
|
||||
threadId: 1,
|
||||
};
|
||||
this._sendContinueRequest(continueArgs);
|
||||
break;
|
||||
case "breakpoint":
|
||||
// format: breakpoint add <filePath> <line> ?<column>
|
||||
if (parts.length !== 4 && parts.length !== 5) return false;
|
||||
if (parts[1] === "add") {
|
||||
let filePath = parts[2];
|
||||
let line = parseInt(parts[3], 10);
|
||||
if (isNaN(line)) return false;
|
||||
let column = 0;
|
||||
if (parts.length === 5) {
|
||||
column = parseInt(parts[4], 10);
|
||||
if (isNaN(column)) return false;
|
||||
}
|
||||
this._sendBreakpointRequest(filePath, line, column);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// invalid command
|
||||
return false;
|
||||
@ -186,6 +238,41 @@ export class UISession {
|
||||
this._packageAndSend(json);
|
||||
}
|
||||
|
||||
// tell the adapter to continue running Prepack
|
||||
_sendContinueRequest(args: DebugProtocol.ContinueArguments) {
|
||||
let message = {
|
||||
type: "request",
|
||||
seq: this._sequenceNum,
|
||||
command: "continue",
|
||||
arguments: args,
|
||||
};
|
||||
let json = JSON.stringify(message);
|
||||
this._packageAndSend(json);
|
||||
this._prepackWaiting = false;
|
||||
}
|
||||
|
||||
_sendBreakpointRequest(filePath: string, line: number, column: number = 0) {
|
||||
let source: DebugProtocol.Source = {
|
||||
path: filePath,
|
||||
};
|
||||
let breakpoint: DebugProtocol.SourceBreakpoint = {
|
||||
line: line,
|
||||
column: column,
|
||||
};
|
||||
let args: DebugProtocol.SetBreakpointsArguments = {
|
||||
source: source,
|
||||
breakpoints: [breakpoint],
|
||||
};
|
||||
let message = {
|
||||
type: "request",
|
||||
seq: this._sequenceNum,
|
||||
command: "setBreakpoints",
|
||||
arguments: args,
|
||||
};
|
||||
let json = JSON.stringify(message);
|
||||
this._packageAndSend(json);
|
||||
}
|
||||
|
||||
// write out a message to the adapter on stdout
|
||||
_packageAndSend(message: string) {
|
||||
// format: Content-Length: <length> separator <message>
|
||||
@ -196,15 +283,15 @@ export class UISession {
|
||||
this._sequenceNum++;
|
||||
}
|
||||
|
||||
_uiOutput(message: string) {
|
||||
console.log(message);
|
||||
}
|
||||
|
||||
serve() {
|
||||
this._uiOutput("Debugger is starting up Prepack...");
|
||||
// Set up the adapter connection
|
||||
this._startAdapter();
|
||||
|
||||
this._reader = readline.createInterface({ input: this._proc.stdin, output: this._proc.stdout });
|
||||
// Start taking in commands and execute them
|
||||
this._reader.question("(dbg) ", (input: string) => {
|
||||
this._dispatch(input);
|
||||
});
|
||||
}
|
||||
|
||||
shutdown() {
|
||||
|
@ -11,13 +11,33 @@
|
||||
|
||||
import { UISession } from "./UISession.js";
|
||||
|
||||
type DebuggerCLIArguments = {
|
||||
adapterPath: string,
|
||||
prepackCommand: string,
|
||||
inFilePath: string,
|
||||
outFilePath: string,
|
||||
};
|
||||
|
||||
/* The entry point to start up the debugger CLI
|
||||
* Reads in command line arguments and starts up a UISession
|
||||
*/
|
||||
|
||||
function run(process, console) {
|
||||
let args = readCLIArguments(process, console);
|
||||
let session = new UISession(process, args.adapterPath, args.prepackCommand, args.inFilePath, args.outFilePath);
|
||||
try {
|
||||
session.serve();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
session.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
function readCLIArguments(process, console): DebuggerCLIArguments {
|
||||
let adapterPath = "";
|
||||
let prepackCommand = "";
|
||||
let inFilePath = "";
|
||||
let outFilePath = "";
|
||||
|
||||
let args = Array.from(process.argv);
|
||||
args.splice(0, 2);
|
||||
@ -33,11 +53,23 @@ function run(process, console) {
|
||||
adapterPath = args.shift();
|
||||
} else if (arg === "prepack") {
|
||||
prepackCommand = args.shift();
|
||||
} else if (arg === "inFilePath") {
|
||||
inFilePath = args.shift();
|
||||
} else if (arg === "outFilePath") {
|
||||
outFilePath = args.shift();
|
||||
} else {
|
||||
console.error("Unknown argument: " + arg);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
if (inFilePath === 0) {
|
||||
console.error("No input file path provided!");
|
||||
process.exit(1);
|
||||
}
|
||||
if (outFilePath === 0) {
|
||||
console.error("No output file path provided!");
|
||||
process.exit(1);
|
||||
}
|
||||
if (adapterPath.length === 0) {
|
||||
console.error("No path to the debug adapter provided!");
|
||||
process.exit(1);
|
||||
@ -46,13 +78,12 @@ function run(process, console) {
|
||||
console.error("No command given to start Prepack");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let session = new UISession(process, adapterPath, prepackCommand);
|
||||
try {
|
||||
session.serve();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
session.shutdown();
|
||||
}
|
||||
let result: DebuggerCLIArguments = {
|
||||
adapterPath: adapterPath,
|
||||
prepackCommand: prepackCommand,
|
||||
inFilePath: inFilePath,
|
||||
outFilePath: outFilePath,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
run(process, console);
|
||||
|
@ -140,6 +140,12 @@ function run(
|
||||
let line = args.shift();
|
||||
additionalFunctions = line.split(",");
|
||||
break;
|
||||
case "debugInFilePath":
|
||||
debugInFilePath = args.shift();
|
||||
break;
|
||||
case "debugOutFilePath":
|
||||
debugOutFilePath = args.shift();
|
||||
break;
|
||||
case "help":
|
||||
console.log(
|
||||
"Usage: prepack.js [ -- | input.js ] [ --out output.js ] [ --compatibility jsc ] [ --mathRandomSeed seedvalue ] [ --srcmapIn inputMap ] [ --srcmapOut outputMap ] [ --maxStackDepth depthValue ] [ --timeout seconds ] [ --additionalFunctions fnc1,fnc2,... ]" +
|
||||
|
@ -18,7 +18,8 @@ import { getDebuggerOptions } from "./prepack-options";
|
||||
import { prepackNodeCLI, prepackNodeCLISync } from "./prepack-node-environment.js";
|
||||
import { prepackSources } from "./prepack-standalone.js";
|
||||
import { type SourceMap } from "./types.js";
|
||||
import { DebugChannel } from "./DebugChannel.js";
|
||||
import { DebugChannel } from "./debugger/channel/DebugChannel.js";
|
||||
import { FileIOWrapper } from "./debugger/channel/FileIOWrapper.js";
|
||||
|
||||
import fs from "fs";
|
||||
|
||||
@ -113,7 +114,8 @@ export function prepackFileSync(filenames: Array<string>, options: PrepackOption
|
||||
//flag to hide the debugger for now
|
||||
if (options.enableDebugger && options.debugInFilePath && options.debugOutFilePath) {
|
||||
let debugOptions = getDebuggerOptions(options);
|
||||
debugChannel = new DebugChannel(fs, debugOptions);
|
||||
let ioWrapper = new FileIOWrapper(false, debugOptions.inFilePath, debugOptions.outFilePath);
|
||||
debugChannel = new DebugChannel(ioWrapper);
|
||||
}
|
||||
return prepackSources(sourceFiles, options, debugChannel);
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ import type { PrepackOptions } from "./prepack-options";
|
||||
import { defaultOptions } from "./options";
|
||||
import type { BabelNodeFile, BabelNodeProgram } from "babel-types";
|
||||
import invariant from "./invariant.js";
|
||||
import { DebugChannel } from "./DebugChannel.js";
|
||||
import type { DebugChannel } from "./debugger/channel/DebugChannel.js";
|
||||
|
||||
// IMPORTANT: This function is now deprecated and will go away in a future release.
|
||||
// Please use FatalError instead.
|
||||
|
Loading…
Reference in New Issue
Block a user