Chromium Code Reviews| Index: test/cctest/interpreter/generate-bytecode-expectations.cc |
| diff --git a/test/cctest/interpreter/generate-bytecode-expectations.cc b/test/cctest/interpreter/generate-bytecode-expectations.cc |
| index 8bf9ba6dfb9ac28e459e71bf1f7480c1962bd074..8b13dcf812a3e16ae872fa17243c689d48cb82e5 100644 |
| --- a/test/cctest/interpreter/generate-bytecode-expectations.cc |
| +++ b/test/cctest/interpreter/generate-bytecode-expectations.cc |
| @@ -28,6 +28,10 @@ class ProgramOptions { |
| print_help_(false), |
| read_raw_js_snippet_(false), |
| read_from_stdin_(false), |
| + fix_(false), |
| + print_banner_(true), |
| + wrap_(true), |
| + execute_(true), |
| const_pool_type_( |
| BytecodeExpectationsPrinter::ConstantPoolType::kMixed) {} |
| @@ -37,18 +41,32 @@ class ProgramOptions { |
| bool print_help() const { return print_help_; } |
| bool read_raw_js_snippet() const { return read_raw_js_snippet_; } |
| bool read_from_stdin() const { return read_from_stdin_; } |
| - std::string filename() const { return filename_; } |
| + bool fix() const { return fix_; } |
| + bool print_banner() const { return print_banner_; } |
| + bool wrap() const { return wrap_; } |
| + bool execute() const { return execute_; } |
| BytecodeExpectationsPrinter::ConstantPoolType const_pool_type() const { |
| return const_pool_type_; |
| } |
| + std::string input_filename() const { return input_filename_; } |
| + std::string output_filename() const { return output_file_name_; } |
| + std::string top_function_name() const { return top_function_name_; } |
| private: |
| bool parsing_failed_; |
| bool print_help_; |
| bool read_raw_js_snippet_; |
| bool read_from_stdin_; |
| + bool fix_; |
| + bool print_banner_; |
| + bool wrap_; |
| + bool execute_; |
| BytecodeExpectationsPrinter::ConstantPoolType const_pool_type_; |
| - std::string filename_; |
| + std::string input_filename_; |
| + std::string output_file_name_; |
| + std::string top_function_name_; |
| + |
| + friend void ParseOptionsHeader(ProgramOptions*, std::istream&); |
| }; |
| class ArrayBufferAllocator final : public v8::ArrayBuffer::Allocator { |
| @@ -95,8 +113,6 @@ BytecodeExpectationsPrinter::ConstantPoolType ParseConstantPoolType( |
| ProgramOptions ProgramOptions::FromCommandLine(int argc, char** argv) { |
| ProgramOptions options; |
| - if (argc <= 1) return options; |
| - |
| for (int i = 1; i < argc; ++i) { |
| if (strcmp(argv[i], "--help") == 0) { |
| options.print_help_ = true; |
| @@ -106,13 +122,25 @@ ProgramOptions ProgramOptions::FromCommandLine(int argc, char** argv) { |
| options.const_pool_type_ = ParseConstantPoolType(argv[i] + 12); |
| } else if (strcmp(argv[i], "--stdin") == 0) { |
| options.read_from_stdin_ = true; |
| + } else if (strcmp(argv[i], "--fix") == 0) { |
| + options.fix_ = true; |
| + } else if (strcmp(argv[i], "--no-banner") == 0) { |
| + options.print_banner_ = false; |
| + } else if (strcmp(argv[i], "--no-wrap") == 0) { |
| + options.wrap_ = false; |
| + } else if (strcmp(argv[i], "--no-execute") == 0) { |
| + options.execute_ = false; |
| + } else if (strncmp(argv[i], "--output=", 9) == 0) { |
| + options.output_file_name_ = argv[i] + 9; |
| + } else if (strncmp(argv[i], "--wrapper-name=", 15) == 0) { |
| + options.top_function_name_ = argv[i] + 15; |
| } else if (strncmp(argv[i], "--", 2) != 0) { // It doesn't start with -- |
| - if (!options.filename_.empty()) { |
| + if (!options.input_filename_.empty()) { |
| std::cerr << "ERROR: More than one input file specified\n"; |
| options.parsing_failed_ = true; |
| break; |
| } |
| - options.filename_ = argv[i]; |
| + options.input_filename_ = argv[i]; |
| } else { |
| std::cerr << "ERROR: Unknonwn option " << argv[i] << "\n"; |
| options.parsing_failed_ = true; |
| @@ -123,6 +151,75 @@ ProgramOptions ProgramOptions::FromCommandLine(int argc, char** argv) { |
| return options; |
| } |
| +bool ParseBoolean(const char* string) { |
| + if (strcmp(string, "yes") == 0) { |
| + return true; |
| + } else if (strcmp(string, "no") == 0) { |
| + return false; |
| + } else { |
| + UNREACHABLE(); |
| + return false; |
| + } |
| +} |
| + |
| +void ParseOptionsHeader(ProgramOptions* options, |
| + std::istream& stream) { // NOLINT |
| + std::string line; |
| + |
| + // Skip to the beginning of the options header |
| + while (std::getline(stream, line)) { |
| + if (line == "---") break; |
| + } |
| + |
| + while (std::getline(stream, line)) { |
| + if (line.compare(0, 11, "pool type: ") == 0) { |
| + options->const_pool_type_ = ParseConstantPoolType(line.c_str() + 11); |
| + } else if (line.compare(0, 9, "execute: ") == 0) { |
| + options->execute_ = ParseBoolean(line.c_str() + 9); |
| + } else if (line.compare(0, 6, "wrap: ") == 0) { |
| + options->wrap_ = ParseBoolean(line.c_str() + 6); |
| + } else if (line.compare(0, 13, "wrapper name: ") == 0) { |
| + options->top_function_name_ = line.c_str() + 14; |
| + } else { |
| + break; |
| + } |
| + } |
| +} |
| + |
| +const char* BooleanString(bool value) { return value ? "yes" : "no"; } |
|
rmcilroy
2016/02/16 16:43:48
nit - move up near ParseBoolean
Stefano Sanfilippo
2016/02/16 20:28:26
Done.
|
| + |
| +const char* ConstantPoolTypeString( |
| + BytecodeExpectationsPrinter::ConstantPoolType type) { |
| + switch (type) { |
| + case BytecodeExpectationsPrinter::ConstantPoolType::kDouble: |
| + return "double"; |
| + case BytecodeExpectationsPrinter::ConstantPoolType::kInteger: |
| + return "integer"; |
| + case BytecodeExpectationsPrinter::ConstantPoolType::kMixed: |
| + return "mixed"; |
| + case BytecodeExpectationsPrinter::ConstantPoolType::kString: |
| + return "string"; |
| + default: |
| + UNREACHABLE(); |
| + return nullptr; |
| + } |
| +} |
| + |
| +void EmitOptionsHeader(std::ostream& stream, // NOLINT |
| + const ProgramOptions& options) { |
| + stream << "---" |
| + "\npool type: " |
| + << ConstantPoolTypeString(options.const_pool_type()) |
| + << "\nexecute: " << BooleanString(options.execute()) |
| + << "\nwrap: " << BooleanString(options.wrap()); |
| + |
| + if (!options.top_function_name().empty()) { |
| + stream << "\nwrapper name: " << options.top_function_name(); |
| + } |
|
rmcilroy
2016/02/16 16:43:48
Could you emit a seperator between the options and
Stefano Sanfilippo
2016/02/16 20:28:26
I have updated the code to use the separator of th
|
| + |
| + stream << "\n\n"; |
| +} |
| + |
| bool ProgramOptions::Validate() const { |
| if (parsing_failed_) return false; |
| if (print_help_) return true; |
| @@ -133,16 +230,21 @@ bool ProgramOptions::Validate() const { |
| return false; |
| } |
| - if (!read_from_stdin_ && filename_.empty()) { |
| + if (!read_from_stdin_ && input_filename_.empty()) { |
| std::cerr << "ERROR: No input file specified.\n"; |
| return false; |
| } |
| - if (read_from_stdin_ && !filename_.empty()) { |
| + if (read_from_stdin_ && !input_filename_.empty()) { |
| std::cerr << "ERROR: Reading from stdin, but input files supplied.\n"; |
| return false; |
| } |
| + if (!wrap_ && !top_function_name_.empty()) { |
| + std::cerr << "ERROR: not wrapping, but wrapper name specified.\n"; |
| + return false; |
| + } |
| + |
|
rmcilroy
2016/02/16 16:43:48
You should also validate that --rebaseline and --r
Stefano Sanfilippo
2016/02/16 20:28:26
Done.
|
| return true; |
| } |
| @@ -194,41 +296,64 @@ bool ReadNextSnippet(std::istream& stream, std::string* string_out) { // NOLINT |
| return false; |
| } |
| +std::string UnescapeString(const std::string& escaped_string) { |
| + std::string unescaped_string; |
| + bool previous_was_backslash = false; |
| + for (char c : escaped_string) { |
| + if (previous_was_backslash) { |
| + // If it was not an escape sequence, emit the backslash |
|
rmcilroy
2016/02/16 16:43:48
/s/the backslash/the previous backslash./
Stefano Sanfilippo
2016/02/16 20:28:26
Done.
|
| + if (c != '\\' && c != '"') unescaped_string += '\\'; |
| + unescaped_string += c; |
| + previous_was_backslash = false; |
| + } else { |
| + if (c == '\\') { |
| + previous_was_backslash = true; |
| + // Defer emission to the point where we can check if it was an escape. |
| + } else { |
| + unescaped_string += c; |
| + } |
| + } |
| + } |
| + return unescaped_string; |
| +} |
| + |
| void ExtractSnippetsFromStream(std::vector<std::string>* snippet_list, |
| + ProgramOptions* options, |
| std::istream& body_stream, // NOLINT |
| bool read_raw_js_snippet) { |
| if (read_raw_js_snippet) { |
| snippet_list->push_back(ReadRawJSSnippet(body_stream)); |
| } else { |
| + ParseOptionsHeader(options, body_stream); |
|
rmcilroy
2016/02/16 16:43:48
This should only happen for --rebaseline I think (
Stefano Sanfilippo
2016/02/16 20:28:26
Please see below.
|
| std::string snippet; |
| while (ReadNextSnippet(body_stream, &snippet)) { |
| - snippet_list->push_back(snippet); |
| + snippet_list->push_back(UnescapeString(snippet)); |
| } |
| } |
| } |
| bool ExtractSnippets(std::vector<std::string>* snippet_list, |
| - const ProgramOptions& options) { |
| - if (options.read_from_stdin()) { |
| - ExtractSnippetsFromStream(snippet_list, std::cin, |
| - options.read_raw_js_snippet()); |
| + ProgramOptions* options) { |
|
rmcilroy
2016/02/16 16:43:48
This should still be const, right? Any reason to m
Stefano Sanfilippo
2016/02/16 20:28:26
The reason is that ParseOptionsHeader called insid
rmcilroy
2016/02/17 10:16:59
I think the solution is to split out the opening o
rmcilroy
2016/02/17 10:16:59
I think the solution is to split out the opening o
Stefano Sanfilippo
2016/02/17 14:32:29
I've rewritten the piece using a better approach w
|
| + if (options->read_from_stdin()) { |
| + ExtractSnippetsFromStream(snippet_list, options, std::cin, |
| + options->read_raw_js_snippet()); |
| } else { |
| - std::ifstream body_file(options.filename().c_str()); |
| + std::ifstream body_file(options->input_filename().c_str()); |
| if (!body_file.is_open()) { |
| - std::cerr << "ERROR: Could not open '" << options.filename() << "'.\n"; |
| + std::cerr << "ERROR: Could not open '" << options->input_filename() |
| + << "'.\n"; |
| return false; |
| } |
| - ExtractSnippetsFromStream(snippet_list, body_file, |
| - options.read_raw_js_snippet()); |
| + ExtractSnippetsFromStream(snippet_list, options, body_file, |
| + options->read_raw_js_snippet()); |
| } |
| return true; |
| } |
| -void GenerateExpectationsFile( |
| - std::ostream& stream, // NOLINT |
| - const std::vector<std::string>& snippet_list, |
| - BytecodeExpectationsPrinter::ConstantPoolType const_pool_type, |
| - const char* exec_path) { |
| +void GenerateExpectationsFile(std::ostream& stream, // NOLINT |
| + const std::vector<std::string>& snippet_list, |
| + const ProgramOptions& options, |
| + const char* exec_path) { |
| V8InitializationScope platform(exec_path); |
| { |
| v8::Isolate::Scope isolate_scope(platform.isolate()); |
| @@ -236,9 +361,20 @@ void GenerateExpectationsFile( |
| v8::Local<v8::Context> context = v8::Context::New(platform.isolate()); |
| v8::Context::Scope context_scope(context); |
| - stream << "#\n# Autogenerated by generate-bytecode-expectations\n#\n\n"; |
| + if (options.print_banner()) { |
| + stream << "#\n# Autogenerated by generate-bytecode-expectations\n#\n\n"; |
| + } |
| + |
| + EmitOptionsHeader(stream, options); |
| + |
| + BytecodeExpectationsPrinter printer(platform.isolate(), |
| + options.const_pool_type()); |
| + printer.set_wrap(options.wrap()); |
| + printer.set_execute(options.execute()); |
| + if (!options.top_function_name().empty()) { |
| + printer.set_top_function_name(options.top_function_name()); |
| + } |
| - BytecodeExpectationsPrinter printer(platform.isolate(), const_pool_type); |
| for (const std::string& snippet : snippet_list) { |
| printer.PrintExpectation(stream, snippet); |
| } |
| @@ -253,8 +389,15 @@ void PrintUsage(const char* exec_path) { |
| " --help Print this help message.\n" |
| " --raw-js Read raw JavaScript, instead of the output format.\n" |
| " --stdin Read from standard input instead of file.\n" |
| + " --fix Overwrite input file.\n" |
|
rmcilroy
2016/02/16 16:43:48
Call this "--rebaseline Rebaseline input snippet
Stefano Sanfilippo
2016/02/16 20:28:26
Done.
|
| + " --no-banner Do not add the \"generated by\" banner.\n" |
|
rmcilroy
2016/02/16 16:43:48
Why is this one required? All golden files should
Stefano Sanfilippo
2016/02/16 20:28:26
ATM the banner is emitted by the utility before ca
rmcilroy
2016/02/17 10:17:00
I think the banner should be printed by this tool
Stefano Sanfilippo
2016/02/17 14:32:29
Done. I ended up changing the API from accepting a
|
| + " --no-wrap Do not wrap the snippet in a function.\n" |
| + " --no-execute Do not execute after compilation.\n" |
| + " --output=file.name Specify the output file. " |
| + "If not specified, output goes to stdout.\n" |
| + " --wrapper-name=foo Specify the name of the wrapper function.\n" |
| " --pool-type=(int|double|string|mixed)\n" |
| - " specify the type of the entries in the constant pool " |
| + " Specify the type of the entries in the constant pool " |
| "(default: mixed).\n" |
| "\n" |
| "Each raw JavaScript file is interpreted as a single snippet.\n\n" |
| @@ -274,10 +417,23 @@ int main(int argc, char** argv) { |
| } |
| std::vector<std::string> snippet_list; |
| - if (!ExtractSnippets(&snippet_list, options)) { |
| + if (!ExtractSnippets(&snippet_list, &options)) { |
| return 2; |
| } |
| - GenerateExpectationsFile(std::cout, snippet_list, options.const_pool_type(), |
| - argv[0]); |
| + CHECK(options.Validate()); |
| + |
| + if (!options.output_filename().empty() || options.fix()) { |
| + std::ofstream output_file(options.fix() |
| + ? options.input_filename().c_str() |
| + : options.output_filename().c_str()); |
| + if (!output_file.is_open()) { |
| + std::cerr << "ERROR: Could not open '" << options.output_filename() |
| + << "' for writing.\n"; |
| + return 3; |
| + } |
| + GenerateExpectationsFile(output_file, snippet_list, options, argv[0]); |
| + } else { |
| + GenerateExpectationsFile(std::cout, snippet_list, options, argv[0]); |
| + } |
| } |