prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
384 lines (295 loc) • 13.4 kB
JavaScript
"use strict";
var _vscodeDebugadapter = require("vscode-debugadapter");
var DebugProtocol = _interopRequireWildcard(require("vscode-debugprotocol"));
var _AdapterChannel = require("./channel/AdapterChannel.js");
var _invariant = _interopRequireDefault(require("./../common/invariant.js"));
var _DebugMessage = require("./../common/channel/DebugMessage.js");
var _DebuggerConstants = require("./../common/DebuggerConstants.js");
var _DebuggerError = require("./../common/DebuggerError.js");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
/**
* 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.
*/
/* strict-local */
/* An implementation of an debugger adapter adhering to the VSCode Debug protocol
* The adapter is responsible for communication between the UI and Prepack
*/
class PrepackDebugSession extends _vscodeDebugadapter.DebugSession {
/**
* Creates a new debug adapter that is used for one debug session.
* We configure the default implementation of a debug adapter here.
*/
constructor() {
super();
this.setDebuggerLinesStartAt1(true);
this.setDebuggerColumnsStartAt1(true);
}
_generateDebugFilePath(direction) {
let time = Date.now();
let filePath = "/tmp/";
if (direction === "in") {
filePath += `prepack-debug-engine2adapter-${time}.txt`;
} else {
filePath += `prepack-debug-adapter2engine-${time}.txt`;
}
return filePath;
}
_registerMessageCallbacks() {
this._ensureAdapterChannelCreated("registerMessageCallbacks");
(0, _invariant.default)(this._adapterChannel !== undefined, "Adapter Channel used before it was created, in debugger."); // Create local copy to ensure external functions don't modify the adapterChannel, satisfy flow.
let localCopyAdapterChannel = this._adapterChannel;
localCopyAdapterChannel.registerChannelEvent(_DebugMessage.DebugMessage.STOPPED_RESPONSE, response => {
let result = response.result;
(0, _invariant.default)(result.kind === "stopped");
let message = `${result.reason}: ${result.filePath} ${result.line}:${result.column}`; // Append message if there exists one (for Prepack errors)
if (result.message !== undefined) {
message += `. ${result.message}`;
}
this.sendEvent(new _vscodeDebugadapter.StoppedEvent(message, _DebuggerConstants.DebuggerConstants.PREPACK_THREAD_ID));
});
localCopyAdapterChannel.registerChannelEvent(_DebugMessage.DebugMessage.STEPINTO_RESPONSE, response => {
let result = response.result;
(0, _invariant.default)(result.kind === "stepInto");
this.sendEvent(new _vscodeDebugadapter.StoppedEvent("Stepped into " + `${result.filePath} ${result.line}:${result.column}`, _DebuggerConstants.DebuggerConstants.PREPACK_THREAD_ID));
});
}
/**
* The 'initialize' request is the first request called by the UI
* to interrogate the features the debug adapter provides.
*/
// Override
initializeRequest(response, args) {
this._clientID = args.clientID;
response.body = response.body || {};
response.body.supportsConfigurationDoneRequest = true; // Respond back to the UI with the configurations. Will add more configurations gradually as needed.
// Adapter can respond immediately here because no message is sent to Prepack
this.sendResponse(response);
} // Override
configurationDoneRequest(response, args) {
// initial handshake with UI is complete
if (this._clientID !== _DebuggerConstants.DebuggerConstants.CLI_CLIENTID) {
this._ensureAdapterChannelCreated("configurationDoneRequest");
(0, _invariant.default)(this._adapterChannel !== undefined, "Adapter Channel used before it was created, in debugger."); // for all ui except the CLI, autosend the first run request
this._adapterChannel.run(_DebuggerConstants.DebuggerConstants.DEFAULT_REQUEST_ID, runResponse => {});
}
this.sendResponse(response);
} // Override
launchRequest(response, args) {
let inFilePath = this._generateDebugFilePath("in");
let outFilePath = this._generateDebugFilePath("out"); // Set up the communication channel to the debugger.
let adapterChannel = new _AdapterChannel.AdapterChannel(inFilePath, outFilePath);
this._adapterChannel = adapterChannel;
this._registerMessageCallbacks();
let launchArgs = {
kind: "launch",
sourceFiles: args.sourceFiles,
prepackRuntime: args.prepackRuntime,
prepackArguments: args.prepackArguments,
debugInFilePath: inFilePath,
debugOutFilePath: outFilePath,
outputCallback: data => {
let outputEvent = new _vscodeDebugadapter.OutputEvent(data.toString(), "stdout");
this.sendEvent(outputEvent);
},
exitCallback: () => {
this.sendEvent(new _vscodeDebugadapter.TerminatedEvent());
process.exit();
}
};
adapterChannel.launch(response.request_seq, launchArgs, dbgResponse => {
this.sendResponse(response);
}); // Important: InitializedEvent indicates to the protocol that further requests (e.g. breakpoints, execution control)
// are ready to be received. Prepack debugger is not ready to receive these requests until the Adapter Channel
// has been created and Prepack has been launched. Thus, the InitializedEvent is sent after Prepack launch and
// the creation of the Adapter Channel.
this.sendEvent(new _vscodeDebugadapter.InitializedEvent());
}
/**
* Request Prepack to continue running when it is stopped
*/
// Override
continueRequest(response, args) {
// send a Run request to Prepack and try to send the next request
this._ensureAdapterChannelCreated("continueRequest");
(0, _invariant.default)(this._adapterChannel !== undefined, "Adapter Channel used before it was created, in debugger.");
this._adapterChannel.run(response.request_seq, dbgResponse => {
this.sendResponse(response);
});
} // Override
setBreakPointsRequest(response, args) {
if (args.source.path === undefined || args.breakpoints === undefined) return;
let filePath = args.source.path;
let breakpointInfos = [];
for (const breakpoint of args.breakpoints) {
let line = breakpoint.line;
let column = 0;
if (breakpoint.column !== undefined) {
column = breakpoint.column;
}
let breakpointInfo = {
kind: "breakpoint",
requestID: response.request_seq,
filePath: filePath,
line: line,
column: column
};
breakpointInfos.push(breakpointInfo);
}
this._ensureAdapterChannelCreated("setBreakPointsRequest");
(0, _invariant.default)(this._adapterChannel !== undefined, "Adapter Channel used before it was created, in debugger.");
this._adapterChannel.setBreakpoints(response.request_seq, breakpointInfos, dbgResponse => {
let result = dbgResponse.result;
(0, _invariant.default)(result.kind === "breakpoint-add");
let breakpoints = [];
for (const breakpointInfo of result.breakpoints) {
let source = {
path: breakpointInfo.filePath
};
let breakpoint = {
verified: true,
source: source,
line: breakpointInfo.line,
column: breakpointInfo.column
};
breakpoints.push(breakpoint);
}
response.body = {
breakpoints: breakpoints
};
this.sendResponse(response);
});
} // Override
stackTraceRequest(response, args) {
this._ensureAdapterChannelCreated("stackTraceRequest");
(0, _invariant.default)(this._adapterChannel !== undefined);
this._adapterChannel.getStackFrames(response.request_seq, dbgResponse => {
let result = dbgResponse.result;
(0, _invariant.default)(result.kind === "stackframe");
let frameInfos = result.stackframes;
let frames = [];
for (const frameInfo of frameInfos) {
let source = {
path: frameInfo.fileName
};
let frame = {
id: frameInfo.id,
name: frameInfo.functionName,
source: source,
line: frameInfo.line,
column: frameInfo.column
};
frames.push(frame);
}
response.body = {
stackFrames: frames
};
this.sendResponse(response);
});
} // Override
threadsRequest(response) {
// There will only be 1 thread, so respond immediately
let thread = {
id: _DebuggerConstants.DebuggerConstants.PREPACK_THREAD_ID,
name: "main"
};
response.body = {
threads: [thread]
};
this.sendResponse(response);
} // Override
scopesRequest(response, args) {
this._ensureAdapterChannelCreated("scopesRequest");
(0, _invariant.default)(this._adapterChannel !== undefined, "Adapter Channel used before it was created, in debugger.");
this._adapterChannel.getScopes(response.request_seq, args.frameId, dbgResponse => {
let result = dbgResponse.result;
(0, _invariant.default)(result.kind === "scopes");
let scopeInfos = result.scopes;
let scopes = [];
for (const scopeInfo of scopeInfos) {
let scope = {
name: scopeInfo.name,
variablesReference: scopeInfo.variablesReference,
expensive: scopeInfo.expensive
};
scopes.push(scope);
}
response.body = {
scopes: scopes
};
this.sendResponse(response);
});
} // Override
variablesRequest(response, args) {
this._ensureAdapterChannelCreated("variablesRequest");
(0, _invariant.default)(this._adapterChannel !== undefined, "Adapter Channel used before it was created, in debugger.");
this._adapterChannel.getVariables(response.request_seq, args.variablesReference, dbgResponse => {
let result = dbgResponse.result;
(0, _invariant.default)(result.kind === "variables");
let variableInfos = result.variables;
let variables = [];
for (const varInfo of variableInfos) {
let variable = {
name: varInfo.name,
value: varInfo.value,
variablesReference: varInfo.variablesReference
};
variables.push(variable);
}
response.body = {
variables: variables
};
this.sendResponse(response);
});
} // Override
stepInRequest(response, args) {
this._ensureAdapterChannelCreated("stepInRequest");
(0, _invariant.default)(this._adapterChannel !== undefined, "Adapter Channel used before it was created, in debugger.");
this._adapterChannel.stepInto(response.request_seq, dbgResponse => {
this.sendResponse(response);
});
} // Override
nextRequest(response, args) {
this._ensureAdapterChannelCreated("nextRequest");
(0, _invariant.default)(this._adapterChannel !== undefined, "Adapter Channel used before it was created, in debugger.");
this._adapterChannel.stepOver(response.request_seq, dbgResponse => {
this.sendResponse(response);
});
} // Override
stepOutRequest(response, args) {
this._ensureAdapterChannelCreated("stepOutRequest");
(0, _invariant.default)(this._adapterChannel !== undefined, "Adapter Channel used before it was created, in debugger.");
this._adapterChannel.stepOut(response.request_seq, dbgResponse => {
this.sendResponse(response);
});
} // Override
evaluateRequest(response, args) {
this._ensureAdapterChannelCreated("evaluateRequest");
(0, _invariant.default)(this._adapterChannel !== undefined, "Adapter Channel used before it was created, in debugger.");
this._adapterChannel.evaluate(response.request_seq, args.frameId, args.expression, dbgResponse => {
let evalResult = dbgResponse.result;
(0, _invariant.default)(evalResult.kind === "evaluate");
response.body = {
result: evalResult.displayValue,
type: evalResult.type,
variablesReference: evalResult.variablesReference
};
this.sendResponse(response);
});
}
_ensureAdapterChannelCreated(callingRequest) {
// All responses that involve the Adapter Channel should only be invoked
// after the channel has been created. If this ordering is perturbed,
// there was likely a change in the protocol implementation by Nuclide.
if (this._adapterChannel === undefined) {
throw new _DebuggerError.DebuggerError("Startup Error", `Adapter Channel in Debugger is being used before it has been created. Caused by ${callingRequest}.`);
}
}
}
_vscodeDebugadapter.DebugSession.run(PrepackDebugSession);
//# sourceMappingURL=DebugAdapter.js.map