"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DebugServer = undefined; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /** * 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. */ var _BreakpointManager = require("./BreakpointManager.js"); var _Breakpoint = require("./Breakpoint.js"); var _invariant = require("../invariant.js"); var _invariant2 = _interopRequireDefault(_invariant); var _DebugMessage = require("./channel/DebugMessage.js"); var _DebuggerError = require("./DebuggerError.js"); var _realm = require("./../realm.js"); var _VariableManager = require("./VariableManager.js"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var DebugServer = exports.DebugServer = function () { function DebugServer(channel, realm) { _classCallCheck(this, DebugServer); this._breakpoints = new _BreakpointManager.BreakpointManager(); this._previousExecutedLine = 0; this._previousExecutedCol = 0; this._lastRunRequestID = 0; this._channel = channel; this._realm = realm; this._variableManager = new _VariableManager.VariableManager(realm); this.waitForRun(); } // the collection of breakpoints // the channel to communicate with the adapter _createClass(DebugServer, [{ key: "waitForRun", /* Block until adapter says to run /* ast: the current ast node we are stopped on */ value: function waitForRun(ast) { var keepRunning = false; var request = void 0; while (!keepRunning) { request = this._channel.readIn(); keepRunning = this.processDebuggerCommand(request, ast); } } // Checking if the debugger needs to take any action on reaching this ast node }, { key: "checkForActions", value: function checkForActions(ast) { 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; this._previousExecutedLine = ast.loc.start.line; this._previousExecutedCol = ast.loc.start.column; } } // Try to find a breakpoint at the given location and check if we should stop on it }, { key: "findStoppableBreakpoint", value: function findStoppableBreakpoint(filePath, lineNum, colNum) { var 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 // if so, we should skip it this time // Note: for the case when the debugger is supposed to stop on the same // 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 (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 breakpoint; } return null; } }, { key: "checkForBreakpoint", value: function checkForBreakpoint(ast) { if (ast.loc && ast.loc.source) { var location = ast.loc; var filePath = location.source; if (filePath === null) return; var lineNum = location.start.line; var colNum = location.start.column; // Check whether there is a breakpoint we need to stop on here var breakpoint = this.findStoppableBreakpoint(filePath, lineNum, colNum); if (breakpoint === null) return; // Tell the adapter that Prepack has stopped on this breakpoint this._channel.sendBreakpointStopped(breakpoint.filePath, breakpoint.line, breakpoint.column); // Wait for the adapter to tell us to run again this.waitForRun(ast); } } // Process a command from a debugger. Returns whether Prepack should unblock // if it is blocked }, { key: "processDebuggerCommand", value: function processDebuggerCommand(request, ast) { var requestID = request.id; var command = request.command; var args = request.arguments; switch (command) { case _DebugMessage.DebugMessage.BREAKPOINT_ADD_COMMAND: (0, _invariant2.default)(args.kind === "breakpoint"); this._breakpoints.addBreakpointMulti(args.breakpoints); this._channel.sendBreakpointsAcknowledge(_DebugMessage.DebugMessage.BREAKPOINT_ADD_ACKNOWLEDGE, requestID, args); break; case _DebugMessage.DebugMessage.BREAKPOINT_REMOVE_COMMAND: (0, _invariant2.default)(args.kind === "breakpoint"); this._breakpoints.removeBreakpointMulti(args.breakpoints); this._channel.sendBreakpointsAcknowledge(_DebugMessage.DebugMessage.BREAKPOINT_REMOVE_ACKNOWLEDGE, requestID, args); break; case _DebugMessage.DebugMessage.BREAKPOINT_ENABLE_COMMAND: (0, _invariant2.default)(args.kind === "breakpoint"); this._breakpoints.enableBreakpointMulti(args.breakpoints); this._channel.sendBreakpointsAcknowledge(_DebugMessage.DebugMessage.BREAKPOINT_ENABLE_ACKNOWLEDGE, requestID, args); break; case _DebugMessage.DebugMessage.BREAKPOINT_DISABLE_COMMAND: (0, _invariant2.default)(args.kind === "breakpoint"); this._breakpoints.disableBreakpointMulti(args.breakpoints); this._channel.sendBreakpointsAcknowledge(_DebugMessage.DebugMessage.BREAKPOINT_DISABLE_ACKNOWLEDGE, requestID, args); break; case _DebugMessage.DebugMessage.PREPACK_RUN_COMMAND: (0, _invariant2.default)(args.kind === "run"); this._onDebuggeeResume(); return true; case _DebugMessage.DebugMessage.STACKFRAMES_COMMAND: (0, _invariant2.default)(args.kind === "stackframe"); (0, _invariant2.default)(ast !== undefined); this.processStackframesCommand(requestID, args, ast); break; case _DebugMessage.DebugMessage.SCOPES_COMMAND: (0, _invariant2.default)(args.kind === "scopes"); this.processScopesCommand(requestID, args); break; case _DebugMessage.DebugMessage.VARIABLES_COMMAND: (0, _invariant2.default)(args.kind === "variables"); this.processVariablesCommand(requestID, args); break; default: throw new _DebuggerError.DebuggerError("Invalid command", "Invalid command from adapter: " + command); } return false; } }, { key: "processStackframesCommand", value: function processStackframesCommand(requestID, args, ast) { var frameInfos = []; var loc = this._getFrameLocation(ast.loc); var fileName = loc.fileName; var line = loc.line; var column = loc.column; // the UI displays the current frame as index 0, so we iterate backwards // from the current frame for (var i = this._realm.contextStack.length - 1; i >= 0; i--) { var frame = this._realm.contextStack[i]; var functionName = "(anonymous function)"; if (frame.function && frame.function.__originalName) { functionName = frame.function.__originalName; } var frameInfo = { id: this._realm.contextStack.length - 1 - i, functionName: functionName, fileName: fileName, line: line, column: column }; frameInfos.push(frameInfo); loc = this._getFrameLocation(frame.loc); fileName = loc.fileName; line = loc.line; column = loc.column; } this._channel.sendStackframeResponse(requestID, frameInfos); } }, { key: "_getFrameLocation", value: function _getFrameLocation(loc) { var fileName = "unknown"; var line = 0; var column = 0; if (loc && loc.source) { fileName = loc.source; line = loc.start.line; column = loc.start.column; } return { fileName: fileName, line: line, column: column }; } }, { key: "processScopesCommand", value: function processScopesCommand(requestID, args) { // first check that frameId is in the valid range if (args.frameId < 0 || args.frameId >= this._realm.contextStack.length) { throw new _DebuggerError.DebuggerError("Invalid command", "Invalid frame id for scopes request: " + args.frameId); } // here the frameId is in reverse order of the contextStack, ie frameId 0 // refers to last element of contextStack var stackIndex = this._realm.contextStack.length - 1 - args.frameId; var context = this._realm.contextStack[stackIndex]; (0, _invariant2.default)(context instanceof _realm.ExecutionContext); var scopes = []; if (context.variableEnvironment) { // get a new mapping for this collection of variables var variableRef = this._variableManager.getReferenceForValue(context.variableEnvironment); var scope = { name: "Locals", variablesReference: variableRef, expensive: false }; scopes.push(scope); } if (context.lexicalEnvironment) { // get a new mapping for this collection of variables var _variableRef = this._variableManager.getReferenceForValue(context.lexicalEnvironment); var _scope = { name: "Globals", variablesReference: _variableRef, expensive: false }; scopes.push(_scope); } this._channel.sendScopesResponse(requestID, scopes); } }, { key: "processVariablesCommand", value: function processVariablesCommand(requestID, args) { var variables = this._variableManager.getVariablesByReference(args.variablesReference); this._channel.sendVariablesResponse(requestID, variables); } // actions that need to happen before Prepack can resume }, { key: "_onDebuggeeResume", value: function _onDebuggeeResume() { // resets the variable manager this._variableManager.clean(); } }, { key: "shutdown", value: function shutdown() { //let the adapter know Prepack is done running this._channel.sendPrepackFinish(); } }]); return DebugServer; }(); //# sourceMappingURL=Debugger.js.map