diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 1ad3ddd487..4e51f78bd6 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -523,14 +523,90 @@ Bytecode::CodeGenerationErrorOr Identifier::generate_bytecode(Bytecode::Ge Bytecode::CodeGenerationErrorOr AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) const { - // FIXME: Implement this for BindingPatterns too. - auto& lhs = m_lhs.get>(); - if (m_op == AssignmentOp::Assignment) { - TRY(m_rhs->generate_bytecode(generator)); - return generator.emit_store_to_reference(lhs); + // AssignmentExpression : LeftHandSideExpression = AssignmentExpression + return m_lhs.visit( + // 1. If LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral, then + [&](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; + + if (is(*lhs)) { + auto& expression = static_cast(*lhs); + TRY(expression.object().generate_bytecode(generator)); + + base_object_register = generator.allocate_register(); + generator.emit(*base_object_register); + + if (expression.is_computed()) { + TRY(expression.property().generate_bytecode(generator)); + computed_property_register = generator.allocate_register(); + generator.emit(*computed_property_register); + + // To be continued later with PutByValue. + } else if (expression.property().is_identifier()) { + // Do nothing, this will be handled by PutById later. + } else { + return Bytecode::CodeGenerationError { + &expression, + "Unimplemented non-computed member expression"sv + }; + } + } 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 { + TRY(lhs->generate_bytecode(generator)); + } + + // FIXME: c. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then + // i. Let rval be ? NamedEvaluation of AssignmentExpression with argument lref.[[ReferencedName]]. + + // d. Else, + // i. Let rref be the result of evaluating AssignmentExpression. + // ii. Let rval be ? GetValue(rref). + TRY(m_rhs->generate_bytecode(generator)); + + // e. Perform ? PutValue(lref, rval). + if (is(*lhs)) { + auto& identifier = static_cast(*lhs); + generator.emit(generator.intern_identifier(identifier.string())); + } else if (is(*lhs)) { + auto& expression = static_cast(*lhs); + + if (expression.is_computed()) { + generator.emit(*base_object_register, *computed_property_register); + } else if (expression.property().is_identifier()) { + auto identifier_table_ref = generator.intern_identifier(verify_cast(expression.property()).string()); + generator.emit(*base_object_register, identifier_table_ref); + } else { + return Bytecode::CodeGenerationError { + &expression, + "Unimplemented non-computed member expression"sv + }; + } + } else { + return Bytecode::CodeGenerationError { + lhs, + "Unimplemented/invalid node used a reference"sv + }; + } + + // f. Return rval. + // NOTE: This is already in the accumulator. + return {}; + }, + // 2. Let assignmentPattern be the AssignmentPattern that is covered by LeftHandSideExpression. + [&](NonnullRefPtr const& pattern) -> Bytecode::CodeGenerationErrorOr { + TODO(); + }); } + VERIFY(m_lhs.has>()); + auto& lhs = m_lhs.get>(); + TRY(generator.emit_load_from_reference(lhs)); Bytecode::BasicBlock* rhs_block_ptr { nullptr };