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 db55a6cbabfe7e7cf7f2c6412fb1aa9a2d46d3a3..0c9874020bdb5a2a138348941591c7c7695839e3 100644 |
--- a/test/cctest/interpreter/generate-bytecode-expectations.cc |
+++ b/test/cctest/interpreter/generate-bytecode-expectations.cc |
@@ -4,6 +4,7 @@ |
#include <cstring> |
#include <fstream> |
+#include <vector> |
#include "test/cctest/interpreter/bytecode-expectations-printer.h" |
@@ -15,10 +16,20 @@ |
#include "src/compiler.h" |
#include "src/interpreter/interpreter.h" |
+#ifdef V8_OS_POSIX |
+#include <dirent.h> |
+#endif |
+ |
using v8::internal::interpreter::BytecodeExpectationsPrinter; |
+#define REPORT_ERROR(MESSAGE) (((std::cerr << "ERROR: ") << MESSAGE) << '\n') |
+ |
namespace { |
+#ifdef V8_OS_POSIX |
+const char* kGoldenFilesPath = "test/cctest/interpreter/bytecode_expectations/"; |
+#endif |
+ |
class ProgramOptions final { |
public: |
static ProgramOptions FromCommandLine(int argc, char** argv); |
@@ -34,6 +45,7 @@ class ProgramOptions final { |
top_level_(false), |
legacy_const_(false), |
do_expressions_(false), |
+ verbose_(false), |
const_pool_type_( |
BytecodeExpectationsPrinter::ConstantPoolType::kMixed) {} |
@@ -54,10 +66,11 @@ class ProgramOptions final { |
bool top_level() const { return top_level_; } |
bool legacy_const() const { return legacy_const_; } |
bool do_expressions() const { return do_expressions_; } |
+ bool verbose() const { return verbose_; } |
BytecodeExpectationsPrinter::ConstantPoolType const_pool_type() const { |
return const_pool_type_; |
} |
- std::string input_filename() const { return input_filename_; } |
+ std::vector<std::string> input_filenames() const { return input_filenames_; } |
std::string output_filename() const { return output_filename_; } |
std::string test_function_name() const { return test_function_name_; } |
@@ -72,8 +85,9 @@ class ProgramOptions final { |
bool top_level_; |
bool legacy_const_; |
bool do_expressions_; |
+ bool verbose_; |
BytecodeExpectationsPrinter::ConstantPoolType const_pool_type_; |
- std::string input_filename_; |
+ std::vector<std::string> input_filenames_; |
std::string output_filename_; |
std::string test_function_name_; |
}; |
@@ -144,6 +158,39 @@ bool ParseBoolean(const char* string) { |
const char* BooleanToString(bool value) { return value ? "yes" : "no"; } |
+#ifdef V8_OS_POSIX |
+ |
+bool StrEndsWith(const char* string, const char* suffix) { |
+ int string_size = i::StrLength(string); |
+ int suffix_size = i::StrLength(suffix); |
+ if (string_size < suffix_size) return false; |
+ |
+ return strcmp(string + (string_size - suffix_size), suffix) == 0; |
+} |
+ |
+bool CollectGoldenFiles(std::vector<std::string>* golden_file_list, |
+ const char* directory_path) { |
+ DIR* directory = opendir(directory_path); |
+ if (!directory) return false; |
+ |
+ dirent entry_buffer; |
+ dirent* entry; |
+ |
+ while (readdir_r(directory, &entry_buffer, &entry) == 0 && entry) { |
+ if (StrEndsWith(entry->d_name, ".golden")) { |
+ std::string golden_filename(kGoldenFilesPath); |
+ golden_filename += entry->d_name; |
+ golden_file_list->push_back(golden_filename); |
+ } |
+ } |
+ |
+ closedir(directory); |
+ |
+ return true; |
+} |
+ |
+#endif // V8_OS_POSIX |
+ |
// static |
ProgramOptions ProgramOptions::FromCommandLine(int argc, char** argv) { |
ProgramOptions options; |
@@ -169,24 +216,36 @@ ProgramOptions ProgramOptions::FromCommandLine(int argc, char** argv) { |
options.legacy_const_ = true; |
} else if (strcmp(argv[i], "--do-expressions") == 0) { |
options.do_expressions_ = true; |
+ } else if (strcmp(argv[i], "--verbose") == 0) { |
+ options.verbose_ = true; |
} else if (strncmp(argv[i], "--output=", 9) == 0) { |
options.output_filename_ = argv[i] + 9; |
} else if (strncmp(argv[i], "--test-function-name=", 21) == 0) { |
options.test_function_name_ = argv[i] + 21; |
} else if (strncmp(argv[i], "--", 2) != 0) { // It doesn't start with -- |
- if (!options.input_filename_.empty()) { |
- std::cerr << "ERROR: More than one input file specified\n"; |
- options.parsing_failed_ = true; |
- break; |
- } |
- options.input_filename_ = argv[i]; |
+ options.input_filenames_.push_back(argv[i]); |
} else { |
- std::cerr << "ERROR: Unknonwn option " << argv[i] << "\n"; |
+ REPORT_ERROR("Unknonwn option " << argv[i]); |
options.parsing_failed_ = true; |
break; |
} |
} |
+ if (options.rebaseline_ && options.input_filenames_.empty()) { |
+#ifdef V8_OS_POSIX |
+ if (options.verbose_) { |
+ std::cout << "Looking for golden files in " << kGoldenFilesPath << '\n'; |
+ } |
+ if (!CollectGoldenFiles(&options.input_filenames_, kGoldenFilesPath)) { |
+ REPORT_ERROR("Golden files autodiscovery failed."); |
+ options.parsing_failed_ = true; |
+ } |
+#else |
+ REPORT_ERROR("Golden files autodiscovery requires a POSIX OS, sorry."); |
+ options.parsing_failed_ = true; |
+#endif |
+ } |
+ |
return options; |
} |
@@ -196,28 +255,44 @@ bool ProgramOptions::Validate() const { |
if (const_pool_type_ == |
BytecodeExpectationsPrinter::ConstantPoolType::kUnknown) { |
- std::cerr << "ERROR: Unknown constant pool type.\n"; |
+ REPORT_ERROR("Unknown constant pool type."); |
return false; |
} |
- if (!read_from_stdin_ && input_filename_.empty()) { |
- std::cerr << "ERROR: No input file specified.\n"; |
+ if (!read_from_stdin_ && input_filenames_.empty()) { |
+ REPORT_ERROR("No input file specified."); |
return false; |
} |
- if (read_from_stdin_ && !input_filename_.empty()) { |
- std::cerr << "ERROR: Reading from stdin, but input files supplied.\n"; |
+ if (read_from_stdin_ && !input_filenames_.empty()) { |
+ REPORT_ERROR("Reading from stdin, but input files supplied."); |
return false; |
} |
if (rebaseline_ && read_raw_js_snippet_) { |
- std::cerr << "ERROR: Cannot use --rebaseline on a raw JS snippet.\n"; |
+ REPORT_ERROR("Cannot use --rebaseline on a raw JS snippet."); |
+ return false; |
+ } |
+ |
+ if (rebaseline_ && !output_filename_.empty()) { |
+ REPORT_ERROR("Output file cannot be specified together with --rebaseline."); |
+ return false; |
+ } |
+ |
+ if (rebaseline_ && read_from_stdin_) { |
+ REPORT_ERROR("Cannot --rebaseline when input is --stdin."); |
+ return false; |
+ } |
+ |
+ if (input_filenames_.size() > 1 && !rebaseline_ && !read_raw_js_snippet()) { |
+ REPORT_ERROR( |
+ "Multiple input files, but no --rebaseline or --raw-js specified."); |
return false; |
} |
if (top_level_ && !test_function_name_.empty()) { |
- std::cerr << "ERROR: test function name specified while processing " |
- "top level code.\n"; |
+ REPORT_ERROR( |
+ "Test function name specified while processing top level code."); |
return false; |
} |
@@ -360,41 +435,62 @@ void ExtractSnippets(std::vector<std::string>* snippet_list, |
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()); |
- v8::HandleScope handle_scope(platform.isolate()); |
- v8::Local<v8::Context> context = v8::Context::New(platform.isolate()); |
- v8::Context::Scope context_scope(context); |
- |
- BytecodeExpectationsPrinter printer(platform.isolate(), |
- options.const_pool_type()); |
- printer.set_wrap(options.wrap()); |
- printer.set_execute(options.execute()); |
- printer.set_top_level(options.top_level()); |
- if (!options.test_function_name().empty()) { |
- printer.set_test_function_name(options.test_function_name()); |
- } |
+ const V8InitializationScope& platform, |
+ const ProgramOptions& options) { |
+ v8::Isolate::Scope isolate_scope(platform.isolate()); |
+ v8::HandleScope handle_scope(platform.isolate()); |
+ v8::Local<v8::Context> context = v8::Context::New(platform.isolate()); |
+ v8::Context::Scope context_scope(context); |
+ |
+ BytecodeExpectationsPrinter printer(platform.isolate(), |
+ options.const_pool_type()); |
+ printer.set_wrap(options.wrap()); |
+ printer.set_execute(options.execute()); |
+ printer.set_top_level(options.top_level()); |
+ if (!options.test_function_name().empty()) { |
+ printer.set_test_function_name(options.test_function_name()); |
+ } |
- if (options.legacy_const()) i::FLAG_legacy_const = true; |
- if (options.do_expressions()) i::FLAG_harmony_do_expressions = true; |
+ if (options.legacy_const()) i::FLAG_legacy_const = true; |
+ if (options.do_expressions()) i::FLAG_harmony_do_expressions = true; |
- stream << "#\n# Autogenerated by generate-bytecode-expectations.\n#\n\n"; |
- options.PrintHeader(stream); |
- for (const std::string& snippet : snippet_list) { |
- printer.PrintExpectation(stream, snippet); |
+ stream << "#\n# Autogenerated by generate-bytecode-expectations.\n#\n\n"; |
+ options.PrintHeader(stream); |
+ for (const std::string& snippet : snippet_list) { |
+ printer.PrintExpectation(stream, snippet); |
+ } |
+ |
+ i::FLAG_legacy_const = false; |
+ i::FLAG_harmony_do_expressions = false; |
+} |
+ |
+bool WriteExpectationsFile(const std::vector<std::string>& snippet_list, |
+ const V8InitializationScope& platform, |
+ const ProgramOptions& options, |
+ const std::string& output_filename) { |
+ std::ofstream output_file_handle; |
+ if (!options.write_to_stdout()) { |
+ output_file_handle.open(output_filename.c_str()); |
+ if (!output_file_handle.is_open()) { |
+ REPORT_ERROR("Could not open " << output_filename << " for writing."); |
+ return false; |
} |
} |
+ std::ostream& output_stream = |
+ options.write_to_stdout() ? std::cout : output_file_handle; |
+ |
+ GenerateExpectationsFile(output_stream, snippet_list, platform, options); |
+ |
+ return true; |
} |
void PrintUsage(const char* exec_path) { |
std::cerr |
<< "\nUsage: " << exec_path |
- << " [OPTIONS]... [INPUT FILE]\n\n" |
+ << " [OPTIONS]... [INPUT FILES]...\n\n" |
"Options:\n" |
" --help Print this help message.\n" |
+ " --verbose Emit messages about the progress of the tool.\n" |
" --raw-js Read raw JavaScript, instead of the output format.\n" |
" --stdin Read from standard input instead of file.\n" |
" --rebaseline Rebaseline input snippet file.\n" |
@@ -402,7 +498,7 @@ void PrintUsage(const char* exec_path) { |
" --no-execute Do not execute after compilation.\n" |
" --test-function-name=foo " |
"Specify the name of the test function.\n" |
- " --top-level Process top level code, not the top-level function." |
+ " --top-level Process top level code, not the top-level function.\n" |
" --legacy-const Enable legacy_const flag.\n" |
" --do-expressions Enable harmony_do_expressions flag.\n" |
" --output=file.name\n" |
@@ -431,39 +527,50 @@ int main(int argc, char** argv) { |
return options.print_help() ? 0 : 1; |
} |
- std::ifstream input_file_handle; |
- if (!options.read_from_stdin()) { |
- input_file_handle.open(options.input_filename().c_str()); |
- if (!input_file_handle.is_open()) { |
- std::cerr << "ERROR: Could not open '" << options.input_filename() |
- << "' for reading.\n"; |
- return 2; |
- } |
- } |
- std::istream& input_stream = |
- options.read_from_stdin() ? std::cin : input_file_handle; |
- |
- if (options.rebaseline()) { |
- options.UpdateFromHeader(input_stream); |
- CHECK(options.Validate()); |
- } |
+ V8InitializationScope platform(argv[0]); |
std::vector<std::string> snippet_list; |
- ExtractSnippets(&snippet_list, input_stream, options.read_raw_js_snippet()); |
- std::ofstream output_file_handle; |
- if (!options.write_to_stdout()) { |
- output_file_handle.open(options.rebaseline() |
- ? options.input_filename().c_str() |
- : options.output_filename().c_str()); |
- if (!output_file_handle.is_open()) { |
- std::cerr << "ERROR: Could not open '" << options.output_filename() |
- << "' for writing.\n"; |
- return 3; |
+ if (options.read_from_stdin()) { |
+ // Rebaseline will never get here, so we will always take the |
+ // GenerateExpectationsFile at the end of this function. |
+ DCHECK(!options.rebaseline()); |
+ ExtractSnippets(&snippet_list, std::cin, options.read_raw_js_snippet()); |
+ } else { |
+ for (const std::string& input_filename : options.input_filenames()) { |
+ if (options.verbose()) { |
+ std::cerr << "Processing " << input_filename << '\n'; |
+ } |
+ |
+ std::ifstream input_stream(input_filename.c_str()); |
+ if (!input_stream.is_open()) { |
+ REPORT_ERROR("Could not open " << input_filename << " for reading."); |
+ return 2; |
+ } |
+ |
+ ProgramOptions updated_options = options; |
+ if (options.rebaseline()) { |
+ updated_options.UpdateFromHeader(input_stream); |
+ CHECK(updated_options.Validate()); |
+ } |
+ |
+ ExtractSnippets(&snippet_list, input_stream, |
+ options.read_raw_js_snippet()); |
+ |
+ if (options.rebaseline()) { |
+ if (!WriteExpectationsFile(snippet_list, platform, updated_options, |
+ input_filename)) { |
+ return 3; |
+ } |
+ snippet_list.clear(); |
+ } |
} |
} |
- std::ostream& output_stream = |
- options.write_to_stdout() ? std::cout : output_file_handle; |
- GenerateExpectationsFile(output_stream, snippet_list, options, argv[0]); |
+ if (!options.rebaseline()) { |
+ if (!WriteExpectationsFile(snippet_list, platform, options, |
+ options.output_filename())) { |
+ return 3; |
+ } |
+ } |
} |