LibJS: Remove obsolete bytecode dump formatting helpers

The Rust bytecode dumper now formats exception handler labels, raw
operands, builtins, labels, and registers. Remove the C++ dump-only
formatters and flatten Operand to expose only the runtime value-array
layout that C++ still observes.
This commit is contained in:
Andreas Kling 2026-06-14 18:35:15 +02:00 committed by Andreas Kling
parent 7a6af95db3
commit 984d3033e9
9 changed files with 23 additions and 195 deletions

View file

@ -6,7 +6,8 @@
#pragma once
#include <AK/Format.h>
#include <AK/Assertions.h>
#include <AK/Types.h>
#include <LibJS/Forward.h>
namespace JS::Bytecode {
@ -45,20 +46,6 @@ enum class Builtin : u8 {
__Count,
};
static StringView builtin_name(Builtin value)
{
switch (value) {
#define DEFINE_BUILTIN_CASE(name, snake_case_name, base, property, ...) \
case Builtin::name: \
return #base "." #property##sv;
JS_ENUMERATE_BUILTINS(DEFINE_BUILTIN_CASE)
#undef DEFINE_BUILTIN_CASE
case Builtin::__Count:
VERIFY_NOT_REACHED();
}
VERIFY_NOT_REACHED();
}
inline size_t builtin_argument_count(Builtin value)
{
switch (value) {
@ -74,15 +61,3 @@ inline size_t builtin_argument_count(Builtin value)
}
}
namespace AK {
template<>
struct Formatter<JS::Bytecode::Builtin> : Formatter<StringView> {
ErrorOr<void> format(FormatBuilder& builder, JS::Bytecode::Builtin value)
{
return Formatter<StringView>::format(builder, builtin_name(value));
}
};
}

View file

@ -12,7 +12,6 @@
#include <LibGC/Heap.h>
#include <LibGC/HeapBlock.h>
#include <LibJS/Bytecode/Executable.h>
#include <LibJS/Bytecode/FormatOperand.h>
#include <LibJS/Bytecode/Instruction.h>
#include <LibJS/Bytecode/Op.h>
#include <LibJS/Bytecode/RegexTable.h>
@ -500,15 +499,6 @@ void Executable::dump() const
output.append('\n');
RustIntegration::dump_bytecode(output, *this);
if (!exception_handlers.is_empty()) {
output.append("\nException handlers:\n"sv);
for (auto const& handler : exception_handlers) {
output.appendff(" [{:4x} .. {:4x}] => handler ", handler.start_offset, handler.end_offset);
Label handler_label(static_cast<u32>(handler.handler_offset));
output.appendff("{}\n", format_label(""sv, handler_label, *this));
}
}
output.append('\n');
warnln("{}", output.string_view());
}
@ -693,16 +683,4 @@ SourceRange const& Executable::get_source_range(u32 program_counter)
});
}
Operand Executable::original_operand_from_raw(u32 raw) const
{
// NB: Layout is [registers | locals | constants | arguments]
if (raw < number_of_registers)
return Operand { Operand::Type::Register, raw };
if (raw < registers_and_locals_count)
return Operand { Operand::Type::Local, raw - local_index_base };
if (raw < argument_index_base)
return Operand { Operand::Type::Constant, raw - registers_and_locals_count };
return Operand { Operand::Type::Argument, raw - argument_index_base };
}
}

View file

@ -356,7 +356,6 @@ public:
[[nodiscard]] SourceRange const& get_source_range(u32 program_counter);
void dump() const;
[[nodiscard]] Operand original_operand_from_raw(u32) const;
virtual Cell const& owner_cell(Badge<GC::Heap>) const override { return *this; }
virtual void remove_dead_cells(Badge<GC::Heap>) override;

View file

@ -1,84 +0,0 @@
/*
* Copyright (c) 2021-2025, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/ByteString.h>
#include <LibJS/Bytecode/Executable.h>
#include <LibJS/Bytecode/Operand.h>
#include <LibJS/Bytecode/Register.h>
#include <LibJS/Runtime/BigInt.h>
#include <LibJS/Runtime/PrimitiveString.h>
#include <LibJS/Runtime/Value.h>
namespace JS::Bytecode {
inline ByteString format_label(StringView name, Label const& label, Bytecode::Executable const& executable)
{
StringBuilder builder;
if (!name.is_empty())
builder.appendff("\033[32m{}\033[0m:", name);
auto address = label.address();
if (auto basic_block_index = executable.basic_block_index_for_offset(address); basic_block_index.has_value()) {
builder.appendff("\033[35mblock{}\033[0m", basic_block_index.value());
return builder.to_byte_string();
}
builder.appendff("@{:x}", address);
return builder.to_byte_string();
}
inline ByteString format_operand(StringView name, Operand encoded_operand, Bytecode::Executable const& executable)
{
StringBuilder builder;
if (!name.is_empty())
builder.appendff("\033[32m{}\033[0m:", name);
auto operand = executable.original_operand_from_raw(encoded_operand.raw());
switch (operand.type()) {
case Operand::Type::Register:
if (operand.index() == Register::this_value().index()) {
builder.appendff("\033[33mthis\033[0m");
} else {
builder.appendff("\033[33mreg{}\033[0m", operand.index());
}
break;
case Operand::Type::Local:
builder.appendff("\033[34m{}~{}\033[0m", executable.local_variable_names[operand.index()].name, operand.index());
break;
case Operand::Type::Argument:
builder.appendff("\033[34marg{}\033[0m", operand.index());
break;
case Operand::Type::Constant: {
builder.append("\033[36m"sv);
auto value = executable.constants[operand.index()];
if (value.is_special_empty_value())
builder.append("<Empty>"sv);
else if (value.is_boolean())
builder.appendff("Bool({})", value.as_bool() ? "true"sv : "false"sv);
else if (value.is_int32())
builder.appendff("Int32({})", value.as_i32());
else if (value.is_double())
builder.appendff("Double({})", value.as_double());
else if (value.is_bigint())
builder.appendff("BigInt({})", MUST(value.as_bigint().to_string()));
else if (value.is_string())
builder.appendff("String(\"{}\")", value.as_string().utf8_string_view());
else if (value.is_undefined())
builder.append("Undefined"sv);
else if (value.is_null())
builder.append("Null"sv);
else
builder.appendff("Value: {}", value);
builder.append("\033[0m"sv);
break;
}
default:
VERIFY_NOT_REACHED();
}
return builder.to_byte_string();
}
}

View file

@ -78,9 +78,4 @@ size_t Instruction::length() const
#undef __BYTECODE_OP
}
Operand::Operand(Register reg)
: Operand(Type::Register, reg.index())
{
}
}

View file

@ -6,7 +6,7 @@
#pragma once
#include <AK/Format.h>
#include <AK/Types.h>
namespace JS::Bytecode {
@ -26,11 +26,3 @@ private:
};
}
template<>
struct AK::Formatter<JS::Bytecode::Label> : AK::Formatter<FormatString> {
ErrorOr<void> format(FormatBuilder& builder, JS::Bytecode::Label const& label)
{
return AK::Formatter<FormatString>::format(builder, "@{:x}"sv, label.address());
}
};

View file

@ -13,53 +13,17 @@ namespace JS::Bytecode {
class Operand {
public:
enum class Type {
Register,
Local,
Constant,
Argument,
};
[[nodiscard]] bool operator==(Operand const&) const = default;
explicit Operand(Type type, u32 index)
: m_raw(to_underlying(type) << 29 | index)
{
}
enum class ShouldMakeInvalid { Indeed };
explicit Operand(ShouldMakeInvalid)
: m_raw(0xffffffffu)
{
}
explicit Operand(Register);
static Operand from_raw(u32 raw)
{
Operand operand;
operand.m_raw = raw;
return operand;
}
[[nodiscard]] bool is_invalid() const { return m_raw == 0xffffffffu; }
[[nodiscard]] bool is_register() const { return type() == Type::Register; }
[[nodiscard]] bool is_local() const { return type() == Type::Local; }
[[nodiscard]] bool is_constant() const { return type() == Type::Constant; }
[[nodiscard]] Type type() const { return static_cast<Type>((m_raw & 0xe0000000u) >> 29); }
[[nodiscard]] u32 index() const { return m_raw & 0x1fffffff; }
[[nodiscard]] u32 raw() const { return m_raw; }
[[nodiscard]] Register as_register() const;
void offset_index_by(u32 offset)
{
m_raw &= 0x1fffffff;
m_raw += offset;
}
private:
Operand() = default;

View file

@ -6,7 +6,7 @@
#pragma once
#include <AK/Format.h>
#include <AK/Types.h>
namespace JS::Bytecode {
@ -60,13 +60,3 @@ private:
};
}
template<>
struct AK::Formatter<JS::Bytecode::Register> : AK::Formatter<FormatString> {
ErrorOr<void> format(FormatBuilder& builder, JS::Bytecode::Register const& value)
{
if (value.index() == JS::Bytecode::Register::accumulator_index)
return builder.put_string("acc"sv);
return AK::Formatter<FormatString>::format(builder, "${}"sv, value.index());
}
};

View file

@ -347,6 +347,23 @@ impl<'a> BytecodeDumper<'a> {
}
self.append("]");
}
pub fn append_exception_handlers(&mut self, exception_handlers: &[FFIDumpExceptionHandler]) {
if exception_handlers.is_empty() {
return;
}
self.append("\nException handlers:\n");
for handler in exception_handlers {
self.append(" [");
self.append(&format!("{:4x}", handler.start_offset));
self.append(" .. ");
self.append(&format!("{:4x}", handler.end_offset));
self.append("] => handler ");
self.append_label("", handler.handler_offset as u32);
self.append("\n");
}
}
}
fn collect_basic_block_start_offsets(bytecode: &[u8], exception_handlers: &[FFIDumpExceptionHandler]) -> Vec<u32> {
@ -459,5 +476,7 @@ pub unsafe extern "C" fn rust_dump_bytecode(
at += instruction_length_from_bytes(bytecode[at], bytecode, at)
.expect("validated bytecode should have valid instruction lengths");
}
dumper.append_exception_handlers(exception_handlers);
});
}