// 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" #include #include #include #include "include/libplatform/libplatform.h" #include "include/v8.h" #include "src/api/api-inl.h" #include "src/base/logging.h" #include "src/codegen/source-position-table.h" #include "src/interpreter/bytecode-array-iterator.h" #include "src/interpreter/bytecode-generator.h" #include "src/interpreter/bytecodes.h" #include "src/interpreter/interpreter-intrinsics.h" #include "src/interpreter/interpreter.h" #include "src/objects/heap-number-inl.h" #include "src/objects/module-inl.h" #include "src/objects/objects-inl.h" #include "src/runtime/runtime.h" #include "src/utils/ostreams.h" #include "test/cctest/cctest.h" namespace v8 { namespace internal { namespace interpreter { 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"; } // static const char* const BytecodeExpectationsPrinter::kDefaultTopFunctionName = "__genbckexp_wrapper__"; const char* const BytecodeExpectationsPrinter::kIndent = " "; v8::Local BytecodeExpectationsPrinter::V8StringFromUTF8( const char* data) const { return v8::String::NewFromUtf8(isolate_, data, v8::NewStringType::kNormal) .ToLocalChecked(); } 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(); } v8::Local BytecodeExpectationsPrinter::CompileScript( const char* program) const { v8::Local source = V8StringFromUTF8(program); return v8::Script::Compile(isolate_->GetCurrentContext(), source) .ToLocalChecked(); } v8::Local BytecodeExpectationsPrinter::CompileModule( const char* program) const { ScriptOrigin origin( Local(), Local(), Local(), Local(), Local(), Local(), Local(), Local(), True(isolate_)); v8::ScriptCompiler::Source source(V8StringFromUTF8(program), origin); return v8::ScriptCompiler::CompileModule(isolate_, &source).ToLocalChecked(); } void BytecodeExpectationsPrinter::Run(v8::Local script) const { MaybeLocal result = script->Run(isolate_->GetCurrentContext()); USE(result); } i::Handle BytecodeExpectationsPrinter::GetBytecodeArrayForGlobal( const char* global_name) const { const v8::Local& context = isolate_->GetCurrentContext(); v8::Local v8_global_name = V8StringFromUTF8(global_name); v8::Local function = v8::Local::Cast( context->Global()->Get(context, v8_global_name).ToLocalChecked()); i::Handle js_function = i::Handle::cast(v8::Utils::OpenHandle(*function)); i::Handle bytecodes = i::handle(js_function->shared().GetBytecodeArray(), i_isolate()); return bytecodes; } i::Handle BytecodeExpectationsPrinter::GetBytecodeArrayForModule( v8::Local module) const { i::Handle i_module = v8::Utils::OpenHandle(*module); return i::handle( SharedFunctionInfo::cast(i_module->code()).GetBytecodeArray(), i_isolate()); } i::Handle BytecodeExpectationsPrinter::GetBytecodeArrayForScript( v8::Local script) const { i::Handle js_function = v8::Utils::OpenHandle(*script); return i::handle(js_function->shared().GetBytecodeArray(), i_isolate()); } i::Handle BytecodeExpectationsPrinter::GetBytecodeArrayOfCallee( const char* source_code) const { i::Handle i_object = v8::Utils::OpenHandle(*CompileRun(source_code)); i::Handle js_function = i::Handle::cast(i_object); CHECK(js_function->shared().HasBytecodeArray()); return i::handle(js_function->shared().GetBytecodeArray(), i_isolate()); } void BytecodeExpectationsPrinter::PrintEscapedString( std::ostream& stream, const std::string& string) const { for (char c : string) { switch (c) { case '"': stream << "\\\""; break; case '\\': stream << "\\\\"; break; default: stream << c; break; } } } void BytecodeExpectationsPrinter::PrintBytecodeOperand( std::ostream& stream, const BytecodeArrayIterator& bytecode_iterator, const Bytecode& bytecode, int op_index, int parameter_count) const { OperandType op_type = Bytecodes::GetOperandType(bytecode, op_index); OperandSize op_size = Bytecodes::GetOperandSize( bytecode, op_index, bytecode_iterator.current_operand_scale()); const char* size_tag; switch (op_size) { case OperandSize::kByte: size_tag = "8"; break; case OperandSize::kShort: size_tag = "16"; break; case OperandSize::kQuad: size_tag = "32"; break; default: UNREACHABLE(); } if (Bytecodes::IsRegisterOperandType(op_type)) { Register register_value = bytecode_iterator.GetRegisterOperand(op_index); stream << 'R'; if (op_size != OperandSize::kByte) stream << size_tag; if (register_value.is_current_context()) { stream << "(context)"; } else if (register_value.is_function_closure()) { stream << "(closure)"; } else if (register_value.is_parameter()) { int parameter_index = register_value.ToParameterIndex(parameter_count); if (parameter_index == 0) { stream << "(this)"; } else { stream << "(arg" << (parameter_index - 1) << ')'; } } else { stream << '(' << register_value.index() << ')'; } } else { switch (op_type) { case OperandType::kFlag8: stream << 'U' << size_tag << '('; stream << bytecode_iterator.GetFlagOperand(op_index); break; case OperandType::kIdx: { stream << 'U' << size_tag << '('; stream << bytecode_iterator.GetIndexOperand(op_index); break; } case OperandType::kUImm: stream << 'U' << size_tag << '('; stream << bytecode_iterator.GetUnsignedImmediateOperand(op_index); break; case OperandType::kImm: stream << 'I' << size_tag << '('; stream << bytecode_iterator.GetImmediateOperand(op_index); break; case OperandType::kRegCount: stream << 'U' << size_tag << '('; stream << bytecode_iterator.GetRegisterCountOperand(op_index); break; case OperandType::kRuntimeId: { stream << 'U' << size_tag << '('; Runtime::FunctionId id = bytecode_iterator.GetRuntimeIdOperand(op_index); stream << "Runtime::k" << i::Runtime::FunctionForId(id)->name; break; } case OperandType::kIntrinsicId: { stream << 'U' << size_tag << '('; Runtime::FunctionId id = bytecode_iterator.GetIntrinsicIdOperand(op_index); stream << "Runtime::k" << i::Runtime::FunctionForId(id)->name; break; } case OperandType::kNativeContextIndex: { stream << 'U' << size_tag << '('; uint32_t idx = bytecode_iterator.GetNativeContextIndexOperand(op_index); stream << "%" << NameForNativeContextIntrinsicIndex(idx); break; } default: UNREACHABLE(); } stream << ')'; } } void BytecodeExpectationsPrinter::PrintBytecode( std::ostream& stream, const BytecodeArrayIterator& bytecode_iterator, int parameter_count) const { Bytecode bytecode = bytecode_iterator.current_bytecode(); OperandScale operand_scale = bytecode_iterator.current_operand_scale(); if (Bytecodes::OperandScaleRequiresPrefixBytecode(operand_scale)) { Bytecode prefix = Bytecodes::OperandScaleToPrefixBytecode(operand_scale); stream << "B(" << Bytecodes::ToString(prefix) << "), "; } stream << "B(" << Bytecodes::ToString(bytecode) << ')'; int operands_count = Bytecodes::NumberOfOperands(bytecode); for (int op_index = 0; op_index < operands_count; ++op_index) { stream << ", "; PrintBytecodeOperand(stream, bytecode_iterator, bytecode, op_index, parameter_count); } } void BytecodeExpectationsPrinter::PrintSourcePosition( std::ostream& stream, SourcePositionTableIterator& source_iterator, int bytecode_offset) const { static const size_t kPositionWidth = 4; 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> */ "; } else { stream << " E> */ "; } source_iterator.Advance(); } else { stream << " " << std::setw(kPositionWidth) << ' ' << " "; } } void BytecodeExpectationsPrinter::PrintV8String(std::ostream& stream, i::String string) const { stream << '"'; for (int i = 0, length = string.length(); i < length; ++i) { stream << i::AsEscapedUC16ForJSON(string.Get(i)); } stream << '"'; } void BytecodeExpectationsPrinter::PrintConstant( std::ostream& stream, i::Handle constant) const { if (constant->IsSmi()) { stream << "Smi ["; i::Smi::cast(*constant).SmiPrint(stream); stream << "]"; } else { stream << i::HeapObject::cast(*constant).map().instance_type(); if (constant->IsHeapNumber()) { stream << " ["; i::HeapNumber::cast(*constant).HeapNumberPrint(stream); stream << "]"; } else if (constant->IsString()) { stream << " ["; PrintV8String(stream, i::String::cast(*constant)); stream << "]"; } } } void BytecodeExpectationsPrinter::PrintFrameSize( std::ostream& stream, i::Handle bytecode_array) const { int32_t frame_size = bytecode_array->frame_size(); DCHECK(IsAligned(frame_size, kSystemPointerSize)); stream << "frame size: " << frame_size / kSystemPointerSize << "\nparameter count: " << bytecode_array->parameter_count() << '\n'; } void BytecodeExpectationsPrinter::PrintBytecodeSequence( std::ostream& stream, i::Handle bytecode_array) const { stream << "bytecode array length: " << bytecode_array->length() << "\nbytecodes: [\n"; SourcePositionTableIterator source_iterator( bytecode_array->SourcePositionTable()); BytecodeArrayIterator bytecode_iterator(bytecode_array); for (; !bytecode_iterator.done(); bytecode_iterator.Advance()) { stream << kIndent; PrintSourcePosition(stream, source_iterator, bytecode_iterator.current_offset()); PrintBytecode(stream, bytecode_iterator, bytecode_array->parameter_count()); stream << ",\n"; } stream << "]\n"; } void BytecodeExpectationsPrinter::PrintConstantPool( std::ostream& stream, i::FixedArray constant_pool) const { stream << "constant pool: [\n"; int num_constants = constant_pool.length(); if (num_constants > 0) { for (int i = 0; i < num_constants; ++i) { stream << kIndent; PrintConstant(stream, i::FixedArray::get(constant_pool, i, i_isolate())); stream << ",\n"; } } stream << "]\n"; } void BytecodeExpectationsPrinter::PrintCodeSnippet( std::ostream& stream, const std::string& body) const { stream << "snippet: \"\n"; std::stringstream body_stream(body); std::string body_line; while (std::getline(body_stream, body_line)) { stream << kIndent; PrintEscapedString(stream, body_line); stream << '\n'; } stream << "\"\n"; } void BytecodeExpectationsPrinter::PrintHandlers( std::ostream& stream, i::Handle bytecode_array) const { stream << "handlers: [\n"; HandlerTable table(*bytecode_array); for (int i = 0, num_entries = table.NumberOfRangeEntries(); i < num_entries; ++i) { stream << " [" << table.GetRangeStart(i) << ", " << table.GetRangeEnd(i) << ", " << table.GetRangeHandler(i) << "],\n"; } stream << "]\n"; } void BytecodeExpectationsPrinter::PrintBytecodeArray( std::ostream& stream, i::Handle bytecode_array) const { PrintFrameSize(stream, bytecode_array); PrintBytecodeSequence(stream, bytecode_array); PrintConstantPool(stream, bytecode_array->constant_pool()); PrintHandlers(stream, bytecode_array); } void BytecodeExpectationsPrinter::PrintExpectation( std::ostream& stream, const std::string& snippet) const { std::string source_code = wrap_ ? WrapCodeInFunction(test_function_name_.c_str(), snippet) : snippet; i::FLAG_enable_one_shot_optimization = oneshot_opt_; i::FLAG_compilation_cache = false; i::Handle bytecode_array; if (module_) { CHECK(top_level_ && !wrap_); v8::Local module = CompileModule(source_code.c_str()); bytecode_array = GetBytecodeArrayForModule(module); } else if (print_callee_) { bytecode_array = GetBytecodeArrayOfCallee(source_code.c_str()); } else { v8::Local script = CompileScript(source_code.c_str()); if (top_level_) { bytecode_array = GetBytecodeArrayForScript(script); } else { Run(script); bytecode_array = GetBytecodeArrayForGlobal(test_function_name_.c_str()); } } stream << "---\n"; PrintCodeSnippet(stream, snippet); PrintBytecodeArray(stream, bytecode_array); stream << '\n'; } } // namespace interpreter } // namespace internal } // namespace v8