Skip to content

Commit 73f347b

Browse files
Hendiadyoin1awesomekling
authored andcommitted
LibJS: Create static unwind mappings for BasicBlocks
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.
1 parent 647f0cc commit 73f347b

File tree

6 files changed

+92
-6
lines changed

6 files changed

+92
-6
lines changed

Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2308,6 +2308,7 @@ Bytecode::CodeGenerationErrorOr<void> TryStatement::generate_bytecode(Bytecode::
23082308

23092309
Optional<Bytecode::Label> handler_target;
23102310
Optional<Bytecode::Label> finalizer_target;
2311+
Optional<Bytecode::Generator::UnwindContext> unwind_context;
23112312

23122313
Bytecode::BasicBlock* next_block { nullptr };
23132314

@@ -2323,10 +2324,10 @@ Bytecode::CodeGenerationErrorOr<void> TryStatement::generate_bytecode(Bytecode::
23232324
generator.emit<Bytecode::Op::ContinuePendingUnwind>(next_target);
23242325
}
23252326
finalizer_target = Bytecode::Label { finalizer_block };
2326-
}
23272327

2328-
if (m_finalizer)
23292328
generator.start_boundary(Bytecode::Generator::BlockBoundaryType::ReturnToFinally);
2329+
unwind_context.emplace(generator, finalizer_target);
2330+
}
23302331
if (m_handler) {
23312332
auto& handler_block = generator.make_block();
23322333
generator.switch_to_basic_block(handler_block);
@@ -2374,6 +2375,7 @@ Bytecode::CodeGenerationErrorOr<void> TryStatement::generate_bytecode(Bytecode::
23742375
generator.emit<Bytecode::Op::Jump>(*finalizer_target);
23752376
} else {
23762377
VERIFY(!next_block);
2378+
VERIFY(!unwind_context.has_value());
23772379
next_block = &generator.make_block();
23782380
auto next_target = Bytecode::Label { *next_block };
23792381
generator.emit<Bytecode::Op::Jump>(next_target);
@@ -2382,6 +2384,11 @@ Bytecode::CodeGenerationErrorOr<void> TryStatement::generate_bytecode(Bytecode::
23822384
}
23832385
if (m_finalizer)
23842386
generator.end_boundary(Bytecode::Generator::BlockBoundaryType::ReturnToFinally);
2387+
if (m_handler) {
2388+
if (!m_finalizer)
2389+
unwind_context.emplace(generator, OptionalNone());
2390+
unwind_context->set_handler(handler_target.value());
2391+
}
23852392

23862393
auto& target_block = generator.make_block();
23872394
generator.switch_to_basic_block(saved_block);
@@ -2396,6 +2403,8 @@ Bytecode::CodeGenerationErrorOr<void> TryStatement::generate_bytecode(Bytecode::
23962403
if (m_finalizer) {
23972404
generator.emit<Bytecode::Op::Jump>(*finalizer_target);
23982405
} else {
2406+
VERIFY(unwind_context.has_value());
2407+
unwind_context.clear();
23992408
if (!next_block)
24002409
next_block = &generator.make_block();
24012410
generator.emit<Bytecode::Op::LeaveUnwindContext>();

Userland/Libraries/LibJS/Bytecode/BasicBlock.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,18 @@ BasicBlock::~BasicBlock()
3333
void BasicBlock::dump(Bytecode::Executable const& executable) const
3434
{
3535
Bytecode::InstructionStreamIterator it(instruction_stream());
36+
3637
if (!m_name.is_empty())
37-
warnln("{}:", m_name);
38+
warn("{}", m_name);
39+
if (m_handler || m_finalizer) {
40+
warn(" [");
41+
if (m_handler)
42+
warn(" Handler: {}", Label { *m_handler });
43+
if (m_finalizer)
44+
warn(" Finalizer: {}", Label { *m_finalizer });
45+
warn(" ]");
46+
}
47+
warnln(":");
3848
while (!it.at_end()) {
3949
warnln("[{:4x}] {}", it.offset(), (*it).to_deprecated_string(executable));
4050
++it;

Userland/Libraries/LibJS/Bytecode/BasicBlock.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,18 @@ class BasicBlock {
4343

4444
DeprecatedString const& name() const { return m_name; }
4545

46+
void set_handler(BasicBlock const& handler) { m_handler = &handler; }
47+
void set_finalizer(BasicBlock const& finalizer) { m_finalizer = &finalizer; }
48+
49+
BasicBlock const* handler() const { return m_handler; }
50+
BasicBlock const* finalizer() const { return m_finalizer; }
51+
4652
private:
4753
explicit BasicBlock(DeprecatedString name);
4854

4955
Vector<u8> m_buffer;
56+
BasicBlock const* m_handler { nullptr };
57+
BasicBlock const* m_finalizer { nullptr };
5058
DeprecatedString m_name;
5159
bool m_terminated { false };
5260
};

Userland/Libraries/LibJS/Bytecode/Executable.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
#pragma once
88

99
#include <AK/DeprecatedFlyString.h>
10+
#include <AK/HashMap.h>
1011
#include <AK/NonnullOwnPtr.h>
1112
#include <AK/OwnPtr.h>
1213
#include <AK/WeakPtr.h>
1314
#include <LibJS/Bytecode/IdentifierTable.h>
15+
#include <LibJS/Bytecode/Label.h>
1416
#include <LibJS/Bytecode/StringTable.h>
1517
#include <LibJS/Forward.h>
1618
#include <LibJS/JIT/NativeExecutable.h>
@@ -57,6 +59,7 @@ class Executable final : public RefCounted<Executable> {
5759
NonnullOwnPtr<StringTable> string_table;
5860
NonnullOwnPtr<IdentifierTable> identifier_table;
5961
NonnullOwnPtr<RegexTable> regex_table;
62+
6063
NonnullRefPtr<SourceCode const> source_code;
6164
size_t number_of_registers { 0 };
6265
bool is_strict_mode { false };

Userland/Libraries/LibJS/Bytecode/Generator.cpp

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* SPDX-License-Identifier: BSD-2-Clause
55
*/
66

7+
#include <AK/TemporaryChange.h>
78
#include <LibJS/AST.h>
89
#include <LibJS/Bytecode/BasicBlock.h>
910
#include <LibJS/Bytecode/Generator.h>
@@ -95,6 +96,20 @@ Generator::SourceLocationScope::~SourceLocationScope()
9596
m_generator.m_current_ast_node = m_previous_node;
9697
}
9798

99+
Generator::UnwindContext::UnwindContext(Generator& generator, Optional<Label> finalizer)
100+
: m_generator(generator)
101+
, m_finalizer(finalizer)
102+
, m_previous_context(m_generator.m_current_unwind_context)
103+
{
104+
m_generator.m_current_unwind_context = this;
105+
}
106+
107+
Generator::UnwindContext::~UnwindContext()
108+
{
109+
VERIFY(m_generator.m_current_unwind_context == this);
110+
m_generator.m_current_unwind_context = m_previous_context;
111+
}
112+
98113
Label Generator::nearest_continuable_scope() const
99114
{
100115
return m_continuable_scopes.last().bytecode_target;
@@ -400,6 +415,7 @@ void Generator::emit_set_variable(JS::Identifier const& identifier, Bytecode::Op
400415

401416
void Generator::generate_scoped_jump(JumpType type)
402417
{
418+
TemporaryChange temp { m_current_unwind_context, m_current_unwind_context };
403419
bool last_was_finally = false;
404420
for (size_t i = m_boundaries.size(); i > 0; --i) {
405421
auto boundary = m_boundaries[i - 1];
@@ -418,14 +434,20 @@ void Generator::generate_scoped_jump(JumpType type)
418434
}
419435
break;
420436
case Unwind:
421-
if (!last_was_finally)
437+
VERIFY(last_was_finally || !m_current_unwind_context->finalizer().has_value());
438+
if (!last_was_finally) {
439+
VERIFY(m_current_unwind_context && m_current_unwind_context->handler().has_value());
422440
emit<Bytecode::Op::LeaveUnwindContext>();
441+
m_current_unwind_context = m_current_unwind_context->previous();
442+
}
423443
last_was_finally = false;
424444
break;
425445
case LeaveLexicalEnvironment:
426446
emit<Bytecode::Op::LeaveLexicalEnvironment>();
427447
break;
428448
case ReturnToFinally: {
449+
VERIFY(m_current_unwind_context->finalizer().has_value());
450+
m_current_unwind_context = m_current_unwind_context->previous();
429451
auto jump_type_name = type == JumpType::Break ? "break"sv : "continue"sv;
430452
auto& block = make_block(DeprecatedString::formatted("{}.{}", current_block().name(), jump_type_name));
431453
emit<Op::ScheduleJump>(Label { block });
@@ -440,6 +462,7 @@ void Generator::generate_scoped_jump(JumpType type)
440462

441463
void Generator::generate_labelled_jump(JumpType type, DeprecatedFlyString const& label)
442464
{
465+
TemporaryChange temp { m_current_unwind_context, m_current_unwind_context };
443466
size_t current_boundary = m_boundaries.size();
444467
bool last_was_finally = false;
445468

@@ -449,12 +472,18 @@ void Generator::generate_labelled_jump(JumpType type, DeprecatedFlyString const&
449472
for (; current_boundary > 0; --current_boundary) {
450473
auto boundary = m_boundaries[current_boundary - 1];
451474
if (boundary == BlockBoundaryType::Unwind) {
452-
if (!last_was_finally)
475+
VERIFY(last_was_finally || !m_current_unwind_context->finalizer().has_value());
476+
if (!last_was_finally) {
477+
VERIFY(m_current_unwind_context && m_current_unwind_context->handler().has_value());
453478
emit<Bytecode::Op::LeaveUnwindContext>();
479+
m_current_unwind_context = m_current_unwind_context->previous();
480+
}
454481
last_was_finally = false;
455482
} else if (boundary == BlockBoundaryType::LeaveLexicalEnvironment) {
456483
emit<Bytecode::Op::LeaveLexicalEnvironment>();
457484
} else if (boundary == BlockBoundaryType::ReturnToFinally) {
485+
VERIFY(m_current_unwind_context->finalizer().has_value());
486+
m_current_unwind_context = m_current_unwind_context->previous();
458487
auto jump_type_name = type == JumpType::Break ? "break"sv : "continue"sv;
459488
auto& block = make_block(DeprecatedString::formatted("{}.{}", current_block().name(), jump_type_name));
460489
emit<Op::ScheduleJump>(Label { block });

Userland/Libraries/LibJS/Bytecode/Generator.h

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,24 @@ class Generator {
4444
ASTNode const* m_previous_node { nullptr };
4545
};
4646

47+
class UnwindContext {
48+
public:
49+
UnwindContext(Generator&, Optional<Label> finalizer);
50+
51+
UnwindContext const* previous() const { return m_previous_context; }
52+
void set_handler(Label handler) { m_handler = handler; }
53+
Optional<Label> handler() const { return m_handler; }
54+
Optional<Label> finalizer() const { return m_finalizer; }
55+
56+
~UnwindContext();
57+
58+
private:
59+
Generator& m_generator;
60+
Optional<Label> m_finalizer;
61+
Optional<Label> m_handler {};
62+
UnwindContext const* m_previous_context { nullptr };
63+
};
64+
4765
template<typename OpType, typename... Args>
4866
void emit(Args&&... args)
4967
{
@@ -114,7 +132,14 @@ class Generator {
114132
{
115133
if (name.is_empty())
116134
name = DeprecatedString::number(m_next_block++);
117-
m_root_basic_blocks.append(BasicBlock::create(name));
135+
auto block = BasicBlock::create(name);
136+
if (auto const* context = m_current_unwind_context) {
137+
if (context->handler().has_value())
138+
block->set_handler(context->handler().value().block());
139+
if (m_current_unwind_context->finalizer().has_value())
140+
block->set_finalizer(context->finalizer().value().block());
141+
}
142+
m_root_basic_blocks.append(move(block));
118143
return *m_root_basic_blocks.last();
119144
}
120145

@@ -228,6 +253,8 @@ class Generator {
228253

229254
BasicBlock* m_current_basic_block { nullptr };
230255
ASTNode const* m_current_ast_node { nullptr };
256+
UnwindContext const* m_current_unwind_context { nullptr };
257+
231258
Vector<NonnullOwnPtr<BasicBlock>> m_root_basic_blocks;
232259
NonnullOwnPtr<StringTable> m_string_table;
233260
NonnullOwnPtr<IdentifierTable> m_identifier_table;

0 commit comments

Comments
 (0)