blob: 8bf9ba6dfb9ac28e459e71bf1f7480c1962bd074 [file] [log] [blame]
// 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.
#include <cstring>
#include <fstream>
#include "test/cctest/interpreter/bytecode-expectations-printer.h"
#include "include/libplatform/libplatform.h"
#include "include/v8.h"
#include "src/base/logging.h"
#include "src/base/smart-pointers.h"
#include "src/compiler.h"
#include "src/interpreter/interpreter.h"
using v8::internal::interpreter::BytecodeExpectationsPrinter;
namespace {
class ProgramOptions {
public:
static ProgramOptions FromCommandLine(int argc, char** argv);
ProgramOptions()
: parsing_failed_(false),
print_help_(false),
read_raw_js_snippet_(false),
read_from_stdin_(false),
const_pool_type_(
BytecodeExpectationsPrinter::ConstantPoolType::kMixed) {}
bool Validate() const;
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_; }
BytecodeExpectationsPrinter::ConstantPoolType const_pool_type() const {
return const_pool_type_;
}
private:
bool parsing_failed_;
bool print_help_;
bool read_raw_js_snippet_;
bool read_from_stdin_;
BytecodeExpectationsPrinter::ConstantPoolType const_pool_type_;
std::string filename_;
};
class ArrayBufferAllocator final : public v8::ArrayBuffer::Allocator {
public:
void* Allocate(size_t length) override {
void* data = AllocateUninitialized(length);
if (data != nullptr) memset(data, 0, length);
return data;
}
void* AllocateUninitialized(size_t length) override { return malloc(length); }
void Free(void* data, size_t) override { free(data); }
};
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:
v8::base::SmartPointer<v8::Platform> platform_;
v8::Isolate* isolate_;
DISALLOW_COPY_AND_ASSIGN(V8InitializationScope);
};
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;
} else if (strcmp(type_string, "string") == 0) {
return BytecodeExpectationsPrinter::ConstantPoolType::kString;
} else if (strcmp(type_string, "mixed") == 0) {
return BytecodeExpectationsPrinter::ConstantPoolType::kMixed;
}
return BytecodeExpectationsPrinter::ConstantPoolType::kUnknown;
}
// 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;
} else if (strcmp(argv[i], "--raw-js") == 0) {
options.read_raw_js_snippet_ = true;
} else if (strncmp(argv[i], "--pool-type=", 12) == 0) {
options.const_pool_type_ = ParseConstantPoolType(argv[i] + 12);
} else if (strcmp(argv[i], "--stdin") == 0) {
options.read_from_stdin_ = true;
} else if (strncmp(argv[i], "--", 2) != 0) { // It doesn't start with --
if (!options.filename_.empty()) {
std::cerr << "ERROR: More than one input file specified\n";
options.parsing_failed_ = true;
break;
}
options.filename_ = argv[i];
} else {
std::cerr << "ERROR: Unknonwn option " << argv[i] << "\n";
options.parsing_failed_ = true;
break;
}
}
return options;
}
bool ProgramOptions::Validate() const {
if (parsing_failed_) return false;
if (print_help_) return true;
if (const_pool_type_ ==
BytecodeExpectationsPrinter::ConstantPoolType::kUnknown) {
std::cerr << "ERROR: Unknown constant pool type.\n";
return false;
}
if (!read_from_stdin_ && filename_.empty()) {
std::cerr << "ERROR: No input file specified.\n";
return false;
}
if (read_from_stdin_ && !filename_.empty()) {
std::cerr << "ERROR: Reading from stdin, but input files supplied.\n";
return false;
}
return true;
}
V8InitializationScope::V8InitializationScope(const char* exec_path)
: platform_(v8::platform::CreateDefaultPlatform()) {
i::FLAG_ignition = true;
i::FLAG_always_opt = false;
i::FLAG_allow_natives_syntax = true;
v8::V8::InitializeICU();
v8::V8::InitializeExternalStartupData(exec_path);
v8::V8::InitializePlatform(platform_.get());
v8::V8::Initialize();
ArrayBufferAllocator allocator;
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = &allocator;
isolate_ = v8::Isolate::New(create_params);
}
V8InitializationScope::~V8InitializationScope() {
isolate_->Dispose();
v8::V8::Dispose();
v8::V8::ShutdownPlatform();
}
std::string ReadRawJSSnippet(std::istream& stream) { // NOLINT
std::stringstream body_buffer;
CHECK(body_buffer << stream.rdbuf());
return body_buffer.str();
}
bool ReadNextSnippet(std::istream& stream, std::string* string_out) { // NOLINT
std::string line;
bool found_begin_snippet = false;
string_out->clear();
while (std::getline(stream, line)) {
if (line == "snippet: \"") {
found_begin_snippet = true;
continue;
}
if (!found_begin_snippet) continue;
if (line == "\"") return true;
CHECK_GE(line.size(), 2u); // We should have the indent
string_out->append(line.begin() + 2, line.end());
*string_out += '\n';
}
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);
}
}
}
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());
} 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;
}
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) {
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);
stream << "#\n# Autogenerated by generate-bytecode-expectations\n#\n\n";
BytecodeExpectationsPrinter printer(platform.isolate(), const_pool_type);
for (const std::string& snippet : snippet_list) {
printer.PrintExpectation(stream, snippet);
}
}
}
void PrintUsage(const char* exec_path) {
std::cerr
<< "\nUsage: " << exec_path
<< " [OPTIONS]... [INPUT FILE]\n\n"
"Options:\n"
" --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"
" --pool-type=(int|double|string|mixed)\n"
" specify the type of the entries in the constant pool "
"(default: mixed).\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 "
"into the test suite.\n";
}
} // namespace
int main(int argc, char** argv) {
ProgramOptions options = ProgramOptions::FromCommandLine(argc, argv);
if (!options.Validate() || options.print_help()) {
PrintUsage(argv[0]);
return options.print_help() ? 0 : 1;
}
std::vector<std::string> snippet_list;
if (!ExtractSnippets(&snippet_list, options)) {
return 2;
}
GenerateExpectationsFile(std::cout, snippet_list, options.const_pool_type(),
argv[0]);
}