/* * Copyright (c) 2021, Andreas Kling * Copyright (c) 2021, Linus Groh * Copyright (c) 2021, Gunnar Beutner * Copyright (c) 2021, Marcin Gasperowicz * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #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 { for (auto& function : functions()) { generator.emit(function); generator.emit(generator.intern_string(function.name())); } HashMap scope_variables_with_declaration_kind; bool is_program_node = is(*this); for (auto& declaration : variables()) { for (auto& declarator : declaration.declarations()) { if (is_program_node && declaration.declaration_kind() == DeclarationKind::Var) { declarator.target().visit( [&](const NonnullRefPtr& id) { generator.emit(js_undefined()); generator.emit(Bytecode::Register::global_object(), generator.intern_string(id->string())); }, [&](const NonnullRefPtr& binding) { binding->for_each_bound_name([&](const auto& name) { generator.emit(js_undefined()); generator.emit(Bytecode::Register::global_object(), generator.intern_string(name)); }); }); } else { declarator.target().visit( [&](const NonnullRefPtr& id) { scope_variables_with_declaration_kind.set((size_t)generator.intern_string(id->string()).value(), { js_undefined(), declaration.declaration_kind() }); }, [&](const NonnullRefPtr& binding) { binding->for_each_bound_name([&](const auto& name) { scope_variables_with_declaration_kind.set((size_t)generator.intern_string(name).value(), { js_undefined(), declaration.declaration_kind() }); }); }); } } } if (!scope_variables_with_declaration_kind.is_empty()) { generator.emit(move(scope_variables_with_declaration_kind)); } for (auto& child : children()) { child.generate_bytecode(generator); if (generator.is_current_block_terminated()) break; } } 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_base(10, m_value.substring(0, m_value.length() - 1))); } void StringLiteral::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(generator.intern_string(m_value)); } void RegExpLiteral::generate_bytecode(Bytecode::Generator& generator) const { auto source_index = generator.intern_string(m_pattern); auto flags_index = generator.intern_string(m_flags); generator.emit(source_index, flags_index); } void Identifier::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(generator.intern_string(m_string)); } void AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) const { // FIXME: Implement this for BindingPatterns too. auto& lhs = m_lhs.get>(); if (is(*lhs)) { auto& identifier = static_cast(*lhs); if (m_op == AssignmentOp::Assignment) { m_rhs->generate_bytecode(generator); generator.emit(generator.intern_string(identifier.string())); return; } lhs->generate_bytecode(generator); Bytecode::BasicBlock* rhs_block_ptr { nullptr }; Bytecode::BasicBlock* end_block_ptr { nullptr }; // Logical assignments short circuit. if (m_op == AssignmentOp::AndAssignment) { // &&= rhs_block_ptr = &generator.make_block(); end_block_ptr = &generator.make_block(); generator.emit().set_targets( Bytecode::Label { *rhs_block_ptr }, Bytecode::Label { *end_block_ptr }); } else if (m_op == AssignmentOp::OrAssignment) { // ||= rhs_block_ptr = &generator.make_block(); end_block_ptr = &generator.make_block(); generator.emit().set_targets( Bytecode::Label { *end_block_ptr }, Bytecode::Label { *rhs_block_ptr }); } else if (m_op == AssignmentOp::NullishAssignment) { // ??= rhs_block_ptr = &generator.make_block(); end_block_ptr = &generator.make_block(); generator.emit().set_targets( Bytecode::Label { *rhs_block_ptr }, Bytecode::Label { *end_block_ptr }); } if (rhs_block_ptr) generator.switch_to_basic_block(*rhs_block_ptr); // lhs_reg is a part of the rhs_block because the store isn't necessary // if the logical assignment condition fails. 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; case AssignmentOp::AndAssignment: case AssignmentOp::OrAssignment: case AssignmentOp::NullishAssignment: break; // These are handled above. default: TODO(); } generator.emit(generator.intern_string(identifier.string())); if (end_block_ptr) { generator.emit().set_targets( Bytecode::Label { *end_block_ptr }, {}); generator.switch_to_basic_block(*end_block_ptr); } return; } if (is(*lhs)) { auto& expression = static_cast(*lhs); expression.object().generate_bytecode(generator); auto object_reg = generator.allocate_register(); generator.emit(object_reg); if (expression.is_computed()) { expression.property().generate_bytecode(generator); auto property_reg = generator.allocate_register(); generator.emit(property_reg); m_rhs->generate_bytecode(generator); generator.emit(object_reg, property_reg); } else { m_rhs->generate_bytecode(generator); auto identifier_table_ref = generator.intern_string(verify_cast(expression.property()).string()); generator.emit(object_reg, identifier_table_ref); } 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 }); generator.begin_breakable_scope(Bytecode::Label { end_block }); m_body->generate_bytecode(generator); if (!generator.is_current_block_terminated()) { generator.emit().set_targets( Bytecode::Label { test_block }, {}); generator.end_continuable_scope(); generator.end_breakable_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 }); generator.begin_breakable_scope(Bytecode::Label { end_block }); m_body->generate_bytecode(generator); if (!generator.is_current_block_terminated()) { generator.emit().set_targets( Bytecode::Label { test_block }, {}); generator.end_continuable_scope(); generator.end_breakable_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 }); generator.begin_breakable_scope(Bytecode::Label { end_block }); m_body->generate_bytecode(generator); generator.end_continuable_scope(); if (!generator.is_current_block_terminated()) { 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.end_breakable_scope(); 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()) return; auto object_reg = generator.allocate_register(); generator.emit(object_reg); for (auto& property : m_properties) { if (property.type() != ObjectProperty::Type::KeyValue) TODO(); if (is(property.key())) { auto& string_literal = static_cast(property.key()); Bytecode::StringTableIndex key_name = generator.intern_string(string_literal.value()); property.value().generate_bytecode(generator); generator.emit(object_reg, key_name); } else { property.key().generate_bytecode(generator); auto property_reg = generator.allocate_register(); generator.emit(property_reg); property.value().generate_bytecode(generator); generator.emit(object_reg, property_reg); } } generator.emit(object_reg); } 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()) { auto object_reg = generator.allocate_register(); generator.emit(object_reg); property().generate_bytecode(generator); generator.emit(object_reg); } else { auto identifier_table_ref = generator.intern_string(verify_cast(property()).string()); generator.emit(identifier_table_ref); } } void FunctionDeclaration::generate_bytecode(Bytecode::Generator&) const { } void FunctionExpression::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(*this); } static void generate_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Register const& value_reg); static void generate_object_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Register const& value_reg) { Vector excluded_property_names; auto has_rest = false; if (pattern.entries.size() > 0) has_rest = pattern.entries[pattern.entries.size() - 1].is_rest; for (auto& [name, alias, initializer, is_rest] : pattern.entries) { if (is_rest) { VERIFY(name.has>()); VERIFY(alias.has()); VERIFY(!initializer); auto identifier = name.get>()->string(); auto interned_identifier = generator.intern_string(identifier); generator.emit_with_extra_register_slots(excluded_property_names.size(), value_reg, excluded_property_names); generator.emit(interned_identifier); return; } Bytecode::StringTableIndex name_index; if (name.has>()) { auto identifier = name.get>()->string(); name_index = generator.intern_string(identifier); if (has_rest) { auto excluded_name_reg = generator.allocate_register(); excluded_property_names.append(excluded_name_reg); generator.emit(name_index); generator.emit(excluded_name_reg); } generator.emit(value_reg); generator.emit(name_index); } else { auto expression = name.get>(); expression->generate_bytecode(generator); if (has_rest) { auto excluded_name_reg = generator.allocate_register(); excluded_property_names.append(excluded_name_reg); generator.emit(excluded_name_reg); } generator.emit(value_reg); } if (initializer) { auto& if_undefined_block = generator.make_block(); auto& if_not_undefined_block = generator.make_block(); generator.emit().set_targets( Bytecode::Label { if_undefined_block }, Bytecode::Label { if_not_undefined_block }); generator.switch_to_basic_block(if_undefined_block); initializer->generate_bytecode(generator); generator.emit().set_targets( Bytecode::Label { if_not_undefined_block }, {}); generator.switch_to_basic_block(if_not_undefined_block); } if (alias.has>()) { auto& binding_pattern = *alias.get>(); auto nested_value_reg = generator.allocate_register(); generator.emit(nested_value_reg); generate_binding_pattern_bytecode(generator, binding_pattern, nested_value_reg); } else if (alias.has()) { if (name.has>()) { // This needs some sort of SetVariableByValue opcode, as it's a runtime binding TODO(); } generator.emit(name_index); } else { auto& identifier = alias.get>()->string(); generator.emit(generator.intern_string(identifier)); } } } static void generate_array_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Register const& value_reg) { /* * Consider the following destructuring assignment: * * let [a, b, c, d, e] = o; * * It would be fairly trivial to just loop through this iterator, getting the value * at each step and assigning them to the binding sequentially. However, this is not * correct: once an iterator is exhausted, it must not be called again. This complicates * the bytecode. In order to accomplish this, we do the following: * * - Reserve a special boolean register which holds 'true' if the iterator is exhausted, * and false otherwise * - When we are retrieving the value which should be bound, we first check this register. * If it is 'true', we load undefined into the accumulator. Otherwise, we grab the next * value from the iterator and store it into the accumulator. * * Note that the is_exhausted register does not need to be loaded with false because the * first IteratorNext bytecode is _not_ proceeded by an exhausted check, as it is * unnecessary. */ auto is_iterator_exhausted_register = generator.allocate_register(); auto iterator_reg = generator.allocate_register(); generator.emit(value_reg); generator.emit(); generator.emit(iterator_reg); bool first = true; auto temp_iterator_result_reg = generator.allocate_register(); auto assign_accumulator_to_alias = [&](auto& alias) { alias.visit( [&](Empty) { // This element is an elision }, [&](NonnullRefPtr const& identifier) { auto interned_index = generator.intern_string(identifier->string()); generator.emit(interned_index); }, [&](NonnullRefPtr const& pattern) { // Store the accumulator value in a permanent register auto target_reg = generator.allocate_register(); generator.emit(target_reg); generate_binding_pattern_bytecode(generator, pattern, target_reg); }); }; for (auto& [name, alias, initializer, is_rest] : pattern.entries) { VERIFY(name.has()); if (is_rest) { if (first) { // The iterator has not been called, and is thus known to be not exhausted generator.emit(iterator_reg); generator.emit(); } else { auto& if_exhausted_block = generator.make_block(); auto& if_not_exhausted_block = generator.make_block(); auto& continuation_block = generator.make_block(); generator.emit(is_iterator_exhausted_register); generator.emit().set_targets( Bytecode::Label { if_exhausted_block }, Bytecode::Label { if_not_exhausted_block }); generator.switch_to_basic_block(if_exhausted_block); generator.emit(); generator.emit().set_targets( Bytecode::Label { continuation_block }, {}); generator.switch_to_basic_block(if_not_exhausted_block); generator.emit(iterator_reg); generator.emit(); generator.emit().set_targets( Bytecode::Label { continuation_block }, {}); generator.switch_to_basic_block(continuation_block); } assign_accumulator_to_alias(alias); return; } // In the first iteration of the loop, a few things are true which can save // us some bytecode: // - the iterator result is still in the accumulator, so we can avoid a load // - the iterator is not yet exhausted, which can save us a jump and some // creation auto& iterator_is_exhausted_block = generator.make_block(); if (!first) { auto& iterator_is_not_exhausted_block = generator.make_block(); generator.emit(is_iterator_exhausted_register); generator.emit().set_targets( Bytecode::Label { iterator_is_exhausted_block }, Bytecode::Label { iterator_is_not_exhausted_block }); generator.switch_to_basic_block(iterator_is_not_exhausted_block); generator.emit(iterator_reg); } generator.emit(); generator.emit(temp_iterator_result_reg); generator.emit(); generator.emit(is_iterator_exhausted_register); // We still have to check for exhaustion here. If the iterator is exhausted, // we need to bail before trying to get the value auto& no_bail_block = generator.make_block(); generator.emit().set_targets( Bytecode::Label { iterator_is_exhausted_block }, Bytecode::Label { no_bail_block }); generator.switch_to_basic_block(no_bail_block); // Get the next value in the iterator generator.emit(temp_iterator_result_reg); generator.emit(); auto& create_binding_block = generator.make_block(); generator.emit().set_targets( Bytecode::Label { create_binding_block }, {}); // The iterator is exhausted, so we just load undefined and continue binding generator.switch_to_basic_block(iterator_is_exhausted_block); generator.emit(js_undefined()); generator.emit().set_targets( Bytecode::Label { create_binding_block }, {}); // Create the actual binding. The value which this entry must bind is now in the // accumulator. We can proceed, processing the alias as a nested destructuring // pattern if necessary. generator.switch_to_basic_block(create_binding_block); assign_accumulator_to_alias(alias); first = false; } } static void generate_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Register const& value_reg) { if (pattern.kind == BindingPattern::Kind::Object) { generate_object_binding_pattern_bytecode(generator, pattern, value_reg); } else { generate_array_binding_pattern_bytecode(generator, pattern, value_reg); } }; void VariableDeclaration::generate_bytecode(Bytecode::Generator& generator) const { for (auto& declarator : m_declarations) { if (declarator.init()) declarator.init()->generate_bytecode(generator); else generator.emit(js_undefined()); declarator.target().visit( [&](NonnullRefPtr const& id) { generator.emit(generator.intern_string(id->string())); }, [&](NonnullRefPtr const& pattern) { auto value_register = generator.allocate_register(); generator.emit(value_register); generate_binding_pattern_bytecode(generator, pattern, value_register); }); } } void CallExpression::generate_bytecode(Bytecode::Generator& generator) const { auto callee_reg = generator.allocate_register(); auto this_reg = generator.allocate_register(); generator.emit(js_undefined()); generator.emit(this_reg); if (is(this)) { m_callee->generate_bytecode(generator); generator.emit(callee_reg); } else if (is(*m_callee)) { TODO(); } else if (is(*m_callee)) { auto& member_expression = static_cast(*m_callee); if (is(member_expression.object())) { TODO(); } else { member_expression.object().generate_bytecode(generator); generator.emit(this_reg); // FIXME: Don't copy this logic here, make MemberExpression generate it. if (!is(member_expression.property())) TODO(); auto identifier_table_ref = generator.intern_string(static_cast(member_expression.property()).string()); generator.emit(identifier_table_ref); generator.emit(callee_reg); } } else { // FIXME: this = global object in sloppy mode. m_callee->generate_bytecode(generator); generator.emit(callee_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); } Bytecode::Op::Call::CallType call_type; if (is(*this)) { call_type = Bytecode::Op::Call::CallType::Construct; } else { call_type = Bytecode::Op::Call::CallType::Call; } generator.emit_with_extra_register_slots(argument_registers.size(), call_type, callee_reg, this_reg, argument_registers); } void ReturnStatement::generate_bytecode(Bytecode::Generator& generator) const { if (m_argument) m_argument->generate_bytecode(generator); if (generator.is_in_generator_function()) generator.emit(nullptr); else generator.emit(); } void YieldExpression::generate_bytecode(Bytecode::Generator& generator) const { VERIFY(generator.is_in_generator_function()); if (m_is_yield_from) TODO(); if (m_argument) m_argument->generate_bytecode(generator); auto& continuation_block = generator.make_block(); generator.emit(Bytecode::Label { continuation_block }); generator.switch_to_basic_block(continuation_block); } 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 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); generator.emit(js_undefined()); m_consequent->generate_bytecode(generator); if (!generator.is_current_block_terminated()) true_block_jump = &generator.emit(); generator.switch_to_basic_block(false_block); auto& end_block = generator.make_block(); generator.emit(js_undefined()); if (m_alternate) 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); } 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); } } generator.emit(string_reg); } void TaggedTemplateLiteral::generate_bytecode(Bytecode::Generator& generator) const { m_tag->generate_bytecode(generator); auto tag_reg = generator.allocate_register(); generator.emit(tag_reg); Vector string_regs; auto& expressions = m_template_literal->expressions(); for (size_t i = 0; i < expressions.size(); ++i) { if (i % 2 != 0) continue; expressions[i].generate_bytecode(generator); auto string_reg = generator.allocate_register(); generator.emit(string_reg); string_regs.append(string_reg); } generator.emit_with_extra_register_slots(string_regs.size(), string_regs); auto strings_reg = generator.allocate_register(); generator.emit(strings_reg); Vector argument_regs; argument_regs.append(strings_reg); for (size_t i = 0; i < expressions.size(); ++i) { if (i % 2 == 0) continue; expressions[i].generate_bytecode(generator); auto string_reg = generator.allocate_register(); generator.emit(string_reg); argument_regs.append(string_reg); } Vector raw_string_regs; for (auto& raw_string : m_template_literal->raw_strings()) { raw_string.generate_bytecode(generator); auto raw_string_reg = generator.allocate_register(); generator.emit(raw_string_reg); raw_string_regs.append(raw_string_reg); } generator.emit_with_extra_register_slots(raw_string_regs.size(), raw_string_regs); auto raw_strings_reg = generator.allocate_register(); generator.emit(raw_strings_reg); generator.emit(strings_reg); generator.emit(raw_strings_reg, generator.intern_string("raw")); generator.emit(js_undefined()); auto this_reg = generator.allocate_register(); generator.emit(this_reg); generator.emit_with_extra_register_slots(argument_regs.size(), Bytecode::Op::Call::CallType::Call, tag_reg, this_reg, move(argument_regs)); } void UpdateExpression::generate_bytecode(Bytecode::Generator& generator) const { if (is(*m_argument)) { auto& identifier = static_cast(*m_argument); generator.emit(generator.intern_string(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(generator.intern_string(identifier.string())); if (!m_prefixed) generator.emit(*previous_value_for_postfix_reg); return; } TODO(); } void ThrowStatement::generate_bytecode(Bytecode::Generator& generator) const { m_argument->generate_bytecode(generator); generator.emit(); } void BreakStatement::generate_bytecode(Bytecode::Generator& generator) const { generator.emit().set_targets( generator.nearest_breakable_scope(), {}); } void TryStatement::generate_bytecode(Bytecode::Generator& generator) const { auto& saved_block = generator.current_block(); Optional handler_target; Optional finalizer_target; Bytecode::BasicBlock* next_block { nullptr }; if (m_finalizer) { auto& finalizer_block = generator.make_block(); generator.switch_to_basic_block(finalizer_block); m_finalizer->generate_bytecode(generator); if (!generator.is_current_block_terminated()) { next_block = &generator.make_block(); auto next_target = Bytecode::Label { *next_block }; generator.emit(next_target); } finalizer_target = Bytecode::Label { finalizer_block }; } if (m_handler) { auto& handler_block = generator.make_block(); generator.switch_to_basic_block(handler_block); if (!m_finalizer) generator.emit(); m_handler->parameter().visit( [&](FlyString const& parameter) { if (parameter.is_empty()) { // FIXME: We need a separate DeclarativeEnvironment here generator.emit(generator.intern_string(parameter)); } }, [&](NonnullRefPtr const&) { // FIXME: Implement this path when the above DeclrativeEnvironment issue is dealt with. TODO(); }); m_handler->body().generate_bytecode(generator); handler_target = Bytecode::Label { handler_block }; if (!generator.is_current_block_terminated()) { if (m_finalizer) { generator.emit(); generator.emit(finalizer_target); } else { VERIFY(!next_block); next_block = &generator.make_block(); auto next_target = Bytecode::Label { *next_block }; generator.emit(next_target); } } } auto& target_block = generator.make_block(); generator.switch_to_basic_block(saved_block); generator.emit(Bytecode::Label { target_block }, handler_target, finalizer_target); generator.switch_to_basic_block(target_block); m_block->generate_bytecode(generator); if (m_finalizer && !generator.is_current_block_terminated()) generator.emit(finalizer_target); 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(discriminant_reg); Vector case_blocks; Bytecode::BasicBlock* default_block { nullptr }; Bytecode::BasicBlock* next_test_block = &generator.make_block(); generator.emit().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(discriminant_reg); next_test_block = &generator.make_block(); generator.emit().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().set_targets(Bytecode::Label { *default_block }, {}); } else { generator.emit(js_undefined()); generator.emit().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(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().set_targets(Bytecode::Label { end_block }, {}); } else { generator.emit().set_targets(Bytecode::Label { *next_block }, {}); } } current_block++; } generator.end_breakable_scope(); generator.switch_to_basic_block(end_block); } void ClassDeclaration::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(m_class_expression); generator.emit(generator.intern_string(m_class_expression.ptr()->name())); } }