From bc05f6303f53a7dfe64250ed194228461db21173 Mon Sep 17 00:00:00 2001 From: Jonne Ransijn Date: Fri, 1 Nov 2024 22:00:32 +0100 Subject: [PATCH] LibJS: Add `PutBySpread` instruction This object property kind had completely different behaviour. By adding an instruction for it, we can remove a bunch of special casing, and avoid creating dummy `PropertyKey` values. --- .../Libraries/LibJS/Bytecode/ASTCodegen.cpp | 19 ++++-------- .../Libraries/LibJS/Bytecode/Instruction.h | 1 + .../Libraries/LibJS/Bytecode/Interpreter.cpp | 31 ++++++++++++++----- Userland/Libraries/LibJS/Bytecode/Op.h | 26 +++++++++++++++- 4 files changed, 56 insertions(+), 21 deletions(-) diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 73d3d9d26eb..e09d1ba30f2 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -1132,12 +1132,12 @@ Bytecode::CodeGenerationErrorOr> ObjectExpression::gener case ObjectProperty::Type::Setter: property_kind = Bytecode::Op::PropertyKind::Setter; break; - case ObjectProperty::Type::Spread: - property_kind = Bytecode::Op::PropertyKind::Spread; - break; case ObjectProperty::Type::ProtoSetter: property_kind = Bytecode::Op::PropertyKind::ProtoSetter; break; + case ObjectProperty::Type::Spread: + generator.emit(object, TRY(property->key().generate_bytecode(generator)).value()); + continue; } if (is(property->key())) { @@ -1147,7 +1147,7 @@ Bytecode::CodeGenerationErrorOr> ObjectExpression::gener Optional value; if (property_kind == Bytecode::Op::PropertyKind::ProtoSetter) { value = TRY(property->value().generate_bytecode(generator)).value(); - } else if (property_kind != Bytecode::Op::PropertyKind::Spread) { + } else { ByteString identifier = string_literal.value(); if (property_kind == Bytecode::Op::PropertyKind::Getter) identifier = ByteString::formatted("get {}", identifier); @@ -1155,21 +1155,14 @@ Bytecode::CodeGenerationErrorOr> ObjectExpression::gener identifier = ByteString::formatted("set {}", identifier); auto name = generator.intern_identifier(identifier); 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, key_name, *value, property_kind, generator.next_property_lookup_cache()); } else { auto property_name = TRY(property->key().generate_bytecode(generator)).value(); - Optional value; - if (property_kind != Bytecode::Op::PropertyKind::Spread) - value = TRY(property->value().generate_bytecode(generator)).value(); - else - value = property_name; + auto value = TRY(property->value().generate_bytecode(generator)).value(); - generator.emit(object, property_name, *value, property_kind); + generator.emit(object, property_name, value, property_kind); } } diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h index a132e1e82b9..26a55d75247 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.h +++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h @@ -120,6 +120,7 @@ O(PostfixIncrement) \ O(PutById) \ O(PutByIdWithThis) \ + O(PutBySpread) \ O(PutByValue) \ O(PutByValueWithThis) \ O(PutPrivateById) \ diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index 91babb18141..f3b71c5f967 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -649,6 +649,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point) HANDLE_INSTRUCTION(PostfixIncrement); HANDLE_INSTRUCTION(PutById); HANDLE_INSTRUCTION(PutByIdWithThis); + HANDLE_INSTRUCTION(PutBySpread); HANDLE_INSTRUCTION(PutByValue); HANDLE_INSTRUCTION(PutByValueWithThis); HANDLE_INSTRUCTION(PutPrivateById); @@ -1213,9 +1214,6 @@ inline ThrowCompletionOr put_by_property_key(VM& vm, Value base, Value thi case Op::PropertyKind::DirectKeyValue: object->define_direct_property(name, value, Attribute::Enumerable | Attribute::Writable | Attribute::Configurable); break; - case Op::PropertyKind::Spread: - TRY(object->copy_data_properties(vm, value, {})); - break; case Op::PropertyKind::ProtoSetter: if (value.is_object() || value.is_null()) MUST(object->internal_set_prototype_of(value.is_object() ? &value.as_object() : nullptr)); @@ -1362,7 +1360,7 @@ inline ThrowCompletionOr put_by_value(VM& vm, Value base, Optional HasPrivateId::execute_impl(Bytecode::Interpreter& interp return {}; } +ThrowCompletionOr PutBySpread::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + auto value = interpreter.get(m_src); + auto base = interpreter.get(m_base); + + // a. Let baseObj be ? ToObject(V.[[Base]]). + auto object = TRY(base.to_object(vm)); + + TRY(object->copy_data_properties(vm, value, {})); + + return {}; +} + ThrowCompletionOr PutById::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); @@ -2807,7 +2819,7 @@ ThrowCompletionOr PutByValueWithThis::execute_impl(Bytecode::Interpreter& auto& vm = interpreter.vm(); 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 { 0 }; + auto property_key = TRY(interpreter.get(m_property).to_property_key(vm)); TRY(put_by_property_key(vm, base, interpreter.get(m_this_value), value, {}, property_key, m_kind)); return {}; } @@ -3158,14 +3170,19 @@ static StringView property_kind_to_string(PropertyKind kind) return "key-value"sv; case PropertyKind::DirectKeyValue: return "direct-key-value"sv; - case PropertyKind::Spread: - return "spread"sv; case PropertyKind::ProtoSetter: return "proto-setter"sv; } VERIFY_NOT_REACHED(); } +ByteString PutBySpread::to_byte_string_impl(Bytecode::Executable const& executable) const +{ + return ByteString::formatted("PutBySpread {}, {}", + format_operand("base"sv, m_base, executable), + format_operand("src"sv, m_src, executable)); +} + ByteString PutById::to_byte_string_impl(Bytecode::Executable const& executable) const { auto kind = property_kind_to_string(m_kind); diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index 065abb27cd8..57aca1ac1e8 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -1120,10 +1120,34 @@ enum class PropertyKind { Setter, KeyValue, DirectKeyValue, // Used for Object expressions. Always sets an own property, never calls a setter. - Spread, ProtoSetter, }; +class PutBySpread final : public Instruction { +public: + PutBySpread(Operand base, Operand src) + : Instruction(Type::PutBySpread) + , m_base(base) + , m_src(src) + { + } + + ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; + ByteString to_byte_string_impl(Bytecode::Executable const&) const; + void visit_operands_impl(Function visitor) + { + visitor(m_base); + visitor(m_src); + } + + Operand base() const { return m_base; } + Operand src() const { return m_src; } + +private: + Operand m_base; + Operand m_src; +}; + class PutById final : public Instruction { public: explicit PutById(Operand base, IdentifierTableIndex property, Operand src, PropertyKind kind, u32 cache_index, Optional base_identifier = {})