generate-bytecode-expectations.cc 22.7 KB
Newer Older
1 2 3 4
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include <algorithm>
6
#include <cstring>
7
#include <fstream>
8
#include <memory>
9
#include <sstream>
10
#include <vector>
11 12

#include "test/cctest/interpreter/bytecode-expectations-printer.h"
13 14 15 16 17 18 19

#include "include/libplatform/libplatform.h"
#include "include/v8.h"

#include "src/base/logging.h"
#include "src/interpreter/interpreter.h"

20 21 22 23
#ifdef V8_OS_POSIX
#include <dirent.h>
#endif

24
using v8::internal::interpreter::BytecodeExpectationsPrinter;
25

26 27
#define REPORT_ERROR(MESSAGE) (((std::cerr << "ERROR: ") << MESSAGE) << '\n')

28 29
namespace {

30 31
const char* kGoldenFilesPath = "test/cctest/interpreter/bytecode_expectations/";

32
class ProgramOptions final {
33 34 35 36 37 38 39 40
 public:
  static ProgramOptions FromCommandLine(int argc, char** argv);

  ProgramOptions()
      : parsing_failed_(false),
        print_help_(false),
        read_raw_js_snippet_(false),
        read_from_stdin_(false),
41
        rebaseline_(false),
42
        check_baseline_(false),
43
        wrap_(true),
44
        module_(false),
45
        top_level_(false),
46
        print_callee_(false),
47
        oneshot_opt_(false),
48
        async_iteration_(false),
49
        private_methods_(false),
50
        top_level_await_(false),
51
        verbose_(false) {}
52 53

  bool Validate() const;
54 55
  void UpdateFromHeader(std::istream* stream);
  void PrintHeader(std::ostream* stream) const;
56 57 58 59 60

  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_; }
61 62 63 64
  bool write_to_stdout() const {
    return output_filename_.empty() && !rebaseline_;
  }
  bool rebaseline() const { return rebaseline_; }
65 66
  bool check_baseline() const { return check_baseline_; }
  bool baseline() const { return rebaseline_ || check_baseline_; }
67
  bool wrap() const { return wrap_; }
68
  bool module() const { return module_; }
69
  bool top_level() const { return top_level_; }
70 71
  bool print_callee() const { return print_callee_; }
  bool oneshot_opt() const { return oneshot_opt_; }
72
  bool async_iteration() const { return async_iteration_; }
73
  bool private_methods() const { return private_methods_; }
74
  bool top_level_await() const { return top_level_await_; }
75
  bool verbose() const { return verbose_; }
76
  bool suppress_runtime_errors() const { return baseline() && !verbose_; }
77
  std::vector<std::string> input_filenames() const { return input_filenames_; }
78 79
  std::string output_filename() const { return output_filename_; }
  std::string test_function_name() const { return test_function_name_; }
80

81 82 83 84 85
 private:
  bool parsing_failed_;
  bool print_help_;
  bool read_raw_js_snippet_;
  bool read_from_stdin_;
86
  bool rebaseline_;
87
  bool check_baseline_;
88
  bool wrap_;
89
  bool module_;
90
  bool top_level_;
91 92
  bool print_callee_;
  bool oneshot_opt_;
93
  bool async_iteration_;
94
  bool private_methods_;
95
  bool top_level_await_;
96 97
  bool verbose_;
  std::vector<std::string> input_filenames_;
98 99
  std::string output_filename_;
  std::string test_function_name_;
100 101
};

102 103 104 105 106 107 108 109 110
class V8InitializationScope final {
 public:
  explicit V8InitializationScope(const char* exec_path);
  ~V8InitializationScope();

  v8::Platform* platform() const { return platform_.get(); }
  v8::Isolate* isolate() const { return isolate_; }

 private:
111 112
  std::unique_ptr<v8::Platform> platform_;
  std::unique_ptr<v8::ArrayBuffer::Allocator> allocator_;
113 114 115 116 117
  v8::Isolate* isolate_;

  DISALLOW_COPY_AND_ASSIGN(V8InitializationScope);
};

118 119 120 121 122 123 124 125 126 127 128 129
bool ParseBoolean(const char* string) {
  if (strcmp(string, "yes") == 0) {
    return true;
  } else if (strcmp(string, "no") == 0) {
    return false;
  } else {
    UNREACHABLE();
  }
}

const char* BooleanToString(bool value) { return value ? "yes" : "no"; }

130 131
bool CollectGoldenFiles(std::vector<std::string>* golden_file_list,
                        const char* directory_path) {
132
#ifdef V8_OS_POSIX
133 134 135
  DIR* directory = opendir(directory_path);
  if (!directory) return false;

136
  auto str_ends_with = [](const char* string, const char* suffix) {
137 138
    size_t string_size = strlen(string);
    size_t suffix_size = strlen(suffix);
139 140 141 142 143
    if (string_size < suffix_size) return false;

    return strcmp(string + (string_size - suffix_size), suffix) == 0;
  };

144 145
  dirent* entry = readdir(directory);
  while (entry) {
146
    if (str_ends_with(entry->d_name, ".golden")) {
147 148 149 150
      std::string golden_filename(kGoldenFilesPath);
      golden_filename += entry->d_name;
      golden_file_list->push_back(golden_filename);
    }
151
    entry = readdir(directory);
152 153 154
  }

  closedir(directory);
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
#elif V8_OS_WIN
  std::string search_path(directory_path + std::string("/*.golden"));
  WIN32_FIND_DATAA fd;
  HANDLE find_handle = FindFirstFileA(search_path.c_str(), &fd);
  if (find_handle == INVALID_HANDLE_VALUE) return false;
  do {
    if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
      std::string golden_filename(kGoldenFilesPath);
      std::string temp_filename(fd.cFileName);
      golden_filename += temp_filename;
      golden_file_list->push_back(golden_filename);
    }
  } while (FindNextFileA(find_handle, &fd));
  FindClose(find_handle);
#endif  // V8_OS_POSIX
170 171 172
  return true;
}

173 174 175 176 177 178 179 180 181 182 183
// static
ProgramOptions ProgramOptions::FromCommandLine(int argc, char** argv) {
  ProgramOptions options;

  for (int i = 1; i < argc; ++i) {
    if (strcmp(argv[i], "--help") == 0) {
      options.print_help_ = true;
    } else if (strcmp(argv[i], "--raw-js") == 0) {
      options.read_raw_js_snippet_ = true;
    } else if (strcmp(argv[i], "--stdin") == 0) {
      options.read_from_stdin_ = true;
184 185
    } else if (strcmp(argv[i], "--rebaseline") == 0) {
      options.rebaseline_ = true;
186 187
    } else if (strcmp(argv[i], "--check-baseline") == 0) {
      options.check_baseline_ = true;
188 189
    } else if (strcmp(argv[i], "--no-wrap") == 0) {
      options.wrap_ = false;
190 191
    } else if (strcmp(argv[i], "--module") == 0) {
      options.module_ = true;
192 193
    } else if (strcmp(argv[i], "--top-level") == 0) {
      options.top_level_ = true;
194 195 196 197
    } else if (strcmp(argv[i], "--print-callee") == 0) {
      options.print_callee_ = true;
    } else if (strcmp(argv[i], "--disable-oneshot-opt") == 0) {
      options.oneshot_opt_ = false;
198 199
    } else if (strcmp(argv[i], "--async-iteration") == 0) {
      options.async_iteration_ = true;
200 201
    } else if (strcmp(argv[i], "--private-methods") == 0) {
      options.private_methods_ = true;
202 203
    } else if (strcmp(argv[i], "--harmony-top-level-await") == 0) {
      options.top_level_await_ = true;
204 205
    } else if (strcmp(argv[i], "--verbose") == 0) {
      options.verbose_ = true;
206 207 208 209
    } 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;
210
    } else if (strncmp(argv[i], "--", 2) != 0) {  // It doesn't start with --
211
      options.input_filenames_.push_back(argv[i]);
212
    } else {
213
      REPORT_ERROR("Unknown option " << argv[i]);
214 215 216 217 218
      options.parsing_failed_ = true;
      break;
    }
  }

219 220 221 222 223 224 225
  if (options.rebaseline() && options.check_baseline()) {
    REPORT_ERROR("Can't check baseline and rebaseline at the same time.");
    std::exit(1);
  }

  if ((options.check_baseline_ || options.rebaseline_) &&
      options.input_filenames_.empty()) {
226
#if defined(V8_OS_POSIX) || defined(V8_OS_WIN)
227 228 229 230 231 232 233 234
    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
235 236
    REPORT_ERROR(
        "Golden files autodiscovery requires a POSIX or Window OS, sorry.");
237 238 239 240
    options.parsing_failed_ = true;
#endif
  }

241 242 243 244 245 246 247
  return options;
}

bool ProgramOptions::Validate() const {
  if (parsing_failed_) return false;
  if (print_help_) return true;

248 249
  if (!read_from_stdin_ && input_filenames_.empty()) {
    REPORT_ERROR("No input file specified.");
250 251 252
    return false;
  }

253 254
  if (read_from_stdin_ && !input_filenames_.empty()) {
    REPORT_ERROR("Reading from stdin, but input files supplied.");
255 256 257
    return false;
  }

258 259 260
  if (baseline() && read_raw_js_snippet_) {
    REPORT_ERROR(
        "Cannot use --rebaseline or --check-baseline on a raw JS snippet.");
261 262 263
    return false;
  }

264 265 266 267
  if (baseline() && !output_filename_.empty()) {
    REPORT_ERROR(
        "Output file cannot be specified together with --rebaseline or "
        "--check-baseline.");
268 269 270
    return false;
  }

271 272 273
  if (baseline() && read_from_stdin_) {
    REPORT_ERROR(
        "Cannot --rebaseline or --check-baseline when input is --stdin.");
274 275 276
    return false;
  }

277
  if (input_filenames_.size() > 1 && !baseline() && !read_raw_js_snippet()) {
278
    REPORT_ERROR(
279 280
        "Multiple input files, but no --rebaseline, --check-baseline or "
        "--raw-js specified.");
281 282 283 284
    return false;
  }

  if (top_level_ && !test_function_name_.empty()) {
285 286
    REPORT_ERROR(
        "Test function name specified while processing top level code.");
287 288 289
    return false;
  }

290 291 292 293 294 295
  if (module_ && (!top_level_ || wrap_)) {
    REPORT_ERROR(
        "The flag --module currently requires --top-level and --no-wrap.");
    return false;
  }

296
  return true;
297 298
}

299
void ProgramOptions::UpdateFromHeader(std::istream* stream) {
300
  std::string line;
301 302
  const char* kPrintCallee = "print callee: ";
  const char* kOneshotOpt = "oneshot opt: ";
303 304

  // Skip to the beginning of the options header
305
  while (std::getline(*stream, line)) {
306 307 308
    if (line == "---") break;
  }

309
  while (std::getline(*stream, line)) {
310 311 312
    if (line.compare(0, 8, "module: ") == 0) {
      module_ = ParseBoolean(line.c_str() + 8);
    } else if (line.compare(0, 6, "wrap: ") == 0) {
313 314 315 316 317
      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);
318 319 320 321
    } else if (line.compare(0, strlen(kPrintCallee), kPrintCallee) == 0) {
      print_callee_ = ParseBoolean(line.c_str() + strlen(kPrintCallee));
    } else if (line.compare(0, strlen(kOneshotOpt), kOneshotOpt) == 0) {
      oneshot_opt_ = ParseBoolean(line.c_str() + strlen(kOneshotOpt));
322 323
    } else if (line.compare(0, 17, "async iteration: ") == 0) {
      async_iteration_ = ParseBoolean(line.c_str() + 17);
324 325
    } else if (line.compare(0, 17, "private methods: ") == 0) {
      private_methods_ = ParseBoolean(line.c_str() + 17);
326 327
    } else if (line.compare(0, 17, "top level await: ") == 0) {
      top_level_await_ = ParseBoolean(line.c_str() + 17);
328 329 330 331 332 333 334 335 336 337
    } else if (line == "---") {
      break;
    } else if (line.empty()) {
      continue;
    } else {
      UNREACHABLE();
    }
  }
}

338 339 340
void ProgramOptions::PrintHeader(std::ostream* stream) const {
  *stream << "---"
          << "\nwrap: " << BooleanToString(wrap_);
341 342

  if (!test_function_name_.empty()) {
343
    *stream << "\ntest function name: " << test_function_name_;
344 345
  }

346 347 348 349 350 351
  if (module_) *stream << "\nmodule: yes";
  if (top_level_) *stream << "\ntop level: yes";
  if (print_callee_) *stream << "\nprint callee: yes";
  if (oneshot_opt_) *stream << "\noneshot opt: yes";
  if (async_iteration_) *stream << "\nasync iteration: yes";
  if (private_methods_) *stream << "\nprivate methods: yes";
352
  if (top_level_await_) *stream << "\ntop level await: yes";
353

354
  *stream << "\n\n";
355 356
}

357
V8InitializationScope::V8InitializationScope(const char* exec_path)
358
    : platform_(v8::platform::NewDefaultPlatform()) {
359 360
  i::FLAG_always_opt = false;
  i::FLAG_allow_natives_syntax = true;
361
  i::FLAG_enable_lazy_source_positions = false;
362

363
  v8::V8::InitializeICUDefaultLocation(exec_path);
364 365 366 367 368
  v8::V8::InitializeExternalStartupData(exec_path);
  v8::V8::InitializePlatform(platform_.get());
  v8::V8::Initialize();

  v8::Isolate::CreateParams create_params;
369
  allocator_.reset(v8::ArrayBuffer::Allocator::NewDefaultAllocator());
370
  create_params.array_buffer_allocator = allocator_.get();
371 372 373 374 375 376 377 378 379 380

  isolate_ = v8::Isolate::New(create_params);
}

V8InitializationScope::~V8InitializationScope() {
  isolate_->Dispose();
  v8::V8::Dispose();
  v8::V8::ShutdownPlatform();
}

381
std::string ReadRawJSSnippet(std::istream* stream) {
382
  std::stringstream body_buffer;
383
  CHECK(body_buffer << stream->rdbuf());
384
  return body_buffer.str();
385 386
}

387
bool ReadNextSnippet(std::istream* stream, std::string* string_out) {
388 389 390
  std::string line;
  bool found_begin_snippet = false;
  string_out->clear();
391
  while (std::getline(*stream, line)) {
392 393 394
    if (line == "snippet: \"") {
      found_begin_snippet = true;
      continue;
395
    }
396 397
    if (!found_begin_snippet) continue;
    if (line == "\"") return true;
398 399 400 401
    if (line.size() == 0) {
      string_out->append("\n");  // consume empty line
      continue;
    }
402 403 404
    CHECK_GE(line.size(), 2u);  // We should have the indent
    string_out->append(line.begin() + 2, line.end());
    *string_out += '\n';
405
  }
406
  return false;
407 408
}

409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
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;
      }
425
    }
426
  }
427
  return unescaped_string;
428 429
}

430
void ExtractSnippets(std::vector<std::string>* snippet_list,
431
                     std::istream* body_stream, bool read_raw_js_snippet) {
432 433
  if (read_raw_js_snippet) {
    snippet_list->push_back(ReadRawJSSnippet(body_stream));
434
  } else {
435 436 437
    std::string snippet;
    while (ReadNextSnippet(body_stream, &snippet)) {
      snippet_list->push_back(UnescapeString(snippet));
438 439
    }
  }
440 441
}

442
void GenerateExpectationsFile(std::ostream* stream,
443
                              const std::vector<std::string>& snippet_list,
444 445 446 447 448 449 450
                              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);

451
  BytecodeExpectationsPrinter printer(platform.isolate());
452
  printer.set_wrap(options.wrap());
453
  printer.set_module(options.module());
454
  printer.set_top_level(options.top_level());
455 456
  printer.set_print_callee(options.print_callee());
  printer.set_oneshot_opt(options.oneshot_opt());
457 458 459
  if (!options.test_function_name().empty()) {
    printer.set_test_function_name(options.test_function_name());
  }
460

461
  if (options.private_methods()) i::FLAG_harmony_private_methods = true;
462
  if (options.top_level_await()) i::FLAG_harmony_top_level_await = true;
463

464
  *stream << "#\n# Autogenerated by generate-bytecode-expectations.\n#\n\n";
465 466 467 468 469
  options.PrintHeader(stream);
  for (const std::string& snippet : snippet_list) {
    printer.PrintExpectation(stream, snippet);
  }

470
  i::FLAG_harmony_private_methods = false;
471
  i::FLAG_harmony_top_level_await = false;
472 473 474 475 476 477 478 479 480 481 482 483
}

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;
484
    }
485
  }
486 487 488
  std::ostream& output_stream =
      options.write_to_stdout() ? std::cout : output_file_handle;

489
  GenerateExpectationsFile(&output_stream, snippet_list, platform, options);
490 491

  return true;
492 493
}

494 495 496 497 498
std::string WriteExpectationsToString(
    const std::vector<std::string>& snippet_list,
    const V8InitializationScope& platform, const ProgramOptions& options) {
  std::stringstream output_string;

499
  GenerateExpectationsFile(&output_string, snippet_list, platform, options);
500 501 502 503

  return output_string.str();
}

504
void PrintMessage(v8::Local<v8::Message> message, v8::Local<v8::Value>) {
505
  std::cerr << "INFO: "
506
            << *v8::String::Utf8Value(message->GetIsolate(), message->Get())
507
            << '\n';
508 509 510 511
}

void DiscardMessage(v8::Local<v8::Message>, v8::Local<v8::Value>) {}

512
void PrintUsage(const char* exec_path) {
513 514
  std::cerr
      << "\nUsage: " << exec_path
515
      << " [OPTIONS]... [INPUT FILES]...\n\n"
516
         "Options:\n"
517 518 519 520
         "  --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"
521
         "  --rebaseline  Rebaseline input snippet file.\n"
522
         "  --check-baseline   Checks the current baseline is valid.\n"
523
         "  --no-wrap     Do not wrap the snippet in a function.\n"
524 525 526
         "  --disable-oneshot-opt     Disable Oneshot Optimization.\n"
         "  --print-callee     Print bytecode of callee, function should "
         "return arguments.callee.\n"
527
         "  --module      Compile as JavaScript module.\n"
528 529
         "  --test-function-name=foo  "
         "Specify the name of the test function.\n"
530
         "  --top-level   Process top level code, not the top-level function.\n"
531
         "  --private-methods  Enable harmony_private_methods flag.\n"
532
         "  --top-level-await  Enable await at the module level.\n"
533 534 535
         "  --output=file.name\n"
         "      Specify the output file. If not specified, output goes to "
         "stdout.\n"
536
         "  --pool-type=(number|string|mixed)\n"
537
         "      Specify the type of the entries in the constant pool "
538 539
         "(default: mixed).\n"
         "\n"
540 541 542
         "When using --rebaseline or --check-baseline, flags --no-wrap,\n"
         "--test-function-name and --pool-type will be overridden by the\n"
         "options specified in the input file header.\n\n"
543 544 545 546
         "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 "
         "into the test suite.\n";
547 548 549 550
}

}  // namespace

551 552 553 554 555 556 557 558 559 560
bool CheckBaselineExpectations(const std::string& input_filename,
                               const std::vector<std::string>& snippet_list,
                               const V8InitializationScope& platform,
                               const ProgramOptions& options) {
  std::string actual =
      WriteExpectationsToString(snippet_list, platform, options);

  std::ifstream input_stream(input_filename);
  if (!input_stream.is_open()) {
    REPORT_ERROR("Could not open " << input_filename << " for reading.");
561
    std::exit(2);
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
  }

  bool check_failed = false;
  std::string expected((std::istreambuf_iterator<char>(input_stream)),
                       std::istreambuf_iterator<char>());
  if (expected != actual) {
    REPORT_ERROR("Mismatch: " << input_filename);
    check_failed = true;
    if (expected.size() != actual.size()) {
      REPORT_ERROR("  Expected size (" << expected.size()
                                       << ") != actual size (" << actual.size()
                                       << ")");
    }

    int line = 1;
    for (size_t i = 0; i < std::min(expected.size(), actual.size()); ++i) {
      if (expected[i] != actual[i]) {
        // Find the start of the line that has the mismatch carefully
        // handling the case where it's the first line that mismatches.
        size_t start = expected[i] != '\n' ? expected.rfind("\n", i)
                                           : actual.rfind("\n", i);
        if (start == std::string::npos) {
          start = 0;
        } else {
          ++start;
        }

        // If there is no new line, then these two lines will consume the
        // remaining characters in the string, because npos - start will
        // always be longer than the string itself.
        std::string expected_line =
            expected.substr(start, expected.find("\n", i) - start);
        std::string actual_line =
            actual.substr(start, actual.find("\n", i) - start);
        REPORT_ERROR("  First mismatch on line " << line << ")");
        REPORT_ERROR("    Expected : '" << expected_line << "'");
        REPORT_ERROR("    Actual   : '" << actual_line << "'");
        break;
      }
      if (expected[i] == '\n') line++;
    }
  }
  return check_failed;
}

607
int main(int argc, char** argv) {
608
  ProgramOptions options = ProgramOptions::FromCommandLine(argc, argv);
609

610
  if (!options.Validate() || options.print_help()) {
611
    PrintUsage(argv[0]);
612
    return options.print_help() ? 0 : 1;
613 614
  }

615
  V8InitializationScope platform(argv[0]);
616 617
  platform.isolate()->AddMessageListener(
      options.suppress_runtime_errors() ? DiscardMessage : PrintMessage);
618

619
  std::vector<std::string> snippet_list;
620

621 622 623
  if (options.read_from_stdin()) {
    // Rebaseline will never get here, so we will always take the
    // GenerateExpectationsFile at the end of this function.
624
    DCHECK(!options.rebaseline() && !options.check_baseline());
625
    ExtractSnippets(&snippet_list, &std::cin, options.read_raw_js_snippet());
626
  } else {
627
    bool check_failed = false;
628 629 630 631 632 633 634 635 636 637 638 639
    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;
640
      if (options.baseline()) {
641
        updated_options.UpdateFromHeader(&input_stream);
642 643 644
        CHECK(updated_options.Validate());
      }

645
      ExtractSnippets(&snippet_list, &input_stream,
646
                      options.read_raw_js_snippet());
647
      input_stream.close();
648 649 650 651 652 653

      if (options.rebaseline()) {
        if (!WriteExpectationsFile(snippet_list, platform, updated_options,
                                   input_filename)) {
          return 3;
        }
654 655 656 657 658 659
      } else if (options.check_baseline()) {
        check_failed |= CheckBaselineExpectations(input_filename, snippet_list,
                                                  platform, updated_options);
      }

      if (options.baseline()) {
660 661
        snippet_list.clear();
      }
662
    }
663 664 665
    if (check_failed) {
      return 4;
    }
666 667
  }

668
  if (!options.baseline()) {
669 670 671 672 673
    if (!WriteExpectationsFile(snippet_list, platform, options,
                               options.output_filename())) {
      return 3;
    }
  }
674
}