blob: 83f11c6d424c779a89aa25065fdb50e5aa640648 [file] [log] [blame]
Ben Murdoch097c5b22016-05-18 11:27:45 +01001// 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
Ben Murdochc5610432016-08-08 18:44:38 +01007#include <iomanip>
Ben Murdoch097c5b22016-05-18 11:27:45 +01008#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"
Ben Murdochda12d292016-06-02 14:46:10 +010017#include "src/runtime/runtime.h"
Ben Murdoch097c5b22016-05-18 11:27:45 +010018
19#include "src/interpreter/bytecode-array-iterator.h"
20#include "src/interpreter/bytecode-generator.h"
21#include "src/interpreter/bytecodes.h"
Ben Murdoch61f157c2016-09-16 13:49:30 +010022#include "src/interpreter/interpreter-intrinsics.h"
Ben Murdoch097c5b22016-05-18 11:27:45 +010023#include "src/interpreter/interpreter.h"
Ben Murdochc5610432016-08-08 18:44:38 +010024#include "src/interpreter/source-position-table.h"
Ben Murdoch097c5b22016-05-18 11:27:45 +010025
26namespace v8 {
27namespace internal {
28namespace interpreter {
29
30// static
31const char* const BytecodeExpectationsPrinter::kDefaultTopFunctionName =
32 "__genbckexp_wrapper__";
Ben Murdochc5610432016-08-08 18:44:38 +010033const char* const BytecodeExpectationsPrinter::kIndent = " ";
Ben Murdoch097c5b22016-05-18 11:27:45 +010034
35v8::Local<v8::String> BytecodeExpectationsPrinter::V8StringFromUTF8(
36 const char* data) const {
37 return v8::String::NewFromUtf8(isolate_, data, v8::NewStringType::kNormal)
38 .ToLocalChecked();
39}
40
41std::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
51v8::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
58void BytecodeExpectationsPrinter::Run(v8::Local<v8::Script> script) const {
59 (void)script->Run(isolate_->GetCurrentContext());
60}
61
62i::Handle<v8::internal::BytecodeArray>
63BytecodeExpectationsPrinter::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
78i::Handle<i::BytecodeArray>
79BytecodeExpectationsPrinter::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
85void 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
102void BytecodeExpectationsPrinter::PrintBytecodeOperand(
Ben Murdochc5610432016-08-08 18:44:38 +0100103 std::ostream& stream, const BytecodeArrayIterator& bytecode_iterator,
Ben Murdoch097c5b22016-05-18 11:27:45 +0100104 const Bytecode& bytecode, int op_index, int parameter_count) const {
105 OperandType op_type = Bytecodes::GetOperandType(bytecode, op_index);
Ben Murdochda12d292016-06-02 14:46:10 +0100106 OperandSize op_size = Bytecodes::GetOperandSize(
Ben Murdochc5610432016-08-08 18:44:38 +0100107 bytecode, op_index, bytecode_iterator.current_operand_scale());
Ben Murdoch097c5b22016-05-18 11:27:45 +0100108
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;
Ben Murdochda12d292016-06-02 14:46:10 +0100117 case OperandSize::kQuad:
118 size_tag = "32";
119 break;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100120 default:
121 UNREACHABLE();
122 return;
123 }
124
125 if (Bytecodes::IsRegisterOperandType(op_type)) {
Ben Murdochc5610432016-08-08 18:44:38 +0100126 Register register_value = bytecode_iterator.GetRegisterOperand(op_index);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100127 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
Ben Murdochda12d292016-06-02 14:46:10 +0100148 switch (op_type) {
149 case OperandType::kFlag8:
Ben Murdochc5610432016-08-08 18:44:38 +0100150 stream << bytecode_iterator.GetFlagOperand(op_index);
Ben Murdochda12d292016-06-02 14:46:10 +0100151 break;
152 case OperandType::kIdx:
Ben Murdochc5610432016-08-08 18:44:38 +0100153 stream << bytecode_iterator.GetIndexOperand(op_index);
Ben Murdochda12d292016-06-02 14:46:10 +0100154 break;
155 case OperandType::kImm:
Ben Murdochc5610432016-08-08 18:44:38 +0100156 stream << bytecode_iterator.GetImmediateOperand(op_index);
Ben Murdochda12d292016-06-02 14:46:10 +0100157 break;
158 case OperandType::kRegCount:
Ben Murdochc5610432016-08-08 18:44:38 +0100159 stream << bytecode_iterator.GetRegisterCountOperand(op_index);
Ben Murdochda12d292016-06-02 14:46:10 +0100160 break;
161 case OperandType::kRuntimeId: {
Ben Murdoch61f157c2016-09-16 13:49:30 +0100162 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;
Ben Murdochda12d292016-06-02 14:46:10 +0100171 break;
172 }
173 default:
174 UNREACHABLE();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100175 }
176
177 stream << ')';
178 }
179}
180
181void BytecodeExpectationsPrinter::PrintBytecode(
Ben Murdochc5610432016-08-08 18:44:38 +0100182 std::ostream& stream, const BytecodeArrayIterator& bytecode_iterator,
Ben Murdoch097c5b22016-05-18 11:27:45 +0100183 int parameter_count) const {
Ben Murdochc5610432016-08-08 18:44:38 +0100184 Bytecode bytecode = bytecode_iterator.current_bytecode();
185 OperandScale operand_scale = bytecode_iterator.current_operand_scale();
Ben Murdochda12d292016-06-02 14:46:10 +0100186 if (Bytecodes::OperandScaleRequiresPrefixBytecode(operand_scale)) {
187 Bytecode prefix = Bytecodes::OperandScaleToPrefixBytecode(operand_scale);
188 stream << "B(" << Bytecodes::ToString(prefix) << "), ";
189 }
Ben Murdoch097c5b22016-05-18 11:27:45 +0100190 stream << "B(" << Bytecodes::ToString(bytecode) << ')';
Ben Murdoch097c5b22016-05-18 11:27:45 +0100191 int operands_count = Bytecodes::NumberOfOperands(bytecode);
192 for (int op_index = 0; op_index < operands_count; ++op_index) {
193 stream << ", ";
Ben Murdochc5610432016-08-08 18:44:38 +0100194 PrintBytecodeOperand(stream, bytecode_iterator, bytecode, op_index,
Ben Murdoch097c5b22016-05-18 11:27:45 +0100195 parameter_count);
196 }
197}
198
Ben Murdochc5610432016-08-08 18:44:38 +0100199void 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
Ben Murdoch097c5b22016-05-18 11:27:45 +0100218void 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
227void 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
258void 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);
Ben Murdochda12d292016-06-02 14:46:10 +0100264 stream << "frame size: " << frame_size / kPointerSize
265 << "\nparameter count: " << bytecode_array->parameter_count() << '\n';
Ben Murdoch097c5b22016-05-18 11:27:45 +0100266}
267
268void BytecodeExpectationsPrinter::PrintBytecodeSequence(
269 std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
Ben Murdochda12d292016-06-02 14:46:10 +0100270 stream << "bytecode array length: " << bytecode_array->length()
271 << "\nbytecodes: [\n";
Ben Murdochc5610432016-08-08 18:44:38 +0100272
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());
Ben Murdoch097c5b22016-05-18 11:27:45 +0100281 stream << ",\n";
282 }
283 stream << "]\n";
284}
285
286void 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) {
Ben Murdochc5610432016-08-08 18:44:38 +0100292 stream << kIndent;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100293 PrintConstant(stream, i::FixedArray::get(constant_pool, i, i_isolate()));
294 stream << ",\n";
295 }
296 }
297 stream << "]\n";
298}
299
300void 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)) {
Ben Murdochc5610432016-08-08 18:44:38 +0100306 stream << kIndent;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100307 PrintEscapedString(stream, body_line);
308 stream << '\n';
309 }
310 stream << "\"\n";
311}
312
313void 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
325void 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
333void 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