/* * 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 { Bytecode::CodeGenerationErrorOr ASTNode::generate_bytecode(Bytecode::Generator&) const { return Bytecode::CodeGenerationError { this, "Missing generate_bytecode()"sv, }; } Bytecode::CodeGenerationErrorOr ScopeNode::generate_bytecode(Bytecode::Generator& generator) const { // FIXME: This is an ad-hoc fix but should be done as the spec says in // {Global, Block, Function, Eval}DeclarationInstantiation. for (auto& function : m_functions_hoistable_with_annexB_extension) { generator.emit(function); generator.emit(generator.intern_identifier(function.name())); } HashTable functions_initialized; for_each_var_function_declaration_in_reverse_order([&](FunctionDeclaration const& function) { if (functions_initialized.set(function.name()) != AK::HashSetResult::InsertedNewEntry) return; generator.emit(function); generator.emit(generator.intern_identifier(function.name())); }); // FIXME: Register lexical and variable scope declarations for (auto& child : children()) { TRY(child.generate_bytecode(generator)); if (generator.is_current_block_terminated()) break; } return {}; } Bytecode::CodeGenerationErrorOr EmptyStatement::generate_bytecode(Bytecode::Generator&) const { return {}; } Bytecode::CodeGenerationErrorOr ExpressionStatement::generate_bytecode(Bytecode::Generator& generator) const { return m_expression->generate_bytecode(generator); } Bytecode::CodeGenerationErrorOr BinaryExpression::generate_bytecode(Bytecode::Generator& generator) const { TRY(m_lhs->generate_bytecode(generator)); auto lhs_reg = generator.allocate_register(); generator.emit(lhs_reg); TRY(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::LooselyInequals: generator.emit(lhs_reg); break; case BinaryOp::LooselyEquals: generator.emit(lhs_reg); break; case BinaryOp::StrictlyInequals: generator.emit(lhs_reg); break; case BinaryOp::StrictlyEquals: 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(); } return {}; } Bytecode::CodeGenerationErrorOr LogicalExpression::generate_bytecode(Bytecode::Generator& generator) const { TRY(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); TRY(m_rhs->generate_bytecode(generator)); generator.emit().set_targets( Bytecode::Label { end_block }, {}); generator.switch_to_basic_block(end_block); return {}; } Bytecode::CodeGenerationErrorOr UnaryExpression::generate_bytecode(Bytecode::Generator& generator) const { TRY(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: return Bytecode::CodeGenerationError { this, "Unimplemented operation"sv, }; } return {}; } Bytecode::CodeGenerationErrorOr NumericLiteral::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(m_value); return {}; } Bytecode::CodeGenerationErrorOr BooleanLiteral::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(Value(m_value)); return {}; } Bytecode::CodeGenerationErrorOr NullLiteral::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(js_null()); return {}; } Bytecode::CodeGenerationErrorOr BigIntLiteral::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(Crypto::SignedBigInteger::from_base(10, m_value.substring(0, m_value.length() - 1))); return {}; } Bytecode::CodeGenerationErrorOr StringLiteral::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(generator.intern_string(m_value)); return {}; } Bytecode::CodeGenerationErrorOr 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); return {}; } Bytecode::CodeGenerationErrorOr Identifier::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(generator.intern_identifier(m_string)); return {}; } Bytecode::CodeGenerationErrorOr AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) const { // FIXME: Implement this for BindingPatterns too. auto& lhs = m_lhs.get>(); if (m_op == AssignmentOp::Assignment) { TRY(m_rhs->generate_bytecode(generator)); return generator.emit_store_to_reference(lhs); } TRY(generator.emit_load_from_reference(lhs)); 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); TRY(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: return Bytecode::CodeGenerationError { this, "Unimplemented operation"sv, }; } TRY(generator.emit_store_to_reference(lhs)); if (end_block_ptr) { generator.emit().set_targets( Bytecode::Label { *end_block_ptr }, {}); generator.switch_to_basic_block(*end_block_ptr); } return {}; } Bytecode::CodeGenerationErrorOr 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); TRY(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 }); TRY(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); } return {}; } Bytecode::CodeGenerationErrorOr 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); TRY(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 }); TRY(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); } return {}; } Bytecode::CodeGenerationErrorOr 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) TRY(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); TRY(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 }); TRY(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); TRY(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); } return {}; } Bytecode::CodeGenerationErrorOr 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) return Bytecode::CodeGenerationError { this, "Unimplemented property kind"sv, }; if (is(property.key())) { auto& string_literal = static_cast(property.key()); Bytecode::IdentifierTableIndex key_name = generator.intern_identifier(string_literal.value()); TRY(property.value().generate_bytecode(generator)); generator.emit(object_reg, key_name); } else { TRY(property.key().generate_bytecode(generator)); auto property_reg = generator.allocate_register(); generator.emit(property_reg); TRY(property.value().generate_bytecode(generator)); generator.emit(object_reg, property_reg); } } generator.emit(object_reg); return {}; } Bytecode::CodeGenerationErrorOr ArrayExpression::generate_bytecode(Bytecode::Generator& generator) const { Vector element_regs; for (auto& element : m_elements) { if (element) { TRY(element->generate_bytecode(generator)); if (is(*element)) { return Bytecode::CodeGenerationError { this, "Unimplemented element kind: SpreadExpression"sv, }; } } 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); return {}; } Bytecode::CodeGenerationErrorOr MemberExpression::generate_bytecode(Bytecode::Generator& generator) const { return generator.emit_load_from_reference(*this); } Bytecode::CodeGenerationErrorOr FunctionDeclaration::generate_bytecode(Bytecode::Generator&) const { return {}; } Bytecode::CodeGenerationErrorOr FunctionExpression::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(*this); return {}; } static Bytecode::CodeGenerationErrorOr generate_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode, Bytecode::Register const& value_reg); static Bytecode::CodeGenerationErrorOr generate_object_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode initialization_mode, 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_identifier(identifier); generator.emit_with_extra_register_slots(excluded_property_names.size(), value_reg, excluded_property_names); generator.emit(interned_identifier, initialization_mode); 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(generator.intern_identifier(identifier)); } else { auto expression = name.get>(); TRY(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); TRY(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); TRY(generate_binding_pattern_bytecode(generator, binding_pattern, initialization_mode, nested_value_reg)); } else if (alias.has()) { if (name.has>()) { // This needs some sort of SetVariableByValue opcode, as it's a runtime binding return Bytecode::CodeGenerationError { name.get>().ptr(), "Unimplemented name/alias pair: Empty/Expression"sv, }; } auto& identifier = alias.get>()->string(); generator.emit(generator.intern_identifier(identifier), initialization_mode); } else { auto& identifier = alias.get>()->string(); generator.emit(generator.intern_identifier(identifier), initialization_mode); } } return {}; } static Bytecode::CodeGenerationErrorOr generate_array_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode initialization_mode, 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) { return alias.visit( [&](Empty) -> Bytecode::CodeGenerationErrorOr { // This element is an elision return {}; }, [&](NonnullRefPtr const& identifier) -> Bytecode::CodeGenerationErrorOr { auto interned_index = generator.intern_identifier(identifier->string()); generator.emit(interned_index, initialization_mode); return {}; }, [&](NonnullRefPtr const& pattern) -> Bytecode::CodeGenerationErrorOr { // Store the accumulator value in a permanent register auto target_reg = generator.allocate_register(); generator.emit(target_reg); return generate_binding_pattern_bytecode(generator, pattern, initialization_mode, target_reg); }, [&](NonnullRefPtr const& expr) -> Bytecode::CodeGenerationErrorOr { return Bytecode::CodeGenerationError { expr.ptr(), "Unimplemented alias mode: MemberExpression"sv, }; }); }; 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); } return assign_accumulator_to_alias(alias); } // 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); TRY(assign_accumulator_to_alias(alias)); first = false; } return {}; } static Bytecode::CodeGenerationErrorOr generate_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode initialization_mode, Bytecode::Register const& value_reg) { if (pattern.kind == BindingPattern::Kind::Object) return generate_object_binding_pattern_bytecode(generator, pattern, initialization_mode, value_reg); return generate_array_binding_pattern_bytecode(generator, pattern, initialization_mode, value_reg); } Bytecode::CodeGenerationErrorOr VariableDeclaration::generate_bytecode(Bytecode::Generator& generator) const { for (auto& declarator : m_declarations) { if (declarator.init()) TRY(declarator.init()->generate_bytecode(generator)); else generator.emit(js_undefined()); auto initialization_mode = is_lexical_declaration() ? Bytecode::Op::SetVariable::InitializationMode::Initialize : Bytecode::Op::SetVariable::InitializationMode::Set; TRY(declarator.target().visit( [&](NonnullRefPtr const& id) -> Bytecode::CodeGenerationErrorOr { generator.emit(generator.intern_identifier(id->string()), initialization_mode); return {}; }, [&](NonnullRefPtr const& pattern) -> Bytecode::CodeGenerationErrorOr { auto value_register = generator.allocate_register(); generator.emit(value_register); return generate_binding_pattern_bytecode(generator, pattern, initialization_mode, value_register); })); } return {}; } Bytecode::CodeGenerationErrorOr 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)) { TRY(m_callee->generate_bytecode(generator)); generator.emit(callee_reg); } else if (is(*m_callee)) { return Bytecode::CodeGenerationError { this, "Unimplemented callee kind: SuperExpression"sv, }; } else if (is(*m_callee)) { auto& member_expression = static_cast(*m_callee); if (is(member_expression.object())) { return Bytecode::CodeGenerationError { this, "Unimplemented callee kind: MemberExpression on SuperExpression"sv, }; } TRY(member_expression.object().generate_bytecode(generator)); generator.emit(this_reg); if (member_expression.is_computed()) { TRY(member_expression.property().generate_bytecode(generator)); generator.emit(this_reg); } else { auto identifier_table_ref = generator.intern_identifier(verify_cast(member_expression.property()).string()); generator.emit(identifier_table_ref); } generator.emit(callee_reg); } else { // FIXME: this = global object in sloppy mode. TRY(m_callee->generate_bytecode(generator)); generator.emit(callee_reg); } Vector argument_registers; for (auto& arg : m_arguments) { TRY(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); return {}; } Bytecode::CodeGenerationErrorOr ReturnStatement::generate_bytecode(Bytecode::Generator& generator) const { if (m_argument) TRY(m_argument->generate_bytecode(generator)); if (generator.is_in_generator_or_async_function()) generator.emit(nullptr); else generator.emit(); return {}; } Bytecode::CodeGenerationErrorOr YieldExpression::generate_bytecode(Bytecode::Generator& generator) const { VERIFY(generator.is_in_generator_function()); if (m_is_yield_from) { return Bytecode::CodeGenerationError { this, "Unimplemented form: `yield*`"sv, }; } if (m_argument) TRY(m_argument->generate_bytecode(generator)); auto& continuation_block = generator.make_block(); generator.emit(Bytecode::Label { continuation_block }); generator.switch_to_basic_block(continuation_block); return {}; } Bytecode::CodeGenerationErrorOr 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(); TRY(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()); TRY(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) TRY(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); return {}; } Bytecode::CodeGenerationErrorOr ContinueStatement::generate_bytecode(Bytecode::Generator& generator) const { generator.emit().set_targets( generator.nearest_continuable_scope(), {}); return {}; } Bytecode::CodeGenerationErrorOr DebuggerStatement::generate_bytecode(Bytecode::Generator&) const { return {}; } Bytecode::CodeGenerationErrorOr 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(); TRY(m_test->generate_bytecode(generator)); generator.emit().set_targets( Bytecode::Label { true_block }, Bytecode::Label { false_block }); generator.switch_to_basic_block(true_block); TRY(m_consequent->generate_bytecode(generator)); generator.emit().set_targets( Bytecode::Label { end_block }, {}); generator.switch_to_basic_block(false_block); TRY(m_alternate->generate_bytecode(generator)); generator.emit().set_targets( Bytecode::Label { end_block }, {}); generator.switch_to_basic_block(end_block); return {}; } Bytecode::CodeGenerationErrorOr SequenceExpression::generate_bytecode(Bytecode::Generator& generator) const { for (auto& expression : m_expressions) TRY(expression.generate_bytecode(generator)); return {}; } Bytecode::CodeGenerationErrorOr TemplateLiteral::generate_bytecode(Bytecode::Generator& generator) const { auto string_reg = generator.allocate_register(); for (size_t i = 0; i < m_expressions.size(); i++) { TRY(m_expressions[i].generate_bytecode(generator)); if (i == 0) { generator.emit(string_reg); } else { generator.emit(string_reg); } } generator.emit(string_reg); return {}; } Bytecode::CodeGenerationErrorOr TaggedTemplateLiteral::generate_bytecode(Bytecode::Generator& generator) const { TRY(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; TRY(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; TRY(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()) { TRY(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_identifier("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)); return {}; } Bytecode::CodeGenerationErrorOr UpdateExpression::generate_bytecode(Bytecode::Generator& generator) const { TRY(generator.emit_load_from_reference(*m_argument)); 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(); TRY(generator.emit_store_to_reference(*m_argument)); if (!m_prefixed) generator.emit(*previous_value_for_postfix_reg); return {}; } Bytecode::CodeGenerationErrorOr ThrowStatement::generate_bytecode(Bytecode::Generator& generator) const { TRY(m_argument->generate_bytecode(generator)); generator.emit(); return {}; } Bytecode::CodeGenerationErrorOr BreakStatement::generate_bytecode(Bytecode::Generator& generator) const { generator.emit().set_targets( generator.nearest_breakable_scope(), {}); return {}; } Bytecode::CodeGenerationErrorOr 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); TRY(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); TRY(m_handler->parameter().visit( [&](FlyString const& parameter) -> Bytecode::CodeGenerationErrorOr { if (!parameter.is_empty()) { // FIXME: We need a separate DeclarativeEnvironment here generator.emit(generator.intern_identifier(parameter)); } return {}; }, [&](NonnullRefPtr const&) -> Bytecode::CodeGenerationErrorOr { // FIXME: Implement this path when the above DeclarativeEnvironment issue is dealt with. return Bytecode::CodeGenerationError { this, "Unimplemented catch argument: BindingPattern"sv, }; })); TRY(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); TRY(m_block->generate_bytecode(generator)); if (!generator.is_current_block_terminated()) { if (m_finalizer) { generator.emit(finalizer_target); } else { auto& block = generator.make_block(); generator.emit(Bytecode::Label { block }); next_block = █ } } generator.switch_to_basic_block(next_block ? *next_block : saved_block); return {}; } Bytecode::CodeGenerationErrorOr SwitchStatement::generate_bytecode(Bytecode::Generator& generator) const { auto discriminant_reg = generator.allocate_register(); TRY(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); TRY(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.children()) { TRY(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); return {}; } void ClassDeclaration::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(m_class_expression); generator.emit(generator.intern_identifier(m_class_expression.ptr()->name())); } Bytecode::CodeGenerationErrorOr ThisExpression::generate_bytecode(Bytecode::Generator& generator) const { generator.emit(); return {}; } Bytecode::CodeGenerationErrorOr AwaitExpression::generate_bytecode(Bytecode::Generator& generator) const { VERIFY(generator.is_in_async_function()); // Transform `await expr` to `yield expr` TRY(m_argument->generate_bytecode(generator)); auto& continuation_block = generator.make_block(); generator.emit(Bytecode::Label { continuation_block }); generator.switch_to_basic_block(continuation_block); return {}; } }