Home | History | Annotate | Download | only in interpreter
      1 // 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 "test/cctest/interpreter/bytecode-expectations-printer.h"
      6 
      7 #include <iomanip>
      8 #include <iostream>
      9 #include <vector>
     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/runtime/runtime.h"
     18 
     19 #include "src/interpreter/bytecode-array-iterator.h"
     20 #include "src/interpreter/bytecode-generator.h"
     21 #include "src/interpreter/bytecodes.h"
     22 #include "src/interpreter/interpreter-intrinsics.h"
     23 #include "src/interpreter/interpreter.h"
     24 #include "src/interpreter/source-position-table.h"
     25 
     26 namespace v8 {
     27 namespace internal {
     28 namespace interpreter {
     29 
     30 // static
     31 const char* const BytecodeExpectationsPrinter::kDefaultTopFunctionName =
     32     "__genbckexp_wrapper__";
     33 const char* const BytecodeExpectationsPrinter::kIndent = "  ";
     34 
     35 v8::Local<v8::String> BytecodeExpectationsPrinter::V8StringFromUTF8(
     36     const char* data) const {
     37   return v8::String::NewFromUtf8(isolate_, data, v8::NewStringType::kNormal)
     38       .ToLocalChecked();
     39 }
     40 
     41 std::string BytecodeExpectationsPrinter::WrapCodeInFunction(
     42     const char* function_name, const std::string& function_body) const {
     43   std::ostringstream program_stream;
     44   program_stream << "function " << function_name << "() {" << function_body
     45                  << "}\n"
     46                  << function_name << "();";
     47 
     48   return program_stream.str();
     49 }
     50 
     51 v8::Local<v8::Script> BytecodeExpectationsPrinter::Compile(
     52     const char* program) const {
     53   v8::Local<v8::String> source = V8StringFromUTF8(program);
     54   return v8::Script::Compile(isolate_->GetCurrentContext(), source)
     55       .ToLocalChecked();
     56 }
     57 
     58 void BytecodeExpectationsPrinter::Run(v8::Local<v8::Script> script) const {
     59   (void)script->Run(isolate_->GetCurrentContext());
     60 }
     61 
     62 i::Handle<v8::internal::BytecodeArray>
     63 BytecodeExpectationsPrinter::GetBytecodeArrayForGlobal(
     64     const char* global_name) const {
     65   const v8::Local<v8::Context>& context = isolate_->GetCurrentContext();
     66   v8::Local<v8::String> v8_global_name = V8StringFromUTF8(global_name);
     67   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
     68       context->Global()->Get(context, v8_global_name).ToLocalChecked());
     69   i::Handle<i::JSFunction> js_function =
     70       i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(*function));
     71 
     72   i::Handle<i::BytecodeArray> bytecodes =
     73       i::handle(js_function->shared()->bytecode_array(), i_isolate());
     74 
     75   return bytecodes;
     76 }
     77 
     78 i::Handle<i::BytecodeArray>
     79 BytecodeExpectationsPrinter::GetBytecodeArrayForScript(
     80     v8::Local<v8::Script> script) const {
     81   i::Handle<i::JSFunction> js_function = v8::Utils::OpenHandle(*script);
     82   return i::handle(js_function->shared()->bytecode_array(), i_isolate());
     83 }
     84 
     85 void BytecodeExpectationsPrinter::PrintEscapedString(
     86     std::ostream& stream, const std::string& string) const {
     87   for (char c : string) {
     88     switch (c) {
     89       case '"':
     90         stream << "\\\"";
     91         break;
     92       case '\\':
     93         stream << "\\\\";
     94         break;
     95       default:
     96         stream << c;
     97         break;
     98     }
     99   }
    100 }
    101 
    102 void BytecodeExpectationsPrinter::PrintBytecodeOperand(
    103     std::ostream& stream, const BytecodeArrayIterator& bytecode_iterator,
    104     const Bytecode& bytecode, int op_index, int parameter_count) const {
    105   OperandType op_type = Bytecodes::GetOperandType(bytecode, op_index);
    106   OperandSize op_size = Bytecodes::GetOperandSize(
    107       bytecode, op_index, bytecode_iterator.current_operand_scale());
    108 
    109   const char* size_tag;
    110   switch (op_size) {
    111     case OperandSize::kByte:
    112       size_tag = "8";
    113       break;
    114     case OperandSize::kShort:
    115       size_tag = "16";
    116       break;
    117     case OperandSize::kQuad:
    118       size_tag = "32";
    119       break;
    120     default:
    121       UNREACHABLE();
    122       return;
    123   }
    124 
    125   if (Bytecodes::IsRegisterOperandType(op_type)) {
    126     Register register_value = bytecode_iterator.GetRegisterOperand(op_index);
    127     stream << 'R';
    128     if (op_size != OperandSize::kByte) stream << size_tag;
    129     if (register_value.is_new_target()) {
    130       stream << "(new_target)";
    131     } else if (register_value.is_current_context()) {
    132       stream << "(context)";
    133     } else if (register_value.is_function_closure()) {
    134       stream << "(closure)";
    135     } else if (register_value.is_parameter()) {
    136       int parameter_index = register_value.ToParameterIndex(parameter_count);
    137       if (parameter_index == 0) {
    138         stream << "(this)";
    139       } else {
    140         stream << "(arg" << (parameter_index - 1) << ')';
    141       }
    142     } else {
    143       stream << '(' << register_value.index() << ')';
    144     }
    145   } else {
    146     stream << 'U' << size_tag << '(';
    147 
    148     switch (op_type) {
    149       case OperandType::kFlag8:
    150         stream << bytecode_iterator.GetFlagOperand(op_index);
    151         break;
    152       case OperandType::kIdx:
    153         stream << bytecode_iterator.GetIndexOperand(op_index);
    154         break;
    155       case OperandType::kImm:
    156         stream << bytecode_iterator.GetImmediateOperand(op_index);
    157         break;
    158       case OperandType::kRegCount:
    159         stream << bytecode_iterator.GetRegisterCountOperand(op_index);
    160         break;
    161       case OperandType::kRuntimeId: {
    162         Runtime::FunctionId id =
    163             bytecode_iterator.GetRuntimeIdOperand(op_index);
    164         stream << "Runtime::k" << i::Runtime::FunctionForId(id)->name;
    165         break;
    166       }
    167       case OperandType::kIntrinsicId: {
    168         Runtime::FunctionId id =
    169             bytecode_iterator.GetIntrinsicIdOperand(op_index);
    170         stream << "Runtime::k" << i::Runtime::FunctionForId(id)->name;
    171         break;
    172       }
    173       default:
    174         UNREACHABLE();
    175     }
    176 
    177     stream << ')';
    178   }
    179 }
    180 
    181 void BytecodeExpectationsPrinter::PrintBytecode(
    182     std::ostream& stream, const BytecodeArrayIterator& bytecode_iterator,
    183     int parameter_count) const {
    184   Bytecode bytecode = bytecode_iterator.current_bytecode();
    185   OperandScale operand_scale = bytecode_iterator.current_operand_scale();
    186   if (Bytecodes::OperandScaleRequiresPrefixBytecode(operand_scale)) {
    187     Bytecode prefix = Bytecodes::OperandScaleToPrefixBytecode(operand_scale);
    188     stream << "B(" << Bytecodes::ToString(prefix) << "), ";
    189   }
    190   stream << "B(" << Bytecodes::ToString(bytecode) << ')';
    191   int operands_count = Bytecodes::NumberOfOperands(bytecode);
    192   for (int op_index = 0; op_index < operands_count; ++op_index) {
    193     stream << ", ";
    194     PrintBytecodeOperand(stream, bytecode_iterator, bytecode, op_index,
    195                          parameter_count);
    196   }
    197 }
    198 
    199 void BytecodeExpectationsPrinter::PrintSourcePosition(
    200     std::ostream& stream, SourcePositionTableIterator& source_iterator,
    201     int bytecode_offset) const {
    202   static const size_t kPositionWidth = 4;
    203   if (!source_iterator.done() &&
    204       source_iterator.bytecode_offset() == bytecode_offset) {
    205     stream << "/* " << std::setw(kPositionWidth)
    206            << source_iterator.source_position();
    207     if (source_iterator.is_statement()) {
    208       stream << " S> */ ";
    209     } else {
    210       stream << " E> */ ";
    211     }
    212     source_iterator.Advance();
    213   } else {
    214     stream << "   " << std::setw(kPositionWidth) << ' ' << "       ";
    215   }
    216 }
    217 
    218 void BytecodeExpectationsPrinter::PrintV8String(std::ostream& stream,
    219                                                 i::String* string) const {
    220   stream << '"';
    221   for (int i = 0, length = string->length(); i < length; ++i) {
    222     stream << i::AsEscapedUC16ForJSON(string->Get(i));
    223   }
    224   stream << '"';
    225 }
    226 
    227 void BytecodeExpectationsPrinter::PrintConstant(
    228     std::ostream& stream, i::Handle<i::Object> constant) const {
    229   switch (const_pool_type_) {
    230     case ConstantPoolType::kString:
    231       CHECK(constant->IsString());
    232       PrintV8String(stream, i::String::cast(*constant));
    233       break;
    234     case ConstantPoolType::kNumber:
    235       if (constant->IsSmi()) {
    236         i::Smi::cast(*constant)->SmiPrint(stream);
    237       } else if (constant->IsHeapNumber()) {
    238         i::HeapNumber::cast(*constant)->HeapNumberPrint(stream);
    239       } else {
    240         UNREACHABLE();
    241       }
    242       break;
    243     case ConstantPoolType::kMixed:
    244       if (constant->IsSmi()) {
    245         stream << "kInstanceTypeDontCare";
    246       } else {
    247         stream << "InstanceType::"
    248                << i::HeapObject::cast(*constant)->map()->instance_type();
    249       }
    250       break;
    251     case ConstantPoolType::kUnknown:
    252     default:
    253       UNREACHABLE();
    254       return;
    255   }
    256 }
    257 
    258 void BytecodeExpectationsPrinter::PrintFrameSize(
    259     std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
    260   const int kPointerSize = sizeof(void*);
    261   int frame_size = bytecode_array->frame_size();
    262 
    263   DCHECK_EQ(frame_size % kPointerSize, 0);
    264   stream << "frame size: " << frame_size / kPointerSize
    265          << "\nparameter count: " << bytecode_array->parameter_count() << '\n';
    266 }
    267 
    268 void BytecodeExpectationsPrinter::PrintBytecodeSequence(
    269     std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
    270   stream << "bytecode array length: " << bytecode_array->length()
    271          << "\nbytecodes: [\n";
    272 
    273   SourcePositionTableIterator source_iterator(
    274       bytecode_array->source_position_table());
    275   BytecodeArrayIterator bytecode_iterator(bytecode_array);
    276   for (; !bytecode_iterator.done(); bytecode_iterator.Advance()) {
    277     stream << kIndent;
    278     PrintSourcePosition(stream, source_iterator,
    279                         bytecode_iterator.current_offset());
    280     PrintBytecode(stream, bytecode_iterator, bytecode_array->parameter_count());
    281     stream << ",\n";
    282   }
    283   stream << "]\n";
    284 }
    285 
    286 void BytecodeExpectationsPrinter::PrintConstantPool(
    287     std::ostream& stream, i::FixedArray* constant_pool) const {
    288   stream << "constant pool: [\n";
    289   int num_constants = constant_pool->length();
    290   if (num_constants > 0) {
    291     for (int i = 0; i < num_constants; ++i) {
    292       stream << kIndent;
    293       PrintConstant(stream, i::FixedArray::get(constant_pool, i, i_isolate()));
    294       stream << ",\n";
    295     }
    296   }
    297   stream << "]\n";
    298 }
    299 
    300 void BytecodeExpectationsPrinter::PrintCodeSnippet(
    301     std::ostream& stream, const std::string& body) const {
    302   stream << "snippet: \"\n";
    303   std::stringstream body_stream(body);
    304   std::string body_line;
    305   while (std::getline(body_stream, body_line)) {
    306     stream << kIndent;
    307     PrintEscapedString(stream, body_line);
    308     stream << '\n';
    309   }
    310   stream << "\"\n";
    311 }
    312 
    313 void BytecodeExpectationsPrinter::PrintHandlers(
    314     std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
    315   stream << "handlers: [\n";
    316   HandlerTable* table = HandlerTable::cast(bytecode_array->handler_table());
    317   for (int i = 0, num_entries = table->NumberOfRangeEntries(); i < num_entries;
    318        ++i) {
    319     stream << "  [" << table->GetRangeStart(i) << ", " << table->GetRangeEnd(i)
    320            << ", " << table->GetRangeHandler(i) << "],\n";
    321   }
    322   stream << "]\n";
    323 }
    324 
    325 void BytecodeExpectationsPrinter::PrintBytecodeArray(
    326     std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
    327   PrintFrameSize(stream, bytecode_array);
    328   PrintBytecodeSequence(stream, bytecode_array);
    329   PrintConstantPool(stream, bytecode_array->constant_pool());
    330   PrintHandlers(stream, bytecode_array);
    331 }
    332 
    333 void BytecodeExpectationsPrinter::PrintExpectation(
    334     std::ostream& stream, const std::string& snippet) const {
    335   std::string source_code =
    336       wrap_ ? WrapCodeInFunction(test_function_name_.c_str(), snippet)
    337             : snippet;
    338 
    339   v8::Local<v8::Script> script = Compile(source_code.c_str());
    340 
    341   if (execute_) Run(script);
    342 
    343   i::Handle<i::BytecodeArray> bytecode_array =
    344       top_level_ ? GetBytecodeArrayForScript(script)
    345                  : GetBytecodeArrayForGlobal(test_function_name_.c_str());
    346 
    347   stream << "---\n";
    348   PrintCodeSnippet(stream, snippet);
    349   PrintBytecodeArray(stream, bytecode_array);
    350   stream << '\n';
    351 }
    352 
    353 }  // namespace interpreter
    354 }  // namespace internal
    355 }  // namespace v8
    356