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:
parent
6913f06b6f
commit
7eba63a8a3
12 changed files with 268 additions and 1 deletions
|
@ -104,6 +104,9 @@ set(SOURCES
|
||||||
Runtime/WeakMap.cpp
|
Runtime/WeakMap.cpp
|
||||||
Runtime/WeakMapConstructor.cpp
|
Runtime/WeakMapConstructor.cpp
|
||||||
Runtime/WeakMapPrototype.cpp
|
Runtime/WeakMapPrototype.cpp
|
||||||
|
Runtime/WeakRef.cpp
|
||||||
|
Runtime/WeakRefConstructor.cpp
|
||||||
|
Runtime/WeakRefPrototype.cpp
|
||||||
Runtime/WeakSet.cpp
|
Runtime/WeakSet.cpp
|
||||||
Runtime/WeakSetConstructor.cpp
|
Runtime/WeakSetConstructor.cpp
|
||||||
Runtime/WeakSetPrototype.cpp
|
Runtime/WeakSetPrototype.cpp
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
__JS_ENUMERATE(StringObject, string, StringPrototype, StringConstructor, void) \
|
__JS_ENUMERATE(StringObject, string, StringPrototype, StringConstructor, void) \
|
||||||
__JS_ENUMERATE(SymbolObject, symbol, SymbolPrototype, SymbolConstructor, void) \
|
__JS_ENUMERATE(SymbolObject, symbol, SymbolPrototype, SymbolConstructor, void) \
|
||||||
__JS_ENUMERATE(WeakMap, weak_map, WeakMapPrototype, WeakMapConstructor, 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)
|
__JS_ENUMERATE(WeakSet, weak_set, WeakSetPrototype, WeakSetConstructor, void)
|
||||||
|
|
||||||
#define JS_ENUMERATE_NATIVE_OBJECTS \
|
#define JS_ENUMERATE_NATIVE_OBJECTS \
|
||||||
|
|
|
@ -93,6 +93,7 @@ namespace JS {
|
||||||
P(defineProperties) \
|
P(defineProperties) \
|
||||||
P(defineProperty) \
|
P(defineProperty) \
|
||||||
P(deleteProperty) \
|
P(deleteProperty) \
|
||||||
|
P(deref) \
|
||||||
P(description) \
|
P(description) \
|
||||||
P(done) \
|
P(done) \
|
||||||
P(dotAll) \
|
P(dotAll) \
|
||||||
|
|
|
@ -64,6 +64,8 @@
|
||||||
#include <LibJS/Runtime/Value.h>
|
#include <LibJS/Runtime/Value.h>
|
||||||
#include <LibJS/Runtime/WeakMapConstructor.h>
|
#include <LibJS/Runtime/WeakMapConstructor.h>
|
||||||
#include <LibJS/Runtime/WeakMapPrototype.h>
|
#include <LibJS/Runtime/WeakMapPrototype.h>
|
||||||
|
#include <LibJS/Runtime/WeakRefConstructor.h>
|
||||||
|
#include <LibJS/Runtime/WeakRefPrototype.h>
|
||||||
#include <LibJS/Runtime/WeakSetConstructor.h>
|
#include <LibJS/Runtime/WeakSetConstructor.h>
|
||||||
#include <LibJS/Runtime/WeakSetPrototype.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.String, m_string_constructor, m_string_prototype);
|
||||||
add_constructor(vm.names.Symbol, m_symbol_constructor, m_symbol_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.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);
|
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);
|
initialize_constructor(vm.names.TypedArray, m_typed_array_constructor, m_typed_array_prototype);
|
||||||
|
|
|
@ -19,12 +19,22 @@ public:
|
||||||
}
|
}
|
||||||
virtual ~WeakContainer()
|
virtual ~WeakContainer()
|
||||||
{
|
{
|
||||||
m_heap.did_destroy_weak_container({}, *this);
|
deregister();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void remove_sweeped_cells(Badge<Heap>, Vector<Cell*>&) = 0;
|
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:
|
private:
|
||||||
|
bool m_registered { true };
|
||||||
Heap& m_heap;
|
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