mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 21:27:44 +00:00
LibJS/JIT: Update "unwind context" stack in JIT code
Until now, the unwind context stack has not been maintained by jitted code, which meant we were unable to support the `with` statement. This is a first step towards supporting that by making jitted code call out to C++ to update the unwind context stack when entering/leaving unwind contexts. We also introduce a new "Catch" bytecode instruction that moves the current exception into the accumulator. It's always emitted at the start of a "catch" block.
This commit is contained in:
parent
298dfa96a4
commit
cfdb8a2756
7 changed files with 60 additions and 7 deletions
|
@ -2337,6 +2337,8 @@ Bytecode::CodeGenerationErrorOr<void> TryStatement::generate_bytecode(Bytecode::
|
||||||
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);
|
||||||
|
|
||||||
|
generator.emit<Bytecode::Op::Catch>();
|
||||||
|
|
||||||
if (!m_finalizer)
|
if (!m_finalizer)
|
||||||
generator.emit<Bytecode::Op::LeaveUnwindContext>();
|
generator.emit<Bytecode::Op::LeaveUnwindContext>();
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
O(BlockDeclarationInstantiation) \
|
O(BlockDeclarationInstantiation) \
|
||||||
O(Call) \
|
O(Call) \
|
||||||
O(CallWithArgumentArray) \
|
O(CallWithArgumentArray) \
|
||||||
|
O(Catch) \
|
||||||
O(ConcatString) \
|
O(ConcatString) \
|
||||||
O(ContinuePendingUnwind) \
|
O(ContinuePendingUnwind) \
|
||||||
O(CopyObjectExcludingProperties) \
|
O(CopyObjectExcludingProperties) \
|
||||||
|
|
|
@ -294,13 +294,7 @@ void Interpreter::run_bytecode()
|
||||||
VERIFY(unwind_context.executable == m_current_executable);
|
VERIFY(unwind_context.executable == m_current_executable);
|
||||||
|
|
||||||
if (handler) {
|
if (handler) {
|
||||||
VERIFY(!unwind_context.handler_called);
|
|
||||||
vm().running_execution_context().lexical_environment = unwind_context.lexical_environment;
|
|
||||||
m_current_block = handler;
|
m_current_block = handler;
|
||||||
unwind_context.handler_called = true;
|
|
||||||
|
|
||||||
accumulator = reg(Register::exception());
|
|
||||||
reg(Register::exception()) = {};
|
|
||||||
goto start;
|
goto start;
|
||||||
}
|
}
|
||||||
if (finalizer) {
|
if (finalizer) {
|
||||||
|
@ -439,6 +433,17 @@ void Interpreter::leave_unwind_context()
|
||||||
unwind_contexts().take_last();
|
unwind_contexts().take_last();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Interpreter::catch_exception()
|
||||||
|
{
|
||||||
|
accumulator() = reg(Register::exception());
|
||||||
|
reg(Register::exception()) = {};
|
||||||
|
auto& context = unwind_contexts().last();
|
||||||
|
VERIFY(!context.handler_called);
|
||||||
|
VERIFY(context.executable == ¤t_executable());
|
||||||
|
context.handler_called = true;
|
||||||
|
vm().running_execution_context().lexical_environment = context.lexical_environment;
|
||||||
|
}
|
||||||
|
|
||||||
ThrowCompletionOr<NonnullRefPtr<Bytecode::Executable>> compile(VM& vm, ASTNode const& node, FunctionKind kind, DeprecatedFlyString const& name)
|
ThrowCompletionOr<NonnullRefPtr<Bytecode::Executable>> compile(VM& vm, ASTNode const& node, FunctionKind kind, DeprecatedFlyString const& name)
|
||||||
{
|
{
|
||||||
auto executable_result = Bytecode::Generator::generate(node, kind);
|
auto executable_result = Bytecode::Generator::generate(node, kind);
|
||||||
|
@ -746,6 +751,12 @@ ThrowCompletionOr<void> EnterObjectEnvironment::execute_impl(Bytecode::Interpret
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThrowCompletionOr<void> Catch::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
|
{
|
||||||
|
interpreter.catch_exception();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
ThrowCompletionOr<void> CreateVariable::execute_impl(Bytecode::Interpreter& interpreter) const
|
ThrowCompletionOr<void> CreateVariable::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
auto const& name = interpreter.current_executable().get_identifier(m_identifier);
|
auto const& name = interpreter.current_executable().get_identifier(m_identifier);
|
||||||
|
@ -1733,4 +1744,9 @@ DeprecatedString ImportCall::to_deprecated_string_impl(Bytecode::Executable cons
|
||||||
return DeprecatedString::formatted("ImportCall specifier:{} options:{}"sv, m_specifier, m_options);
|
return DeprecatedString::formatted("ImportCall specifier:{} options:{}"sv, m_specifier, m_options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DeprecatedString Catch::to_deprecated_string_impl(Bytecode::Executable const&) const
|
||||||
|
{
|
||||||
|
return "Catch"sv;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,6 +74,7 @@ public:
|
||||||
|
|
||||||
void enter_unwind_context();
|
void enter_unwind_context();
|
||||||
void leave_unwind_context();
|
void leave_unwind_context();
|
||||||
|
void catch_exception();
|
||||||
|
|
||||||
Executable& current_executable() { return *m_current_executable; }
|
Executable& current_executable() { return *m_current_executable; }
|
||||||
Executable const& current_executable() const { return *m_current_executable; }
|
Executable const& current_executable() const { return *m_current_executable; }
|
||||||
|
|
|
@ -407,6 +407,17 @@ public:
|
||||||
DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const;
|
DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Catch final : public Instruction {
|
||||||
|
public:
|
||||||
|
explicit Catch()
|
||||||
|
: Instruction(Type::Catch, sizeof(*this))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
|
||||||
|
DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const;
|
||||||
|
};
|
||||||
|
|
||||||
class CreateVariable final : public Instruction {
|
class CreateVariable final : public Instruction {
|
||||||
public:
|
public:
|
||||||
explicit CreateVariable(IdentifierTableIndex identifier, EnvironmentMode mode, bool is_immutable, bool is_global = false, bool is_strict = false)
|
explicit CreateVariable(IdentifierTableIndex identifier, EnvironmentMode mode, bool is_immutable, bool is_global = false, bool is_strict = false)
|
||||||
|
|
|
@ -560,14 +560,25 @@ void Compiler::check_exception()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cxx_enter_unwind_context(VM& vm)
|
||||||
|
{
|
||||||
|
vm.bytecode_interpreter().enter_unwind_context();
|
||||||
|
}
|
||||||
|
|
||||||
void Compiler::compile_enter_unwind_context(Bytecode::Op::EnterUnwindContext const& op)
|
void Compiler::compile_enter_unwind_context(Bytecode::Op::EnterUnwindContext const& op)
|
||||||
{
|
{
|
||||||
|
native_call((void*)cxx_enter_unwind_context);
|
||||||
m_assembler.jump(label_for(op.entry_point().block()));
|
m_assembler.jump(label_for(op.entry_point().block()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cxx_leave_unwind_context(VM& vm)
|
||||||
|
{
|
||||||
|
vm.bytecode_interpreter().leave_unwind_context();
|
||||||
|
}
|
||||||
|
|
||||||
void Compiler::compile_leave_unwind_context(Bytecode::Op::LeaveUnwindContext const&)
|
void Compiler::compile_leave_unwind_context(Bytecode::Op::LeaveUnwindContext const&)
|
||||||
{
|
{
|
||||||
/* Nothing */
|
native_call((void*)cxx_leave_unwind_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::compile_throw(Bytecode::Op::Throw const&)
|
void Compiler::compile_throw(Bytecode::Op::Throw const&)
|
||||||
|
@ -577,6 +588,16 @@ void Compiler::compile_throw(Bytecode::Op::Throw const&)
|
||||||
check_exception();
|
check_exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cxx_catch(VM& vm)
|
||||||
|
{
|
||||||
|
vm.bytecode_interpreter().catch_exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compiler::compile_catch(Bytecode::Op::Catch const&)
|
||||||
|
{
|
||||||
|
native_call((void*)cxx_catch);
|
||||||
|
}
|
||||||
|
|
||||||
static ThrowCompletionOr<Value> loosely_inequals(VM& vm, Value src1, Value src2)
|
static ThrowCompletionOr<Value> loosely_inequals(VM& vm, Value src1, Value src2)
|
||||||
{
|
{
|
||||||
return Value(!TRY(is_loosely_equal(vm, src1, src2)));
|
return Value(!TRY(is_loosely_equal(vm, src1, src2)));
|
||||||
|
|
|
@ -83,6 +83,7 @@ private:
|
||||||
O(EnterUnwindContext, enter_unwind_context) \
|
O(EnterUnwindContext, enter_unwind_context) \
|
||||||
O(LeaveUnwindContext, leave_unwind_context) \
|
O(LeaveUnwindContext, leave_unwind_context) \
|
||||||
O(Throw, throw) \
|
O(Throw, throw) \
|
||||||
|
O(Catch, catch) \
|
||||||
O(CreateLexicalEnvironment, create_lexical_environment) \
|
O(CreateLexicalEnvironment, create_lexical_environment) \
|
||||||
O(LeaveLexicalEnvironment, leave_lexical_environment) \
|
O(LeaveLexicalEnvironment, leave_lexical_environment) \
|
||||||
O(ToNumeric, to_numeric) \
|
O(ToNumeric, to_numeric) \
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue