fuzzverter.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. // An utility for converting between fuzzer protos and Carbon sources.
  5. //
  6. // For example, to convert a crashing input in text proto to carbon source:
  7. // `fuzzverter --mode=proto_to_carbon --input file.textproto`
  8. //
  9. // To generate a new text proto from carbon source for seeding the corpus:
  10. // `fuzzverter --mode=carbon_to_proto --input file.carbon`
  11. #include <google/protobuf/text_format.h>
  12. #include <cstdlib>
  13. #include <fstream>
  14. #include <ios>
  15. #include <sstream>
  16. #include "common/error.h"
  17. #include "common/fuzzing/carbon.pb.h"
  18. #include "executable_semantics/common/error.h"
  19. #include "executable_semantics/fuzzing/ast_to_proto.h"
  20. #include "executable_semantics/fuzzing/fuzzer_util.h"
  21. #include "executable_semantics/syntax/parse.h"
  22. #include "llvm/Support/CommandLine.h"
  23. #include "llvm/Support/InitLLVM.h"
  24. namespace Carbon {
  25. // Reads a file and returns its contents as a string.
  26. static auto ReadFile(std::string_view file_name) -> ErrorOr<std::string> {
  27. std::ifstream file(file_name, std::ios::in);
  28. if (!file.is_open()) {
  29. return ErrorBuilder() << "Could not open " << file_name << " for reading";
  30. }
  31. std::stringstream ss;
  32. ss << file.rdbuf();
  33. return ss.str();
  34. }
  35. // Writes string `s` to `file_name`.
  36. static auto WriteFile(std::string_view s, std::string_view file_name)
  37. -> ErrorOr<Success> {
  38. std::ofstream file(file_name, std::ios::out);
  39. if (!file.is_open()) {
  40. return ErrorBuilder() << "Could not open " << file_name << " for writing";
  41. }
  42. file << s;
  43. return Success();
  44. }
  45. // Converts text proto to Carbon source.
  46. static auto TextProtoToCarbon(std::string_view input_file_name,
  47. std::string_view output_file_name)
  48. -> ErrorOr<Success> {
  49. ASSIGN_OR_RETURN(const std::string input_contents, ReadFile(input_file_name));
  50. Fuzzing::Carbon carbon_proto;
  51. if (!google::protobuf::TextFormat::ParseFromString(input_contents,
  52. &carbon_proto)) {
  53. return Error("Could not parse text proto");
  54. }
  55. const std::string carbon_source =
  56. ProtoToCarbonWithMain(carbon_proto.compilation_unit());
  57. return WriteFile(carbon_source, output_file_name);
  58. }
  59. // Converts Carbon source to text proto.
  60. static auto CarbonToTextProto(std::string_view input_file_name,
  61. std::string_view output_file_name)
  62. -> ErrorOr<Success> {
  63. Carbon::Arena arena;
  64. const ErrorOr<AST> ast = Carbon::Parse(&arena, input_file_name,
  65. /*trace=*/false);
  66. if (!ast.ok()) {
  67. return ErrorBuilder() << "Parsing failed: " << ast.error().message();
  68. }
  69. Fuzzing::Carbon carbon_proto;
  70. *carbon_proto.mutable_compilation_unit() = AstToProto(*ast);
  71. std::string proto_string;
  72. google::protobuf::TextFormat::Printer p;
  73. if (!p.PrintToString(carbon_proto, &proto_string)) {
  74. return Error("Failed to convert to text proto");
  75. }
  76. return WriteFile(proto_string, output_file_name);
  77. }
  78. // Command line options for defining input/output format.
  79. enum class ConversionMode { TextProtoToCarbon, CarbonToTextProto };
  80. // Returns string representation of an enum option.
  81. static auto GetEnumString(llvm::cl::opt<ConversionMode>& o) -> llvm::StringRef {
  82. // TODO: is there a better way?
  83. return o.getParser().getOption(static_cast<int>(ConversionMode(o)));
  84. }
  85. // Performs the conversion specified by `mode`.
  86. auto Convert(const ConversionMode mode, std::string_view input_file_name,
  87. std::string_view output_file_name) -> ErrorOr<Success> {
  88. switch (mode) {
  89. case ConversionMode::TextProtoToCarbon:
  90. return TextProtoToCarbon(input_file_name, output_file_name);
  91. case ConversionMode::CarbonToTextProto:
  92. return CarbonToTextProto(input_file_name, output_file_name);
  93. }
  94. }
  95. } // namespace Carbon
  96. auto main(int argc, char* argv[]) -> int {
  97. llvm::InitLLVM init_llvm(argc, argv);
  98. llvm::cl::opt<Carbon::ConversionMode> mode(
  99. "mode", llvm::cl::desc("Conversion mode"),
  100. llvm::cl::values(
  101. clEnumValN(Carbon::ConversionMode::TextProtoToCarbon,
  102. "proto_to_carbon", "Convert text proto to Carbon source"),
  103. clEnumValN(Carbon::ConversionMode::CarbonToTextProto,
  104. "carbon_to_proto",
  105. "Convert Carbon source to text proto")));
  106. llvm::cl::opt<std::string> input_file_name(
  107. "input", llvm::cl::desc("<input file>"), llvm::cl::init("/dev/stdin"));
  108. llvm::cl::opt<std::string> output_file_name(
  109. "output", llvm::cl::desc("<output file>"), llvm::cl::init("/dev/stdout"));
  110. llvm::cl::ParseCommandLineOptions(argc, argv);
  111. if (const auto result =
  112. Carbon::Convert(mode, input_file_name, output_file_name);
  113. !result.ok()) {
  114. llvm::errs() << GetEnumString(mode)
  115. << " conversion failed: " << result.error().message() << "\n";
  116. return EXIT_FAILURE;
  117. }
  118. return EXIT_SUCCESS;
  119. }