diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 734c345600..4f34295b3b 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -1523,29 +1523,57 @@ Bytecode::CodeGenerationErrorOr CallExpression::generate_bytecode(Bytecode if (is(this)) { TRY(m_callee->generate_bytecode(generator)); generator.emit(callee_reg); - } else if (is(*m_callee)) { - return Bytecode::CodeGenerationError { - this, - "Unimplemented callee kind: SuperExpression"sv, - }; } else if (is(*m_callee)) { auto& member_expression = static_cast(*m_callee); + + // https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation if (is(member_expression.object())) { - return Bytecode::CodeGenerationError { - this, - "Unimplemented callee kind: MemberExpression on SuperExpression"sv, - }; + // 1. Let env be GetThisEnvironment(). + // 2. Let actualThis be ? env.GetThisBinding(). + generator.emit(); + generator.emit(this_reg); + + Optional computed_property_value_register; + + if (member_expression.is_computed()) { + // SuperProperty : super [ Expression ] + // 3. Let propertyNameReference be ? Evaluation of Expression. + // 4. Let propertyNameValue be ? GetValue(propertyNameReference). + TRY(member_expression.property().generate_bytecode(generator)); + computed_property_value_register = generator.allocate_register(); + generator.emit(*computed_property_value_register); + } + + // 5/7. Return ? MakeSuperPropertyReference(actualThis, propertyKey, strict). + + // https://tc39.es/ecma262/#sec-makesuperpropertyreference + // 1. Let env be GetThisEnvironment(). + // 2. Assert: env.HasSuperBinding() is true. + // 3. Let baseValue be ? env.GetSuperBase(). + generator.emit(); + + // 4. Return the Reference Record { [[Base]]: baseValue, [[ReferencedName]]: propertyKey, [[Strict]]: strict, [[ThisValue]]: actualThis }. + if (computed_property_value_register.has_value()) { + // 5. Let propertyKey be ? ToPropertyKey(propertyNameValue). + // FIXME: This does ToPropertyKey out of order, which is observable by Symbol.toPrimitive! + generator.emit(*computed_property_value_register); + } else { + // 3. Let propertyKey be StringValue of IdentifierName. + auto identifier_table_ref = generator.intern_identifier(verify_cast(member_expression.property()).string()); + generator.emit(identifier_table_ref); + } + } else { + TRY(member_expression.object().generate_bytecode(generator)); + generator.emit(this_reg); + if (member_expression.is_computed()) { + TRY(member_expression.property().generate_bytecode(generator)); + generator.emit(this_reg); + } else { + auto identifier_table_ref = generator.intern_identifier(verify_cast(member_expression.property()).string()); + generator.emit(identifier_table_ref); + } } - TRY(member_expression.object().generate_bytecode(generator)); - generator.emit(this_reg); - if (member_expression.is_computed()) { - TRY(member_expression.property().generate_bytecode(generator)); - generator.emit(this_reg); - } else { - auto identifier_table_ref = generator.intern_identifier(verify_cast(member_expression.property()).string()); - generator.emit(identifier_table_ref); - } generator.emit(callee_reg); } else { // FIXME: this = global object in sloppy mode. diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.cpp b/Userland/Libraries/LibJS/Bytecode/Generator.cpp index 46d844eff6..714db25c36 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Generator.cpp @@ -151,22 +151,61 @@ CodeGenerationErrorOr Generator::emit_load_from_reference(JS::ASTNode cons } if (is(node)) { auto& expression = static_cast(node); - TRY(expression.object().generate_bytecode(*this)); - if (expression.is_computed()) { - auto object_reg = allocate_register(); - emit(object_reg); + // https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation + if (is(expression.object())) { + // 1. Let env be GetThisEnvironment(). + // 2. Let actualThis be ? env.GetThisBinding(). + // NOTE: Whilst this isn't used, it's still observable (e.g. it throws if super() hasn't been called) + emit(); - TRY(expression.property().generate_bytecode(*this)); - emit(object_reg); - } else if (expression.property().is_identifier()) { - auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); - emit(identifier_table_ref); + Optional computed_property_value_register; + + if (expression.is_computed()) { + // SuperProperty : super [ Expression ] + // 3. Let propertyNameReference be ? Evaluation of Expression. + // 4. Let propertyNameValue be ? GetValue(propertyNameReference). + TRY(expression.property().generate_bytecode(*this)); + computed_property_value_register = allocate_register(); + emit(*computed_property_value_register); + } + + // 5/7. Return ? MakeSuperPropertyReference(actualThis, propertyKey, strict). + + // https://tc39.es/ecma262/#sec-makesuperpropertyreference + // 1. Let env be GetThisEnvironment(). + // 2. Assert: env.HasSuperBinding() is true. + // 3. Let baseValue be ? env.GetSuperBase(). + emit(); + + // 4. Return the Reference Record { [[Base]]: baseValue, [[ReferencedName]]: propertyKey, [[Strict]]: strict, [[ThisValue]]: actualThis }. + if (computed_property_value_register.has_value()) { + // 5. Let propertyKey be ? ToPropertyKey(propertyNameValue). + // FIXME: This does ToPropertyKey out of order, which is observable by Symbol.toPrimitive! + emit(*computed_property_value_register); + } else { + // 3. Let propertyKey be StringValue of IdentifierName. + 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 - }; + TRY(expression.object().generate_bytecode(*this)); + + if (expression.is_computed()) { + auto object_reg = allocate_register(); + emit(object_reg); + + TRY(expression.property().generate_bytecode(*this)); + emit(object_reg); + } else if (expression.property().is_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 + }; + } } return {}; } diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h index 56b1171022..b9c79726a9 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.h +++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h @@ -76,6 +76,7 @@ O(PutById) \ O(PutByValue) \ O(ResolveThisBinding) \ + O(ResolveSuperBase) \ O(Return) \ O(RightShift) \ O(ScheduleJump) \ diff --git a/Userland/Libraries/LibJS/Bytecode/Op.cpp b/Userland/Libraries/LibJS/Bytecode/Op.cpp index 399556e95e..46b63caa7a 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Op.cpp @@ -530,6 +530,26 @@ ThrowCompletionOr ResolveThisBinding::execute_impl(Bytecode::Interpreter& return {}; } +// https://tc39.es/ecma262/#sec-makesuperpropertyreference +ThrowCompletionOr ResolveSuperBase::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + + // 1. Let env be GetThisEnvironment(). + auto& env = verify_cast(*get_this_environment(vm)); + + // 2. Assert: env.HasSuperBinding() is true. + VERIFY(env.has_super_binding()); + + // 3. Let baseValue be ? env.GetSuperBase(). + auto base_value = TRY(env.get_super_base()); + + // 4. Let bv be ? RequireObjectCoercible(baseValue). + interpreter.accumulator() = TRY(require_object_coercible(vm, base_value)); + + return {}; +} + ThrowCompletionOr GetNewTarget::execute_impl(Bytecode::Interpreter& interpreter) const { interpreter.accumulator() = interpreter.vm().get_new_target(); @@ -1383,6 +1403,11 @@ DeprecatedString ResolveThisBinding::to_deprecated_string_impl(Bytecode::Executa return "ResolveThisBinding"sv; } +DeprecatedString ResolveSuperBase::to_deprecated_string_impl(Bytecode::Executable const&) const +{ + return "ResolveSuperBase"sv; +} + DeprecatedString GetNewTarget::to_deprecated_string_impl(Bytecode::Executable const&) const { return "GetNewTarget"sv; diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index a309f13f2d..c48beba20a 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -1130,6 +1130,19 @@ public: void replace_references_impl(Register, Register) { } }; +class ResolveSuperBase final : public Instruction { +public: + explicit ResolveSuperBase() + : Instruction(Type::ResolveSuperBase) + { + } + + ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; + DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } + void replace_references_impl(Register, Register) { } +}; + class GetNewTarget final : public Instruction { public: explicit GetNewTarget()