Parcourir la source

LibJS: Implement bytecode generation for all ObjectExpression properties

Ali Mohammad Pur il y a 3 ans
Parent
commit
007ffcd763

+ 26 - 9
Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp

@@ -817,25 +817,42 @@ Bytecode::CodeGenerationErrorOr<void> ObjectExpression::generate_bytecode(Byteco
     generator.emit<Bytecode::Op::Store>(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<StringLiteral>(property.key())) {
             auto& string_literal = static_cast<StringLiteral const&>(property.key());
             Bytecode::IdentifierTableIndex key_name = generator.intern_identifier(string_literal.value());
 
-            TRY(property.value().generate_bytecode(generator));
-            generator.emit<Bytecode::Op::PutById>(object_reg, key_name);
+            if (property_kind != Bytecode::Op::PropertyKind::Spread)
+                TRY(property.value().generate_bytecode(generator));
+
+            generator.emit<Bytecode::Op::PutById>(object_reg, key_name, property_kind);
         } else {
             TRY(property.key().generate_bytecode(generator));
             auto property_reg = generator.allocate_register();
             generator.emit<Bytecode::Op::Store>(property_reg);
 
-            TRY(property.value().generate_bytecode(generator));
-            generator.emit<Bytecode::Op::PutByValue>(object_reg, property_reg);
+            if (property_kind != Bytecode::Op::PropertyKind::Spread)
+                TRY(property.value().generate_bytecode(generator));
+
+            generator.emit<Bytecode::Op::PutByValue>(object_reg, property_reg, property_kind);
         }
     }
 

+ 54 - 6
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<void> 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<ECMAScriptFunctionObject>(function))
+            static_cast<ECMAScriptFunctionObject*>(&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<ECMAScriptFunctionObject>(function))
+            static_cast<ECMAScriptFunctionObject*>(&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<void> Load::execute_impl(Bytecode::Interpreter& interpreter) const
 {
     interpreter.accumulator() = interpreter.reg(m_src);
@@ -350,8 +386,9 @@ ThrowCompletionOr<void> GetById::execute_impl(Bytecode::Interpreter& interpreter
 ThrowCompletionOr<void> 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<void> DeleteById::execute_impl(Bytecode::Interpreter& interpreter) const
@@ -592,8 +629,7 @@ ThrowCompletionOr<void> 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<void> 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

+ 14 - 2
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 {