Преглед на файлове

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.
Andreas Kling преди 1 година
родител
ревизия
cfdb8a2756

+ 2 - 0
Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp

@@ -2337,6 +2337,8 @@ Bytecode::CodeGenerationErrorOr<void> TryStatement::generate_bytecode(Bytecode::
         auto& handler_block = generator.make_block();
         generator.switch_to_basic_block(handler_block);
 
+        generator.emit<Bytecode::Op::Catch>();
+
         if (!m_finalizer)
             generator.emit<Bytecode::Op::LeaveUnwindContext>();
 

+ 1 - 0
Userland/Libraries/LibJS/Bytecode/Instruction.h

@@ -24,6 +24,7 @@
     O(BlockDeclarationInstantiation)   \
     O(Call)                            \
     O(CallWithArgumentArray)           \
+    O(Catch)                           \
     O(ConcatString)                    \
     O(ContinuePendingUnwind)           \
     O(CopyObjectExcludingProperties)   \

+ 22 - 6
Userland/Libraries/LibJS/Bytecode/Interpreter.cpp

@@ -294,13 +294,7 @@ void Interpreter::run_bytecode()
                 VERIFY(unwind_context.executable == m_current_executable);
 
                 if (handler) {
-                    VERIFY(!unwind_context.handler_called);
-                    vm().running_execution_context().lexical_environment = unwind_context.lexical_environment;
                     m_current_block = handler;
-                    unwind_context.handler_called = true;
-
-                    accumulator = reg(Register::exception());
-                    reg(Register::exception()) = {};
                     goto start;
                 }
                 if (finalizer) {
@@ -439,6 +433,17 @@ void Interpreter::leave_unwind_context()
     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 == &current_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)
 {
     auto executable_result = Bytecode::Generator::generate(node, kind);
@@ -746,6 +751,12 @@ ThrowCompletionOr<void> EnterObjectEnvironment::execute_impl(Bytecode::Interpret
     return {};
 }
 
+ThrowCompletionOr<void> Catch::execute_impl(Bytecode::Interpreter& interpreter) const
+{
+    interpreter.catch_exception();
+    return {};
+}
+
 ThrowCompletionOr<void> CreateVariable::execute_impl(Bytecode::Interpreter& interpreter) const
 {
     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);
 }
 
+DeprecatedString Catch::to_deprecated_string_impl(Bytecode::Executable const&) const
+{
+    return "Catch"sv;
+}
+
 }

+ 1 - 0
Userland/Libraries/LibJS/Bytecode/Interpreter.h

@@ -74,6 +74,7 @@ public:
 
     void enter_unwind_context();
     void leave_unwind_context();
+    void catch_exception();
 
     Executable& current_executable() { return *m_current_executable; }
     Executable const& current_executable() const { return *m_current_executable; }

+ 11 - 0
Userland/Libraries/LibJS/Bytecode/Op.h

@@ -407,6 +407,17 @@ public:
     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 {
 public:
     explicit CreateVariable(IdentifierTableIndex identifier, EnvironmentMode mode, bool is_immutable, bool is_global = false, bool is_strict = false)

+ 22 - 1
Userland/Libraries/LibJS/JIT/Compiler.cpp

@@ -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)
 {
+    native_call((void*)cxx_enter_unwind_context);
     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&)
 {
-    /* Nothing */
+    native_call((void*)cxx_leave_unwind_context);
 }
 
 void Compiler::compile_throw(Bytecode::Op::Throw const&)
@@ -577,6 +588,16 @@ void Compiler::compile_throw(Bytecode::Op::Throw const&)
     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)
 {
     return Value(!TRY(is_loosely_equal(vm, src1, src2)));

+ 1 - 0
Userland/Libraries/LibJS/JIT/Compiler.h

@@ -83,6 +83,7 @@ private:
         O(EnterUnwindContext, enter_unwind_context)                              \
         O(LeaveUnwindContext, leave_unwind_context)                              \
         O(Throw, throw)                                                          \
+        O(Catch, catch)                                                          \
         O(CreateLexicalEnvironment, create_lexical_environment)                  \
         O(LeaveLexicalEnvironment, leave_lexical_environment)                    \
         O(ToNumeric, to_numeric)                                                 \