blob: 370c5d813120790ce573f892c76a22620a05529a [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 "test/cctest/interpreter/bytecode-expectations-printer.h"
#include <iomanip>
#include <iostream>
#include <vector>
#include "include/libplatform/libplatform.h"
#include "include/v8.h"
#include "src/api/api-inl.h"
#include "src/base/logging.h"
#include "src/codegen/source-position-table.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/bytecode-generator.h"
#include "src/interpreter/bytecodes.h"
#include "src/interpreter/interpreter-intrinsics.h"
#include "src/interpreter/interpreter.h"
#include "src/objects/heap-number-inl.h"
#include "src/objects/module-inl.h"
#include "src/objects/objects-inl.h"
#include "src/runtime/runtime.h"
#include "src/utils/ostreams.h"
#include "test/cctest/cctest.h"
namespace v8 {
namespace internal {
namespace interpreter {
static const char* NameForNativeContextIntrinsicIndex(uint32_t idx) {
switch (idx) {
#define COMPARE_NATIVE_CONTEXT_INTRINSIC_IDX(NAME, Type, name) \
case Context::NAME: \
return #name;
NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(COMPARE_NATIVE_CONTEXT_INTRINSIC_IDX)
default:
break;
}
return "UnknownIntrinsicIndex";
}
// static
const char* const BytecodeExpectationsPrinter::kDefaultTopFunctionName =
"__genbckexp_wrapper__";
const char* const BytecodeExpectationsPrinter::kIndent = " ";
v8::Local<v8::String> BytecodeExpectationsPrinter::V8StringFromUTF8(
const char* data) const {
return v8::String::NewFromUtf8(isolate_, data, v8::NewStringType::kNormal)
.ToLocalChecked();
}
std::string BytecodeExpectationsPrinter::WrapCodeInFunction(
const char* function_name, const std::string& function_body) const {
std::ostringstream program_stream;
program_stream << "function " << function_name << "() {" << function_body
<< "}\n"
<< function_name << "();";
return program_stream.str();
}
v8::Local<v8::Script> BytecodeExpectationsPrinter::CompileScript(
const char* program) const {
v8::Local<v8::String> source = V8StringFromUTF8(program);
return v8::Script::Compile(isolate_->GetCurrentContext(), source)
.ToLocalChecked();
}
v8::Local<v8::Module> BytecodeExpectationsPrinter::CompileModule(
const char* program) const {
ScriptOrigin origin(
Local<v8::Value>(), Local<v8::Integer>(), Local<v8::Integer>(),
Local<v8::Boolean>(), Local<v8::Integer>(), Local<v8::Value>(),
Local<v8::Boolean>(), Local<v8::Boolean>(), True(isolate_));
v8::ScriptCompiler::Source source(V8StringFromUTF8(program), origin);
return v8::ScriptCompiler::CompileModule(isolate_, &source).ToLocalChecked();
}
void BytecodeExpectationsPrinter::Run(v8::Local<v8::Script> script) const {
MaybeLocal<Value> result = script->Run(isolate_->GetCurrentContext());
USE(result);
}
i::Handle<v8::internal::BytecodeArray>
BytecodeExpectationsPrinter::GetBytecodeArrayForGlobal(
const char* global_name) const {
const v8::Local<v8::Context>& context = isolate_->GetCurrentContext();
v8::Local<v8::String> v8_global_name = V8StringFromUTF8(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().GetBytecodeArray(), i_isolate());
return bytecodes;
}
i::Handle<i::BytecodeArray>
BytecodeExpectationsPrinter::GetBytecodeArrayForModule(
v8::Local<v8::Module> module) const {
i::Handle<i::Module> i_module = v8::Utils::OpenHandle(*module);
return i::handle(SharedFunctionInfo::cast(
Handle<i::SourceTextModule>::cast(i_module)->code())
.GetBytecodeArray(),
i_isolate());
}
i::Handle<i::BytecodeArray>
BytecodeExpectationsPrinter::GetBytecodeArrayForScript(
v8::Local<v8::Script> script) const {
i::Handle<i::JSFunction> js_function = v8::Utils::OpenHandle(*script);
return i::handle(js_function->shared().GetBytecodeArray(), i_isolate());
}
i::Handle<i::BytecodeArray>
BytecodeExpectationsPrinter::GetBytecodeArrayOfCallee(
const char* source_code) const {
i::Handle<i::Object> i_object =
v8::Utils::OpenHandle(*CompileRun(source_code));
i::Handle<i::JSFunction> js_function =
i::Handle<i::JSFunction>::cast(i_object);
CHECK(js_function->shared().HasBytecodeArray());
return i::handle(js_function->shared().GetBytecodeArray(), i_isolate());
}
void BytecodeExpectationsPrinter::PrintEscapedString(
std::ostream& stream, const std::string& string) const {
for (char c : string) {
switch (c) {
case '"':
stream << "\\\"";
break;
case '\\':
stream << "\\\\";
break;
default:
stream << c;
break;
}
}
}
void BytecodeExpectationsPrinter::PrintBytecodeOperand(
std::ostream& stream, const BytecodeArrayIterator& bytecode_iterator,
const Bytecode& bytecode, int op_index, int parameter_count) const {
OperandType op_type = Bytecodes::GetOperandType(bytecode, op_index);
OperandSize op_size = Bytecodes::GetOperandSize(
bytecode, op_index, bytecode_iterator.current_operand_scale());
const char* size_tag;
switch (op_size) {
case OperandSize::kByte:
size_tag = "8";
break;
case OperandSize::kShort:
size_tag = "16";
break;
case OperandSize::kQuad:
size_tag = "32";
break;
default:
UNREACHABLE();
}
if (Bytecodes::IsRegisterOperandType(op_type)) {
Register register_value = bytecode_iterator.GetRegisterOperand(op_index);
stream << 'R';
if (op_size != OperandSize::kByte) stream << size_tag;
if (register_value.is_current_context()) {
stream << "(context)";
} else if (register_value.is_function_closure()) {
stream << "(closure)";
} else if (register_value.is_parameter()) {
int parameter_index = register_value.ToParameterIndex(parameter_count);
if (parameter_index == 0) {
stream << "(this)";
} else {
stream << "(arg" << (parameter_index - 1) << ')';
}
} else {
stream << '(' << register_value.index() << ')';
}
} else {
switch (op_type) {
case OperandType::kFlag8:
stream << 'U' << size_tag << '(';
stream << bytecode_iterator.GetFlagOperand(op_index);
break;
case OperandType::kIdx: {
stream << 'U' << size_tag << '(';
stream << bytecode_iterator.GetIndexOperand(op_index);
break;
}
case OperandType::kUImm:
stream << 'U' << size_tag << '(';
stream << bytecode_iterator.GetUnsignedImmediateOperand(op_index);
break;
case OperandType::kImm:
stream << 'I' << size_tag << '(';
stream << bytecode_iterator.GetImmediateOperand(op_index);
break;
case OperandType::kRegCount:
stream << 'U' << size_tag << '(';
stream << bytecode_iterator.GetRegisterCountOperand(op_index);
break;
case OperandType::kRuntimeId: {
stream << 'U' << size_tag << '(';
Runtime::FunctionId id =
bytecode_iterator.GetRuntimeIdOperand(op_index);
stream << "Runtime::k" << i::Runtime::FunctionForId(id)->name;
break;
}
case OperandType::kIntrinsicId: {
stream << 'U' << size_tag << '(';
Runtime::FunctionId id =
bytecode_iterator.GetIntrinsicIdOperand(op_index);
stream << "Runtime::k" << i::Runtime::FunctionForId(id)->name;
break;
}
case OperandType::kNativeContextIndex: {
stream << 'U' << size_tag << '(';
uint32_t idx = bytecode_iterator.GetNativeContextIndexOperand(op_index);
stream << "%" << NameForNativeContextIntrinsicIndex(idx);
break;
}
default:
UNREACHABLE();
}
stream << ')';
}
}
void BytecodeExpectationsPrinter::PrintBytecode(
std::ostream& stream, const BytecodeArrayIterator& bytecode_iterator,
int parameter_count) const {
Bytecode bytecode = bytecode_iterator.current_bytecode();
OperandScale operand_scale = bytecode_iterator.current_operand_scale();
if (Bytecodes::OperandScaleRequiresPrefixBytecode(operand_scale)) {
Bytecode prefix = Bytecodes::OperandScaleToPrefixBytecode(operand_scale);
stream << "B(" << Bytecodes::ToString(prefix) << "), ";
}
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_iterator, bytecode, op_index,
parameter_count);
}
}
void BytecodeExpectationsPrinter::PrintSourcePosition(
std::ostream& stream, SourcePositionTableIterator& source_iterator,
int bytecode_offset) const {
static const size_t kPositionWidth = 4;
if (!source_iterator.done() &&
source_iterator.code_offset() == bytecode_offset) {
stream << "/* " << std::setw(kPositionWidth)
<< source_iterator.source_position().ScriptOffset();
if (source_iterator.is_statement()) {
stream << " S> */ ";
} else {
stream << " E> */ ";
}
source_iterator.Advance();
} else {
stream << " " << std::setw(kPositionWidth) << ' ' << " ";
}
}
void BytecodeExpectationsPrinter::PrintV8String(std::ostream& stream,
i::String string) const {
stream << '"';
for (int i = 0, length = string.length(); i < length; ++i) {
stream << i::AsEscapedUC16ForJSON(string.Get(i));
}
stream << '"';
}
void BytecodeExpectationsPrinter::PrintConstant(
std::ostream& stream, i::Handle<i::Object> constant) const {
if (constant->IsSmi()) {
stream << "Smi [";
i::Smi::cast(*constant).SmiPrint(stream);
stream << "]";
} else {
stream << i::HeapObject::cast(*constant).map().instance_type();
if (constant->IsHeapNumber()) {
stream << " [";
i::HeapNumber::cast(*constant).HeapNumberPrint(stream);
stream << "]";
} else if (constant->IsString()) {
stream << " [";
PrintV8String(stream, i::String::cast(*constant));
stream << "]";
}
}
}
void BytecodeExpectationsPrinter::PrintFrameSize(
std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
int32_t frame_size = bytecode_array->frame_size();
DCHECK(IsAligned(frame_size, kSystemPointerSize));
stream << "frame size: " << frame_size / kSystemPointerSize
<< "\nparameter count: " << bytecode_array->parameter_count() << '\n';
}
void BytecodeExpectationsPrinter::PrintBytecodeSequence(
std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
stream << "bytecode array length: " << bytecode_array->length()
<< "\nbytecodes: [\n";
SourcePositionTableIterator source_iterator(
bytecode_array->SourcePositionTable());
BytecodeArrayIterator bytecode_iterator(bytecode_array);
for (; !bytecode_iterator.done(); bytecode_iterator.Advance()) {
stream << kIndent;
PrintSourcePosition(stream, source_iterator,
bytecode_iterator.current_offset());
PrintBytecode(stream, bytecode_iterator, bytecode_array->parameter_count());
stream << ",\n";
}
stream << "]\n";
}
void BytecodeExpectationsPrinter::PrintConstantPool(
std::ostream& stream, i::FixedArray constant_pool) const {
stream << "constant pool: [\n";
int num_constants = constant_pool.length();
if (num_constants > 0) {
for (int i = 0; i < num_constants; ++i) {
stream << kIndent;
PrintConstant(stream, i::FixedArray::get(constant_pool, i, i_isolate()));
stream << ",\n";
}
}
stream << "]\n";
}
void BytecodeExpectationsPrinter::PrintCodeSnippet(
std::ostream& stream, const std::string& body) const {
stream << "snippet: \"\n";
std::stringstream body_stream(body);
std::string body_line;
while (std::getline(body_stream, body_line)) {
stream << kIndent;
PrintEscapedString(stream, body_line);
stream << '\n';
}
stream << "\"\n";
}
void BytecodeExpectationsPrinter::PrintHandlers(
std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
stream << "handlers: [\n";
HandlerTable table(*bytecode_array);
for (int i = 0, num_entries = table.NumberOfRangeEntries(); i < num_entries;
++i) {
stream << " [" << table.GetRangeStart(i) << ", " << table.GetRangeEnd(i)
<< ", " << table.GetRangeHandler(i) << "],\n";
}
stream << "]\n";
}
void BytecodeExpectationsPrinter::PrintBytecodeArray(
std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
PrintFrameSize(stream, bytecode_array);
PrintBytecodeSequence(stream, bytecode_array);
PrintConstantPool(stream, bytecode_array->constant_pool());
PrintHandlers(stream, bytecode_array);
}
void BytecodeExpectationsPrinter::PrintExpectation(
std::ostream& stream, const std::string& snippet) const {
std::string source_code =
wrap_ ? WrapCodeInFunction(test_function_name_.c_str(), snippet)
: snippet;
i::FLAG_enable_one_shot_optimization = oneshot_opt_;
i::FLAG_compilation_cache = false;
i::Handle<i::BytecodeArray> bytecode_array;
if (module_) {
CHECK(top_level_ && !wrap_);
v8::Local<v8::Module> module = CompileModule(source_code.c_str());
bytecode_array = GetBytecodeArrayForModule(module);
} else if (print_callee_) {
bytecode_array = GetBytecodeArrayOfCallee(source_code.c_str());
} else {
v8::Local<v8::Script> script = CompileScript(source_code.c_str());
if (top_level_) {
bytecode_array = GetBytecodeArrayForScript(script);
} else {
Run(script);
bytecode_array = GetBytecodeArrayForGlobal(test_function_name_.c_str());
}
}
stream << "---\n";
PrintCodeSnippet(stream, snippet);
PrintBytecodeArray(stream, bytecode_array);
stream << '\n';
}
} // namespace interpreter
} // namespace internal
} // namespace v8