ソースを参照

LibJS: Implement bytecode generation for switch

Marcin Gasperowicz 4 年 前
コミット
a64089092f

+ 1 - 0
Userland/Libraries/LibJS/AST.h

@@ -1309,6 +1309,7 @@ public:
 
     virtual void dump(int indent) const override;
     virtual Value execute(Interpreter&, GlobalObject&) const override;
+    virtual void generate_bytecode(Bytecode::Generator&) const override;
 
 private:
     NonnullRefPtr<Expression> m_discriminant;

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

@@ -2,10 +2,12 @@
  * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
  * Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
  * Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
+ * Copyright (c) 2021, Marcin Gasperowicz <xnooga@gmail.com>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
+#include <AK/Format.h>
 #include <LibJS/AST.h>
 #include <LibJS/Bytecode/Generator.h>
 #include <LibJS/Bytecode/Instruction.h>
@@ -968,4 +970,60 @@ void TryStatement::generate_bytecode(Bytecode::Generator& generator) const
     generator.switch_to_basic_block(next_block ? *next_block : saved_block);
 }
 
+void SwitchStatement::generate_bytecode(Bytecode::Generator& generator) const
+{
+    auto discriminant_reg = generator.allocate_register();
+    m_discriminant->generate_bytecode(generator);
+    generator.emit<Bytecode::Op::Store>(discriminant_reg);
+    Vector<Bytecode::BasicBlock&> case_blocks;
+    Bytecode::BasicBlock* default_block { nullptr };
+    Bytecode::BasicBlock* next_test_block = &generator.make_block();
+    generator.emit<Bytecode::Op::Jump>().set_targets(Bytecode::Label { *next_test_block }, {});
+    for (auto& switch_case : m_cases) {
+        auto& case_block = generator.make_block();
+        if (switch_case.test()) {
+            generator.switch_to_basic_block(*next_test_block);
+            switch_case.test()->generate_bytecode(generator);
+            generator.emit<Bytecode::Op::TypedEquals>(discriminant_reg);
+            next_test_block = &generator.make_block();
+            generator.emit<Bytecode::Op::JumpConditional>().set_targets(Bytecode::Label { case_block }, Bytecode::Label { *next_test_block });
+        } else {
+            default_block = &case_block;
+        }
+        case_blocks.append(case_block);
+    }
+    generator.switch_to_basic_block(*next_test_block);
+    auto& end_block = generator.make_block();
+
+    if (default_block != nullptr) {
+        generator.emit<Bytecode::Op::Jump>().set_targets(Bytecode::Label { *default_block }, {});
+    } else {
+        generator.emit<Bytecode::Op::LoadImmediate>(js_undefined());
+        generator.emit<Bytecode::Op::Jump>().set_targets(Bytecode::Label { end_block }, {});
+    }
+    auto current_block = case_blocks.begin();
+    generator.begin_breakable_scope(Bytecode::Label { end_block });
+    for (auto& switch_case : m_cases) {
+        generator.switch_to_basic_block(*current_block);
+
+        generator.emit<Bytecode::Op::LoadImmediate>(js_undefined());
+        for (auto& statement : switch_case.consequent()) {
+            statement.generate_bytecode(generator);
+        }
+        if (!generator.is_current_block_terminated()) {
+            auto next_block = current_block;
+            next_block++;
+            if (next_block.is_end()) {
+                generator.emit<Bytecode::Op::Jump>().set_targets(Bytecode::Label { end_block }, {});
+            } else {
+                generator.emit<Bytecode::Op::Jump>().set_targets(Bytecode::Label { *next_block }, {});
+            }
+        }
+        current_block++;
+    }
+    generator.end_breakable_scope();
+
+    generator.switch_to_basic_block(end_block);
+}
+
 }