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