diff --git a/Userland/Libraries/LibJS/CMakeLists.txt b/Userland/Libraries/LibJS/CMakeLists.txt index d28e72c016..ffcba7b6a1 100644 --- a/Userland/Libraries/LibJS/CMakeLists.txt +++ b/Userland/Libraries/LibJS/CMakeLists.txt @@ -97,6 +97,9 @@ set(SOURCES Runtime/TypedArrayPrototype.cpp Runtime/VM.cpp Runtime/Value.cpp + Runtime/WeakSet.cpp + Runtime/WeakSetConstructor.cpp + Runtime/WeakSetPrototype.cpp Runtime/WithScope.cpp SyntaxHighlighter.cpp Token.cpp diff --git a/Userland/Libraries/LibJS/Forward.h b/Userland/Libraries/LibJS/Forward.h index 9e0a6058ce..2af2650c1c 100644 --- a/Userland/Libraries/LibJS/Forward.h +++ b/Userland/Libraries/LibJS/Forward.h @@ -39,7 +39,8 @@ __JS_ENUMERATE(RegExpObject, regexp, RegExpPrototype, RegExpConstructor, void) \ __JS_ENUMERATE(Set, set, SetPrototype, SetConstructor, void) \ __JS_ENUMERATE(StringObject, string, StringPrototype, StringConstructor, void) \ - __JS_ENUMERATE(SymbolObject, symbol, SymbolPrototype, SymbolConstructor, void) + __JS_ENUMERATE(SymbolObject, symbol, SymbolPrototype, SymbolConstructor, void) \ + __JS_ENUMERATE(WeakSet, weak_set, WeakSetPrototype, WeakSetConstructor, void) #define JS_ENUMERATE_NATIVE_OBJECTS \ JS_ENUMERATE_NATIVE_OBJECTS_EXCLUDING_TEMPLATES \ diff --git a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp index f168a11d24..8d162b2f15 100644 --- a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -60,6 +60,8 @@ #include #include #include +#include +#include namespace JS { @@ -143,6 +145,7 @@ void GlobalObject::initialize_global_object() add_constructor(vm.names.Set, m_set_constructor, m_set_prototype); add_constructor(vm.names.String, m_string_constructor, m_string_prototype); add_constructor(vm.names.Symbol, m_symbol_constructor, m_symbol_prototype); + add_constructor(vm.names.WeakSet, m_weak_set_constructor, m_weak_set_prototype); initialize_constructor(vm.names.TypedArray, m_typed_array_constructor, m_typed_array_prototype); diff --git a/Userland/Libraries/LibJS/Runtime/WeakSet.cpp b/Userland/Libraries/LibJS/Runtime/WeakSet.cpp new file mode 100644 index 0000000000..50bf41c0ba --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/WeakSet.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021, Idan Horowitz + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace JS { + +WeakSet* WeakSet::create(GlobalObject& global_object) +{ + return global_object.heap().allocate(global_object, *global_object.weak_set_prototype()); +} + +WeakSet::WeakSet(Object& prototype) + : Object(prototype) +{ +} + +WeakSet::~WeakSet() +{ +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/WeakSet.h b/Userland/Libraries/LibJS/Runtime/WeakSet.h new file mode 100644 index 0000000000..f8c06ad017 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/WeakSet.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021, Idan Horowitz + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace JS { + +class WeakSet : public Object { + JS_OBJECT(WeakSet, Object); + +public: + static WeakSet* create(GlobalObject&); + + explicit WeakSet(Object& prototype); + virtual ~WeakSet() override; + + HashTable const& values() const { return m_values; }; + HashTable& values() { return m_values; }; + +private: + HashTable m_values; +}; + +} diff --git a/Userland/Libraries/LibJS/Runtime/WeakSetConstructor.cpp b/Userland/Libraries/LibJS/Runtime/WeakSetConstructor.cpp new file mode 100644 index 0000000000..e42bee40ab --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/WeakSetConstructor.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021, Idan Horowitz + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +namespace JS { + +WeakSetConstructor::WeakSetConstructor(GlobalObject& global_object) + : NativeFunction(vm().names.WeakSet, *global_object.function_prototype()) +{ +} + +void WeakSetConstructor::initialize(GlobalObject& global_object) +{ + auto& vm = this->vm(); + NativeFunction::initialize(global_object); + define_property(vm.names.prototype, global_object.weak_set_prototype(), 0); + define_property(vm.names.length, Value(0), Attribute::Configurable); +} + +WeakSetConstructor::~WeakSetConstructor() +{ +} + +Value WeakSetConstructor::call() +{ + auto& vm = this->vm(); + vm.throw_exception(global_object(), ErrorType::ConstructorWithoutNew, vm.names.WeakSet); + return {}; +} + +Value WeakSetConstructor::construct(Function&) +{ + auto& vm = this->vm(); + if (vm.argument(0).is_nullish()) + return WeakSet::create(global_object()); + + auto* weak_set = WeakSet::create(global_object()); + auto adder = weak_set->get(vm.names.add); + if (vm.exception()) + return {}; + if (!adder.is_function()) { + vm.throw_exception(global_object(), ErrorType::NotAFunction, "'add' property of WeakSet"); + return {}; + } + get_iterator_values(global_object(), vm.argument(0), [&](Value iterator_value) { + if (vm.exception()) + return IterationDecision::Break; + (void)vm.call(adder.as_function(), Value(weak_set), iterator_value); + return vm.exception() ? IterationDecision::Break : IterationDecision::Continue; + }); + if (vm.exception()) + return {}; + return weak_set; +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/WeakSetConstructor.h b/Userland/Libraries/LibJS/Runtime/WeakSetConstructor.h new file mode 100644 index 0000000000..0e3c5838ab --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/WeakSetConstructor.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021, Idan Horowitz + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace JS { + +class WeakSetConstructor final : public NativeFunction { + JS_OBJECT(WeakSetConstructor, NativeFunction); + +public: + explicit WeakSetConstructor(GlobalObject&); + virtual void initialize(GlobalObject&) override; + virtual ~WeakSetConstructor() override; + + virtual Value call() override; + virtual Value construct(Function&) override; + +private: + virtual bool has_constructor() const override { return true; } +}; + +} diff --git a/Userland/Libraries/LibJS/Runtime/WeakSetPrototype.cpp b/Userland/Libraries/LibJS/Runtime/WeakSetPrototype.cpp new file mode 100644 index 0000000000..8058621a4d --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/WeakSetPrototype.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021, Idan Horowitz + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace JS { + +WeakSetPrototype::WeakSetPrototype(GlobalObject& global_object) + : Object(*global_object.object_prototype()) +{ +} + +void WeakSetPrototype::initialize(GlobalObject& global_object) +{ + auto& vm = this->vm(); + Object::initialize(global_object); + + define_property(vm.well_known_symbol_to_string_tag(), js_string(global_object.heap(), vm.names.WeakSet), Attribute::Configurable); +} + +WeakSetPrototype::~WeakSetPrototype() +{ +} + +WeakSet* WeakSetPrototype::typed_this(VM& vm, GlobalObject& global_object) +{ + auto* this_object = vm.this_value(global_object).to_object(global_object); + if (!this_object) + return {}; + if (!is(this_object)) { + vm.throw_exception(global_object, ErrorType::NotA, "WeakSet"); + return nullptr; + } + return static_cast(this_object); +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/WeakSetPrototype.h b/Userland/Libraries/LibJS/Runtime/WeakSetPrototype.h new file mode 100644 index 0000000000..31a6a2cf96 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/WeakSetPrototype.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021, Idan Horowitz + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace JS { + +class WeakSetPrototype final : public Object { + JS_OBJECT(WeakSetPrototype, Object); + +public: + WeakSetPrototype(GlobalObject&); + virtual void initialize(GlobalObject&) override; + virtual ~WeakSetPrototype() override; + +private: + static WeakSet* typed_this(VM&, GlobalObject&); +} diff --git a/Userland/Libraries/LibJS/Tests/builtins/WeakSet/WeakSet.js b/Userland/Libraries/LibJS/Tests/builtins/WeakSet/WeakSet.js new file mode 100644 index 0000000000..61400a048d --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/WeakSet/WeakSet.js @@ -0,0 +1,30 @@ +test("constructor properties", () => { + expect(WeakSet).toHaveLength(0); + expect(WeakSet.name).toBe("WeakSet"); +}); + +describe("errors", () => { + test("invalid array iterators", () => { + [-100, Infinity, NaN, {}, 152n].forEach(value => { + expect(() => { + new WeakSet(value); + }).toThrowWithMessage(TypeError, "is not iterable"); + }); + }); + test("called without new", () => { + expect(() => { + WeakSet(); + }).toThrowWithMessage(TypeError, "WeakSet constructor must be called with 'new'"); + }); +}); + +describe("normal behavior", () => { + test("typeof", () => { + expect(typeof new WeakSet()).toBe("object"); + }); + + test("constructor with single array argument", () => { + var a = new WeakSet([{ a: 1 }, { a: 2 }, { a: 3 }]); + expect(a instanceof WeakSet).toBeTrue(); + }); +});