blob: 92ba9ba0bd49d1caa318243762adbaaa7a1569b1 [file] [log] [blame]
Ben Murdoch097c5b22016-05-18 11:27:45 +01001// Copyright 2016 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <cstring>
6#include <fstream>
Ben Murdochda12d292016-06-02 14:46:10 +01007#include <vector>
Ben Murdoch097c5b22016-05-18 11:27:45 +01008
9#include "test/cctest/interpreter/bytecode-expectations-printer.h"
10
11#include "include/libplatform/libplatform.h"
12#include "include/v8.h"
13
14#include "src/base/logging.h"
15#include "src/base/smart-pointers.h"
16#include "src/compiler.h"
17#include "src/interpreter/interpreter.h"
18
Ben Murdochda12d292016-06-02 14:46:10 +010019#ifdef V8_OS_POSIX
20#include <dirent.h>
21#endif
22
Ben Murdoch097c5b22016-05-18 11:27:45 +010023using v8::internal::interpreter::BytecodeExpectationsPrinter;
24
Ben Murdochda12d292016-06-02 14:46:10 +010025#define REPORT_ERROR(MESSAGE) (((std::cerr << "ERROR: ") << MESSAGE) << '\n')
26
Ben Murdoch097c5b22016-05-18 11:27:45 +010027namespace {
28
Ben Murdochda12d292016-06-02 14:46:10 +010029#ifdef V8_OS_POSIX
30const char* kGoldenFilesPath = "test/cctest/interpreter/bytecode_expectations/";
31#endif
32
Ben Murdoch097c5b22016-05-18 11:27:45 +010033class ProgramOptions final {
34 public:
35 static ProgramOptions FromCommandLine(int argc, char** argv);
36
37 ProgramOptions()
38 : parsing_failed_(false),
39 print_help_(false),
40 read_raw_js_snippet_(false),
41 read_from_stdin_(false),
42 rebaseline_(false),
43 wrap_(true),
44 execute_(true),
45 top_level_(false),
Ben Murdoch097c5b22016-05-18 11:27:45 +010046 do_expressions_(false),
Ben Murdochda12d292016-06-02 14:46:10 +010047 verbose_(false),
Ben Murdoch097c5b22016-05-18 11:27:45 +010048 const_pool_type_(
49 BytecodeExpectationsPrinter::ConstantPoolType::kMixed) {}
50
51 bool Validate() const;
52 void UpdateFromHeader(std::istream& stream); // NOLINT
53 void PrintHeader(std::ostream& stream) const; // NOLINT
54
55 bool parsing_failed() const { return parsing_failed_; }
56 bool print_help() const { return print_help_; }
57 bool read_raw_js_snippet() const { return read_raw_js_snippet_; }
58 bool read_from_stdin() const { return read_from_stdin_; }
59 bool write_to_stdout() const {
60 return output_filename_.empty() && !rebaseline_;
61 }
62 bool rebaseline() const { return rebaseline_; }
63 bool wrap() const { return wrap_; }
64 bool execute() const { return execute_; }
65 bool top_level() const { return top_level_; }
Ben Murdoch097c5b22016-05-18 11:27:45 +010066 bool do_expressions() const { return do_expressions_; }
Ben Murdochda12d292016-06-02 14:46:10 +010067 bool verbose() const { return verbose_; }
68 bool suppress_runtime_errors() const { return rebaseline_ && !verbose_; }
Ben Murdoch097c5b22016-05-18 11:27:45 +010069 BytecodeExpectationsPrinter::ConstantPoolType const_pool_type() const {
70 return const_pool_type_;
71 }
Ben Murdochda12d292016-06-02 14:46:10 +010072 std::vector<std::string> input_filenames() const { return input_filenames_; }
Ben Murdoch097c5b22016-05-18 11:27:45 +010073 std::string output_filename() const { return output_filename_; }
74 std::string test_function_name() const { return test_function_name_; }
75
76 private:
77 bool parsing_failed_;
78 bool print_help_;
79 bool read_raw_js_snippet_;
80 bool read_from_stdin_;
81 bool rebaseline_;
82 bool wrap_;
83 bool execute_;
84 bool top_level_;
Ben Murdoch097c5b22016-05-18 11:27:45 +010085 bool do_expressions_;
Ben Murdochda12d292016-06-02 14:46:10 +010086 bool verbose_;
Ben Murdoch097c5b22016-05-18 11:27:45 +010087 BytecodeExpectationsPrinter::ConstantPoolType const_pool_type_;
Ben Murdochda12d292016-06-02 14:46:10 +010088 std::vector<std::string> input_filenames_;
Ben Murdoch097c5b22016-05-18 11:27:45 +010089 std::string output_filename_;
90 std::string test_function_name_;
91};
92
93class ArrayBufferAllocator final : public v8::ArrayBuffer::Allocator {
94 public:
95 void* Allocate(size_t length) override {
96 void* data = AllocateUninitialized(length);
97 if (data != nullptr) memset(data, 0, length);
98 return data;
99 }
100 void* AllocateUninitialized(size_t length) override { return malloc(length); }
101 void Free(void* data, size_t) override { free(data); }
102};
103
104class V8InitializationScope final {
105 public:
106 explicit V8InitializationScope(const char* exec_path);
107 ~V8InitializationScope();
108
109 v8::Platform* platform() const { return platform_.get(); }
110 v8::Isolate* isolate() const { return isolate_; }
111
112 private:
113 v8::base::SmartPointer<v8::Platform> platform_;
114 v8::Isolate* isolate_;
115
116 DISALLOW_COPY_AND_ASSIGN(V8InitializationScope);
117};
118
119BytecodeExpectationsPrinter::ConstantPoolType ParseConstantPoolType(
120 const char* type_string) {
121 if (strcmp(type_string, "number") == 0) {
122 return BytecodeExpectationsPrinter::ConstantPoolType::kNumber;
123 } else if (strcmp(type_string, "string") == 0) {
124 return BytecodeExpectationsPrinter::ConstantPoolType::kString;
125 } else if (strcmp(type_string, "mixed") == 0) {
126 return BytecodeExpectationsPrinter::ConstantPoolType::kMixed;
127 }
128 return BytecodeExpectationsPrinter::ConstantPoolType::kUnknown;
129}
130
131const char* ConstantPoolTypeToString(
132 BytecodeExpectationsPrinter::ConstantPoolType type) {
133 switch (type) {
134 case BytecodeExpectationsPrinter::ConstantPoolType::kNumber:
135 return "number";
136 case BytecodeExpectationsPrinter::ConstantPoolType::kMixed:
137 return "mixed";
138 case BytecodeExpectationsPrinter::ConstantPoolType::kString:
139 return "string";
140 default:
141 UNREACHABLE();
142 return nullptr;
143 }
144}
145
146bool ParseBoolean(const char* string) {
147 if (strcmp(string, "yes") == 0) {
148 return true;
149 } else if (strcmp(string, "no") == 0) {
150 return false;
151 } else {
152 UNREACHABLE();
153 return false;
154 }
155}
156
157const char* BooleanToString(bool value) { return value ? "yes" : "no"; }
158
Ben Murdochda12d292016-06-02 14:46:10 +0100159#ifdef V8_OS_POSIX
160
161bool StrEndsWith(const char* string, const char* suffix) {
162 int string_size = i::StrLength(string);
163 int suffix_size = i::StrLength(suffix);
164 if (string_size < suffix_size) return false;
165
166 return strcmp(string + (string_size - suffix_size), suffix) == 0;
167}
168
169bool CollectGoldenFiles(std::vector<std::string>* golden_file_list,
170 const char* directory_path) {
171 DIR* directory = opendir(directory_path);
172 if (!directory) return false;
173
174 dirent entry_buffer;
175 dirent* entry;
176
177 while (readdir_r(directory, &entry_buffer, &entry) == 0 && entry) {
178 if (StrEndsWith(entry->d_name, ".golden")) {
179 std::string golden_filename(kGoldenFilesPath);
180 golden_filename += entry->d_name;
181 golden_file_list->push_back(golden_filename);
182 }
183 }
184
185 closedir(directory);
186
187 return true;
188}
189
190#endif // V8_OS_POSIX
191
Ben Murdoch097c5b22016-05-18 11:27:45 +0100192// static
193ProgramOptions ProgramOptions::FromCommandLine(int argc, char** argv) {
194 ProgramOptions options;
195
196 for (int i = 1; i < argc; ++i) {
197 if (strcmp(argv[i], "--help") == 0) {
198 options.print_help_ = true;
199 } else if (strcmp(argv[i], "--raw-js") == 0) {
200 options.read_raw_js_snippet_ = true;
201 } else if (strncmp(argv[i], "--pool-type=", 12) == 0) {
202 options.const_pool_type_ = ParseConstantPoolType(argv[i] + 12);
203 } else if (strcmp(argv[i], "--stdin") == 0) {
204 options.read_from_stdin_ = true;
205 } else if (strcmp(argv[i], "--rebaseline") == 0) {
206 options.rebaseline_ = true;
207 } else if (strcmp(argv[i], "--no-wrap") == 0) {
208 options.wrap_ = false;
209 } else if (strcmp(argv[i], "--no-execute") == 0) {
210 options.execute_ = false;
211 } else if (strcmp(argv[i], "--top-level") == 0) {
212 options.top_level_ = true;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100213 } else if (strcmp(argv[i], "--do-expressions") == 0) {
214 options.do_expressions_ = true;
Ben Murdochda12d292016-06-02 14:46:10 +0100215 } else if (strcmp(argv[i], "--verbose") == 0) {
216 options.verbose_ = true;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100217 } else if (strncmp(argv[i], "--output=", 9) == 0) {
218 options.output_filename_ = argv[i] + 9;
219 } else if (strncmp(argv[i], "--test-function-name=", 21) == 0) {
220 options.test_function_name_ = argv[i] + 21;
221 } else if (strncmp(argv[i], "--", 2) != 0) { // It doesn't start with --
Ben Murdochda12d292016-06-02 14:46:10 +0100222 options.input_filenames_.push_back(argv[i]);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100223 } else {
Ben Murdochda12d292016-06-02 14:46:10 +0100224 REPORT_ERROR("Unknown option " << argv[i]);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100225 options.parsing_failed_ = true;
226 break;
227 }
228 }
229
Ben Murdochda12d292016-06-02 14:46:10 +0100230 if (options.rebaseline_ && options.input_filenames_.empty()) {
231#ifdef V8_OS_POSIX
232 if (options.verbose_) {
233 std::cout << "Looking for golden files in " << kGoldenFilesPath << '\n';
234 }
235 if (!CollectGoldenFiles(&options.input_filenames_, kGoldenFilesPath)) {
236 REPORT_ERROR("Golden files autodiscovery failed.");
237 options.parsing_failed_ = true;
238 }
239#else
240 REPORT_ERROR("Golden files autodiscovery requires a POSIX OS, sorry.");
241 options.parsing_failed_ = true;
242#endif
243 }
244
Ben Murdoch097c5b22016-05-18 11:27:45 +0100245 return options;
246}
247
248bool ProgramOptions::Validate() const {
249 if (parsing_failed_) return false;
250 if (print_help_) return true;
251
252 if (const_pool_type_ ==
253 BytecodeExpectationsPrinter::ConstantPoolType::kUnknown) {
Ben Murdochda12d292016-06-02 14:46:10 +0100254 REPORT_ERROR("Unknown constant pool type.");
Ben Murdoch097c5b22016-05-18 11:27:45 +0100255 return false;
256 }
257
Ben Murdochda12d292016-06-02 14:46:10 +0100258 if (!read_from_stdin_ && input_filenames_.empty()) {
259 REPORT_ERROR("No input file specified.");
Ben Murdoch097c5b22016-05-18 11:27:45 +0100260 return false;
261 }
262
Ben Murdochda12d292016-06-02 14:46:10 +0100263 if (read_from_stdin_ && !input_filenames_.empty()) {
264 REPORT_ERROR("Reading from stdin, but input files supplied.");
Ben Murdoch097c5b22016-05-18 11:27:45 +0100265 return false;
266 }
267
268 if (rebaseline_ && read_raw_js_snippet_) {
Ben Murdochda12d292016-06-02 14:46:10 +0100269 REPORT_ERROR("Cannot use --rebaseline on a raw JS snippet.");
270 return false;
271 }
272
273 if (rebaseline_ && !output_filename_.empty()) {
274 REPORT_ERROR("Output file cannot be specified together with --rebaseline.");
275 return false;
276 }
277
278 if (rebaseline_ && read_from_stdin_) {
279 REPORT_ERROR("Cannot --rebaseline when input is --stdin.");
280 return false;
281 }
282
283 if (input_filenames_.size() > 1 && !rebaseline_ && !read_raw_js_snippet()) {
284 REPORT_ERROR(
285 "Multiple input files, but no --rebaseline or --raw-js specified.");
Ben Murdoch097c5b22016-05-18 11:27:45 +0100286 return false;
287 }
288
289 if (top_level_ && !test_function_name_.empty()) {
Ben Murdochda12d292016-06-02 14:46:10 +0100290 REPORT_ERROR(
291 "Test function name specified while processing top level code.");
Ben Murdoch097c5b22016-05-18 11:27:45 +0100292 return false;
293 }
294
295 return true;
296}
297
298void ProgramOptions::UpdateFromHeader(std::istream& stream) {
299 std::string line;
300
301 // Skip to the beginning of the options header
302 while (std::getline(stream, line)) {
303 if (line == "---") break;
304 }
305
306 while (std::getline(stream, line)) {
307 if (line.compare(0, 11, "pool type: ") == 0) {
308 const_pool_type_ = ParseConstantPoolType(line.c_str() + 11);
309 } else if (line.compare(0, 9, "execute: ") == 0) {
310 execute_ = ParseBoolean(line.c_str() + 9);
311 } else if (line.compare(0, 6, "wrap: ") == 0) {
312 wrap_ = ParseBoolean(line.c_str() + 6);
313 } else if (line.compare(0, 20, "test function name: ") == 0) {
314 test_function_name_ = line.c_str() + 20;
315 } else if (line.compare(0, 11, "top level: ") == 0) {
316 top_level_ = ParseBoolean(line.c_str() + 11);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100317 } else if (line.compare(0, 16, "do expressions: ") == 0) {
318 do_expressions_ = ParseBoolean(line.c_str() + 16);
319 } else if (line == "---") {
320 break;
321 } else if (line.empty()) {
322 continue;
323 } else {
324 UNREACHABLE();
325 return;
326 }
327 }
328}
329
330void ProgramOptions::PrintHeader(std::ostream& stream) const { // NOLINT
331 stream << "---"
332 "\npool type: "
333 << ConstantPoolTypeToString(const_pool_type_)
334 << "\nexecute: " << BooleanToString(execute_)
335 << "\nwrap: " << BooleanToString(wrap_);
336
337 if (!test_function_name_.empty()) {
338 stream << "\ntest function name: " << test_function_name_;
339 }
340
341 if (top_level_) stream << "\ntop level: yes";
Ben Murdoch097c5b22016-05-18 11:27:45 +0100342 if (do_expressions_) stream << "\ndo expressions: yes";
343
344 stream << "\n\n";
345}
346
347V8InitializationScope::V8InitializationScope(const char* exec_path)
348 : platform_(v8::platform::CreateDefaultPlatform()) {
349 i::FLAG_ignition = true;
350 i::FLAG_always_opt = false;
351 i::FLAG_allow_natives_syntax = true;
352
353 v8::V8::InitializeICU();
354 v8::V8::InitializeExternalStartupData(exec_path);
355 v8::V8::InitializePlatform(platform_.get());
356 v8::V8::Initialize();
357
358 ArrayBufferAllocator allocator;
359 v8::Isolate::CreateParams create_params;
360 create_params.array_buffer_allocator = &allocator;
361
362 isolate_ = v8::Isolate::New(create_params);
363}
364
365V8InitializationScope::~V8InitializationScope() {
366 isolate_->Dispose();
367 v8::V8::Dispose();
368 v8::V8::ShutdownPlatform();
369}
370
371std::string ReadRawJSSnippet(std::istream& stream) { // NOLINT
372 std::stringstream body_buffer;
373 CHECK(body_buffer << stream.rdbuf());
374 return body_buffer.str();
375}
376
377bool ReadNextSnippet(std::istream& stream, std::string* string_out) { // NOLINT
378 std::string line;
379 bool found_begin_snippet = false;
380 string_out->clear();
381 while (std::getline(stream, line)) {
382 if (line == "snippet: \"") {
383 found_begin_snippet = true;
384 continue;
385 }
386 if (!found_begin_snippet) continue;
387 if (line == "\"") return true;
388 CHECK_GE(line.size(), 2u); // We should have the indent
389 string_out->append(line.begin() + 2, line.end());
390 *string_out += '\n';
391 }
392 return false;
393}
394
395std::string UnescapeString(const std::string& escaped_string) {
396 std::string unescaped_string;
397 bool previous_was_backslash = false;
398 for (char c : escaped_string) {
399 if (previous_was_backslash) {
400 // If it was not an escape sequence, emit the previous backslash
401 if (c != '\\' && c != '"') unescaped_string += '\\';
402 unescaped_string += c;
403 previous_was_backslash = false;
404 } else {
405 if (c == '\\') {
406 previous_was_backslash = true;
407 // Defer emission to the point where we can check if it was an escape.
408 } else {
409 unescaped_string += c;
410 }
411 }
412 }
413 return unescaped_string;
414}
415
416void ExtractSnippets(std::vector<std::string>* snippet_list,
417 std::istream& body_stream, // NOLINT
418 bool read_raw_js_snippet) {
419 if (read_raw_js_snippet) {
420 snippet_list->push_back(ReadRawJSSnippet(body_stream));
421 } else {
422 std::string snippet;
423 while (ReadNextSnippet(body_stream, &snippet)) {
424 snippet_list->push_back(UnescapeString(snippet));
425 }
426 }
427}
428
429void GenerateExpectationsFile(std::ostream& stream, // NOLINT
430 const std::vector<std::string>& snippet_list,
Ben Murdochda12d292016-06-02 14:46:10 +0100431 const V8InitializationScope& platform,
432 const ProgramOptions& options) {
433 v8::Isolate::Scope isolate_scope(platform.isolate());
434 v8::HandleScope handle_scope(platform.isolate());
435 v8::Local<v8::Context> context = v8::Context::New(platform.isolate());
436 v8::Context::Scope context_scope(context);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100437
Ben Murdochda12d292016-06-02 14:46:10 +0100438 BytecodeExpectationsPrinter printer(platform.isolate(),
439 options.const_pool_type());
440 printer.set_wrap(options.wrap());
441 printer.set_execute(options.execute());
442 printer.set_top_level(options.top_level());
443 if (!options.test_function_name().empty()) {
444 printer.set_test_function_name(options.test_function_name());
445 }
Ben Murdoch097c5b22016-05-18 11:27:45 +0100446
Ben Murdochda12d292016-06-02 14:46:10 +0100447 if (options.do_expressions()) i::FLAG_harmony_do_expressions = true;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100448
Ben Murdochda12d292016-06-02 14:46:10 +0100449 stream << "#\n# Autogenerated by generate-bytecode-expectations.\n#\n\n";
450 options.PrintHeader(stream);
451 for (const std::string& snippet : snippet_list) {
452 printer.PrintExpectation(stream, snippet);
453 }
454
455 i::FLAG_harmony_do_expressions = false;
456}
457
458bool WriteExpectationsFile(const std::vector<std::string>& snippet_list,
459 const V8InitializationScope& platform,
460 const ProgramOptions& options,
461 const std::string& output_filename) {
462 std::ofstream output_file_handle;
463 if (!options.write_to_stdout()) {
464 output_file_handle.open(output_filename.c_str());
465 if (!output_file_handle.is_open()) {
466 REPORT_ERROR("Could not open " << output_filename << " for writing.");
467 return false;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100468 }
469 }
Ben Murdochda12d292016-06-02 14:46:10 +0100470 std::ostream& output_stream =
471 options.write_to_stdout() ? std::cout : output_file_handle;
472
473 GenerateExpectationsFile(output_stream, snippet_list, platform, options);
474
475 return true;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100476}
477
Ben Murdochda12d292016-06-02 14:46:10 +0100478void PrintMessage(v8::Local<v8::Message> message, v8::Local<v8::Value>) {
479 std::cerr << "INFO: " << *v8::String::Utf8Value(message->Get()) << '\n';
480}
481
482void DiscardMessage(v8::Local<v8::Message>, v8::Local<v8::Value>) {}
483
Ben Murdoch097c5b22016-05-18 11:27:45 +0100484void PrintUsage(const char* exec_path) {
485 std::cerr
486 << "\nUsage: " << exec_path
Ben Murdochda12d292016-06-02 14:46:10 +0100487 << " [OPTIONS]... [INPUT FILES]...\n\n"
Ben Murdoch097c5b22016-05-18 11:27:45 +0100488 "Options:\n"
489 " --help Print this help message.\n"
Ben Murdochda12d292016-06-02 14:46:10 +0100490 " --verbose Emit messages about the progress of the tool.\n"
Ben Murdoch097c5b22016-05-18 11:27:45 +0100491 " --raw-js Read raw JavaScript, instead of the output format.\n"
492 " --stdin Read from standard input instead of file.\n"
493 " --rebaseline Rebaseline input snippet file.\n"
494 " --no-wrap Do not wrap the snippet in a function.\n"
495 " --no-execute Do not execute after compilation.\n"
496 " --test-function-name=foo "
497 "Specify the name of the test function.\n"
Ben Murdochda12d292016-06-02 14:46:10 +0100498 " --top-level Process top level code, not the top-level function.\n"
Ben Murdoch097c5b22016-05-18 11:27:45 +0100499 " --do-expressions Enable harmony_do_expressions flag.\n"
500 " --output=file.name\n"
501 " Specify the output file. If not specified, output goes to "
502 "stdout.\n"
503 " --pool-type=(number|string|mixed)\n"
504 " Specify the type of the entries in the constant pool "
505 "(default: mixed).\n"
506 "\n"
507 "When using --rebaseline, flags --no-wrap, --no-execute, "
508 "--test-function-name\nand --pool-type will be overridden by the "
509 "options specified in the input file\nheader.\n\n"
510 "Each raw JavaScript file is interpreted as a single snippet.\n\n"
511 "This tool is intended as a help in writing tests.\n"
512 "Please, DO NOT blindly copy and paste the output "
513 "into the test suite.\n";
514}
515
516} // namespace
517
518int main(int argc, char** argv) {
519 ProgramOptions options = ProgramOptions::FromCommandLine(argc, argv);
520
521 if (!options.Validate() || options.print_help()) {
522 PrintUsage(argv[0]);
523 return options.print_help() ? 0 : 1;
524 }
525
Ben Murdochda12d292016-06-02 14:46:10 +0100526 V8InitializationScope platform(argv[0]);
527 platform.isolate()->AddMessageListener(
528 options.suppress_runtime_errors() ? DiscardMessage : PrintMessage);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100529
530 std::vector<std::string> snippet_list;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100531
Ben Murdochda12d292016-06-02 14:46:10 +0100532 if (options.read_from_stdin()) {
533 // Rebaseline will never get here, so we will always take the
534 // GenerateExpectationsFile at the end of this function.
535 DCHECK(!options.rebaseline());
536 ExtractSnippets(&snippet_list, std::cin, options.read_raw_js_snippet());
537 } else {
538 for (const std::string& input_filename : options.input_filenames()) {
539 if (options.verbose()) {
540 std::cerr << "Processing " << input_filename << '\n';
541 }
542
543 std::ifstream input_stream(input_filename.c_str());
544 if (!input_stream.is_open()) {
545 REPORT_ERROR("Could not open " << input_filename << " for reading.");
546 return 2;
547 }
548
549 ProgramOptions updated_options = options;
550 if (options.rebaseline()) {
551 updated_options.UpdateFromHeader(input_stream);
552 CHECK(updated_options.Validate());
553 }
554
555 ExtractSnippets(&snippet_list, input_stream,
556 options.read_raw_js_snippet());
557
558 if (options.rebaseline()) {
559 if (!WriteExpectationsFile(snippet_list, platform, updated_options,
560 input_filename)) {
561 return 3;
562 }
563 snippet_list.clear();
564 }
565 }
566 }
567
568 if (!options.rebaseline()) {
569 if (!WriteExpectationsFile(snippet_list, platform, options,
570 options.output_filename())) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100571 return 3;
572 }
573 }
Ben Murdoch097c5b22016-05-18 11:27:45 +0100574}