UNPKG

prepack

Version:

Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.

404 lines (294 loc) 13.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DebugServer = void 0; var _BreakpointManager = require("./BreakpointManager.js"); var _types = require("@babel/types"); var _invariant = _interopRequireDefault(require("../common/invariant.js")); var _DebugMessage = require("./../common/channel/DebugMessage.js"); var _DebuggerError = require("./../common/DebuggerError.js"); var _realm = require("./../../realm.js"); var _VariableManager = require("./VariableManager.js"); var _SteppingManager = require("./SteppingManager.js"); var _StopEventManager = require("./StopEventManager.js"); var _environment = require("./../../environment.js"); var _errors = require("../../errors.js"); var _SourceMapManager = require("../../utils/SourceMapManager.js"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * 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 */ class DebugServer { constructor(channel, realm, configArgs) { this._channel = channel; this._realm = realm; this._breakpointManager = new _BreakpointManager.BreakpointManager(); this._variableManager = new _VariableManager.VariableManager(realm); this._stepManager = new _SteppingManager.SteppingManager(this._realm, /* default discard old steppers */ false); this._stopEventManager = new _StopEventManager.StopEventManager(); this._diagnosticSeverity = configArgs.diagnosticSeverity || "FatalError"; this._sourceMapManager = new _SourceMapManager.SourceMapManager(configArgs.buckRoot, configArgs.sourcemaps); this.waitForRun(undefined); } // the collection of breakpoints /* Block until adapter says to run /* ast: the current ast node we are stopped on /* reason: the reason the debuggee is stopping */ waitForRun(loc) { let keepRunning = false; let request; while (!keepRunning) { request = this._channel.readIn(); keepRunning = this.processDebuggerCommand(request, loc); } } // Checking if the debugger needs to take any action on reaching this ast node checkForActions(ast) { if (this._checkAndUpdateLastExecuted(ast)) { let stoppables = this._stepManager.getAndDeleteCompletedSteppers(ast); let breakpoint = this._breakpointManager.getStoppableBreakpoint(ast); if (breakpoint) stoppables.push(breakpoint); let reason = this._stopEventManager.getDebuggeeStopReason(ast, stoppables); if (reason) { let location = ast.loc; (0, _invariant.default)(location && location.source !== null); let absolutePath = this._sourceMapManager.relativeToAbsolute(location.source); this._channel.sendStoppedResponse(reason, absolutePath, location.start.line, location.start.column); this.waitForRun(location); } } } // Process a command from a debugger. Returns whether Prepack should unblock // if it is blocked processDebuggerCommand(request, loc) { let requestID = request.id; let command = request.command; let args = request.arguments; // Convert incoming location sources to relative paths in order to match internal representation of filenames. if (args.kind === "breakpoint") { for (let bp of args.breakpoints) { bp.filePath = this._sourceMapManager.absoluteToRelative(bp.filePath); } } switch (command) { case _DebugMessage.DebugMessage.BREAKPOINT_ADD_COMMAND: (0, _invariant.default)(args.kind === "breakpoint"); this._breakpointManager.addBreakpointMulti(args.breakpoints); this._channel.sendBreakpointsAcknowledge(_DebugMessage.DebugMessage.BREAKPOINT_ADD_ACKNOWLEDGE, requestID, args); break; case _DebugMessage.DebugMessage.BREAKPOINT_REMOVE_COMMAND: (0, _invariant.default)(args.kind === "breakpoint"); this._breakpointManager.removeBreakpointMulti(args.breakpoints); this._channel.sendBreakpointsAcknowledge(_DebugMessage.DebugMessage.BREAKPOINT_REMOVE_ACKNOWLEDGE, requestID, args); break; case _DebugMessage.DebugMessage.BREAKPOINT_ENABLE_COMMAND: (0, _invariant.default)(args.kind === "breakpoint"); this._breakpointManager.enableBreakpointMulti(args.breakpoints); this._channel.sendBreakpointsAcknowledge(_DebugMessage.DebugMessage.BREAKPOINT_ENABLE_ACKNOWLEDGE, requestID, args); break; case _DebugMessage.DebugMessage.BREAKPOINT_DISABLE_COMMAND: (0, _invariant.default)(args.kind === "breakpoint"); this._breakpointManager.disableBreakpointMulti(args.breakpoints); this._channel.sendBreakpointsAcknowledge(_DebugMessage.DebugMessage.BREAKPOINT_DISABLE_ACKNOWLEDGE, requestID, args); break; case _DebugMessage.DebugMessage.PREPACK_RUN_COMMAND: (0, _invariant.default)(args.kind === "run"); this._onDebuggeeResume(); return true; case _DebugMessage.DebugMessage.STACKFRAMES_COMMAND: (0, _invariant.default)(args.kind === "stackframe"); this.processStackframesCommand(requestID, args, loc); break; case _DebugMessage.DebugMessage.SCOPES_COMMAND: (0, _invariant.default)(args.kind === "scopes"); this.processScopesCommand(requestID, args); break; case _DebugMessage.DebugMessage.VARIABLES_COMMAND: (0, _invariant.default)(args.kind === "variables"); this.processVariablesCommand(requestID, args); break; case _DebugMessage.DebugMessage.STEPINTO_COMMAND: (0, _invariant.default)(loc !== undefined); this._stepManager.processStepCommand("in", loc); this._onDebuggeeResume(); return true; case _DebugMessage.DebugMessage.STEPOVER_COMMAND: (0, _invariant.default)(loc !== undefined); this._stepManager.processStepCommand("over", loc); this._onDebuggeeResume(); return true; case _DebugMessage.DebugMessage.STEPOUT_COMMAND: (0, _invariant.default)(loc !== undefined); this._stepManager.processStepCommand("out", loc); this._onDebuggeeResume(); return true; case _DebugMessage.DebugMessage.EVALUATE_COMMAND: (0, _invariant.default)(args.kind === "evaluate"); this.processEvaluateCommand(requestID, args); break; default: throw new _DebuggerError.DebuggerError("Invalid command", "Invalid command from adapter: " + command); } return false; } processStackframesCommand(requestID, args, astLoc) { let frameInfos = []; let loc = this._getFrameLocation(astLoc ? astLoc : null); let fileName = loc.fileName; let line = loc.line; let column = loc.column; // the UI displays the current frame as index 0, so we iterate backwards // from the current frame for (let i = this._realm.contextStack.length - 1; i >= 0; i--) { let frame = this._realm.contextStack[i]; let functionName = "(anonymous function)"; if (frame.function && frame.function.__originalName !== undefined) { functionName = frame.function.__originalName; } let frameInfo = { id: this._realm.contextStack.length - 1 - i, functionName: functionName, fileName: this._sourceMapManager.relativeToAbsolute(fileName), // Outward facing paths must be absolute. 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); } _getFrameLocation(loc) { let fileName = "unknown"; let line = 0; let column = 0; if (loc && loc.source !== null) { fileName = loc.source; line = loc.start.line; column = loc.start.column; } return { fileName: fileName, line: line, column: column }; } 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 let stackIndex = this._realm.contextStack.length - 1 - args.frameId; let context = this._realm.contextStack[stackIndex]; (0, _invariant.default)(context instanceof _realm.ExecutionContext); let scopes = []; let lexicalEnv = context.lexicalEnvironment; while (lexicalEnv) { let scope = { name: this._getScopeName(lexicalEnv.environmentRecord), // key used by UI to retrieve variables in this scope variablesReference: this._variableManager.getReferenceForValue(lexicalEnv), // the variables are easy to retrieve expensive: false }; scopes.push(scope); lexicalEnv = lexicalEnv.parent; } this._channel.sendScopesResponse(requestID, scopes); } _getScopeName(envRec) { if (envRec instanceof _environment.GlobalEnvironmentRecord) { return "Global"; } else if (envRec instanceof _environment.DeclarativeEnvironmentRecord) { if (envRec instanceof _environment.FunctionEnvironmentRecord) { let name = envRec.$FunctionObject.__originalName; if (name === undefined) name = "anonymous function"; return "Local: " + name; } else { return "Block"; } } else if (envRec instanceof _environment.ObjectEnvironmentRecord) { return "With"; } else { (0, _invariant.default)(false, "Invalid type of environment record"); } } processVariablesCommand(requestID, args) { let variables = this._variableManager.getVariablesByReference(args.variablesReference); this._channel.sendVariablesResponse(requestID, variables); } processEvaluateCommand(requestID, args) { let evalResult = this._variableManager.evaluate(args.frameId, args.expression); this._channel.sendEvaluateResponse(requestID, evalResult); } // actions that need to happen before Prepack can resume _onDebuggeeResume() { // resets the variable manager this._variableManager.clean(); } /* Returns whether there are more nodes in the ast. */ _checkAndUpdateLastExecuted(ast) { if (ast.loc && ast.loc.source !== null) { let filePath = ast.loc.source; let line = ast.loc.start.line; let column = ast.loc.start.column; let stackSize = this._realm.contextStack.length; // Check if the current location is same as the last one. // Does not check columns since column debugging is not supported. // Column support is unnecessary because these nodes will have been sourcemap-translated. // Ignoring columns prevents: // - Lines with multiple AST nodes from triggering the same breakpoint more than once. // - Step-out from completing in the same line that it was set in. if (this._lastExecuted && filePath === this._lastExecuted.filePath && line === this._lastExecuted.line && stackSize === this._lastExecuted.stackSize) { return false; } this._lastExecuted = { filePath: filePath, line: line, column: column, stackSize: this._realm.contextStack.length }; return true; } return false; } // Displays Prepack error message, then waits for user to run the program to continue (similar to a breakpoint). handlePrepackError(diagnostic) { (0, _invariant.default)(diagnostic.location && diagnostic.location.source !== null); // The following constructs the message and stop-instruction that is sent to the UI to actually stop the execution. let location = diagnostic.location; let absoluteSource = ""; if (location.source !== null) absoluteSource = this._sourceMapManager.relativeToAbsolute(location.source); let message = `${diagnostic.severity} ${diagnostic.errorCode}: ${diagnostic.message}`; console.log(message); this._channel.sendStoppedResponse("Diagnostic", absoluteSource, location.start.line, location.start.column, message); // The AST Node's location is needed to satisfy the subsequent stackTrace request. this.waitForRun(location); } // Return whether the debugger should stop on a CompilerDiagnostic of a given severity. shouldStopForSeverity(severity) { switch (this._diagnosticSeverity) { case "Information": return true; case "Warning": return severity !== "Information"; case "RecoverableError": return severity === "RecoverableError" || severity === "FatalError"; case "FatalError": return severity === "FatalError"; default: (0, _invariant.default)(false, "Unexpected severity type"); } } shutdown() { // clean the channel pipes this._channel.shutdown(); } } exports.DebugServer = DebugServer; //# sourceMappingURL=Debugger.js.map