bool parseAndRunImmediateCommand()

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;
  }