diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.cpp b/Userland/Libraries/LibJS/Bytecode/Generator.cpp index f06f6e70ce7..88398297d0e 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Generator.cpp @@ -304,6 +304,21 @@ CodeGenerationErrorOr Generator::emit_delete_reference(JS::ASTNode const& if (is(node)) { 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)); + + if (super_reference.referenced_name.has_value()) { + emit(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); + } + + return {}; + } + TRY(expression.object().generate_bytecode(*this)); if (expression.is_computed()) { diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h index c79d7c4e840..be9a96a78ce 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.h +++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h @@ -27,7 +27,9 @@ O(CreateVariable) \ O(Decrement) \ O(DeleteById) \ + O(DeleteByIdWithThis) \ O(DeleteByValue) \ + O(DeleteByValueWithThis) \ O(DeleteVariable) \ O(Div) \ O(EnterUnwindContext) \ diff --git a/Userland/Libraries/LibJS/Bytecode/Op.cpp b/Userland/Libraries/LibJS/Bytecode/Op.cpp index 2400bbbd390..9e5c1ed43a8 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Op.cpp @@ -627,6 +627,17 @@ ThrowCompletionOr DeleteById::execute_impl(Bytecode::Interpreter& interpre return {}; }; +ThrowCompletionOr DeleteByIdWithThis::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + auto base_value = interpreter.accumulator(); + 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))); + return {}; +}; + ThrowCompletionOr Jump::execute_impl(Bytecode::Interpreter& interpreter) const { interpreter.jump(*m_true_target); @@ -1115,6 +1126,21 @@ ThrowCompletionOr DeleteByValue::execute_impl(Bytecode::Interpreter& inter return {}; } +ThrowCompletionOr DeleteByValueWithThis::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 base_value = interpreter.reg(m_base); + auto property_key = TRY(property_key_value.to_property_key(vm)); + bool strict = vm.in_strict_mode(); + auto reference = Reference { base_value, property_key, interpreter.reg(m_this_value), strict }; + interpreter.accumulator() = Value(TRY(reference.delete_(vm))); + return {}; +}; + ThrowCompletionOr GetIterator::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); @@ -1511,6 +1537,11 @@ DeprecatedString DeleteById::to_deprecated_string_impl(Bytecode::Executable cons return DeprecatedString::formatted("DeleteById {} ({})", m_property, executable.identifier_table->get(m_property)); } +DeprecatedString DeleteByIdWithThis::to_deprecated_string_impl(Bytecode::Executable const& executable) const +{ + return DeprecatedString::formatted("DeleteByIdWithThis {} ({}) this_value:{}", m_property, executable.identifier_table->get(m_property), m_this_value); +} + DeprecatedString Jump::to_deprecated_string_impl(Bytecode::Executable const&) const { if (m_true_target.has_value()) @@ -1712,6 +1743,11 @@ DeprecatedString DeleteByValue::to_deprecated_string_impl(Bytecode::Executable c return DeprecatedString::formatted("DeleteByValue base:{}", m_base); } +DeprecatedString DeleteByValueWithThis::to_deprecated_string_impl(Bytecode::Executable const&) const +{ + return DeprecatedString::formatted("DeleteByValueWithThis base:{} this_value:{}", m_base, m_this_value); +} + DeprecatedString GetIterator::to_deprecated_string_impl(Executable const&) const { auto hint = m_hint == IteratorHint::Sync ? "sync" : "async"; diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index 1710f4d7b86..48d1e759586 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -740,6 +740,25 @@ private: IdentifierTableIndex m_property; }; +class DeleteByIdWithThis final : public Instruction { +public: + DeleteByIdWithThis(Register this_value, IdentifierTableIndex property) + : Instruction(Type::DeleteByIdWithThis) + , m_this_value(this_value) + , m_property(property) + { + } + + 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) { } + +private: + Register m_this_value; + IdentifierTableIndex m_property; +}; + class GetByValue final : public Instruction { public: explicit GetByValue(Register base) @@ -865,6 +884,29 @@ private: Register m_base; }; +class DeleteByValueWithThis final : public Instruction { +public: + DeleteByValueWithThis(Register base, Register this_value) + : Instruction(Type::DeleteByValueWithThis) + , m_base(base) + , m_this_value(this_value) + { + } + + 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 from, Register to) + { + if (m_base == from) + m_base = to; + } + +private: + Register m_base; + Register m_this_value; +}; + class Jump : public Instruction { public: constexpr static bool IsTerminator = true; diff --git a/Userland/Libraries/LibJS/Bytecode/Pass/LoadElimination.cpp b/Userland/Libraries/LibJS/Bytecode/Pass/LoadElimination.cpp index f2bd173218f..0745fc1c715 100644 --- a/Userland/Libraries/LibJS/Bytecode/Pass/LoadElimination.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Pass/LoadElimination.cpp @@ -118,7 +118,9 @@ static NonnullOwnPtr eliminate_loads(BasicBlock const& block, size_t break; } case DeleteById: + case DeleteByIdWithThis: case DeleteByValue: + case DeleteByValueWithThis: // These can trigger proxies, which call into user code // So these are treated like calls case GetByValue: