| 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 | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..4fc54e12c91010fae925cd30e6ed9ffe0c95f4a3 | 
| --- /dev/null | 
| +++ b/test/cctest/interpreter/generate-bytecode-expectations.cc | 
| @@ -0,0 +1,294 @@ | 
| +// 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 <fstream> | 
| +#include <iostream> | 
| + | 
| +#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/bytecode-array-iterator.h" | 
| +#include "src/interpreter/bytecode-generator.h" | 
| +#include "src/interpreter/bytecodes.h" | 
| +#include "src/interpreter/interpreter.h" | 
| + | 
| +using namespace i::interpreter; | 
| + | 
| +namespace { | 
| + | 
| +const char* kIndent = "       "; | 
| + | 
| +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); | 
| +}; | 
| + | 
| +i::Isolate* GetInternalIsolate(v8::Isolate* isolate) { | 
| +  return reinterpret_cast<i::Isolate*>(isolate); | 
| +} | 
| + | 
| +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); | 
| +  GetInternalIsolate(isolate_)->interpreter()->Initialize(); | 
| +} | 
| + | 
| +V8InitializationScope::~V8InitializationScope() { | 
| +  isolate_->Dispose(); | 
| +  v8::V8::Dispose(); | 
| +  v8::V8::ShutdownPlatform(); | 
| +} | 
| + | 
| +v8::Local<v8::String> V8StringFromUTF8(v8::Isolate* isolate, const char* data) { | 
| +  return v8::String::NewFromUtf8(isolate, data, v8::NewStringType::kNormal) | 
| +      .ToLocalChecked(); | 
| +} | 
| + | 
| +std::string WrapCodeInFunction(const char* function_name, | 
| +                               const std::string& function_body) { | 
| +  std::ostringstream program_stream; | 
| +  program_stream << "function " << function_name << "() {" << function_body | 
| +                 << "}\n" | 
| +                 << function_name << "();"; | 
| + | 
| +  return program_stream.str(); | 
| +} | 
| + | 
| +v8::Local<v8::Value> CompileAndRun(v8::Isolate* isolate, | 
| +                                   const v8::Local<v8::Context>& context, | 
| +                                   const char* program) { | 
| +  v8::Local<v8::String> source = V8StringFromUTF8(isolate, program); | 
| +  v8::Local<v8::Script> script = | 
| +      v8::Script::Compile(context, source).ToLocalChecked(); | 
| + | 
| +  v8::Local<v8::Value> result; | 
| +  CHECK(script->Run(context).ToLocal(&result)); | 
| + | 
| +  return result; | 
| +} | 
| + | 
| +i::Handle<v8::internal::BytecodeArray> GetBytecodeArrayForGlobal( | 
| +    v8::Isolate* isolate, const v8::Local<v8::Context>& context, | 
| +    const char* global_name) { | 
| +  v8::Local<v8::String> v8_global_name = V8StringFromUTF8(isolate, global_name); | 
| +  v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast( | 
| +      context->Global()->Get(context, v8_global_name).ToLocalChecked()); | 
| +  i::Handle<i::JSFunction> js_function = | 
| +      i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(*function)); | 
| + | 
| +  i::Handle<i::BytecodeArray> bytecodes = i::handle( | 
| +      js_function->shared()->bytecode_array(), GetInternalIsolate(isolate)); | 
| + | 
| +  return bytecodes; | 
| +} | 
| + | 
| +void PrintBytecodeOperand(std::ostream& stream, | 
| +                          const BytecodeArrayIterator& bytecode_iter, | 
| +                          const Bytecode& bytecode, int op_index) { | 
| +  OperandType op_type = Bytecodes::GetOperandType(bytecode, op_index); | 
| +  OperandSize op_size = Bytecodes::GetOperandSize(bytecode, op_index); | 
| + | 
| +  const char* size_tag; | 
| +  switch (op_size) { | 
| +    case OperandSize::kByte: | 
| +      size_tag = "8"; | 
| +      break; | 
| +    case OperandSize::kShort: | 
| +      size_tag = "16"; | 
| +      break; | 
| +    default: | 
| +      UNREACHABLE(); | 
| +      return; | 
| +  } | 
| + | 
| +  if (Bytecodes::IsRegisterOperandType(op_type)) { | 
| +    Register register_value = bytecode_iter.GetRegisterOperand(op_index); | 
| +    stream << 'R'; | 
| +    if (op_size != OperandSize::kByte) stream << size_tag; | 
| +    stream << '(' << register_value.index() << ')'; | 
| +  } else { | 
| +    uint32_t raw_value = bytecode_iter.GetRawOperand(op_index, op_type); | 
| +    stream << 'U' << size_tag << '(' << raw_value << ')'; | 
| +  } | 
| +} | 
| + | 
| +void PrintBytecode(std::ostream& stream, | 
| +                   const BytecodeArrayIterator& bytecode_iter) { | 
| +  Bytecode bytecode = bytecode_iter.current_bytecode(); | 
| + | 
| +  stream << "B(" << Bytecodes::ToString(bytecode) << ')'; | 
| + | 
| +  int operands_count = Bytecodes::NumberOfOperands(bytecode); | 
| +  for (int op_index = 0; op_index < operands_count; ++op_index) { | 
| +    stream << ", "; | 
| +    PrintBytecodeOperand(stream, bytecode_iter, bytecode, op_index); | 
| +  } | 
| +} | 
| + | 
| +std::string QuoteCString(const std::string& source) { | 
| +  std::string quoted_buffer; | 
| +  for (char c : source) { | 
| +    switch (c) { | 
| +      case '"': | 
| +        quoted_buffer += "\\\""; | 
| +        break; | 
| +      case '\n': | 
| +        quoted_buffer += "\\n"; | 
| +        break; | 
| +      case '\t': | 
| +        quoted_buffer += "\\t"; | 
| +        break; | 
| +      case '\\': | 
| +        quoted_buffer += "\\\\"; | 
| +        break; | 
| +      default: | 
| +        quoted_buffer += c; | 
| +        break; | 
| +    } | 
| +  } | 
| +  return quoted_buffer; | 
| +} | 
| + | 
| +void PrintBytecodeArray(std::ostream& stream, | 
| +                        i::Handle<i::BytecodeArray> bytecode_array, | 
| +                        const std::string& body, bool print_banner = true) { | 
| +  const int kPointerSize = sizeof(void*); | 
| + | 
| +  if (print_banner) { | 
| +    stream << kIndent << "// === ExpectedSnippet generated by " | 
| +                         "generate-bytecode-expectations. ===\n"; | 
| +  } | 
| + | 
| +  // Print the code snippet as a quoted C string. | 
| +  stream << kIndent << "{" << '"' << QuoteCString(body) << "\",\n" << kIndent; | 
| + | 
| +  // Print the frame size, in multiples of kPointerSize. | 
| +  int frame_size = bytecode_array->frame_size(); | 
| +  DCHECK(frame_size % kPointerSize == 0); | 
| +  if (frame_size > kPointerSize) { | 
| +    stream << ' ' << frame_size / kPointerSize << " * kPointerSize,\n" | 
| +           << kIndent; | 
| +  } else if (frame_size == kPointerSize) { | 
| +    stream << " kPointerSize,\n" << kIndent; | 
| +  } else if (frame_size == 0) { | 
| +    stream << " 0,\n" << kIndent; | 
| +  } | 
| + | 
| +  // Print parameter count and size of the bytecode array. | 
| +  stream << ' ' << bytecode_array->parameter_count() << ",\n" | 
| +         << kIndent << ' ' << bytecode_array->length() << ",\n" | 
| +         << kIndent << " {\n" | 
| +         << kIndent << "     "; | 
| + | 
| +  // Print bytecodes. | 
| +  BytecodeArrayIterator bytecode_iter{bytecode_array}; | 
| +  for (; !bytecode_iter.done(); bytecode_iter.Advance()) { | 
| +    // Print separator before each instruction, except the first one. | 
| +    if (bytecode_iter.current_offset() > 0) { | 
| +      stream << ",\n" << kIndent << "     "; | 
| +    } | 
| +    PrintBytecode(stream, bytecode_iter); | 
| +  } | 
| + | 
| +  stream << "\n" << kIndent << " },\n"; | 
| +  // TODO(ssanfilippo) add representation of constant pool and handlers. | 
| +  stream << kIndent << " // constant pool and handlers here\n"; | 
| +  stream << kIndent << "}\n"; | 
| +} | 
| + | 
| +bool ReadFromFileOrStdin(std::string* body, const char* body_filename) { | 
| +  std::stringstream body_buffer; | 
| +  if (strcmp(body_filename, "-") == 0) { | 
| +    body_buffer << std::cin.rdbuf(); | 
| +  } else { | 
| +    std::ifstream body_file{body_filename}; | 
| +    if (!body_file) return false; | 
| +    body_buffer << body_file.rdbuf(); | 
| +  } | 
| +  *body = body_buffer.str(); | 
| +  return true; | 
| +} | 
| + | 
| +void PrintUsage(const char* exec_path) { | 
| +  std::cerr << "Usage: " << exec_path | 
| +            << " [filename.js|-]\n\n" | 
| +               "No arguments or - reads from standard input.\n" | 
| +               "Anything else is interpreted as a filename.\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) { | 
| +  if (argc > 1 && strcmp(argv[1], "--help") == 0) { | 
| +    PrintUsage(argv[0]); | 
| +    return 0; | 
| +  } | 
| + | 
| +  const char* body_filename = (argc > 1 ? argv[1] : "-"); | 
| +  const char* wrapper_function_name = "__genbckexp_wrapper__"; | 
| + | 
| +  std::string body; | 
| +  if (!ReadFromFileOrStdin(&body, body_filename)) { | 
| +    std::cerr << "Could not open '" << body_filename << "'.\n\n"; | 
| +    PrintUsage(argv[0]); | 
| +    return 1; | 
| +  } | 
| + | 
| +  V8InitializationScope platform(argv[0]); | 
| +  { | 
| +    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); | 
| + | 
| +    std::string source_code = WrapCodeInFunction(wrapper_function_name, body); | 
| +    CompileAndRun(platform.isolate(), context, source_code.c_str()); | 
| + | 
| +    i::Handle<i::BytecodeArray> bytecode_array = GetBytecodeArrayForGlobal( | 
| +        platform.isolate(), context, wrapper_function_name); | 
| + | 
| +    PrintBytecodeArray(std::cout, bytecode_array, body); | 
| +  } | 
| +} | 
|  |