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..1a09ac62c105432262f082e80f28e1f5a283a7b2 100644 |
--- a/test/cctest/interpreter/generate-bytecode-expectations.cc |
+++ b/test/cctest/interpreter/generate-bytecode-expectations.cc |
@@ -19,7 +19,7 @@ using v8::internal::interpreter::BytecodeExpectationsPrinter; |
namespace { |
-class ProgramOptions { |
+class ProgramOptions final { |
public: |
static ProgramOptions FromCommandLine(int argc, char** argv); |
@@ -28,27 +28,48 @@ class ProgramOptions { |
print_help_(false), |
read_raw_js_snippet_(false), |
read_from_stdin_(false), |
+ rebaseline_(false), |
+ wrap_(true), |
+ execute_(true), |
+ top_level_(false), |
const_pool_type_( |
BytecodeExpectationsPrinter::ConstantPoolType::kMixed) {} |
bool Validate() const; |
+ void UpdateFromHeader(std::istream& stream); // NOLINT |
+ void PrintHeader(std::ostream& stream) const; // NOLINT |
bool parsing_failed() const { return parsing_failed_; } |
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 write_to_stdout() const { |
+ return output_filename_.empty() && !rebaseline_; |
+ } |
+ bool rebaseline() const { return rebaseline_; } |
+ bool wrap() const { return wrap_; } |
+ bool execute() const { return execute_; } |
+ bool top_level() const { return top_level_; } |
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_filename_; } |
+ std::string test_function_name() const { return test_function_name_; } |
private: |
bool parsing_failed_; |
bool print_help_; |
bool read_raw_js_snippet_; |
bool read_from_stdin_; |
+ bool rebaseline_; |
+ bool wrap_; |
+ bool execute_; |
+ bool top_level_; |
BytecodeExpectationsPrinter::ConstantPoolType const_pool_type_; |
- std::string filename_; |
+ std::string input_filename_; |
+ std::string output_filename_; |
+ std::string test_function_name_; |
}; |
class ArrayBufferAllocator final : public v8::ArrayBuffer::Allocator { |
@@ -79,10 +100,8 @@ class V8InitializationScope final { |
BytecodeExpectationsPrinter::ConstantPoolType ParseConstantPoolType( |
const char* type_string) { |
- if (strcmp(type_string, "int") == 0) { |
- return BytecodeExpectationsPrinter::ConstantPoolType::kInteger; |
- } else if (strcmp(type_string, "double") == 0) { |
- return BytecodeExpectationsPrinter::ConstantPoolType::kDouble; |
+ if (strcmp(type_string, "number") == 0) { |
+ return BytecodeExpectationsPrinter::ConstantPoolType::kNumber; |
} else if (strcmp(type_string, "string") == 0) { |
return BytecodeExpectationsPrinter::ConstantPoolType::kString; |
} else if (strcmp(type_string, "mixed") == 0) { |
@@ -91,12 +110,38 @@ BytecodeExpectationsPrinter::ConstantPoolType ParseConstantPoolType( |
return BytecodeExpectationsPrinter::ConstantPoolType::kUnknown; |
} |
+const char* ConstantPoolTypeToString( |
+ BytecodeExpectationsPrinter::ConstantPoolType type) { |
+ switch (type) { |
+ case BytecodeExpectationsPrinter::ConstantPoolType::kNumber: |
+ return "number"; |
+ case BytecodeExpectationsPrinter::ConstantPoolType::kMixed: |
+ return "mixed"; |
+ case BytecodeExpectationsPrinter::ConstantPoolType::kString: |
+ return "string"; |
+ default: |
+ UNREACHABLE(); |
+ return nullptr; |
+ } |
+} |
+ |
+bool ParseBoolean(const char* string) { |
+ if (strcmp(string, "yes") == 0) { |
+ return true; |
+ } else if (strcmp(string, "no") == 0) { |
+ return false; |
+ } else { |
+ UNREACHABLE(); |
+ return false; |
+ } |
+} |
+ |
+const char* BooleanToString(bool value) { return value ? "yes" : "no"; } |
+ |
// static |
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 +151,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], "--rebaseline") == 0) { |
+ options.rebaseline_ = true; |
+ } else if (strcmp(argv[i], "--no-wrap") == 0) { |
+ options.wrap_ = false; |
+ } else if (strcmp(argv[i], "--no-execute") == 0) { |
+ options.execute_ = false; |
+ } else if (strcmp(argv[i], "--top-level") == 0) { |
+ options.top_level_ = 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.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; |
@@ -133,19 +190,76 @@ 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 (rebaseline_ && read_raw_js_snippet_) { |
+ std::cerr << "ERROR: Cannot use --rebaseline on a raw JS snippet.\n"; |
+ return false; |
+ } |
+ |
+ if (top_level_ && !test_function_name_.empty()) { |
+ std::cerr << "ERROR: test function name specified while processing " |
+ "top level code.\n"; |
+ return false; |
+ } |
+ |
return true; |
} |
+void ProgramOptions::UpdateFromHeader(std::istream& stream) { |
+ 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) { |
+ const_pool_type_ = ParseConstantPoolType(line.c_str() + 11); |
+ } else if (line.compare(0, 9, "execute: ") == 0) { |
+ execute_ = ParseBoolean(line.c_str() + 9); |
+ } else if (line.compare(0, 6, "wrap: ") == 0) { |
+ wrap_ = ParseBoolean(line.c_str() + 6); |
+ } else if (line.compare(0, 20, "test function name: ") == 0) { |
+ test_function_name_ = line.c_str() + 20; |
+ } else if (line.compare(0, 11, "top level: ") == 0) { |
+ top_level_ = ParseBoolean(line.c_str() + 11); |
+ } else if (line == "---") { |
+ break; |
+ } else if (line.empty()) { |
+ continue; |
+ } else { |
+ UNREACHABLE(); |
+ return; |
+ } |
+ } |
+} |
+ |
+void ProgramOptions::PrintHeader(std::ostream& stream) const { // NOLINT |
+ stream << "---" |
+ "\npool type: " |
+ << ConstantPoolTypeToString(const_pool_type_) |
+ << "\nexecute: " << BooleanToString(execute_) |
+ << "\nwrap: " << BooleanToString(wrap_); |
+ |
+ if (!test_function_name_.empty()) { |
+ stream << "\ntest function name: " << test_function_name_; |
+ } |
+ |
+ if (top_level_) stream << "\ntop level: yes"; |
+ |
+ stream << "\n\n"; |
+} |
+ |
V8InitializationScope::V8InitializationScope(const char* exec_path) |
: platform_(v8::platform::CreateDefaultPlatform()) { |
i::FLAG_ignition = true; |
@@ -194,41 +308,44 @@ bool ReadNextSnippet(std::istream& stream, std::string* string_out) { // NOLINT |
return false; |
} |
-void ExtractSnippetsFromStream(std::vector<std::string>* snippet_list, |
- std::istream& body_stream, // NOLINT |
- bool read_raw_js_snippet) { |
- if (read_raw_js_snippet) { |
- snippet_list->push_back(ReadRawJSSnippet(body_stream)); |
- } else { |
- std::string snippet; |
- while (ReadNextSnippet(body_stream, &snippet)) { |
- snippet_list->push_back(snippet); |
+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 previous backslash |
+ 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; |
} |
-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()); |
+void ExtractSnippets(std::vector<std::string>* snippet_list, |
+ std::istream& body_stream, // NOLINT |
+ bool read_raw_js_snippet) { |
+ if (read_raw_js_snippet) { |
+ snippet_list->push_back(ReadRawJSSnippet(body_stream)); |
} else { |
- std::ifstream body_file(options.filename().c_str()); |
- if (!body_file.is_open()) { |
- std::cerr << "ERROR: Could not open '" << options.filename() << "'.\n"; |
- return false; |
+ std::string snippet; |
+ while (ReadNextSnippet(body_stream, &snippet)) { |
+ snippet_list->push_back(UnescapeString(snippet)); |
} |
- ExtractSnippetsFromStream(snippet_list, 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 +353,17 @@ 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"; |
+ 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()); |
+ } |
- BytecodeExpectationsPrinter printer(platform.isolate(), const_pool_type); |
+ stream << "#\n# Autogenerated by generate-bytecode-expectations\n#\n\n"; |
+ options.PrintHeader(stream); |
for (const std::string& snippet : snippet_list) { |
printer.PrintExpectation(stream, snippet); |
} |
@@ -253,10 +378,22 @@ 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" |
+ " --rebaseline Rebaseline input snippet file.\n" |
+ " --no-wrap Do not wrap the snippet in a function.\n" |
+ " --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." |
+ " --output=file.name\n" |
+ " Specify the output file. If not specified, output goes to " |
+ "stdout.\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" |
+ "When using --rebaseline, flags --no-wrap, --no-execute, " |
+ "--test-function-name\nand --pool-type will be overridden by the " |
+ "options specified in the input file\nheader.\n\n" |
"Each raw JavaScript file is interpreted as a single snippet.\n\n" |
"This tool is intended as a help in writing tests.\n" |
"Please, DO NOT blindly copy and paste the output " |
@@ -273,11 +410,39 @@ 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()); |
+ } |
+ |
std::vector<std::string> snippet_list; |
- if (!ExtractSnippets(&snippet_list, options)) { |
- return 2; |
+ 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; |
+ } |
} |
+ std::ostream& output_stream = |
+ options.write_to_stdout() ? std::cout : output_file_handle; |
- GenerateExpectationsFile(std::cout, snippet_list, options.const_pool_type(), |
- argv[0]); |
+ GenerateExpectationsFile(output_stream, snippet_list, options, argv[0]); |
} |