Line data Source code
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/api.h"
15 : #include "src/base/logging.h"
16 : #include "src/objects-inl.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/source-position-table.h"
25 :
26 : namespace v8 {
27 : namespace internal {
28 : namespace interpreter {
29 :
30 366 : static const char* NameForNativeContextIntrinsicIndex(uint32_t idx) {
31 366 : switch (idx) {
32 : #define COMPARE_NATIVE_CONTEXT_INTRINSIC_IDX(NAME, Type, name) \
33 : case Context::NAME: \
34 : return #name;
35 :
36 48 : NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(COMPARE_NATIVE_CONTEXT_INTRINSIC_IDX)
37 :
38 : default:
39 : break;
40 : }
41 :
42 0 : return "UnknownIntrinsicIndex";
43 : }
44 :
45 : // static
46 : const char* const BytecodeExpectationsPrinter::kDefaultTopFunctionName =
47 : "__genbckexp_wrapper__";
48 : const char* const BytecodeExpectationsPrinter::kIndent = " ";
49 :
50 5016 : v8::Local<v8::String> BytecodeExpectationsPrinter::V8StringFromUTF8(
51 : const char* data) const {
52 5016 : return v8::String::NewFromUtf8(isolate_, data, v8::NewStringType::kNormal)
53 10032 : .ToLocalChecked();
54 : }
55 :
56 1638 : std::string BytecodeExpectationsPrinter::WrapCodeInFunction(
57 : const char* function_name, const std::string& function_body) const {
58 1638 : std::ostringstream program_stream;
59 1638 : program_stream << "function " << function_name << "() {" << function_body
60 1638 : << "}\n"
61 1638 : << function_name << "();";
62 :
63 1638 : return program_stream.str();
64 : }
65 :
66 2490 : v8::Local<v8::Script> BytecodeExpectationsPrinter::CompileScript(
67 : const char* program) const {
68 2490 : v8::Local<v8::String> source = V8StringFromUTF8(program);
69 2490 : return v8::Script::Compile(isolate_->GetCurrentContext(), source)
70 4980 : .ToLocalChecked();
71 : }
72 :
73 66 : v8::Local<v8::Module> BytecodeExpectationsPrinter::CompileModule(
74 : const char* program) const {
75 : ScriptOrigin origin(
76 : Local<v8::Value>(), Local<v8::Integer>(), Local<v8::Integer>(),
77 : Local<v8::Boolean>(), Local<v8::Integer>(), Local<v8::Value>(),
78 66 : Local<v8::Boolean>(), Local<v8::Boolean>(), True(isolate_));
79 66 : v8::ScriptCompiler::Source source(V8StringFromUTF8(program), origin);
80 132 : return v8::ScriptCompiler::CompileModule(isolate_, &source).ToLocalChecked();
81 : }
82 :
83 2460 : void BytecodeExpectationsPrinter::Run(v8::Local<v8::Script> script) const {
84 4920 : (void)script->Run(isolate_->GetCurrentContext());
85 2460 : }
86 :
87 : i::Handle<v8::internal::BytecodeArray>
88 2460 : BytecodeExpectationsPrinter::GetBytecodeArrayForGlobal(
89 2460 : const char* global_name) const {
90 2460 : const v8::Local<v8::Context>& context = isolate_->GetCurrentContext();
91 2460 : v8::Local<v8::String> v8_global_name = V8StringFromUTF8(global_name);
92 : v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
93 7380 : context->Global()->Get(context, v8_global_name).ToLocalChecked());
94 : i::Handle<i::JSFunction> js_function =
95 : i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(*function));
96 :
97 : i::Handle<i::BytecodeArray> bytecodes =
98 : i::handle(js_function->shared()->bytecode_array(), i_isolate());
99 :
100 2460 : return bytecodes;
101 : }
102 :
103 : i::Handle<i::BytecodeArray>
104 66 : BytecodeExpectationsPrinter::GetBytecodeArrayForModule(
105 66 : v8::Local<v8::Module> module) const {
106 : i::Handle<i::Module> i_module = v8::Utils::OpenHandle(*module);
107 : return i::handle(SharedFunctionInfo::cast(i_module->code())->bytecode_array(),
108 66 : i_isolate());
109 : }
110 :
111 : i::Handle<i::BytecodeArray>
112 30 : BytecodeExpectationsPrinter::GetBytecodeArrayForScript(
113 30 : v8::Local<v8::Script> script) const {
114 : i::Handle<i::JSFunction> js_function = v8::Utils::OpenHandle(*script);
115 30 : return i::handle(js_function->shared()->bytecode_array(), i_isolate());
116 : }
117 :
118 39276 : void BytecodeExpectationsPrinter::PrintEscapedString(
119 : std::ostream& stream, const std::string& string) const {
120 600504 : for (char c : string) {
121 521952 : switch (c) {
122 : case '"':
123 13140 : stream << "\\\"";
124 13140 : break;
125 : case '\\':
126 18 : stream << "\\\\";
127 18 : break;
128 : default:
129 : stream << c;
130 : break;
131 : }
132 : }
133 39276 : }
134 :
135 121254 : void BytecodeExpectationsPrinter::PrintBytecodeOperand(
136 : std::ostream& stream, const BytecodeArrayIterator& bytecode_iterator,
137 : const Bytecode& bytecode, int op_index, int parameter_count) const {
138 121254 : OperandType op_type = Bytecodes::GetOperandType(bytecode, op_index);
139 : OperandSize op_size = Bytecodes::GetOperandSize(
140 121254 : bytecode, op_index, bytecode_iterator.current_operand_scale());
141 :
142 : const char* size_tag;
143 121254 : switch (op_size) {
144 : case OperandSize::kByte:
145 : size_tag = "8";
146 : break;
147 : case OperandSize::kShort:
148 : size_tag = "16";
149 2256 : break;
150 : case OperandSize::kQuad:
151 : size_tag = "32";
152 6 : break;
153 : default:
154 0 : UNREACHABLE();
155 : return;
156 : }
157 :
158 121254 : if (Bytecodes::IsRegisterOperandType(op_type)) {
159 57750 : Register register_value = bytecode_iterator.GetRegisterOperand(op_index);
160 : stream << 'R';
161 57750 : if (op_size != OperandSize::kByte) stream << size_tag;
162 57750 : if (register_value.is_current_context()) {
163 534 : stream << "(context)";
164 57216 : } else if (register_value.is_function_closure()) {
165 750 : stream << "(closure)";
166 56466 : } else if (register_value.is_parameter()) {
167 10986 : int parameter_index = register_value.ToParameterIndex(parameter_count);
168 10986 : if (parameter_index == 0) {
169 360 : stream << "(this)";
170 : } else {
171 10626 : stream << "(arg" << (parameter_index - 1) << ')';
172 : }
173 : } else {
174 45480 : stream << '(' << register_value.index() << ')';
175 : }
176 : } else {
177 63504 : switch (op_type) {
178 : case OperandType::kFlag8:
179 1020 : stream << 'U' << size_tag << '(';
180 1020 : stream << bytecode_iterator.GetFlagOperand(op_index);
181 : break;
182 : case OperandType::kIdx: {
183 39342 : stream << 'U' << size_tag << '(';
184 39342 : stream << bytecode_iterator.GetIndexOperand(op_index);
185 : break;
186 : }
187 : case OperandType::kUImm:
188 5994 : stream << 'U' << size_tag << '(';
189 5994 : stream << bytecode_iterator.GetUnsignedImmediateOperand(op_index);
190 : break;
191 : case OperandType::kImm:
192 10608 : stream << 'I' << size_tag << '(';
193 10608 : stream << bytecode_iterator.GetImmediateOperand(op_index);
194 10608 : break;
195 : case OperandType::kRegCount:
196 3600 : stream << 'U' << size_tag << '(';
197 3600 : stream << bytecode_iterator.GetRegisterCountOperand(op_index);
198 : break;
199 : case OperandType::kRuntimeId: {
200 1092 : stream << 'U' << size_tag << '(';
201 : Runtime::FunctionId id =
202 1092 : bytecode_iterator.GetRuntimeIdOperand(op_index);
203 1092 : stream << "Runtime::k" << i::Runtime::FunctionForId(id)->name;
204 1092 : break;
205 : }
206 : case OperandType::kIntrinsicId: {
207 1482 : stream << 'U' << size_tag << '(';
208 : Runtime::FunctionId id =
209 1482 : bytecode_iterator.GetIntrinsicIdOperand(op_index);
210 1482 : stream << "Runtime::k" << i::Runtime::FunctionForId(id)->name;
211 1482 : break;
212 : }
213 : case OperandType::kNativeContextIndex: {
214 366 : stream << 'U' << size_tag << '(';
215 366 : uint32_t idx = bytecode_iterator.GetNativeContextIndexOperand(op_index);
216 366 : stream << "%" << NameForNativeContextIntrinsicIndex(idx);
217 366 : break;
218 : }
219 : default:
220 0 : UNREACHABLE();
221 : }
222 :
223 : stream << ')';
224 : }
225 121254 : }
226 :
227 99336 : void BytecodeExpectationsPrinter::PrintBytecode(
228 : std::ostream& stream, const BytecodeArrayIterator& bytecode_iterator,
229 : int parameter_count) const {
230 99336 : Bytecode bytecode = bytecode_iterator.current_bytecode();
231 : OperandScale operand_scale = bytecode_iterator.current_operand_scale();
232 99336 : if (Bytecodes::OperandScaleRequiresPrefixBytecode(operand_scale)) {
233 906 : Bytecode prefix = Bytecodes::OperandScaleToPrefixBytecode(operand_scale);
234 906 : stream << "B(" << Bytecodes::ToString(prefix) << "), ";
235 : }
236 99336 : stream << "B(" << Bytecodes::ToString(bytecode) << ')';
237 99336 : int operands_count = Bytecodes::NumberOfOperands(bytecode);
238 220590 : for (int op_index = 0; op_index < operands_count; ++op_index) {
239 121254 : stream << ", ";
240 : PrintBytecodeOperand(stream, bytecode_iterator, bytecode, op_index,
241 121254 : parameter_count);
242 : }
243 99336 : }
244 :
245 99336 : void BytecodeExpectationsPrinter::PrintSourcePosition(
246 286440 : std::ostream& stream, SourcePositionTableIterator& source_iterator,
247 : int bytecode_offset) const {
248 : static const size_t kPositionWidth = 4;
249 198672 : if (!source_iterator.done() &&
250 : source_iterator.code_offset() == bytecode_offset) {
251 43884 : stream << "/* " << std::setw(kPositionWidth)
252 43884 : << source_iterator.source_position().ScriptOffset();
253 43884 : if (source_iterator.is_statement()) {
254 32862 : stream << " S> */ ";
255 : } else {
256 11022 : stream << " E> */ ";
257 : }
258 43884 : source_iterator.Advance();
259 : } else {
260 110904 : stream << " " << std::setw(kPositionWidth) << ' ' << " ";
261 : }
262 99336 : }
263 :
264 1776 : void BytecodeExpectationsPrinter::PrintV8String(std::ostream& stream,
265 : i::String* string) const {
266 : stream << '"';
267 8358 : for (int i = 0, length = string->length(); i < length; ++i) {
268 6582 : stream << i::AsEscapedUC16ForJSON(string->Get(i));
269 : }
270 : stream << '"';
271 1776 : }
272 :
273 18474 : void BytecodeExpectationsPrinter::PrintConstant(
274 : std::ostream& stream, i::Handle<i::Object> constant) const {
275 18474 : if (constant->IsSmi()) {
276 1056 : stream << "Smi [";
277 1056 : i::Smi::cast(*constant)->SmiPrint(stream);
278 1056 : stream << "]";
279 : } else {
280 17418 : stream << i::HeapObject::cast(*constant)->map()->instance_type();
281 17418 : if (constant->IsHeapNumber()) {
282 14202 : stream << " [";
283 14202 : i::HeapNumber::cast(*constant)->HeapNumberPrint(stream);
284 14202 : stream << "]";
285 3216 : } else if (constant->IsString()) {
286 1776 : stream << " [";
287 1776 : PrintV8String(stream, i::String::cast(*constant));
288 1776 : stream << "]";
289 : }
290 : }
291 18474 : }
292 :
293 2556 : void BytecodeExpectationsPrinter::PrintFrameSize(
294 : std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
295 : const int kPointerSize = sizeof(void*);
296 : int frame_size = bytecode_array->frame_size();
297 :
298 : DCHECK_EQ(frame_size % kPointerSize, 0);
299 2556 : stream << "frame size: " << frame_size / kPointerSize
300 2556 : << "\nparameter count: " << bytecode_array->parameter_count() << '\n';
301 2556 : }
302 :
303 2556 : void BytecodeExpectationsPrinter::PrintBytecodeSequence(
304 : std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
305 2556 : stream << "bytecode array length: " << bytecode_array->length()
306 2556 : << "\nbytecodes: [\n";
307 :
308 : SourcePositionTableIterator source_iterator(
309 2556 : bytecode_array->SourcePositionTable());
310 2556 : BytecodeArrayIterator bytecode_iterator(bytecode_array);
311 101892 : for (; !bytecode_iterator.done(); bytecode_iterator.Advance()) {
312 99336 : stream << kIndent;
313 : PrintSourcePosition(stream, source_iterator,
314 99336 : bytecode_iterator.current_offset());
315 99336 : PrintBytecode(stream, bytecode_iterator, bytecode_array->parameter_count());
316 99336 : stream << ",\n";
317 : }
318 2556 : stream << "]\n";
319 2556 : }
320 :
321 2556 : void BytecodeExpectationsPrinter::PrintConstantPool(
322 18474 : std::ostream& stream, i::FixedArray* constant_pool) const {
323 2556 : stream << "constant pool: [\n";
324 : int num_constants = constant_pool->length();
325 2556 : if (num_constants > 0) {
326 18474 : for (int i = 0; i < num_constants; ++i) {
327 18474 : stream << kIndent;
328 18474 : PrintConstant(stream, i::FixedArray::get(constant_pool, i, i_isolate()));
329 18474 : stream << ",\n";
330 : }
331 : }
332 2556 : stream << "]\n";
333 2556 : }
334 :
335 2556 : void BytecodeExpectationsPrinter::PrintCodeSnippet(
336 : std::ostream& stream, const std::string& body) const {
337 2556 : stream << "snippet: \"\n";
338 2556 : std::stringstream body_stream(body);
339 : std::string body_line;
340 83664 : while (std::getline(body_stream, body_line)) {
341 39276 : stream << kIndent;
342 39276 : PrintEscapedString(stream, body_line);
343 : stream << '\n';
344 : }
345 5112 : stream << "\"\n";
346 2556 : }
347 :
348 2556 : void BytecodeExpectationsPrinter::PrintHandlers(
349 : std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
350 2556 : stream << "handlers: [\n";
351 : HandlerTable* table = HandlerTable::cast(bytecode_array->handler_table());
352 3078 : for (int i = 0, num_entries = table->NumberOfRangeEntries(); i < num_entries;
353 : ++i) {
354 1044 : stream << " [" << table->GetRangeStart(i) << ", " << table->GetRangeEnd(i)
355 1044 : << ", " << table->GetRangeHandler(i) << "],\n";
356 : }
357 2556 : stream << "]\n";
358 2556 : }
359 :
360 2556 : void BytecodeExpectationsPrinter::PrintBytecodeArray(
361 : std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
362 2556 : PrintFrameSize(stream, bytecode_array);
363 2556 : PrintBytecodeSequence(stream, bytecode_array);
364 2556 : PrintConstantPool(stream, bytecode_array->constant_pool());
365 2556 : PrintHandlers(stream, bytecode_array);
366 2556 : }
367 :
368 2556 : void BytecodeExpectationsPrinter::PrintExpectation(
369 : std::ostream& stream, const std::string& snippet) const {
370 : std::string source_code =
371 : wrap_ ? WrapCodeInFunction(test_function_name_.c_str(), snippet)
372 4194 : : snippet;
373 :
374 : i::Handle<i::BytecodeArray> bytecode_array;
375 2556 : if (module_) {
376 66 : CHECK(top_level_ && !wrap_);
377 66 : v8::Local<v8::Module> module = CompileModule(source_code.c_str());
378 66 : bytecode_array = GetBytecodeArrayForModule(module);
379 : } else {
380 2490 : v8::Local<v8::Script> script = CompileScript(source_code.c_str());
381 2490 : if (top_level_) {
382 30 : bytecode_array = GetBytecodeArrayForScript(script);
383 : } else {
384 2460 : Run(script);
385 2460 : bytecode_array = GetBytecodeArrayForGlobal(test_function_name_.c_str());
386 : }
387 : }
388 :
389 2556 : stream << "---\n";
390 2556 : PrintCodeSnippet(stream, snippet);
391 2556 : PrintBytecodeArray(stream, bytecode_array);
392 : stream << '\n';
393 2556 : }
394 :
395 : } // namespace interpreter
396 : } // namespace internal
397 71154 : } // namespace v8
|