diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index 15a792c9fd..8a10cd9fc4 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -557,6 +557,7 @@ public: virtual Value execute(Interpreter&, GlobalObject&) const override; virtual void dump(int indent) const override; + virtual Optional generate_bytecode(Bytecode::Generator&) const override; private: LogicalOp m_op; diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 41e2da493e..7c808aaee7 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -121,6 +121,53 @@ Optional BinaryExpression::generate_bytecode(Bytecode::Gener } } +Optional LogicalExpression::generate_bytecode(Bytecode::Generator& generator) const +{ + auto result_reg = generator.allocate_register(); + auto lhs_reg = m_lhs->generate_bytecode(generator); + + Bytecode::Instruction* test_instr; + switch (m_op) { + case LogicalOp::And: + test_instr = &generator.emit(*lhs_reg); + break; + case LogicalOp::Or: + test_instr = &generator.emit(*lhs_reg); + break; + case LogicalOp::NullishCoalescing: + test_instr = &generator.emit(*lhs_reg); + break; + default: + VERIFY_NOT_REACHED(); + } + + generator.emit(result_reg, *lhs_reg); + auto& end_jump = generator.emit(); + + auto rhs_label = generator.make_label(); + + switch (m_op) { + case LogicalOp::And: + static_cast(test_instr)->set_target(rhs_label); + break; + case LogicalOp::Or: + static_cast(test_instr)->set_target(rhs_label); + break; + case LogicalOp::NullishCoalescing: + static_cast(test_instr)->set_target(rhs_label); + break; + default: + VERIFY_NOT_REACHED(); + } + + auto rhs_reg = m_rhs->generate_bytecode(generator); + generator.emit(result_reg, *rhs_reg); + + end_jump.set_target(generator.make_label()); + + return result_reg; +} + Optional UnaryExpression::generate_bytecode(Bytecode::Generator& generator) const { auto lhs_reg = m_lhs->generate_bytecode(generator); diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h index 11d0bf2978..5679c47a92 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.h +++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h @@ -35,6 +35,7 @@ O(Jump) \ O(JumpIfFalse) \ O(JumpIfTrue) \ + O(JumpIfNullish) \ O(Call) \ O(EnterScope) \ O(Return) \ diff --git a/Userland/Libraries/LibJS/Bytecode/Op.cpp b/Userland/Libraries/LibJS/Bytecode/Op.cpp index e3d7a997e1..32057060f5 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Op.cpp @@ -165,6 +165,14 @@ void JumpIfTrue::execute(Bytecode::Interpreter& interpreter) const interpreter.jump(m_target.value()); } +void JumpIfNullish::execute(Bytecode::Interpreter& interpreter) const +{ + VERIFY(m_target.has_value()); + auto result = interpreter.reg(m_result); + if (result.is_nullish()) + interpreter.jump(m_target.value()); +} + void Call::execute(Bytecode::Interpreter& interpreter) const { auto callee = interpreter.reg(m_callee); @@ -272,6 +280,13 @@ String JumpIfTrue::to_string() const return String::formatted("JumpIfTrue result:{}, target:", m_result); } +String JumpIfNullish::to_string() const +{ + if (m_target.has_value()) + return String::formatted("JumpIfNullish result:{}, target:{}", m_result, m_target.value()); + return String::formatted("JumpIfNullish result:{}, target:", m_result); +} + String Call::to_string() const { StringBuilder builder; diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index 735dcbf331..d60138196c 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -284,6 +284,25 @@ private: Optional