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
Notes:
sideshowbarker
2024-07-18 12:12:42 +09:00
Author: https://github.com/IdanHo Commit: https://github.com/SerenityOS/serenity/commit/de9fa6622a5 Pull-request: https://github.com/SerenityOS/serenity/pull/8075 Reviewed-by: https://github.com/linusg
14 changed files with 365 additions and 20 deletions
|
@ -53,6 +53,9 @@ set(SOURCES
|
|||
Runtime/ErrorPrototype.cpp
|
||||
Runtime/ErrorTypes.cpp
|
||||
Runtime/Exception.cpp
|
||||
Runtime/FinalizationRegistry.cpp
|
||||
Runtime/FinalizationRegistryConstructor.cpp
|
||||
Runtime/FinalizationRegistryPrototype.cpp
|
||||
Runtime/FunctionConstructor.cpp
|
||||
Runtime/Function.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)
|
||||
|
||||
// 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 \
|
||||
__JS_ENUMERATE(AggregateError, aggregate_error, AggregateErrorPrototype, AggregateErrorConstructor, void) \
|
||||
__JS_ENUMERATE(Array, array, ArrayPrototype, ArrayConstructor, void) \
|
||||
__JS_ENUMERATE(ArrayBuffer, array_buffer, ArrayBufferPrototype, ArrayBufferConstructor, void) \
|
||||
__JS_ENUMERATE(BigIntObject, bigint, BigIntPrototype, BigIntConstructor, void) \
|
||||
__JS_ENUMERATE(BooleanObject, boolean, BooleanPrototype, BooleanConstructor, void) \
|
||||
__JS_ENUMERATE(DataView, data_view, DataViewPrototype, DataViewConstructor, void) \
|
||||
__JS_ENUMERATE(Date, date, DatePrototype, DateConstructor, void) \
|
||||
__JS_ENUMERATE(Error, error, ErrorPrototype, ErrorConstructor, void) \
|
||||
__JS_ENUMERATE(Function, function, FunctionPrototype, FunctionConstructor, void) \
|
||||
__JS_ENUMERATE(Map, map, MapPrototype, MapConstructor, void) \
|
||||
__JS_ENUMERATE(NumberObject, number, NumberPrototype, NumberConstructor, void) \
|
||||
__JS_ENUMERATE(Object, object, ObjectPrototype, ObjectConstructor, void) \
|
||||
__JS_ENUMERATE(Promise, promise, PromisePrototype, PromiseConstructor, void) \
|
||||
__JS_ENUMERATE(RegExpObject, regexp, RegExpPrototype, RegExpConstructor, void) \
|
||||
__JS_ENUMERATE(Set, set, SetPrototype, SetConstructor, void) \
|
||||
__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) \
|
||||
#define JS_ENUMERATE_NATIVE_OBJECTS_EXCLUDING_TEMPLATES \
|
||||
__JS_ENUMERATE(AggregateError, aggregate_error, AggregateErrorPrototype, AggregateErrorConstructor, void) \
|
||||
__JS_ENUMERATE(Array, array, ArrayPrototype, ArrayConstructor, void) \
|
||||
__JS_ENUMERATE(ArrayBuffer, array_buffer, ArrayBufferPrototype, ArrayBufferConstructor, void) \
|
||||
__JS_ENUMERATE(BigIntObject, bigint, BigIntPrototype, BigIntConstructor, void) \
|
||||
__JS_ENUMERATE(BooleanObject, boolean, BooleanPrototype, BooleanConstructor, void) \
|
||||
__JS_ENUMERATE(DataView, data_view, DataViewPrototype, DataViewConstructor, void) \
|
||||
__JS_ENUMERATE(Date, date, DatePrototype, DateConstructor, void) \
|
||||
__JS_ENUMERATE(Error, error, ErrorPrototype, ErrorConstructor, void) \
|
||||
__JS_ENUMERATE(FinalizationRegistry, finalization_registry, FinalizationRegistryPrototype, FinalizationRegistryConstructor, void) \
|
||||
__JS_ENUMERATE(Function, function, FunctionPrototype, FunctionConstructor, void) \
|
||||
__JS_ENUMERATE(Map, map, MapPrototype, MapConstructor, void) \
|
||||
__JS_ENUMERATE(NumberObject, number, NumberPrototype, NumberConstructor, void) \
|
||||
__JS_ENUMERATE(Object, object, ObjectPrototype, ObjectConstructor, void) \
|
||||
__JS_ENUMERATE(Promise, promise, PromisePrototype, PromiseConstructor, void) \
|
||||
__JS_ENUMERATE(RegExpObject, regexp, RegExpPrototype, RegExpConstructor, void) \
|
||||
__JS_ENUMERATE(Set, set, SetPrototype, SetConstructor, void) \
|
||||
__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 \
|
||||
|
|
|
@ -64,6 +64,8 @@ void Interpreter::run(GlobalObject& global_object, const Program& program)
|
|||
// in which case this is a no-op.
|
||||
vm.run_queued_promise_jobs();
|
||||
|
||||
vm.run_queued_finalization_registry_cleanup_jobs();
|
||||
|
||||
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/ErrorConstructor.h>
|
||||
#include <LibJS/Runtime/ErrorPrototype.h>
|
||||
#include <LibJS/Runtime/FinalizationRegistryConstructor.h>
|
||||
#include <LibJS/Runtime/FinalizationRegistryPrototype.h>
|
||||
#include <LibJS/Runtime/FunctionConstructor.h>
|
||||
#include <LibJS/Runtime/FunctionPrototype.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.Date, m_date_constructor, m_date_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.Map, m_map_constructor, m_map_prototype);
|
||||
add_constructor(vm.names.Number, m_number_constructor, m_number_prototype);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <LibJS/Interpreter.h>
|
||||
#include <LibJS/Runtime/Array.h>
|
||||
#include <LibJS/Runtime/Error.h>
|
||||
#include <LibJS/Runtime/FinalizationRegistry.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/IteratorOperations.h>
|
||||
#include <LibJS/Runtime/NativeFunction.h>
|
||||
|
@ -557,6 +558,20 @@ void VM::enqueue_promise_job(NativeFunction& 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
|
||||
void VM::promise_rejection_tracker(const Promise& promise, Promise::RejectionOperation operation) const
|
||||
{
|
||||
|
|
|
@ -244,6 +244,9 @@ public:
|
|||
void run_queued_promise_jobs();
|
||||
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;
|
||||
|
||||
AK::Function<void()> on_call_stack_emptied;
|
||||
|
@ -272,6 +275,8 @@ private:
|
|||
|
||||
Vector<NativeFunction*> m_promise_jobs;
|
||||
|
||||
Vector<FinalizationRegistry*> m_finalization_registry_cleanup_jobs;
|
||||
|
||||
PrimitiveString* m_empty_string { nullptr };
|
||||
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] {
|
||||
auto& vm = m_interpreter->vm();
|
||||
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
|
||||
// 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
|
||||
|
|
Loading…
Add table
Reference in a new issue