/* * Copyright (c) 2021, Andreas Kling * Copyright (c) 2021, Linus Groh * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include namespace JS { void ASTNode::generate_bytecode(Bytecode::Generator&) const { dbgln("Missing generate_bytecode() in {}", class_name()); TODO(); } void ScopeNode::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(*this); for (auto& child : children()) child.generate_bytecode(generator); } void EmptyStatement::generate_bytecode(Bytecode::Generator&) const { } void ExpressionStatement::generate_bytecode(Bytecode::Generator& generator) const { m_expression->generate_bytecode(generator); } void BinaryExpression::generate_bytecode(Bytecode::Generator& generator) const { m_lhs->generate_bytecode(generator); auto lhs_reg = generator.allocate_register(); generator.emit(lhs_reg); m_rhs->generate_bytecode(generator); switch (m_op) { case BinaryOp::Addition: generator.emit(lhs_reg); break; case BinaryOp::Subtraction: generator.emit(lhs_reg); break; case BinaryOp::Multiplication: generator.emit(lhs_reg); break; case BinaryOp::Division: generator.emit(lhs_reg); break; case BinaryOp::Modulo: generator.emit(lhs_reg); break; case BinaryOp::Exponentiation: generator.emit(lhs_reg); break; case BinaryOp::GreaterThan: generator.emit(lhs_reg); break; case BinaryOp::GreaterThanEquals: generator.emit(lhs_reg); break; case BinaryOp::LessThan: generator.emit(lhs_reg); break; case BinaryOp::LessThanEquals: generator.emit(lhs_reg); break; case BinaryOp::AbstractInequals: generator.emit(lhs_reg); break; case BinaryOp::AbstractEquals: generator.emit(lhs_reg); break; case BinaryOp::TypedInequals: generator.emit(lhs_reg); break; case BinaryOp::TypedEquals: generator.emit(lhs_reg); break; case BinaryOp::BitwiseAnd: generator.emit(lhs_reg); break; case BinaryOp::BitwiseOr: generator.emit(lhs_reg); break; case BinaryOp::BitwiseXor: generator.emit(lhs_reg); break; case BinaryOp::LeftShift: generator.emit(lhs_reg); break; case BinaryOp::RightShift: generator.emit(lhs_reg); break; case BinaryOp::UnsignedRightShift: generator.emit(lhs_reg); break; case BinaryOp::In: generator.emit(lhs_reg); break; case BinaryOp::InstanceOf: generator.emit(lhs_reg); break; default: VERIFY_NOT_REACHED(); } } void LogicalExpression::generate_bytecode(Bytecode::Generator& generator) const { m_lhs->generate_bytecode(generator); // lhs // jump op (true) end (false) rhs // rhs // jump always (true) end // end auto& rhs_block = generator.make_block(); auto& end_block = generator.make_block(); switch (m_op) { case LogicalOp::And: generator.emit().set_targets( Bytecode::Label { rhs_block }, Bytecode::Label { end_block }); break; case LogicalOp::Or: generator.emit().set_targets( Bytecode::Label { end_block }, Bytecode::Label { rhs_block }); break; case LogicalOp::NullishCoalescing: generator.emit().set_targets( Bytecode::Label { rhs_block }, Bytecode::Label { end_block }); break; default: VERIFY_NOT_REACHED(); } generator.switch_to_basic_block(rhs_block); m_rhs->generate_bytecode(generator); generator.emit().set_targets( Bytecode::Label { end_block }, {}); generator.switch_to_basic_block(end_block); } void UnaryExpression::generate_bytecode(Bytecode::Generator& generator) const { m_lhs->generate_bytecode(generator); switch (m_op) { case UnaryOp::BitwiseNot: generator.emit(); break; case UnaryOp::Not: generator.emit(); break; case UnaryOp::Plus: generator.emit(); break; case UnaryOp::Minus: generator.emit(); break; case UnaryOp::Typeof: generator.emit(); break; case UnaryOp::Void: generator.emit(js_undefined()); break; default: TODO(); } } void NumericLiteral::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(m_value); } void BooleanLiteral::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(Value(m_value)); } void NullLiteral::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(js_null()); } void BigIntLiteral::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(Crypto::SignedBigInteger::from_base10(m_value.substring(0, m_value.length() - 1))); } void StringLiteral::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(m_value); } void Identifier::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(m_string); } void AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) const { if (is(*m_lhs)) { auto& identifier = static_cast(*m_lhs); if (m_op == AssignmentOp::Assignment) { m_rhs->generate_bytecode(generator); generator.emit(identifier.string()); return; } m_lhs->generate_bytecode(generator); auto lhs_reg = generator.allocate_register(); generator.emit(lhs_reg); m_rhs->generate_bytecode(generator); switch (m_op) { case AssignmentOp::AdditionAssignment: generator.emit(lhs_reg); break; case AssignmentOp::SubtractionAssignment: generator.emit(lhs_reg); break; case AssignmentOp::MultiplicationAssignment: generator.emit(lhs_reg); break; case AssignmentOp::DivisionAssignment: generator.emit(lhs_reg); break; case AssignmentOp::ModuloAssignment: generator.emit(lhs_reg); break; case AssignmentOp::ExponentiationAssignment: generator.emit(lhs_reg); break; case AssignmentOp::BitwiseAndAssignment: generator.emit(lhs_reg); break; case AssignmentOp::BitwiseOrAssignment: generator.emit(lhs_reg); break; case AssignmentOp::BitwiseXorAssignment: generator.emit(lhs_reg); break; case AssignmentOp::LeftShiftAssignment: generator.emit(lhs_reg); break; case AssignmentOp::RightShiftAssignment: generator.emit(lhs_reg); break; case AssignmentOp::UnsignedRightShiftAssignment: generator.emit(lhs_reg); break; default: TODO(); } generator.emit(identifier.string()); return; } if (is(*m_lhs)) { auto& expression = static_cast(*m_lhs); expression.object().generate_bytecode(generator); auto object_reg = generator.allocate_register(); generator.emit(object_reg); if (expression.is_computed()) { TODO(); } else { VERIFY(is(expression.property())); m_rhs->generate_bytecode(generator); generator.emit(object_reg, static_cast(expression.property()).string()); return; } } TODO(); } void WhileStatement::generate_bytecode(Bytecode::Generator& generator) const { // test // jump if_false (true) end (false) body // body // jump always (true) test // end auto& test_block = generator.make_block(); auto& body_block = generator.make_block(); auto& end_block = generator.make_block(); // Init result register generator.emit(js_undefined()); auto result_reg = generator.allocate_register(); generator.emit(result_reg); // jump to the test block generator.emit().set_targets( Bytecode::Label { test_block }, {}); generator.switch_to_basic_block(test_block); m_test->generate_bytecode(generator); generator.emit().set_targets( Bytecode::Label { body_block }, Bytecode::Label { end_block }); generator.switch_to_basic_block(body_block); generator.begin_continuable_scope(Bytecode::Label { test_block }); m_body->generate_bytecode(generator); generator.emit().set_targets( Bytecode::Label { test_block }, {}); generator.end_continuable_scope(); generator.switch_to_basic_block(end_block); generator.emit(result_reg); } void DoWhileStatement::generate_bytecode(Bytecode::Generator& generator) const { // jump always (true) body // test // jump if_false (true) end (false) body // body // jump always (true) test // end auto& test_block = generator.make_block(); auto& body_block = generator.make_block(); auto& end_block = generator.make_block(); // Init result register generator.emit(js_undefined()); auto result_reg = generator.allocate_register(); generator.emit(result_reg); // jump to the body block generator.emit().set_targets( Bytecode::Label { body_block }, {}); generator.switch_to_basic_block(test_block); m_test->generate_bytecode(generator); generator.emit().set_targets( Bytecode::Label { body_block }, Bytecode::Label { end_block }); generator.switch_to_basic_block(body_block); generator.begin_continuable_scope(Bytecode::Label { test_block }); m_body->generate_bytecode(generator); generator.emit().set_targets( Bytecode::Label { test_block }, {}); generator.end_continuable_scope(); generator.switch_to_basic_block(end_block); generator.emit(result_reg); } void ForStatement::generate_bytecode(Bytecode::Generator& generator) const { // init // jump always (true) test // test // jump if_true (true) body (false) end // body // jump always (true) update // update // jump always (true) test // end // If 'test' is missing, fuse the 'test' and 'body' basic blocks // If 'update' is missing, fuse the 'body' and 'update' basic blocks Bytecode::BasicBlock* test_block_ptr { nullptr }; Bytecode::BasicBlock* body_block_ptr { nullptr }; Bytecode::BasicBlock* update_block_ptr { nullptr }; auto& end_block = generator.make_block(); if (m_init) m_init->generate_bytecode(generator); body_block_ptr = &generator.make_block(); if (m_test) test_block_ptr = &generator.make_block(); else test_block_ptr = body_block_ptr; if (m_update) update_block_ptr = &generator.make_block(); else update_block_ptr = body_block_ptr; generator.emit(js_undefined()); auto result_reg = generator.allocate_register(); generator.emit(result_reg); generator.emit().set_targets( Bytecode::Label { *test_block_ptr }, {}); if (m_test) { generator.switch_to_basic_block(*test_block_ptr); m_test->generate_bytecode(generator); generator.emit().set_targets( Bytecode::Label { *body_block_ptr }, Bytecode::Label { end_block }); } generator.switch_to_basic_block(*body_block_ptr); generator.begin_continuable_scope(Bytecode::Label { *update_block_ptr }); m_body->generate_bytecode(generator); generator.end_continuable_scope(); if (m_update) { generator.emit().set_targets( Bytecode::Label { *update_block_ptr }, {}); generator.switch_to_basic_block(*update_block_ptr); m_update->generate_bytecode(generator); } generator.emit().set_targets( Bytecode::Label { *test_block_ptr }, {}); generator.switch_to_basic_block(end_block); generator.emit(result_reg); } void ObjectExpression::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(); if (!m_properties.is_empty()) TODO(); } void ArrayExpression::generate_bytecode(Bytecode::Generator& generator) const { Vector element_regs; for (auto& element : m_elements) { if (element) { element->generate_bytecode(generator); if (is(*element)) { TODO(); continue; } } else { generator.emit(Value {}); } auto element_reg = generator.allocate_register(); generator.emit(element_reg); element_regs.append(element_reg); } generator.emit_with_extra_register_slots(element_regs.size(), element_regs); } void MemberExpression::generate_bytecode(Bytecode::Generator& generator) const { object().generate_bytecode(generator); if (is_computed()) { TODO(); } else { VERIFY(is(property())); generator.emit(static_cast(property()).string()); } } void FunctionDeclaration::generate_bytecode(Bytecode::Generator&) const { } void CallExpression::generate_bytecode(Bytecode::Generator& generator) const { m_callee->generate_bytecode(generator); auto callee_reg = generator.allocate_register(); generator.emit(callee_reg); // FIXME: Load the correct 'this' value into 'this_reg'. auto this_reg = generator.allocate_register(); generator.emit(js_undefined()); generator.emit(this_reg); Vector argument_registers; for (auto& arg : m_arguments) { arg.value->generate_bytecode(generator); auto arg_reg = generator.allocate_register(); generator.emit(arg_reg); argument_registers.append(arg_reg); } generator.emit_with_extra_register_slots(argument_registers.size(), callee_reg, this_reg, argument_registers); } void ReturnStatement::generate_bytecode(Bytecode::Generator& generator) const { if (m_argument) m_argument->generate_bytecode(generator); generator.emit(); } void IfStatement::generate_bytecode(Bytecode::Generator& generator) const { // test // jump if_true (true) true (false) false // true // jump always (true) end // false // jump always (true) end // end // If the 'false' branch doesn't exist, we're just gonna substitute it for 'end' and elide the last two entries above. auto& true_block = generator.make_block(); auto& false_block = generator.make_block(); m_predicate->generate_bytecode(generator); generator.emit().set_targets( Bytecode::Label { true_block }, Bytecode::Label { false_block }); Bytecode::Op::Jump* true_block_jump { nullptr }; generator.switch_to_basic_block(true_block); m_consequent->generate_bytecode(generator); if (!generator.is_current_block_terminated()) true_block_jump = &generator.emit(); generator.switch_to_basic_block(false_block); if (m_alternate) { auto& end_block = generator.make_block(); m_alternate->generate_bytecode(generator); if (!generator.is_current_block_terminated()) generator.emit().set_targets( Bytecode::Label { end_block }, {}); if (true_block_jump) true_block_jump->set_targets( Bytecode::Label { end_block }, {}); generator.switch_to_basic_block(end_block); } else { if (true_block_jump) true_block_jump->set_targets( Bytecode::Label { false_block }, {}); } } void ContinueStatement::generate_bytecode(Bytecode::Generator& generator) const { generator.emit().set_targets( generator.nearest_continuable_scope(), {}); } void DebuggerStatement::generate_bytecode(Bytecode::Generator&) const { } void ConditionalExpression::generate_bytecode(Bytecode::Generator& generator) const { // test // jump if_true (true) true (false) false // true // jump always (true) end // false // jump always (true) end // end auto& true_block = generator.make_block(); auto& false_block = generator.make_block(); auto& end_block = generator.make_block(); m_test->generate_bytecode(generator); generator.emit().set_targets( Bytecode::Label { true_block }, Bytecode::Label { false_block }); generator.switch_to_basic_block(true_block); m_consequent->generate_bytecode(generator); generator.emit().set_targets( Bytecode::Label { end_block }, {}); generator.switch_to_basic_block(false_block); m_alternate->generate_bytecode(generator); generator.emit().set_targets( Bytecode::Label { end_block }, {}); generator.switch_to_basic_block(end_block); } void SequenceExpression::generate_bytecode(Bytecode::Generator& generator) const { for (auto& expression : m_expressions) expression.generate_bytecode(generator); } void TemplateLiteral::generate_bytecode(Bytecode::Generator& generator) const { auto string_reg = generator.allocate_register(); for (size_t i = 0; i < m_expressions.size(); i++) { m_expressions[i].generate_bytecode(generator); if (i == 0) { generator.emit(string_reg); } else { generator.emit(string_reg); } } } void UpdateExpression::generate_bytecode(Bytecode::Generator& generator) const { if (is(*m_argument)) { auto& identifier = static_cast(*m_argument); generator.emit(identifier.string()); Optional previous_value_for_postfix_reg; if (!m_prefixed) { previous_value_for_postfix_reg = generator.allocate_register(); generator.emit(*previous_value_for_postfix_reg); } if (m_op == UpdateOp::Increment) generator.emit(); else generator.emit(); generator.emit(identifier.string()); if (!m_prefixed) generator.emit(*previous_value_for_postfix_reg); return; } TODO(); } }