mirror of
https://github.com/RGBCube/serenity
synced 2025-05-20 11:55:08 +00:00
LibJS: Create static unwind mappings for BasicBlock
s
This is currently only used in the bytecode dump to annotate to where unwinds lead per block, but will be hooked up to the virtual machine in the next commit.
This commit is contained in:
parent
647f0ccd3f
commit
73f347b75c
6 changed files with 92 additions and 6 deletions
|
@ -2308,6 +2308,7 @@ Bytecode::CodeGenerationErrorOr<void> TryStatement::generate_bytecode(Bytecode::
|
||||||
|
|
||||||
Optional<Bytecode::Label> handler_target;
|
Optional<Bytecode::Label> handler_target;
|
||||||
Optional<Bytecode::Label> finalizer_target;
|
Optional<Bytecode::Label> finalizer_target;
|
||||||
|
Optional<Bytecode::Generator::UnwindContext> unwind_context;
|
||||||
|
|
||||||
Bytecode::BasicBlock* next_block { nullptr };
|
Bytecode::BasicBlock* next_block { nullptr };
|
||||||
|
|
||||||
|
@ -2323,10 +2324,10 @@ Bytecode::CodeGenerationErrorOr<void> TryStatement::generate_bytecode(Bytecode::
|
||||||
generator.emit<Bytecode::Op::ContinuePendingUnwind>(next_target);
|
generator.emit<Bytecode::Op::ContinuePendingUnwind>(next_target);
|
||||||
}
|
}
|
||||||
finalizer_target = Bytecode::Label { finalizer_block };
|
finalizer_target = Bytecode::Label { finalizer_block };
|
||||||
}
|
|
||||||
|
|
||||||
if (m_finalizer)
|
|
||||||
generator.start_boundary(Bytecode::Generator::BlockBoundaryType::ReturnToFinally);
|
generator.start_boundary(Bytecode::Generator::BlockBoundaryType::ReturnToFinally);
|
||||||
|
unwind_context.emplace(generator, finalizer_target);
|
||||||
|
}
|
||||||
if (m_handler) {
|
if (m_handler) {
|
||||||
auto& handler_block = generator.make_block();
|
auto& handler_block = generator.make_block();
|
||||||
generator.switch_to_basic_block(handler_block);
|
generator.switch_to_basic_block(handler_block);
|
||||||
|
@ -2374,6 +2375,7 @@ Bytecode::CodeGenerationErrorOr<void> TryStatement::generate_bytecode(Bytecode::
|
||||||
generator.emit<Bytecode::Op::Jump>(*finalizer_target);
|
generator.emit<Bytecode::Op::Jump>(*finalizer_target);
|
||||||
} else {
|
} else {
|
||||||
VERIFY(!next_block);
|
VERIFY(!next_block);
|
||||||
|
VERIFY(!unwind_context.has_value());
|
||||||
next_block = &generator.make_block();
|
next_block = &generator.make_block();
|
||||||
auto next_target = Bytecode::Label { *next_block };
|
auto next_target = Bytecode::Label { *next_block };
|
||||||
generator.emit<Bytecode::Op::Jump>(next_target);
|
generator.emit<Bytecode::Op::Jump>(next_target);
|
||||||
|
@ -2382,6 +2384,11 @@ Bytecode::CodeGenerationErrorOr<void> TryStatement::generate_bytecode(Bytecode::
|
||||||
}
|
}
|
||||||
if (m_finalizer)
|
if (m_finalizer)
|
||||||
generator.end_boundary(Bytecode::Generator::BlockBoundaryType::ReturnToFinally);
|
generator.end_boundary(Bytecode::Generator::BlockBoundaryType::ReturnToFinally);
|
||||||
|
if (m_handler) {
|
||||||
|
if (!m_finalizer)
|
||||||
|
unwind_context.emplace(generator, OptionalNone());
|
||||||
|
unwind_context->set_handler(handler_target.value());
|
||||||
|
}
|
||||||
|
|
||||||
auto& target_block = generator.make_block();
|
auto& target_block = generator.make_block();
|
||||||
generator.switch_to_basic_block(saved_block);
|
generator.switch_to_basic_block(saved_block);
|
||||||
|
@ -2396,6 +2403,8 @@ Bytecode::CodeGenerationErrorOr<void> TryStatement::generate_bytecode(Bytecode::
|
||||||
if (m_finalizer) {
|
if (m_finalizer) {
|
||||||
generator.emit<Bytecode::Op::Jump>(*finalizer_target);
|
generator.emit<Bytecode::Op::Jump>(*finalizer_target);
|
||||||
} else {
|
} else {
|
||||||
|
VERIFY(unwind_context.has_value());
|
||||||
|
unwind_context.clear();
|
||||||
if (!next_block)
|
if (!next_block)
|
||||||
next_block = &generator.make_block();
|
next_block = &generator.make_block();
|
||||||
generator.emit<Bytecode::Op::LeaveUnwindContext>();
|
generator.emit<Bytecode::Op::LeaveUnwindContext>();
|
||||||
|
|
|
@ -33,8 +33,18 @@ BasicBlock::~BasicBlock()
|
||||||
void BasicBlock::dump(Bytecode::Executable const& executable) const
|
void BasicBlock::dump(Bytecode::Executable const& executable) const
|
||||||
{
|
{
|
||||||
Bytecode::InstructionStreamIterator it(instruction_stream());
|
Bytecode::InstructionStreamIterator it(instruction_stream());
|
||||||
|
|
||||||
if (!m_name.is_empty())
|
if (!m_name.is_empty())
|
||||||
warnln("{}:", m_name);
|
warn("{}", m_name);
|
||||||
|
if (m_handler || m_finalizer) {
|
||||||
|
warn(" [");
|
||||||
|
if (m_handler)
|
||||||
|
warn(" Handler: {}", Label { *m_handler });
|
||||||
|
if (m_finalizer)
|
||||||
|
warn(" Finalizer: {}", Label { *m_finalizer });
|
||||||
|
warn(" ]");
|
||||||
|
}
|
||||||
|
warnln(":");
|
||||||
while (!it.at_end()) {
|
while (!it.at_end()) {
|
||||||
warnln("[{:4x}] {}", it.offset(), (*it).to_deprecated_string(executable));
|
warnln("[{:4x}] {}", it.offset(), (*it).to_deprecated_string(executable));
|
||||||
++it;
|
++it;
|
||||||
|
|
|
@ -43,10 +43,18 @@ public:
|
||||||
|
|
||||||
DeprecatedString const& name() const { return m_name; }
|
DeprecatedString 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; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit BasicBlock(DeprecatedString name);
|
explicit BasicBlock(DeprecatedString name);
|
||||||
|
|
||||||
Vector<u8> m_buffer;
|
Vector<u8> m_buffer;
|
||||||
|
BasicBlock const* m_handler { nullptr };
|
||||||
|
BasicBlock const* m_finalizer { nullptr };
|
||||||
DeprecatedString m_name;
|
DeprecatedString m_name;
|
||||||
bool m_terminated { false };
|
bool m_terminated { false };
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,10 +7,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/DeprecatedFlyString.h>
|
#include <AK/DeprecatedFlyString.h>
|
||||||
|
#include <AK/HashMap.h>
|
||||||
#include <AK/NonnullOwnPtr.h>
|
#include <AK/NonnullOwnPtr.h>
|
||||||
#include <AK/OwnPtr.h>
|
#include <AK/OwnPtr.h>
|
||||||
#include <AK/WeakPtr.h>
|
#include <AK/WeakPtr.h>
|
||||||
#include <LibJS/Bytecode/IdentifierTable.h>
|
#include <LibJS/Bytecode/IdentifierTable.h>
|
||||||
|
#include <LibJS/Bytecode/Label.h>
|
||||||
#include <LibJS/Bytecode/StringTable.h>
|
#include <LibJS/Bytecode/StringTable.h>
|
||||||
#include <LibJS/Forward.h>
|
#include <LibJS/Forward.h>
|
||||||
#include <LibJS/JIT/NativeExecutable.h>
|
#include <LibJS/JIT/NativeExecutable.h>
|
||||||
|
@ -57,6 +59,7 @@ public:
|
||||||
NonnullOwnPtr<StringTable> string_table;
|
NonnullOwnPtr<StringTable> string_table;
|
||||||
NonnullOwnPtr<IdentifierTable> identifier_table;
|
NonnullOwnPtr<IdentifierTable> identifier_table;
|
||||||
NonnullOwnPtr<RegexTable> regex_table;
|
NonnullOwnPtr<RegexTable> regex_table;
|
||||||
|
|
||||||
NonnullRefPtr<SourceCode const> source_code;
|
NonnullRefPtr<SourceCode const> source_code;
|
||||||
size_t number_of_registers { 0 };
|
size_t number_of_registers { 0 };
|
||||||
bool is_strict_mode { false };
|
bool is_strict_mode { false };
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <AK/TemporaryChange.h>
|
||||||
#include <LibJS/AST.h>
|
#include <LibJS/AST.h>
|
||||||
#include <LibJS/Bytecode/BasicBlock.h>
|
#include <LibJS/Bytecode/BasicBlock.h>
|
||||||
#include <LibJS/Bytecode/Generator.h>
|
#include <LibJS/Bytecode/Generator.h>
|
||||||
|
@ -95,6 +96,20 @@ Generator::SourceLocationScope::~SourceLocationScope()
|
||||||
m_generator.m_current_ast_node = m_previous_node;
|
m_generator.m_current_ast_node = m_previous_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Generator::UnwindContext::UnwindContext(Generator& generator, Optional<Label> finalizer)
|
||||||
|
: m_generator(generator)
|
||||||
|
, m_finalizer(finalizer)
|
||||||
|
, m_previous_context(m_generator.m_current_unwind_context)
|
||||||
|
{
|
||||||
|
m_generator.m_current_unwind_context = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Generator::UnwindContext::~UnwindContext()
|
||||||
|
{
|
||||||
|
VERIFY(m_generator.m_current_unwind_context == this);
|
||||||
|
m_generator.m_current_unwind_context = m_previous_context;
|
||||||
|
}
|
||||||
|
|
||||||
Label Generator::nearest_continuable_scope() const
|
Label Generator::nearest_continuable_scope() const
|
||||||
{
|
{
|
||||||
return m_continuable_scopes.last().bytecode_target;
|
return m_continuable_scopes.last().bytecode_target;
|
||||||
|
@ -400,6 +415,7 @@ void Generator::emit_set_variable(JS::Identifier const& identifier, Bytecode::Op
|
||||||
|
|
||||||
void Generator::generate_scoped_jump(JumpType type)
|
void Generator::generate_scoped_jump(JumpType type)
|
||||||
{
|
{
|
||||||
|
TemporaryChange temp { m_current_unwind_context, m_current_unwind_context };
|
||||||
bool last_was_finally = false;
|
bool last_was_finally = false;
|
||||||
for (size_t i = m_boundaries.size(); i > 0; --i) {
|
for (size_t i = m_boundaries.size(); i > 0; --i) {
|
||||||
auto boundary = m_boundaries[i - 1];
|
auto boundary = m_boundaries[i - 1];
|
||||||
|
@ -418,14 +434,20 @@ void Generator::generate_scoped_jump(JumpType type)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Unwind:
|
case Unwind:
|
||||||
if (!last_was_finally)
|
VERIFY(last_was_finally || !m_current_unwind_context->finalizer().has_value());
|
||||||
|
if (!last_was_finally) {
|
||||||
|
VERIFY(m_current_unwind_context && m_current_unwind_context->handler().has_value());
|
||||||
emit<Bytecode::Op::LeaveUnwindContext>();
|
emit<Bytecode::Op::LeaveUnwindContext>();
|
||||||
|
m_current_unwind_context = m_current_unwind_context->previous();
|
||||||
|
}
|
||||||
last_was_finally = false;
|
last_was_finally = false;
|
||||||
break;
|
break;
|
||||||
case LeaveLexicalEnvironment:
|
case LeaveLexicalEnvironment:
|
||||||
emit<Bytecode::Op::LeaveLexicalEnvironment>();
|
emit<Bytecode::Op::LeaveLexicalEnvironment>();
|
||||||
break;
|
break;
|
||||||
case ReturnToFinally: {
|
case ReturnToFinally: {
|
||||||
|
VERIFY(m_current_unwind_context->finalizer().has_value());
|
||||||
|
m_current_unwind_context = m_current_unwind_context->previous();
|
||||||
auto jump_type_name = type == JumpType::Break ? "break"sv : "continue"sv;
|
auto jump_type_name = type == JumpType::Break ? "break"sv : "continue"sv;
|
||||||
auto& block = make_block(DeprecatedString::formatted("{}.{}", current_block().name(), jump_type_name));
|
auto& block = make_block(DeprecatedString::formatted("{}.{}", current_block().name(), jump_type_name));
|
||||||
emit<Op::ScheduleJump>(Label { block });
|
emit<Op::ScheduleJump>(Label { block });
|
||||||
|
@ -440,6 +462,7 @@ void Generator::generate_scoped_jump(JumpType type)
|
||||||
|
|
||||||
void Generator::generate_labelled_jump(JumpType type, DeprecatedFlyString const& label)
|
void Generator::generate_labelled_jump(JumpType type, DeprecatedFlyString const& label)
|
||||||
{
|
{
|
||||||
|
TemporaryChange temp { m_current_unwind_context, m_current_unwind_context };
|
||||||
size_t current_boundary = m_boundaries.size();
|
size_t current_boundary = m_boundaries.size();
|
||||||
bool last_was_finally = false;
|
bool last_was_finally = false;
|
||||||
|
|
||||||
|
@ -449,12 +472,18 @@ void Generator::generate_labelled_jump(JumpType type, DeprecatedFlyString const&
|
||||||
for (; current_boundary > 0; --current_boundary) {
|
for (; current_boundary > 0; --current_boundary) {
|
||||||
auto boundary = m_boundaries[current_boundary - 1];
|
auto boundary = m_boundaries[current_boundary - 1];
|
||||||
if (boundary == BlockBoundaryType::Unwind) {
|
if (boundary == BlockBoundaryType::Unwind) {
|
||||||
if (!last_was_finally)
|
VERIFY(last_was_finally || !m_current_unwind_context->finalizer().has_value());
|
||||||
|
if (!last_was_finally) {
|
||||||
|
VERIFY(m_current_unwind_context && m_current_unwind_context->handler().has_value());
|
||||||
emit<Bytecode::Op::LeaveUnwindContext>();
|
emit<Bytecode::Op::LeaveUnwindContext>();
|
||||||
|
m_current_unwind_context = m_current_unwind_context->previous();
|
||||||
|
}
|
||||||
last_was_finally = false;
|
last_was_finally = false;
|
||||||
} else if (boundary == BlockBoundaryType::LeaveLexicalEnvironment) {
|
} else if (boundary == BlockBoundaryType::LeaveLexicalEnvironment) {
|
||||||
emit<Bytecode::Op::LeaveLexicalEnvironment>();
|
emit<Bytecode::Op::LeaveLexicalEnvironment>();
|
||||||
} else if (boundary == BlockBoundaryType::ReturnToFinally) {
|
} else if (boundary == BlockBoundaryType::ReturnToFinally) {
|
||||||
|
VERIFY(m_current_unwind_context->finalizer().has_value());
|
||||||
|
m_current_unwind_context = m_current_unwind_context->previous();
|
||||||
auto jump_type_name = type == JumpType::Break ? "break"sv : "continue"sv;
|
auto jump_type_name = type == JumpType::Break ? "break"sv : "continue"sv;
|
||||||
auto& block = make_block(DeprecatedString::formatted("{}.{}", current_block().name(), jump_type_name));
|
auto& block = make_block(DeprecatedString::formatted("{}.{}", current_block().name(), jump_type_name));
|
||||||
emit<Op::ScheduleJump>(Label { block });
|
emit<Op::ScheduleJump>(Label { block });
|
||||||
|
|
|
@ -44,6 +44,24 @@ public:
|
||||||
ASTNode const* m_previous_node { nullptr };
|
ASTNode const* m_previous_node { nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class UnwindContext {
|
||||||
|
public:
|
||||||
|
UnwindContext(Generator&, Optional<Label> finalizer);
|
||||||
|
|
||||||
|
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 };
|
||||||
|
};
|
||||||
|
|
||||||
template<typename OpType, typename... Args>
|
template<typename OpType, typename... Args>
|
||||||
void emit(Args&&... args)
|
void emit(Args&&... args)
|
||||||
{
|
{
|
||||||
|
@ -114,7 +132,14 @@ public:
|
||||||
{
|
{
|
||||||
if (name.is_empty())
|
if (name.is_empty())
|
||||||
name = DeprecatedString::number(m_next_block++);
|
name = DeprecatedString::number(m_next_block++);
|
||||||
m_root_basic_blocks.append(BasicBlock::create(name));
|
auto block = BasicBlock::create(name);
|
||||||
|
if (auto const* context = m_current_unwind_context) {
|
||||||
|
if (context->handler().has_value())
|
||||||
|
block->set_handler(context->handler().value().block());
|
||||||
|
if (m_current_unwind_context->finalizer().has_value())
|
||||||
|
block->set_finalizer(context->finalizer().value().block());
|
||||||
|
}
|
||||||
|
m_root_basic_blocks.append(move(block));
|
||||||
return *m_root_basic_blocks.last();
|
return *m_root_basic_blocks.last();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,6 +253,8 @@ private:
|
||||||
|
|
||||||
BasicBlock* m_current_basic_block { nullptr };
|
BasicBlock* m_current_basic_block { nullptr };
|
||||||
ASTNode const* m_current_ast_node { nullptr };
|
ASTNode const* m_current_ast_node { nullptr };
|
||||||
|
UnwindContext const* m_current_unwind_context { nullptr };
|
||||||
|
|
||||||
Vector<NonnullOwnPtr<BasicBlock>> m_root_basic_blocks;
|
Vector<NonnullOwnPtr<BasicBlock>> m_root_basic_blocks;
|
||||||
NonnullOwnPtr<StringTable> m_string_table;
|
NonnullOwnPtr<StringTable> m_string_table;
|
||||||
NonnullOwnPtr<IdentifierTable> m_identifier_table;
|
NonnullOwnPtr<IdentifierTable> m_identifier_table;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue