瀏覽代碼

LibJS: Add the WeakSet built-in object

Idan Horowitz 4 年之前
父節點
當前提交
8b6beac5ce

+ 3 - 0
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

+ 2 - 1
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 \

+ 3 - 0
Userland/Libraries/LibJS/Runtime/GlobalObject.cpp

@@ -60,6 +60,8 @@
 #include <LibJS/Runtime/TypedArrayConstructor.h>
 #include <LibJS/Runtime/TypedArrayPrototype.h>
 #include <LibJS/Runtime/Value.h>
+#include <LibJS/Runtime/WeakSetConstructor.h>
+#include <LibJS/Runtime/WeakSetPrototype.h>
 
 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);
 

+ 25 - 0
Userland/Libraries/LibJS/Runtime/WeakSet.cpp

@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibJS/Runtime/WeakSet.h>
+
+namespace JS {
+
+WeakSet* WeakSet::create(GlobalObject& global_object)
+{
+    return global_object.heap().allocate<WeakSet>(global_object, *global_object.weak_set_prototype());
+}
+
+WeakSet::WeakSet(Object& prototype)
+    : Object(prototype)
+{
+}
+
+WeakSet::~WeakSet()
+{
+}
+
+}

+ 31 - 0
Userland/Libraries/LibJS/Runtime/WeakSet.h

@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/HashTable.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Object.h>
+
+namespace JS {
+
+class WeakSet : public Object {
+    JS_OBJECT(WeakSet, Object);
+
+public:
+    static WeakSet* create(GlobalObject&);
+
+    explicit WeakSet(Object& prototype);
+    virtual ~WeakSet() override;
+
+    HashTable<Object*> const& values() const { return m_values; };
+    HashTable<Object*>& values() { return m_values; };
+
+private:
+    HashTable<Object*> m_values;
+};
+
+}

+ 64 - 0
Userland/Libraries/LibJS/Runtime/WeakSetConstructor.cpp

@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/IteratorOperations.h>
+#include <LibJS/Runtime/WeakSet.h>
+#include <LibJS/Runtime/WeakSetConstructor.h>
+
+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<TypeError>(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<TypeError>(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;
+}
+
+}

+ 28 - 0
Userland/Libraries/LibJS/Runtime/WeakSetConstructor.h

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/NativeFunction.h>
+
+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; }
+};
+
+}

+ 41 - 0
Userland/Libraries/LibJS/Runtime/WeakSetPrototype.cpp

@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/HashTable.h>
+#include <LibJS/Runtime/WeakSetPrototype.h>
+
+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<WeakSet>(this_object)) {
+        vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "WeakSet");
+        return nullptr;
+    }
+    return static_cast<WeakSet*>(this_object);
+}
+
+}

+ 23 - 0
Userland/Libraries/LibJS/Runtime/WeakSetPrototype.h

@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/WeakSet.h>
+
+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&);
+}

+ 30 - 0
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();
+    });
+});