浏览代码

LibJS: Leave the unwind context on break/continue/return in bytecode

Otherwise we'd keep the old unwind context, and end up never invoking
the other handlers up the stack.
Ali Mohammad Pur 3 年之前
父节点
当前提交
ba9c4959d6

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

@@ -1207,10 +1207,13 @@ Bytecode::CodeGenerationErrorOr<void> ReturnStatement::generate_bytecode(Bytecod
     if (m_argument)
     if (m_argument)
         TRY(m_argument->generate_bytecode(generator));
         TRY(m_argument->generate_bytecode(generator));
 
 
-    if (generator.is_in_generator_or_async_function())
+    if (generator.is_in_generator_or_async_function()) {
+        generator.perform_needed_unwinds<Bytecode::Op::Yield>();
         generator.emit<Bytecode::Op::Yield>(nullptr);
         generator.emit<Bytecode::Op::Yield>(nullptr);
-    else
+    } else {
+        generator.perform_needed_unwinds<Bytecode::Op::Return>();
         generator.emit<Bytecode::Op::Return>();
         generator.emit<Bytecode::Op::Return>();
+    }
 
 
     return {};
     return {};
 }
 }
@@ -1279,6 +1282,7 @@ Bytecode::CodeGenerationErrorOr<void> IfStatement::generate_bytecode(Bytecode::G
 
 
 Bytecode::CodeGenerationErrorOr<void> ContinueStatement::generate_bytecode(Bytecode::Generator& generator) const
 Bytecode::CodeGenerationErrorOr<void> ContinueStatement::generate_bytecode(Bytecode::Generator& generator) const
 {
 {
+    generator.perform_needed_unwinds<Bytecode::Op::Jump>();
     generator.emit<Bytecode::Op::Jump>().set_targets(
     generator.emit<Bytecode::Op::Jump>().set_targets(
         generator.nearest_continuable_scope(),
         generator.nearest_continuable_scope(),
         {});
         {});
@@ -1438,6 +1442,7 @@ Bytecode::CodeGenerationErrorOr<void> ThrowStatement::generate_bytecode(Bytecode
 
 
 Bytecode::CodeGenerationErrorOr<void> BreakStatement::generate_bytecode(Bytecode::Generator& generator) const
 Bytecode::CodeGenerationErrorOr<void> BreakStatement::generate_bytecode(Bytecode::Generator& generator) const
 {
 {
+    generator.perform_needed_unwinds<Bytecode::Op::Jump>(true);
     generator.emit<Bytecode::Op::Jump>().set_targets(
     generator.emit<Bytecode::Op::Jump>().set_targets(
         generator.nearest_breakable_scope(),
         generator.nearest_breakable_scope(),
         {});
         {});
@@ -1502,6 +1507,7 @@ Bytecode::CodeGenerationErrorOr<void> TryStatement::generate_bytecode(Bytecode::
     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);
     generator.emit<Bytecode::Op::EnterUnwindContext>(Bytecode::Label { target_block }, handler_target, finalizer_target);
     generator.emit<Bytecode::Op::EnterUnwindContext>(Bytecode::Label { target_block }, handler_target, finalizer_target);
+    generator.start_boundary(Bytecode::Generator::BlockBoundaryType::Unwind);
 
 
     generator.switch_to_basic_block(target_block);
     generator.switch_to_basic_block(target_block);
     TRY(m_block->generate_bytecode(generator));
     TRY(m_block->generate_bytecode(generator));
@@ -1514,6 +1520,7 @@ Bytecode::CodeGenerationErrorOr<void> TryStatement::generate_bytecode(Bytecode::
             next_block = &block;
             next_block = &block;
         }
         }
     }
     }
+    generator.end_boundary(Bytecode::Generator::BlockBoundaryType::Unwind);
 
 
     generator.switch_to_basic_block(next_block ? *next_block : saved_block);
     generator.switch_to_basic_block(next_block ? *next_block : saved_block);
     return {};
     return {};

+ 4 - 0
Userland/Libraries/LibJS/Bytecode/Generator.cpp

@@ -79,11 +79,13 @@ Label Generator::nearest_continuable_scope() const
 void Generator::begin_continuable_scope(Label continue_target)
 void Generator::begin_continuable_scope(Label continue_target)
 {
 {
     m_continuable_scopes.append(continue_target);
     m_continuable_scopes.append(continue_target);
+    start_boundary(BlockBoundaryType::Continue);
 }
 }
 
 
 void Generator::end_continuable_scope()
 void Generator::end_continuable_scope()
 {
 {
     m_continuable_scopes.take_last();
     m_continuable_scopes.take_last();
+    end_boundary(BlockBoundaryType::Continue);
 }
 }
 Label Generator::nearest_breakable_scope() const
 Label Generator::nearest_breakable_scope() const
 {
 {
@@ -92,11 +94,13 @@ Label Generator::nearest_breakable_scope() const
 void Generator::begin_breakable_scope(Label breakable_target)
 void Generator::begin_breakable_scope(Label breakable_target)
 {
 {
     m_breakable_scopes.append(breakable_target);
     m_breakable_scopes.append(breakable_target);
+    start_boundary(BlockBoundaryType::Break);
 }
 }
 
 
 void Generator::end_breakable_scope()
 void Generator::end_breakable_scope()
 {
 {
     m_breakable_scopes.take_last();
     m_breakable_scopes.take_last();
+    end_boundary(BlockBoundaryType::Break);
 }
 }
 
 
 CodeGenerationErrorOr<void> Generator::emit_load_from_reference(JS::ASTNode const& node)
 CodeGenerationErrorOr<void> Generator::emit_load_from_reference(JS::ASTNode const& node)

+ 31 - 0
Userland/Libraries/LibJS/Bytecode/Generator.h

@@ -171,6 +171,36 @@ public:
         }
         }
     }
     }
 
 
+    enum class BlockBoundaryType {
+        Break,
+        Continue,
+        Unwind,
+    };
+    template<typename OpType>
+    void perform_needed_unwinds(bool is_break_node = false) requires(OpType::IsTerminator)
+    {
+        Optional<BlockBoundaryType> boundary_to_stop_at;
+        if constexpr (IsSame<OpType, Bytecode::Op::Return> || IsSame<OpType, Bytecode::Op::Yield>)
+            VERIFY(!is_break_node);
+        else
+            boundary_to_stop_at = is_break_node ? BlockBoundaryType::Break : BlockBoundaryType::Continue;
+
+        for (size_t i = m_boundaries.size(); i > 0; --i) {
+            auto boundary = m_boundaries[i - 1];
+            if (boundary_to_stop_at.has_value() && boundary == *boundary_to_stop_at)
+                break;
+            if (boundary == BlockBoundaryType::Unwind)
+                emit<Bytecode::Op::LeaveUnwindContext>();
+        }
+    }
+
+    void start_boundary(BlockBoundaryType type) { m_boundaries.append(type); }
+    void end_boundary(BlockBoundaryType type)
+    {
+        VERIFY(m_boundaries.last() == type);
+        m_boundaries.take_last();
+    }
+
 private:
 private:
     Generator();
     Generator();
     ~Generator();
     ~Generator();
@@ -189,6 +219,7 @@ private:
     Vector<Label> m_continuable_scopes;
     Vector<Label> m_continuable_scopes;
     Vector<Label> m_breakable_scopes;
     Vector<Label> m_breakable_scopes;
     Vector<LexicalScope> m_variable_scopes;
     Vector<LexicalScope> m_variable_scopes;
+    Vector<BlockBoundaryType> m_boundaries;
 };
 };
 
 
 }
 }