Procházet zdrojové kódy

LibJS: Add Module Namespace Exotic Object

davidot před 3 roky
rodič
revize
55366703d0

+ 1 - 0
Userland/Libraries/LibJS/CMakeLists.txt

@@ -116,6 +116,7 @@ set(SOURCES
     Runtime/MarkedValueList.cpp
     Runtime/MarkedValueList.cpp
     Runtime/MathObject.cpp
     Runtime/MathObject.cpp
     Runtime/ModuleEnvironment.cpp
     Runtime/ModuleEnvironment.cpp
+    Runtime/ModuleNamespaceObject.cpp
     Runtime/NativeFunction.cpp
     Runtime/NativeFunction.cpp
     Runtime/NumberConstructor.cpp
     Runtime/NumberConstructor.cpp
     Runtime/NumberObject.cpp
     Runtime/NumberObject.cpp

+ 199 - 0
Userland/Libraries/LibJS/Runtime/ModuleNamespaceObject.cpp

@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2022, David Tuin <davidot@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/QuickSort.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/ModuleNamespaceObject.h>
+
+namespace JS {
+
+ModuleNamespaceObject::ModuleNamespaceObject(GlobalObject& global_object, Module* module, Vector<FlyString> exports)
+    : Object(*global_object.object_prototype())
+    , m_module(module)
+    , m_exports(move(exports))
+{
+    // Note: We just perform step 6 of 10.4.6.12 ModuleNamespaceCreate ( module, exports ), https://tc39.es/ecma262/#sec-modulenamespacecreate
+    // 6. Let sortedExports be a List whose elements are the elements of exports ordered as if an Array of the same values had been sorted using %Array.prototype.sort% using undefined as comparefn.
+    quick_sort(m_exports, [&](FlyString const& lhs, FlyString const& rhs) {
+        return lhs.view() < rhs.view();
+    });
+}
+
+void ModuleNamespaceObject::initialize(GlobalObject& global_object)
+{
+    Object::initialize(global_object);
+
+    // 28.3.1 @@toStringTag, https://tc39.es/ecma262/#sec-@@tostringtag
+    define_direct_property(*vm().well_known_symbol_to_string_tag(), js_string(vm(), "Module"sv), 0);
+}
+
+// 10.4.6.1 [[GetPrototypeOf]] ( ), https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-getprototypeof
+ThrowCompletionOr<Object*> ModuleNamespaceObject::internal_get_prototype_of() const
+{
+    // 1. Return null.
+    return nullptr;
+}
+
+// 10.4.6.2 [[SetPrototypeOf]] ( V ), https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-setprototypeof-v
+ThrowCompletionOr<bool> ModuleNamespaceObject::internal_set_prototype_of(Object* prototype)
+{
+    // 1. Return ? SetImmutablePrototype(O, V).
+    return set_immutable_prototype(prototype);
+}
+
+// 10.4.6.3 [[IsExtensible]] ( ), https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-isextensible
+ThrowCompletionOr<bool> ModuleNamespaceObject::internal_is_extensible() const
+{
+    // 1. Return false.
+    return false;
+}
+
+// 10.4.6.4 [[PreventExtensions]] ( ), https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-preventextensions
+ThrowCompletionOr<bool> ModuleNamespaceObject::internal_prevent_extensions()
+{
+    // 1. Return true.
+    return true;
+}
+
+// 10.4.6.5 [[GetOwnProperty]] ( P ), https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-getownproperty-p
+ThrowCompletionOr<Optional<PropertyDescriptor>> ModuleNamespaceObject::internal_get_own_property(PropertyKey const& property_key) const
+{
+    // 1. If Type(P) is Symbol, return OrdinaryGetOwnProperty(O, P).
+    if (property_key.is_symbol())
+        return Object::internal_get_own_property(property_key);
+
+    // 2. Let exports be O.[[Exports]].
+    // 3. If P is not an element of exports, return undefined.
+    auto export_element = m_exports.find(property_key.to_string());
+    if (export_element.is_end())
+        return Optional<PropertyDescriptor> {};
+
+    // 4. Let value be ? O.[[Get]](P, O).
+    auto value = TRY(internal_get(property_key, this));
+
+    // 5. Return PropertyDescriptor { [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: false }.
+    return PropertyDescriptor { .value = value, .writable = true, .enumerable = true, .configurable = false };
+}
+
+// 10.4.6.6 [[DefineOwnProperty]] ( P, Desc ), https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-defineownproperty-p-desc
+ThrowCompletionOr<bool> ModuleNamespaceObject::internal_define_own_property(PropertyKey const& property_key, PropertyDescriptor const& descriptor)
+{
+    // 1. If Type(P) is Symbol, return OrdinaryDefineOwnProperty(O, P, Desc).
+    if (property_key.is_symbol())
+        return Object::internal_define_own_property(property_key, descriptor);
+
+    // 2. Let current be ? O.[[GetOwnProperty]](P).
+    auto current = TRY(internal_get_own_property(property_key));
+
+    // 3. If current is undefined, return false.
+    if (!current.has_value())
+        return false;
+
+    // 4. If Desc has a [[Configurable]] field and Desc.[[Configurable]] is true, return false.
+    if (descriptor.configurable.has_value() && descriptor.configurable.value())
+        return false;
+
+    // 5. If Desc has an [[Enumerable]] field and Desc.[[Enumerable]] is false, return false.
+    if (descriptor.enumerable.has_value() && !descriptor.enumerable.value())
+        return false;
+
+    // 6. If ! IsAccessorDescriptor(Desc) is true, return false.
+    if (descriptor.is_accessor_descriptor())
+        return false;
+
+    // 7. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, return false.
+    if (descriptor.writable.has_value() && !descriptor.writable.value())
+        return false;
+
+    // 8. If Desc has a [[Value]] field, return SameValue(Desc.[[Value]], current.[[Value]]).
+    if (descriptor.value.has_value())
+        return same_value(descriptor.value.value(), current->value.value());
+
+    // 9. Return true.
+    return true;
+}
+
+// 10.4.6.7 [[HasProperty]] ( P ), https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-hasproperty-p
+ThrowCompletionOr<bool> ModuleNamespaceObject::internal_has_property(PropertyKey const& property_key) const
+{
+    // 1. If Type(P) is Symbol, return OrdinaryHasProperty(O, P).
+    if (property_key.is_symbol())
+        return Object::internal_has_property(property_key);
+
+    // 2. Let exports be O.[[Exports]].
+    // 3. If P is an element of exports, return true.
+    auto export_element = m_exports.find(property_key.to_string());
+    if (!export_element.is_end())
+        return true;
+
+    // 4. Return false.
+    return false;
+}
+
+// 10.4.6.8 [[Get]] ( P, Receiver ), https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-get-p-receiver
+ThrowCompletionOr<Value> ModuleNamespaceObject::internal_get(PropertyKey const& property_key, Value receiver) const
+{
+    // 1. If Type(P) is Symbol, then
+    if (property_key.is_symbol()) {
+        // a. Return ? OrdinaryGet(O, P, Receiver).
+        return Object::internal_get(property_key, receiver);
+    }
+
+    // 2. Let exports be O.[[Exports]].
+    // 3. If P is not an element of exports, return undefined.
+    auto export_element = m_exports.find(property_key.to_string());
+    if (export_element.is_end())
+        return js_undefined();
+
+    // 4. Let m be O.[[Module]].
+    // 5. Let binding be ! m.ResolveExport(P).
+    // FIXME: Add steps 5 through 12
+    return vm().throw_completion<ReferenceError>(global_object(), ErrorType::ModuleNoEnvironment);
+}
+
+// 10.4.6.9 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-set-p-v-receiver
+ThrowCompletionOr<bool> ModuleNamespaceObject::internal_set(PropertyKey const&, Value, Value)
+{
+    // 1. Return false.
+    return false;
+}
+
+// 10.4.6.10 [[Delete]] ( P ), https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-delete-p
+ThrowCompletionOr<bool> ModuleNamespaceObject::internal_delete(PropertyKey const& property_key)
+{
+    // 1. If Type(P) is Symbol, then
+    if (property_key.is_symbol()) {
+        // a. Return ? OrdinaryDelete(O, P).
+        return Object::internal_delete(property_key);
+    }
+
+    // 2. Let exports be O.[[Exports]].
+    // 3. If P is an element of exports, return false.
+    auto export_element = m_exports.find(property_key.to_string());
+    if (!export_element.is_end())
+        return false;
+
+    // 4. Return true.
+    return true;
+}
+
+// 10.4.6.11 [[OwnPropertyKeys]] ( ), https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-ownpropertykeys
+ThrowCompletionOr<MarkedValueList> ModuleNamespaceObject::internal_own_property_keys() const
+{
+    // 1. Let exports be O.[[Exports]].
+
+    // 2. Let symbolKeys be ! OrdinaryOwnPropertyKeys(O).
+    auto symbol_keys = MUST(Object::internal_own_property_keys());
+
+    // 3. Return the list-concatenation of exports and symbolKeys.
+    for (auto& export_name : m_exports) {
+        symbol_keys.append(js_string(vm(), export_name));
+    }
+
+    return symbol_keys;
+}
+
+}

+ 42 - 0
Userland/Libraries/LibJS/Runtime/ModuleNamespaceObject.h

@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2022, David Tuin <davidot@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibJS/Module.h>
+#include <LibJS/Runtime/Completion.h>
+#include <LibJS/Runtime/Object.h>
+
+namespace JS {
+
+class ModuleNamespaceObject final : public Object {
+    JS_OBJECT(ModuleNamespaceObject, Object);
+
+public:
+    ModuleNamespaceObject(GlobalObject&, Module* module, Vector<FlyString> exports);
+
+    // 10.4.6 Module Namespace Exotic Objects, https://tc39.es/ecma262/#sec-module-namespace-exotic-objects
+
+    virtual ThrowCompletionOr<Object*> internal_get_prototype_of() const override;
+    virtual ThrowCompletionOr<bool> internal_set_prototype_of(Object* prototype) override;
+    virtual ThrowCompletionOr<bool> internal_is_extensible() const override;
+    virtual ThrowCompletionOr<bool> internal_prevent_extensions() override;
+    virtual ThrowCompletionOr<Optional<PropertyDescriptor>> internal_get_own_property(PropertyKey const&) const override;
+    virtual ThrowCompletionOr<bool> internal_define_own_property(PropertyKey const&, PropertyDescriptor const&) override;
+    virtual ThrowCompletionOr<bool> internal_has_property(PropertyKey const&) const override;
+    virtual ThrowCompletionOr<Value> internal_get(PropertyKey const&, Value receiver) const override;
+    virtual ThrowCompletionOr<bool> internal_set(PropertyKey const&, Value value, Value receiver) override;
+    virtual ThrowCompletionOr<bool> internal_delete(PropertyKey const&) override;
+    virtual ThrowCompletionOr<MarkedValueList> internal_own_property_keys() const override;
+    virtual void initialize(GlobalObject& object) override;
+
+private:
+    // FIXME: UHHH how do we want to store this to avoid cycles but be safe??
+    Module* m_module;            // [[Module]]
+    Vector<FlyString> m_exports; // [[Exports]]
+};
+
+}