diff --git a/Userland/Libraries/LibJS/CMakeLists.txt b/Userland/Libraries/LibJS/CMakeLists.txt index 01f448b28b..bef88b9548 100644 --- a/Userland/Libraries/LibJS/CMakeLists.txt +++ b/Userland/Libraries/LibJS/CMakeLists.txt @@ -116,6 +116,7 @@ set(SOURCES Runtime/MarkedValueList.cpp Runtime/MathObject.cpp Runtime/ModuleEnvironment.cpp + Runtime/ModuleNamespaceObject.cpp Runtime/NativeFunction.cpp Runtime/NumberConstructor.cpp Runtime/NumberObject.cpp diff --git a/Userland/Libraries/LibJS/Runtime/ModuleNamespaceObject.cpp b/Userland/Libraries/LibJS/Runtime/ModuleNamespaceObject.cpp new file mode 100644 index 0000000000..59aa4f55fe --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/ModuleNamespaceObject.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2022, David Tuin + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace JS { + +ModuleNamespaceObject::ModuleNamespaceObject(GlobalObject& global_object, Module* module, Vector 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 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 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 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 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> 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 {}; + + // 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 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 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 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(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 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 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 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; +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/ModuleNamespaceObject.h b/Userland/Libraries/LibJS/Runtime/ModuleNamespaceObject.h new file mode 100644 index 0000000000..573d8a936f --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/ModuleNamespaceObject.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022, David Tuin + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace JS { + +class ModuleNamespaceObject final : public Object { + JS_OBJECT(ModuleNamespaceObject, Object); + +public: + ModuleNamespaceObject(GlobalObject&, Module* module, Vector exports); + + // 10.4.6 Module Namespace Exotic Objects, https://tc39.es/ecma262/#sec-module-namespace-exotic-objects + + virtual ThrowCompletionOr internal_get_prototype_of() const override; + virtual ThrowCompletionOr internal_set_prototype_of(Object* prototype) override; + virtual ThrowCompletionOr internal_is_extensible() const override; + virtual ThrowCompletionOr internal_prevent_extensions() override; + virtual ThrowCompletionOr> internal_get_own_property(PropertyKey const&) const override; + virtual ThrowCompletionOr internal_define_own_property(PropertyKey const&, PropertyDescriptor const&) override; + virtual ThrowCompletionOr internal_has_property(PropertyKey const&) const override; + virtual ThrowCompletionOr internal_get(PropertyKey const&, Value receiver) const override; + virtual ThrowCompletionOr internal_set(PropertyKey const&, Value value, Value receiver) override; + virtual ThrowCompletionOr internal_delete(PropertyKey const&) override; + virtual ThrowCompletionOr 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 m_exports; // [[Exports]] +}; + +}