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 : #include "src/api-inl.h"
14 : #include "src/base/logging.h"
15 : #include "src/interpreter/bytecode-array-iterator.h"
16 : #include "src/interpreter/bytecode-generator.h"
17 : #include "src/interpreter/bytecodes.h"
18 : #include "src/interpreter/interpreter-intrinsics.h"
19 : #include "src/interpreter/interpreter.h"
20 : #include "src/objects-inl.h"
21 : #include "src/objects/heap-number-inl.h"
22 : #include "src/objects/module-inl.h"
23 : #include "src/ostreams.h"
24 : #include "src/runtime/runtime.h"
25 : #include "src/source-position-table.h"
26 : #include "test/cctest/cctest.h"
27 :
28 : namespace v8 {
29 : namespace internal {
30 : namespace interpreter {
31 :
32 15 : static const char* NameForNativeContextIntrinsicIndex(uint32_t idx) {
33 15 : switch (idx) {
34 : #define COMPARE_NATIVE_CONTEXT_INTRINSIC_IDX(NAME, Type, name) \
35 : case Context::NAME: \
36 : return #name;
37 :
38 0 : NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(COMPARE_NATIVE_CONTEXT_INTRINSIC_IDX)
39 :
40 : default:
41 : break;
42 : }
43 :
44 0 : return "UnknownIntrinsicIndex";
45 : }
46 :
47 : // static
48 : const char* const BytecodeExpectationsPrinter::kDefaultTopFunctionName =
49 : "__genbckexp_wrapper__";
50 : const char* const BytecodeExpectationsPrinter::kIndent = " ";
51 :
52 4315 : v8::Local<v8::String> BytecodeExpectationsPrinter::V8StringFromUTF8(
53 : const char* data) const {
54 4315 : return v8::String::NewFromUtf8(isolate_, data, v8::NewStringType::kNormal)
55 4315 : .ToLocalChecked();
56 : }
57 :
58 1435 : std::string BytecodeExpectationsPrinter::WrapCodeInFunction(
59 : const char* function_name, const std::string& function_body) const {
60 2870 : std::ostringstream program_stream;
61 1435 : program_stream << "function " << function_name << "() {" << function_body
62 : << "}\n"
63 1435 : << function_name << "();";
64 :
65 1435 : return program_stream.str();
66 : }
67 :
68 2165 : v8::Local<v8::Script> BytecodeExpectationsPrinter::CompileScript(
69 : const char* program) const {
70 2165 : v8::Local<v8::String> source = V8StringFromUTF8(program);
71 2165 : return v8::Script::Compile(isolate_->GetCurrentContext(), source)
72 2165 : .ToLocalChecked();
73 : }
74 :
75 55 : v8::Local<v8::Module> BytecodeExpectationsPrinter::CompileModule(
76 : const char* program) const {
77 : ScriptOrigin origin(
78 : Local<v8::Value>(), Local<v8::Integer>(), Local<v8::Integer>(),
79 : Local<v8::Boolean>(), Local<v8::Integer>(), Local<v8::Value>(),
80 55 : Local<v8::Boolean>(), Local<v8::Boolean>(), True(isolate_));
81 55 : v8::ScriptCompiler::Source source(V8StringFromUTF8(program), origin);
82 110 : return v8::ScriptCompiler::CompileModule(isolate_, &source).ToLocalChecked();
83 : }
84 :
85 2095 : void BytecodeExpectationsPrinter::Run(v8::Local<v8::Script> script) const {
86 4190 : MaybeLocal<Value> result = script->Run(isolate_->GetCurrentContext());
87 : USE(result);
88 2095 : }
89 :
90 : i::Handle<v8::internal::BytecodeArray>
91 2095 : BytecodeExpectationsPrinter::GetBytecodeArrayForGlobal(
92 : const char* global_name) const {
93 2095 : const v8::Local<v8::Context>& context = isolate_->GetCurrentContext();
94 2095 : v8::Local<v8::String> v8_global_name = V8StringFromUTF8(global_name);
95 : v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
96 6285 : context->Global()->Get(context, v8_global_name).ToLocalChecked());
97 : i::Handle<i::JSFunction> js_function =
98 : i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(*function));
99 :
100 : i::Handle<i::BytecodeArray> bytecodes =
101 4190 : i::handle(js_function->shared()->GetBytecodeArray(), i_isolate());
102 :
103 2095 : return bytecodes;
104 : }
105 :
106 : i::Handle<i::BytecodeArray>
107 55 : BytecodeExpectationsPrinter::GetBytecodeArrayForModule(
108 : v8::Local<v8::Module> module) const {
109 : i::Handle<i::Module> i_module = v8::Utils::OpenHandle(*module);
110 : return i::handle(
111 110 : SharedFunctionInfo::cast(i_module->code())->GetBytecodeArray(),
112 110 : i_isolate());
113 : }
114 :
115 : i::Handle<i::BytecodeArray>
116 70 : BytecodeExpectationsPrinter::GetBytecodeArrayForScript(
117 : v8::Local<v8::Script> script) const {
118 : i::Handle<i::JSFunction> js_function = v8::Utils::OpenHandle(*script);
119 140 : return i::handle(js_function->shared()->GetBytecodeArray(), i_isolate());
120 : }
121 :
122 : i::Handle<i::BytecodeArray>
123 105 : BytecodeExpectationsPrinter::GetBytecodeArrayOfCallee(
124 : const char* source_code) const {
125 : i::Handle<i::Object> i_object =
126 : v8::Utils::OpenHandle(*CompileRun(source_code));
127 : i::Handle<i::JSFunction> js_function =
128 : i::Handle<i::JSFunction>::cast(i_object);
129 105 : CHECK(js_function->shared()->HasBytecodeArray());
130 210 : return i::handle(js_function->shared()->GetBytecodeArray(), i_isolate());
131 : }
132 :
133 22985 : void BytecodeExpectationsPrinter::PrintEscapedString(
134 : std::ostream& stream, const std::string& string) const {
135 350190 : for (char c : string) {
136 327205 : switch (c) {
137 : case '"':
138 540 : stream << "\\\"";
139 540 : break;
140 : case '\\':
141 15 : stream << "\\\\";
142 15 : break;
143 : default:
144 : stream << c;
145 : break;
146 : }
147 : }
148 22985 : }
149 :
150 82585 : void BytecodeExpectationsPrinter::PrintBytecodeOperand(
151 : std::ostream& stream, const BytecodeArrayIterator& bytecode_iterator,
152 : const Bytecode& bytecode, int op_index, int parameter_count) const {
153 82585 : OperandType op_type = Bytecodes::GetOperandType(bytecode, op_index);
154 : OperandSize op_size = Bytecodes::GetOperandSize(
155 : bytecode, op_index, bytecode_iterator.current_operand_scale());
156 :
157 : const char* size_tag;
158 82585 : switch (op_size) {
159 : case OperandSize::kByte:
160 : size_tag = "8";
161 : break;
162 : case OperandSize::kShort:
163 : size_tag = "16";
164 2825 : break;
165 : case OperandSize::kQuad:
166 : size_tag = "32";
167 5 : break;
168 : default:
169 0 : UNREACHABLE();
170 : return;
171 : }
172 :
173 82585 : if (Bytecodes::IsRegisterOperandType(op_type)) {
174 41890 : Register register_value = bytecode_iterator.GetRegisterOperand(op_index);
175 : stream << 'R';
176 41890 : if (op_size != OperandSize::kByte) stream << size_tag;
177 41890 : if (register_value.is_current_context()) {
178 345 : stream << "(context)";
179 41545 : } else if (register_value.is_function_closure()) {
180 290 : stream << "(closure)";
181 41255 : } else if (register_value.is_parameter()) {
182 4925 : int parameter_index = register_value.ToParameterIndex(parameter_count);
183 4925 : if (parameter_index == 0) {
184 445 : stream << "(this)";
185 : } else {
186 8960 : stream << "(arg" << (parameter_index - 1) << ')';
187 : }
188 : } else {
189 36330 : stream << '(' << register_value.index() << ')';
190 : }
191 : } else {
192 40695 : switch (op_type) {
193 : case OperandType::kFlag8:
194 1095 : stream << 'U' << size_tag << '(';
195 1095 : stream << bytecode_iterator.GetFlagOperand(op_index);
196 : break;
197 : case OperandType::kIdx: {
198 24030 : stream << 'U' << size_tag << '(';
199 24030 : stream << bytecode_iterator.GetIndexOperand(op_index);
200 : break;
201 : }
202 : case OperandType::kUImm:
203 4415 : stream << 'U' << size_tag << '(';
204 4415 : stream << bytecode_iterator.GetUnsignedImmediateOperand(op_index);
205 : break;
206 : case OperandType::kImm:
207 6990 : stream << 'I' << size_tag << '(';
208 6990 : stream << bytecode_iterator.GetImmediateOperand(op_index);
209 6990 : break;
210 : case OperandType::kRegCount:
211 2425 : stream << 'U' << size_tag << '(';
212 2425 : stream << bytecode_iterator.GetRegisterCountOperand(op_index);
213 : break;
214 : case OperandType::kRuntimeId: {
215 1070 : stream << 'U' << size_tag << '(';
216 : Runtime::FunctionId id =
217 1070 : bytecode_iterator.GetRuntimeIdOperand(op_index);
218 2140 : stream << "Runtime::k" << i::Runtime::FunctionForId(id)->name;
219 1070 : break;
220 : }
221 : case OperandType::kIntrinsicId: {
222 655 : stream << 'U' << size_tag << '(';
223 : Runtime::FunctionId id =
224 655 : bytecode_iterator.GetIntrinsicIdOperand(op_index);
225 1310 : stream << "Runtime::k" << i::Runtime::FunctionForId(id)->name;
226 655 : break;
227 : }
228 : case OperandType::kNativeContextIndex: {
229 15 : stream << 'U' << size_tag << '(';
230 15 : uint32_t idx = bytecode_iterator.GetNativeContextIndexOperand(op_index);
231 30 : stream << "%" << NameForNativeContextIntrinsicIndex(idx);
232 15 : break;
233 : }
234 : default:
235 0 : UNREACHABLE();
236 : }
237 :
238 : stream << ')';
239 : }
240 82585 : }
241 :
242 68970 : void BytecodeExpectationsPrinter::PrintBytecode(
243 : std::ostream& stream, const BytecodeArrayIterator& bytecode_iterator,
244 : int parameter_count) const {
245 68970 : Bytecode bytecode = bytecode_iterator.current_bytecode();
246 : OperandScale operand_scale = bytecode_iterator.current_operand_scale();
247 68970 : if (Bytecodes::OperandScaleRequiresPrefixBytecode(operand_scale)) {
248 : Bytecode prefix = Bytecodes::OperandScaleToPrefixBytecode(operand_scale);
249 3140 : stream << "B(" << Bytecodes::ToString(prefix) << "), ";
250 : }
251 137940 : stream << "B(" << Bytecodes::ToString(bytecode) << ')';
252 68970 : int operands_count = Bytecodes::NumberOfOperands(bytecode);
253 234140 : for (int op_index = 0; op_index < operands_count; ++op_index) {
254 82585 : stream << ", ";
255 : PrintBytecodeOperand(stream, bytecode_iterator, bytecode, op_index,
256 82585 : parameter_count);
257 : }
258 68970 : }
259 :
260 68970 : void BytecodeExpectationsPrinter::PrintSourcePosition(
261 : std::ostream& stream, SourcePositionTableIterator& source_iterator,
262 : int bytecode_offset) const {
263 : static const size_t kPositionWidth = 4;
264 68970 : if (!source_iterator.done() &&
265 : source_iterator.code_offset() == bytecode_offset) {
266 : stream << "/* " << std::setw(kPositionWidth)
267 31130 : << source_iterator.source_position().ScriptOffset();
268 31130 : if (source_iterator.is_statement()) {
269 22085 : stream << " S> */ ";
270 : } else {
271 9045 : stream << " E> */ ";
272 : }
273 31130 : source_iterator.Advance();
274 : } else {
275 37840 : stream << " " << std::setw(kPositionWidth) << ' ' << " ";
276 : }
277 68970 : }
278 :
279 6575 : void BytecodeExpectationsPrinter::PrintV8String(std::ostream& stream,
280 : i::String string) const {
281 : stream << '"';
282 81655 : for (int i = 0, length = string->length(); i < length; ++i) {
283 37540 : stream << i::AsEscapedUC16ForJSON(string->Get(i));
284 : }
285 : stream << '"';
286 6575 : }
287 :
288 9130 : void BytecodeExpectationsPrinter::PrintConstant(
289 : std::ostream& stream, i::Handle<i::Object> constant) const {
290 9130 : if (constant->IsSmi()) {
291 725 : stream << "Smi [";
292 725 : i::Smi::cast(*constant)->SmiPrint(stream);
293 725 : stream << "]";
294 : } else {
295 8405 : stream << i::HeapObject::cast(*constant)->map()->instance_type();
296 8405 : if (constant->IsHeapNumber()) {
297 25 : stream << " [";
298 25 : i::HeapNumber::cast(*constant)->HeapNumberPrint(stream);
299 25 : stream << "]";
300 8380 : } else if (constant->IsString()) {
301 6575 : stream << " [";
302 6575 : PrintV8String(stream, i::String::cast(*constant));
303 6575 : stream << "]";
304 : }
305 : }
306 9130 : }
307 :
308 2325 : void BytecodeExpectationsPrinter::PrintFrameSize(
309 : std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
310 : int frame_size = bytecode_array->frame_size();
311 :
312 : DCHECK(IsAligned(frame_size, kSystemPointerSize));
313 4650 : stream << "frame size: " << frame_size / kSystemPointerSize
314 2325 : << "\nparameter count: " << bytecode_array->parameter_count() << '\n';
315 2325 : }
316 :
317 2325 : void BytecodeExpectationsPrinter::PrintBytecodeSequence(
318 : std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
319 2325 : stream << "bytecode array length: " << bytecode_array->length()
320 2325 : << "\nbytecodes: [\n";
321 :
322 : SourcePositionTableIterator source_iterator(
323 2325 : bytecode_array->SourcePositionTable());
324 2325 : BytecodeArrayIterator bytecode_iterator(bytecode_array);
325 140265 : for (; !bytecode_iterator.done(); bytecode_iterator.Advance()) {
326 68970 : stream << kIndent;
327 : PrintSourcePosition(stream, source_iterator,
328 68970 : bytecode_iterator.current_offset());
329 68970 : PrintBytecode(stream, bytecode_iterator, bytecode_array->parameter_count());
330 68970 : stream << ",\n";
331 : }
332 2325 : stream << "]\n";
333 2325 : }
334 :
335 2325 : void BytecodeExpectationsPrinter::PrintConstantPool(
336 : std::ostream& stream, i::FixedArray constant_pool) const {
337 2325 : stream << "constant pool: [\n";
338 : int num_constants = constant_pool->length();
339 2325 : if (num_constants > 0) {
340 19620 : for (int i = 0; i < num_constants; ++i) {
341 9130 : stream << kIndent;
342 9130 : PrintConstant(stream, i::FixedArray::get(constant_pool, i, i_isolate()));
343 9130 : stream << ",\n";
344 : }
345 : }
346 2325 : stream << "]\n";
347 2325 : }
348 :
349 2325 : void BytecodeExpectationsPrinter::PrintCodeSnippet(
350 : std::ostream& stream, const std::string& body) const {
351 2325 : stream << "snippet: \"\n";
352 4650 : std::stringstream body_stream(body);
353 : std::string body_line;
354 50620 : while (std::getline(body_stream, body_line)) {
355 22985 : stream << kIndent;
356 22985 : PrintEscapedString(stream, body_line);
357 : stream << '\n';
358 : }
359 2325 : stream << "\"\n";
360 2325 : }
361 :
362 2325 : void BytecodeExpectationsPrinter::PrintHandlers(
363 : std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
364 2325 : stream << "handlers: [\n";
365 2325 : HandlerTable table(*bytecode_array);
366 2660 : for (int i = 0, num_entries = table.NumberOfRangeEntries(); i < num_entries;
367 : ++i) {
368 1005 : stream << " [" << table.GetRangeStart(i) << ", " << table.GetRangeEnd(i)
369 670 : << ", " << table.GetRangeHandler(i) << "],\n";
370 : }
371 2325 : stream << "]\n";
372 2325 : }
373 :
374 2325 : void BytecodeExpectationsPrinter::PrintBytecodeArray(
375 : std::ostream& stream, i::Handle<i::BytecodeArray> bytecode_array) const {
376 2325 : PrintFrameSize(stream, bytecode_array);
377 2325 : PrintBytecodeSequence(stream, bytecode_array);
378 2325 : PrintConstantPool(stream, bytecode_array->constant_pool());
379 2325 : PrintHandlers(stream, bytecode_array);
380 2325 : }
381 :
382 2325 : void BytecodeExpectationsPrinter::PrintExpectation(
383 : std::ostream& stream, const std::string& snippet) const {
384 : std::string source_code =
385 2325 : wrap_ ? WrapCodeInFunction(test_function_name_.c_str(), snippet)
386 2325 : : snippet;
387 :
388 2325 : i::FLAG_enable_one_shot_optimization = oneshot_opt_;
389 2325 : i::FLAG_compilation_cache = false;
390 : i::Handle<i::BytecodeArray> bytecode_array;
391 2325 : if (module_) {
392 55 : CHECK(top_level_ && !wrap_);
393 55 : v8::Local<v8::Module> module = CompileModule(source_code.c_str());
394 55 : bytecode_array = GetBytecodeArrayForModule(module);
395 2270 : } else if (print_callee_) {
396 105 : bytecode_array = GetBytecodeArrayOfCallee(source_code.c_str());
397 : } else {
398 2165 : v8::Local<v8::Script> script = CompileScript(source_code.c_str());
399 2165 : if (top_level_) {
400 70 : bytecode_array = GetBytecodeArrayForScript(script);
401 : } else {
402 2095 : Run(script);
403 2095 : bytecode_array = GetBytecodeArrayForGlobal(test_function_name_.c_str());
404 : }
405 : }
406 :
407 2325 : stream << "---\n";
408 2325 : PrintCodeSnippet(stream, snippet);
409 2325 : PrintBytecodeArray(stream, bytecode_array);
410 : stream << '\n';
411 2325 : }
412 :
413 : } // namespace interpreter
414 : } // namespace internal
415 79917 : } // namespace v8
|