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