diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index 3efc5d7670..b6d8e878a9 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -239,7 +239,7 @@ ThrowCompletionOr ClassField::class_element_evaluation // FIXME: A potential optimization is not creating the functions here since these are never directly accessible. auto function_code = create_ast_node(m_initializer->source_range(), copy_initializer.release_nonnull(), name); - initializer = make_handle(*ECMAScriptFunctionObject::create(realm, ByteString::empty(), ByteString::empty(), *function_code, {}, 0, {}, vm.lexical_environment(), vm.running_execution_context().private_environment, FunctionKind::Normal, true, false, m_contains_direct_call_to_eval, false, property_key_or_private_name)); + initializer = make_handle(*ECMAScriptFunctionObject::create(realm, "field", ByteString::empty(), *function_code, {}, 0, {}, vm.lexical_environment(), vm.running_execution_context().private_environment, FunctionKind::Normal, true, false, m_contains_direct_call_to_eval, false, property_key_or_private_name)); initializer->make_method(target); } diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 17acb46770..0266c87c9f 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -7,8 +7,6 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#define FIXME_NEWBC (void) - #include #include #include @@ -21,6 +19,13 @@ namespace JS { +static Bytecode::Operand choose_dst(Bytecode::Generator& generator, Optional const& preferred_dst) +{ + if (preferred_dst.has_value()) + return preferred_dst.value(); + return Bytecode::Operand(generator.allocate_register()); +} + Bytecode::CodeGenerationErrorOr> ASTNode::generate_bytecode(Bytecode::Generator&, [[maybe_unused]] Optional preferred_dst) const { return Bytecode::CodeGenerationError { @@ -45,8 +50,11 @@ Bytecode::CodeGenerationErrorOr> ScopeNode::generate // FunctionDeclarationInstantiation is handled by the C++ AO. } + Optional last_result; for (auto& child : children()) { - FIXME_NEWBC TRY(child->generate_bytecode(generator)); + auto result = TRY(child->generate_bytecode(generator)); + if (result.has_value()) + last_result = result; if (generator.is_current_block_terminated()) break; } @@ -54,7 +62,7 @@ Bytecode::CodeGenerationErrorOr> ScopeNode::generate if (did_create_lexical_environment) generator.end_variable_scope(); - return Optional {}; + return last_result; } Bytecode::CodeGenerationErrorOr> EmptyStatement::generate_bytecode(Bytecode::Generator&, [[maybe_unused]] Optional preferred_dst) const @@ -68,99 +76,101 @@ Bytecode::CodeGenerationErrorOr> ExpressionStatement return m_expression->generate_bytecode(generator); } -Bytecode::CodeGenerationErrorOr> BinaryExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> BinaryExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); if (m_op == BinaryOp::In && is(*m_lhs)) { auto const& private_identifier = static_cast(*m_lhs).string(); - FIXME_NEWBC TRY(m_rhs->generate_bytecode(generator)); - generator.emit(generator.intern_identifier(private_identifier)); - return Optional {}; + auto base = TRY(m_rhs->generate_bytecode(generator)).value(); + auto dst = choose_dst(generator, preferred_dst); + generator.emit(dst, base, generator.intern_identifier(private_identifier)); + return dst; } - FIXME_NEWBC TRY(m_lhs->generate_bytecode(generator)); - auto lhs_reg = generator.allocate_register(); - generator.emit(lhs_reg); - - FIXME_NEWBC TRY(m_rhs->generate_bytecode(generator)); + auto lhs = TRY(m_lhs->generate_bytecode(generator)).value(); + auto rhs = TRY(m_rhs->generate_bytecode(generator)).value(); + auto dst = choose_dst(generator, preferred_dst); switch (m_op) { case BinaryOp::Addition: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::Subtraction: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::Multiplication: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::Division: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::Modulo: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::Exponentiation: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::GreaterThan: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::GreaterThanEquals: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::LessThan: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::LessThanEquals: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::LooselyInequals: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::LooselyEquals: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::StrictlyInequals: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::StrictlyEquals: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::BitwiseAnd: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::BitwiseOr: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::BitwiseXor: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::LeftShift: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::RightShift: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::UnsignedRightShift: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::In: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case BinaryOp::InstanceOf: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; default: VERIFY_NOT_REACHED(); } - return Optional {}; + return dst; } -Bytecode::CodeGenerationErrorOr> LogicalExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> LogicalExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - FIXME_NEWBC TRY(m_lhs->generate_bytecode(generator)); + auto dst = choose_dst(generator, preferred_dst); + auto lhs = TRY(m_lhs->generate_bytecode(generator, preferred_dst)).value(); + // FIXME: Only mov lhs into dst in case lhs is the value taken. + generator.emit(dst, lhs); // lhs // jump op (true) end (false) rhs @@ -173,17 +183,20 @@ Bytecode::CodeGenerationErrorOr> LogicalExpression:: switch (m_op) { case LogicalOp::And: - generator.emit( + generator.emit( + lhs, Bytecode::Label { rhs_block }, Bytecode::Label { end_block }); break; case LogicalOp::Or: - generator.emit( + generator.emit( + lhs, Bytecode::Label { end_block }, Bytecode::Label { rhs_block }); break; case LogicalOp::NullishCoalescing: generator.emit( + lhs, Bytecode::Label { rhs_block }, Bytecode::Label { end_block }); break; @@ -192,83 +205,81 @@ Bytecode::CodeGenerationErrorOr> LogicalExpression:: } generator.switch_to_basic_block(rhs_block); - FIXME_NEWBC TRY(m_rhs->generate_bytecode(generator)); + auto rhs = TRY(m_rhs->generate_bytecode(generator)).value(); + generator.emit(dst, rhs); generator.emit(Bytecode::Label { end_block }); generator.switch_to_basic_block(end_block); - return Optional {}; + return dst; } -Bytecode::CodeGenerationErrorOr> UnaryExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> UnaryExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); if (m_op == UnaryOp::Delete) return generator.emit_delete_reference(m_lhs); + Optional src; // Typeof needs some special handling for when the LHS is an Identifier. Namely, it shouldn't throw on unresolvable references, but instead return "undefined". if (m_op != UnaryOp::Typeof) - FIXME_NEWBC TRY(m_lhs->generate_bytecode(generator)); + src = TRY(m_lhs->generate_bytecode(generator)).value(); + + auto dst = choose_dst(generator, preferred_dst); switch (m_op) { case UnaryOp::BitwiseNot: - generator.emit(); + generator.emit(dst, *src); break; case UnaryOp::Not: - generator.emit(); + generator.emit(dst, *src); break; case UnaryOp::Plus: - generator.emit(); + generator.emit(dst, *src); break; case UnaryOp::Minus: - generator.emit(); + generator.emit(dst, *src); break; case UnaryOp::Typeof: if (is(*m_lhs)) { auto& identifier = static_cast(*m_lhs); - if (identifier.is_local()) { - generator.emit(identifier.local_variable_index()); - } else { - generator.emit(generator.intern_identifier(identifier.string())); + if (!identifier.is_local()) { + generator.emit(dst, generator.intern_identifier(identifier.string())); + break; } - break; } - FIXME_NEWBC TRY(m_lhs->generate_bytecode(generator)); - generator.emit(); + src = TRY(m_lhs->generate_bytecode(generator)).value(); + generator.emit(dst, *src); break; case UnaryOp::Void: - generator.emit(js_undefined()); - break; + return generator.add_constant(js_undefined()); case UnaryOp::Delete: // Delete is implemented above. default: VERIFY_NOT_REACHED(); } - return Optional {}; + return dst; } Bytecode::CodeGenerationErrorOr> NumericLiteral::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - generator.emit(m_value); - return Optional {}; + return generator.add_constant(Value(m_value), Bytecode::Generator::DeduplicateConstant::No); } Bytecode::CodeGenerationErrorOr> BooleanLiteral::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - generator.emit(Value(m_value)); - return Optional {}; + return generator.add_constant(Value(m_value)); } Bytecode::CodeGenerationErrorOr> NullLiteral::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - generator.emit(js_null()); - return Optional {}; + return generator.add_constant(js_null()); } -Bytecode::CodeGenerationErrorOr> BigIntLiteral::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> BigIntLiteral::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); // 1. Return the NumericValue of NumericLiteral as defined in 12.8.3. @@ -283,18 +294,20 @@ Bytecode::CodeGenerationErrorOr> BigIntLiteral::gene return MUST(Crypto::SignedBigInteger::from_base(10, m_value.substring(0, m_value.length() - 1))); }(); - generator.emit(integer); - return Optional {}; + auto dst = choose_dst(generator, preferred_dst); + generator.emit(dst, integer); + return dst; } Bytecode::CodeGenerationErrorOr> StringLiteral::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - generator.emit(generator.intern_string(m_value)); - return Optional {}; + auto dst = choose_dst(generator, preferred_dst); + generator.emit(dst, generator.intern_string(m_value)); + return dst; } -Bytecode::CodeGenerationErrorOr> RegExpLiteral::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> RegExpLiteral::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); auto source_index = generator.intern_string(m_pattern); @@ -304,29 +317,38 @@ Bytecode::CodeGenerationErrorOr> RegExpLiteral::gene .pattern = m_parsed_pattern, .flags = m_parsed_flags, }); - generator.emit(source_index, flags_index, regex_index); - return Optional {}; + auto dst = choose_dst(generator, preferred_dst); + generator.emit(dst, source_index, flags_index, regex_index); + return dst; } -Bytecode::CodeGenerationErrorOr> Identifier::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> Identifier::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - if (is_global()) { - generator.emit(generator.intern_identifier(m_string), generator.next_global_variable_cache()); - } else if (is_local()) { - generator.emit(local_variable_index()); - } else { - generator.emit(generator.intern_identifier(m_string), generator.next_environment_variable_cache()); + + if (is_local()) { + auto local = Bytecode::Operand(Bytecode::Operand::Type::Local, local_variable_index()); + if (!generator.is_local_initialized(local_variable_index())) { + generator.emit(local); + } + return local; } - return Optional {}; + + auto dst = choose_dst(generator, preferred_dst); + if (is_global()) { + generator.emit(dst, generator.intern_identifier(m_string), generator.next_global_variable_cache()); + } else { + generator.emit(dst, generator.intern_identifier(m_string), generator.next_environment_variable_cache()); + } + return dst; } -static Bytecode::CodeGenerationErrorOr> arguments_to_array_for_call(Bytecode::Generator& generator, ReadonlySpan arguments, [[maybe_unused]] Optional preferred_dst = {}) +static Bytecode::CodeGenerationErrorOr> arguments_to_array_for_call(Bytecode::Generator& generator, ReadonlySpan arguments) { - + auto dst = Bytecode::Operand(generator.allocate_register()); if (arguments.is_empty()) { - generator.emit(); - return Optional {}; + generator.emit(dst); + return dst; } auto first_spread = find_if(arguments.begin(), arguments.end(), [](auto el) { return el.is_spread; }); @@ -341,51 +363,49 @@ static Bytecode::CodeGenerationErrorOr> arguments_to for (auto it = arguments.begin(); it != first_spread; ++it, ++i) { VERIFY(it->is_spread == false); Bytecode::Register reg { args_start_reg.index() + i }; - FIXME_NEWBC TRY(it->value->generate_bytecode(generator)); - generator.emit(reg); + auto value = TRY(it->value->generate_bytecode(generator)).value(); + generator.emit(Bytecode::Operand(reg), value); } if (first_spread.index() != 0) - generator.emit_with_extra_register_slots(2u, AK::Array { args_start_reg, Bytecode::Register { args_start_reg.index() + static_cast(first_spread.index() - 1) } }); + generator.emit_with_extra_operand_slots(2u, dst, AK::Array { Bytecode::Operand(args_start_reg), Bytecode::Operand { Bytecode::Register { args_start_reg.index() + static_cast(first_spread.index() - 1) } } }); else - generator.emit(); + generator.emit(dst); if (first_spread != arguments.end()) { - auto array_reg = generator.allocate_register(); - generator.emit(array_reg); for (auto it = first_spread; it != arguments.end(); ++it) { - FIXME_NEWBC TRY(it->value->generate_bytecode(generator)); - generator.emit(array_reg, it->is_spread); + auto value = TRY(it->value->generate_bytecode(generator)).value(); + generator.emit(dst, value, it->is_spread); } - generator.emit(array_reg); } - return Optional {}; + return dst; } -Bytecode::CodeGenerationErrorOr> SuperCall::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> SuperCall::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); + Optional arguments; if (m_is_synthetic == IsPartOfSyntheticConstructor::Yes) { // NOTE: This is the case where we have a fake constructor(...args) { super(...args); } which // shouldn't call @@iterator of %Array.prototype%. VERIFY(m_arguments.size() == 1); VERIFY(m_arguments[0].is_spread); auto const& argument = m_arguments[0]; - // This generates a single argument, which will be implicitly passed in accumulator - FIXME_NEWBC MUST(argument.value->generate_bytecode(generator)); + // This generates a single argument. + arguments = MUST(argument.value->generate_bytecode(generator)); } else { - FIXME_NEWBC TRY(arguments_to_array_for_call(generator, m_arguments)); + arguments = TRY(arguments_to_array_for_call(generator, m_arguments)).value(); } - generator.emit(m_is_synthetic == IsPartOfSyntheticConstructor::Yes); - - return Optional {}; + auto dst = choose_dst(generator, preferred_dst); + generator.emit(dst, *arguments, m_is_synthetic == IsPartOfSyntheticConstructor::Yes); + return dst; } -static Bytecode::CodeGenerationErrorOr> generate_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode, Bytecode::Register const& value_reg, bool create_variables, [[maybe_unused]] Optional preferred_dst = {}); +static Bytecode::CodeGenerationErrorOr generate_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode, Bytecode::Operand const& input_value, bool create_variables); -Bytecode::CodeGenerationErrorOr> AssignmentExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> AssignmentExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); if (m_op == AssignmentOp::Assignment) { @@ -395,27 +415,24 @@ Bytecode::CodeGenerationErrorOr> AssignmentExpressio [&](NonnullRefPtr const& lhs) -> Bytecode::CodeGenerationErrorOr> { // a. Let lref be the result of evaluating LeftHandSideExpression. // b. ReturnIfAbrupt(lref). - Optional base_object_register; - Optional computed_property_register; - Optional this_value_register; + Optional base; + Optional computed_property; + Optional this_value; bool lhs_is_super_expression = false; if (is(*lhs)) { auto& expression = static_cast(*lhs); lhs_is_super_expression = is(expression.object()); - base_object_register = generator.allocate_register(); if (!lhs_is_super_expression) { - FIXME_NEWBC TRY(expression.object().generate_bytecode(generator)); - generator.emit(*base_object_register); + base = TRY(expression.object().generate_bytecode(generator)).value(); } else { // https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation // 1. Let env be GetThisEnvironment(). // 2. Let actualThis be ? env.GetThisBinding(). - this_value_register = generator.allocate_register(); - generator.emit(); - generator.emit(*this_value_register); + this_value = Bytecode::Operand(generator.allocate_register()); + generator.emit(*this_value); // SuperProperty : super [ Expression ] // 3. Let propertyNameReference be ? Evaluation of Expression. @@ -423,9 +440,7 @@ Bytecode::CodeGenerationErrorOr> AssignmentExpressio } if (expression.is_computed()) { - FIXME_NEWBC TRY(expression.property().generate_bytecode(generator)); - computed_property_register = generator.allocate_register(); - generator.emit(*computed_property_register); + computed_property = TRY(expression.property().generate_bytecode(generator)).value(); // To be continued later with PutByValue. } else if (expression.property().is_identifier()) { @@ -447,14 +462,14 @@ Bytecode::CodeGenerationErrorOr> AssignmentExpressio // 2. Assert: env.HasSuperBinding() is true. // 3. Let baseValue be ? env.GetSuperBase(). // 4. Return the Reference Record { [[Base]]: baseValue, [[ReferencedName]]: propertyKey, [[Strict]]: strict, [[ThisValue]]: actualThis }. - generator.emit(); - generator.emit(*base_object_register); + base = Bytecode::Operand(generator.allocate_register()); + generator.emit(*base); } } else if (is(*lhs)) { // NOTE: For Identifiers, we cannot perform GetVariable and then write into the reference it retrieves, only SetVariable can do this. // FIXME: However, this breaks spec as we are doing variable lookup after evaluating the RHS. This is observable in an object environment, where we visibly perform HasOwnProperty and Get(@@unscopables) on the binded object. } else { - FIXME_NEWBC TRY(lhs->generate_bytecode(generator)); + (void)TRY(lhs->generate_bytecode(generator)); } // FIXME: c. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then @@ -463,33 +478,35 @@ Bytecode::CodeGenerationErrorOr> AssignmentExpressio // d. Else, // i. Let rref be the result of evaluating AssignmentExpression. // ii. Let rval be ? GetValue(rref). - if (lhs->is_identifier()) { - TRY(generator.emit_named_evaluation_if_anonymous_function(*m_rhs, generator.intern_identifier(static_cast(*lhs).string()))); - } else { - FIXME_NEWBC TRY(m_rhs->generate_bytecode(generator)); - } + auto rval = TRY([&]() -> Bytecode::CodeGenerationErrorOr { + if (lhs->is_identifier()) { + return TRY(generator.emit_named_evaluation_if_anonymous_function(*m_rhs, generator.intern_identifier(static_cast(*lhs).string()))).value(); + } else { + return TRY(m_rhs->generate_bytecode(generator)).value(); + } + }()); // e. Perform ? PutValue(lref, rval). if (is(*lhs)) { auto& identifier = static_cast(*lhs); - generator.emit_set_variable(identifier); + generator.emit_set_variable(identifier, rval); } else if (is(*lhs)) { auto& expression = static_cast(*lhs); if (expression.is_computed()) { if (!lhs_is_super_expression) - generator.emit(*base_object_register, *computed_property_register); + generator.emit(*base, *computed_property, rval); else - generator.emit(*base_object_register, *computed_property_register, *this_value_register); + generator.emit(*base, *computed_property, *this_value, rval); } else if (expression.property().is_identifier()) { auto identifier_table_ref = generator.intern_identifier(verify_cast(expression.property()).string()); if (!lhs_is_super_expression) - generator.emit(*base_object_register, identifier_table_ref, Bytecode::Op::PropertyKind::KeyValue, generator.next_property_lookup_cache()); + generator.emit(*base, identifier_table_ref, rval, Bytecode::Op::PropertyKind::KeyValue, generator.next_property_lookup_cache()); else - generator.emit(*base_object_register, *this_value_register, identifier_table_ref, Bytecode::Op::PropertyKind::KeyValue, generator.next_property_lookup_cache()); + generator.emit(*base, *this_value, identifier_table_ref, rval, Bytecode::Op::PropertyKind::KeyValue, generator.next_property_lookup_cache()); } else if (expression.property().is_private_identifier()) { auto identifier_table_ref = generator.intern_identifier(verify_cast(expression.property()).string()); - generator.emit(*base_object_register, identifier_table_ref); + generator.emit(*base, identifier_table_ref, rval); } else { return Bytecode::CodeGenerationError { &expression, @@ -504,112 +521,116 @@ Bytecode::CodeGenerationErrorOr> AssignmentExpressio } // f. Return rval. - // NOTE: This is already in the accumulator. - return Optional {}; + return rval; }, // 2. Let assignmentPattern be the AssignmentPattern that is covered by LeftHandSideExpression. [&](NonnullRefPtr const& pattern) -> Bytecode::CodeGenerationErrorOr> { // 3. Let rref be the result of evaluating AssignmentExpression. // 4. Let rval be ? GetValue(rref). - FIXME_NEWBC TRY(m_rhs->generate_bytecode(generator)); - auto value_register = generator.allocate_register(); - generator.emit(value_register); + auto rval = TRY(m_rhs->generate_bytecode(generator)).value(); // 5. Perform ? DestructuringAssignmentEvaluation of assignmentPattern with argument rval. - FIXME_NEWBC TRY(generate_binding_pattern_bytecode(generator, pattern, Bytecode::Op::SetVariable::InitializationMode::Set, value_register, false)); + TRY(generate_binding_pattern_bytecode(generator, pattern, Bytecode::Op::SetVariable::InitializationMode::Set, rval, false)); // 6. Return rval. - generator.emit(value_register); - return Optional {}; + return rval; }); } VERIFY(m_lhs.has>()); - auto& lhs = m_lhs.get>(); + auto& lhs_expression = m_lhs.get>(); - auto reference_registers = TRY(generator.emit_load_from_reference(lhs, Bytecode::Generator::CollectRegisters::Yes)); + auto reference_operands = TRY(generator.emit_load_from_reference(lhs_expression)); + auto lhs = reference_operands.loaded_value.value(); Bytecode::BasicBlock* rhs_block_ptr { nullptr }; + Bytecode::BasicBlock* lhs_block_ptr { nullptr }; Bytecode::BasicBlock* end_block_ptr { nullptr }; // Logical assignments short circuit. if (m_op == AssignmentOp::AndAssignment) { // &&= rhs_block_ptr = &generator.make_block(); + lhs_block_ptr = &generator.make_block(); end_block_ptr = &generator.make_block(); - generator.emit( + generator.emit( + lhs, Bytecode::Label { *rhs_block_ptr }, - Bytecode::Label { *end_block_ptr }); + Bytecode::Label { *lhs_block_ptr }); } else if (m_op == AssignmentOp::OrAssignment) { // ||= rhs_block_ptr = &generator.make_block(); + lhs_block_ptr = &generator.make_block(); end_block_ptr = &generator.make_block(); - generator.emit( - Bytecode::Label { *end_block_ptr }, + generator.emit( + lhs, + Bytecode::Label { *lhs_block_ptr }, Bytecode::Label { *rhs_block_ptr }); } else if (m_op == AssignmentOp::NullishAssignment) { // ??= rhs_block_ptr = &generator.make_block(); + lhs_block_ptr = &generator.make_block(); end_block_ptr = &generator.make_block(); generator.emit( + lhs, Bytecode::Label { *rhs_block_ptr }, - Bytecode::Label { *end_block_ptr }); + Bytecode::Label { *lhs_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); + auto rhs = TRY([&]() -> Bytecode::CodeGenerationErrorOr { + if (lhs_expression->is_identifier()) { + return TRY(generator.emit_named_evaluation_if_anonymous_function(*m_rhs, generator.intern_identifier(static_cast(*lhs_expression).string()))).value(); + } + return TRY(m_rhs->generate_bytecode(generator)).value(); + }()); - if (lhs->is_identifier()) - TRY(generator.emit_named_evaluation_if_anonymous_function(*m_rhs, generator.intern_identifier(static_cast(*lhs).string()))); - else - FIXME_NEWBC TRY(m_rhs->generate_bytecode(generator)); + auto dst = choose_dst(generator, preferred_dst); switch (m_op) { case AssignmentOp::AdditionAssignment: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case AssignmentOp::SubtractionAssignment: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case AssignmentOp::MultiplicationAssignment: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case AssignmentOp::DivisionAssignment: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case AssignmentOp::ModuloAssignment: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case AssignmentOp::ExponentiationAssignment: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case AssignmentOp::BitwiseAndAssignment: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case AssignmentOp::BitwiseOrAssignment: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case AssignmentOp::BitwiseXorAssignment: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case AssignmentOp::LeftShiftAssignment: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case AssignmentOp::RightShiftAssignment: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case AssignmentOp::UnsignedRightShiftAssignment: - generator.emit(lhs_reg); + generator.emit(dst, lhs, rhs); break; case AssignmentOp::AndAssignment: case AssignmentOp::OrAssignment: case AssignmentOp::NullishAssignment: - break; // These are handled above. + generator.emit(dst, rhs); + break; default: return Bytecode::CodeGenerationError { this, @@ -617,17 +638,26 @@ Bytecode::CodeGenerationErrorOr> AssignmentExpressio }; } - if (reference_registers.has_value()) - FIXME_NEWBC TRY(generator.emit_store_to_reference(*reference_registers)); + if (lhs_expression->is_identifier()) + generator.emit_set_variable(static_cast(*lhs_expression), dst); else - FIXME_NEWBC TRY(generator.emit_store_to_reference(lhs)); + (void)TRY(generator.emit_store_to_reference(reference_operands, dst)); + + if (rhs_block_ptr) { + generator.emit(Bytecode::Label { *end_block_ptr }); + } + + if (lhs_block_ptr) { + generator.switch_to_basic_block(*lhs_block_ptr); + generator.emit(dst, lhs); + generator.emit(Bytecode::Label { *end_block_ptr }); + } if (end_block_ptr) { - generator.emit(Bytecode::Label { *end_block_ptr }); generator.switch_to_basic_block(*end_block_ptr); } - return Optional {}; + return dst; } // 14.13.3 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-labelled-statements-runtime-semantics-evaluation @@ -656,22 +686,22 @@ Bytecode::CodeGenerationErrorOr> LabelledStatement:: new_label_set.append(m_label); // 3. Let stmtResult be LabelledEvaluation of LabelledItem with argument newLabelSet. - // NOTE: stmtResult will be in the accumulator after running the generated bytecode. + Optional stmt_result; if (is(labelled_item)) { auto const& iteration_statement = static_cast(labelled_item); - FIXME_NEWBC TRY(iteration_statement.generate_labelled_evaluation(generator, new_label_set)); + stmt_result = TRY(iteration_statement.generate_labelled_evaluation(generator, new_label_set)); } else if (is(labelled_item)) { auto const& switch_statement = static_cast(labelled_item); - FIXME_NEWBC TRY(switch_statement.generate_labelled_evaluation(generator, new_label_set)); + stmt_result = TRY(switch_statement.generate_labelled_evaluation(generator, new_label_set)); } else if (is(labelled_item)) { auto const& labelled_statement = static_cast(labelled_item); - FIXME_NEWBC TRY(labelled_statement.generate_labelled_evaluation(generator, new_label_set)); + stmt_result = TRY(labelled_statement.generate_labelled_evaluation(generator, new_label_set)); } else { auto& labelled_break_block = generator.make_block(); // NOTE: We do not need a continuable scope as `continue;` is not allowed outside of iteration statements, throwing a SyntaxError in the parser. generator.begin_breakable_scope(Bytecode::Label { labelled_break_block }, new_label_set); - FIXME_NEWBC TRY(labelled_item.generate_bytecode(generator)); + stmt_result = TRY(labelled_item.generate_bytecode(generator)); generator.end_breakable_scope(); if (!generator.is_current_block_terminated()) { @@ -686,8 +716,7 @@ Bytecode::CodeGenerationErrorOr> LabelledStatement:: // NOTE: These steps are performed by making labelled break jump straight to the appropriate break block, which preserves the statement result's value in the accumulator. // 5. Return Completion(stmtResult). - // NOTE: This is in the accumulator. - return Optional {}; + return stmt_result; } Bytecode::CodeGenerationErrorOr> IterationStatement::generate_labelled_evaluation(Bytecode::Generator&, Vector const&, [[maybe_unused]] Optional preferred_dst) const @@ -714,41 +743,35 @@ Bytecode::CodeGenerationErrorOr> WhileStatement::gen // end auto& test_block = generator.make_block(); auto& body_block = generator.make_block(); - auto& load_result_and_jump_to_end_block = generator.make_block(); auto& end_block = generator.make_block(); - // Init result register - generator.emit(js_undefined()); - auto result_reg = generator.allocate_register(); + auto result = Bytecode::Operand(generator.allocate_register()); + generator.emit(result, generator.add_constant(js_undefined())); - // jump to the test block generator.emit(Bytecode::Label { test_block }); generator.switch_to_basic_block(test_block); - generator.emit(result_reg); - FIXME_NEWBC TRY(m_test->generate_bytecode(generator)); - generator.emit( + auto test = TRY(m_test->generate_bytecode(generator)).value(); + generator.emit( + test, Bytecode::Label { body_block }, - Bytecode::Label { load_result_and_jump_to_end_block }); + Bytecode::Label { end_block }); generator.switch_to_basic_block(body_block); generator.begin_continuable_scope(Bytecode::Label { test_block }, label_set); generator.begin_breakable_scope(Bytecode::Label { end_block }, label_set); - generator.emit(js_undefined()); - FIXME_NEWBC TRY(m_body->generate_bytecode(generator)); + auto body = TRY(m_body->generate_bytecode(generator)); generator.end_breakable_scope(); generator.end_continuable_scope(); if (!generator.is_current_block_terminated()) { + if (body.has_value()) + generator.emit(result, body.value()); generator.emit(Bytecode::Label { test_block }); } - generator.switch_to_basic_block(load_result_and_jump_to_end_block); - generator.emit(result_reg); - generator.emit(Bytecode::Label { end_block }); - generator.switch_to_basic_block(end_block); - return Optional {}; + return result; } Bytecode::CodeGenerationErrorOr> DoWhileStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const @@ -771,38 +794,37 @@ Bytecode::CodeGenerationErrorOr> DoWhileStatement::g auto& load_result_and_jump_to_end_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); + auto completion_value = Bytecode::Operand(generator.allocate_register()); + generator.emit(completion_value, generator.add_constant(js_undefined())); // jump to the body block generator.emit(Bytecode::Label { body_block }); generator.switch_to_basic_block(test_block); - generator.emit(result_reg); - FIXME_NEWBC TRY(m_test->generate_bytecode(generator)); - generator.emit( + auto test = TRY(m_test->generate_bytecode(generator)).value(); + generator.emit( + test, Bytecode::Label { body_block }, Bytecode::Label { load_result_and_jump_to_end_block }); generator.switch_to_basic_block(body_block); generator.begin_continuable_scope(Bytecode::Label { test_block }, label_set); generator.begin_breakable_scope(Bytecode::Label { end_block }, label_set); - FIXME_NEWBC TRY(m_body->generate_bytecode(generator)); + auto body_result = TRY(m_body->generate_bytecode(generator)); generator.end_breakable_scope(); generator.end_continuable_scope(); if (!generator.is_current_block_terminated()) { + if (body_result.has_value()) + generator.emit(completion_value, body_result.value()); generator.emit(Bytecode::Label { test_block }); } generator.switch_to_basic_block(load_result_and_jump_to_end_block); - generator.emit(result_reg); generator.emit(Bytecode::Label { end_block }); generator.switch_to_basic_block(end_block); - return Optional {}; + return completion_value; } Bytecode::CodeGenerationErrorOr> ForStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const @@ -863,7 +885,7 @@ Bytecode::CodeGenerationErrorOr> ForStatement::gener } } - FIXME_NEWBC TRY(m_init->generate_bytecode(generator)); + (void)TRY(m_init->generate_bytecode(generator)); } body_block_ptr = &generator.make_block(); @@ -878,23 +900,15 @@ Bytecode::CodeGenerationErrorOr> ForStatement::gener else update_block_ptr = body_block_ptr; - generator.emit(js_undefined()); - auto result_reg = generator.allocate_register(); - - if (m_test && m_update) - generator.emit(result_reg); - generator.emit(Bytecode::Label { *test_block_ptr }); if (m_test) { load_result_and_jump_to_end_block_ptr = &generator.make_block(); generator.switch_to_basic_block(*test_block_ptr); - if (!m_update) - generator.emit(result_reg); - - FIXME_NEWBC TRY(m_test->generate_bytecode(generator)); - generator.emit( + auto test = TRY(m_test->generate_bytecode(generator)).value(); + generator.emit( + test, Bytecode::Label { *body_block_ptr }, Bytecode::Label { *load_result_and_jump_to_end_block_ptr }); } @@ -902,18 +916,14 @@ Bytecode::CodeGenerationErrorOr> ForStatement::gener if (m_update) { generator.switch_to_basic_block(*update_block_ptr); - if (m_test) - generator.emit(result_reg); - - FIXME_NEWBC TRY(m_update->generate_bytecode(generator)); + (void)TRY(m_update->generate_bytecode(generator)); generator.emit(Bytecode::Label { *test_block_ptr }); } generator.switch_to_basic_block(*body_block_ptr); generator.begin_continuable_scope(Bytecode::Label { m_update ? *update_block_ptr : *test_block_ptr }, label_set); generator.begin_breakable_scope(Bytecode::Label { end_block }, label_set); - generator.emit(js_undefined()); - FIXME_NEWBC TRY(m_body->generate_bytecode(generator)); + auto body_result = TRY(m_body->generate_bytecode(generator)); generator.end_breakable_scope(); generator.end_continuable_scope(); @@ -927,7 +937,6 @@ Bytecode::CodeGenerationErrorOr> ForStatement::gener if (load_result_and_jump_to_end_block_ptr) { generator.switch_to_basic_block(*load_result_and_jump_to_end_block_ptr); - generator.emit(result_reg); generator.emit(Bytecode::Label { end_block }); } @@ -936,20 +945,20 @@ Bytecode::CodeGenerationErrorOr> ForStatement::gener if (has_lexical_environment) generator.end_variable_scope(); - return Optional {}; + return body_result; } -Bytecode::CodeGenerationErrorOr> ObjectExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ObjectExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - generator.emit(); + + auto object = choose_dst(generator, preferred_dst); + + generator.emit(object); if (m_properties.is_empty()) - return Optional {}; + return object; - auto object_reg = generator.allocate_register(); - generator.emit(object_reg); - - generator.push_home_object(object_reg); + generator.push_home_object(object); for (auto& property : m_properties) { Bytecode::Op::PropertyKind property_kind; @@ -975,8 +984,9 @@ Bytecode::CodeGenerationErrorOr> ObjectExpression::g auto& string_literal = static_cast(property->key()); Bytecode::IdentifierTableIndex key_name = generator.intern_identifier(string_literal.value()); + Optional value; if (property_kind == Bytecode::Op::PropertyKind::ProtoSetter) { - FIXME_NEWBC TRY(property->value().generate_bytecode(generator)); + value = TRY(property->value().generate_bytecode(generator)).value(); } else if (property_kind != Bytecode::Op::PropertyKind::Spread) { ByteString identifier = string_literal.value(); if (property_kind == Bytecode::Op::PropertyKind::Getter) @@ -984,34 +994,36 @@ Bytecode::CodeGenerationErrorOr> ObjectExpression::g else if (property_kind == Bytecode::Op::PropertyKind::Setter) identifier = ByteString::formatted("set {}", identifier); auto name = generator.intern_identifier(identifier); - TRY(generator.emit_named_evaluation_if_anonymous_function(property->value(), name)); + value = TRY(generator.emit_named_evaluation_if_anonymous_function(property->value(), name)).value(); + } else { + // Spread the key. + value = TRY(property->key().generate_bytecode(generator)).value(); } - generator.emit(object_reg, key_name, property_kind, generator.next_property_lookup_cache()); + generator.emit(object, key_name, *value, property_kind, generator.next_property_lookup_cache()); } else { - FIXME_NEWBC TRY(property->key().generate_bytecode(generator)); - auto property_reg = generator.allocate_register(); - generator.emit(property_reg); - + auto property_name = TRY(property->key().generate_bytecode(generator)).value(); + Optional value; if (property_kind != Bytecode::Op::PropertyKind::Spread) - FIXME_NEWBC TRY(property->value().generate_bytecode(generator)); + value = TRY(property->value().generate_bytecode(generator)).value(); + else + value = property_name; - generator.emit(object_reg, property_reg, property_kind); + generator.emit(object, property_name, *value, property_kind); } } - generator.emit(object_reg); - generator.pop_home_object(); - return Optional {}; + return object; } -Bytecode::CodeGenerationErrorOr> ArrayExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ArrayExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); if (m_elements.is_empty()) { - generator.emit(); - return Optional {}; + auto dst = choose_dst(generator, preferred_dst); + generator.emit(dst); + return dst; } if (all_of(m_elements, [](auto element) { return !element || is(*element); })) { @@ -1023,8 +1035,9 @@ Bytecode::CodeGenerationErrorOr> ArrayExpression::ge continue; values[i] = static_cast(*m_elements[i]).value(); } - generator.emit(move(values)); - return Optional {}; + auto dst = choose_dst(generator, preferred_dst); + generator.emit(dst, move(values)); + return dst; } auto first_spread = find_if(m_elements.begin(), m_elements.end(), [](auto el) { return el && is(*el); }); @@ -1038,42 +1051,39 @@ Bytecode::CodeGenerationErrorOr> ArrayExpression::ge u32 i = 0; for (auto it = m_elements.begin(); it != first_spread; ++it, ++i) { Bytecode::Register reg { args_start_reg.index() + i }; - if (!*it) - generator.emit(Value {}); - else { - FIXME_NEWBC TRY((*it)->generate_bytecode(generator)); + if (*it) { + auto value = TRY((*it)->generate_bytecode(generator)).value(); + generator.emit(Bytecode::Operand(reg), value); } - generator.emit(reg); } - if (first_spread.index() != 0) - generator.emit_with_extra_register_slots(2u, AK::Array { args_start_reg, Bytecode::Register { args_start_reg.index() + static_cast(first_spread.index() - 1) } }); - else - generator.emit(); + auto dst = choose_dst(generator, preferred_dst); + if (first_spread.index() != 0) { + auto reg = Bytecode::Register { args_start_reg.index() + static_cast(first_spread.index() - 1) }; + generator.emit_with_extra_operand_slots(2u, dst, AK::Array { Bytecode::Operand(args_start_reg), Bytecode::Operand(reg) }); + } else { + generator.emit(dst); + } if (first_spread != m_elements.end()) { - auto array_reg = generator.allocate_register(); - generator.emit(array_reg); for (auto it = first_spread; it != m_elements.end(); ++it) { if (!*it) { - generator.emit(Value {}); - generator.emit(array_reg, false); + generator.emit(dst, generator.add_constant(Value()), false); } else { - FIXME_NEWBC TRY((*it)->generate_bytecode(generator)); - generator.emit(array_reg, *it && is(**it)); + auto value = TRY((*it)->generate_bytecode(generator)).value(); + generator.emit(dst, value, *it && is(**it)); } } - generator.emit(array_reg); } - return Optional {}; + return dst; } -Bytecode::CodeGenerationErrorOr> MemberExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> MemberExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - (void)TRY(generator.emit_load_from_reference(*this, Bytecode::Generator::CollectRegisters::No)); - return Optional {}; + auto reference = TRY(generator.emit_load_from_reference(*this, preferred_dst)); + return reference.loaded_value; } Bytecode::CodeGenerationErrorOr> FunctionDeclaration::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const @@ -1081,13 +1091,14 @@ Bytecode::CodeGenerationErrorOr> FunctionDeclaration if (m_is_hoisted) { Bytecode::Generator::SourceLocationScope scope(generator, *this); auto index = generator.intern_identifier(name()); - generator.emit(index, generator.next_environment_variable_cache()); - generator.emit(index, generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Set, Bytecode::Op::EnvironmentMode::Var); + auto value = Bytecode::Operand(generator.allocate_register()); + generator.emit(value, index, generator.next_environment_variable_cache()); + generator.emit(index, value, generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Set, Bytecode::Op::EnvironmentMode::Var); } return Optional {}; } -Bytecode::CodeGenerationErrorOr> FunctionExpression::generate_bytecode_with_lhs_name(Bytecode::Generator& generator, Optional lhs_name, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> FunctionExpression::generate_bytecode_with_lhs_name(Bytecode::Generator& generator, Optional lhs_name, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); bool has_name = !name().is_empty(); @@ -1100,27 +1111,28 @@ Bytecode::CodeGenerationErrorOr> FunctionExpression: generator.emit(*name_identifier, Bytecode::Op::EnvironmentMode::Lexical, true); } - generator.emit_new_function(*this, lhs_name); + auto new_function = choose_dst(generator, preferred_dst); + generator.emit_new_function(new_function, *this, lhs_name); if (has_name) { - generator.emit(*name_identifier, generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize, Bytecode::Op::EnvironmentMode::Lexical); + generator.emit(*name_identifier, new_function, generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize, Bytecode::Op::EnvironmentMode::Lexical); generator.end_variable_scope(); } - return Optional {}; + return new_function; } -Bytecode::CodeGenerationErrorOr> FunctionExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> FunctionExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - return generate_bytecode_with_lhs_name(generator, {}); + return generate_bytecode_with_lhs_name(generator, {}, preferred_dst); } -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, bool create_variables, [[maybe_unused]] Optional preferred_dst = {}) +static Bytecode::CodeGenerationErrorOr generate_object_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode initialization_mode, Bytecode::Operand const& object, bool create_variables) { - generator.emit(); + generator.emit(object); - Vector excluded_property_names; + Vector excluded_property_names; auto has_rest = false; if (pattern.entries.size() > 0) has_rest = pattern.entries[pattern.entries.size() - 1].is_rest; @@ -1132,49 +1144,53 @@ static Bytecode::CodeGenerationErrorOr> generate_obj auto identifier = name.get>(); auto interned_identifier = generator.intern_identifier(identifier->string()); - generator.emit_with_extra_register_slots(excluded_property_names.size(), value_reg, excluded_property_names); + auto copy = Bytecode::Operand(generator.allocate_register()); + generator.emit_with_extra_operand_slots( + excluded_property_names.size(), copy, object, excluded_property_names); if (create_variables) { VERIFY(!identifier->is_local()); generator.emit(interned_identifier, Bytecode::Op::EnvironmentMode::Lexical, false); } - generator.emit_set_variable(*identifier, initialization_mode); + generator.emit_set_variable(*identifier, copy, initialization_mode); - return Optional {}; + return {}; } if (alias.has>()) { - generator.emit_with_extra_register_slots(excluded_property_names.size(), value_reg, excluded_property_names); - FIXME_NEWBC TRY(generator.emit_store_to_reference(alias.get>())); - return Optional {}; + auto copy = Bytecode::Operand(generator.allocate_register()); + generator.emit_with_extra_operand_slots( + excluded_property_names.size(), copy, object, excluded_property_names); + (void)TRY(generator.emit_store_to_reference(alias.get>(), object)); + return {}; } VERIFY_NOT_REACHED(); } Bytecode::StringTableIndex name_index; + auto value = Bytecode::Operand(generator.allocate_register()); + 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); + auto excluded_name = Bytecode::Operand(generator.allocate_register()); + excluded_property_names.append(excluded_name); + generator.emit(excluded_name, name_index); } - generator.emit(value_reg); - generator.emit_get_by_id(generator.intern_identifier(identifier)); + generator.emit_get_by_id(value, object, generator.intern_identifier(identifier)); } else { auto expression = name.get>(); - FIXME_NEWBC TRY(expression->generate_bytecode(generator)); + auto property_name = TRY(expression->generate_bytecode(generator)).value(); if (has_rest) { - auto excluded_name_reg = generator.allocate_register(); - excluded_property_names.append(excluded_name_reg); - generator.emit(excluded_name_reg); + auto excluded_name = Bytecode::Operand(generator.allocate_register()); + excluded_property_names.append(excluded_name); + generator.emit(excluded_name, property_name); } - generator.emit(value_reg); + generator.emit(value, object, property_name); } if (initializer) { @@ -1182,17 +1198,20 @@ static Bytecode::CodeGenerationErrorOr> generate_obj auto& if_not_undefined_block = generator.make_block(); generator.emit( + value, Bytecode::Label { if_undefined_block }, Bytecode::Label { if_not_undefined_block }); generator.switch_to_basic_block(if_undefined_block); + Optional default_value; if (auto const* alias_identifier = alias.get_pointer>()) { - TRY(generator.emit_named_evaluation_if_anonymous_function(*initializer, generator.intern_identifier((*alias_identifier)->string()))); + default_value = TRY(generator.emit_named_evaluation_if_anonymous_function(*initializer, generator.intern_identifier((*alias_identifier)->string()))).value(); } else if (auto const* lhs = name.get_pointer>()) { - TRY(generator.emit_named_evaluation_if_anonymous_function(*initializer, generator.intern_identifier((*lhs)->string()))); + default_value = TRY(generator.emit_named_evaluation_if_anonymous_function(*initializer, generator.intern_identifier((*lhs)->string()))).value(); } else { - FIXME_NEWBC TRY(initializer->generate_bytecode(generator)); + default_value = TRY(initializer->generate_bytecode(generator)).value(); } + generator.emit(value, *default_value); generator.emit(Bytecode::Label { if_not_undefined_block }); generator.switch_to_basic_block(if_not_undefined_block); @@ -1200,9 +1219,9 @@ static Bytecode::CodeGenerationErrorOr> generate_obj if (alias.has>()) { auto& binding_pattern = *alias.get>(); - auto nested_value_reg = generator.allocate_register(); - generator.emit(nested_value_reg); - FIXME_NEWBC TRY(generate_binding_pattern_bytecode(generator, binding_pattern, initialization_mode, nested_value_reg, create_variables)); + auto nested_value = Bytecode::Operand(generator.allocate_register()); + generator.emit(nested_value, value); + TRY(generate_binding_pattern_bytecode(generator, binding_pattern, initialization_mode, nested_value, create_variables)); } else if (alias.has()) { if (name.has>()) { // This needs some sort of SetVariableByValue opcode, as it's a runtime binding @@ -1216,21 +1235,21 @@ static Bytecode::CodeGenerationErrorOr> generate_obj auto identifier_ref = generator.intern_identifier(identifier.string()); if (create_variables) generator.emit(identifier_ref, Bytecode::Op::EnvironmentMode::Lexical, false); - generator.emit_set_variable(identifier, initialization_mode); + generator.emit_set_variable(identifier, value, initialization_mode); } else if (alias.has>()) { - FIXME_NEWBC TRY(generator.emit_store_to_reference(alias.get>())); + TRY(generator.emit_store_to_reference(alias.get>(), value)); } else { auto const& identifier = *alias.get>(); auto identifier_ref = generator.intern_identifier(identifier.string()); if (create_variables) generator.emit(identifier_ref, Bytecode::Op::EnvironmentMode::Lexical, false); - generator.emit_set_variable(identifier, initialization_mode); + generator.emit_set_variable(identifier, value, initialization_mode); } } - return Optional {}; + 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, bool create_variables, [[maybe_unused]] Optional preferred_dst = {}) +static Bytecode::CodeGenerationErrorOr generate_array_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode initialization_mode, Bytecode::Operand const& input_array, bool create_variables, [[maybe_unused]] Optional preferred_dst = {}) { /* * Consider the following destructuring assignment: @@ -1245,134 +1264,119 @@ static Bytecode::CodeGenerationErrorOr> generate_arr * - 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. + * If it is 'true', we load undefined. Otherwise, we grab the next value from the iterator. * * 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(); - generator.emit(Value(false)); - generator.emit(is_iterator_exhausted_register); + auto is_iterator_exhausted = Bytecode::Operand(generator.allocate_register()); + generator.emit(is_iterator_exhausted, generator.add_constant(Value(false))); - auto iterator_reg = generator.allocate_register(); - generator.emit(value_reg); - generator.emit(); - generator.emit(iterator_reg); + auto iterator = Bytecode::Operand(generator.allocate_register()); + generator.emit(iterator, input_array); bool first = true; - auto temp_iterator_result_reg = generator.allocate_register(); - - auto assign_accumulator_to_alias = [&](auto& alias) { + auto assign_value_to_alias = [&](auto& alias, Bytecode::Operand value) { return alias.visit( - [&](Empty) -> Bytecode::CodeGenerationErrorOr> { + [&](Empty) -> Bytecode::CodeGenerationErrorOr { // This element is an elision - return Optional {}; + return {}; }, - [&](NonnullRefPtr const& identifier) -> Bytecode::CodeGenerationErrorOr> { + [&](NonnullRefPtr const& identifier) -> Bytecode::CodeGenerationErrorOr { auto interned_index = generator.intern_identifier(identifier->string()); if (create_variables) generator.emit(interned_index, Bytecode::Op::EnvironmentMode::Lexical, false); - generator.emit_set_variable(*identifier, initialization_mode); - return Optional {}; + generator.emit_set_variable(*identifier, value, 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, create_variables); + [&](NonnullRefPtr const& pattern) -> Bytecode::CodeGenerationErrorOr { + return generate_binding_pattern_bytecode(generator, pattern, initialization_mode, value, create_variables); }, - [&](NonnullRefPtr const& expr) -> Bytecode::CodeGenerationErrorOr> { - return generator.emit_store_to_reference(*expr); + [&](NonnullRefPtr const& expr) -> Bytecode::CodeGenerationErrorOr { + (void)generator.emit_store_to_reference(*expr, value); + return {}; }); }; + auto temp_iterator_result = Bytecode::Operand(generator.allocate_register()); + for (auto& [name, alias, initializer, is_rest] : pattern.entries) { VERIFY(name.has()); if (is_rest) { VERIFY(!initializer); + auto value = Bytecode::Operand(generator.allocate_register()); + if (first) { // The iterator has not been called, and is thus known to be not exhausted - generator.emit(iterator_reg); - generator.emit(); + generator.emit(value, iterator); } 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( + generator.emit( + is_iterator_exhausted, Bytecode::Label { if_exhausted_block }, Bytecode::Label { if_not_exhausted_block }); + value = Bytecode::Operand(generator.allocate_register()); + generator.switch_to_basic_block(if_exhausted_block); - generator.emit(); + generator.emit(value); generator.emit(Bytecode::Label { continuation_block }); generator.switch_to_basic_block(if_not_exhausted_block); - generator.emit(iterator_reg); - generator.emit(); + generator.emit(value, iterator); generator.emit(Bytecode::Label { continuation_block }); generator.switch_to_basic_block(continuation_block); } - return assign_accumulator_to_alias(alias); + return assign_value_to_alias(alias, value); } - // 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( + generator.emit( + is_iterator_exhausted, 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_iterator_complete(); - generator.emit(is_iterator_exhausted_register); + generator.emit(temp_iterator_result, iterator); + generator.emit_iterator_complete(is_iterator_exhausted, temp_iterator_result); // 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( + generator.emit( + is_iterator_exhausted, 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_iterator_value(); + auto value = Bytecode::Operand { generator.allocate_register() }; + generator.emit_iterator_value(value, temp_iterator_result); auto& create_binding_block = generator.make_block(); generator.emit(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(value, generator.add_constant(js_undefined())); generator.emit(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); if (initializer) { @@ -1380,24 +1384,27 @@ static Bytecode::CodeGenerationErrorOr> generate_arr auto& value_is_not_undefined_block = generator.make_block(); generator.emit( + value, Bytecode::Label { value_is_undefined_block }, Bytecode::Label { value_is_not_undefined_block }); generator.switch_to_basic_block(value_is_undefined_block); + Optional default_value; if (auto const* alias_identifier = alias.get_pointer>()) { - TRY(generator.emit_named_evaluation_if_anonymous_function(*initializer, generator.intern_identifier((*alias_identifier)->string()))); + default_value = TRY(generator.emit_named_evaluation_if_anonymous_function(*initializer, generator.intern_identifier((*alias_identifier)->string()))).value(); } else if (auto const* name_identifier = name.get_pointer>()) { - TRY(generator.emit_named_evaluation_if_anonymous_function(*initializer, generator.intern_identifier((*name_identifier)->string()))); + default_value = TRY(generator.emit_named_evaluation_if_anonymous_function(*initializer, generator.intern_identifier((*name_identifier)->string()))).value(); } else { - FIXME_NEWBC TRY(initializer->generate_bytecode(generator)); + default_value = TRY(initializer->generate_bytecode(generator)).value(); } + generator.emit(value, *default_value); generator.emit(Bytecode::Label { value_is_not_undefined_block }); generator.switch_to_basic_block(value_is_not_undefined_block); } - FIXME_NEWBC TRY(assign_accumulator_to_alias(alias)); + TRY(assign_value_to_alias(alias, value)); first = false; } @@ -1405,88 +1412,100 @@ static Bytecode::CodeGenerationErrorOr> generate_arr auto& done_block = generator.make_block(); auto& not_done_block = generator.make_block(); - generator.emit(is_iterator_exhausted_register); - generator.emit( + generator.emit( + is_iterator_exhausted, Bytecode::Label { done_block }, Bytecode::Label { not_done_block }); generator.switch_to_basic_block(not_done_block); - generator.emit(iterator_reg); - generator.emit(Completion::Type::Normal, Optional {}); + generator.emit(iterator, Completion::Type::Normal, Optional {}); generator.emit(Bytecode::Label { done_block }); generator.switch_to_basic_block(done_block); - return Optional {}; + 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, bool create_variables, [[maybe_unused]] Optional preferred_dst) +static Bytecode::CodeGenerationErrorOr generate_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode initialization_mode, Bytecode::Operand const& input_value, bool create_variables) { if (pattern.kind == BindingPattern::Kind::Object) - return generate_object_binding_pattern_bytecode(generator, pattern, initialization_mode, value_reg, create_variables); + return generate_object_binding_pattern_bytecode(generator, pattern, initialization_mode, input_value, create_variables); - return generate_array_binding_pattern_bytecode(generator, pattern, initialization_mode, value_reg, create_variables); + return generate_array_binding_pattern_bytecode(generator, pattern, initialization_mode, input_value, create_variables); } -static Bytecode::CodeGenerationErrorOr> assign_accumulator_to_variable_declarator(Bytecode::Generator& generator, VariableDeclarator const& declarator, VariableDeclaration const& declaration, [[maybe_unused]] Optional preferred_dst = {}) +static Bytecode::CodeGenerationErrorOr assign_value_to_variable_declarator(Bytecode::Generator& generator, VariableDeclarator const& declarator, VariableDeclaration const& declaration, Bytecode::Operand value) { auto initialization_mode = declaration.is_lexical_declaration() ? Bytecode::Op::SetVariable::InitializationMode::Initialize : Bytecode::Op::SetVariable::InitializationMode::Set; return declarator.target().visit( - [&](NonnullRefPtr const& id) -> Bytecode::CodeGenerationErrorOr> { - generator.emit_set_variable(*id, initialization_mode); - return Optional {}; + [&](NonnullRefPtr const& id) -> Bytecode::CodeGenerationErrorOr { + generator.emit_set_variable(*id, value, 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, false); + [&](NonnullRefPtr const& pattern) -> Bytecode::CodeGenerationErrorOr { + return generate_binding_pattern_bytecode(generator, pattern, initialization_mode, value, false); }); } Bytecode::CodeGenerationErrorOr> VariableDeclaration::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - // The completion value of a VariableDeclaration is empty, but there might already be a non-empty - // completion value in the accumulator. We need to save it and restore it after the declaration executed. - auto saved_accumulator = generator.allocate_register(); - generator.emit(saved_accumulator); for (auto& declarator : m_declarations) { - if (declarator->init()) { - if (auto const* lhs = declarator->target().get_pointer>()) { - TRY(generator.emit_named_evaluation_if_anonymous_function(*declarator->init(), generator.intern_identifier((*lhs)->string()))); - } else { - FIXME_NEWBC TRY(declarator->init()->generate_bytecode(generator)); + Optional init_dst; + if (auto const* identifier = declarator->target().get_pointer>()) { + if ((*identifier)->is_local()) { + init_dst = Bytecode::Operand(Bytecode::Operand::Type::Local, (*identifier)->local_variable_index()); } - FIXME_NEWBC TRY(assign_accumulator_to_variable_declarator(generator, declarator, *this)); + } + + if (declarator->init()) { + auto value = TRY([&]() -> Bytecode::CodeGenerationErrorOr { + if (auto const* lhs = declarator->target().get_pointer>()) { + return TRY(generator.emit_named_evaluation_if_anonymous_function(*declarator->init(), generator.intern_identifier((*lhs)->string()), init_dst)).value(); + } else { + return TRY(declarator->init()->generate_bytecode(generator, init_dst)).value(); + } + }()); + (void)TRY(assign_value_to_variable_declarator(generator, declarator, *this, value)); } else if (m_declaration_kind != DeclarationKind::Var) { - generator.emit(js_undefined()); - FIXME_NEWBC TRY(assign_accumulator_to_variable_declarator(generator, declarator, *this)); + (void)TRY(assign_value_to_variable_declarator(generator, declarator, *this, generator.add_constant(js_undefined()))); } } - generator.emit(saved_accumulator); + for (auto& declarator : m_declarations) { + if (auto const* identifier = declarator->target().get_pointer>()) { + if ((*identifier)->is_local()) { + generator.set_local_initialized((*identifier)->local_variable_index()); + } + } + } + + // NOTE: VariableDeclaration doesn't return a completion value. return Optional {}; } -static Bytecode::CodeGenerationErrorOr> get_base_and_value_from_member_expression(Bytecode::Generator& generator, MemberExpression const& member_expression, Bytecode::Register this_reg, [[maybe_unused]] Optional preferred_dst = {}) +struct BaseAndValue { + Bytecode::Operand base; + Bytecode::Operand value; +}; + +static Bytecode::CodeGenerationErrorOr get_base_and_value_from_member_expression(Bytecode::Generator& generator, MemberExpression const& member_expression) { // https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation if (is(member_expression.object())) { // 1. Let env be GetThisEnvironment(). // 2. Let actualThis be ? env.GetThisBinding(). - generator.emit(); - generator.emit(this_reg); + auto this_value = Bytecode::Operand(generator.allocate_register()); + generator.emit(this_value); - Optional computed_property_value_register; + Optional computed_property; if (member_expression.is_computed()) { // SuperProperty : super [ Expression ] // 3. Let propertyNameReference be ? Evaluation of Expression. // 4. Let propertyNameValue be ? GetValue(propertyNameReference). - FIXME_NEWBC TRY(member_expression.property().generate_bytecode(generator)); - computed_property_value_register = generator.allocate_register(); - generator.emit(*computed_property_value_register); + computed_property = TRY(member_expression.property().generate_bytecode(generator)); } // 5/7. Return ? MakeSuperPropertyReference(actualThis, propertyKey, strict). @@ -1495,79 +1514,99 @@ static Bytecode::CodeGenerationErrorOr> get_base_and // 1. Let env be GetThisEnvironment(). // 2. Assert: env.HasSuperBinding() is true. // 3. Let baseValue be ? env.GetSuperBase(). - generator.emit(); + auto super_base = Bytecode::Operand(generator.allocate_register()); + generator.emit(super_base); + + auto value = Bytecode::Operand { generator.allocate_register() }; // 4. Return the Reference Record { [[Base]]: baseValue, [[ReferencedName]]: propertyKey, [[Strict]]: strict, [[ThisValue]]: actualThis }. - if (computed_property_value_register.has_value()) { + if (computed_property.has_value()) { // 5. Let propertyKey be ? ToPropertyKey(propertyNameValue). // FIXME: This does ToPropertyKey out of order, which is observable by Symbol.toPrimitive! - auto super_base_register = generator.allocate_register(); - generator.emit(super_base_register); - generator.emit(*computed_property_value_register); - generator.emit(super_base_register, this_reg); + generator.emit(value, super_base, *computed_property, this_value); } else { // 3. Let propertyKey be StringValue of IdentifierName. auto identifier_table_ref = generator.intern_identifier(verify_cast(member_expression.property()).string()); - generator.emit_get_by_id_with_this(identifier_table_ref, this_reg); - } - } else { - FIXME_NEWBC TRY(member_expression.object().generate_bytecode(generator)); - generator.emit(this_reg); - if (member_expression.is_computed()) { - FIXME_NEWBC TRY(member_expression.property().generate_bytecode(generator)); - generator.emit(this_reg); - } else if (is(member_expression.property())) { - generator.emit(generator.intern_identifier(verify_cast(member_expression.property()).string())); - } else { - generator.emit_get_by_id(generator.intern_identifier(verify_cast(member_expression.property()).string())); + generator.emit_get_by_id_with_this(value, super_base, identifier_table_ref, this_value); } + + return BaseAndValue { this_value, value }; } - return Optional {}; + auto base = TRY(member_expression.object().generate_bytecode(generator)).value(); + auto value = Bytecode::Operand { generator.allocate_register() }; + if (member_expression.is_computed()) { + auto property = TRY(member_expression.property().generate_bytecode(generator)).value(); + generator.emit(value, base, property); + } else if (is(member_expression.property())) { + generator.emit( + value, + base, + generator.intern_identifier(verify_cast(member_expression.property()).string())); + } else { + generator.emit_get_by_id(value, base, generator.intern_identifier(verify_cast(member_expression.property()).string())); + } + + return BaseAndValue { base, value }; } -static Bytecode::CodeGenerationErrorOr> generate_optional_chain(Bytecode::Generator& generator, OptionalChain const& optional_chain, Bytecode::Register current_value_register, Bytecode::Register current_base_register, [[maybe_unused]] Optional preferred_dst = {}); +static Bytecode::CodeGenerationErrorOr generate_optional_chain(Bytecode::Generator& generator, OptionalChain const& optional_chain, Bytecode::Operand current_value, Bytecode::Operand current_base, [[maybe_unused]] Optional preferred_dst = {}); -Bytecode::CodeGenerationErrorOr> CallExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> CallExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - auto callee_reg = generator.allocate_register(); - auto this_reg = generator.allocate_register(); - generator.emit(js_undefined()); - generator.emit(this_reg); Optional builtin; + Optional original_callee; + auto this_value = generator.add_constant(js_undefined()); + if (is(this)) { - FIXME_NEWBC TRY(m_callee->generate_bytecode(generator)); - generator.emit(callee_reg); + original_callee = TRY(m_callee->generate_bytecode(generator)).value(); } else if (is(*m_callee)) { auto& member_expression = static_cast(*m_callee); - FIXME_NEWBC TRY(get_base_and_value_from_member_expression(generator, member_expression, this_reg)); - generator.emit(callee_reg); + auto base_and_value = TRY(get_base_and_value_from_member_expression(generator, member_expression)); + original_callee = base_and_value.value; + this_value = base_and_value.base; builtin = Bytecode::get_builtin(member_expression); } else if (is(*m_callee)) { auto& optional_chain = static_cast(*m_callee); - FIXME_NEWBC TRY(generate_optional_chain(generator, optional_chain, callee_reg, this_reg)); + original_callee = Bytecode::Operand(generator.allocate_register()); + this_value = Bytecode::Operand(generator.allocate_register()); + TRY(generate_optional_chain(generator, optional_chain, *original_callee, this_value)); } else if (is(*m_callee)) { - // If the callee is an identifier, we may need to extract a `this` value. + // If the original_callee is an identifier, we may need to extract a `this` value. // This is important when we're inside a `with` statement and calling a method on // the environment's binding object. // NOTE: If the identifier refers to a known "local" or "global", we know it can't be // a `with` binding, so we can skip this. auto& identifier = static_cast(*m_callee); - if (!identifier.is_local() && !identifier.is_global()) { - generator.emit(generator.intern_identifier(identifier.string()), callee_reg, this_reg, generator.next_environment_variable_cache()); + if (identifier.is_local()) { + auto local = Bytecode::Operand(Bytecode::Operand::Type::Local, identifier.local_variable_index()); + if (!generator.is_local_initialized(local.index())) { + generator.emit(local); + } + original_callee = local; + } else if (identifier.is_global()) { + original_callee = m_callee->generate_bytecode(generator).value(); } else { - FIXME_NEWBC TRY(m_callee->generate_bytecode(generator)); - generator.emit(callee_reg); + original_callee = Bytecode::Operand(generator.allocate_register()); + this_value = Bytecode::Operand(generator.allocate_register()); + generator.emit( + *original_callee, + this_value, + generator.intern_identifier(identifier.string()), + generator.next_environment_variable_cache()); } } else { // FIXME: this = global object in sloppy mode. - FIXME_NEWBC TRY(m_callee->generate_bytecode(generator)); - generator.emit(callee_reg); + original_callee = TRY(m_callee->generate_bytecode(generator)).value(); } + // NOTE: We copy the callee to a new register to avoid overwriting it while evaluating arguments. + auto callee = Bytecode::Operand(generator.allocate_register()); + generator.emit(callee, *original_callee); + Bytecode::Op::CallType call_type; if (is(*this)) { call_type = Bytecode::Op::CallType::Construct; @@ -1582,10 +1621,11 @@ Bytecode::CodeGenerationErrorOr> CallExpression::gen expression_string_index = generator.intern_string(expression_string.release_value()); bool has_spread = any_of(arguments(), [](auto& argument) { return argument.is_spread; }); + auto dst = choose_dst(generator, preferred_dst); if (has_spread) { - FIXME_NEWBC TRY(arguments_to_array_for_call(generator, arguments())); - generator.emit(call_type, callee_reg, this_reg, expression_string_index); + auto arguments = TRY(arguments_to_array_for_call(generator, this->arguments())).value(); + generator.emit(call_type, dst, callee, this_value, arguments, expression_string_index); } else { Optional first_argument_reg {}; for (size_t i = 0; i < arguments().size(); ++i) { @@ -1595,27 +1635,37 @@ Bytecode::CodeGenerationErrorOr> CallExpression::gen } u32 register_offset = 0; for (auto const& argument : arguments()) { - FIXME_NEWBC TRY(argument.value->generate_bytecode(generator)); - generator.emit(Bytecode::Register { first_argument_reg.value().index() + register_offset }); + auto value = TRY(argument.value->generate_bytecode(generator)).value(); + generator.emit(Bytecode::Operand(Bytecode::Register(first_argument_reg.value().index() + register_offset)), value); register_offset += 1; } - generator.emit(call_type, callee_reg, this_reg, first_argument_reg.value_or(Bytecode::Register { 0 }), arguments().size(), expression_string_index, builtin); + generator.emit(call_type, dst, callee, this_value, first_argument_reg.value_or(Bytecode::Register { 0 }), arguments().size(), expression_string_index, builtin); } - return Optional {}; + return dst; } -static void generate_await(Bytecode::Generator& generator, Bytecode::Register received_completion_register, Bytecode::Register received_completion_type_register, Bytecode::Register received_completion_value_register, Bytecode::IdentifierTableIndex type_identifier, Bytecode::IdentifierTableIndex value_identifier); +static Bytecode::Operand generate_await( + Bytecode::Generator& generator, + Bytecode::Operand argument, + Bytecode::Operand received_completion, + Bytecode::Operand received_completion_type, + Bytecode::Operand received_completion_value, + Bytecode::IdentifierTableIndex type_identifier, + Bytecode::IdentifierTableIndex value_identifier); // https://tc39.es/ecma262/#sec-return-statement-runtime-semantics-evaluation -Bytecode::CodeGenerationErrorOr> ReturnStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ReturnStatement::generate_bytecode(Bytecode::Generator& generator, Optional) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); + + Optional return_value; + if (m_argument) { // ReturnStatement : return Expression ; // 1. Let exprRef be ? Evaluation of Expression. // 2. Let exprValue be ? GetValue(exprRef). - FIXME_NEWBC TRY(m_argument->generate_bytecode(generator)); + return_value = TRY(m_argument->generate_bytecode(generator)).value(); // 3. If GetGeneratorKind() is async, set exprValue to ? Await(exprValue). // Spec Issue?: The spec doesn't seem to do implicit await on explicit return for async functions, but does for @@ -1624,44 +1674,43 @@ Bytecode::CodeGenerationErrorOr> ReturnStatement::ge // See: https://tc39.es/ecma262/#sec-asyncblockstart // c. Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done. if (generator.is_in_async_function()) { - auto received_completion_register = generator.allocate_register(); - auto received_completion_type_register = generator.allocate_register(); - auto received_completion_value_register = generator.allocate_register(); + auto received_completion = Bytecode::Operand(generator.allocate_register()); + auto received_completion_type = Bytecode::Operand(generator.allocate_register()); + auto received_completion_value = Bytecode::Operand(generator.allocate_register()); auto type_identifier = generator.intern_identifier("type"); auto value_identifier = generator.intern_identifier("value"); - generate_await(generator, received_completion_register, received_completion_type_register, received_completion_value_register, type_identifier, value_identifier); + return_value = generate_await(generator, *return_value, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier); } // 4. Return Completion Record { [[Type]]: return, [[Value]]: exprValue, [[Target]]: empty }. } else { // ReturnStatement : return ; // 1. Return Completion Record { [[Type]]: return, [[Value]]: undefined, [[Target]]: empty }. - generator.emit(js_undefined()); + return_value = generator.add_constant(js_undefined()); } if (generator.is_in_generator_or_async_function()) { generator.perform_needed_unwinds(); - generator.emit(nullptr); + generator.emit(nullptr, *return_value); } else { generator.perform_needed_unwinds(); - generator.emit(); + generator.emit(return_value); } - return Optional {}; + return return_value; } -static void get_received_completion_type_and_value(Bytecode::Generator& generator, Bytecode::Register received_completion_register, Bytecode::Register received_completion_type_register, Bytecode::Register received_completion_value_register, Bytecode::IdentifierTableIndex type_identifier, Bytecode::IdentifierTableIndex value_identifier) +static void get_received_completion_type_and_value( + Bytecode::Generator& generator, + Bytecode::Operand received_completion, + Bytecode::Operand received_completion_type, + Bytecode::Operand received_completion_value, + Bytecode::IdentifierTableIndex type_identifier, + Bytecode::IdentifierTableIndex value_identifier) { - // The accumulator is set to an object, for example: { "type": 1 (normal), value: 1337 } - generator.emit(received_completion_register); - - generator.emit_get_by_id(type_identifier); - generator.emit(received_completion_type_register); - - generator.emit(received_completion_register); - generator.emit_get_by_id(value_identifier); - generator.emit(received_completion_value_register); + generator.emit_get_by_id(received_completion_type, received_completion, type_identifier); + generator.emit_get_by_id(received_completion_value, received_completion, value_identifier); } enum class AwaitBeforeYield { @@ -1669,55 +1718,71 @@ enum class AwaitBeforeYield { Yes, }; -static void generate_yield(Bytecode::Generator& generator, Bytecode::Label continuation_label, Bytecode::Register received_completion_register, Bytecode::Register received_completion_type_register, Bytecode::Register received_completion_value_register, Bytecode::IdentifierTableIndex type_identifier, Bytecode::IdentifierTableIndex value_identifier, AwaitBeforeYield await_before_yield) +static void generate_yield(Bytecode::Generator& generator, + Bytecode::Label continuation_label, + Bytecode::Operand argument, + Bytecode::Operand received_completion, + Bytecode::Operand received_completion_type, + Bytecode::Operand received_completion_value, + Bytecode::IdentifierTableIndex type_identifier, + Bytecode::IdentifierTableIndex value_identifier, + AwaitBeforeYield await_before_yield) { if (!generator.is_in_async_generator_function()) { - generator.emit(Bytecode::Label { continuation_label }); + generator.emit(Bytecode::Label { continuation_label }, argument); return; } if (await_before_yield == AwaitBeforeYield::Yes) - generate_await(generator, received_completion_register, received_completion_type_register, received_completion_value_register, type_identifier, value_identifier); + argument = generate_await(generator, argument, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier); auto& unwrap_yield_resumption_block = generator.make_block(); - generator.emit(Bytecode::Label { unwrap_yield_resumption_block }); + generator.emit(Bytecode::Label { unwrap_yield_resumption_block }, argument); generator.switch_to_basic_block(unwrap_yield_resumption_block); - get_received_completion_type_and_value(generator, received_completion_register, received_completion_type_register, received_completion_value_register, type_identifier, value_identifier); + + generator.emit(received_completion, Bytecode::Operand(Bytecode::Register(0))); + get_received_completion_type_and_value(generator, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier); // 27.6.3.7 AsyncGeneratorUnwrapYieldResumption ( resumptionValue ), https://tc39.es/ecma262/#sec-asyncgeneratorunwrapyieldresumption // 1. If resumptionValue.[[Type]] is not return, return ? resumptionValue. - auto& load_completion_and_jump_to_continuation_label_block = generator.make_block(); auto& resumption_value_type_is_return_block = generator.make_block(); - generator.emit(Value(to_underlying(Completion::Type::Return))); - generator.emit(received_completion_type_register); - generator.emit( - Bytecode::Label { load_completion_and_jump_to_continuation_label_block }, + auto resumption_value_type_is_not_return_result = Bytecode::Operand(generator.allocate_register()); + generator.emit( + resumption_value_type_is_not_return_result, + received_completion_type, + generator.add_constant(Value(to_underlying(Completion::Type::Return)))); + generator.emit( + resumption_value_type_is_not_return_result, + Bytecode::Label { continuation_label }, Bytecode::Label { resumption_value_type_is_return_block }); generator.switch_to_basic_block(resumption_value_type_is_return_block); // 2. Let awaited be Completion(Await(resumptionValue.[[Value]])). - generator.emit(received_completion_value_register); - generate_await(generator, received_completion_register, received_completion_type_register, received_completion_value_register, type_identifier, value_identifier); + generate_await(generator, received_completion_value, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier); // 3. If awaited.[[Type]] is throw, return ? awaited. auto& awaited_type_is_normal_block = generator.make_block(); - generator.emit(Value(to_underlying(Completion::Type::Throw))); - generator.emit(received_completion_type_register); - generator.emit( - Bytecode::Label { load_completion_and_jump_to_continuation_label_block }, + auto awaited_type_is_throw_result = Bytecode::Operand(generator.allocate_register()); + generator.emit( + awaited_type_is_throw_result, + received_completion_type, + generator.add_constant(Value(to_underlying(Completion::Type::Throw)))); + generator.emit( + awaited_type_is_throw_result, + Bytecode::Label { continuation_label }, Bytecode::Label { awaited_type_is_normal_block }); // 4. Assert: awaited.[[Type]] is normal. generator.switch_to_basic_block(awaited_type_is_normal_block); // 5. Return Completion Record { [[Type]]: return, [[Value]]: awaited.[[Value]], [[Target]]: empty }. - generator.emit(Value(to_underlying(Completion::Type::Return))); - generator.emit(received_completion_register, type_identifier, Bytecode::Op::PropertyKind::KeyValue, generator.next_property_lookup_cache()); - generator.emit(Bytecode::Label { load_completion_and_jump_to_continuation_label_block }); - - generator.switch_to_basic_block(load_completion_and_jump_to_continuation_label_block); - generator.emit(received_completion_register); + generator.emit( + received_completion, + type_identifier, + generator.add_constant(Value(to_underlying(Completion::Type::Return))), + Bytecode::Op::PropertyKind::KeyValue, + generator.next_property_lookup_cache()); generator.emit(continuation_label); } @@ -1726,9 +1791,9 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge Bytecode::Generator::SourceLocationScope scope(generator, *this); VERIFY(generator.is_in_generator_function()); - auto received_completion_register = generator.allocate_register(); - auto received_completion_type_register = generator.allocate_register(); - auto received_completion_value_register = generator.allocate_register(); + auto received_completion = Bytecode::Operand(generator.allocate_register()); + auto received_completion_type = Bytecode::Operand(generator.allocate_register()); + auto received_completion_value = Bytecode::Operand(generator.allocate_register()); auto type_identifier = generator.intern_identifier("type"); auto value_identifier = generator.intern_identifier("value"); @@ -1741,29 +1806,26 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge // 2. Let exprRef be ? Evaluation of AssignmentExpression. // 3. Let value be ? GetValue(exprRef). VERIFY(m_argument); - FIXME_NEWBC TRY(m_argument->generate_bytecode(generator)); + auto value = TRY(m_argument->generate_bytecode(generator)).value(); // 4. Let iteratorRecord be ? GetIterator(value, generatorKind). - auto iterator_record_register = generator.allocate_register(); + auto iterator_record = Bytecode::Operand(generator.allocate_register()); auto iterator_hint = generator.is_in_async_generator_function() ? IteratorHint::Async : IteratorHint::Sync; - generator.emit(iterator_hint); - generator.emit(iterator_record_register); + generator.emit(iterator_record, value, iterator_hint); // 5. Let iterator be iteratorRecord.[[Iterator]]. - auto iterator_register = generator.allocate_register(); - generator.emit(iterator_register, iterator_record_register); + auto iterator = Bytecode::Operand(generator.allocate_register()); + generator.emit(iterator, iterator_record); // Cache iteratorRecord.[[NextMethod]] for use in step 7.a.i. - auto next_method_register = generator.allocate_register(); - generator.emit(next_method_register, iterator_record_register); + auto next_method = Bytecode::Operand(generator.allocate_register()); + generator.emit(next_method, iterator_record); // 6. Let received be NormalCompletion(undefined). // See get_received_completion_type_and_value above. - generator.emit(Value(to_underlying(Completion::Type::Normal))); - generator.emit(received_completion_type_register); + generator.emit(received_completion_type, generator.add_constant(Value(to_underlying(Completion::Type::Normal)))); - generator.emit(js_undefined()); - generator.emit(received_completion_value_register); + generator.emit(received_completion_value, generator.add_constant(js_undefined())); // 7. Repeat, auto& loop_block = generator.make_block(); @@ -1777,129 +1839,149 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge auto& type_is_normal_block = generator.make_block(); auto& is_type_throw_block = generator.make_block(); - generator.emit(Value(to_underlying(Completion::Type::Normal))); - generator.emit(received_completion_type_register); - generator.emit( + auto received_completion_type_register_is_normal = Bytecode::Operand(generator.allocate_register()); + generator.emit( + received_completion_type_register_is_normal, + received_completion_type, + generator.add_constant(Value(to_underlying(Completion::Type::Normal)))); + generator.emit( + received_completion_type_register_is_normal, Bytecode::Label { type_is_normal_block }, Bytecode::Label { is_type_throw_block }); generator.switch_to_basic_block(type_is_normal_block); // i. Let innerResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]], « received.[[Value]] »). - generator.emit_with_extra_register_slots(2, AK::Array { received_completion_value_register, received_completion_value_register }); - generator.emit(Bytecode::Op::CallType::Call, next_method_register, iterator_register); + auto array = Bytecode::Operand(generator.allocate_register()); + generator.emit_with_extra_operand_slots(2u, array, AK::Array { received_completion_value, received_completion_value }); + auto inner_result = Bytecode::Operand(generator.allocate_register()); + generator.emit(Bytecode::Op::CallType::Call, inner_result, next_method, iterator, array); // ii. If generatorKind is async, set innerResult to ? Await(innerResult). - if (generator.is_in_async_generator_function()) - generate_await(generator, received_completion_register, received_completion_type_register, received_completion_value_register, type_identifier, value_identifier); + if (generator.is_in_async_generator_function()) { + auto new_inner_result = generate_await(generator, inner_result, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier); + generator.emit(inner_result, new_inner_result); + } // iii. If innerResult is not an Object, throw a TypeError exception. - generator.emit(); - - auto inner_result_register = generator.allocate_register(); - generator.emit(inner_result_register); + generator.emit(inner_result); // iv. Let done be ? IteratorComplete(innerResult). - generator.emit_iterator_complete(); + auto done = Bytecode::Operand(generator.allocate_register()); + generator.emit_iterator_complete(done, inner_result); // v. If done is true, then auto& type_is_normal_done_block = generator.make_block(); auto& type_is_normal_not_done_block = generator.make_block(); - generator.emit( + generator.emit( + done, Bytecode::Label { type_is_normal_done_block }, Bytecode::Label { type_is_normal_not_done_block }); generator.switch_to_basic_block(type_is_normal_done_block); // 1. Return ? IteratorValue(innerResult). - generator.emit(inner_result_register); - generator.emit_iterator_value(); + auto return_value = Bytecode::Operand(generator.allocate_register()); + generator.emit_iterator_value(return_value, inner_result); generator.emit(Bytecode::Label { loop_end_block }); generator.switch_to_basic_block(type_is_normal_not_done_block); // vi. If generatorKind is async, set received to Completion(AsyncGeneratorYield(? IteratorValue(innerResult))). // vii. Else, set received to Completion(GeneratorYield(innerResult)). - generator.emit(inner_result_register); - // FIXME: Yield currently only accepts a Value, not an object conforming to the IteratorResult interface, so we have to do an observable lookup of `value` here. - // This only matters for non-async generators. - generator.emit_iterator_value(); + { + // FIXME: Yield currently only accepts a Value, not an object conforming to the IteratorResult interface, so we have to do an observable lookup of `value` here. + // This only matters for non-async generators. + auto current_value = Bytecode::Operand(generator.allocate_register()); + generator.emit_iterator_value(current_value, inner_result); - generate_yield(generator, Bytecode::Label { continuation_block }, received_completion_register, received_completion_type_register, received_completion_value_register, type_identifier, value_identifier, AwaitBeforeYield::No); + generate_yield(generator, + Bytecode::Label { continuation_block }, + current_value, + received_completion, + received_completion_type, + received_completion_value, + type_identifier, + value_identifier, + AwaitBeforeYield::No); + } // b. Else if received.[[Type]] is throw, then generator.switch_to_basic_block(is_type_throw_block); auto& type_is_throw_block = generator.make_block(); auto& type_is_return_block = generator.make_block(); - generator.emit(Value(to_underlying(Completion::Type::Throw))); - generator.emit(received_completion_type_register); - generator.emit( + auto received_completion_type_register_is_throw = Bytecode::Operand(generator.allocate_register()); + generator.emit( + received_completion_type_register_is_throw, + Bytecode::Operand(received_completion_type), + generator.add_constant(Value(to_underlying(Completion::Type::Throw)))); + generator.emit( + received_completion_type_register_is_throw, Bytecode::Label { type_is_throw_block }, Bytecode::Label { type_is_return_block }); generator.switch_to_basic_block(type_is_throw_block); // i. Let throw be ? GetMethod(iterator, "throw"). - auto throw_method_register = generator.allocate_register(); - auto throw_identifier = generator.intern_identifier("throw"); - generator.emit(iterator_register); - generator.emit(throw_identifier); - generator.emit(throw_method_register); + auto throw_method = Bytecode::Operand(generator.allocate_register()); + generator.emit(throw_method, iterator, generator.intern_identifier("throw")); // ii. If throw is not undefined, then auto& throw_method_is_defined_block = generator.make_block(); auto& throw_method_is_undefined_block = generator.make_block(); - generator.emit(js_undefined()); - generator.emit(throw_method_register); - generator.emit( - Bytecode::Label { throw_method_is_defined_block }, - Bytecode::Label { throw_method_is_undefined_block }); + generator.emit( + throw_method, + Bytecode::Label { throw_method_is_undefined_block }, + Bytecode::Label { throw_method_is_defined_block }); generator.switch_to_basic_block(throw_method_is_defined_block); // 1. Let innerResult be ? Call(throw, iterator, « received.[[Value]] »). - generator.emit_with_extra_register_slots(2, AK::Array { received_completion_value_register, received_completion_value_register }); - generator.emit(Bytecode::Op::CallType::Call, throw_method_register, iterator_register); + auto received_value_array = Bytecode::Operand(generator.allocate_register()); + generator.emit_with_extra_operand_slots(2u, received_value_array, AK::Array { received_completion_value, received_completion_value }); + generator.emit(Bytecode::Op::CallType::Call, inner_result, throw_method, iterator, received_value_array); // 2. If generatorKind is async, set innerResult to ? Await(innerResult). - if (generator.is_in_async_generator_function()) - generate_await(generator, received_completion_register, received_completion_type_register, received_completion_value_register, type_identifier, value_identifier); + if (generator.is_in_async_generator_function()) { + auto new_result = generate_await(generator, inner_result, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier); + generator.emit(inner_result, new_result); + } // 3. NOTE: Exceptions from the inner iterator throw method are propagated. Normal completions from an inner throw method are processed similarly to an inner next. // 4. If innerResult is not an Object, throw a TypeError exception. - generator.emit(); - generator.emit(inner_result_register); + generator.emit(inner_result); // 5. Let done be ? IteratorComplete(innerResult). - generator.emit_iterator_complete(); + generator.emit_iterator_complete(done, inner_result); // 6. If done is true, then auto& type_is_throw_done_block = generator.make_block(); auto& type_is_throw_not_done_block = generator.make_block(); - generator.emit( + generator.emit( + done, Bytecode::Label { type_is_throw_done_block }, Bytecode::Label { type_is_throw_not_done_block }); generator.switch_to_basic_block(type_is_throw_done_block); // a. Return ? IteratorValue(innerResult). - generator.emit(inner_result_register); - generator.emit_iterator_value(); + generator.emit_iterator_value(return_value, inner_result); generator.emit(Bytecode::Label { loop_end_block }); generator.switch_to_basic_block(type_is_throw_not_done_block); - // 7. If generatorKind is async, set received to Completion(AsyncGeneratorYield(? IteratorValue(innerResult))). - // 8. Else, set received to Completion(GeneratorYield(innerResult)). - generator.emit(inner_result_register); + { + // 7. If generatorKind is async, set received to Completion(AsyncGeneratorYield(? IteratorValue(innerResult))). + // 8. Else, set received to Completion(GeneratorYield(innerResult)). - // FIXME: Yield currently only accepts a Value, not an object conforming to the IteratorResult interface, so we have to do an observable lookup of `value` here. - // This only matters for non-async generators. - generator.emit_iterator_value(); - - generate_yield(generator, Bytecode::Label { continuation_block }, received_completion_register, received_completion_type_register, received_completion_value_register, type_identifier, value_identifier, AwaitBeforeYield::No); + // FIXME: Yield currently only accepts a Value, not an object conforming to the IteratorResult interface, so we have to do an observable lookup of `value` here. + // This only matters for non-async generators. + auto yield_value = Bytecode::Operand(generator.allocate_register()); + generator.emit_iterator_value(yield_value, inner_result); + generate_yield(generator, Bytecode::Label { continuation_block }, yield_value, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier, AwaitBeforeYield::No); + } generator.switch_to_basic_block(throw_method_is_undefined_block); @@ -1907,126 +1989,130 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge // 2. Let closeCompletion be Completion Record { [[Type]]: normal, [[Value]]: empty, [[Target]]: empty }. // 3. If generatorKind is async, perform ? AsyncIteratorClose(iteratorRecord, closeCompletion). - generator.emit(iterator_record_register); if (generator.is_in_async_generator_function()) { // FIXME: This performs `await` outside of the generator! - generator.emit(Completion::Type::Normal, Optional {}); + generator.emit(iterator_record, Completion::Type::Normal, Optional {}); } // 4. Else, perform ? IteratorClose(iteratorRecord, closeCompletion). else { - generator.emit(Completion::Type::Normal, Optional {}); + generator.emit(iterator_record, Completion::Type::Normal, Optional {}); } // 5. NOTE: The next step throws a TypeError to indicate that there was a yield* protocol violation: iterator does not have a throw method. // 6. Throw a TypeError exception. - generator.emit(generator.intern_string(ErrorType::YieldFromIteratorMissingThrowMethod.message())); + auto exception = Bytecode::Operand(generator.allocate_register()); + generator.emit(exception, generator.intern_string(ErrorType::YieldFromIteratorMissingThrowMethod.message())); generator.perform_needed_unwinds(); - generator.emit(); + generator.emit(exception); // c. Else, // i. Assert: received.[[Type]] is return. generator.switch_to_basic_block(type_is_return_block); // ii. Let return be ? GetMethod(iterator, "return"). - auto return_method_register = generator.allocate_register(); - auto return_identifier = generator.intern_identifier("return"); - generator.emit(iterator_register); - generator.emit(return_identifier); - generator.emit(return_method_register); + auto return_method = Bytecode::Operand(generator.allocate_register()); + generator.emit(return_method, iterator, generator.intern_identifier("return")); // iii. If return is undefined, then auto& return_is_undefined_block = generator.make_block(); auto& return_is_defined_block = generator.make_block(); - generator.emit(js_undefined()); - generator.emit(return_method_register); - generator.emit( + generator.emit( + return_method, Bytecode::Label { return_is_undefined_block }, Bytecode::Label { return_is_defined_block }); generator.switch_to_basic_block(return_is_undefined_block); // 1. If generatorKind is async, set received.[[Value]] to ? Await(received.[[Value]]). - generator.emit(received_completion_value_register); - if (generator.is_in_async_generator_function()) - generate_await(generator, received_completion_register, received_completion_type_register, received_completion_value_register, type_identifier, value_identifier); + if (generator.is_in_async_generator_function()) { + generate_await(generator, received_completion_value, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier); + } // 2. Return ? received. // NOTE: This will always be a return completion. generator.perform_needed_unwinds(); - generator.emit(nullptr); + generator.emit(nullptr, received_completion_value); generator.switch_to_basic_block(return_is_defined_block); // iv. Let innerReturnResult be ? Call(return, iterator, « received.[[Value]] »). - generator.emit_with_extra_register_slots(2, AK::Array { received_completion_value_register, received_completion_value_register }); - generator.emit(Bytecode::Op::CallType::Call, return_method_register, iterator_register); + auto call_array = Bytecode::Operand(generator.allocate_register()); + generator.emit_with_extra_operand_slots(2, call_array, AK::Array { received_completion_value, received_completion_value }); + auto inner_return_result = Bytecode::Operand(generator.allocate_register()); + generator.emit(Bytecode::Op::CallType::Call, inner_return_result, return_method, iterator, call_array); // v. If generatorKind is async, set innerReturnResult to ? Await(innerReturnResult). - if (generator.is_in_async_generator_function()) - generate_await(generator, received_completion_register, received_completion_type_register, received_completion_value_register, type_identifier, value_identifier); + if (generator.is_in_async_generator_function()) { + auto new_value = generate_await(generator, inner_return_result, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier); + generator.emit(inner_return_result, new_value); + } // vi. If innerReturnResult is not an Object, throw a TypeError exception. - generator.emit(); - - auto inner_return_result_register = generator.allocate_register(); - generator.emit(inner_return_result_register); + generator.emit(inner_return_result); // vii. Let done be ? IteratorComplete(innerReturnResult). - generator.emit_iterator_complete(); + generator.emit_iterator_complete(done, inner_return_result); // viii. If done is true, then auto& type_is_return_done_block = generator.make_block(); auto& type_is_return_not_done_block = generator.make_block(); - generator.emit( + generator.emit( + done, Bytecode::Label { type_is_return_done_block }, Bytecode::Label { type_is_return_not_done_block }); generator.switch_to_basic_block(type_is_return_done_block); // 1. Let value be ? IteratorValue(innerReturnResult). - generator.emit(inner_result_register); - generator.emit_iterator_value(); + auto inner_return_result_value = Bytecode::Operand(generator.allocate_register()); + generator.emit_iterator_value(inner_return_result_value, inner_return_result); // 2. Return Completion Record { [[Type]]: return, [[Value]]: value, [[Target]]: empty }. generator.perform_needed_unwinds(); - generator.emit(nullptr); + generator.emit(nullptr, inner_return_result_value); generator.switch_to_basic_block(type_is_return_not_done_block); // ix. If generatorKind is async, set received to Completion(AsyncGeneratorYield(? IteratorValue(innerReturnResult))). // x. Else, set received to Completion(GeneratorYield(innerReturnResult)). - generator.emit(inner_return_result_register); - // FIXME: Yield currently only accepts a Value, not an object conforming to the IteratorResult interface, so we have to do an observable lookup of `value` here. // This only matters for non-async generators. - generator.emit_iterator_value(); + auto received = Bytecode::Operand(generator.allocate_register()); + generator.emit_iterator_value(received, inner_return_result); - generate_yield(generator, Bytecode::Label { continuation_block }, received_completion_register, received_completion_type_register, received_completion_value_register, type_identifier, value_identifier, AwaitBeforeYield::No); + generate_yield(generator, Bytecode::Label { continuation_block }, received, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier, AwaitBeforeYield::No); generator.switch_to_basic_block(continuation_block); - get_received_completion_type_and_value(generator, received_completion_register, received_completion_type_register, received_completion_value_register, type_identifier, value_identifier); + generator.emit(received_completion, Bytecode::Operand(Bytecode::Register(0))); + get_received_completion_type_and_value(generator, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier); generator.emit(Bytecode::Label { loop_block }); generator.switch_to_basic_block(loop_end_block); - return Optional {}; + return return_value; } + Optional argument; if (m_argument) - FIXME_NEWBC TRY(m_argument->generate_bytecode(generator)); + argument = TRY(m_argument->generate_bytecode(generator)).value(); else - generator.emit(js_undefined()); + argument = generator.add_constant(js_undefined()); auto& continuation_block = generator.make_block(); - generate_yield(generator, Bytecode::Label { continuation_block }, received_completion_register, received_completion_type_register, received_completion_value_register, type_identifier, value_identifier, AwaitBeforeYield::Yes); + generate_yield(generator, Bytecode::Label { continuation_block }, *argument, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier, AwaitBeforeYield::Yes); generator.switch_to_basic_block(continuation_block); - get_received_completion_type_and_value(generator, received_completion_register, received_completion_type_register, received_completion_value_register, type_identifier, value_identifier); + generator.emit(received_completion, Bytecode::Operand(Bytecode::Register(0))); + get_received_completion_type_and_value(generator, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier); auto& normal_completion_continuation_block = generator.make_block(); auto& throw_completion_continuation_block = generator.make_block(); - generator.emit(Value(to_underlying(Completion::Type::Normal))); - generator.emit(received_completion_type_register); - generator.emit( + auto received_completion_type_is_normal = Bytecode::Operand(generator.allocate_register()); + generator.emit( + received_completion_type_is_normal, + received_completion_type, + generator.add_constant(Value(to_underlying(Completion::Type::Normal)))); + generator.emit( + received_completion_type_is_normal, Bytecode::Label { normal_completion_continuation_block }, Bytecode::Label { throw_completion_continuation_block }); @@ -2034,30 +2120,31 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge auto& return_value_block = generator.make_block(); generator.switch_to_basic_block(throw_completion_continuation_block); - generator.emit(Value(to_underlying(Completion::Type::Throw))); - generator.emit(received_completion_type_register); + auto received_completion_type_is_throw = Bytecode::Operand(generator.allocate_register()); + generator.emit( + received_completion_type_is_throw, + received_completion_type, + generator.add_constant(Value(to_underlying(Completion::Type::Throw)))); // If type is not equal to "throw" or "normal", assume it's "return". - generator.emit( + generator.emit( + received_completion_type_is_throw, Bytecode::Label { throw_value_block }, Bytecode::Label { return_value_block }); generator.switch_to_basic_block(throw_value_block); - generator.emit(received_completion_value_register); generator.perform_needed_unwinds(); - generator.emit(); + generator.emit(received_completion_value); generator.switch_to_basic_block(return_value_block); - generator.emit(received_completion_value_register); generator.perform_needed_unwinds(); - generator.emit(nullptr); + generator.emit(nullptr, received_completion_value); generator.switch_to_basic_block(normal_completion_continuation_block); - generator.emit(received_completion_value_register); - return Optional {}; + return received_completion_value; } -Bytecode::CodeGenerationErrorOr> IfStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> IfStatement::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); // test @@ -2072,28 +2159,38 @@ Bytecode::CodeGenerationErrorOr> IfStatement::genera auto& false_block = generator.make_block(); auto& end_block = generator.make_block(); - FIXME_NEWBC TRY(m_predicate->generate_bytecode(generator)); - generator.emit( + auto dst = choose_dst(generator, preferred_dst); + generator.emit(dst, generator.add_constant(js_undefined())); + + auto predicate = TRY(m_predicate->generate_bytecode(generator)).value(); + generator.emit( + predicate, Bytecode::Label { true_block }, Bytecode::Label { false_block }); generator.switch_to_basic_block(true_block); - generator.emit(js_undefined()); - FIXME_NEWBC TRY(m_consequent->generate_bytecode(generator)); + auto consequent = TRY(m_consequent->generate_bytecode(generator, dst)); if (!generator.is_current_block_terminated()) { + if (consequent.has_value()) + generator.emit(dst, *consequent); generator.emit(Bytecode::Label { end_block }); } generator.switch_to_basic_block(false_block); - generator.emit(js_undefined()); - if (m_alternate) - FIXME_NEWBC TRY(m_alternate->generate_bytecode(generator)); - if (!generator.is_current_block_terminated()) + Optional alternate; + if (m_alternate) { + alternate = TRY(m_alternate->generate_bytecode(generator, dst)); + } + if (!generator.is_current_block_terminated()) { + if (alternate.has_value()) + generator.emit(dst, *alternate); generator.emit(Bytecode::Label { end_block }); + } generator.switch_to_basic_block(end_block); - return Optional {}; + + return dst; } Bytecode::CodeGenerationErrorOr> ContinueStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const @@ -2116,7 +2213,7 @@ Bytecode::CodeGenerationErrorOr> DebuggerStatement:: return Optional {}; } -Bytecode::CodeGenerationErrorOr> ConditionalExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ConditionalExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); // test @@ -2131,56 +2228,62 @@ Bytecode::CodeGenerationErrorOr> ConditionalExpressi auto& false_block = generator.make_block(); auto& end_block = generator.make_block(); - FIXME_NEWBC TRY(m_test->generate_bytecode(generator)); - generator.emit( + auto test = TRY(m_test->generate_bytecode(generator)).value(); + generator.emit( + test, Bytecode::Label { true_block }, Bytecode::Label { false_block }); + auto dst = choose_dst(generator, preferred_dst); + generator.switch_to_basic_block(true_block); - FIXME_NEWBC TRY(m_consequent->generate_bytecode(generator)); + auto consequent = TRY(m_consequent->generate_bytecode(generator)).value(); + generator.emit(dst, consequent); + generator.emit(Bytecode::Label { end_block }); generator.switch_to_basic_block(false_block); - FIXME_NEWBC TRY(m_alternate->generate_bytecode(generator)); + auto alternate = TRY(m_alternate->generate_bytecode(generator)).value(); + generator.emit(dst, alternate); generator.emit(Bytecode::Label { end_block }); generator.switch_to_basic_block(end_block); - return Optional {}; + return dst; } Bytecode::CodeGenerationErrorOr> SequenceExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - for (auto& expression : m_expressions) - FIXME_NEWBC TRY(expression->generate_bytecode(generator)); + Optional last_value; + for (auto& expression : m_expressions) { + last_value = TRY(expression->generate_bytecode(generator)); + } - return Optional {}; + return last_value; } -Bytecode::CodeGenerationErrorOr> TemplateLiteral::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> TemplateLiteral::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - auto string_reg = generator.allocate_register(); + + auto dst = choose_dst(generator, preferred_dst); for (size_t i = 0; i < m_expressions.size(); i++) { - FIXME_NEWBC TRY(m_expressions[i]->generate_bytecode(generator)); + auto value = TRY(m_expressions[i]->generate_bytecode(generator)).value(); if (i == 0) { - generator.emit(string_reg); + generator.emit(dst, value); } else { - generator.emit(string_reg); + generator.emit(dst, value); } } - generator.emit(string_reg); - return Optional {}; + return dst; } -Bytecode::CodeGenerationErrorOr> TaggedTemplateLiteral::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> TaggedTemplateLiteral::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - FIXME_NEWBC TRY(m_tag->generate_bytecode(generator)); - auto tag_reg = generator.allocate_register(); - generator.emit(tag_reg); + auto tag = TRY(m_tag->generate_bytecode(generator)).value(); // FIXME: We only need to record the first and last register, // due to packing everything in an array, same goes for argument_regs @@ -2190,12 +2293,12 @@ Bytecode::CodeGenerationErrorOr> TaggedTemplateLiter // * cache this somehow // * add a raw object accessor // * freeze array and raw member - Vector string_regs; + Vector string_regs; auto& expressions = m_template_literal->expressions(); for (size_t i = 0; i < expressions.size(); ++i) { if (i % 2 != 0) continue; - string_regs.append(generator.allocate_register()); + string_regs.append(Bytecode::Operand(generator.allocate_register())); } size_t reg_index = 0; @@ -2205,102 +2308,97 @@ Bytecode::CodeGenerationErrorOr> TaggedTemplateLiter // NOTE: If the string contains invalid escapes we get a null expression here, // which we then convert to the expected `undefined` TV. See // 12.9.6.1 Static Semantics: TV, https://tc39.es/ecma262/#sec-static-semantics-tv - if (is(expressions[i])) - generator.emit(js_undefined()); - else - FIXME_NEWBC TRY(expressions[i]->generate_bytecode(generator)); - - auto string_reg = string_regs[reg_index++]; - generator.emit(string_reg); + auto string_reg = Bytecode::Operand(string_regs[reg_index++]); + if (is(expressions[i])) { + generator.emit(string_reg, generator.add_constant(js_undefined())); + } else { + auto value = TRY(expressions[i]->generate_bytecode(generator)).value(); + generator.emit(string_reg, value); + } } + auto strings_array = Bytecode::Operand(generator.allocate_register()); if (string_regs.is_empty()) { - generator.emit(); + generator.emit(strings_array); } else { - generator.emit_with_extra_register_slots(2u, AK::Array { string_regs.first(), string_regs.last() }); + generator.emit_with_extra_operand_slots(2u, strings_array, AK::Array { string_regs.first(), string_regs.last() }); } - auto strings_reg = generator.allocate_register(); - generator.emit(strings_reg); - Vector argument_regs; - argument_regs.append(strings_reg); + Vector argument_regs; + argument_regs.append(strings_array); for (size_t i = 1; i < expressions.size(); i += 2) - argument_regs.append(generator.allocate_register()); + argument_regs.append(Bytecode::Operand(generator.allocate_register())); for (size_t i = 1; i < expressions.size(); i += 2) { auto string_reg = argument_regs[1 + i / 2]; - FIXME_NEWBC TRY(expressions[i]->generate_bytecode(generator)); - generator.emit(string_reg); + auto string = TRY(expressions[i]->generate_bytecode(generator)).value(); + generator.emit(string_reg, string); } - Vector raw_string_regs; + Vector raw_string_regs; for ([[maybe_unused]] auto& raw_string : m_template_literal->raw_strings()) - string_regs.append(generator.allocate_register()); + string_regs.append(Bytecode::Operand(generator.allocate_register())); reg_index = 0; for (auto& raw_string : m_template_literal->raw_strings()) { - FIXME_NEWBC TRY(raw_string->generate_bytecode(generator)); + auto value = TRY(raw_string->generate_bytecode(generator)).value(); auto raw_string_reg = string_regs[reg_index++]; - generator.emit(raw_string_reg); + generator.emit(raw_string_reg, value); raw_string_regs.append(raw_string_reg); } + auto raw_strings_array = Bytecode::Operand(generator.allocate_register()); if (raw_string_regs.is_empty()) { - generator.emit(); + generator.emit(raw_strings_array); } else { - generator.emit_with_extra_register_slots(2u, AK::Array { raw_string_regs.first(), raw_string_regs.last() }); + generator.emit_with_extra_operand_slots(2u, raw_strings_array, AK::Array { raw_string_regs.first(), raw_string_regs.last() }); } - auto raw_strings_reg = generator.allocate_register(); - generator.emit(raw_strings_reg); - generator.emit(strings_reg, generator.intern_identifier("raw"), Bytecode::Op::PropertyKind::KeyValue, generator.next_property_lookup_cache()); - - generator.emit(js_undefined()); - auto this_reg = generator.allocate_register(); - generator.emit(this_reg); + generator.emit(strings_array, generator.intern_identifier("raw"), raw_strings_array, Bytecode::Op::PropertyKind::KeyValue, generator.next_property_lookup_cache()); + auto arguments = Bytecode::Operand(generator.allocate_register()); if (!argument_regs.is_empty()) - generator.emit_with_extra_register_slots(2, AK::Array { argument_regs.first(), argument_regs.last() }); + generator.emit_with_extra_operand_slots(2, arguments, AK::Array { argument_regs.first(), argument_regs.last() }); else - generator.emit(); + generator.emit(arguments); - generator.emit(Bytecode::Op::CallType::Call, tag_reg, this_reg); - return Optional {}; + auto dst = choose_dst(generator, preferred_dst); + generator.emit(Bytecode::Op::CallType::Call, dst, tag, generator.add_constant(js_undefined()), arguments); + return dst; } -Bytecode::CodeGenerationErrorOr> UpdateExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> UpdateExpression::generate_bytecode(Bytecode::Generator& generator, Optional) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - auto reference_registers = TRY(generator.emit_load_from_reference(*m_argument, Bytecode::Generator::CollectRegisters::Yes)); + auto reference = TRY(generator.emit_load_from_reference(*m_argument)); - Optional previous_value_for_postfix_reg; + Optional previous_value_for_postfix; if (!m_prefixed) { - previous_value_for_postfix_reg = generator.allocate_register(); - generator.emit(); - generator.emit(*previous_value_for_postfix_reg); + previous_value_for_postfix = Bytecode::Operand(generator.allocate_register()); + generator.emit(*previous_value_for_postfix, *reference.loaded_value); } if (m_op == UpdateOp::Increment) - generator.emit(); + generator.emit(*reference.loaded_value); else - generator.emit(); + generator.emit(*reference.loaded_value); - if (reference_registers.has_value()) - FIXME_NEWBC TRY(generator.emit_store_to_reference(*reference_registers)); + if (is(*m_argument)) + (void)TRY(generator.emit_store_to_reference(static_cast(*m_argument), *reference.loaded_value)); else - FIXME_NEWBC TRY(generator.emit_store_to_reference(*m_argument)); + (void)TRY(generator.emit_store_to_reference(reference, *reference.loaded_value)); if (!m_prefixed) - generator.emit(*previous_value_for_postfix_reg); - return Optional {}; + return *previous_value_for_postfix; + return *reference.loaded_value; } Bytecode::CodeGenerationErrorOr> ThrowStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - FIXME_NEWBC TRY(m_argument->generate_bytecode(generator)); + auto argument = TRY(m_argument->generate_bytecode(generator)).value(); generator.perform_needed_unwinds(); - generator.emit(); + generator.emit(argument); return Optional {}; } @@ -2330,12 +2428,14 @@ Bytecode::CodeGenerationErrorOr> TryStatement::gener Bytecode::BasicBlock* next_block { nullptr }; + Optional dst; + if (m_finalizer) { // FIXME: See notes in Op.h->ScheduleJump auto& finalizer_block = generator.make_block(); generator.switch_to_basic_block(finalizer_block); generator.emit(); - FIXME_NEWBC TRY(m_finalizer->generate_bytecode(generator)); + (void)TRY(m_finalizer->generate_bytecode(generator)); if (!generator.is_current_block_terminated()) { next_block = &generator.make_block(); auto next_target = Bytecode::Label { *next_block }; @@ -2350,7 +2450,8 @@ Bytecode::CodeGenerationErrorOr> TryStatement::gener auto& handler_block = generator.make_block(); generator.switch_to_basic_block(handler_block); - generator.emit(); + auto caught_value = Bytecode::Operand { generator.allocate_register() }; + generator.emit(caught_value); if (!m_finalizer) generator.emit(); @@ -2358,33 +2459,29 @@ Bytecode::CodeGenerationErrorOr> TryStatement::gener // OPTIMIZATION: We avoid creating a lexical environment if the catch clause has no parameter. bool did_create_variable_scope_for_catch_clause = false; - FIXME_NEWBC TRY(m_handler->parameter().visit( - [&](DeprecatedFlyString const& parameter) -> Bytecode::CodeGenerationErrorOr> { + TRY(m_handler->parameter().visit( + [&](DeprecatedFlyString const& parameter) -> Bytecode::CodeGenerationErrorOr { if (!parameter.is_empty()) { generator.begin_variable_scope(); did_create_variable_scope_for_catch_clause = true; auto parameter_identifier = generator.intern_identifier(parameter); generator.emit(parameter_identifier, Bytecode::Op::EnvironmentMode::Lexical, false); - generator.emit(parameter_identifier, generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize); + generator.emit(parameter_identifier, caught_value, generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize); } - return Optional {}; + return {}; }, - [&](NonnullRefPtr const& binding_pattern) -> Bytecode::CodeGenerationErrorOr> { + [&](NonnullRefPtr const& binding_pattern) -> Bytecode::CodeGenerationErrorOr { generator.begin_variable_scope(); did_create_variable_scope_for_catch_clause = true; - - auto value_register = generator.allocate_register(); - generator.emit(value_register); - FIXME_NEWBC TRY(generate_binding_pattern_bytecode(generator, *binding_pattern, Bytecode::Op::SetVariable::InitializationMode::Initialize, value_register, true)); - return Optional {}; + TRY(generate_binding_pattern_bytecode(generator, *binding_pattern, Bytecode::Op::SetVariable::InitializationMode::Initialize, caught_value, true)); + return {}; })); - // Set accumulator to undefined, otherwise we leak the error object through the accumulator. - // For example: `try { BigInt.call() } catch {}` would result in the error object. Note that - // the exception _is_ caught here, it just leaks the error object through to the result. - generator.emit(js_undefined()); - - FIXME_NEWBC TRY(m_handler->body().generate_bytecode(generator)); + auto handler_result = TRY(m_handler->body().generate_bytecode(generator)); + if (handler_result.has_value() && !generator.is_current_block_terminated()) { + dst = Bytecode::Operand(generator.allocate_register()); + generator.emit(*dst, *handler_result); + } handler_target = Bytecode::Label { handler_block }; if (did_create_variable_scope_for_catch_clause) @@ -2418,8 +2515,13 @@ Bytecode::CodeGenerationErrorOr> TryStatement::gener generator.start_boundary(Bytecode::Generator::BlockBoundaryType::ReturnToFinally); generator.switch_to_basic_block(target_block); - FIXME_NEWBC TRY(m_block->generate_bytecode(generator)); + auto block_result = TRY(m_block->generate_bytecode(generator)); if (!generator.is_current_block_terminated()) { + if (block_result.has_value()) { + dst = Bytecode::Operand(generator.allocate_register()); + generator.emit(*dst, *block_result); + } + if (m_finalizer) { generator.emit(*finalizer_target); } else { @@ -2437,7 +2539,9 @@ Bytecode::CodeGenerationErrorOr> TryStatement::gener generator.end_boundary(Bytecode::Generator::BlockBoundaryType::Unwind); generator.switch_to_basic_block(next_block ? *next_block : saved_block); - return Optional {}; + if (!dst.has_value()) + return generator.add_constant(js_undefined()); + return dst; } Bytecode::CodeGenerationErrorOr> SwitchStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const @@ -2449,9 +2553,7 @@ Bytecode::CodeGenerationErrorOr> SwitchStatement::ge Bytecode::CodeGenerationErrorOr> SwitchStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - auto discriminant_reg = generator.allocate_register(); - FIXME_NEWBC TRY(m_discriminant->generate_bytecode(generator)); - generator.emit(discriminant_reg); + auto discriminant = TRY(m_discriminant->generate_bytecode(generator)).value(); Vector case_blocks; Bytecode::BasicBlock* entry_block_for_default { nullptr }; Bytecode::BasicBlock* next_test_block = &generator.make_block(); @@ -2467,10 +2569,12 @@ Bytecode::CodeGenerationErrorOr> SwitchStatement::ge auto& case_entry_block = generator.make_block(); if (switch_case->test()) { generator.switch_to_basic_block(*next_test_block); - FIXME_NEWBC TRY(switch_case->test()->generate_bytecode(generator)); - generator.emit(discriminant_reg); + auto test_value = TRY(switch_case->test()->generate_bytecode(generator)).value(); + auto result = Bytecode::Operand(generator.allocate_register()); + generator.emit(result, test_value, discriminant); next_test_block = &generator.make_block(); - generator.emit( + generator.emit( + result, Bytecode::Label { case_entry_block }, Bytecode::Label { *next_test_block }); } else { @@ -2480,7 +2584,6 @@ Bytecode::CodeGenerationErrorOr> SwitchStatement::ge // Initialize the completion value of the switch statement to empty. We can't do this in the case's basic block directly, // as we must not clobber the possible non-empty completion value of the previous case when falling through. generator.switch_to_basic_block(case_entry_block); - generator.emit(js_undefined()); generator.emit(Bytecode::Label { case_block }); case_blocks.append(case_block); @@ -2491,18 +2594,21 @@ Bytecode::CodeGenerationErrorOr> SwitchStatement::ge if (entry_block_for_default != nullptr) { generator.emit(Bytecode::Label { *entry_block_for_default }); } else { - generator.emit(js_undefined()); generator.emit(Bytecode::Label { end_block }); } auto current_block = case_blocks.begin(); generator.begin_breakable_scope(Bytecode::Label { end_block }, label_set); + auto dst = Bytecode::Operand(generator.allocate_register()); for (auto& switch_case : m_cases) { generator.switch_to_basic_block(*current_block); - for (auto& statement : switch_case->children()) { - FIXME_NEWBC TRY(statement->generate_bytecode(generator)); + auto result = TRY(statement->generate_bytecode(generator)); if (generator.is_current_block_terminated()) break; + if (result.has_value()) + generator.emit(dst, *result); + else + generator.emit(dst, generator.add_constant(js_undefined())); } if (!generator.is_current_block_terminated()) { auto next_block = current_block; @@ -2522,7 +2628,7 @@ Bytecode::CodeGenerationErrorOr> SwitchStatement::ge if (has_lexical_declarations) generator.end_variable_scope(); - return Optional {}; + return dst; } Bytecode::CodeGenerationErrorOr> SuperExpression::generate_bytecode(Bytecode::Generator&, [[maybe_unused]] Optional preferred_dst) const @@ -2534,18 +2640,14 @@ Bytecode::CodeGenerationErrorOr> SuperExpression::ge Bytecode::CodeGenerationErrorOr> ClassDeclaration::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - auto accumulator_backup_reg = generator.allocate_register(); - generator.emit(accumulator_backup_reg); - - FIXME_NEWBC TRY(m_class_expression->generate_bytecode(generator)); - generator.emit_set_variable(*m_class_expression.ptr()->m_name, Bytecode::Op::SetVariable::InitializationMode::Initialize); - - generator.emit(accumulator_backup_reg); + auto value = TRY(m_class_expression->generate_bytecode(generator)).value(); + generator.emit_set_variable(*m_class_expression.ptr()->m_name, value, Bytecode::Op::SetVariable::InitializationMode::Initialize); + // NOTE: ClassDeclaration does not produce a value. return Optional {}; } // 15.7.14 Runtime Semantics: ClassDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation -Bytecode::CodeGenerationErrorOr> ClassExpression::generate_bytecode_with_lhs_name(Bytecode::Generator& generator, Optional lhs_name, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ClassExpression::generate_bytecode_with_lhs_name(Bytecode::Generator& generator, Optional lhs_name, Optional preferred_dst) const { // NOTE: Step 2 is not a part of NewClass instruction because it is assumed to be done before super class expression evaluation generator.emit(); @@ -2556,18 +2658,20 @@ Bytecode::CodeGenerationErrorOr> ClassExpression::ge generator.emit(interned_index, Bytecode::Op::EnvironmentMode::Lexical, true); } + Optional super_class; if (m_super_class) - FIXME_NEWBC TRY(m_super_class->generate_bytecode(generator)); + super_class = TRY(m_super_class->generate_bytecode(generator)).value(); - generator.emit(*this, lhs_name); + auto dst = choose_dst(generator, preferred_dst); + generator.emit(dst, super_class, *this, lhs_name); - return Optional {}; + return dst; } -Bytecode::CodeGenerationErrorOr> ClassExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ClassExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - return generate_bytecode_with_lhs_name(generator, {}); + return generate_bytecode_with_lhs_name(generator, {}, preferred_dst); } Bytecode::CodeGenerationErrorOr> SpreadExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const @@ -2577,77 +2681,93 @@ Bytecode::CodeGenerationErrorOr> SpreadExpression::g return m_target->generate_bytecode(generator); } -Bytecode::CodeGenerationErrorOr> ThisExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ThisExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - generator.emit(); - return Optional {}; + auto dst = choose_dst(generator, preferred_dst); + generator.emit(dst); + return dst; } -static void generate_await(Bytecode::Generator& generator, Bytecode::Register received_completion_register, Bytecode::Register received_completion_type_register, Bytecode::Register received_completion_value_register, Bytecode::IdentifierTableIndex type_identifier, Bytecode::IdentifierTableIndex value_identifier) +static Bytecode::Operand generate_await( + Bytecode::Generator& generator, + Bytecode::Operand argument, + Bytecode::Operand received_completion, + Bytecode::Operand received_completion_type, + Bytecode::Operand received_completion_value, + Bytecode::IdentifierTableIndex type_identifier, + Bytecode::IdentifierTableIndex value_identifier) { VERIFY(generator.is_in_async_function()); auto& continuation_block = generator.make_block(); - generator.emit(Bytecode::Label { continuation_block }); + generator.emit(Bytecode::Label { continuation_block }, argument); generator.switch_to_basic_block(continuation_block); - get_received_completion_type_and_value(generator, received_completion_register, received_completion_type_register, received_completion_value_register, type_identifier, value_identifier); + // FIXME: It's really magical that we can just assume that the completion value is in register 0. + // It ends up there because we "return" from the Await instruction above via the synthetic + // generator function that actually drives async execution. + generator.emit(received_completion, Bytecode::Operand(Bytecode::Register(0))); + get_received_completion_type_and_value(generator, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier); auto& normal_completion_continuation_block = generator.make_block(); auto& throw_value_block = generator.make_block(); - generator.emit(Value(to_underlying(Completion::Type::Normal))); - generator.emit(received_completion_type_register); - generator.emit( + auto received_completion_type_is_normal = Bytecode::Operand(generator.allocate_register()); + generator.emit( + received_completion_type_is_normal, + received_completion_type, + generator.add_constant(Value(to_underlying(Completion::Type::Normal)))); + generator.emit( + received_completion_type_is_normal, Bytecode::Label { normal_completion_continuation_block }, Bytecode::Label { throw_value_block }); // Simplification: The only abrupt completion we receive from AsyncFunctionDriverWrapper or AsyncGenerator is Type::Throw // So we do not need to account for the Type::Return path generator.switch_to_basic_block(throw_value_block); - generator.emit(received_completion_value_register); generator.perform_needed_unwinds(); - generator.emit(); + generator.emit(received_completion_value); generator.switch_to_basic_block(normal_completion_continuation_block); - generator.emit(received_completion_value_register); + return received_completion_value; } Bytecode::CodeGenerationErrorOr> AwaitExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - FIXME_NEWBC TRY(m_argument->generate_bytecode(generator)); + auto argument = TRY(m_argument->generate_bytecode(generator)).value(); - auto received_completion_register = generator.allocate_register(); - auto received_completion_type_register = generator.allocate_register(); - auto received_completion_value_register = generator.allocate_register(); + auto received_completion = Bytecode::Operand(generator.allocate_register()); + auto received_completion_type = Bytecode::Operand(generator.allocate_register()); + auto received_completion_value = Bytecode::Operand(generator.allocate_register()); + + generator.emit(received_completion, Bytecode::Operand(Bytecode::Register(0))); auto type_identifier = generator.intern_identifier("type"); auto value_identifier = generator.intern_identifier("value"); - generate_await(generator, received_completion_register, received_completion_type_register, received_completion_value_register, type_identifier, value_identifier); - return Optional {}; + return generate_await(generator, argument, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier); } Bytecode::CodeGenerationErrorOr> WithStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - FIXME_NEWBC TRY(m_object->generate_bytecode(generator)); - generator.emit(); + auto object = TRY(m_object->generate_bytecode(generator)).value(); + generator.emit(object); // EnterObjectEnvironment sets the running execution context's lexical_environment to a new Object Environment. generator.start_boundary(Bytecode::Generator::BlockBoundaryType::LeaveLexicalEnvironment); - generator.emit(js_undefined()); - - FIXME_NEWBC TRY(m_body->generate_bytecode(generator)); + auto body_result = TRY(m_body->generate_bytecode(generator)); + if (!body_result.has_value()) + body_result = generator.add_constant(js_undefined()); generator.end_boundary(Bytecode::Generator::BlockBoundaryType::LeaveLexicalEnvironment); if (!generator.is_current_block_terminated()) generator.emit(); - return Optional {}; + return body_result; } enum class LHSKind { @@ -2666,6 +2786,7 @@ enum class IterationKind { struct ForInOfHeadEvaluationResult { bool is_destructuring { false }; LHSKind lhs_kind { LHSKind::Assignment }; + Optional iterator; }; static Bytecode::CodeGenerationErrorOr for_in_of_head_evaluation(Bytecode::Generator& generator, IterationKind iteration_kind, Variant, NonnullRefPtr> const& lhs, NonnullRefPtr const& rhs) { @@ -2690,8 +2811,8 @@ static Bytecode::CodeGenerationErrorOr for_in_of_he VERIFY(variable->target().has>()); auto identifier = variable->target().get>(); auto identifier_table_ref = generator.intern_identifier(identifier->string()); - TRY(generator.emit_named_evaluation_if_anonymous_function(*variable->init(), identifier_table_ref)); - generator.emit_set_variable(*identifier); + auto value = TRY(generator.emit_named_evaluation_if_anonymous_function(*variable->init(), identifier_table_ref)).value(); + generator.emit_set_variable(*identifier, value); } } else { auto has_non_local_variables = false; @@ -2729,7 +2850,7 @@ static Bytecode::CodeGenerationErrorOr for_in_of_he } // 3. Let exprRef be the result of evaluating expr. - FIXME_NEWBC TRY(rhs->generate_bytecode(generator)); + auto object = TRY(rhs->generate_bytecode(generator)).value(); // 4. Set the running execution context's LexicalEnvironment to oldEnv. if (entered_lexical_scope) @@ -2738,12 +2859,15 @@ static Bytecode::CodeGenerationErrorOr for_in_of_he // 5. Let exprValue be ? GetValue(exprRef). // NOTE: No need to store this anywhere. + auto iterator = Bytecode::Operand(generator.allocate_register()); + // 6. If iterationKind is enumerate, then if (iteration_kind == IterationKind::Enumerate) { // a. If exprValue is undefined or null, then auto& nullish_block = generator.make_block(); auto& continuation_block = generator.make_block(); generator.emit( + object, Bytecode::Label { nullish_block }, Bytecode::Label { continuation_block }); @@ -2757,7 +2881,7 @@ static Bytecode::CodeGenerationErrorOr for_in_of_he // c. Let iterator be EnumerateObjectProperties(obj). // d. Let nextMethod be ! GetV(iterator, "next"). // e. Return the Iterator Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }. - generator.emit(); + generator.emit(iterator, object); } // 7. Else, else { @@ -2767,25 +2891,24 @@ static Bytecode::CodeGenerationErrorOr for_in_of_he auto iterator_kind = iteration_kind == IterationKind::AsyncIterate ? IteratorHint::Async : IteratorHint::Sync; // d. Return ? GetIterator(exprValue, iteratorKind). - generator.emit(iterator_kind); + generator.emit(iterator, object, iterator_kind); } + result.iterator = iterator; return result; } // 14.7.5.7 ForIn/OfBodyEvaluation ( lhs, stmt, iteratorRecord, iterationKind, lhsKind, labelSet [ , iteratorKind ] ), https://tc39.es/ecma262/#sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset static Bytecode::CodeGenerationErrorOr> for_in_of_body_evaluation(Bytecode::Generator& generator, ASTNode const& node, Variant, NonnullRefPtr> const& lhs, ASTNode const& body, ForInOfHeadEvaluationResult const& head_result, Vector const& label_set, Bytecode::BasicBlock& loop_end, Bytecode::BasicBlock& loop_update, IteratorHint iterator_kind = IteratorHint::Sync, [[maybe_unused]] Optional preferred_dst = {}) { - auto iterator_register = generator.allocate_register(); - generator.emit(iterator_register); - // 1. If iteratorKind is not present, set iteratorKind to sync. // 2. Let oldEnv be the running execution context's LexicalEnvironment. bool has_lexical_binding = false; // 3. Let V be undefined. - // NOTE: We don't need 'V' as the resulting value will naturally flow through via the accumulator register. + auto completion_value = Bytecode::Operand(generator.allocate_register()); + generator.emit(completion_value, generator.add_constant(js_undefined())); // 4. Let destructuring be IsDestructuring of lhs. auto destructuring = head_result.is_destructuring; @@ -2806,39 +2929,41 @@ static Bytecode::CodeGenerationErrorOr> for_in_of_bo generator.begin_continuable_scope(Bytecode::Label { loop_update }, label_set); // a. Let nextResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]). - generator.emit(iterator_register); - generator.emit(); + auto next_result = Bytecode::Operand(generator.allocate_register()); + generator.emit(next_result, *head_result.iterator); // b. If iteratorKind is async, set nextResult to ? Await(nextResult). if (iterator_kind == IteratorHint::Async) { - auto received_completion_register = generator.allocate_register(); - auto received_completion_type_register = generator.allocate_register(); - auto received_completion_value_register = generator.allocate_register(); + auto received_completion = Bytecode::Operand(generator.allocate_register()); + auto received_completion_type = Bytecode::Operand(generator.allocate_register()); + auto received_completion_value = Bytecode::Operand(generator.allocate_register()); auto type_identifier = generator.intern_identifier("type"); auto value_identifier = generator.intern_identifier("value"); - generate_await(generator, received_completion_register, received_completion_type_register, received_completion_value_register, type_identifier, value_identifier); + generator.emit(received_completion, Bytecode::Operand(Bytecode::Register(0))); + auto new_result = generate_await(generator, next_result, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier); + generator.emit(next_result, new_result); } // c. If Type(nextResult) is not Object, throw a TypeError exception. - generator.emit(); + generator.emit(next_result); // d. Let done be ? IteratorComplete(nextResult). - auto iterator_result_register = generator.allocate_register(); - generator.emit(iterator_result_register); - generator.emit_iterator_complete(); + auto done = Bytecode::Operand(generator.allocate_register()); + generator.emit_iterator_complete(done, next_result); // e. If done is true, return V. auto& loop_continue = generator.make_block(); - generator.emit( + generator.emit( + done, Bytecode::Label { loop_end }, Bytecode::Label { loop_continue }); generator.switch_to_basic_block(loop_continue); // f. Let nextValue be ? IteratorValue(nextResult). - generator.emit(iterator_result_register); - generator.emit_iterator_value(); + auto next_value = Bytecode::Operand(generator.allocate_register()); + generator.emit_iterator_value(next_value, next_result); // g. If lhsKind is either assignment or varBinding, then if (head_result.lhs_kind != LHSKind::LexicalBinding) { @@ -2849,15 +2974,13 @@ static Bytecode::CodeGenerationErrorOr> for_in_of_bo if (head_result.lhs_kind == LHSKind::VarBinding) { auto& declaration = static_cast(*lhs.get>()); VERIFY(declaration.declarations().size() == 1); - FIXME_NEWBC TRY(assign_accumulator_to_variable_declarator(generator, declaration.declarations().first(), declaration)); + TRY(assign_value_to_variable_declarator(generator, declaration.declarations().first(), declaration, next_value)); } else { if (auto ptr = lhs.get_pointer>()) { - FIXME_NEWBC TRY(generator.emit_store_to_reference(**ptr)); + TRY(generator.emit_store_to_reference(**ptr, next_value)); } else { auto& binding_pattern = lhs.get>(); - auto value_register = generator.allocate_register(); - generator.emit(value_register); - FIXME_NEWBC TRY(generate_binding_pattern_bytecode(generator, *binding_pattern, Bytecode::Op::SetVariable::InitializationMode::Set, value_register, false)); + TRY(generate_binding_pattern_bytecode(generator, *binding_pattern, Bytecode::Op::SetVariable::InitializationMode::Set, next_value, false)); } } } @@ -2912,10 +3035,8 @@ static Bytecode::CodeGenerationErrorOr> for_in_of_bo auto lhs_name = variable_declaration.declarations().first()->target().get>(); // 3. Let lhsRef be ! ResolveBinding(lhsName). // NOTE: We're skipping all the completion stuff that the spec does, as the unwinding mechanism will take case of doing that. - if (lhs_name->is_local()) - generator.emit(lhs_name->local_variable_index()); - else - generator.emit_set_variable(*lhs_name, Bytecode::Op::SetVariable::InitializationMode::Initialize, Bytecode::Op::EnvironmentMode::Lexical); + + generator.emit_set_variable(*lhs_name, next_value, Bytecode::Op::SetVariable::InitializationMode::Initialize, Bytecode::Op::EnvironmentMode::Lexical); } } // i. If destructuring is false, then @@ -2944,10 +3065,12 @@ static Bytecode::CodeGenerationErrorOr> for_in_of_bo auto& declaration = static_cast(*lhs.get>()); VERIFY(declaration.declarations().size() == 1); auto& binding_pattern = declaration.declarations().first()->target().get>(); - - auto value_register = generator.allocate_register(); - generator.emit(value_register); - FIXME_NEWBC TRY(generate_binding_pattern_bytecode(generator, *binding_pattern, head_result.lhs_kind == LHSKind::VarBinding ? Bytecode::Op::SetVariable::InitializationMode::Set : Bytecode::Op::SetVariable::InitializationMode::Initialize, value_register, false)); + (void)TRY(generate_binding_pattern_bytecode( + generator, + *binding_pattern, + head_result.lhs_kind == LHSKind::VarBinding ? Bytecode::Op::SetVariable::InitializationMode::Set : Bytecode::Op::SetVariable::InitializationMode::Initialize, + next_value, + false)); } else { return Bytecode::CodeGenerationError { &node, @@ -2966,14 +3089,8 @@ static Bytecode::CodeGenerationErrorOr> for_in_of_bo // 1. Assert: iterationKind is iterate. // 2. Return ? IteratorClose(iteratorRecord, status). - generator.emit(js_undefined()); - // l. Let result be the result of evaluating stmt. - FIXME_NEWBC TRY(body.generate_bytecode(generator)); - - auto result_register = generator.allocate_register(); - if (!generator.is_current_block_terminated()) - generator.emit(result_register); + auto result = TRY(body.generate_bytecode(generator)); // m. Set the running execution context's LexicalEnvironment to oldEnv. if (has_lexical_binding) @@ -2993,12 +3110,15 @@ static Bytecode::CodeGenerationErrorOr> for_in_of_bo // o. If result.[[Value]] is not empty, set V to result.[[Value]]. // The body can contain an unconditional block terminator (e.g. return, throw), so we have to check for that before generating the Jump. - if (!generator.is_current_block_terminated()) + if (!generator.is_current_block_terminated()) { + if (result.has_value()) + generator.emit(completion_value, *result); + generator.emit(Bytecode::Label { loop_update }); + } generator.switch_to_basic_block(loop_end); - generator.emit(result_register); - return Optional {}; + return completion_value; } Bytecode::CodeGenerationErrorOr> ForInStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const @@ -3015,8 +3135,6 @@ Bytecode::CodeGenerationErrorOr> ForInStatement::gen generator.begin_breakable_scope(Bytecode::Label { loop_end }, label_set); auto head_result = TRY(for_in_of_head_evaluation(generator, IterationKind::Enumerate, m_lhs, m_rhs)); - - // Now perform the rest of ForInOfLoopEvaluation, given that the accumulator holds the iterator we're supposed to iterate over. return for_in_of_body_evaluation(generator, *this, m_lhs, body(), head_result, label_set, loop_end, loop_update); } @@ -3033,8 +3151,6 @@ Bytecode::CodeGenerationErrorOr> ForOfStatement::gen generator.begin_breakable_scope(Bytecode::Label { loop_end }, label_set); auto head_result = TRY(for_in_of_head_evaluation(generator, IterationKind::Iterate, m_lhs, m_rhs)); - - // Now perform the rest of ForInOfLoopEvaluation, given that the accumulator holds the iterator we're supposed to iterate over. return for_in_of_body_evaluation(generator, *this, m_lhs, body(), head_result, label_set, loop_end, loop_update); } @@ -3051,53 +3167,57 @@ Bytecode::CodeGenerationErrorOr> ForAwaitOfStatement generator.begin_breakable_scope(Bytecode::Label { loop_end }, label_set); auto head_result = TRY(for_in_of_head_evaluation(generator, IterationKind::AsyncIterate, m_lhs, m_rhs)); - - // Now perform the rest of ForInOfLoopEvaluation, given that the accumulator holds the iterator we're supposed to iterate over. return for_in_of_body_evaluation(generator, *this, m_lhs, m_body, head_result, label_set, loop_end, loop_update, IteratorHint::Async); } // 13.3.12.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-meta-properties-runtime-semantics-evaluation -Bytecode::CodeGenerationErrorOr> MetaProperty::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> MetaProperty::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); // NewTarget : new . target if (m_type == MetaProperty::Type::NewTarget) { // 1. Return GetNewTarget(). - generator.emit(); - return Optional {}; + auto dst = choose_dst(generator, preferred_dst); + generator.emit(dst); + return dst; } // ImportMeta : import . meta if (m_type == MetaProperty::Type::ImportMeta) { - generator.emit(); - return Optional {}; + auto dst = choose_dst(generator, preferred_dst); + generator.emit(dst); + return dst; } VERIFY_NOT_REACHED(); } -Bytecode::CodeGenerationErrorOr> ClassFieldInitializerStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ClassFieldInitializerStatement::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - TRY(generator.emit_named_evaluation_if_anonymous_function(*m_expression, generator.intern_identifier(m_class_field_identifier_name))); + auto value = TRY(generator.emit_named_evaluation_if_anonymous_function(*m_expression, generator.intern_identifier(m_class_field_identifier_name), preferred_dst)); generator.perform_needed_unwinds(); - generator.emit(); - return Optional {}; + generator.emit(value); + return value; } -static Bytecode::CodeGenerationErrorOr> generate_optional_chain(Bytecode::Generator& generator, OptionalChain const& optional_chain, Bytecode::Register current_value_register, Bytecode::Register current_base_register, [[maybe_unused]] Optional preferred_dst) +static Bytecode::CodeGenerationErrorOr generate_optional_chain(Bytecode::Generator& generator, OptionalChain const& optional_chain, Bytecode::Operand current_value, Bytecode::Operand current_base, [[maybe_unused]] Optional preferred_dst) { + Optional new_current_value; if (is(optional_chain.base())) { auto& member_expression = static_cast(optional_chain.base()); - FIXME_NEWBC TRY(get_base_and_value_from_member_expression(generator, member_expression, current_base_register)); + auto base_and_value = TRY(get_base_and_value_from_member_expression(generator, member_expression)); + new_current_value = base_and_value.value; + generator.emit(current_base, base_and_value.base); } else if (is(optional_chain.base())) { auto& sub_optional_chain = static_cast(optional_chain.base()); - FIXME_NEWBC TRY(generate_optional_chain(generator, sub_optional_chain, current_value_register, current_base_register)); + TRY(generate_optional_chain(generator, sub_optional_chain, current_value, current_base)); + new_current_value = current_value; } else { - FIXME_NEWBC TRY(optional_chain.base().generate_bytecode(generator)); + new_current_value = TRY(optional_chain.base().generate_bytecode(generator)).value(); } - generator.emit(current_value_register); + generator.emit(current_value, *new_current_value); auto& load_undefined_and_jump_to_end_block = generator.make_block(); auto& end_block = generator.make_block(); @@ -3107,82 +3227,72 @@ static Bytecode::CodeGenerationErrorOr> generate_opt if (is_optional) { auto& not_nullish_block = generator.make_block(); generator.emit( + current_value, Bytecode::Label { load_undefined_and_jump_to_end_block }, Bytecode::Label { not_nullish_block }); generator.switch_to_basic_block(not_nullish_block); } - FIXME_NEWBC TRY(reference.visit( - [&](OptionalChain::Call const& call) -> Bytecode::CodeGenerationErrorOr> { - FIXME_NEWBC TRY(arguments_to_array_for_call(generator, call.arguments)); - generator.emit(Bytecode::Op::CallType::Call, current_value_register, current_base_register); - - generator.emit(current_value_register); - - generator.emit(js_undefined()); - generator.emit(current_base_register); - - generator.emit(current_value_register); - return Optional {}; + TRY(reference.visit( + [&](OptionalChain::Call const& call) -> Bytecode::CodeGenerationErrorOr { + auto arguments = TRY(arguments_to_array_for_call(generator, call.arguments)).value(); + generator.emit(Bytecode::Op::CallType::Call, current_value, current_value, current_base, arguments); + generator.emit(current_base, generator.add_constant(js_undefined())); + return {}; }, - [&](OptionalChain::ComputedReference const& ref) -> Bytecode::CodeGenerationErrorOr> { - generator.emit(current_base_register); - FIXME_NEWBC TRY(ref.expression->generate_bytecode(generator)); - generator.emit(current_base_register); - generator.emit(current_value_register); - return Optional {}; + [&](OptionalChain::ComputedReference const& ref) -> Bytecode::CodeGenerationErrorOr { + generator.emit(current_base, current_value); + auto property = TRY(ref.expression->generate_bytecode(generator)).value(); + generator.emit(current_value, current_value, property); + return {}; }, - [&](OptionalChain::MemberReference const& ref) -> Bytecode::CodeGenerationErrorOr> { - generator.emit(current_base_register); - generator.emit_get_by_id(generator.intern_identifier(ref.identifier->string())); - generator.emit(current_value_register); - return Optional {}; + [&](OptionalChain::MemberReference const& ref) -> Bytecode::CodeGenerationErrorOr { + generator.emit(current_base, current_value); + generator.emit_get_by_id(current_value, current_value, generator.intern_identifier(ref.identifier->string())); + return {}; }, - [&](OptionalChain::PrivateMemberReference const& ref) -> Bytecode::CodeGenerationErrorOr> { - generator.emit(current_base_register); - generator.emit(generator.intern_identifier(ref.private_identifier->string())); - generator.emit(current_value_register); - return Optional {}; + [&](OptionalChain::PrivateMemberReference const& ref) -> Bytecode::CodeGenerationErrorOr { + generator.emit(current_base, current_value); + generator.emit(current_value, current_value, generator.intern_identifier(ref.private_identifier->string())); + + return {}; })); } generator.emit(Bytecode::Label { end_block }); generator.switch_to_basic_block(load_undefined_and_jump_to_end_block); - generator.emit(js_undefined()); + generator.emit(current_value, generator.add_constant(js_undefined())); generator.emit(Bytecode::Label { end_block }); generator.switch_to_basic_block(end_block); - return Optional {}; + return {}; } Bytecode::CodeGenerationErrorOr> OptionalChain::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - auto current_base_register = generator.allocate_register(); - auto current_value_register = generator.allocate_register(); - generator.emit(js_undefined()); - generator.emit(current_base_register); - return generate_optional_chain(generator, *this, current_value_register, current_base_register); + auto current_base = Bytecode::Operand(generator.allocate_register()); + auto current_value = choose_dst(generator, preferred_dst); + generator.emit(current_base, generator.add_constant(js_undefined())); + TRY(generate_optional_chain(generator, *this, current_value, current_base)); + return current_value; } -Bytecode::CodeGenerationErrorOr> ImportCall::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ImportCall::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - FIXME_NEWBC TRY(m_specifier->generate_bytecode(generator)); - auto specifier_reg = generator.allocate_register(); - generator.emit(specifier_reg); + auto specifier = TRY(m_specifier->generate_bytecode(generator)).value(); + Optional options; if (m_options) { - FIXME_NEWBC TRY(m_options->generate_bytecode(generator)); + options = TRY(m_options->generate_bytecode(generator)).value(); } else { - generator.emit(js_undefined()); + options = generator.add_constant(js_undefined()); } - auto options_reg = generator.allocate_register(); - generator.emit(options_reg); - - generator.emit(specifier_reg, options_reg); - return Optional {}; + auto dst = choose_dst(generator, preferred_dst); + generator.emit(dst, specifier, *options); + return dst; } Bytecode::CodeGenerationErrorOr> ExportStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const @@ -3202,19 +3312,28 @@ Bytecode::CodeGenerationErrorOr> ExportStatement::ge } if (is(*m_statement)) { - TRY(generator.emit_named_evaluation_if_anonymous_function(static_cast(*m_statement), generator.intern_identifier("default"sv))); + auto value = TRY(generator.emit_named_evaluation_if_anonymous_function(static_cast(*m_statement), generator.intern_identifier("default"sv))).value(); - if (!static_cast(*m_statement).has_name()) - generator.emit(generator.intern_identifier(ExportStatement::local_name_for_default), generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize); + if (!static_cast(*m_statement).has_name()) { + generator.emit( + generator.intern_identifier(ExportStatement::local_name_for_default), + value, + generator.next_environment_variable_cache(), + Bytecode::Op::SetVariable::InitializationMode::Initialize); + } - return Optional {}; + return value; } // ExportDeclaration : export default AssignmentExpression ; VERIFY(is(*m_statement)); - TRY(generator.emit_named_evaluation_if_anonymous_function(static_cast(*m_statement), generator.intern_identifier("default"sv))); - generator.emit(generator.intern_identifier(ExportStatement::local_name_for_default), generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize); - return Optional {}; + auto value = TRY(generator.emit_named_evaluation_if_anonymous_function(static_cast(*m_statement), generator.intern_identifier("default"sv))).value(); + generator.emit( + generator.intern_identifier(ExportStatement::local_name_for_default), + value, + generator.next_environment_variable_cache(), + Bytecode::Op::SetVariable::InitializationMode::Initialize); + return value; } Bytecode::CodeGenerationErrorOr> ImportStatement::generate_bytecode(Bytecode::Generator&, [[maybe_unused]] Optional preferred_dst) const diff --git a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h index dc72e4d99a..af5210b4a7 100644 --- a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h +++ b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h @@ -367,7 +367,7 @@ inline ThrowCompletionOr set_variable( return {}; } -inline Value new_function(VM& vm, FunctionExpression const& function_node, Optional const& lhs_name, Optional const& home_object) +inline Value new_function(VM& vm, FunctionExpression const& function_node, Optional const& lhs_name, Optional const& home_object) { Value value; @@ -381,7 +381,7 @@ inline Value new_function(VM& vm, FunctionExpression const& function_node, Optio } if (home_object.has_value()) { - auto home_object_value = vm.bytecode_interpreter().reg(home_object.value()); + auto home_object_value = vm.bytecode_interpreter().get(home_object.value()); static_cast(value.as_function()).set_home_object(&home_object_value.as_object()); } diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.cpp b/Userland/Libraries/LibJS/Bytecode/Generator.cpp index 15ca38434c..813a716a43 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Generator.cpp @@ -1,11 +1,9 @@ /* - * Copyright (c) 2021, Andreas Kling + * Copyright (c) 2021-2024, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ -#define FIXME_NEWBC (void) - #include #include #include @@ -24,29 +22,42 @@ Generator::Generator() { } -CodeGenerationErrorOr> Generator::generate(VM& vm, ASTNode const& node, FunctionKind enclosing_function_kind) +CodeGenerationErrorOr> Generator::generate(VM& vm, ASTNode const& node, ReadonlySpan parameters, FunctionKind enclosing_function_kind) { Generator generator; + + for (auto const& parameter : parameters) { + if (auto const* identifier = parameter.binding.get_pointer>(); + identifier && (*identifier)->is_local()) { + generator.set_local_initialized((*identifier)->local_variable_index()); + } + } + generator.switch_to_basic_block(generator.make_block()); SourceLocationScope scope(generator, node); generator.m_enclosing_function_kind = enclosing_function_kind; if (generator.is_in_generator_or_async_function()) { // Immediately yield with no value. auto& start_block = generator.make_block(); - generator.emit(Label { start_block }); + generator.emit(Label { start_block }, generator.add_constant(js_undefined())); generator.switch_to_basic_block(start_block); // NOTE: This doesn't have to handle received throw/return completions, as GeneratorObject::resume_abrupt // will not enter the generator from the SuspendedStart state and immediately completes the generator. } - FIXME_NEWBC TRY(node.generate_bytecode(generator)); + + auto last_value = TRY(node.generate_bytecode(generator)); + + if (!generator.current_block().is_terminated() && last_value.has_value()) { + generator.emit(last_value.value()); + } + if (generator.is_in_generator_or_async_function()) { // Terminate all unterminated blocks with yield return for (auto& block : generator.m_root_basic_blocks) { if (block->is_terminated()) continue; generator.switch_to_basic_block(*block); - generator.emit(js_undefined()); - generator.emit(nullptr); + generator.emit(nullptr, generator.add_constant(js_undefined())); } } @@ -169,26 +180,23 @@ void Generator::end_breakable_scope() end_boundary(BlockBoundaryType::Break); } -CodeGenerationErrorOr Generator::emit_super_reference(MemberExpression const& expression) +CodeGenerationErrorOr Generator::emit_super_reference(MemberExpression const& expression) { VERIFY(is(expression.object())); // https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation // 1. Let env be GetThisEnvironment(). // 2. Let actualThis be ? env.GetThisBinding(). - auto actual_this_register = allocate_register(); - emit(); - emit(actual_this_register); + auto actual_this = Operand(allocate_register()); + emit(actual_this); - Optional computed_property_value_register; + Optional computed_property_value; if (expression.is_computed()) { // SuperProperty : super [ Expression ] // 3. Let propertyNameReference be ? Evaluation of Expression. // 4. Let propertyNameValue be ? GetValue(propertyNameReference). - FIXME_NEWBC TRY(expression.property().generate_bytecode(*this)); - computed_property_value_register = allocate_register(); - emit(*computed_property_value_register); + computed_property_value = TRY(expression.property().generate_bytecode(*this)).value(); } // 5/7. Return ? MakeSuperPropertyReference(actualThis, propertyKey, strict). @@ -197,135 +205,128 @@ CodeGenerationErrorOr Generator::emit_super_refer // 1. Let env be GetThisEnvironment(). // 2. Assert: env.HasSuperBinding() is true. // 3. Let baseValue be ? env.GetSuperBase(). - auto super_base_register = allocate_register(); - emit(); - emit(super_base_register); + auto base_value = Operand(allocate_register()); + emit(base_value); // 4. Return the Reference Record { [[Base]]: baseValue, [[ReferencedName]]: propertyKey, [[Strict]]: strict, [[ThisValue]]: actualThis }. - return ReferenceRegisters { - .base = super_base_register, - .referenced_name = move(computed_property_value_register), - .this_value = actual_this_register, + return ReferenceOperands { + .base = base_value, + .referenced_name = computed_property_value, + .this_value = actual_this, }; } -CodeGenerationErrorOr> Generator::emit_load_from_reference(JS::ASTNode const& node, CollectRegisters collect_registers) +CodeGenerationErrorOr Generator::emit_load_from_reference(JS::ASTNode const& node, Optional preferred_dst) { if (is(node)) { auto& identifier = static_cast(node); - FIXME_NEWBC TRY(identifier.generate_bytecode(*this)); - return Optional {}; + auto loaded_value = TRY(identifier.generate_bytecode(*this, preferred_dst)).value(); + return ReferenceOperands { + .loaded_value = loaded_value, + }; } - if (is(node)) { - auto& expression = static_cast(node); + if (!is(node)) { + return CodeGenerationError { + &node, + "Unimplemented/invalid node used as a reference"sv + }; + } + auto& expression = static_cast(node); - // https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation - if (is(expression.object())) { - auto super_reference = TRY(emit_super_reference(expression)); + // https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation + if (is(expression.object())) { + auto super_reference = TRY(emit_super_reference(expression)); + auto dst = preferred_dst.has_value() ? preferred_dst.value() : Operand(allocate_register()); - if (super_reference.referenced_name.has_value()) { - // 5. Let propertyKey be ? ToPropertyKey(propertyNameValue). - // FIXME: This does ToPropertyKey out of order, which is observable by Symbol.toPrimitive! - emit(*super_reference.referenced_name); - emit(super_reference.base, super_reference.this_value); - } else { - // 3. Let propertyKey be StringValue of IdentifierName. - auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); - emit_get_by_id_with_this(identifier_table_ref, super_reference.this_value); - } - - return super_reference; + if (super_reference.referenced_name.has_value()) { + // 5. Let propertyKey be ? ToPropertyKey(propertyNameValue). + // FIXME: This does ToPropertyKey out of order, which is observable by Symbol.toPrimitive! + emit(dst, *super_reference.base, *super_reference.referenced_name, *super_reference.this_value); } else { - FIXME_NEWBC TRY(expression.object().generate_bytecode(*this)); - if (expression.is_computed()) { - auto object_reg = allocate_register(); - emit(object_reg); - - FIXME_NEWBC TRY(expression.property().generate_bytecode(*this)); - Optional property_reg {}; - if (collect_registers == CollectRegisters::Yes) { - property_reg = allocate_register(); - emit(property_reg.value()); - } - - emit(object_reg); - if (collect_registers == CollectRegisters::Yes) - return ReferenceRegisters { - .base = object_reg, - .referenced_name = property_reg.value(), - .this_value = object_reg, - }; - return Optional {}; - } else if (expression.property().is_identifier()) { - auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); - emit_get_by_id(identifier_table_ref); - } else if (expression.property().is_private_identifier()) { - auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); - emit(identifier_table_ref); - } else { - return CodeGenerationError { - &expression, - "Unimplemented non-computed member expression"sv - }; - } + // 3. Let propertyKey be StringValue of IdentifierName. + auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); + emit_get_by_id_with_this(dst, *super_reference.base, identifier_table_ref, *super_reference.this_value); } - return Optional {}; + + super_reference.loaded_value = dst; + return super_reference; + } + auto base = TRY(expression.object().generate_bytecode(*this)).value(); + if (expression.is_computed()) { + auto property = TRY(expression.property().generate_bytecode(*this)).value(); + auto dst = preferred_dst.has_value() ? preferred_dst.value() : Operand(allocate_register()); + emit(dst, base, property); + return ReferenceOperands { + .base = base, + .referenced_name = property, + .this_value = base, + .loaded_value = dst, + }; + } + if (expression.property().is_identifier()) { + auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); + auto dst = preferred_dst.has_value() ? preferred_dst.value() : Operand(allocate_register()); + emit_get_by_id(dst, base, identifier_table_ref); + return ReferenceOperands { + .base = base, + .referenced_identifier = identifier_table_ref, + .this_value = base, + .loaded_value = dst, + }; + } + if (expression.property().is_private_identifier()) { + auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); + auto dst = preferred_dst.has_value() ? preferred_dst.value() : Operand(allocate_register()); + emit(dst, base, identifier_table_ref); + return ReferenceOperands { + .base = base, + .referenced_private_identifier = identifier_table_ref, + .this_value = base, + .loaded_value = dst, + }; } return CodeGenerationError { - &node, - "Unimplemented/invalid node used a reference"sv + &expression, + "Unimplemented non-computed member expression"sv }; } -CodeGenerationErrorOr> Generator::emit_store_to_reference(JS::ASTNode const& node) +CodeGenerationErrorOr Generator::emit_store_to_reference(JS::ASTNode const& node, Operand value) { if (is(node)) { auto& identifier = static_cast(node); - emit_set_variable(identifier); - return Optional {}; + emit_set_variable(identifier, value); + return {}; } if (is(node)) { - // NOTE: The value is in the accumulator, so we have to store that away first. - auto value_reg = allocate_register(); - emit(value_reg); - auto& expression = static_cast(node); // https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation if (is(expression.object())) { auto super_reference = TRY(emit_super_reference(expression)); - emit(value_reg); // 4. Return the Reference Record { [[Base]]: baseValue, [[ReferencedName]]: propertyKey, [[Strict]]: strict, [[ThisValue]]: actualThis }. if (super_reference.referenced_name.has_value()) { // 5. Let propertyKey be ? ToPropertyKey(propertyNameValue). // FIXME: This does ToPropertyKey out of order, which is observable by Symbol.toPrimitive! - emit(super_reference.base, *super_reference.referenced_name, super_reference.this_value); + emit(*super_reference.base, *super_reference.referenced_name, *super_reference.this_value, value); } else { // 3. Let propertyKey be StringValue of IdentifierName. auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); - emit(super_reference.base, super_reference.this_value, identifier_table_ref, Bytecode::Op::PropertyKind::KeyValue, next_property_lookup_cache()); + emit(*super_reference.base, *super_reference.this_value, identifier_table_ref, value, Bytecode::Op::PropertyKind::KeyValue, next_property_lookup_cache()); } } else { - FIXME_NEWBC TRY(expression.object().generate_bytecode(*this)); - - auto object_reg = allocate_register(); - emit(object_reg); + auto object = TRY(expression.object().generate_bytecode(*this)).value(); if (expression.is_computed()) { - FIXME_NEWBC TRY(expression.property().generate_bytecode(*this)); - auto property_reg = allocate_register(); - emit(property_reg); - emit(value_reg); - emit(object_reg, property_reg); + auto property = TRY(expression.property().generate_bytecode(*this)).value(); + emit(object, property, value); } else if (expression.property().is_identifier()) { - emit(value_reg); auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); - emit(object_reg, identifier_table_ref, Bytecode::Op::PropertyKind::KeyValue, next_property_lookup_cache()); + emit(object, identifier_table_ref, value, Bytecode::Op::PropertyKind::KeyValue, next_property_lookup_cache()); } else if (expression.property().is_private_identifier()) { - emit(value_reg); auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); - emit(object_reg, identifier_table_ref); + emit(object, identifier_table_ref, value); } else { return CodeGenerationError { &expression, @@ -334,7 +335,7 @@ CodeGenerationErrorOr> Generator::emit_store_to_reference(JS:: } } - return Optional {}; + return {}; } return CodeGenerationError { @@ -343,24 +344,36 @@ CodeGenerationErrorOr> Generator::emit_store_to_reference(JS:: }; } -CodeGenerationErrorOr> Generator::emit_store_to_reference(ReferenceRegisters const& reference_registers) +CodeGenerationErrorOr Generator::emit_store_to_reference(ReferenceOperands const& reference, Operand value) { - if (reference_registers.base == reference_registers.this_value) - emit(reference_registers.base, reference_registers.referenced_name.value()); + if (reference.referenced_private_identifier.has_value()) { + emit(*reference.base, *reference.referenced_private_identifier, value); + return {}; + } + if (reference.referenced_identifier.has_value()) { + if (reference.base == reference.this_value) + emit(*reference.base, *reference.referenced_identifier, value, Bytecode::Op::PropertyKind::KeyValue, next_property_lookup_cache()); + else + emit(*reference.base, *reference.this_value, *reference.referenced_identifier, value, Bytecode::Op::PropertyKind::KeyValue, next_property_lookup_cache()); + return {}; + } + if (reference.base == reference.this_value) + emit(*reference.base, *reference.referenced_name, value); else - emit(reference_registers.base, reference_registers.referenced_name.value(), reference_registers.this_value); - return Optional {}; + emit(*reference.base, *reference.referenced_name, *reference.this_value, value); + return {}; } CodeGenerationErrorOr> Generator::emit_delete_reference(JS::ASTNode const& node) { if (is(node)) { auto& identifier = static_cast(node); - if (identifier.is_local()) - emit(Value(false)); - else - emit(intern_identifier(identifier.string())); - return Optional {}; + if (identifier.is_local()) { + return add_constant(Value(false)); + } + auto dst = Operand(allocate_register()); + emit(dst, intern_identifier(identifier.string())); + return dst; } if (is(node)) { @@ -370,27 +383,26 @@ CodeGenerationErrorOr> Generator::emit_delete_reference(JS::AS if (is(expression.object())) { auto super_reference = TRY(emit_super_reference(expression)); + auto dst = Operand(allocate_register()); if (super_reference.referenced_name.has_value()) { - emit(super_reference.this_value, *super_reference.referenced_name); + emit(dst, *super_reference.base, *super_reference.this_value, *super_reference.referenced_name); } else { auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); - emit(super_reference.this_value, identifier_table_ref); + emit(dst, *super_reference.base, *super_reference.this_value, identifier_table_ref); } return Optional {}; } - FIXME_NEWBC TRY(expression.object().generate_bytecode(*this)); + auto object = TRY(expression.object().generate_bytecode(*this)).value(); + auto dst = Operand(allocate_register()); if (expression.is_computed()) { - auto object_reg = allocate_register(); - emit(object_reg); - - FIXME_NEWBC TRY(expression.property().generate_bytecode(*this)); - emit(object_reg); + auto property = TRY(expression.property().generate_bytecode(*this)).value(); + emit(dst, object, property); } else if (expression.property().is_identifier()) { auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); - emit(identifier_table_ref); + emit(dst, object, identifier_table_ref); } else { // NOTE: Trying to delete a private field generates a SyntaxError in the parser. return CodeGenerationError { @@ -398,7 +410,7 @@ CodeGenerationErrorOr> Generator::emit_delete_reference(JS::AS "Unimplemented non-computed member expression"sv }; } - return Optional {}; + return dst; } // Though this will have no deletion effect, we still have to evaluate the node as it can have side effects. @@ -407,21 +419,23 @@ CodeGenerationErrorOr> Generator::emit_delete_reference(JS::AS // 13.5.1.2 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-delete-operator-runtime-semantics-evaluation // 1. Let ref be the result of evaluating UnaryExpression. // 2. ReturnIfAbrupt(ref). - FIXME_NEWBC TRY(node.generate_bytecode(*this)); + (void)TRY(node.generate_bytecode(*this)); // 3. If ref is not a Reference Record, return true. - emit(Value(true)); - // NOTE: The rest of the steps are handled by Delete{Variable,ByValue,Id}. - return Optional {}; + return add_constant(Value(true)); } -void Generator::emit_set_variable(JS::Identifier const& identifier, Bytecode::Op::SetVariable::InitializationMode initialization_mode, Bytecode::Op::EnvironmentMode mode) +void Generator::emit_set_variable(JS::Identifier const& identifier, Operand value, Bytecode::Op::SetVariable::InitializationMode initialization_mode, Bytecode::Op::EnvironmentMode mode) { if (identifier.is_local()) { - emit(identifier.local_variable_index()); + if (value.is_local() && value.index() == identifier.local_variable_index()) { + // Moving a local to itself is a no-op. + return; + } + emit(identifier.local_variable_index(), value); } else { - emit(intern_identifier(identifier.string()), next_environment_variable_cache(), initialization_mode, mode); + emit(intern_identifier(identifier.string()), value, next_environment_variable_cache(), initialization_mode, mode); } } @@ -540,9 +554,9 @@ void Generator::generate_continue(DeprecatedFlyString const& continue_label) generate_labelled_jump(JumpType::Continue, continue_label); } -void Generator::push_home_object(Register register_) +void Generator::push_home_object(Operand object) { - m_home_objects.append(register_); + m_home_objects.append(object); } void Generator::pop_home_object() @@ -550,54 +564,62 @@ void Generator::pop_home_object() m_home_objects.take_last(); } -void Generator::emit_new_function(FunctionExpression const& function_node, Optional lhs_name) +void Generator::emit_new_function(Operand dst, FunctionExpression const& function_node, Optional lhs_name) { - if (m_home_objects.is_empty()) - emit(function_node, lhs_name); - else - emit(function_node, lhs_name, m_home_objects.last()); + if (m_home_objects.is_empty()) { + emit(dst, function_node, lhs_name); + } else { + emit(dst, function_node, lhs_name, m_home_objects.last()); + } } -CodeGenerationErrorOr Generator::emit_named_evaluation_if_anonymous_function(Expression const& expression, Optional lhs_name) +CodeGenerationErrorOr> Generator::emit_named_evaluation_if_anonymous_function(Expression const& expression, Optional lhs_name, Optional preferred_dst) { if (is(expression)) { auto const& function_expression = static_cast(expression); if (!function_expression.has_name()) { - FIXME_NEWBC TRY(function_expression.generate_bytecode_with_lhs_name(*this, move(lhs_name))); - return {}; + return TRY(function_expression.generate_bytecode_with_lhs_name(*this, move(lhs_name), preferred_dst)).value(); } } if (is(expression)) { auto const& class_expression = static_cast(expression); if (!class_expression.has_name()) { - FIXME_NEWBC TRY(class_expression.generate_bytecode_with_lhs_name(*this, move(lhs_name))); - return {}; + return TRY(class_expression.generate_bytecode_with_lhs_name(*this, move(lhs_name), preferred_dst)).value(); } } - FIXME_NEWBC TRY(expression.generate_bytecode(*this)); - return {}; + return expression.generate_bytecode(*this, preferred_dst); } -void Generator::emit_get_by_id(IdentifierTableIndex id) +void Generator::emit_get_by_id(Operand dst, Operand base, IdentifierTableIndex id) { - emit(id, m_next_property_lookup_cache++); + emit(dst, base, id, m_next_property_lookup_cache++); } -void Generator::emit_get_by_id_with_this(IdentifierTableIndex id, Register this_reg) +void Generator::emit_get_by_id_with_this(Operand dst, Operand base, IdentifierTableIndex id, Operand this_value) { - emit(id, this_reg, m_next_property_lookup_cache++); + emit(dst, base, id, this_value, m_next_property_lookup_cache++); } -void Generator::emit_iterator_value() +void Generator::emit_iterator_value(Operand dst, Operand result) { - emit_get_by_id(intern_identifier("value"sv)); + emit_get_by_id(dst, result, intern_identifier("value"sv)); } -void Generator::emit_iterator_complete() +void Generator::emit_iterator_complete(Operand dst, Operand result) { - emit_get_by_id(intern_identifier("done"sv)); + emit_get_by_id(dst, result, intern_identifier("done"sv)); +} + +bool Generator::is_local_initialized(u32 local_index) const +{ + return m_initialized_locals.find(local_index) != m_initialized_locals.end(); +} + +void Generator::set_local_initialized(u32 local_index) +{ + m_initialized_locals.set(local_index); } } diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.h b/Userland/Libraries/LibJS/Bytecode/Generator.h index 235ae3d79f..5a2ae26f1a 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.h +++ b/Userland/Libraries/LibJS/Bytecode/Generator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Andreas Kling + * Copyright (c) 2021-2024, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ @@ -30,10 +30,13 @@ public: Function, Block, }; - static CodeGenerationErrorOr> generate(VM&, ASTNode const&, FunctionKind = FunctionKind::Normal); + static CodeGenerationErrorOr> generate(VM&, ASTNode const&, ReadonlySpan parameters, FunctionKind = FunctionKind::Normal); Register allocate_register(); + void set_local_initialized(u32 local_index); + [[nodiscard]] bool is_local_initialized(u32 local_index) const; + class SourceLocationScope { public: SourceLocationScope(Generator&, ASTNode const& node); @@ -77,11 +80,11 @@ public: } template - void emit_with_extra_register_slots(size_t extra_register_slots, Args&&... args) + void emit_with_extra_operand_slots(size_t extra_operand_slots, Args&&... args) { VERIFY(!is_current_block_terminated()); - size_t size_to_allocate = round_up_to_power_of_two(sizeof(OpType) + extra_register_slots * sizeof(Register), alignof(void*)); + size_t size_to_allocate = round_up_to_power_of_two(sizeof(OpType) + extra_operand_slots * sizeof(Operand), alignof(void*)); size_t slot_offset = m_current_basic_block->size(); grow(size_to_allocate); void* slot = m_current_basic_block->data() + slot_offset; @@ -92,30 +95,29 @@ public: op->set_source_record({ m_current_ast_node->start_offset(), m_current_ast_node->end_offset() }); } - struct ReferenceRegisters { - Register base; // [[Base]] - Optional referenced_name; // [[ReferencedName]] - Register this_value; // [[ThisValue]] + struct ReferenceOperands { + Optional base {}; // [[Base]] + Optional referenced_name {}; // [[ReferencedName]] as an operand + Optional referenced_identifier {}; // [[ReferencedName]] as an identifier + Optional referenced_private_identifier {}; // [[ReferencedName]] as a private identifier + Optional this_value {}; // [[ThisValue]] + Optional loaded_value {}; // Loaded value, if we've performed a load. }; - enum class CollectRegisters { - Yes, - No - }; - CodeGenerationErrorOr> emit_load_from_reference(JS::ASTNode const&, CollectRegisters); - CodeGenerationErrorOr> emit_store_to_reference(JS::ASTNode const&); - CodeGenerationErrorOr> emit_store_to_reference(ReferenceRegisters const&); + CodeGenerationErrorOr emit_load_from_reference(JS::ASTNode const&, Optional preferred_dst = {}); + CodeGenerationErrorOr emit_store_to_reference(JS::ASTNode const&, Operand value); + CodeGenerationErrorOr emit_store_to_reference(ReferenceOperands const&, Operand value); CodeGenerationErrorOr> emit_delete_reference(JS::ASTNode const&); - CodeGenerationErrorOr emit_super_reference(MemberExpression const&); + CodeGenerationErrorOr emit_super_reference(MemberExpression const&); - void emit_set_variable(JS::Identifier const& identifier, Bytecode::Op::SetVariable::InitializationMode initialization_mode = Bytecode::Op::SetVariable::InitializationMode::Set, Bytecode::Op::EnvironmentMode mode = Bytecode::Op::EnvironmentMode::Lexical); + void emit_set_variable(JS::Identifier const& identifier, Operand value, Bytecode::Op::SetVariable::InitializationMode initialization_mode = Bytecode::Op::SetVariable::InitializationMode::Set, Bytecode::Op::EnvironmentMode mode = Bytecode::Op::EnvironmentMode::Lexical); - void push_home_object(Register); + void push_home_object(Operand); void pop_home_object(); - void emit_new_function(JS::FunctionExpression const&, Optional lhs_name); + void emit_new_function(Operand dst, JS::FunctionExpression const&, Optional lhs_name); - CodeGenerationErrorOr emit_named_evaluation_if_anonymous_function(Expression const&, Optional lhs_name); + CodeGenerationErrorOr> emit_named_evaluation_if_anonymous_function(Expression const&, Optional lhs_name, Optional preferred_dst = {}); void begin_continuable_scope(Label continue_target, Vector const& language_label_set); void end_continuable_scope(); @@ -231,21 +233,28 @@ public: m_boundaries.take_last(); } - void emit_get_by_id(IdentifierTableIndex); - void emit_get_by_id_with_this(IdentifierTableIndex, Register); + void emit_get_by_id(Operand dst, Operand base, IdentifierTableIndex); - void emit_iterator_value(); - void emit_iterator_complete(); + void emit_get_by_id_with_this(Operand dst, Operand base, IdentifierTableIndex, Operand this_value); + + void emit_iterator_value(Operand dst, Operand result); + void emit_iterator_complete(Operand dst, Operand result); [[nodiscard]] size_t next_global_variable_cache() { return m_next_global_variable_cache++; } [[nodiscard]] size_t next_environment_variable_cache() { return m_next_environment_variable_cache++; } [[nodiscard]] size_t next_property_lookup_cache() { return m_next_property_lookup_cache++; } - [[nodiscard]] Operand add_constant(Value value) + enum class DeduplicateConstant { + Yes, + No, + }; + [[nodiscard]] Operand add_constant(Value value, DeduplicateConstant deduplicate_constant = DeduplicateConstant::Yes) { - for (size_t i = 0; i < m_constants.size(); ++i) { - if (m_constants[i] == value) - return Operand(Operand::Type::Constant, i); + if (deduplicate_constant == DeduplicateConstant::Yes) { + for (size_t i = 0; i < m_constants.size(); ++i) { + if (m_constants[i] == value) + return Operand(Operand::Type::Constant, i); + } } m_constants.append(value); return Operand(Operand::Type::Constant, m_constants.size() - 1); @@ -288,7 +297,9 @@ private: Vector m_continuable_scopes; Vector m_breakable_scopes; Vector m_boundaries; - Vector m_home_objects; + Vector m_home_objects; + + HashTable m_initialized_locals; }; } diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h index 54d4758983..158f68c041 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.h +++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h @@ -14,7 +14,7 @@ #define ENUMERATE_BYTECODE_OPS(O) \ O(Add) \ - O(Append) \ + O(ArrayAppend) \ O(AsyncIteratorClose) \ O(Await) \ O(BitwiseAnd) \ @@ -37,6 +37,8 @@ O(DeleteByValueWithThis) \ O(DeleteVariable) \ O(Div) \ + O(Dump) \ + O(End) \ O(EnterUnwindContext) \ O(EnterObjectEnvironment) \ O(Exp) \ @@ -55,7 +57,6 @@ O(GetPrivateById) \ O(GetVariable) \ O(GetGlobal) \ - O(GetLocal) \ O(GreaterThan) \ O(GreaterThanEquals) \ O(HasPrivateId) \ @@ -67,7 +68,7 @@ O(IteratorNext) \ O(IteratorToArray) \ O(Jump) \ - O(JumpConditional) \ + O(JumpIf) \ O(JumpNullish) \ O(JumpUndefined) \ O(LeaveLexicalEnvironment) \ @@ -75,11 +76,10 @@ O(LeftShift) \ O(LessThan) \ O(LessThanEquals) \ - O(Load) \ - O(LoadImmediate) \ O(LooselyEquals) \ O(LooselyInequals) \ O(Mod) \ + O(Mov) \ O(Mul) \ O(NewArray) \ O(NewBigInt) \ @@ -103,7 +103,6 @@ O(ScheduleJump) \ O(SetVariable) \ O(SetLocal) \ - O(Store) \ O(StrictlyEquals) \ O(StrictlyInequals) \ O(Sub) \ @@ -111,10 +110,10 @@ O(Throw) \ O(ThrowIfNotObject) \ O(ThrowIfNullish) \ + O(ThrowIfTDZ) \ O(ToNumeric) \ O(Typeof) \ O(TypeofVariable) \ - O(TypeofLocal) \ O(UnaryMinus) \ O(UnaryPlus) \ O(UnsignedRightShift) \ diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index d33b2a6e30..50cb939283 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -39,6 +39,44 @@ namespace JS::Bytecode { bool g_dump_bytecode = false; +static ByteString format_operand(StringView name, Operand operand, Bytecode::Executable const& executable) +{ + StringBuilder builder; + builder.appendff("\033[32m{}\033[0m:", name); + switch (operand.type()) { + case Operand::Type::Register: + builder.appendff("\033[33mreg{}\033[0m", operand.index()); + break; + case Operand::Type::Local: + // FIXME: Show local name. + builder.appendff("\033[34mloc{}\033[0m", operand.index()); + break; + case Operand::Type::Constant: { + builder.append("\033[36m"sv); + auto value = executable.constants[operand.index()]; + if (value.is_empty()) + builder.append(""sv); + else if (value.is_boolean()) + builder.appendff("Bool({})", value.as_bool() ? "true"sv : "false"sv); + else if (value.is_int32()) + builder.appendff("Int32({})", value.as_i32()); + else if (value.is_double()) + builder.appendff("Double({})", value.as_double()); + else if (value.is_undefined()) + builder.append("Undefined"sv); + else if (value.is_null()) + builder.append("Null"sv); + else + builder.appendff("Value: {}", value); + builder.append("\033[0m"sv); + break; + } + default: + VERIFY_NOT_REACHED(); + } + return builder.to_byte_string(); +} + NonnullOwnPtr CallFrame::create(size_t register_count) { size_t allocation_size = sizeof(CallFrame) + sizeof(Value) * register_count; @@ -67,7 +105,7 @@ void Interpreter::visit_edges(Cell::Visitor& visitor) } } -Value Interpreter::get(Operand op) const +ALWAYS_INLINE Value Interpreter::get(Operand op) const { switch (op.type()) { case Operand::Type::Register: @@ -77,10 +115,10 @@ Value Interpreter::get(Operand op) const case Operand::Type::Constant: return current_executable().constants[op.index()]; } - VERIFY_NOT_REACHED(); + __builtin_unreachable(); } -void Interpreter::set(Operand op, Value value) +ALWAYS_INLINE void Interpreter::set(Operand op, Value value) { switch (op.type()) { case Operand::Type::Register: @@ -92,7 +130,7 @@ void Interpreter::set(Operand op, Value value) case Operand::Type::Constant: VERIFY_NOT_REACHED(); } - VERIFY_NOT_REACHED(); + __builtin_unreachable(); } // 16.1.6 ScriptEvaluation ( scriptRecord ), https://tc39.es/ecma262/#sec-runtime-semantics-scriptevaluation @@ -144,7 +182,7 @@ ThrowCompletionOr Interpreter::run(Script& script_record, JS::GCPtr Interpreter::run(SourceTextModule& module) return js_undefined(); } -void Interpreter::run_bytecode() +FLATTEN void Interpreter::run_bytecode() { auto* locals = vm().running_execution_context().locals.data(); - auto* registers = this->registers().data(); auto& accumulator = this->accumulator(); for (;;) { start: @@ -239,45 +276,32 @@ void Interpreter::run_bytecode() auto& instruction = *pc; switch (instruction.type()) { - case Instruction::Type::GetLocal: { - auto& local = locals[static_cast(instruction).index()]; - if (local.is_empty()) { - auto const& variable_name = vm().running_execution_context().function->local_variables_names()[static_cast(instruction).index()]; - result = vm().throw_completion(ErrorType::BindingNotInitialized, variable_name); - break; - } - accumulator = local; - break; - } case Instruction::Type::SetLocal: - locals[static_cast(instruction).index()] = accumulator; + locals[static_cast(instruction).index()] = get(static_cast(instruction).src()); break; - case Instruction::Type::Load: - accumulator = registers[static_cast(instruction).src().index()]; - break; - case Instruction::Type::Store: - registers[static_cast(instruction).dst().index()] = accumulator; - break; - case Instruction::Type::LoadImmediate: - accumulator = static_cast(instruction).value(); + case Instruction::Type::Mov: + set(static_cast(instruction).dst(), get(static_cast(instruction).src())); break; + case Instruction::Type::End: + accumulator = get(static_cast(instruction).value()); + return; case Instruction::Type::Jump: m_current_block = &static_cast(instruction).true_target()->block(); goto start; - case Instruction::Type::JumpConditional: - if (accumulator.to_boolean()) - m_current_block = &static_cast(instruction).true_target()->block(); + case Instruction::Type::JumpIf: + if (get(static_cast(instruction).condition()).to_boolean()) + m_current_block = &static_cast(instruction).true_target()->block(); else - m_current_block = &static_cast(instruction).false_target()->block(); + m_current_block = &static_cast(instruction).false_target()->block(); goto start; case Instruction::Type::JumpNullish: - if (accumulator.is_nullish()) + if (get(static_cast(instruction).condition()).is_nullish()) m_current_block = &static_cast(instruction).true_target()->block(); else m_current_block = &static_cast(instruction).false_target()->block(); goto start; case Instruction::Type::JumpUndefined: - if (accumulator.is_undefined()) + if (get(static_cast(instruction).condition()).is_undefined()) m_current_block = &static_cast(instruction).true_target()->block(); else m_current_block = &static_cast(instruction).false_target()->block(); @@ -458,9 +482,9 @@ void Interpreter::leave_unwind_context() unwind_contexts().take_last(); } -void Interpreter::catch_exception() +void Interpreter::catch_exception(Operand dst) { - accumulator() = reg(Register::exception()); + set(dst, reg(Register::exception())); reg(Register::exception()) = {}; auto& context = unwind_contexts().last(); VERIFY(!context.handler_called); @@ -476,9 +500,9 @@ void Interpreter::enter_object_environment(Object& object) vm().running_execution_context().lexical_environment = new_object_environment(object, true, old_environment); } -ThrowCompletionOr> compile(VM& vm, ASTNode const& node, FunctionKind kind, DeprecatedFlyString const& name) +ThrowCompletionOr> compile(VM& vm, ASTNode const& node, ReadonlySpan parameters, FunctionKind kind, DeprecatedFlyString const& name) { - auto executable_result = Bytecode::Generator::generate(vm, node, kind); + auto executable_result = Bytecode::Generator::generate(vm, node, parameters, kind); if (executable_result.is_error()) return vm.throw_completion(ErrorType::NotImplemented, TRY_OR_THROW_OOM(vm, executable_result.error().to_string())); @@ -533,19 +557,32 @@ ByteString Instruction::to_byte_string(Bytecode::Executable const& executable) c namespace JS::Bytecode::Op { -ThrowCompletionOr Load::execute_impl(Bytecode::Interpreter&) const +static void dump_object(Object& o, HashTable& seen, int indent = 0) { - // Handled in the interpreter loop. - __builtin_unreachable(); + if (seen.contains(&o)) + return; + seen.set(&o); + for (auto& it : o.shape().property_table()) { + auto value = o.get_direct(it.value.offset); + dbgln("{} {} -> {}", String::repeated(' ', indent).release_value(), it.key.to_display_string(), value); + if (value.is_object()) { + dump_object(value.as_object(), seen, indent + 2); + } + } } -ThrowCompletionOr LoadImmediate::execute_impl(Bytecode::Interpreter&) const +ThrowCompletionOr Dump::execute_impl(Bytecode::Interpreter& interpreter) const { - // Handled in the interpreter loop. - __builtin_unreachable(); + auto value = interpreter.get(m_value); + dbgln("(DUMP) {}: {}", m_text, value); + if (value.is_object()) { + HashTable seen; + dump_object(value.as_object(), seen); + } + return {}; } -ThrowCompletionOr Store::execute_impl(Bytecode::Interpreter&) const +ThrowCompletionOr End::execute_impl(Bytecode::Interpreter&) const { // Handled in the interpreter loop. __builtin_unreachable(); @@ -575,14 +612,17 @@ static ThrowCompletionOr strict_equals(VM&, Value src1, Value src2) ThrowCompletionOr OpTitleCase::execute_impl(Bytecode::Interpreter& interpreter) const \ { \ auto& vm = interpreter.vm(); \ - auto lhs = interpreter.reg(m_lhs_reg); \ - auto rhs = interpreter.accumulator(); \ - interpreter.accumulator() = TRY(op_snake_case(vm, lhs, rhs)); \ + auto lhs = interpreter.get(m_lhs); \ + auto rhs = interpreter.get(m_rhs); \ + interpreter.set(m_dst, TRY(op_snake_case(vm, lhs, rhs))); \ return {}; \ } \ - ByteString OpTitleCase::to_byte_string_impl(Bytecode::Executable const&) const \ + ByteString OpTitleCase::to_byte_string_impl(Bytecode::Executable const& executable) const \ { \ - return ByteString::formatted(#OpTitleCase " {}", m_lhs_reg); \ + return ByteString::formatted(#OpTitleCase " {}, {}, {}", \ + format_operand("dst"sv, m_dst, executable), \ + format_operand("lhs"sv, m_lhs, executable), \ + format_operand("rhs"sv, m_rhs, executable)); \ } JS_ENUMERATE_COMMON_BINARY_OPS(JS_DEFINE_COMMON_BINARY_OP) @@ -601,12 +641,14 @@ static ThrowCompletionOr typeof_(VM& vm, Value value) ThrowCompletionOr OpTitleCase::execute_impl(Bytecode::Interpreter& interpreter) const \ { \ auto& vm = interpreter.vm(); \ - interpreter.accumulator() = TRY(op_snake_case(vm, interpreter.accumulator())); \ + interpreter.set(dst(), TRY(op_snake_case(vm, interpreter.get(src())))); \ return {}; \ } \ - ByteString OpTitleCase::to_byte_string_impl(Bytecode::Executable const&) const \ + ByteString OpTitleCase::to_byte_string_impl(Bytecode::Executable const& executable) const \ { \ - return #OpTitleCase; \ + return ByteString::formatted(#OpTitleCase " {}, {}", \ + format_operand("dst"sv, dst(), executable), \ + format_operand("src"sv, src(), executable)); \ } JS_ENUMERATE_COMMON_UNARY_OPS(JS_DEFINE_COMMON_UNARY_OP) @@ -614,7 +656,7 @@ JS_ENUMERATE_COMMON_UNARY_OPS(JS_DEFINE_COMMON_UNARY_OP) ThrowCompletionOr NewBigInt::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - interpreter.accumulator() = BigInt::create(vm, m_bigint); + interpreter.set(dst(), BigInt::create(vm, m_bigint)); return {}; } @@ -625,7 +667,7 @@ ThrowCompletionOr NewArray::execute_impl(Bytecode::Interpreter& interprete auto& value = interpreter.reg(Register(m_elements[0].index() + i)); array->indexed_properties().put(i, value, default_attributes); } - interpreter.accumulator() = array; + interpreter.set(dst(), array); return {}; } @@ -634,33 +676,33 @@ ThrowCompletionOr NewPrimitiveArray::execute_impl(Bytecode::Interpreter& i auto array = MUST(Array::create(interpreter.realm(), 0)); for (size_t i = 0; i < m_values.size(); i++) array->indexed_properties().put(i, m_values[i], default_attributes); - interpreter.accumulator() = array; + interpreter.set(dst(), array); return {}; } -ThrowCompletionOr Append::execute_impl(Bytecode::Interpreter& interpreter) const +ThrowCompletionOr ArrayAppend::execute_impl(Bytecode::Interpreter& interpreter) const { - return append(interpreter.vm(), interpreter.reg(m_lhs), interpreter.accumulator(), m_is_spread); + return append(interpreter.vm(), interpreter.get(dst()), interpreter.get(src()), m_is_spread); } ThrowCompletionOr ImportCall::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - auto specifier = interpreter.reg(m_specifier); - auto options_value = interpreter.reg(m_options); - interpreter.accumulator() = TRY(perform_import_call(vm, specifier, options_value)); + auto specifier = interpreter.get(m_specifier); + auto options_value = interpreter.get(m_options); + interpreter.set(dst(), TRY(perform_import_call(vm, specifier, options_value))); return {}; } ThrowCompletionOr IteratorToArray::execute_impl(Bytecode::Interpreter& interpreter) const { - interpreter.accumulator() = TRY(iterator_to_array(interpreter.vm(), interpreter.accumulator())); + interpreter.set(dst(), TRY(iterator_to_array(interpreter.vm(), interpreter.get(iterator())))); return {}; } ThrowCompletionOr NewString::execute_impl(Bytecode::Interpreter& interpreter) const { - interpreter.accumulator() = PrimitiveString::create(interpreter.vm(), interpreter.current_executable().get_string(m_string)); + interpreter.set(dst(), PrimitiveString::create(interpreter.vm(), interpreter.current_executable().get_string(m_string))); return {}; } @@ -668,32 +710,34 @@ ThrowCompletionOr NewObject::execute_impl(Bytecode::Interpreter& interpret { auto& vm = interpreter.vm(); auto& realm = *vm.current_realm(); - - interpreter.accumulator() = Object::create(realm, realm.intrinsics().object_prototype()); + interpreter.set(dst(), Object::create(realm, realm.intrinsics().object_prototype())); return {}; } ThrowCompletionOr NewRegExp::execute_impl(Bytecode::Interpreter& interpreter) const { - interpreter.accumulator() = new_regexp( - interpreter.vm(), - interpreter.current_executable().regex_table->get(m_regex_index), - interpreter.current_executable().get_string(m_source_index), - interpreter.current_executable().get_string(m_flags_index)); + interpreter.set(dst(), + new_regexp( + interpreter.vm(), + interpreter.current_executable().regex_table->get(m_regex_index), + interpreter.current_executable().get_string(m_source_index), + interpreter.current_executable().get_string(m_flags_index))); return {}; } -#define JS_DEFINE_NEW_BUILTIN_ERROR_OP(ErrorName) \ - ThrowCompletionOr New##ErrorName::execute_impl(Bytecode::Interpreter& interpreter) const \ - { \ - auto& vm = interpreter.vm(); \ - auto& realm = *vm.current_realm(); \ - interpreter.accumulator() = ErrorName::create(realm, interpreter.current_executable().get_string(m_error_string)); \ - return {}; \ - } \ - ByteString New##ErrorName::to_byte_string_impl(Bytecode::Executable const& executable) const \ - { \ - return ByteString::formatted("New" #ErrorName " {} (\"{}\")", m_error_string, executable.string_table->get(m_error_string)); \ +#define JS_DEFINE_NEW_BUILTIN_ERROR_OP(ErrorName) \ + ThrowCompletionOr New##ErrorName::execute_impl(Bytecode::Interpreter& interpreter) const \ + { \ + auto& vm = interpreter.vm(); \ + auto& realm = *vm.current_realm(); \ + interpreter.set(dst(), ErrorName::create(realm, interpreter.current_executable().get_string(m_error_string))); \ + return {}; \ + } \ + ByteString New##ErrorName::to_byte_string_impl(Bytecode::Executable const& executable) const \ + { \ + return ByteString::formatted("New" #ErrorName " {}, {}", \ + format_operand("dst"sv, m_dst, executable), \ + executable.string_table->get(m_error_string)); \ } JS_ENUMERATE_NEW_BUILTIN_ERROR_OPS(JS_DEFINE_NEW_BUILTIN_ERROR_OP) @@ -703,35 +747,32 @@ ThrowCompletionOr CopyObjectExcludingProperties::execute_impl(Bytecode::In auto& vm = interpreter.vm(); auto& realm = *vm.current_realm(); - auto from_object = interpreter.reg(m_from_object); + auto from_object = interpreter.get(m_from_object); auto to_object = Object::create(realm, realm.intrinsics().object_prototype()); HashTable excluded_names; for (size_t i = 0; i < m_excluded_names_count; ++i) { - excluded_names.set(TRY(interpreter.reg(m_excluded_names[i]).to_property_key(vm))); + excluded_names.set(TRY(interpreter.get(m_excluded_names[i]).to_property_key(vm))); } TRY(to_object->copy_data_properties(vm, from_object, excluded_names)); - interpreter.accumulator() = to_object; + interpreter.set(dst(), to_object); return {}; } ThrowCompletionOr ConcatString::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - auto string = TRY(interpreter.accumulator().to_primitive_string(vm)); - interpreter.reg(m_lhs) = PrimitiveString::create(vm, interpreter.reg(m_lhs).as_string(), string); + auto string = TRY(interpreter.get(src()).to_primitive_string(vm)); + interpreter.set(dst(), PrimitiveString::create(vm, interpreter.get(dst()).as_string(), string)); return {}; } ThrowCompletionOr GetVariable::execute_impl(Bytecode::Interpreter& interpreter) const { - interpreter.accumulator() = TRY(get_variable( - interpreter, - interpreter.current_executable().get_identifier(m_identifier), - interpreter.current_executable().environment_variable_caches[m_cache_index])); + interpreter.set(dst(), TRY(get_variable(interpreter, interpreter.current_executable().get_identifier(m_identifier), interpreter.current_executable().environment_variable_caches[m_cache_index]))); return {}; } @@ -741,32 +782,23 @@ ThrowCompletionOr GetCalleeAndThisFromEnvironment::execute_impl(Bytecode:: interpreter, interpreter.current_executable().get_identifier(m_identifier), interpreter.current_executable().environment_variable_caches[m_cache_index])); - interpreter.reg(m_callee_reg) = callee_and_this.callee; - interpreter.reg(m_this_reg) = callee_and_this.this_value; + interpreter.set(m_callee, callee_and_this.callee); + interpreter.set(m_this_value, callee_and_this.this_value); return {}; } ThrowCompletionOr GetGlobal::execute_impl(Bytecode::Interpreter& interpreter) const { - interpreter.accumulator() = TRY(get_global( - interpreter, - interpreter.current_executable().get_identifier(m_identifier), - interpreter.current_executable().global_variable_caches[m_cache_index])); + interpreter.set(dst(), TRY(get_global(interpreter, interpreter.current_executable().get_identifier(m_identifier), interpreter.current_executable().global_variable_caches[m_cache_index]))); return {}; } -ThrowCompletionOr GetLocal::execute_impl(Bytecode::Interpreter&) const -{ - // Handled in the interpreter loop. - __builtin_unreachable(); -} - ThrowCompletionOr DeleteVariable::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); auto const& string = interpreter.current_executable().get_identifier(m_identifier); auto reference = TRY(vm.resolve_binding(string)); - interpreter.accumulator() = Value(TRY(reference.delete_(vm))); + interpreter.set(dst(), Value(TRY(reference.delete_(vm)))); return {}; } @@ -783,14 +815,14 @@ ThrowCompletionOr CreateLexicalEnvironment::execute_impl(Bytecode::Interpr ThrowCompletionOr EnterObjectEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const { - auto object = TRY(interpreter.accumulator().to_object(interpreter.vm())); + auto object = TRY(interpreter.get(m_object).to_object(interpreter.vm())); interpreter.enter_object_environment(*object); return {}; } ThrowCompletionOr Catch::execute_impl(Bytecode::Interpreter& interpreter) const { - interpreter.catch_exception(); + interpreter.catch_exception(dst()); return {}; } @@ -806,7 +838,7 @@ ThrowCompletionOr SetVariable::execute_impl(Bytecode::Interpreter& interpr auto const& name = interpreter.current_executable().get_identifier(m_identifier); TRY(set_variable(vm, name, - interpreter.accumulator(), + interpreter.get(src()), m_mode, m_initialization_mode, interpreter.current_executable().environment_variable_caches[m_cache_index])); @@ -821,18 +853,18 @@ ThrowCompletionOr SetLocal::execute_impl(Bytecode::Interpreter&) const ThrowCompletionOr GetById::execute_impl(Bytecode::Interpreter& interpreter) const { - auto base_value = interpreter.accumulator(); + auto base_value = interpreter.get(base()); auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; - interpreter.accumulator() = TRY(get_by_id(interpreter.vm(), interpreter.current_executable().get_identifier(m_property), base_value, base_value, cache)); + interpreter.set(dst(), TRY(get_by_id(interpreter.vm(), interpreter.current_executable().get_identifier(m_property), base_value, base_value, cache))); return {}; } ThrowCompletionOr GetByIdWithThis::execute_impl(Bytecode::Interpreter& interpreter) const { - auto base_value = interpreter.accumulator(); - auto this_value = interpreter.reg(m_this_value); + auto base_value = interpreter.get(m_base); + auto this_value = interpreter.get(m_this_value); auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; - interpreter.accumulator() = TRY(get_by_id(interpreter.vm(), interpreter.current_executable().get_identifier(m_property), base_value, this_value, cache)); + interpreter.set(dst(), TRY(get_by_id(interpreter.vm(), interpreter.current_executable().get_identifier(m_property), base_value, this_value, cache))); return {}; } @@ -840,9 +872,9 @@ ThrowCompletionOr GetPrivateById::execute_impl(Bytecode::Interpreter& inte { auto& vm = interpreter.vm(); auto const& name = interpreter.current_executable().get_identifier(m_property); - auto base_value = interpreter.accumulator(); + auto base_value = interpreter.get(m_base); auto private_reference = make_private_reference(vm, base_value, name); - interpreter.accumulator() = TRY(private_reference.get_value(vm)); + interpreter.set(dst(), TRY(private_reference.get_value(vm))); return {}; } @@ -850,70 +882,65 @@ ThrowCompletionOr HasPrivateId::execute_impl(Bytecode::Interpreter& interp { auto& vm = interpreter.vm(); - if (!interpreter.accumulator().is_object()) + auto base = interpreter.get(m_base); + if (!base.is_object()) return vm.throw_completion(ErrorType::InOperatorWithObject); auto private_environment = vm.running_execution_context().private_environment; VERIFY(private_environment); auto private_name = private_environment->resolve_private_identifier(interpreter.current_executable().get_identifier(m_property)); - interpreter.accumulator() = Value(interpreter.accumulator().as_object().private_element_find(private_name) != nullptr); + interpreter.set(dst(), Value(base.as_object().private_element_find(private_name) != nullptr)); return {}; } ThrowCompletionOr PutById::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - // NOTE: Get the value from the accumulator before side effects have a chance to overwrite it. - auto value = interpreter.accumulator(); - auto base = interpreter.reg(m_base); + auto value = interpreter.get(m_src); + auto base = interpreter.get(m_base); PropertyKey name = interpreter.current_executable().get_identifier(m_property); auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; TRY(put_by_property_key(vm, base, base, value, name, m_kind, &cache)); - interpreter.accumulator() = value; return {}; } ThrowCompletionOr PutByIdWithThis::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - // NOTE: Get the value from the accumulator before side effects have a chance to overwrite it. - auto value = interpreter.accumulator(); - auto base = interpreter.reg(m_base); + auto value = interpreter.get(m_src); + auto base = interpreter.get(m_base); PropertyKey name = interpreter.current_executable().get_identifier(m_property); auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; - TRY(put_by_property_key(vm, base, interpreter.reg(m_this_value), value, name, m_kind, &cache)); - interpreter.accumulator() = value; + TRY(put_by_property_key(vm, base, interpreter.get(m_this_value), value, name, m_kind, &cache)); return {}; } ThrowCompletionOr PutPrivateById::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - // NOTE: Get the value from the accumulator before side effects have a chance to overwrite it. - auto value = interpreter.accumulator(); - auto object = TRY(interpreter.reg(m_base).to_object(vm)); + auto value = interpreter.get(m_src); + auto object = TRY(interpreter.get(m_base).to_object(vm)); auto name = interpreter.current_executable().get_identifier(m_property); auto private_reference = make_private_reference(vm, object, name); TRY(private_reference.put_value(vm, value)); - interpreter.accumulator() = value; return {}; } ThrowCompletionOr DeleteById::execute_impl(Bytecode::Interpreter& interpreter) const { - auto base_value = interpreter.accumulator(); - interpreter.accumulator() = TRY(Bytecode::delete_by_id(interpreter, base_value, m_property)); + auto base_value = interpreter.get(m_base); + interpreter.set(dst(), TRY(Bytecode::delete_by_id(interpreter, base_value, m_property))); return {}; } ThrowCompletionOr DeleteByIdWithThis::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - auto base_value = interpreter.accumulator(); + auto base_value = interpreter.get(m_base); auto const& identifier = interpreter.current_executable().get_identifier(m_property); bool strict = vm.in_strict_mode(); - auto reference = Reference { base_value, identifier, interpreter.reg(m_this_value), strict }; - interpreter.accumulator() = Value(TRY(reference.delete_(vm))); + auto reference = Reference { base_value, identifier, interpreter.get(m_this_value), strict }; + interpreter.set(dst(), Value(TRY(reference.delete_(vm)))); return {}; } @@ -932,7 +959,7 @@ ThrowCompletionOr ResolveThisBinding::execute_impl(Bytecode::Interpreter& auto& vm = interpreter.vm(); cached_this_value = TRY(vm.resolve_this_binding()); } - interpreter.accumulator() = cached_this_value; + interpreter.set(dst(), cached_this_value); return {}; } @@ -948,24 +975,30 @@ ThrowCompletionOr ResolveSuperBase::execute_impl(Bytecode::Interpreter& in VERIFY(env.has_super_binding()); // 3. Let baseValue be ? env.GetSuperBase(). - interpreter.accumulator() = TRY(env.get_super_base()); + interpreter.set(dst(), TRY(env.get_super_base())); return {}; } ThrowCompletionOr GetNewTarget::execute_impl(Bytecode::Interpreter& interpreter) const { - interpreter.accumulator() = interpreter.vm().get_new_target(); + interpreter.set(dst(), interpreter.vm().get_new_target()); return {}; } ThrowCompletionOr GetImportMeta::execute_impl(Bytecode::Interpreter& interpreter) const { - interpreter.accumulator() = interpreter.vm().get_import_meta(); + interpreter.set(dst(), interpreter.vm().get_import_meta()); return {}; } -ThrowCompletionOr JumpConditional::execute_impl(Bytecode::Interpreter&) const +ThrowCompletionOr JumpIf::execute_impl(Bytecode::Interpreter&) const +{ + // Handled in the interpreter loop. + __builtin_unreachable(); +} + +ThrowCompletionOr JumpUndefined::execute_impl(Bytecode::Interpreter&) const { // Handled in the interpreter loop. __builtin_unreachable(); @@ -977,7 +1010,7 @@ ThrowCompletionOr JumpNullish::execute_impl(Bytecode::Interpreter&) const __builtin_unreachable(); } -ThrowCompletionOr JumpUndefined::execute_impl(Bytecode::Interpreter&) const +ThrowCompletionOr Mov::execute_impl(Bytecode::Interpreter&) const { // Handled in the interpreter loop. __builtin_unreachable(); @@ -1012,58 +1045,61 @@ static ThrowCompletionOr dispatch_builtin_call(Bytecode::Interpreter& int ThrowCompletionOr Call::execute_impl(Bytecode::Interpreter& interpreter) const { - auto callee = interpreter.reg(m_callee); + auto callee = interpreter.get(m_callee); TRY(throw_if_needed_for_call(interpreter, callee, call_type(), expression_string())); if (m_builtin.has_value() && m_argument_count == Bytecode::builtin_argument_count(m_builtin.value()) && interpreter.realm().get_builtin_value(m_builtin.value()) == callee) { - interpreter.accumulator() = TRY(dispatch_builtin_call(interpreter, m_builtin.value(), m_first_argument)); + interpreter.set(dst(), TRY(dispatch_builtin_call(interpreter, m_builtin.value(), m_first_argument))); return {}; } - interpreter.accumulator() = TRY(perform_call(interpreter, interpreter.reg(m_this_value), call_type(), callee, interpreter.registers().slice(m_first_argument.index(), m_argument_count))); + interpreter.set(dst(), TRY(perform_call(interpreter, interpreter.get(m_this_value), call_type(), callee, interpreter.registers().slice(m_first_argument.index(), m_argument_count)))); return {}; } ThrowCompletionOr CallWithArgumentArray::execute_impl(Bytecode::Interpreter& interpreter) const { - auto callee = interpreter.reg(m_callee); + auto callee = interpreter.get(m_callee); TRY(throw_if_needed_for_call(interpreter, callee, call_type(), expression_string())); - auto argument_values = argument_list_evaluation(interpreter.vm(), interpreter.accumulator()); - interpreter.accumulator() = TRY(perform_call(interpreter, interpreter.reg(m_this_value), call_type(), callee, move(argument_values))); + auto argument_values = argument_list_evaluation(interpreter.vm(), interpreter.get(arguments())); + interpreter.set(dst(), TRY(perform_call(interpreter, interpreter.get(m_this_value), call_type(), callee, move(argument_values)))); return {}; } // 13.3.7.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation ThrowCompletionOr SuperCallWithArgumentArray::execute_impl(Bytecode::Interpreter& interpreter) const { - interpreter.accumulator() = TRY(super_call_with_argument_array(interpreter.vm(), interpreter.accumulator(), m_is_synthetic)); + interpreter.set(dst(), TRY(super_call_with_argument_array(interpreter.vm(), interpreter.get(arguments()), m_is_synthetic))); return {}; } ThrowCompletionOr NewFunction::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - interpreter.accumulator() = new_function(vm, m_function_node, m_lhs_name, m_home_object); + interpreter.set(dst(), new_function(vm, m_function_node, m_lhs_name, m_home_object)); return {}; } ThrowCompletionOr Return::execute_impl(Bytecode::Interpreter& interpreter) const { - interpreter.do_return(interpreter.accumulator().value_or(js_undefined())); + if (m_value.has_value()) + interpreter.do_return(interpreter.get(*m_value)); + else + interpreter.do_return(js_undefined()); return {}; } ThrowCompletionOr Increment::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - auto old_value = interpreter.accumulator(); + auto old_value = interpreter.get(dst()); // OPTIMIZATION: Fast path for Int32 values. if (old_value.is_int32()) { auto integer_value = old_value.as_i32(); if (integer_value != NumericLimits::max()) [[likely]] { - interpreter.accumulator() = Value { integer_value + 1 }; + interpreter.set(dst(), Value { integer_value + 1 }); return {}; } } @@ -1071,46 +1107,58 @@ ThrowCompletionOr Increment::execute_impl(Bytecode::Interpreter& interpret old_value = TRY(old_value.to_numeric(vm)); if (old_value.is_number()) - interpreter.accumulator() = Value(old_value.as_double() + 1); + interpreter.set(dst(), Value(old_value.as_double() + 1)); else - interpreter.accumulator() = BigInt::create(vm, old_value.as_bigint().big_integer().plus(Crypto::SignedBigInteger { 1 })); + interpreter.set(dst(), BigInt::create(vm, old_value.as_bigint().big_integer().plus(Crypto::SignedBigInteger { 1 }))); return {}; } ThrowCompletionOr Decrement::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - auto old_value = TRY(interpreter.accumulator().to_numeric(vm)); + auto old_value = interpreter.get(dst()); + + old_value = TRY(old_value.to_numeric(vm)); if (old_value.is_number()) - interpreter.accumulator() = Value(old_value.as_double() - 1); + interpreter.set(dst(), Value(old_value.as_double() - 1)); else - interpreter.accumulator() = BigInt::create(vm, old_value.as_bigint().big_integer().minus(Crypto::SignedBigInteger { 1 })); + interpreter.set(dst(), BigInt::create(vm, old_value.as_bigint().big_integer().minus(Crypto::SignedBigInteger { 1 }))); return {}; } ThrowCompletionOr Throw::execute_impl(Bytecode::Interpreter& interpreter) const { - return throw_completion(interpreter.accumulator()); + return throw_completion(interpreter.get(src())); } ThrowCompletionOr ThrowIfNotObject::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - if (!interpreter.accumulator().is_object()) - return vm.throw_completion(ErrorType::NotAnObject, interpreter.accumulator().to_string_without_side_effects()); + auto src = interpreter.get(m_src); + if (!src.is_object()) + return vm.throw_completion(ErrorType::NotAnObject, src.to_string_without_side_effects()); return {}; } ThrowCompletionOr ThrowIfNullish::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - auto value = interpreter.accumulator(); + auto value = interpreter.get(m_src); if (value.is_nullish()) return vm.throw_completion(ErrorType::NotObjectCoercible, value.to_string_without_side_effects()); return {}; } +ThrowCompletionOr ThrowIfTDZ::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + auto value = interpreter.get(m_src); + if (value.is_empty()) + return vm.throw_completion(ErrorType::BindingNotInitialized, value.to_string_without_side_effects()); + return {}; +} + ThrowCompletionOr EnterUnwindContext::execute_impl(Bytecode::Interpreter&) const { // Handled in the interpreter loop. @@ -1143,7 +1191,8 @@ ThrowCompletionOr ContinuePendingUnwind::execute_impl(Bytecode::Interprete ThrowCompletionOr Yield::execute_impl(Bytecode::Interpreter& interpreter) const { - auto yielded_value = interpreter.accumulator().value_or(js_undefined()); + auto yielded_value = interpreter.get(m_value).value_or(js_undefined()); + auto object = Object::create(interpreter.realm(), nullptr); object->define_direct_property("result", yielded_value, JS::default_attributes); @@ -1156,12 +1205,13 @@ ThrowCompletionOr Yield::execute_impl(Bytecode::Interpreter& interpreter) object->define_direct_property("isAwait", Value(false), JS::default_attributes); interpreter.do_return(object); + return {}; } ThrowCompletionOr Await::execute_impl(Bytecode::Interpreter& interpreter) const { - auto yielded_value = interpreter.accumulator().value_or(js_undefined()); + auto yielded_value = interpreter.get(m_argument).value_or(js_undefined()); auto object = Object::create(interpreter.realm(), nullptr); object->define_direct_property("result", yielded_value, JS::default_attributes); // FIXME: If we get a pointer, which is not accurately representable as a double @@ -1174,67 +1224,53 @@ ThrowCompletionOr Await::execute_impl(Bytecode::Interpreter& interpreter) ThrowCompletionOr GetByValue::execute_impl(Bytecode::Interpreter& interpreter) const { - interpreter.accumulator() = TRY(get_by_value(interpreter.vm(), interpreter.reg(m_base), interpreter.accumulator())); + interpreter.set(dst(), TRY(get_by_value(interpreter.vm(), interpreter.get(m_base), interpreter.get(m_property)))); return {}; } ThrowCompletionOr GetByValueWithThis::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - - // NOTE: Get the property key from the accumulator before side effects have a chance to overwrite it. - auto property_key_value = interpreter.accumulator(); - - auto object = TRY(interpreter.reg(m_base).to_object(vm)); - + auto property_key_value = interpreter.get(m_property); + auto object = TRY(interpreter.get(m_base).to_object(vm)); auto property_key = TRY(property_key_value.to_property_key(vm)); - - interpreter.accumulator() = TRY(object->internal_get(property_key, interpreter.reg(m_this_value))); + interpreter.set(dst(), TRY(object->internal_get(property_key, interpreter.get(m_this_value)))); return {}; } ThrowCompletionOr PutByValue::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - - auto value = interpreter.accumulator(); - TRY(put_by_value(vm, interpreter.reg(m_base), interpreter.reg(m_property), interpreter.accumulator(), m_kind)); - interpreter.accumulator() = value; - + auto value = interpreter.get(m_src); + TRY(put_by_value(vm, interpreter.get(m_base), interpreter.get(m_property), value, m_kind)); return {}; } ThrowCompletionOr PutByValueWithThis::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - - // NOTE: Get the value from the accumulator before side effects have a chance to overwrite it. - auto value = interpreter.accumulator(); - - auto base = interpreter.reg(m_base); - - auto property_key = m_kind != PropertyKind::Spread ? TRY(interpreter.reg(m_property).to_property_key(vm)) : PropertyKey {}; - TRY(put_by_property_key(vm, base, interpreter.reg(m_this_value), value, property_key, m_kind)); - interpreter.accumulator() = value; + auto value = interpreter.get(m_src); + auto base = interpreter.get(m_base); + auto property_key = m_kind != PropertyKind::Spread ? TRY(interpreter.get(m_property).to_property_key(vm)) : PropertyKey {}; + TRY(put_by_property_key(vm, base, interpreter.get(m_this_value), value, property_key, m_kind)); return {}; } ThrowCompletionOr DeleteByValue::execute_impl(Bytecode::Interpreter& interpreter) const { - auto base_value = interpreter.reg(m_base); - auto property_key_value = interpreter.accumulator(); - interpreter.accumulator() = TRY(delete_by_value(interpreter, base_value, property_key_value)); + auto base_value = interpreter.get(m_base); + auto property_key_value = interpreter.get(m_property); + interpreter.set(dst(), TRY(delete_by_value(interpreter, base_value, property_key_value))); return {}; } ThrowCompletionOr DeleteByValueWithThis::execute_impl(Bytecode::Interpreter& interpreter) const { - // NOTE: Get the property key from the accumulator before side effects have a chance to overwrite it. - auto property_key_value = interpreter.accumulator(); - auto base_value = interpreter.reg(m_base); - auto this_value = interpreter.reg(m_this_value); - interpreter.accumulator() = TRY(delete_by_value_with_this(interpreter, base_value, property_key_value, this_value)); + auto property_key_value = interpreter.get(m_property); + auto base_value = interpreter.get(m_base); + auto this_value = interpreter.get(m_this_value); + interpreter.set(dst(), TRY(delete_by_value_with_this(interpreter, base_value, property_key_value, this_value))); return {}; } @@ -1242,21 +1278,21 @@ ThrowCompletionOr DeleteByValueWithThis::execute_impl(Bytecode::Interprete ThrowCompletionOr GetIterator::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - interpreter.accumulator() = TRY(get_iterator(vm, interpreter.accumulator(), m_hint)); + interpreter.set(dst(), TRY(get_iterator(vm, interpreter.get(iterable()), m_hint))); return {}; } ThrowCompletionOr GetObjectFromIteratorRecord::execute_impl(Bytecode::Interpreter& interpreter) const { - auto& iterator_record = verify_cast(interpreter.reg(m_iterator_record).as_object()); - interpreter.reg(m_object) = iterator_record.iterator; + auto& iterator_record = verify_cast(interpreter.get(m_iterator_record).as_object()); + interpreter.set(m_object, iterator_record.iterator); return {}; } ThrowCompletionOr GetNextMethodFromIteratorRecord::execute_impl(Bytecode::Interpreter& interpreter) const { - auto& iterator_record = verify_cast(interpreter.reg(m_iterator_record).as_object()); - interpreter.reg(m_next_method) = iterator_record.next_method; + auto& iterator_record = verify_cast(interpreter.get(m_iterator_record).as_object()); + interpreter.set(m_next_method, iterator_record.next_method); return {}; } @@ -1264,21 +1300,21 @@ ThrowCompletionOr GetMethod::execute_impl(Bytecode::Interpreter& interpret { auto& vm = interpreter.vm(); auto identifier = interpreter.current_executable().get_identifier(m_property); - auto method = TRY(interpreter.accumulator().get_method(vm, identifier)); - interpreter.accumulator() = method ?: js_undefined(); + auto method = TRY(interpreter.get(m_object).get_method(vm, identifier)); + interpreter.set(dst(), method ?: js_undefined()); return {}; } ThrowCompletionOr GetObjectPropertyIterator::execute_impl(Bytecode::Interpreter& interpreter) const { - interpreter.accumulator() = TRY(get_object_property_iterator(interpreter.vm(), interpreter.accumulator())); + interpreter.set(dst(), TRY(get_object_property_iterator(interpreter.vm(), interpreter.get(object())))); return {}; } ThrowCompletionOr IteratorClose::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - auto& iterator = verify_cast(interpreter.accumulator().as_object()); + auto& iterator = verify_cast(interpreter.get(m_iterator_record).as_object()); // FIXME: Return the value of the resulting completion. (Note that m_completion_value can be empty!) TRY(iterator_close(vm, iterator, Completion { m_completion_type, m_completion_value, {} })); @@ -1288,7 +1324,7 @@ ThrowCompletionOr IteratorClose::execute_impl(Bytecode::Interpreter& inter ThrowCompletionOr AsyncIteratorClose::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - auto& iterator = verify_cast(interpreter.accumulator().as_object()); + auto& iterator = verify_cast(interpreter.get(m_iterator_record).as_object()); // FIXME: Return the value of the resulting completion. (Note that m_completion_value can be empty!) TRY(async_iterator_close(vm, iterator, Completion { m_completion_type, m_completion_value, {} })); @@ -1298,15 +1334,17 @@ ThrowCompletionOr AsyncIteratorClose::execute_impl(Bytecode::Interpreter& ThrowCompletionOr IteratorNext::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - auto& iterator = verify_cast(interpreter.accumulator().as_object()); - - interpreter.accumulator() = TRY(iterator_next(vm, iterator)); + auto& iterator_record = verify_cast(interpreter.get(m_iterator_record).as_object()); + interpreter.set(dst(), TRY(iterator_next(vm, iterator_record))); return {}; } ThrowCompletionOr NewClass::execute_impl(Bytecode::Interpreter& interpreter) const { - interpreter.accumulator() = TRY(new_class(interpreter.vm(), interpreter.accumulator(), m_class_expression, m_lhs_name)); + Value super_class; + if (m_super_class.has_value()) + super_class = interpreter.get(m_super_class.value()); + interpreter.set(dst(), TRY(new_class(interpreter.vm(), super_class, m_class_expression, m_lhs_name))); return {}; } @@ -1314,21 +1352,13 @@ ThrowCompletionOr NewClass::execute_impl(Bytecode::Interpreter& interprete ThrowCompletionOr TypeofVariable::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - interpreter.accumulator() = TRY(typeof_variable(vm, interpreter.current_executable().get_identifier(m_identifier))); - return {}; -} - -ThrowCompletionOr TypeofLocal::execute_impl(Bytecode::Interpreter& interpreter) const -{ - auto& vm = interpreter.vm(); - auto const& value = vm.running_execution_context().local(m_index); - interpreter.accumulator() = PrimitiveString::create(vm, value.typeof()); + interpreter.set(dst(), TRY(typeof_variable(vm, interpreter.current_executable().get_identifier(m_identifier)))); return {}; } ThrowCompletionOr ToNumeric::execute_impl(Bytecode::Interpreter& interpreter) const { - interpreter.accumulator() = TRY(interpreter.accumulator().to_numeric(interpreter.vm())); + interpreter.set(dst(), TRY(interpreter.get(src()).to_numeric(interpreter.vm()))); return {}; } @@ -1342,103 +1372,115 @@ ThrowCompletionOr BlockDeclarationInstantiation::execute_impl(Bytecode::In return {}; } -ByteString Load::to_byte_string_impl(Bytecode::Executable const&) const +ByteString Mov::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("Load {}", m_src); + return ByteString::formatted("Mov {}, {}", + format_operand("dst"sv, m_dst, executable), + format_operand("src"sv, m_src, executable)); } -ByteString LoadImmediate::to_byte_string_impl(Bytecode::Executable const&) const +ByteString NewBigInt::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("LoadImmediate {}", m_value); + return ByteString::formatted("NewBigInt {}, {}", + format_operand("dst"sv, dst(), executable), + m_bigint.to_base_deprecated(10)); } -ByteString Store::to_byte_string_impl(Bytecode::Executable const&) const -{ - return ByteString::formatted("Store {}", m_dst); -} - -ByteString NewBigInt::to_byte_string_impl(Bytecode::Executable const&) const -{ - return ByteString::formatted("NewBigInt \"{}\"", m_bigint.to_base_deprecated(10)); -} - -ByteString NewArray::to_byte_string_impl(Bytecode::Executable const&) const +ByteString NewArray::to_byte_string_impl(Bytecode::Executable const& executable) const { StringBuilder builder; - builder.append("NewArray"sv); + builder.appendff("NewArray {}", format_operand("dst"sv, dst(), executable)); if (m_element_count != 0) { - builder.appendff(" [{}-{}]", m_elements[0], m_elements[1]); + builder.appendff(", [{}-{}]", format_operand("from"sv, m_elements[0], executable), format_operand("to"sv, m_elements[1], executable)); } return builder.to_byte_string(); } -ByteString NewPrimitiveArray::to_byte_string_impl(Bytecode::Executable const&) const +ByteString NewPrimitiveArray::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("NewPrimitiveArray {}"sv, m_values.span()); + return ByteString::formatted("NewPrimitiveArray {}, {}"sv, + format_operand("dst"sv, dst(), executable), + m_values.span()); } -ByteString Append::to_byte_string_impl(Bytecode::Executable const&) const +ByteString ArrayAppend::to_byte_string_impl(Bytecode::Executable const& executable) const { - if (m_is_spread) - return ByteString::formatted("Append lhs: **{}", m_lhs); - return ByteString::formatted("Append lhs: {}", m_lhs); + return ByteString::formatted("Append {}, {}{}", + format_operand("dst"sv, dst(), executable), + format_operand("src"sv, src(), executable), + m_is_spread ? " **"sv : ""sv); } -ByteString IteratorToArray::to_byte_string_impl(Bytecode::Executable const&) const +ByteString IteratorToArray::to_byte_string_impl(Bytecode::Executable const& executable) const { - return "IteratorToArray"; + return ByteString::formatted("IteratorToArray {}, {}", + format_operand("dst"sv, dst(), executable), + format_operand("iterator"sv, iterator(), executable)); } ByteString NewString::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("NewString {} (\"{}\")", m_string, executable.string_table->get(m_string)); + return ByteString::formatted("NewString {}, \"{}\"", + format_operand("dst"sv, dst(), executable), + executable.string_table->get(m_string)); } -ByteString NewObject::to_byte_string_impl(Bytecode::Executable const&) const +ByteString NewObject::to_byte_string_impl(Bytecode::Executable const& executable) const { - return "NewObject"; + return ByteString::formatted("NewObject {}", format_operand("dst"sv, dst(), executable)); } ByteString NewRegExp::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("NewRegExp source:{} (\"{}\") flags:{} (\"{}\")", m_source_index, executable.get_string(m_source_index), m_flags_index, executable.get_string(m_flags_index)); + return ByteString::formatted("NewRegExp {}, source:{} (\"{}\") flags:{} (\"{}\")", + format_operand("dst"sv, dst(), executable), + m_source_index, executable.get_string(m_source_index), m_flags_index, executable.get_string(m_flags_index)); } -ByteString CopyObjectExcludingProperties::to_byte_string_impl(Bytecode::Executable const&) const +ByteString CopyObjectExcludingProperties::to_byte_string_impl(Bytecode::Executable const& executable) const { StringBuilder builder; - builder.appendff("CopyObjectExcludingProperties from:{}", m_from_object); + builder.appendff("CopyObjectExcludingProperties {}, {}", + format_operand("dst"sv, dst(), executable), + format_operand("from"sv, m_from_object, executable)); if (m_excluded_names_count != 0) { builder.append(" excluding:["sv); - builder.join(", "sv, ReadonlySpan(m_excluded_names, m_excluded_names_count)); + for (size_t i = 0; i < m_excluded_names_count; ++i) { + if (i != 0) + builder.append(", "sv); + builder.append(format_operand("#"sv, m_excluded_names[i], executable)); + } builder.append(']'); } return builder.to_byte_string(); } -ByteString ConcatString::to_byte_string_impl(Bytecode::Executable const&) const +ByteString ConcatString::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("ConcatString {}", m_lhs); + return ByteString::formatted("ConcatString {}, {}", + format_operand("dst"sv, dst(), executable), + format_operand("src"sv, src(), executable)); } ByteString GetCalleeAndThisFromEnvironment::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("GetCalleeAndThisFromEnvironment {} -> callee: {}, this:{} ", executable.identifier_table->get(m_identifier), m_callee_reg, m_this_reg); + return ByteString::formatted("GetCalleeAndThisFromEnvironment {}, {} <- {}", + format_operand("callee"sv, m_callee, executable), + format_operand("this"sv, m_this_value, executable), + executable.identifier_table->get(m_identifier)); } ByteString GetVariable::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("GetVariable {} ({})", m_identifier, executable.identifier_table->get(m_identifier)); + return ByteString::formatted("GetVariable {}, {}", + format_operand("dst"sv, dst(), executable), + executable.identifier_table->get(m_identifier)); } ByteString GetGlobal::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("GetGlobal {} ({})", m_identifier, executable.identifier_table->get(m_identifier)); -} - -ByteString GetLocal::to_byte_string_impl(Bytecode::Executable const&) const -{ - return ByteString::formatted("GetLocal {}", m_index); + return ByteString::formatted("GetGlobal {}, {}", format_operand("dst"sv, dst(), executable), + executable.identifier_table->get(m_identifier)); } ByteString DeleteVariable::to_byte_string_impl(Bytecode::Executable const& executable) const @@ -1457,21 +1499,27 @@ ByteString CreateVariable::to_byte_string_impl(Bytecode::Executable const& execu return ByteString::formatted("CreateVariable env:{} immutable:{} global:{} {} ({})", mode_string, m_is_immutable, m_is_global, m_identifier, executable.identifier_table->get(m_identifier)); } -ByteString EnterObjectEnvironment::to_byte_string_impl(Executable const&) const +ByteString EnterObjectEnvironment::to_byte_string_impl(Executable const& executable) const { - return ByteString::formatted("EnterObjectEnvironment"); + return ByteString::formatted("EnterObjectEnvironment {}", + format_operand("object"sv, m_object, executable)); } ByteString SetVariable::to_byte_string_impl(Bytecode::Executable const& executable) const { auto initialization_mode_name = m_initialization_mode == InitializationMode::Initialize ? "Initialize" : "Set"; auto mode_string = m_mode == EnvironmentMode::Lexical ? "Lexical" : "Variable"; - return ByteString::formatted("SetVariable env:{} init:{} {} ({})", mode_string, initialization_mode_name, m_identifier, executable.identifier_table->get(m_identifier)); + return ByteString::formatted("SetVariable {}, {}, env:{} init:{}", + executable.identifier_table->get(m_identifier), + format_operand("src"sv, src(), executable), + mode_string, initialization_mode_name); } -ByteString SetLocal::to_byte_string_impl(Bytecode::Executable const&) const +ByteString SetLocal::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("SetLocal {}", m_index); + return ByteString::formatted("SetLocal {}, {}", + format_operand("dst"sv, dst(), executable), + format_operand("src"sv, src(), executable)); } static StringView property_kind_to_string(PropertyKind kind) @@ -1496,49 +1544,83 @@ static StringView property_kind_to_string(PropertyKind kind) ByteString PutById::to_byte_string_impl(Bytecode::Executable const& executable) const { auto kind = property_kind_to_string(m_kind); - return ByteString::formatted("PutById kind:{} base:{}, property:{} ({})", kind, m_base, m_property, executable.identifier_table->get(m_property)); + return ByteString::formatted("PutById {}, {}, {}, kind:{}", + format_operand("base"sv, m_base, executable), + executable.identifier_table->get(m_property), + format_operand("src"sv, m_src, executable), + kind); } ByteString PutByIdWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const { auto kind = property_kind_to_string(m_kind); - return ByteString::formatted("PutByIdWithThis kind:{} base:{}, property:{} ({}) this_value:{}", kind, m_base, m_property, executable.identifier_table->get(m_property), m_this_value); + return ByteString::formatted("PutByIdWithThis {}, {}, {}, {}, kind:{}", + format_operand("base"sv, m_base, executable), + executable.identifier_table->get(m_property), + format_operand("src"sv, m_src, executable), + format_operand("this"sv, m_this_value, executable), + kind); } ByteString PutPrivateById::to_byte_string_impl(Bytecode::Executable const& executable) const { auto kind = property_kind_to_string(m_kind); - return ByteString::formatted("PutPrivateById kind:{} base:{}, property:{} ({})", kind, m_base, m_property, executable.identifier_table->get(m_property)); + return ByteString::formatted( + "PutPrivateById {}, {}, {}, kind:{} ", + format_operand("base"sv, m_base, executable), + executable.identifier_table->get(m_property), + format_operand("src"sv, m_src, executable), + kind); } ByteString GetById::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("GetById {} ({})", m_property, executable.identifier_table->get(m_property)); + return ByteString::formatted("GetById {}, {}, {}", + format_operand("dst"sv, m_dst, executable), + format_operand("base"sv, m_base, executable), + executable.identifier_table->get(m_property)); } ByteString GetByIdWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("GetByIdWithThis {} ({}) this_value:{}", m_property, executable.identifier_table->get(m_property), m_this_value); + return ByteString::formatted("GetByIdWithThis {}, {}, {}, {}", + format_operand("dst"sv, m_dst, executable), + format_operand("base"sv, m_base, executable), + executable.identifier_table->get(m_property), + format_operand("this"sv, m_this_value, executable)); } ByteString GetPrivateById::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("GetPrivateById {} ({})", m_property, executable.identifier_table->get(m_property)); + return ByteString::formatted("GetPrivateById {}, {}, {}", + format_operand("dst"sv, m_dst, executable), + format_operand("base"sv, m_base, executable), + executable.identifier_table->get(m_property)); } ByteString HasPrivateId::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("HasPrivateId {} ({})", m_property, executable.identifier_table->get(m_property)); + return ByteString::formatted("HasPrivateId {}, {}, {}", + format_operand("dst"sv, m_dst, executable), + format_operand("base"sv, m_base, executable), + executable.identifier_table->get(m_property)); } ByteString DeleteById::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("DeleteById {} ({})", m_property, executable.identifier_table->get(m_property)); + return ByteString::formatted("DeleteById {}, {}, {}", + format_operand("dst"sv, m_dst, executable), + format_operand("base"sv, m_base, executable), + executable.identifier_table->get(m_property)); } ByteString DeleteByIdWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("DeleteByIdWithThis {} ({}) this_value:{}", m_property, executable.identifier_table->get(m_property), m_this_value); + return ByteString::formatted("DeleteByIdWithThis {}, {}, {}, {}", + format_operand("dst"sv, m_dst, executable), + format_operand("base"sv, m_base, executable), + executable.identifier_table->get(m_property), + format_operand("this"sv, m_this_value, executable)); } ByteString Jump::to_byte_string_impl(Bytecode::Executable const&) const @@ -1548,25 +1630,31 @@ ByteString Jump::to_byte_string_impl(Bytecode::Executable const&) const return ByteString::formatted("Jump "); } -ByteString JumpConditional::to_byte_string_impl(Bytecode::Executable const&) const +ByteString JumpIf::to_byte_string_impl(Bytecode::Executable const& executable) const { auto true_string = m_true_target.has_value() ? ByteString::formatted("{}", *m_true_target) : ""; auto false_string = m_false_target.has_value() ? ByteString::formatted("{}", *m_false_target) : ""; - return ByteString::formatted("JumpConditional true:{} false:{}", true_string, false_string); + return ByteString::formatted("JumpIf {}, \033[32mtrue\033[0m:{} \033[32mfalse\033[0m:{}", + format_operand("condition"sv, m_condition, executable), + true_string, false_string); } -ByteString JumpNullish::to_byte_string_impl(Bytecode::Executable const&) const +ByteString JumpNullish::to_byte_string_impl(Bytecode::Executable const& executable) const { auto true_string = m_true_target.has_value() ? ByteString::formatted("{}", *m_true_target) : ""; auto false_string = m_false_target.has_value() ? ByteString::formatted("{}", *m_false_target) : ""; - return ByteString::formatted("JumpNullish null:{} nonnull:{}", true_string, false_string); + return ByteString::formatted("JumpNullish {}, null:{} nonnull:{}", + format_operand("condition"sv, m_condition, executable), + true_string, false_string); } -ByteString JumpUndefined::to_byte_string_impl(Bytecode::Executable const&) const +ByteString JumpUndefined::to_byte_string_impl(Bytecode::Executable const& executable) const { auto true_string = m_true_target.has_value() ? ByteString::formatted("{}", *m_true_target) : ""; auto false_string = m_false_target.has_value() ? ByteString::formatted("{}", *m_false_target) : ""; - return ByteString::formatted("JumpUndefined undefined:{} not undefined:{}", true_string, false_string); + return ByteString::formatted("JumpUndefined {}, undefined:{} defined:{}", + format_operand("condition"sv, m_condition, executable), + true_string, false_string); } static StringView call_type_to_string(CallType type) @@ -1585,57 +1673,94 @@ static StringView call_type_to_string(CallType type) ByteString Call::to_byte_string_impl(Bytecode::Executable const& executable) const { auto type = call_type_to_string(m_type); - if (m_builtin.has_value()) - return ByteString::formatted("Call{} callee:{}, this:{}, first_arg:{} (builtin {})", type, m_callee, m_this_value, m_first_argument, m_builtin.value()); - if (m_expression_string.has_value()) - return ByteString::formatted("Call{} callee:{}, this:{}, first_arg:{} ({})", type, m_callee, m_this_value, m_first_argument, executable.get_string(m_expression_string.value())); - return ByteString::formatted("Call{} callee:{}, this:{}, first_arg:{}", type, m_callee, m_first_argument, m_this_value); + if (m_builtin.has_value()) { + return ByteString::formatted("Call{} {}, {}, {}, first_arg:{} (builtin {})", + type, + format_operand("dst"sv, m_dst, executable), + format_operand("callee"sv, m_callee, executable), + format_operand("this"sv, m_this_value, executable), + m_first_argument, + m_builtin.value()); + } + if (m_expression_string.has_value()) { + return ByteString::formatted("Call{} {}, {}, {}, first_arg:{} ({})", + type, + format_operand("dst"sv, m_dst, executable), + format_operand("callee"sv, m_callee, executable), + format_operand("this"sv, m_this_value, executable), + m_first_argument, + executable.get_string(m_expression_string.value())); + } + return ByteString::formatted("Call{} {}, {}, {}, first_arg:{}", + type, + format_operand("dst"sv, m_dst, executable), + format_operand("callee"sv, m_callee, executable), + format_operand("this"sv, m_this_value, executable), + m_first_argument); } ByteString CallWithArgumentArray::to_byte_string_impl(Bytecode::Executable const& executable) const { auto type = call_type_to_string(m_type); + StringBuilder builder; + builder.appendff("CallWithArgumentArray{} {}, {}, {}, {}", + type, + format_operand("dst"sv, m_dst, executable), + format_operand("callee"sv, m_callee, executable), + format_operand("this"sv, m_this_value, executable), + format_operand("arguments"sv, m_arguments, executable)); + if (m_expression_string.has_value()) - return ByteString::formatted("CallWithArgumentArray{} callee:{}, this:{}, arguments:[...acc] ({})", type, m_callee, m_this_value, executable.get_string(m_expression_string.value())); - return ByteString::formatted("CallWithArgumentArray{} callee:{}, this:{}, arguments:[...acc]", type, m_callee, m_this_value); + builder.appendff(" ({})", executable.get_string(m_expression_string.value())); + return builder.to_byte_string(); } -ByteString SuperCallWithArgumentArray::to_byte_string_impl(Bytecode::Executable const&) const +ByteString SuperCallWithArgumentArray::to_byte_string_impl(Bytecode::Executable const& executable) const { - return "SuperCallWithArgumentArray arguments:[...acc]"sv; + return ByteString::formatted("SuperCallWithArgumentArray {}, {}", + format_operand("dst"sv, m_dst, executable), + format_operand("arguments"sv, m_arguments, executable)); } -ByteString NewFunction::to_byte_string_impl(Bytecode::Executable const&) const +ByteString NewFunction::to_byte_string_impl(Bytecode::Executable const& executable) const { StringBuilder builder; - builder.append("NewFunction"sv); + builder.appendff("NewFunction {}", + format_operand("dst"sv, m_dst, executable)); if (m_function_node.has_name()) builder.appendff(" name:{}"sv, m_function_node.name()); if (m_lhs_name.has_value()) - builder.appendff(" lhs_name:{}"sv, m_lhs_name.value()); + builder.appendff(" lhs_name:{}"sv, executable.get_identifier(m_lhs_name.value())); if (m_home_object.has_value()) - builder.appendff(" home_object:{}"sv, m_home_object.value()); + builder.appendff(", {}"sv, format_operand("home_object"sv, m_home_object.value(), executable)); return builder.to_byte_string(); } -ByteString NewClass::to_byte_string_impl(Bytecode::Executable const&) const +ByteString NewClass::to_byte_string_impl(Bytecode::Executable const& executable) const { StringBuilder builder; auto name = m_class_expression.name(); - builder.appendff("NewClass '{}'"sv, name.is_null() ? ""sv : name); + builder.appendff("NewClass {}", + format_operand("dst"sv, m_dst, executable)); + if (m_super_class.has_value()) + builder.appendff(", {}", format_operand("super_class"sv, *m_super_class, executable)); + if (!name.is_empty()) + builder.appendff(", {}", name); if (m_lhs_name.has_value()) - builder.appendff(" lhs_name:{}"sv, m_lhs_name.value()); + builder.appendff(", lhs_name:{}"sv, m_lhs_name.value()); return builder.to_byte_string(); } -ByteString Return::to_byte_string_impl(Bytecode::Executable const&) const +ByteString Return::to_byte_string_impl(Bytecode::Executable const& executable) const { + if (m_value.has_value()) + return ByteString::formatted("Return {}", format_operand("value"sv, m_value.value(), executable)); return "Return"; } -ByteString Increment::to_byte_string_impl(Bytecode::Executable const&) const +ByteString Increment::to_byte_string_impl(Bytecode::Executable const& executable) const { - return "Increment"; + return ByteString::formatted("Increment {}", format_operand("dst"sv, m_dst, executable)); } ByteString Decrement::to_byte_string_impl(Bytecode::Executable const&) const @@ -1643,19 +1768,28 @@ ByteString Decrement::to_byte_string_impl(Bytecode::Executable const&) const return "Decrement"; } -ByteString Throw::to_byte_string_impl(Bytecode::Executable const&) const +ByteString Throw::to_byte_string_impl(Bytecode::Executable const& executable) const { - return "Throw"; + return ByteString::formatted("Throw {}", + format_operand("src"sv, m_src, executable)); } -ByteString ThrowIfNotObject::to_byte_string_impl(Bytecode::Executable const&) const +ByteString ThrowIfNotObject::to_byte_string_impl(Bytecode::Executable const& executable) const { - return "ThrowIfNotObject"; + return ByteString::formatted("ThrowIfNotObject {}", + format_operand("src"sv, m_src, executable)); } -ByteString ThrowIfNullish::to_byte_string_impl(Bytecode::Executable const&) const +ByteString ThrowIfNullish::to_byte_string_impl(Bytecode::Executable const& executable) const { - return "ThrowIfNullish"; + return ByteString::formatted("ThrowIfNullish {}", + format_operand("src"sv, m_src, executable)); +} + +ByteString ThrowIfTDZ::to_byte_string_impl(Bytecode::Executable const& executable) const +{ + return ByteString::formatted("ThrowIfTDZ {}", + format_operand("src"sv, m_src, executable)); } ByteString EnterUnwindContext::to_byte_string_impl(Bytecode::Executable const&) const @@ -1683,122 +1817,168 @@ ByteString ContinuePendingUnwind::to_byte_string_impl(Bytecode::Executable const return ByteString::formatted("ContinuePendingUnwind resume:{}", m_resume_target); } -ByteString Yield::to_byte_string_impl(Bytecode::Executable const&) const +ByteString Yield::to_byte_string_impl(Bytecode::Executable const& executable) const { - if (m_continuation_label.has_value()) - return ByteString::formatted("Yield continuation:@{}", m_continuation_label->block().name()); - return ByteString::formatted("Yield return"); + if (m_continuation_label.has_value()) { + return ByteString::formatted("Yield continuation:@{}, {}", + m_continuation_label->block().name(), + format_operand("value"sv, m_value, executable)); + } + return ByteString::formatted("Yield return {}", + format_operand("value"sv, m_value, executable)); } -ByteString Await::to_byte_string_impl(Bytecode::Executable const&) const +ByteString Await::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("Await continuation:@{}", m_continuation_label.block().name()); + return ByteString::formatted("Await {}, continuation:@{}", + format_operand("argument"sv, m_argument, executable), + m_continuation_label.block().name()); } -ByteString GetByValue::to_byte_string_impl(Bytecode::Executable const&) const +ByteString GetByValue::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("GetByValue base:{}", m_base); + return ByteString::formatted("GetByValue {}, {}, {}", + format_operand("dst"sv, m_dst, executable), + format_operand("base"sv, m_base, executable), + format_operand("property"sv, m_property, executable)); } -ByteString GetByValueWithThis::to_byte_string_impl(Bytecode::Executable const&) const +ByteString GetByValueWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("GetByValueWithThis base:{} this_value:{}", m_base, m_this_value); + return ByteString::formatted("GetByValueWithThis {}, {}, {}", + format_operand("dst"sv, m_dst, executable), + format_operand("base"sv, m_base, executable), + format_operand("property"sv, m_property, executable)); } -ByteString PutByValue::to_byte_string_impl(Bytecode::Executable const&) const +ByteString PutByValue::to_byte_string_impl(Bytecode::Executable const& executable) const { auto kind = property_kind_to_string(m_kind); - return ByteString::formatted("PutByValue kind:{} base:{}, property:{}", kind, m_base, m_property); + return ByteString::formatted("PutByValue {}, {}, {}, kind:{}", + format_operand("base"sv, m_base, executable), + format_operand("property"sv, m_property, executable), + format_operand("src"sv, m_src, executable), + kind); } -ByteString PutByValueWithThis::to_byte_string_impl(Bytecode::Executable const&) const +ByteString PutByValueWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const { auto kind = property_kind_to_string(m_kind); - return ByteString::formatted("PutByValueWithThis kind:{} base:{}, property:{} this_value:{}", kind, m_base, m_property, m_this_value); + return ByteString::formatted("PutByValueWithThis {}, {}, {}, {}, kind:{}", + format_operand("base"sv, m_base, executable), + format_operand("property"sv, m_property, executable), + format_operand("src"sv, m_src, executable), + format_operand("this"sv, m_this_value, executable), + kind); } -ByteString DeleteByValue::to_byte_string_impl(Bytecode::Executable const&) const +ByteString DeleteByValue::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("DeleteByValue base:{}", m_base); + return ByteString::formatted("DeleteByValue {}, {}, {}", + format_operand("dst"sv, dst(), executable), + format_operand("base"sv, m_base, executable), + format_operand("property"sv, m_property, executable)); } -ByteString DeleteByValueWithThis::to_byte_string_impl(Bytecode::Executable const&) const +ByteString DeleteByValueWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("DeleteByValueWithThis base:{} this_value:{}", m_base, m_this_value); + return ByteString::formatted("DeleteByValueWithThis {}, {}, {}, {}", + format_operand("dst"sv, dst(), executable), + format_operand("base"sv, m_base, executable), + format_operand("property"sv, m_property, executable), + format_operand("this"sv, m_this_value, executable)); } -ByteString GetIterator::to_byte_string_impl(Executable const&) const +ByteString GetIterator::to_byte_string_impl(Executable const& executable) const { auto hint = m_hint == IteratorHint::Sync ? "sync" : "async"; - return ByteString::formatted("GetIterator hint:{}", hint); + return ByteString::formatted("GetIterator {}, {}, hint:{}", + format_operand("dst"sv, m_dst, executable), + format_operand("iterable"sv, m_iterable, executable), + hint); } ByteString GetMethod::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("GetMethod {} ({})", m_property, executable.identifier_table->get(m_property)); + return ByteString::formatted("GetMethod {}, {}, {}", + format_operand("dst"sv, m_dst, executable), + format_operand("object"sv, m_object, executable), + executable.identifier_table->get(m_property)); } -ByteString GetObjectPropertyIterator::to_byte_string_impl(Bytecode::Executable const&) const +ByteString GetObjectPropertyIterator::to_byte_string_impl(Bytecode::Executable const& executable) const { - return "GetObjectPropertyIterator"; + return ByteString::formatted("GetObjectPropertyIterator {}, {}", + format_operand("dst"sv, dst(), executable), + format_operand("object"sv, object(), executable)); } -ByteString IteratorClose::to_byte_string_impl(Bytecode::Executable const&) const +ByteString IteratorClose::to_byte_string_impl(Bytecode::Executable const& executable) const { if (!m_completion_value.has_value()) - return ByteString::formatted("IteratorClose completion_type={} completion_value=", to_underlying(m_completion_type)); + return ByteString::formatted("IteratorClose {}, completion_type={} completion_value=", + format_operand("iterator_record"sv, m_iterator_record, executable), + to_underlying(m_completion_type)); auto completion_value_string = m_completion_value->to_string_without_side_effects(); - return ByteString::formatted("IteratorClose completion_type={} completion_value={}", to_underlying(m_completion_type), completion_value_string); + return ByteString::formatted("IteratorClose {}, completion_type={} completion_value={}", + format_operand("iterator_record"sv, m_iterator_record, executable), + to_underlying(m_completion_type), completion_value_string); } -ByteString AsyncIteratorClose::to_byte_string_impl(Bytecode::Executable const&) const +ByteString AsyncIteratorClose::to_byte_string_impl(Bytecode::Executable const& executable) const { - if (!m_completion_value.has_value()) - return ByteString::formatted("AsyncIteratorClose completion_type={} completion_value=", to_underlying(m_completion_type)); + if (!m_completion_value.has_value()) { + return ByteString::formatted("AsyncIteratorClose {}, completion_type:{} completion_value:", + format_operand("iterator_record"sv, m_iterator_record, executable), + to_underlying(m_completion_type)); + } - auto completion_value_string = m_completion_value->to_string_without_side_effects(); - return ByteString::formatted("AsyncIteratorClose completion_type={} completion_value={}", to_underlying(m_completion_type), completion_value_string); + return ByteString::formatted("AsyncIteratorClose {}, completion_type:{}, completion_value:{}", + format_operand("iterator_record"sv, m_iterator_record, executable), + to_underlying(m_completion_type), m_completion_value); } -ByteString IteratorNext::to_byte_string_impl(Executable const&) const +ByteString IteratorNext::to_byte_string_impl(Executable const& executable) const { - return "IteratorNext"; + return ByteString::formatted("IteratorNext {}, {}", + format_operand("dst"sv, m_dst, executable), + format_operand("iterator_record"sv, m_iterator_record, executable)); } -ByteString ResolveThisBinding::to_byte_string_impl(Bytecode::Executable const&) const +ByteString ResolveThisBinding::to_byte_string_impl(Bytecode::Executable const& executable) const { - return "ResolveThisBinding"sv; + return ByteString::formatted("ResolveThisBinding {}", format_operand("dst"sv, m_dst, executable)); } -ByteString ResolveSuperBase::to_byte_string_impl(Bytecode::Executable const&) const +ByteString ResolveSuperBase::to_byte_string_impl(Bytecode::Executable const& executable) const { - return "ResolveSuperBase"sv; + return ByteString::formatted("ResolveSuperBase {}", + format_operand("dst"sv, m_dst, executable)); } -ByteString GetNewTarget::to_byte_string_impl(Bytecode::Executable const&) const +ByteString GetNewTarget::to_byte_string_impl(Bytecode::Executable const& executable) const { - return "GetNewTarget"sv; + return ByteString::formatted("GetNewTarget {}", format_operand("dst"sv, m_dst, executable)); } -ByteString GetImportMeta::to_byte_string_impl(Bytecode::Executable const&) const +ByteString GetImportMeta::to_byte_string_impl(Bytecode::Executable const& executable) const { - return "GetImportMeta"sv; + return ByteString::formatted("GetImportMeta {}", format_operand("dst"sv, m_dst, executable)); } ByteString TypeofVariable::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("TypeofVariable {} ({})", m_identifier, executable.identifier_table->get(m_identifier)); + return ByteString::formatted("TypeofVariable {}, {}", + format_operand("dst"sv, m_dst, executable), + executable.identifier_table->get(m_identifier)); } -ByteString TypeofLocal::to_byte_string_impl(Bytecode::Executable const&) const +ByteString ToNumeric::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("TypeofLocal {}", m_index); -} - -ByteString ToNumeric::to_byte_string_impl(Bytecode::Executable const&) const -{ - return "ToNumeric"sv; + return ByteString::formatted("ToNumeric {}, {}", + format_operand("dst"sv, m_dst, executable), + format_operand("src"sv, m_src, executable)); } ByteString BlockDeclarationInstantiation::to_byte_string_impl(Bytecode::Executable const&) const @@ -1806,24 +1986,43 @@ ByteString BlockDeclarationInstantiation::to_byte_string_impl(Bytecode::Executab return "BlockDeclarationInstantiation"sv; } -ByteString ImportCall::to_byte_string_impl(Bytecode::Executable const&) const +ByteString ImportCall::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("ImportCall specifier:{} options:{}"sv, m_specifier, m_options); + return ByteString::formatted("ImportCall {}, {}, {}", + format_operand("dst"sv, m_dst, executable), + format_operand("specifier"sv, m_specifier, executable), + format_operand("options"sv, m_options, executable)); } -ByteString Catch::to_byte_string_impl(Bytecode::Executable const&) const +ByteString Catch::to_byte_string_impl(Bytecode::Executable const& executable) const { - return "Catch"sv; + return ByteString::formatted("Catch {}", + format_operand("dst"sv, m_dst, executable)); } -ByteString GetObjectFromIteratorRecord::to_byte_string_impl(Bytecode::Executable const&) const +ByteString GetObjectFromIteratorRecord::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("GetObjectFromIteratorRecord object:{} <- iterator_record:{}", m_object, m_iterator_record); + return ByteString::formatted("GetObjectFromIteratorRecord {}, {}", + format_operand("object"sv, m_object, executable), + format_operand("iterator_record"sv, m_iterator_record, executable)); } -ByteString GetNextMethodFromIteratorRecord::to_byte_string_impl(Bytecode::Executable const&) const +ByteString GetNextMethodFromIteratorRecord::to_byte_string_impl(Bytecode::Executable const& executable) const { - return ByteString::formatted("GetNextMethodFromIteratorRecord next_method:{} <- iterator_record:{}", m_next_method, m_iterator_record); + return ByteString::formatted("GetNextMethodFromIteratorRecord {}, {}", + format_operand("next_method"sv, m_next_method, executable), + format_operand("iterator_record"sv, m_iterator_record, executable)); +} + +ByteString End::to_byte_string_impl(Bytecode::Executable const& executable) const +{ + return ByteString::formatted("End {}", format_operand("value"sv, m_value, executable)); +} + +ByteString Dump::to_byte_string_impl(Bytecode::Executable const& executable) const +{ + return ByteString::formatted("Dump '{}', {}", m_text, + format_operand("value"sv, m_value, executable)); } } diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.h b/Userland/Libraries/LibJS/Bytecode/Interpreter.h index 595cd3437d..0ef2185eaf 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.h +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.h @@ -89,7 +89,7 @@ public: void enter_unwind_context(); void leave_unwind_context(); - void catch_exception(); + void catch_exception(Operand dst); void enter_object_environment(Object&); @@ -130,6 +130,6 @@ private: extern bool g_dump_bytecode; -ThrowCompletionOr> compile(VM&, ASTNode const& no, JS::FunctionKind kind, DeprecatedFlyString const& name); +ThrowCompletionOr> compile(VM&, ASTNode const&, ReadonlySpan, JS::FunctionKind kind, DeprecatedFlyString const& name); } diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index effd121605..3843964d64 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -30,10 +31,11 @@ class FunctionExpression; namespace JS::Bytecode::Op { -class Load final : public Instruction { +class Mov final : public Instruction { public: - explicit Load(Register src) - : Instruction(Type::Load, sizeof(*this)) + Mov(Operand dst, Operand src) + : Instruction(Type::Mov, sizeof(*this)) + , m_dst(dst) , m_src(src) { } @@ -41,44 +43,12 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; - Register src() const { return m_src; } + Operand dst() const { return m_dst; } + Operand src() const { return m_src; } private: - Register m_src; -}; - -class LoadImmediate final : public Instruction { -public: - explicit LoadImmediate(Value value) - : Instruction(Type::LoadImmediate, sizeof(*this)) - , m_value(value) - { - } - - ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; - ByteString to_byte_string_impl(Bytecode::Executable const&) const; - - Value value() const { return m_value; } - -private: - Value m_value; -}; - -class Store final : public Instruction { -public: - explicit Store(Register dst) - : Instruction(Type::Store, sizeof(*this)) - , m_dst(dst) - { - } - - ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; - ByteString to_byte_string_impl(Bytecode::Executable const&) const; - - Register dst() const { return m_dst; } - -private: - Register m_dst; + Operand m_dst; + Operand m_src; }; #define JS_ENUMERATE_COMMON_BINARY_OPS(O) \ @@ -108,19 +78,25 @@ private: #define JS_DECLARE_COMMON_BINARY_OP(OpTitleCase, op_snake_case) \ class OpTitleCase final : public Instruction { \ public: \ - explicit OpTitleCase(Register lhs_reg) \ + explicit OpTitleCase(Operand dst, Operand lhs, Operand rhs) \ : Instruction(Type::OpTitleCase, sizeof(*this)) \ - , m_lhs_reg(lhs_reg) \ + , m_dst(dst) \ + , m_lhs(lhs) \ + , m_rhs(rhs) \ { \ } \ \ ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; \ ByteString to_byte_string_impl(Bytecode::Executable const&) const; \ \ - Register lhs() const { return m_lhs_reg; } \ + Operand dst() const { return m_dst; } \ + Operand lhs() const { return m_lhs; } \ + Operand rhs() const { return m_rhs; } \ \ private: \ - Register m_lhs_reg; \ + Operand m_dst; \ + Operand m_lhs; \ + Operand m_rhs; \ }; JS_ENUMERATE_COMMON_BINARY_OPS(JS_DECLARE_COMMON_BINARY_OP) @@ -136,13 +112,22 @@ JS_ENUMERATE_COMMON_BINARY_OPS(JS_DECLARE_COMMON_BINARY_OP) #define JS_DECLARE_COMMON_UNARY_OP(OpTitleCase, op_snake_case) \ class OpTitleCase final : public Instruction { \ public: \ - OpTitleCase() \ + OpTitleCase(Operand dst, Operand src) \ : Instruction(Type::OpTitleCase, sizeof(*this)) \ + , m_dst(dst) \ + , m_src(src) \ { \ } \ \ ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; \ ByteString to_byte_string_impl(Bytecode::Executable const&) const; \ + \ + Operand dst() const { return m_dst; } \ + Operand src() const { return m_src; } \ + \ + private: \ + Operand m_dst; \ + Operand m_src; \ }; JS_ENUMERATE_COMMON_UNARY_OPS(JS_DECLARE_COMMON_UNARY_OP) @@ -150,8 +135,9 @@ JS_ENUMERATE_COMMON_UNARY_OPS(JS_DECLARE_COMMON_UNARY_OP) class NewString final : public Instruction { public: - explicit NewString(StringTableIndex string) + NewString(Operand dst, StringTableIndex string) : Instruction(Type::NewString, sizeof(*this)) + , m_dst(dst) , m_string(string) { } @@ -159,27 +145,36 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; + Operand dst() const { return m_dst; } StringTableIndex index() const { return m_string; } private: + Operand m_dst; StringTableIndex m_string; }; class NewObject final : public Instruction { public: - NewObject() + explicit NewObject(Operand dst) : Instruction(Type::NewObject, sizeof(*this)) + , m_dst(dst) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; + + Operand dst() const { return m_dst; } + +private: + Operand m_dst; }; class NewRegExp final : public Instruction { public: - NewRegExp(StringTableIndex source_index, StringTableIndex flags_index, RegexTableIndex regex_index) + NewRegExp(Operand dst, StringTableIndex source_index, StringTableIndex flags_index, RegexTableIndex regex_index) : Instruction(Type::NewRegExp, sizeof(*this)) + , m_dst(dst) , m_source_index(source_index) , m_flags_index(flags_index) , m_regex_index(regex_index) @@ -189,11 +184,13 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; + Operand dst() const { return m_dst; } StringTableIndex source_index() const { return m_source_index; } StringTableIndex flags_index() const { return m_flags_index; } RegexTableIndex regex_index() const { return m_regex_index; } private: + Operand m_dst; StringTableIndex m_source_index; StringTableIndex m_flags_index; RegexTableIndex m_regex_index; @@ -205,8 +202,9 @@ private: #define JS_DECLARE_NEW_BUILTIN_ERROR_OP(ErrorName) \ class New##ErrorName final : public Instruction { \ public: \ - explicit New##ErrorName(StringTableIndex error_string) \ + New##ErrorName(Operand dst, StringTableIndex error_string) \ : Instruction(Type::New##ErrorName, sizeof(*this)) \ + , m_dst(dst) \ , m_error_string(error_string) \ { \ } \ @@ -214,9 +212,11 @@ private: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; \ ByteString to_byte_string_impl(Bytecode::Executable const&) const; \ \ + Operand dst() const { return m_dst; } \ StringTableIndex error_string() const { return m_error_string; } \ \ private: \ + Operand m_dst; \ StringTableIndex m_error_string; \ }; @@ -226,8 +226,9 @@ JS_ENUMERATE_NEW_BUILTIN_ERROR_OPS(JS_DECLARE_NEW_BUILTIN_ERROR_OP) // NOTE: This instruction is variable-width depending on the number of excluded names class CopyObjectExcludingProperties final : public Instruction { public: - CopyObjectExcludingProperties(Register from_object, Vector const& excluded_names) + CopyObjectExcludingProperties(Operand dst, Operand from_object, Vector const& excluded_names) : Instruction(Type::CopyObjectExcludingProperties, length_impl(excluded_names.size())) + , m_dst(dst) , m_from_object(from_object) , m_excluded_names_count(excluded_names.size()) { @@ -240,23 +241,26 @@ public: size_t length_impl(size_t excluded_names_count) const { - return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Register) * excluded_names_count); + return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Operand) * excluded_names_count); } - Register from_object() const { return m_from_object; } + Operand dst() const { return m_dst; } + Operand from_object() const { return m_from_object; } size_t excluded_names_count() const { return m_excluded_names_count; } - Register const* excluded_names() const { return m_excluded_names; } + Operand const* excluded_names() const { return m_excluded_names; } private: - Register m_from_object; + Operand m_dst; + Operand m_from_object; size_t m_excluded_names_count { 0 }; - Register m_excluded_names[]; + Operand m_excluded_names[]; }; class NewBigInt final : public Instruction { public: - explicit NewBigInt(Crypto::SignedBigInteger bigint) + NewBigInt(Operand dst, Crypto::SignedBigInteger bigint) : Instruction(Type::NewBigInt, sizeof(*this)) + , m_dst(dst) , m_bigint(move(bigint)) { } @@ -264,23 +268,27 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; + Operand dst() const { return m_dst; } Crypto::SignedBigInteger const& bigint() const { return m_bigint; } private: + Operand m_dst; Crypto::SignedBigInteger m_bigint; }; // NOTE: This instruction is variable-width depending on the number of elements! class NewArray final : public Instruction { public: - NewArray() + explicit NewArray(Operand dst) : Instruction(Type::NewArray, length_impl(0)) + , m_dst(dst) , m_element_count(0) { } - explicit NewArray(AK::Array const& elements_range) + NewArray(Operand dst, AK::Array const& elements_range) : Instruction(Type::NewArray, length_impl(elements_range[1].index() - elements_range[0].index() + 1)) + , m_dst(dst) , m_element_count(elements_range[1].index() - elements_range[0].index() + 1) { m_elements[0] = elements_range[0]; @@ -290,18 +298,20 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; + Operand dst() const { return m_dst; } + size_t length_impl(size_t element_count) const { - return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Register) * (element_count == 0 ? 0 : 2)); + return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Operand) * (element_count == 0 ? 0 : 2)); } - Register start() const + Operand start() const { VERIFY(m_element_count); return m_elements[0]; } - Register end() const + Operand end() const { VERIFY(m_element_count); return m_elements[1]; @@ -310,14 +320,16 @@ public: size_t element_count() const { return m_element_count; } private: + Operand m_dst; size_t m_element_count { 0 }; - Register m_elements[]; + Operand m_elements[]; }; class NewPrimitiveArray final : public Instruction { public: - explicit NewPrimitiveArray(FixedArray values) + NewPrimitiveArray(Operand dst, FixedArray values) : Instruction(Type::NewPrimitiveArray, sizeof(*this)) + , m_dst(dst) , m_values(move(values)) { } @@ -325,17 +337,20 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; + Operand dst() const { return m_dst; } ReadonlySpan values() const { return m_values.span(); } private: + Operand m_dst; FixedArray m_values; }; -class Append final : public Instruction { +class ArrayAppend final : public Instruction { public: - Append(Register lhs, bool is_spread) - : Instruction(Type::Append, sizeof(*this)) - , m_lhs(lhs) + ArrayAppend(Operand dst, Operand src, bool is_spread) + : Instruction(Type::ArrayAppend, sizeof(*this)) + , m_dst(dst) + , m_src(src) , m_is_spread(is_spread) { } @@ -343,18 +358,21 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; - Register lhs() const { return m_lhs; } + Operand dst() const { return m_dst; } + Operand src() const { return m_src; } bool is_spread() const { return m_is_spread; } private: - Register m_lhs; + Operand m_dst; + Operand m_src; bool m_is_spread = false; }; class ImportCall final : public Instruction { public: - ImportCall(Register specifier, Register options) + ImportCall(Operand dst, Operand specifier, Operand options) : Instruction(Type::ImportCall, sizeof(*this)) + , m_dst(dst) , m_specifier(specifier) , m_options(options) { @@ -363,40 +381,54 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; - Register specifier() const { return m_specifier; } - Register options() const { return m_options; } + Operand dst() const { return m_dst; } + Operand specifier() const { return m_specifier; } + Operand options() const { return m_options; } private: - Register m_specifier; - Register m_options; + Operand m_dst; + Operand m_specifier; + Operand m_options; }; class IteratorToArray final : public Instruction { public: - IteratorToArray() + explicit IteratorToArray(Operand dst, Operand iterator) : Instruction(Type::IteratorToArray, sizeof(*this)) + , m_dst(dst) + , m_iterator(iterator) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; + + Operand dst() const { return m_dst; } + Operand iterator() const { return m_iterator; } + +private: + Operand m_dst; + Operand m_iterator; }; class ConcatString final : public Instruction { public: - explicit ConcatString(Register lhs) + explicit ConcatString(Operand dst, Operand src) : Instruction(Type::ConcatString, sizeof(*this)) - , m_lhs(lhs) + , m_dst(dst) + , m_src(src) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; - Register lhs() const { return m_lhs; } + Operand dst() const { return m_dst; } + Operand src() const { return m_src; } private: - Register m_lhs; + Operand m_dst; + Operand m_src; }; enum class EnvironmentMode { @@ -417,24 +449,36 @@ public: class EnterObjectEnvironment final : public Instruction { public: - explicit EnterObjectEnvironment() + explicit EnterObjectEnvironment(Operand object) : Instruction(Type::EnterObjectEnvironment, sizeof(*this)) + , m_object(object) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; + + Operand object() const { return m_object; } + +private: + Operand m_object; }; class Catch final : public Instruction { public: - explicit Catch() + explicit Catch(Operand dst) : Instruction(Type::Catch, sizeof(*this)) + , m_dst(dst) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; + + Operand dst() const { return m_dst; } + +private: + Operand m_dst; }; class CreateVariable final : public Instruction { @@ -472,9 +516,10 @@ public: Initialize, Set, }; - explicit SetVariable(IdentifierTableIndex identifier, u32 cache_index, InitializationMode initialization_mode = InitializationMode::Set, EnvironmentMode mode = EnvironmentMode::Lexical) + explicit SetVariable(IdentifierTableIndex identifier, Operand src, u32 cache_index, InitializationMode initialization_mode = InitializationMode::Set, EnvironmentMode mode = EnvironmentMode::Lexical) : Instruction(Type::SetVariable, sizeof(*this)) , m_identifier(identifier) + , m_src(src) , m_mode(mode) , m_initialization_mode(initialization_mode) , m_cache_index(cache_index) @@ -485,12 +530,14 @@ public: ByteString to_byte_string_impl(Bytecode::Executable const&) const; IdentifierTableIndex identifier() const { return m_identifier; } + Operand src() const { return m_src; } EnvironmentMode mode() const { return m_mode; } InitializationMode initialization_mode() const { return m_initialization_mode; } u32 cache_index() const { return m_cache_index; } private: IdentifierTableIndex m_identifier; + Operand m_src; EnvironmentMode m_mode; InitializationMode m_initialization_mode { InitializationMode::Set }; u32 m_cache_index { 0 }; @@ -498,9 +545,10 @@ private: class SetLocal final : public Instruction { public: - explicit SetLocal(size_t index) + SetLocal(size_t index, Operand src) : Instruction(Type::SetLocal, sizeof(*this)) , m_index(index) + , m_src(src) { } @@ -508,18 +556,21 @@ public: ByteString to_byte_string_impl(Bytecode::Executable const&) const; size_t index() const { return m_index; } + Operand dst() const { return Operand(Operand::Type::Local, m_index); } + Operand src() const { return m_src; } private: size_t m_index; + Operand m_src; }; class GetCalleeAndThisFromEnvironment final : public Instruction { public: - explicit GetCalleeAndThisFromEnvironment(IdentifierTableIndex identifier, Register callee_reg, Register this_reg, u32 cache_index) + explicit GetCalleeAndThisFromEnvironment(Operand callee, Operand this_value, IdentifierTableIndex identifier, u32 cache_index) : Instruction(Type::GetCalleeAndThisFromEnvironment, sizeof(*this)) , m_identifier(identifier) - , m_callee_reg(callee_reg) - , m_this_reg(this_reg) + , m_callee(callee) + , m_this_value(this_value) , m_cache_index(cache_index) { } @@ -529,20 +580,21 @@ public: IdentifierTableIndex identifier() const { return m_identifier; } u32 cache_index() const { return m_cache_index; } - Register callee() const { return m_callee_reg; } - Register this_() const { return m_this_reg; } + Operand callee() const { return m_callee; } + Operand this_() const { return m_this_value; } private: IdentifierTableIndex m_identifier; - Register m_callee_reg; - Register m_this_reg; + Operand m_callee; + Operand m_this_value; u32 m_cache_index { 0 }; }; class GetVariable final : public Instruction { public: - explicit GetVariable(IdentifierTableIndex identifier, u32 cache_index) + explicit GetVariable(Operand dst, IdentifierTableIndex identifier, u32 cache_index) : Instruction(Type::GetVariable, sizeof(*this)) + , m_dst(dst) , m_identifier(identifier) , m_cache_index(cache_index) { @@ -551,18 +603,21 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; + Operand dst() const { return m_dst; } IdentifierTableIndex identifier() const { return m_identifier; } u32 cache_index() const { return m_cache_index; } private: + Operand m_dst; IdentifierTableIndex m_identifier; u32 m_cache_index { 0 }; }; class GetGlobal final : public Instruction { public: - explicit GetGlobal(IdentifierTableIndex identifier, u32 cache_index) + GetGlobal(Operand dst, IdentifierTableIndex identifier, u32 cache_index) : Instruction(Type::GetGlobal, sizeof(*this)) + , m_dst(dst) , m_identifier(identifier) , m_cache_index(cache_index) { @@ -571,35 +626,21 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; + Operand dst() const { return m_dst; } IdentifierTableIndex identifier() const { return m_identifier; } u32 cache_index() const { return m_cache_index; } private: + Operand m_dst; IdentifierTableIndex m_identifier; u32 m_cache_index { 0 }; }; -class GetLocal final : public Instruction { -public: - explicit GetLocal(size_t index) - : Instruction(Type::GetLocal, sizeof(*this)) - , m_index(index) - { - } - - ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; - ByteString to_byte_string_impl(Bytecode::Executable const&) const; - - size_t index() const { return m_index; } - -private: - size_t m_index; -}; - class DeleteVariable final : public Instruction { public: - explicit DeleteVariable(IdentifierTableIndex identifier) + explicit DeleteVariable(Operand dst, IdentifierTableIndex identifier) : Instruction(Type::DeleteVariable, sizeof(*this)) + , m_dst(dst) , m_identifier(identifier) { } @@ -607,16 +648,20 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; + Operand dst() const { return m_dst; } IdentifierTableIndex identifier() const { return m_identifier; } private: + Operand m_dst; IdentifierTableIndex m_identifier; }; class GetById final : public Instruction { public: - GetById(IdentifierTableIndex property, u32 cache_index) + GetById(Operand dst, Operand base, IdentifierTableIndex property, u32 cache_index) : Instruction(Type::GetById, sizeof(*this)) + , m_dst(dst) + , m_base(base) , m_property(property) , m_cache_index(cache_index) { @@ -625,18 +670,24 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; + Operand dst() const { return m_dst; } + Operand base() const { return m_base; } IdentifierTableIndex property() const { return m_property; } u32 cache_index() const { return m_cache_index; } private: + Operand m_dst; + Operand m_base; IdentifierTableIndex m_property; u32 m_cache_index { 0 }; }; class GetByIdWithThis final : public Instruction { public: - GetByIdWithThis(IdentifierTableIndex property, Register this_value, u32 cache_index) + GetByIdWithThis(Operand dst, Operand base, IdentifierTableIndex property, Operand this_value, u32 cache_index) : Instruction(Type::GetByIdWithThis, sizeof(*this)) + , m_dst(dst) + , m_base(base) , m_property(property) , m_this_value(this_value) , m_cache_index(cache_index) @@ -646,20 +697,26 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; + Operand dst() const { return m_dst; } + Operand base() const { return m_base; } IdentifierTableIndex property() const { return m_property; } - Register this_value() const { return m_this_value; } + Operand this_value() const { return m_this_value; } u32 cache_index() const { return m_cache_index; } private: + Operand m_dst; + Operand m_base; IdentifierTableIndex m_property; - Register m_this_value; + Operand m_this_value; u32 m_cache_index { 0 }; }; class GetPrivateById final : public Instruction { public: - explicit GetPrivateById(IdentifierTableIndex property) + explicit GetPrivateById(Operand dst, Operand base, IdentifierTableIndex property) : Instruction(Type::GetPrivateById, sizeof(*this)) + , m_dst(dst) + , m_base(base) , m_property(property) { } @@ -667,16 +724,22 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; + Operand dst() const { return m_dst; } + Operand base() const { return m_base; } IdentifierTableIndex property() const { return m_property; } private: + Operand m_dst; + Operand m_base; IdentifierTableIndex m_property; }; class HasPrivateId final : public Instruction { public: - explicit HasPrivateId(IdentifierTableIndex property) + HasPrivateId(Operand dst, Operand base, IdentifierTableIndex property) : Instruction(Type::HasPrivateId, sizeof(*this)) + , m_dst(dst) + , m_base(base) , m_property(property) { } @@ -684,9 +747,13 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; + Operand dst() const { return m_dst; } + Operand base() const { return m_base; } IdentifierTableIndex property() const { return m_property; } private: + Operand m_dst; + Operand m_base; IdentifierTableIndex m_property; }; @@ -701,10 +768,11 @@ enum class PropertyKind { class PutById final : public Instruction { public: - explicit PutById(Register base, IdentifierTableIndex property, PropertyKind kind, u32 cache_index) + explicit PutById(Operand base, IdentifierTableIndex property, Operand src, PropertyKind kind, u32 cache_index) : Instruction(Type::PutById, sizeof(*this)) , m_base(base) , m_property(property) + , m_src(src) , m_kind(kind) , m_cache_index(cache_index) { @@ -713,25 +781,28 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; - Register base() const { return m_base; } + Operand base() const { return m_base; } IdentifierTableIndex property() const { return m_property; } + Operand src() const { return m_src; } PropertyKind kind() const { return m_kind; } u32 cache_index() const { return m_cache_index; } private: - Register m_base; + Operand m_base; IdentifierTableIndex m_property; + Operand m_src; PropertyKind m_kind; u32 m_cache_index { 0 }; }; class PutByIdWithThis final : public Instruction { public: - PutByIdWithThis(Register base, Register this_value, IdentifierTableIndex property, PropertyKind kind, u32 cache_index) + PutByIdWithThis(Operand base, Operand this_value, IdentifierTableIndex property, Operand src, PropertyKind kind, u32 cache_index) : Instruction(Type::PutByIdWithThis, sizeof(*this)) , m_base(base) , m_this_value(this_value) , m_property(property) + , m_src(src) , m_kind(kind) , m_cache_index(cache_index) { @@ -740,26 +811,29 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; - Register base() const { return m_base; } - Register this_value() const { return m_this_value; } + Operand base() const { return m_base; } + Operand this_value() const { return m_this_value; } IdentifierTableIndex property() const { return m_property; } + Operand src() const { return m_src; } PropertyKind kind() const { return m_kind; } u32 cache_index() const { return m_cache_index; } private: - Register m_base; - Register m_this_value; + Operand m_base; + Operand m_this_value; IdentifierTableIndex m_property; + Operand m_src; PropertyKind m_kind; u32 m_cache_index { 0 }; }; class PutPrivateById final : public Instruction { public: - explicit PutPrivateById(Register base, IdentifierTableIndex property, PropertyKind kind = PropertyKind::KeyValue) + explicit PutPrivateById(Operand base, IdentifierTableIndex property, Operand src, PropertyKind kind = PropertyKind::KeyValue) : Instruction(Type::PutPrivateById, sizeof(*this)) , m_base(base) , m_property(property) + , m_src(src) , m_kind(kind) { } @@ -767,19 +841,23 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; - Register base() const { return m_base; } + Operand base() const { return m_base; } IdentifierTableIndex property() const { return m_property; } + Operand src() const { return m_src; } private: - Register m_base; + Operand m_base; IdentifierTableIndex m_property; + Operand m_src; PropertyKind m_kind; }; class DeleteById final : public Instruction { public: - explicit DeleteById(IdentifierTableIndex property) + explicit DeleteById(Operand dst, Operand base, IdentifierTableIndex property) : Instruction(Type::DeleteById, sizeof(*this)) + , m_dst(dst) + , m_base(base) , m_property(property) { } @@ -787,16 +865,22 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; + Operand dst() const { return m_dst; } + Operand base() const { return m_base; } IdentifierTableIndex property() const { return m_property; } private: + Operand m_dst; + Operand m_base; IdentifierTableIndex m_property; }; class DeleteByIdWithThis final : public Instruction { public: - DeleteByIdWithThis(Register this_value, IdentifierTableIndex property) + DeleteByIdWithThis(Operand dst, Operand base, Operand this_value, IdentifierTableIndex property) : Instruction(Type::DeleteByIdWithThis, sizeof(*this)) + , m_dst(dst) + , m_base(base) , m_this_value(this_value) , m_property(property) { @@ -805,36 +889,48 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; - Register this_value() const { return m_this_value; } + Operand dst() const { return m_dst; } + Operand base() const { return m_base; } + Operand this_value() const { return m_this_value; } IdentifierTableIndex property() const { return m_property; } private: - Register m_this_value; + Operand m_dst; + Operand m_base; + Operand m_this_value; IdentifierTableIndex m_property; }; class GetByValue final : public Instruction { public: - explicit GetByValue(Register base) + explicit GetByValue(Operand dst, Operand base, Operand property) : Instruction(Type::GetByValue, sizeof(*this)) + , m_dst(dst) , m_base(base) + , m_property(property) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; - Register base() const { return m_base; } + Operand dst() const { return m_dst; } + Operand base() const { return m_base; } + Operand property() const { return m_property; } private: - Register m_base; + Operand m_dst; + Operand m_base; + Operand m_property; }; class GetByValueWithThis final : public Instruction { public: - GetByValueWithThis(Register base, Register this_value) + GetByValueWithThis(Operand dst, Operand base, Operand property, Operand this_value) : Instruction(Type::GetByValueWithThis, sizeof(*this)) + , m_dst(dst) , m_base(base) + , m_property(property) , m_this_value(this_value) { } @@ -842,20 +938,25 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; - Register base() const { return m_base; } - Register this_value() const { return m_this_value; } + Operand dst() const { return m_dst; } + Operand base() const { return m_base; } + Operand property() const { return m_property; } + Operand this_value() const { return m_this_value; } private: - Register m_base; - Register m_this_value; + Operand m_dst; + Operand m_base; + Operand m_property; + Operand m_this_value; }; class PutByValue final : public Instruction { public: - PutByValue(Register base, Register property, PropertyKind kind = PropertyKind::KeyValue) + PutByValue(Operand base, Operand property, Operand src, PropertyKind kind = PropertyKind::KeyValue) : Instruction(Type::PutByValue, sizeof(*this)) , m_base(base) , m_property(property) + , m_src(src) , m_kind(kind) { } @@ -863,23 +964,26 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; - Register base() const { return m_base; } - Register property() const { return m_property; } + Operand base() const { return m_base; } + Operand property() const { return m_property; } + Operand src() const { return m_src; } PropertyKind kind() const { return m_kind; } private: - Register m_base; - Register m_property; + Operand m_base; + Operand m_property; + Operand m_src; PropertyKind m_kind; }; class PutByValueWithThis final : public Instruction { public: - PutByValueWithThis(Register base, Register property, Register this_value, PropertyKind kind = PropertyKind::KeyValue) + PutByValueWithThis(Operand base, Operand property, Operand this_value, Operand src, PropertyKind kind = PropertyKind::KeyValue) : Instruction(Type::PutByValueWithThis, sizeof(*this)) , m_base(base) , m_property(property) , m_this_value(this_value) + , m_src(src) , m_kind(kind) { } @@ -887,53 +991,67 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; - Register base() const { return m_base; } - Register property() const { return m_property; } - Register this_value() const { return m_this_value; } + Operand base() const { return m_base; } + Operand property() const { return m_property; } + Operand this_value() const { return m_this_value; } + Operand src() const { return m_src; } PropertyKind kind() const { return m_kind; } private: - Register m_base; - Register m_property; - Register m_this_value; + Operand m_base; + Operand m_property; + Operand m_this_value; + Operand m_src; PropertyKind m_kind; }; class DeleteByValue final : public Instruction { public: - DeleteByValue(Register base) + DeleteByValue(Operand dst, Operand base, Operand property) : Instruction(Type::DeleteByValue, sizeof(*this)) + , m_dst(dst) , m_base(base) + , m_property(property) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; - Register base() const { return m_base; } + Operand dst() const { return m_dst; } + Operand base() const { return m_base; } + Operand property() const { return m_property; } private: - Register m_base; + Operand m_dst; + Operand m_base; + Operand m_property; }; class DeleteByValueWithThis final : public Instruction { public: - DeleteByValueWithThis(Register base, Register this_value) + DeleteByValueWithThis(Operand dst, Operand base, Operand this_value, Operand property) : Instruction(Type::DeleteByValueWithThis, sizeof(*this)) + , m_dst(dst) , m_base(base) , m_this_value(this_value) + , m_property(property) { } - Register base() const { return m_base; } - Register this_value() const { return m_this_value; } + Operand dst() const { return m_dst; } + Operand base() const { return m_base; } + Operand this_value() const { return m_this_value; } + Operand property() const { return m_property; } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; ByteString to_byte_string_impl(Bytecode::Executable const&) const; private: - Register m_base; - Register m_this_value; + Operand m_dst; + Operand m_base; + Operand m_this_value; + Operand m_property; }; class Jump : public Instruction { @@ -947,6 +1065,13 @@ public: { } + explicit Jump(Type type, Label taken_target, Label nontaken_target, size_t sizeof_self) + : Instruction(type, sizeof_self) + , m_true_target(move(taken_target)) + , m_false_target(move(nontaken_target)) + { + } + explicit Jump(Label taken_target, Optional