From 007ffcd763b63fed274d71c940f727391e887745 Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Thu, 31 Mar 2022 00:59:58 +0430 Subject: [PATCH] LibJS: Implement bytecode generation for all ObjectExpression properties --- .../Libraries/LibJS/Bytecode/ASTCodegen.cpp | 35 ++++++++--- Userland/Libraries/LibJS/Bytecode/Op.cpp | 60 +++++++++++++++++-- Userland/Libraries/LibJS/Bytecode/Op.h | 16 ++++- 3 files changed, 94 insertions(+), 17 deletions(-) diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 4ec394e34c..68a8d0dc64 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -817,25 +817,42 @@ Bytecode::CodeGenerationErrorOr ObjectExpression::generate_bytecode(Byteco generator.emit(object_reg); for (auto& property : m_properties) { - if (property.type() != ObjectProperty::Type::KeyValue) - return Bytecode::CodeGenerationError { - this, - "Unimplemented property kind"sv, - }; + Bytecode::Op::PropertyKind property_kind; + switch (property.type()) { + case ObjectProperty::Type::KeyValue: + property_kind = Bytecode::Op::PropertyKind::KeyValue; + break; + case ObjectProperty::Type::Getter: + property_kind = Bytecode::Op::PropertyKind::Getter; + break; + 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; + } if (is(property.key())) { auto& string_literal = static_cast(property.key()); Bytecode::IdentifierTableIndex key_name = generator.intern_identifier(string_literal.value()); - TRY(property.value().generate_bytecode(generator)); - generator.emit(object_reg, key_name); + if (property_kind != Bytecode::Op::PropertyKind::Spread) + TRY(property.value().generate_bytecode(generator)); + + generator.emit(object_reg, key_name, property_kind); } else { TRY(property.key().generate_bytecode(generator)); auto property_reg = generator.allocate_register(); generator.emit(property_reg); - TRY(property.value().generate_bytecode(generator)); - generator.emit(object_reg, property_reg); + if (property_kind != Bytecode::Op::PropertyKind::Spread) + TRY(property.value().generate_bytecode(generator)); + + generator.emit(object_reg, property_reg, property_kind); } } diff --git a/Userland/Libraries/LibJS/Bytecode/Op.cpp b/Userland/Libraries/LibJS/Bytecode/Op.cpp index 76b7414ae3..7886f5f363 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Op.cpp @@ -44,6 +44,42 @@ String Instruction::to_string(Bytecode::Executable const& executable) const namespace JS::Bytecode::Op { +static ThrowCompletionOr put_by_property_key(Object* object, Value value, PropertyKey name, Bytecode::Interpreter& interpreter, PropertyKind kind) +{ + if (kind == PropertyKind::Getter || kind == PropertyKind::Setter) { + // The generator should only pass us functions for getters and setters. + VERIFY(value.is_function()); + } + switch (kind) { + case PropertyKind::Getter: { + auto& function = value.as_function(); + if (function.name().is_empty() && is(function)) + static_cast(&function)->set_name(String::formatted("get {}", name)); + object->define_direct_accessor(name, &function, nullptr, Attribute::Configurable | Attribute::Enumerable); + break; + } + case PropertyKind::Setter: { + auto& function = value.as_function(); + if (function.name().is_empty() && is(function)) + static_cast(&function)->set_name(String::formatted("set {}", name)); + object->define_direct_accessor(name, nullptr, &function, Attribute::Configurable | Attribute::Enumerable); + break; + } + case PropertyKind::KeyValue: + TRY(object->set(name, interpreter.accumulator(), Object::ShouldThrowExceptions::Yes)); + break; + case PropertyKind::Spread: + TRY(object->copy_data_properties(value, {}, interpreter.global_object())); + break; + case PropertyKind::ProtoSetter: + if (value.is_object() || value.is_null()) + MUST(object->internal_set_prototype_of(value.is_object() ? &value.as_object() : nullptr)); + break; + } + + return {}; +} + ThrowCompletionOr Load::execute_impl(Bytecode::Interpreter& interpreter) const { interpreter.accumulator() = interpreter.reg(m_src); @@ -350,8 +386,9 @@ ThrowCompletionOr GetById::execute_impl(Bytecode::Interpreter& interpreter ThrowCompletionOr PutById::execute_impl(Bytecode::Interpreter& interpreter) const { auto* object = TRY(interpreter.reg(m_base).to_object(interpreter.global_object())); - TRY(object->set(interpreter.current_executable().get_identifier(m_property), interpreter.accumulator(), Object::ShouldThrowExceptions::Yes)); - return {}; + PropertyKey name = interpreter.current_executable().get_identifier(m_property); + auto value = interpreter.accumulator(); + return put_by_property_key(object, value, name, interpreter, m_kind); } ThrowCompletionOr DeleteById::execute_impl(Bytecode::Interpreter& interpreter) const @@ -592,8 +629,7 @@ ThrowCompletionOr PutByValue::execute_impl(Bytecode::Interpreter& interpre auto* object = TRY(interpreter.reg(m_base).to_object(interpreter.global_object())); auto property_key = TRY(interpreter.reg(m_property).to_property_key(interpreter.global_object())); - TRY(object->set(property_key, interpreter.accumulator(), Object::ShouldThrowExceptions::Yes)); - return {}; + return put_by_property_key(object, interpreter.accumulator(), property_key, interpreter, m_kind); } ThrowCompletionOr DeleteByValue::execute_impl(Bytecode::Interpreter& interpreter) const @@ -839,7 +875,13 @@ String SetVariable::to_string_impl(Bytecode::Executable const& executable) const String PutById::to_string_impl(Bytecode::Executable const& executable) const { - return String::formatted("PutById base:{}, property:{} ({})", m_base, m_property, executable.identifier_table->get(m_property)); + auto kind = m_kind == PropertyKind::Getter + ? "getter" + : m_kind == PropertyKind::Setter + ? "setter" + : "property"; + + return String::formatted("PutById kind:{} base:{}, property:{} ({})", kind, m_base, m_property, executable.identifier_table->get(m_property)); } String GetById::to_string_impl(Bytecode::Executable const& executable) const @@ -985,7 +1027,13 @@ String GetByValue::to_string_impl(const Bytecode::Executable&) const String PutByValue::to_string_impl(const Bytecode::Executable&) const { - return String::formatted("PutByValue base:{}, property:{}", m_base, m_property); + auto kind = m_kind == PropertyKind::Getter + ? "getter" + : m_kind == PropertyKind::Setter + ? "setter" + : "property"; + + return String::formatted("PutByValue kind:{} base:{}, property:{}", kind, m_base, m_property); } String DeleteByValue::to_string_impl(Bytecode::Executable const&) const diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index 7c4f35c8a7..6f2f80bf80 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -410,12 +410,21 @@ private: IdentifierTableIndex m_property; }; +enum class PropertyKind { + Getter, + Setter, + KeyValue, + Spread, + ProtoSetter, +}; + class PutById final : public Instruction { public: - explicit PutById(Register base, IdentifierTableIndex property) + explicit PutById(Register base, IdentifierTableIndex property, PropertyKind kind = PropertyKind::KeyValue) : Instruction(Type::PutById) , m_base(base) , m_property(property) + , m_kind(kind) { } @@ -426,6 +435,7 @@ public: private: Register m_base; IdentifierTableIndex m_property; + PropertyKind m_kind; }; class DeleteById final : public Instruction { @@ -462,10 +472,11 @@ private: class PutByValue final : public Instruction { public: - PutByValue(Register base, Register property) + PutByValue(Register base, Register property, PropertyKind kind = PropertyKind::KeyValue) : Instruction(Type::PutByValue) , m_base(base) , m_property(property) + , m_kind(kind) { } @@ -476,6 +487,7 @@ public: private: Register m_base; Register m_property; + PropertyKind m_kind; }; class DeleteByValue final : public Instruction {