mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 08:47:34 +00:00
LibJS: Add the WeakRef built-in object
This commit is contained in:
parent
6913f06b6f
commit
7eba63a8a3
12 changed files with 268 additions and 1 deletions
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -93,6 +93,7 @@ namespace JS {
|
|||
P(defineProperties) \
|
||||
P(defineProperty) \
|
||||
P(deleteProperty) \
|
||||
P(deref) \
|
||||
P(description) \
|
||||
P(done) \
|
||||
P(dotAll) \
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
50
Userland/Libraries/LibJS/Runtime/WeakRef.cpp
Normal file
50
Userland/Libraries/LibJS/Runtime/WeakRef.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
39
Userland/Libraries/LibJS/Runtime/WeakRef.h
Normal file
39
Userland/Libraries/LibJS/Runtime/WeakRef.h
Normal 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 };
|
||||
};
|
||||
|
||||
}
|
52
Userland/Libraries/LibJS/Runtime/WeakRefConstructor.cpp
Normal file
52
Userland/Libraries/LibJS/Runtime/WeakRefConstructor.cpp
Normal 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());
|
||||
}
|
||||
|
||||
}
|
28
Userland/Libraries/LibJS/Runtime/WeakRefConstructor.h
Normal file
28
Userland/Libraries/LibJS/Runtime/WeakRefConstructor.h
Normal 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; }
|
||||
};
|
||||
|
||||
}
|
28
Userland/Libraries/LibJS/Runtime/WeakRefPrototype.cpp
Normal file
28
Userland/Libraries/LibJS/Runtime/WeakRefPrototype.cpp
Normal 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()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
22
Userland/Libraries/LibJS/Runtime/WeakRefPrototype.h
Normal file
22
Userland/Libraries/LibJS/Runtime/WeakRefPrototype.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
30
Userland/Libraries/LibJS/Tests/builtins/WeakRef/WeakRef.js
Normal file
30
Userland/Libraries/LibJS/Tests/builtins/WeakRef/WeakRef.js
Normal 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();
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue