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