diff options
author | Idan Horowitz <idan.horowitz@gmail.com> | 2021-06-12 17:38:34 +0300 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-06-12 18:39:23 +0100 |
commit | 7eba63a8a30aa155c13bd2b475294c3f327fa9a1 (patch) | |
tree | 9b73a27dbc7e1f47d800d151879672985cda3c71 /Userland/Libraries | |
parent | 6913f06b6f8c58be771caa5af155d775e570aa73 (diff) | |
download | serenity-7eba63a8a30aa155c13bd2b475294c3f327fa9a1.zip |
LibJS: Add the WeakRef built-in object
Diffstat (limited to 'Userland/Libraries')
-rw-r--r-- | Userland/Libraries/LibJS/CMakeLists.txt | 3 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Forward.h | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/GlobalObject.cpp | 3 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/WeakContainer.h | 12 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/WeakRef.cpp | 50 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/WeakRef.h | 39 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/WeakRefConstructor.cpp | 52 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/WeakRefConstructor.h | 28 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/WeakRefPrototype.cpp | 28 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/WeakRefPrototype.h | 22 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Tests/builtins/WeakRef/WeakRef.js | 30 |
12 files changed, 268 insertions, 1 deletions
diff --git a/Userland/Libraries/LibJS/CMakeLists.txt b/Userland/Libraries/LibJS/CMakeLists.txt index 8ad3f792d3..76fee7c8a7 100644 --- a/Userland/Libraries/LibJS/CMakeLists.txt +++ b/Userland/Libraries/LibJS/CMakeLists.txt @@ -104,6 +104,9 @@ set(SOURCES Runtime/WeakMap.cpp Runtime/WeakMapConstructor.cpp Runtime/WeakMapPrototype.cpp + Runtime/WeakRef.cpp + Runtime/WeakRefConstructor.cpp + Runtime/WeakRefPrototype.cpp Runtime/WeakSet.cpp Runtime/WeakSetConstructor.cpp Runtime/WeakSetPrototype.cpp diff --git a/Userland/Libraries/LibJS/Forward.h b/Userland/Libraries/LibJS/Forward.h index b3ad3fd32b..1071f90645 100644 --- a/Userland/Libraries/LibJS/Forward.h +++ b/Userland/Libraries/LibJS/Forward.h @@ -42,6 +42,7 @@ __JS_ENUMERATE(StringObject, string, StringPrototype, StringConstructor, void) \ __JS_ENUMERATE(SymbolObject, symbol, SymbolPrototype, SymbolConstructor, void) \ __JS_ENUMERATE(WeakMap, weak_map, WeakMapPrototype, WeakMapConstructor, void) \ + __JS_ENUMERATE(WeakRef, weak_ref, WeakRefPrototype, WeakRefConstructor, void) \ __JS_ENUMERATE(WeakSet, weak_set, WeakSetPrototype, WeakSetConstructor, void) #define JS_ENUMERATE_NATIVE_OBJECTS \ diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h index baea3fbf69..9f38eb08a5 100644 --- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h +++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h @@ -93,6 +93,7 @@ namespace JS { P(defineProperties) \ P(defineProperty) \ P(deleteProperty) \ + P(deref) \ P(description) \ P(done) \ P(dotAll) \ diff --git a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp index 246cbc25ec..98bdebd4b9 100644 --- a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -64,6 +64,8 @@ #include <LibJS/Runtime/Value.h> #include <LibJS/Runtime/WeakMapConstructor.h> #include <LibJS/Runtime/WeakMapPrototype.h> +#include <LibJS/Runtime/WeakRefConstructor.h> +#include <LibJS/Runtime/WeakRefPrototype.h> #include <LibJS/Runtime/WeakSetConstructor.h> #include <LibJS/Runtime/WeakSetPrototype.h> @@ -157,6 +159,7 @@ void GlobalObject::initialize_global_object() 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.WeakMap, m_weak_map_constructor, m_weak_map_prototype); + add_constructor(vm.names.WeakRef, m_weak_ref_constructor, m_weak_ref_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/WeakContainer.h b/Userland/Libraries/LibJS/Runtime/WeakContainer.h index 04cf3a83ac..493f4fde51 100644 --- a/Userland/Libraries/LibJS/Runtime/WeakContainer.h +++ b/Userland/Libraries/LibJS/Runtime/WeakContainer.h @@ -19,12 +19,22 @@ public: } virtual ~WeakContainer() { - m_heap.did_destroy_weak_container({}, *this); + deregister(); } virtual void remove_sweeped_cells(Badge<Heap>, Vector<Cell*>&) = 0; +protected: + void deregister() + { + if (!m_registered) + return; + m_heap.did_destroy_weak_container({}, *this); + m_registered = false; + } + private: + bool m_registered { true }; Heap& m_heap; }; diff --git a/Userland/Libraries/LibJS/Runtime/WeakRef.cpp b/Userland/Libraries/LibJS/Runtime/WeakRef.cpp new file mode 100644 index 0000000000..1e71557249 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/WeakRef.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibJS/Runtime/WeakRef.h> + +namespace JS { + +WeakRef* WeakRef::create(GlobalObject& global_object, Object* object) +{ + return global_object.heap().allocate<WeakRef>(global_object, *global_object.weak_ref_prototype(), object); +} + +WeakRef::WeakRef(Object& prototype, Object* object) + : Object(prototype) + , WeakContainer(heap()) + , m_value(object) + , m_last_execution_generation(vm().execution_generation()) +{ +} + +WeakRef::~WeakRef() +{ +} + +void WeakRef::remove_sweeped_cells(Badge<Heap>, Vector<Cell*>& cells) +{ + VERIFY(m_value); + for (auto* cell : cells) { + if (m_value != cell) + continue; + m_value = nullptr; + // This is an optimization, we deregister from the garbage collector early (even if we were not garbage collected ourself yet) + // to reduce the garbage collection overhead, which we can do because a cleared weak ref cannot be reused. + WeakContainer::deregister(); + break; + } +} + +void WeakRef::visit_edges(Visitor& visitor) +{ + Object::visit_edges(visitor); + + if (vm().execution_generation() == m_last_execution_generation) + visitor.visit(m_value); +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/WeakRef.h b/Userland/Libraries/LibJS/Runtime/WeakRef.h new file mode 100644 index 0000000000..43f03e3c2c --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/WeakRef.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <LibJS/Runtime/GlobalObject.h> +#include <LibJS/Runtime/Object.h> +#include <LibJS/Runtime/WeakContainer.h> + +namespace JS { + +class WeakRef final + : public Object + , public WeakContainer { + JS_OBJECT(WeakRef, Object); + +public: + static WeakRef* create(GlobalObject&, Object*); + + explicit WeakRef(Object& prototype, Object*); + virtual ~WeakRef() override; + + Object* value() const { return m_value; }; + + void update_execution_generation() { m_last_execution_generation = vm().execution_generation(); }; + + virtual void remove_sweeped_cells(Badge<Heap>, Vector<Cell*>&) override; + +private: + virtual void visit_edges(Visitor&) override; + + Object* m_value { nullptr }; + u32 m_last_execution_generation { 0 }; +}; + +} diff --git a/Userland/Libraries/LibJS/Runtime/WeakRefConstructor.cpp b/Userland/Libraries/LibJS/Runtime/WeakRefConstructor.cpp new file mode 100644 index 0000000000..6940580ecc --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/WeakRefConstructor.cpp @@ -0,0 +1,52 @@ +/* + * 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/WeakRef.h> +#include <LibJS/Runtime/WeakRefConstructor.h> + +namespace JS { + +WeakRefConstructor::WeakRefConstructor(GlobalObject& global_object) + : NativeFunction(vm().names.WeakRef, *global_object.function_prototype()) +{ +} + +void WeakRefConstructor::initialize(GlobalObject& global_object) +{ + auto& vm = this->vm(); + NativeFunction::initialize(global_object); + define_property(vm.names.prototype, global_object.weak_ref_prototype(), 0); + define_property(vm.names.length, Value(1), Attribute::Configurable); +} + +WeakRefConstructor::~WeakRefConstructor() +{ +} + +Value WeakRefConstructor::call() +{ + auto& vm = this->vm(); + vm.throw_exception<TypeError>(global_object(), ErrorType::ConstructorWithoutNew, vm.names.WeakRef); + return {}; +} + +// 26.1.1.1 WeakRef ( target ), https://tc39.es/ecma262/#sec-weak-ref-target +Value WeakRefConstructor::construct(Function&) +{ + auto& vm = this->vm(); + auto target = vm.argument(0); + if (!target.is_object()) { + vm.throw_exception<TypeError>(global_object(), ErrorType::NotAnObject, target.to_string_without_side_effects()); + return {}; + } + + // FIXME: Use OrdinaryCreateFromConstructor(newTarget, "%WeakRef.prototype%") + return WeakRef::create(global_object(), &target.as_object()); +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/WeakRefConstructor.h b/Userland/Libraries/LibJS/Runtime/WeakRefConstructor.h new file mode 100644 index 0000000000..543bc5049f --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/WeakRefConstructor.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 WeakRefConstructor final : public NativeFunction { + JS_OBJECT(WeakRefConstructor, NativeFunction); + +public: + explicit WeakRefConstructor(GlobalObject&); + virtual void initialize(GlobalObject&) override; + virtual ~WeakRefConstructor() 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/WeakRefPrototype.cpp b/Userland/Libraries/LibJS/Runtime/WeakRefPrototype.cpp new file mode 100644 index 0000000000..2967ee4aa4 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/WeakRefPrototype.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibJS/Runtime/WeakRefPrototype.h> + +namespace JS { + +WeakRefPrototype::WeakRefPrototype(GlobalObject& global_object) + : Object(*global_object.object_prototype()) +{ +} + +void WeakRefPrototype::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.WeakRef), Attribute::Configurable); +} + +WeakRefPrototype::~WeakRefPrototype() +{ +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/WeakRefPrototype.h b/Userland/Libraries/LibJS/Runtime/WeakRefPrototype.h new file mode 100644 index 0000000000..56cac90c88 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/WeakRefPrototype.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <LibJS/Runtime/WeakRef.h> + +namespace JS { + +class WeakRefPrototype final : public Object { + JS_OBJECT(WeakRefPrototype, Object); + +public: + WeakRefPrototype(GlobalObject&); + virtual void initialize(GlobalObject&) override; + virtual ~WeakRefPrototype() override; +}; + +} diff --git a/Userland/Libraries/LibJS/Tests/builtins/WeakRef/WeakRef.js b/Userland/Libraries/LibJS/Tests/builtins/WeakRef/WeakRef.js new file mode 100644 index 0000000000..d098bf2ed6 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/WeakRef/WeakRef.js @@ -0,0 +1,30 @@ +test("constructor properties", () => { + expect(WeakRef).toHaveLength(1); + expect(WeakRef.name).toBe("WeakRef"); +}); + +describe("errors", () => { + test("invalid array iterators", () => { + [-100, Infinity, NaN, 152n, undefined].forEach(value => { + expect(() => { + new WeakRef(value); + }).toThrowWithMessage(TypeError, "is not an object"); + }); + }); + test("called without new", () => { + expect(() => { + WeakRef(); + }).toThrowWithMessage(TypeError, "WeakRef constructor must be called with 'new'"); + }); +}); + +describe("normal behavior", () => { + test("typeof", () => { + expect(typeof new WeakRef({})).toBe("object"); + }); + + test("constructor with single object argument", () => { + var a = new WeakRef({}); + expect(a instanceof WeakRef).toBeTrue(); + }); +}); |