bytecode-expectations-printer.cc 14 KB
Newer Older
1 2 3 4 5 6
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "test/cctest/interpreter/bytecode-expectations-printer.h"

7
#include <iomanip>
8
#include <iostream>
9
#include <vector>
10 11 12

#include "include/libplatform/libplatform.h"
#include "include/v8.h"
13
#include "src/api/api-inl.h"
14
#include "src/base/logging.h"
15
#include "src/codegen/source-position-table.h"
16 17 18
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/bytecode-generator.h"
#include "src/interpreter/bytecodes.h"
19
#include "src/interpreter/interpreter-intrinsics.h"
20
#include "src/interpreter/interpreter.h"
21
#include "src/objects/heap-number-inl.h"
22
#include "src/objects/module-inl.h"
23
#include "src/objects/objects-inl.h"
24
#include "src/runtime/runtime.h"
25
#include "src/utils/ostreams.h"
26
#include "test/cctest/cctest.h"
27 28 29 30 31

namespace v8 {
namespace internal {
namespace interpreter {

32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
static const char* NameForNativeContextIntrinsicIndex(uint32_t idx) {
  switch (idx) {
#define COMPARE_NATIVE_CONTEXT_INTRINSIC_IDX(NAME, Type, name) \
  case Context::NAME:                                          \
    return #name;

    NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(COMPARE_NATIVE_CONTEXT_INTRINSIC_IDX)

    default:
      break;
  }

  return "UnknownIntrinsicIndex";
}

47 48 49
// static
const char* const BytecodeExpectationsPrinter::kDefaultTopFunctionName =
    "__genbckexp_wrapper__";
50
const char* const BytecodeExpectationsPrinter::kIndent = "  ";
51

52 53
v8::Local<v8::String> BytecodeExpectationsPrinter::V8StringFromUTF8(
    const char* data) const {
54
  return v8::String::NewFromUtf8(isolate_, data).ToLocalChecked();
55 56 57 58 59 60 61 62 63 64 65 66
}

std::string BytecodeExpectationsPrinter::WrapCodeInFunction(
    const char* function_name, const std::string& function_body) const {
  std::ostringstream program_stream;
  program_stream << "function " << function_name << "() {" << function_body
                 << "}\n"
                 << function_name << "();";

  return program_stream.str();
}

67
v8::Local<v8::Script> BytecodeExpectationsPrinter::CompileScript(
68 69
    const char* program) const {
  v8::Local<v8::String> source = V8StringFromUTF8(program);
70 71 72
  return v8::Script::Compile(isolate_->GetCurrentContext(), source)
      .ToLocalChecked();
}
73

74 75
v8::Local<v8::Module> BytecodeExpectationsPrinter::CompileModule(
    const char* program) const {
76 77
  ScriptOrigin origin(isolate_, Local<v8::Value>(), 0, 0, false, -1,
                      Local<v8::Value>(), false, false, true);
78
  v8::ScriptCompiler::Source source(V8StringFromUTF8(program), origin);
79 80 81
  return v8::ScriptCompiler::CompileModule(isolate_, &source).ToLocalChecked();
}

82
void BytecodeExpectationsPrinter::Run(v8::Local<v8::Script> script) const {
83 84
  MaybeLocal<Value> result = script->Run(isolate_->GetCurrentContext());
  USE(result);
85 86 87 88 89 90 91 92 93 94 95 96
}

i::Handle<v8::internal::BytecodeArray>
BytecodeExpectationsPrinter::GetBytecodeArrayForGlobal(
    const char* global_name) const {
  const v8::Local<v8::Context>& context = isolate_->GetCurrentContext();
  v8::Local<v8::String> v8_global_name = V8StringFromUTF8(global_name);
  v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
      context->Global()->Get(context, v8_global_name).ToLocalChecked());
  i::Handle<i::JSFunction> js_function =
      i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(*function));

97 98
  i::Handle<i::BytecodeArray> bytecodes = i::handle(
      js_function->shared().GetBytecodeArray(i_isolate()), i_isolate());
99 100 101 102

  return bytecodes;
}

103 104 105 106
i::Handle<i::BytecodeArray>
BytecodeExpectationsPrinter::GetBytecodeArrayForModule(
    v8::Local<v8::Module> module) const {
  i::Handle<i::Module> i_module = v8::Utils::OpenHandle(*module);
107 108
  return i::handle(SharedFunctionInfo::cast(
                       Handle<i::SourceTextModule>::cast(i_module)->code())
109
                       .GetBytecodeArray(i_isolate()),
110
                   i_isolate());
111 112
}

113 114 115 116
i::Handle<i::BytecodeArray>
BytecodeExpectationsPrinter::GetBytecodeArrayForScript(
    v8::Local<v8::Script> script) const {
  i::Handle<i::JSFunction> js_function = v8::Utils::OpenHandle(*script);
117 118
  return i::handle(js_function->shared().GetBytecodeArray(i_isolate()),
                   i_isolate());
119 120
}

121 122 123 124 125 126 127
i::Handle<i::BytecodeArray>
BytecodeExpectationsPrinter::GetBytecodeArrayOfCallee(
    const char* source_code) const {
  i::Handle<i::Object> i_object =
      v8::Utils::OpenHandle(*CompileRun(source_code));
  i::Handle<i::JSFunction> js_function =
      i::Handle<i::JSFunction>::cast(i_object);
128
  CHECK(js_function->shared().HasBytecodeArray());
129 130
  return i::handle(js_function->shared().GetBytecodeArray(i_isolate()),
                   i_isolate());
131 132
}

133
void BytecodeExpectationsPrinter::PrintEscapedString(
134
    std::ostream* stream, const std::string& string) const {
135 136 137
  for (char c : string) {
    switch (c) {
      case '"':
138
        *stream << "\\\"";
139 140
        break;
      case '\\':
141
        *stream << "\\\\";
142 143
        break;
      default:
144
        *stream << c;
145 146 147 148 149 150
        break;
    }
  }
}

void BytecodeExpectationsPrinter::PrintBytecodeOperand(
151
    std::ostream* stream, const BytecodeArrayIterator& bytecode_iterator,
152
    const Bytecode& bytecode, int op_index, int parameter_count) const {
153
  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 158 159 160 161 162 163 164

  const char* size_tag;
  switch (op_size) {
    case OperandSize::kByte:
      size_tag = "8";
      break;
    case OperandSize::kShort:
      size_tag = "16";
      break;
165 166 167
    case OperandSize::kQuad:
      size_tag = "32";
      break;
168 169 170 171 172
    default:
      UNREACHABLE();
  }

  if (Bytecodes::IsRegisterOperandType(op_type)) {
173
    Register register_value = bytecode_iterator.GetRegisterOperand(op_index);
174 175
    *stream << 'R';
    if (op_size != OperandSize::kByte) *stream << size_tag;
176
    if (register_value.is_current_context()) {
177
      *stream << "(context)";
178
    } else if (register_value.is_function_closure()) {
179
      *stream << "(closure)";
180 181 182
    } else if (register_value.is_parameter()) {
      int parameter_index = register_value.ToParameterIndex(parameter_count);
      if (parameter_index == 0) {
183
        *stream << "(this)";
184
      } else {
185
        *stream << "(arg" << (parameter_index - 1) << ')';
186 187
      }
    } else {
188
      *stream << '(' << register_value.index() << ')';
189
    }
190
  } else {
191 192
    switch (op_type) {
      case OperandType::kFlag8:
193 194
        *stream << 'U' << size_tag << '(';
        *stream << bytecode_iterator.GetFlagOperand(op_index);
195
        break;
196
      case OperandType::kIdx: {
197 198
        *stream << 'U' << size_tag << '(';
        *stream << bytecode_iterator.GetIndexOperand(op_index);
199
        break;
200
      }
201
      case OperandType::kUImm:
202 203
        *stream << 'U' << size_tag << '(';
        *stream << bytecode_iterator.GetUnsignedImmediateOperand(op_index);
204
        break;
205
      case OperandType::kImm:
206 207
        *stream << 'I' << size_tag << '(';
        *stream << bytecode_iterator.GetImmediateOperand(op_index);
208 209
        break;
      case OperandType::kRegCount:
210 211
        *stream << 'U' << size_tag << '(';
        *stream << bytecode_iterator.GetRegisterCountOperand(op_index);
212 213
        break;
      case OperandType::kRuntimeId: {
214
        *stream << 'U' << size_tag << '(';
215 216
        Runtime::FunctionId id =
            bytecode_iterator.GetRuntimeIdOperand(op_index);
217
        *stream << "Runtime::k" << i::Runtime::FunctionForId(id)->name;
218 219 220
        break;
      }
      case OperandType::kIntrinsicId: {
221
        *stream << 'U' << size_tag << '(';
222 223
        Runtime::FunctionId id =
            bytecode_iterator.GetIntrinsicIdOperand(op_index);
224
        *stream << "Runtime::k" << i::Runtime::FunctionForId(id)->name;
225
        break;
226 227
      }
      case OperandType::kNativeContextIndex: {
228
        *stream << 'U' << size_tag << '(';
229
        uint32_t idx = bytecode_iterator.GetNativeContextIndexOperand(op_index);
230
        *stream << "%" << NameForNativeContextIntrinsicIndex(idx);
231
        break;
232 233 234
      }
      default:
        UNREACHABLE();
235 236
    }

237
    *stream << ')';
238 239 240 241
  }
}

void BytecodeExpectationsPrinter::PrintBytecode(
242
    std::ostream* stream, const BytecodeArrayIterator& bytecode_iterator,
243
    int parameter_count) const {
244 245
  Bytecode bytecode = bytecode_iterator.current_bytecode();
  OperandScale operand_scale = bytecode_iterator.current_operand_scale();
246 247
  if (Bytecodes::OperandScaleRequiresPrefixBytecode(operand_scale)) {
    Bytecode prefix = Bytecodes::OperandScaleToPrefixBytecode(operand_scale);
248
    *stream << "B(" << Bytecodes::ToString(prefix) << "), ";
249
  }
250
  *stream << "B(" << Bytecodes::ToString(bytecode) << ')';
251 252
  int operands_count = Bytecodes::NumberOfOperands(bytecode);
  for (int op_index = 0; op_index < operands_count; ++op_index) {
253
    *stream << ", ";
254
    PrintBytecodeOperand(stream, bytecode_iterator, bytecode, op_index,
255
                         parameter_count);
256 257 258
  }
}

259
void BytecodeExpectationsPrinter::PrintSourcePosition(
260
    std::ostream* stream, SourcePositionTableIterator* source_iterator,
261 262
    int bytecode_offset) const {
  static const size_t kPositionWidth = 4;
263 264 265 266 267 268
  if (!source_iterator->done() &&
      source_iterator->code_offset() == bytecode_offset) {
    *stream << "/* " << std::setw(kPositionWidth)
            << source_iterator->source_position().ScriptOffset();
    if (source_iterator->is_statement()) {
      *stream << " S> */ ";
269
    } else {
270
      *stream << " E> */ ";
271
    }
272
    source_iterator->Advance();
273
  } else {
274
    *stream << "   " << std::setw(kPositionWidth) << ' ' << "       ";
275 276 277
  }
}

278
void BytecodeExpectationsPrinter::PrintV8String(std::ostream* stream,
279
                                                i::String string) const {
280
  *stream << '"';
281
  for (int i = 0, length = string.length(); i < length; ++i) {
282
    *stream << i::AsEscapedUC16ForJSON(string.Get(i));
283
  }
284
  *stream << '"';
285 286 287
}

void BytecodeExpectationsPrinter::PrintConstant(
288
    std::ostream* stream, i::Handle<i::Object> constant) const {
289
  if (constant->IsSmi()) {
290 291 292
    *stream << "Smi [";
    i::Smi::cast(*constant).SmiPrint(*stream);
    *stream << "]";
293
  } else {
294
    *stream << i::HeapObject::cast(*constant).map().instance_type();
295
    if (constant->IsHeapNumber()) {
296
      *stream << " [";
297
      i::HeapNumber::cast(*constant).HeapNumberShortPrint(*stream);
298
      *stream << "]";
299
    } else if (constant->IsString()) {
300
      *stream << " [";
301
      PrintV8String(stream, i::String::cast(*constant));
302
      *stream << "]";
303
    }
304 305 306 307
  }
}

void BytecodeExpectationsPrinter::PrintFrameSize(
308
    std::ostream* stream, i::Handle<i::BytecodeArray> bytecode_array) const {
309
  int32_t frame_size = bytecode_array->frame_size();
310

311
  DCHECK(IsAligned(frame_size, kSystemPointerSize));
312 313
  *stream << "frame size: " << frame_size / kSystemPointerSize
          << "\nparameter count: " << bytecode_array->parameter_count() << '\n';
314 315 316
}

void BytecodeExpectationsPrinter::PrintBytecodeSequence(
317 318 319
    std::ostream* stream, i::Handle<i::BytecodeArray> bytecode_array) const {
  *stream << "bytecode array length: " << bytecode_array->length()
          << "\nbytecodes: [\n";
320 321

  SourcePositionTableIterator source_iterator(
322
      bytecode_array->SourcePositionTable());
323 324
  BytecodeArrayIterator bytecode_iterator(bytecode_array);
  for (; !bytecode_iterator.done(); bytecode_iterator.Advance()) {
325 326
    *stream << kIndent;
    PrintSourcePosition(stream, &source_iterator,
327 328
                        bytecode_iterator.current_offset());
    PrintBytecode(stream, bytecode_iterator, bytecode_array->parameter_count());
329
    *stream << ",\n";
330
  }
331
  *stream << "]\n";
332 333 334
}

void BytecodeExpectationsPrinter::PrintConstantPool(
335 336
    std::ostream* stream, i::FixedArray constant_pool) const {
  *stream << "constant pool: [\n";
337
  int num_constants = constant_pool.length();
338 339
  if (num_constants > 0) {
    for (int i = 0; i < num_constants; ++i) {
340
      *stream << kIndent;
341
      PrintConstant(stream, i::FixedArray::get(constant_pool, i, i_isolate()));
342
      *stream << ",\n";
343 344
    }
  }
345
  *stream << "]\n";
346 347 348
}

void BytecodeExpectationsPrinter::PrintCodeSnippet(
349 350
    std::ostream* stream, const std::string& body) const {
  *stream << "snippet: \"\n";
351 352 353
  std::stringstream body_stream(body);
  std::string body_line;
  while (std::getline(body_stream, body_line)) {
354
    *stream << kIndent;
355
    PrintEscapedString(stream, body_line);
356
    *stream << '\n';
357
  }
358
  *stream << "\"\n";
359 360
}

361
void BytecodeExpectationsPrinter::PrintHandlers(
362 363
    std::ostream* stream, i::Handle<i::BytecodeArray> bytecode_array) const {
  *stream << "handlers: [\n";
364 365
  HandlerTable table(*bytecode_array);
  for (int i = 0, num_entries = table.NumberOfRangeEntries(); i < num_entries;
366
       ++i) {
367 368
    *stream << "  [" << table.GetRangeStart(i) << ", " << table.GetRangeEnd(i)
            << ", " << table.GetRangeHandler(i) << "],\n";
369
  }
370
  *stream << "]\n";
371 372
}

373
void BytecodeExpectationsPrinter::PrintBytecodeArray(
374
    std::ostream* stream, i::Handle<i::BytecodeArray> bytecode_array) const {
375 376 377
  PrintFrameSize(stream, bytecode_array);
  PrintBytecodeSequence(stream, bytecode_array);
  PrintConstantPool(stream, bytecode_array->constant_pool());
378
  PrintHandlers(stream, bytecode_array);
379 380 381
}

void BytecodeExpectationsPrinter::PrintExpectation(
382
    std::ostream* stream, const std::string& snippet) const {
383 384 385
  std::string source_code =
      wrap_ ? WrapCodeInFunction(test_function_name_.c_str(), snippet)
            : snippet;
386

387
  i::FLAG_compilation_cache = false;
388
  i::Handle<i::BytecodeArray> bytecode_array;
389 390 391 392
  if (module_) {
    CHECK(top_level_ && !wrap_);
    v8::Local<v8::Module> module = CompileModule(source_code.c_str());
    bytecode_array = GetBytecodeArrayForModule(module);
393 394
  } else if (print_callee_) {
    bytecode_array = GetBytecodeArrayOfCallee(source_code.c_str());
395
  } else {
396 397 398 399 400 401 402
    v8::Local<v8::Script> script = CompileScript(source_code.c_str());
    if (top_level_) {
      bytecode_array = GetBytecodeArrayForScript(script);
    } else {
      Run(script);
      bytecode_array = GetBytecodeArrayForGlobal(test_function_name_.c_str());
    }
403
  }
404

405
  *stream << "---\n";
406 407
  PrintCodeSnippet(stream, snippet);
  PrintBytecodeArray(stream, bytecode_array);
408
  *stream << '\n';
409 410 411 412 413
}

}  // namespace interpreter
}  // namespace internal
}  // namespace v8