1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 08:17:45 +00:00

LibJS: Add the WeakRef built-in object

This commit is contained in:
Idan Horowitz 2021-06-12 17:38:34 +03:00 committed by Linus Groh
parent 6913f06b6f
commit 7eba63a8a3
12 changed files with 268 additions and 1 deletions

View file

@ -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

View file

@ -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 \

View file

@ -93,6 +93,7 @@ namespace JS {
P(defineProperties) \
P(defineProperty) \
P(deleteProperty) \
P(deref) \
P(description) \
P(done) \
P(dotAll) \

View file

@ -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);

View file

@ -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;
};

View file

@ -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);
}
}

View file

@ -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 };
};
}

View file

@ -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());
}
}

View file

@ -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; }
};
}

View file

@ -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()
{
}
}

View file

@ -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;
};
}

View file

@ -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();
});
});