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