mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 02:27:43 +00:00
LibJS: Add the FinalizationRegistry built-in object
As well as the needed functionality in VM to enqueue and run cleanup jobs for the FinalizationRegistry instances.
This commit is contained in:
parent
8c7fe8d6c8
commit
de9fa6622a
14 changed files with 365 additions and 20 deletions
|
@ -53,6 +53,9 @@ set(SOURCES
|
||||||
Runtime/ErrorPrototype.cpp
|
Runtime/ErrorPrototype.cpp
|
||||||
Runtime/ErrorTypes.cpp
|
Runtime/ErrorTypes.cpp
|
||||||
Runtime/Exception.cpp
|
Runtime/Exception.cpp
|
||||||
|
Runtime/FinalizationRegistry.cpp
|
||||||
|
Runtime/FinalizationRegistryConstructor.cpp
|
||||||
|
Runtime/FinalizationRegistryPrototype.cpp
|
||||||
Runtime/FunctionConstructor.cpp
|
Runtime/FunctionConstructor.cpp
|
||||||
Runtime/Function.cpp
|
Runtime/Function.cpp
|
||||||
Runtime/FunctionPrototype.cpp
|
Runtime/FunctionPrototype.cpp
|
||||||
|
|
|
@ -25,26 +25,27 @@
|
||||||
void name([[maybe_unused]] JS::VM& vm, [[maybe_unused]] JS::GlobalObject& global_object, [[maybe_unused]] JS::Value value)
|
void name([[maybe_unused]] JS::VM& vm, [[maybe_unused]] JS::GlobalObject& global_object, [[maybe_unused]] JS::Value value)
|
||||||
|
|
||||||
// NOTE: Proxy is not included here as it doesn't have a prototype - m_proxy_constructor is initialized separately.
|
// NOTE: Proxy is not included here as it doesn't have a prototype - m_proxy_constructor is initialized separately.
|
||||||
#define JS_ENUMERATE_NATIVE_OBJECTS_EXCLUDING_TEMPLATES \
|
#define JS_ENUMERATE_NATIVE_OBJECTS_EXCLUDING_TEMPLATES \
|
||||||
__JS_ENUMERATE(AggregateError, aggregate_error, AggregateErrorPrototype, AggregateErrorConstructor, void) \
|
__JS_ENUMERATE(AggregateError, aggregate_error, AggregateErrorPrototype, AggregateErrorConstructor, void) \
|
||||||
__JS_ENUMERATE(Array, array, ArrayPrototype, ArrayConstructor, void) \
|
__JS_ENUMERATE(Array, array, ArrayPrototype, ArrayConstructor, void) \
|
||||||
__JS_ENUMERATE(ArrayBuffer, array_buffer, ArrayBufferPrototype, ArrayBufferConstructor, void) \
|
__JS_ENUMERATE(ArrayBuffer, array_buffer, ArrayBufferPrototype, ArrayBufferConstructor, void) \
|
||||||
__JS_ENUMERATE(BigIntObject, bigint, BigIntPrototype, BigIntConstructor, void) \
|
__JS_ENUMERATE(BigIntObject, bigint, BigIntPrototype, BigIntConstructor, void) \
|
||||||
__JS_ENUMERATE(BooleanObject, boolean, BooleanPrototype, BooleanConstructor, void) \
|
__JS_ENUMERATE(BooleanObject, boolean, BooleanPrototype, BooleanConstructor, void) \
|
||||||
__JS_ENUMERATE(DataView, data_view, DataViewPrototype, DataViewConstructor, void) \
|
__JS_ENUMERATE(DataView, data_view, DataViewPrototype, DataViewConstructor, void) \
|
||||||
__JS_ENUMERATE(Date, date, DatePrototype, DateConstructor, void) \
|
__JS_ENUMERATE(Date, date, DatePrototype, DateConstructor, void) \
|
||||||
__JS_ENUMERATE(Error, error, ErrorPrototype, ErrorConstructor, void) \
|
__JS_ENUMERATE(Error, error, ErrorPrototype, ErrorConstructor, void) \
|
||||||
__JS_ENUMERATE(Function, function, FunctionPrototype, FunctionConstructor, void) \
|
__JS_ENUMERATE(FinalizationRegistry, finalization_registry, FinalizationRegistryPrototype, FinalizationRegistryConstructor, void) \
|
||||||
__JS_ENUMERATE(Map, map, MapPrototype, MapConstructor, void) \
|
__JS_ENUMERATE(Function, function, FunctionPrototype, FunctionConstructor, void) \
|
||||||
__JS_ENUMERATE(NumberObject, number, NumberPrototype, NumberConstructor, void) \
|
__JS_ENUMERATE(Map, map, MapPrototype, MapConstructor, void) \
|
||||||
__JS_ENUMERATE(Object, object, ObjectPrototype, ObjectConstructor, void) \
|
__JS_ENUMERATE(NumberObject, number, NumberPrototype, NumberConstructor, void) \
|
||||||
__JS_ENUMERATE(Promise, promise, PromisePrototype, PromiseConstructor, void) \
|
__JS_ENUMERATE(Object, object, ObjectPrototype, ObjectConstructor, void) \
|
||||||
__JS_ENUMERATE(RegExpObject, regexp, RegExpPrototype, RegExpConstructor, void) \
|
__JS_ENUMERATE(Promise, promise, PromisePrototype, PromiseConstructor, void) \
|
||||||
__JS_ENUMERATE(Set, set, SetPrototype, SetConstructor, void) \
|
__JS_ENUMERATE(RegExpObject, regexp, RegExpPrototype, RegExpConstructor, void) \
|
||||||
__JS_ENUMERATE(StringObject, string, StringPrototype, StringConstructor, void) \
|
__JS_ENUMERATE(Set, set, SetPrototype, SetConstructor, void) \
|
||||||
__JS_ENUMERATE(SymbolObject, symbol, SymbolPrototype, SymbolConstructor, void) \
|
__JS_ENUMERATE(StringObject, string, StringPrototype, StringConstructor, void) \
|
||||||
__JS_ENUMERATE(WeakMap, weak_map, WeakMapPrototype, WeakMapConstructor, void) \
|
__JS_ENUMERATE(SymbolObject, symbol, SymbolPrototype, SymbolConstructor, void) \
|
||||||
__JS_ENUMERATE(WeakRef, weak_ref, WeakRefPrototype, WeakRefConstructor, 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 \
|
||||||
|
|
|
@ -64,6 +64,8 @@ void Interpreter::run(GlobalObject& global_object, const Program& program)
|
||||||
// in which case this is a no-op.
|
// in which case this is a no-op.
|
||||||
vm.run_queued_promise_jobs();
|
vm.run_queued_promise_jobs();
|
||||||
|
|
||||||
|
vm.run_queued_finalization_registry_cleanup_jobs();
|
||||||
|
|
||||||
vm.finish_execution_generation();
|
vm.finish_execution_generation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
86
Userland/Libraries/LibJS/Runtime/FinalizationRegistry.cpp
Normal file
86
Userland/Libraries/LibJS/Runtime/FinalizationRegistry.cpp
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <LibJS/Runtime/FinalizationRegistry.h>
|
||||||
|
|
||||||
|
namespace JS {
|
||||||
|
|
||||||
|
FinalizationRegistry* FinalizationRegistry::create(GlobalObject& global_object, Function& cleanup_callback)
|
||||||
|
{
|
||||||
|
return global_object.heap().allocate<FinalizationRegistry>(global_object, *global_object.finalization_registry_prototype(), cleanup_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
FinalizationRegistry::FinalizationRegistry(Object& prototype, Function& cleanup_callback)
|
||||||
|
: Object(prototype)
|
||||||
|
, WeakContainer(heap())
|
||||||
|
, m_cleanup_callback(&cleanup_callback)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FinalizationRegistry::~FinalizationRegistry()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinalizationRegistry::add_finalization_record(Cell& target, Value held_value, Object* unregister_token)
|
||||||
|
{
|
||||||
|
VERIFY(!held_value.is_empty());
|
||||||
|
m_records.append({ &target, held_value, unregister_token });
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FinalizationRegistry::remove_by_token(Object& unregister_token)
|
||||||
|
{
|
||||||
|
auto removed = false;
|
||||||
|
for (auto it = m_records.begin(); it != m_records.end(); ++it) {
|
||||||
|
if (it->unregister_token == &unregister_token) {
|
||||||
|
it.remove(m_records);
|
||||||
|
removed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinalizationRegistry::remove_sweeped_cells(Badge<Heap>, Vector<Cell*>& cells)
|
||||||
|
{
|
||||||
|
auto any_cells_were_sweeped = false;
|
||||||
|
for (auto cell : cells) {
|
||||||
|
for (auto& record : m_records) {
|
||||||
|
if (record.target != cell)
|
||||||
|
continue;
|
||||||
|
record.target = nullptr;
|
||||||
|
any_cells_were_sweeped = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (any_cells_were_sweeped)
|
||||||
|
vm().enqueue_finalization_registry_cleanup_job(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9.13 CleanupFinalizationRegistry ( finalizationRegistry ), https://tc39.es/ecma262/#sec-cleanup-finalization-registry
|
||||||
|
void FinalizationRegistry::cleanup(Function* callback)
|
||||||
|
{
|
||||||
|
auto& vm = this->vm();
|
||||||
|
auto cleanup_callback = callback ?: m_cleanup_callback;
|
||||||
|
for (auto it = m_records.begin(); it != m_records.end(); ++it) {
|
||||||
|
if (it->target != nullptr)
|
||||||
|
continue;
|
||||||
|
(void)vm.call(*cleanup_callback, js_undefined(), it->held_value);
|
||||||
|
it.remove(m_records);
|
||||||
|
if (vm.exception())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinalizationRegistry::visit_edges(Cell::Visitor& visitor)
|
||||||
|
{
|
||||||
|
Object::visit_edges(visitor);
|
||||||
|
visitor.visit(m_cleanup_callback);
|
||||||
|
for (auto& record : m_records) {
|
||||||
|
visitor.visit(record.held_value);
|
||||||
|
visitor.visit(record.unregister_token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
48
Userland/Libraries/LibJS/Runtime/FinalizationRegistry.h
Normal file
48
Userland/Libraries/LibJS/Runtime/FinalizationRegistry.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/SinglyLinkedList.h>
|
||||||
|
#include <LibJS/Runtime/Function.h>
|
||||||
|
#include <LibJS/Runtime/GlobalObject.h>
|
||||||
|
#include <LibJS/Runtime/Object.h>
|
||||||
|
#include <LibJS/Runtime/Value.h>
|
||||||
|
#include <LibJS/Runtime/WeakContainer.h>
|
||||||
|
|
||||||
|
namespace JS {
|
||||||
|
|
||||||
|
class FinalizationRegistry final
|
||||||
|
: public Object
|
||||||
|
, public WeakContainer {
|
||||||
|
JS_OBJECT(FinalizationRegistry, Object);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static FinalizationRegistry* create(GlobalObject&, Function&);
|
||||||
|
|
||||||
|
explicit FinalizationRegistry(Object& prototype, Function&);
|
||||||
|
virtual ~FinalizationRegistry() override;
|
||||||
|
|
||||||
|
void add_finalization_record(Cell& target, Value held_value, Object* unregister_token);
|
||||||
|
bool remove_by_token(Object& unregister_token);
|
||||||
|
void cleanup(Function* callback = nullptr);
|
||||||
|
|
||||||
|
virtual void remove_sweeped_cells(Badge<Heap>, Vector<Cell*>&) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void visit_edges(Visitor& visitor) override;
|
||||||
|
|
||||||
|
Function* m_cleanup_callback { nullptr };
|
||||||
|
|
||||||
|
struct FinalizationRecord {
|
||||||
|
Cell* target { nullptr };
|
||||||
|
Value held_value;
|
||||||
|
Object* unregister_token { nullptr };
|
||||||
|
};
|
||||||
|
SinglyLinkedList<FinalizationRecord> m_records;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <LibJS/Runtime/Error.h>
|
||||||
|
#include <LibJS/Runtime/FinalizationRegistry.h>
|
||||||
|
#include <LibJS/Runtime/FinalizationRegistryConstructor.h>
|
||||||
|
#include <LibJS/Runtime/GlobalObject.h>
|
||||||
|
|
||||||
|
namespace JS {
|
||||||
|
|
||||||
|
FinalizationRegistryConstructor::FinalizationRegistryConstructor(GlobalObject& global_object)
|
||||||
|
: NativeFunction(vm().names.FinalizationRegistry, *global_object.function_prototype())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinalizationRegistryConstructor::initialize(GlobalObject& global_object)
|
||||||
|
{
|
||||||
|
auto& vm = this->vm();
|
||||||
|
NativeFunction::initialize(global_object);
|
||||||
|
|
||||||
|
// 26.2.2.1 FinalizationRegistry.prototype, https://tc39.es/ecma262/#sec-finalization-registry.prototype
|
||||||
|
define_property(vm.names.prototype, global_object.finalization_registry_prototype(), 0);
|
||||||
|
|
||||||
|
define_property(vm.names.length, Value(1), Attribute::Configurable);
|
||||||
|
}
|
||||||
|
|
||||||
|
FinalizationRegistryConstructor::~FinalizationRegistryConstructor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// 26.2.1.1 FinalizationRegistry ( cleanupCallback ), https://tc39.es/ecma262/#sec-finalization-registry-cleanup-callback
|
||||||
|
Value FinalizationRegistryConstructor::call()
|
||||||
|
{
|
||||||
|
auto& vm = this->vm();
|
||||||
|
vm.throw_exception<TypeError>(global_object(), ErrorType::ConstructorWithoutNew, vm.names.FinalizationRegistry);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 26.2.1.1 FinalizationRegistry ( cleanupCallback ), https://tc39.es/ecma262/#sec-finalization-registry-cleanup-callback
|
||||||
|
Value FinalizationRegistryConstructor::construct(Function&)
|
||||||
|
{
|
||||||
|
auto& vm = this->vm();
|
||||||
|
auto cleanup_callback = vm.argument(0);
|
||||||
|
if (!cleanup_callback.is_function()) {
|
||||||
|
vm.throw_exception<TypeError>(global_object(), ErrorType::NotAFunction, cleanup_callback.to_string_without_side_effects());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Use OrdinaryCreateFromConstructor(NewTarget, "%FinalizationRegistry.prototype%")
|
||||||
|
return FinalizationRegistry::create(global_object(), cleanup_callback.as_function());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 FinalizationRegistryConstructor final : public NativeFunction {
|
||||||
|
JS_OBJECT(FinalizationRegistryConstructor, NativeFunction);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FinalizationRegistryConstructor(GlobalObject&);
|
||||||
|
virtual void initialize(GlobalObject&) override;
|
||||||
|
virtual ~FinalizationRegistryConstructor() override;
|
||||||
|
|
||||||
|
virtual Value call() override;
|
||||||
|
virtual Value construct(Function&) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual bool has_constructor() const override { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <LibJS/Runtime/FinalizationRegistryPrototype.h>
|
||||||
|
|
||||||
|
namespace JS {
|
||||||
|
|
||||||
|
FinalizationRegistryPrototype::FinalizationRegistryPrototype(GlobalObject& global_object)
|
||||||
|
: Object(*global_object.object_prototype())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinalizationRegistryPrototype::initialize(GlobalObject& global_object)
|
||||||
|
{
|
||||||
|
auto& vm = this->vm();
|
||||||
|
Object::initialize(global_object);
|
||||||
|
|
||||||
|
// 26.2.3.4 FinalizationRegistry.prototype [ @@toStringTag ], https://tc39.es/ecma262/#sec-finalization-registry.prototype-@@tostringtag
|
||||||
|
define_property(vm.well_known_symbol_to_string_tag(), js_string(global_object.heap(), vm.names.FinalizationRegistry.as_string()), Attribute::Configurable);
|
||||||
|
}
|
||||||
|
|
||||||
|
FinalizationRegistryPrototype::~FinalizationRegistryPrototype()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FinalizationRegistry* FinalizationRegistryPrototype::typed_this(VM& vm, GlobalObject& global_object)
|
||||||
|
{
|
||||||
|
auto* this_object = vm.this_value(global_object).to_object(global_object);
|
||||||
|
if (!this_object)
|
||||||
|
return nullptr;
|
||||||
|
if (!is<FinalizationRegistry>(this_object)) {
|
||||||
|
vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "FinalizationRegistry");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return static_cast<FinalizationRegistry*>(this_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibJS/Runtime/FinalizationRegistry.h>
|
||||||
|
|
||||||
|
namespace JS {
|
||||||
|
|
||||||
|
class FinalizationRegistryPrototype final : public Object {
|
||||||
|
JS_OBJECT(FinalizationRegistryPrototype, Object);
|
||||||
|
|
||||||
|
public:
|
||||||
|
FinalizationRegistryPrototype(GlobalObject&);
|
||||||
|
virtual void initialize(GlobalObject&) override;
|
||||||
|
virtual ~FinalizationRegistryPrototype() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static FinalizationRegistry* typed_this(VM&, GlobalObject&);
|
||||||
|
}
|
|
@ -33,6 +33,8 @@
|
||||||
#include <LibJS/Runtime/DatePrototype.h>
|
#include <LibJS/Runtime/DatePrototype.h>
|
||||||
#include <LibJS/Runtime/ErrorConstructor.h>
|
#include <LibJS/Runtime/ErrorConstructor.h>
|
||||||
#include <LibJS/Runtime/ErrorPrototype.h>
|
#include <LibJS/Runtime/ErrorPrototype.h>
|
||||||
|
#include <LibJS/Runtime/FinalizationRegistryConstructor.h>
|
||||||
|
#include <LibJS/Runtime/FinalizationRegistryPrototype.h>
|
||||||
#include <LibJS/Runtime/FunctionConstructor.h>
|
#include <LibJS/Runtime/FunctionConstructor.h>
|
||||||
#include <LibJS/Runtime/FunctionPrototype.h>
|
#include <LibJS/Runtime/FunctionPrototype.h>
|
||||||
#include <LibJS/Runtime/GlobalObject.h>
|
#include <LibJS/Runtime/GlobalObject.h>
|
||||||
|
@ -155,6 +157,7 @@ void GlobalObject::initialize_global_object()
|
||||||
add_constructor(vm.names.DataView, m_data_view_constructor, m_data_view_prototype);
|
add_constructor(vm.names.DataView, m_data_view_constructor, m_data_view_prototype);
|
||||||
add_constructor(vm.names.Date, m_date_constructor, m_date_prototype);
|
add_constructor(vm.names.Date, m_date_constructor, m_date_prototype);
|
||||||
add_constructor(vm.names.Error, m_error_constructor, m_error_prototype);
|
add_constructor(vm.names.Error, m_error_constructor, m_error_prototype);
|
||||||
|
add_constructor(vm.names.FinalizationRegistry, m_finalization_registry_constructor, m_finalization_registry_prototype);
|
||||||
add_constructor(vm.names.Function, m_function_constructor, m_function_prototype);
|
add_constructor(vm.names.Function, m_function_constructor, m_function_prototype);
|
||||||
add_constructor(vm.names.Map, m_map_constructor, m_map_prototype);
|
add_constructor(vm.names.Map, m_map_constructor, m_map_prototype);
|
||||||
add_constructor(vm.names.Number, m_number_constructor, m_number_prototype);
|
add_constructor(vm.names.Number, m_number_constructor, m_number_prototype);
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <LibJS/Interpreter.h>
|
#include <LibJS/Interpreter.h>
|
||||||
#include <LibJS/Runtime/Array.h>
|
#include <LibJS/Runtime/Array.h>
|
||||||
#include <LibJS/Runtime/Error.h>
|
#include <LibJS/Runtime/Error.h>
|
||||||
|
#include <LibJS/Runtime/FinalizationRegistry.h>
|
||||||
#include <LibJS/Runtime/GlobalObject.h>
|
#include <LibJS/Runtime/GlobalObject.h>
|
||||||
#include <LibJS/Runtime/IteratorOperations.h>
|
#include <LibJS/Runtime/IteratorOperations.h>
|
||||||
#include <LibJS/Runtime/NativeFunction.h>
|
#include <LibJS/Runtime/NativeFunction.h>
|
||||||
|
@ -557,6 +558,20 @@ void VM::enqueue_promise_job(NativeFunction& job)
|
||||||
m_promise_jobs.append(&job);
|
m_promise_jobs.append(&job);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VM::run_queued_finalization_registry_cleanup_jobs()
|
||||||
|
{
|
||||||
|
while (!m_finalization_registry_cleanup_jobs.is_empty()) {
|
||||||
|
auto* registry = m_finalization_registry_cleanup_jobs.take_first();
|
||||||
|
registry->cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9.10.4.1 HostEnqueueFinalizationRegistryCleanupJob ( finalizationRegistry ), https://tc39.es/ecma262/#sec-host-cleanup-finalization-registry
|
||||||
|
void VM::enqueue_finalization_registry_cleanup_job(FinalizationRegistry& registry)
|
||||||
|
{
|
||||||
|
m_finalization_registry_cleanup_jobs.append(®istry);
|
||||||
|
}
|
||||||
|
|
||||||
// 27.2.1.9 HostPromiseRejectionTracker ( promise, operation ), https://tc39.es/ecma262/#sec-host-promise-rejection-tracker
|
// 27.2.1.9 HostPromiseRejectionTracker ( promise, operation ), https://tc39.es/ecma262/#sec-host-promise-rejection-tracker
|
||||||
void VM::promise_rejection_tracker(const Promise& promise, Promise::RejectionOperation operation) const
|
void VM::promise_rejection_tracker(const Promise& promise, Promise::RejectionOperation operation) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -244,6 +244,9 @@ public:
|
||||||
void run_queued_promise_jobs();
|
void run_queued_promise_jobs();
|
||||||
void enqueue_promise_job(NativeFunction&);
|
void enqueue_promise_job(NativeFunction&);
|
||||||
|
|
||||||
|
void run_queued_finalization_registry_cleanup_jobs();
|
||||||
|
void enqueue_finalization_registry_cleanup_job(FinalizationRegistry&);
|
||||||
|
|
||||||
void promise_rejection_tracker(const Promise&, Promise::RejectionOperation) const;
|
void promise_rejection_tracker(const Promise&, Promise::RejectionOperation) const;
|
||||||
|
|
||||||
AK::Function<void()> on_call_stack_emptied;
|
AK::Function<void()> on_call_stack_emptied;
|
||||||
|
@ -272,6 +275,8 @@ private:
|
||||||
|
|
||||||
Vector<NativeFunction*> m_promise_jobs;
|
Vector<NativeFunction*> m_promise_jobs;
|
||||||
|
|
||||||
|
Vector<FinalizationRegistry*> m_finalization_registry_cleanup_jobs;
|
||||||
|
|
||||||
PrimitiveString* m_empty_string { nullptr };
|
PrimitiveString* m_empty_string { nullptr };
|
||||||
PrimitiveString* m_single_ascii_character_strings[128] {};
|
PrimitiveString* m_single_ascii_character_strings[128] {};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
test("constructor properties", () => {
|
||||||
|
expect(FinalizationRegistry).toHaveLength(1);
|
||||||
|
expect(FinalizationRegistry.name).toBe("FinalizationRegistry");
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("errors", () => {
|
||||||
|
test("invalid callbacks", () => {
|
||||||
|
[-100, Infinity, NaN, 152n, undefined].forEach(value => {
|
||||||
|
expect(() => {
|
||||||
|
new FinalizationRegistry(value);
|
||||||
|
}).toThrowWithMessage(TypeError, "is not a function");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test("called without new", () => {
|
||||||
|
expect(() => {
|
||||||
|
FinalizationRegistry();
|
||||||
|
}).toThrowWithMessage(
|
||||||
|
TypeError,
|
||||||
|
"FinalizationRegistry constructor must be called with 'new'"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("normal behavior", () => {
|
||||||
|
test("typeof", () => {
|
||||||
|
expect(typeof new FinalizationRegistry(() => {})).toBe("object");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("constructor with single callback argument", () => {
|
||||||
|
var a = new FinalizationRegistry(() => {});
|
||||||
|
expect(a instanceof FinalizationRegistry).toBeTrue();
|
||||||
|
});
|
||||||
|
});
|
|
@ -630,6 +630,7 @@ JS::Interpreter& Document::interpreter()
|
||||||
vm.on_call_stack_emptied = [this] {
|
vm.on_call_stack_emptied = [this] {
|
||||||
auto& vm = m_interpreter->vm();
|
auto& vm = m_interpreter->vm();
|
||||||
vm.run_queued_promise_jobs();
|
vm.run_queued_promise_jobs();
|
||||||
|
vm.run_queued_finalization_registry_cleanup_jobs();
|
||||||
// Note: This is not an exception check for the promise jobs, they will just leave any
|
// Note: This is not an exception check for the promise jobs, they will just leave any
|
||||||
// exception that already exists intact and never throw a new one (without cleaning it
|
// exception that already exists intact and never throw a new one (without cleaning it
|
||||||
// up, that is). Taking care of any previous unhandled exception just happens to be the
|
// up, that is). Taking care of any previous unhandled exception just happens to be the
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue