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