blob: 9ec834a61fabd7c990dcd93e02ae2f387ead857d [file]
// Copyright 2023 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 "src/objects/bytecode-array.h"
#include <iomanip>
#include <sstream>
#include "src/base/string-format.h"
#include "src/codegen/handler-table.h"
#include "src/codegen/source-position-table.h"
#include "src/common/globals.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/bytecode-decoder.h"
#include "src/objects/bytecode-array-inl.h"
#include "src/utils/memcopy.h"
namespace v8 {
namespace internal {
int BytecodeArray::SourcePosition(int offset) const {
int position = 0;
if (!HasSourcePositionTable()) return position;
for (SourcePositionTableIterator it(
source_position_table(kAcquireLoad),
SourcePositionTableIterator::kJavaScriptOnly,
SourcePositionTableIterator::kDontSkipFunctionEntry);
!it.done() && it.code_offset() <= offset; it.Advance()) {
position = it.source_position().ScriptOffset();
}
return position;
}
int BytecodeArray::SourceStatementPosition(int offset) const {
int position = 0;
if (!HasSourcePositionTable()) return position;
for (SourcePositionTableIterator it(source_position_table(kAcquireLoad));
!it.done() && it.code_offset() <= offset; it.Advance()) {
if (it.is_statement()) {
position = it.source_position().ScriptOffset();
}
}
return position;
}
void BytecodeArray::PrintJson(std::ostream& os) {
DisallowGarbageCollection no_gc;
Tagged<BytecodeArray> handle_storage(this);
Handle<BytecodeArray> handle(reinterpret_cast<Address*>(&handle_storage));
interpreter::BytecodeArrayIterator iterator(handle);
bool first_data = true;
os << "{\"data\": [";
while (!iterator.done()) {
if (!first_data) os << ", ";
first_data = false;
os << "{\"offset\":" << iterator.current_offset() << ", \"disassembly\":\"";
std::stringstream disassembly_stream;
iterator.PrintCurrentBytecodeTo(disassembly_stream);
if (interpreter::Bytecodes::IsJump(iterator.current_bytecode())) {
disassembly_stream << " (" << iterator.GetJumpTargetOffset() << ")";
}
if (interpreter::Bytecodes::IsSwitch(iterator.current_bytecode())) {
disassembly_stream << " {";
bool first_entry = true;
for (interpreter::JumpTableTargetOffset entry :
iterator.GetJumpTableTargetOffsets()) {
if (!first_entry) disassembly_stream << ", ";
first_entry = false;
disassembly_stream << entry.target_offset;
}
disassembly_stream << "}";
}
os << base::JSONEscaped(disassembly_stream.str()) << "\"}";
iterator.Advance();
}
os << "]";
uint32_t constant_pool_length = constant_pool()->ulength().value();
if (constant_pool_length > 0) {
os << ", \"constantPool\": [";
for (uint32_t i = 0; i < constant_pool_length; i++) {
Tagged<Object> object = constant_pool()->get(i);
if (i > 0) os << ", ";
os << "\"" << base::JSONEscaped(object) << "\"";
}
os << "]";
}
os << "}";
}
void BytecodeArray::Disassemble(std::ostream& os) {
DisallowGarbageCollection no_gc;
// Storage for backing the handle passed to the iterator. This handle won't be
// updated by the gc, but that's ok because we've disallowed GCs anyway.
Tagged<BytecodeArray> handle_storage(this);
Handle<BytecodeArray> handle(reinterpret_cast<Address*>(&handle_storage));
Disassemble(handle, os);
}
// static
void BytecodeArray::Disassemble(Handle<BytecodeArray> handle,
std::ostream& os) {
DisallowGarbageCollection no_gc;
os << "Parameter count " << handle->parameter_count() << "\n";
os << "Register count " << handle->register_count() << "\n";
os << "Frame size " << handle->frame_size() << "\n";
Address base_address = handle->GetFirstBytecodeAddress();
SourcePositionTableIterator source_positions(handle->SourcePositionTable());
interpreter::BytecodeArrayIterator iterator(handle);
while (!iterator.done()) {
if (!source_positions.done() &&
iterator.current_offset() == source_positions.code_offset()) {
os << std::setw(5) << source_positions.source_position().ScriptOffset();
if (source_positions.is_breakable()) {
os << (source_positions.is_statement() ? " S> " : " E> ");
} else {
os << (source_positions.is_statement() ? " s> " : " e> ");
}
source_positions.Advance();
} else {
os << " ";
}
os << reinterpret_cast<const void*>(base_address +
iterator.current_offset())
<< " @ " << std::setw(4) << iterator.current_offset() << " : ";
iterator.PrintCurrentBytecodeTo(os);
if (interpreter::Bytecodes::IsJump(iterator.current_bytecode())) {
Address jump_target = base_address + iterator.GetJumpTargetOffset();
os << " (" << reinterpret_cast<void*>(jump_target) << " @ "
<< iterator.GetJumpTargetOffset() << ")";
}
if (interpreter::Bytecodes::IsSwitch(iterator.current_bytecode())) {
os << " {";
bool first_entry = true;
for (interpreter::JumpTableTargetOffset entry :
iterator.GetJumpTableTargetOffsets()) {
if (first_entry) {
first_entry = false;
} else {
os << ",";
}
os << " " << entry.case_value << ": @" << entry.target_offset;
}
os << " }";
}
os << std::endl;
iterator.Advance();
}
os << "Constant pool (size = " << handle->constant_pool()->ulength().value()
<< ")\n";
#ifdef OBJECT_PRINT
if (handle->constant_pool()->ulength().value() > 0) {
Print(handle->constant_pool(), os);
}
#endif
os << "Handler Table (size = " << handle->handler_table()->ulength().value()
<< ")\n";
#ifdef ENABLE_DISASSEMBLER
if (handle->handler_table()->ulength().value() > 0) {
HandlerTable table(*handle);
table.HandlerTableRangePrint(os);
}
#endif
Tagged<TrustedByteArray> source_position_table =
handle->SourcePositionTable();
os << "Source Position Table (size = "
<< source_position_table->ulength().value() << ")\n";
#ifdef OBJECT_PRINT
if (source_position_table->ulength().value() > 0) {
os << Brief(source_position_table) << std::endl;
}
#endif
}
void BytecodeArray::CopyBytecodesTo(Tagged<BytecodeArray> to) {
DCHECK_EQ(length(), to->length());
CopyBytes(reinterpret_cast<uint8_t*>(to->GetFirstBytecodeAddress()),
reinterpret_cast<uint8_t*>(GetFirstBytecodeAddress()), length());
}
} // namespace internal
} // namespace v8