in tools/hdb/hdb.cpp [334:538]
bool parseAndRunImmediateCommand(
std::string input,
debugger::Debugger &debugger) {
using debugger::BreakpointID;
using debugger::PauseOnThrowMode;
std::string command = chompToken(&input);
if (command == "frame" || command == "f") {
// Modify the selected frame.
try {
frame_ = std::stoul(input);
std::cout << "Selected frame " << frame_ << '\n';
} catch (const std::invalid_argument &e) {
std::cout << "Invalid frame: " << e.what() << '\n';
}
} else if (command == "set") {
std::string toSet = chompToken(&input);
std::string value = chompToken(&input);
if (toSet == "pauseOnThrow" && value == "on") {
debugger.setPauseOnThrowMode(PauseOnThrowMode::All);
std::cout << "Set pauseOnThrow: all errors\n";
} else if (toSet == "pauseOnThrow" && value == "uncaught") {
debugger.setPauseOnThrowMode(PauseOnThrowMode::Uncaught);
std::cout << "Set pauseOnThrow: uncaught errors\n";
} else if (toSet == "pauseOnThrow" && value == "off") {
debugger.setPauseOnThrowMode(PauseOnThrowMode::None);
std::cout << "Disabled pauseOnThrow\n";
} else {
std::cout << "Invalid 'set' command\n";
}
} else if (command == "break" || command == "b") {
// Break command supports an "if" token.
// When it exists, the rest of the input is interpreted as a condition.
// Chomp one token at a time, and stop if we see "if",
// since every location argument except the first must be a number.
const char *ifStr = "if";
bool conditional = false;
std::vector<std::string> toks{};
// In conditional breakpoints, the 4th token could be "if",
// so read 4 tokens.
for (uint32_t i = 0; i < 4; ++i) {
auto tok = chompToken(&input);
if (tok.empty()) {
break;
}
if (tok == ifStr) {
conditional = true;
break;
}
toks.push_back(tok);
}
try {
debugger::SourceLocation loc{};
// Formats accepted:
// <line>
// <filename> <line>
// <line> <column>
// <filename> <line> <column>
switch (toks.size()) {
case 1:
loc.line = std::stoul(toks[0]);
loc.column = debugger::kInvalidLocation;
loc.fileId = debugger::kInvalidLocation;
break;
case 2:
try {
// Attempt to parse as <line> <column> and use <filename> <line>
// as a fallback.
loc.line = std::stoul(toks[0]);
loc.column = std::stoul(toks[1]);
loc.fileId = debugger::kInvalidLocation;
} catch (const std::invalid_argument &e) {
// If this fails, then give up on parsing the command.
loc.fileName = toks[0];
loc.line = std::stoul(toks[1]);
loc.column = debugger::kInvalidLocation;
loc.fileId = debugger::kInvalidLocation;
}
break;
case 3:
loc.fileName = toks[0];
loc.line = std::stoul(toks[1]);
loc.column = std::stoul(toks[2]);
loc.fileId = debugger::kInvalidLocation;
break;
default:
std::cout << "Invalid breakpoint request\n";
return false;
}
// If the breakpoint was conditional, we've already taken the "if"
// token, so use the rest of the line as the condition.
BreakpointID breakpointId = debugger.setBreakpoint(loc);
if (conditional) {
debugger.setBreakpointCondition(breakpointId, input);
}
if (breakpointId == debugger::kInvalidBreakpoint) {
// Failed to set the breakpoint.
// TODO: Improve error reporting here.
std::cout << "Invalid or duplicate breakpoint not set\n";
} else {
const auto info = debugger.getBreakpointInfo(breakpointId);
const auto &loc =
info.resolved ? info.resolvedLocation : info.requestedLocation;
std::cout << "Set breakpoint " << breakpointId << " at "
<< loc.fileName << '[' << loc.fileId << ']' << ':'
<< loc.line << ':' << loc.column
<< (info.resolved ? "" : " (unresolved)");
if (conditional) {
std::cout << " if " << input;
}
std::cout << '\n';
}
} catch (const std::invalid_argument &e) {
std::cout << "Invalid breakpoint request: " << e.what() << '\n';
}
} else if (command == "delete") {
std::string request = chompToken(&input);
if (request == "all" || request == "a") {
debugger.deleteAllBreakpoints();
std::cout << "Deleted all breakpoints\n";
} else {
try {
BreakpointID breakpointId = std::stoul(request);
debugger.deleteBreakpoint(breakpointId);
std::cout << "Deleted breakpoint " << breakpointId << "\n";
} catch (const std::invalid_argument &e) {
std::cout << "Invalid breakpoint: " << e.what() << '\n';
}
}
} else if (command == "enable") {
try {
BreakpointID breakpointId = std::stoul(input);
debugger.setBreakpointEnabled(breakpointId, true);
std::cout << "Enabled breakpoint " << breakpointId << "\n";
} catch (const std::invalid_argument &e) {
std::cout << "Invalid breakpoint: " << e.what() << '\n';
}
} else if (command == "disable") {
try {
BreakpointID breakpointId = std::stoul(input);
debugger.setBreakpointEnabled(breakpointId, false);
std::cout << "Disabled breakpoint " << breakpointId << "\n";
} catch (const std::invalid_argument &e) {
std::cout << "Invalid breakpoint: " << e.what() << '\n';
}
} else if (command == "info" || command == "i") {
std::string request = chompToken(&input);
if (request == "breakpoints" || request == "b") {
const std::vector<BreakpointID> ids = debugger.getBreakpoints();
for (const auto &id : ids) {
const auto info = debugger.getBreakpointInfo(id);
assert(info.id == id && "invalid breakpoint ID");
const auto &loc =
info.resolved ? info.resolvedLocation : info.requestedLocation;
std::cout << id << ' ' << (info.enabled ? 'E' : 'D') << ' '
<< loc.fileName << ':' << loc.line << ':' << loc.column
<< (info.resolved ? "" : " (unresolved)") << '\n';
}
} else if (request == "variables" || request == "v") {
printVariables(debugger.getProgramState());
} else {
std::cout << "Not a valid info sub-command. See below for list of"
<< "info sub-commands.\n\n"
<< "info breakpoints\n"
<< "info variables\n";
}
} else if (command == "backtrace" || command == "bt") {
printStackTrace(debugger.getProgramState().getStackTrace(), frame_);
} else if (command == "expand") {
try {
expandProperties(debugger, input);
} catch (const std::exception &e) {
std::cout << e.what() << '\n';
}
} else if (command == "sourceMap") {
try {
uint32_t fileId = std::stoul(input);
auto sourceMappingUrl = debugger.getSourceMappingUrl(fileId);
if (sourceMappingUrl.empty()) {
std::cout << "Source map not found for file\n";
} else {
std::cout << sourceMappingUrl << '\n';
}
} catch (const std::invalid_argument &e) {
std::cout << "Invalid fileId: " << e.what() << '\n';
}
} else if (command == "help" || command == "h") {
// Given help <command> print definition of command otherwise
// print the list of available commands
std::string helpCmd = chompToken(&input);
if (commandToHelpText.find(helpCmd) != commandToHelpText.end()) {
std::cout << commandToHelpText.at(helpCmd);
} else if (!helpCmd.empty()) {
std::cout << "Not a valid command. See below for list of commands.\n\n"
<< topLevelHelpText;
} else {
std::cout << topLevelHelpText;
}
} else if (command == "quit" || command == "q") {
exit(0);
} else {
return false;
}
return true;
}