LibJS: Collapse handler/finalizer into single exception handler target

After replacing the runtime unwind context stack with explicit
completion records for try/finally dispatch, the distinction between
"handler" (catch) and "finalizer" (finally) in the exception handler
table is no longer meaningful at runtime.

handle_exception() checked handler first, then finalizer, but they
did the exact same thing (set the PC). When both were present, the
finalizer was dead code.

Collapse both fields into a single handler_offset (now non-optional,
since an entry always has a target), remove the finalizer concept
from BasicBlock, UnwindContext, and ExceptionHandlers, and simplify
handle_exception() to a direct assignment.
This commit is contained in:
Andreas Kling 2026-02-09 12:08:49 +01:00 committed by Andreas Kling
parent 4fa4ecf31b
commit 720fd567b1
7 changed files with 14 additions and 36 deletions

View file

@ -2948,7 +2948,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> TryStatement::generate_
if (!m_finalizer) {
auto const* parent_unwind_context = generator.current_unwind_context();
if (parent_unwind_context)
unwind_context.emplace(generator, parent_unwind_context->finalizer());
unwind_context.emplace(generator, parent_unwind_context->handler());
else
unwind_context.emplace(generator, OptionalNone());
}

View file

@ -42,10 +42,8 @@ public:
String const& name() const { return m_name; }
void set_handler(BasicBlock const& handler) { m_handler = &handler; }
void set_finalizer(BasicBlock const& finalizer) { m_finalizer = &finalizer; }
BasicBlock const* handler() const { return m_handler; }
BasicBlock const* finalizer() const { return m_finalizer; }
auto const& source_map() const { return m_source_map; }
void add_source_map_entry(u32 bytecode_offset, SourceRecord const& source_record) { m_source_map.append({ bytecode_offset, source_record }); }
@ -62,7 +60,6 @@ private:
u32 m_index { 0 };
Vector<u8> m_buffer;
BasicBlock const* m_handler { nullptr };
BasicBlock const* m_finalizer { nullptr };
String m_name;
bool m_terminated { false };
bool m_has_resolved_this { false };

View file

@ -81,11 +81,10 @@ void Executable::dump() const
warnln("");
warnln("Exception handlers:");
for (auto& handlers : exception_handlers) {
warnln(" from {:4x} to {:4x} handler {:4x} finalizer {:4x}",
warnln(" from {:4x} to {:4x} handler {:4x}",
handlers.start_offset,
handlers.end_offset,
handlers.handler_offset.value_or(0),
handlers.finalizer_offset.value_or(0));
handlers.handler_offset);
}
}

View file

@ -140,8 +140,7 @@ public:
struct ExceptionHandlers {
size_t start_offset;
size_t end_offset;
Optional<size_t> handler_offset;
Optional<size_t> finalizer_offset;
size_t handler_offset;
};
Vector<ExceptionHandlers> exception_handlers;

View file

@ -301,7 +301,6 @@ CodeGenerationErrorOr<GC::Ref<Executable>> Generator::compile(VM& vm, ASTNode co
size_t start_offset;
size_t end_offset;
BasicBlock const* handler;
BasicBlock const* finalizer;
};
Vector<UnlinkedExceptionHandlers> unlinked_exception_handlers;
@ -361,12 +360,11 @@ CodeGenerationErrorOr<GC::Ref<Executable>> Generator::compile(VM& vm, ASTNode co
for (auto& block : generator.m_root_basic_blocks) {
basic_block_start_offsets.append(bytecode.size());
if (block->handler() || block->finalizer()) {
if (block->handler()) {
unlinked_exception_handlers.append({
.start_offset = bytecode.size(),
.end_offset = 0,
.handler = block->handler(),
.finalizer = block->finalizer(),
});
}
@ -453,7 +451,7 @@ CodeGenerationErrorOr<GC::Ref<Executable>> Generator::compile(VM& vm, ASTNode co
Op::End end(*undefined_constant);
bytecode.append(reinterpret_cast<u8 const*>(&end), end.length());
}
if (block->handler() || block->finalizer()) {
if (block->handler()) {
unlinked_exception_handlers.last().end_offset = bytecode.size();
}
}
@ -483,18 +481,17 @@ CodeGenerationErrorOr<GC::Ref<Executable>> Generator::compile(VM& vm, ASTNode co
for (auto& unlinked_handler : unlinked_exception_handlers) {
auto start_offset = unlinked_handler.start_offset;
auto end_offset = unlinked_handler.end_offset;
auto handler_offset = unlinked_handler.handler ? block_offsets.get(unlinked_handler.handler).value() : Optional<size_t> {};
auto finalizer_offset = unlinked_handler.finalizer ? block_offsets.get(unlinked_handler.finalizer).value() : Optional<size_t> {};
auto handler_offset = block_offsets.get(unlinked_handler.handler).value();
auto maybe_exception_handler_to_merge_with = linked_exception_handlers.find_if([&](Executable::ExceptionHandlers const& exception_handler) {
return exception_handler.end_offset == start_offset && exception_handler.handler_offset == handler_offset && exception_handler.finalizer_offset == finalizer_offset;
return exception_handler.end_offset == start_offset && exception_handler.handler_offset == handler_offset;
});
if (!maybe_exception_handler_to_merge_with.is_end()) {
auto& exception_handler_to_merge_with = *maybe_exception_handler_to_merge_with;
exception_handler_to_merge_with.end_offset = end_offset;
} else {
linked_exception_handlers.append({ start_offset, end_offset, handler_offset, finalizer_offset });
linked_exception_handlers.append({ start_offset, end_offset, handler_offset });
}
}
@ -586,9 +583,9 @@ Generator::SourceLocationScope::~SourceLocationScope()
m_generator.m_current_ast_node = m_previous_node;
}
Generator::UnwindContext::UnwindContext(Generator& generator, Optional<Label> finalizer)
Generator::UnwindContext::UnwindContext(Generator& generator, Optional<Label> handler)
: m_generator(generator)
, m_finalizer(finalizer)
, m_handler(handler)
, m_previous_context(m_generator.m_current_unwind_context)
{
m_generator.m_current_unwind_context = this;

View file

@ -69,18 +69,16 @@ public:
class UnwindContext {
public:
UnwindContext(Generator&, Optional<Label> finalizer);
UnwindContext(Generator&, Optional<Label> handler);
UnwindContext const* previous() const { return m_previous_context; }
void set_handler(Label handler) { m_handler = handler; }
Optional<Label> handler() const { return m_handler; }
Optional<Label> finalizer() const { return m_finalizer; }
~UnwindContext();
private:
Generator& m_generator;
Optional<Label> m_finalizer;
Optional<Label> m_handler {};
UnwindContext const* m_previous_context { nullptr };
};
@ -239,8 +237,6 @@ public:
if (auto const* context = m_current_unwind_context) {
if (context->handler().has_value())
block->set_handler(*m_root_basic_blocks[context->handler().value().basic_block_index()]);
if (m_current_unwind_context->finalizer().has_value())
block->set_finalizer(*m_root_basic_blocks[context->finalizer().value().basic_block_index()]);
}
m_root_basic_blocks.append(move(block));
return *m_root_basic_blocks.last();

View file

@ -235,18 +235,8 @@ Interpreter::HandleExceptionResponse Interpreter::handle_exception(u32& program_
if (!handlers.has_value()) {
return HandleExceptionResponse::ExitFromExecutable;
}
auto& handler = handlers->handler_offset;
auto& finalizer = handlers->finalizer_offset;
if (handler.has_value()) {
program_counter = handler.value();
return HandleExceptionResponse::ContinueInThisExecutable;
}
if (finalizer.has_value()) {
program_counter = finalizer.value();
return HandleExceptionResponse::ContinueInThisExecutable;
}
VERIFY_NOT_REACHED();
program_counter = handlers->handler_offset;
return HandleExceptionResponse::ContinueInThisExecutable;
}
void Interpreter::run_bytecode(size_t entry_point)