mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 07:38:10 +00:00
LibJS: Implement stage 3 proposal FinalizationRegistry changes
Specifically the 'Symbol as WeakMap Keys Proposal'.
This commit is contained in:
parent
53ed8decaf
commit
a79796ea4a
5 changed files with 48 additions and 27 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
* Copyright (c) 2021-2022, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -17,13 +17,13 @@ FinalizationRegistry::FinalizationRegistry(Realm& realm, JobCallback cleanup_cal
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void FinalizationRegistry::add_finalization_record(Cell& target, Value held_value, Object* unregister_token)
|
void FinalizationRegistry::add_finalization_record(Cell& target, Value held_value, Cell* unregister_token)
|
||||||
{
|
{
|
||||||
VERIFY(!held_value.is_empty());
|
VERIFY(!held_value.is_empty());
|
||||||
m_records.append({ &target, held_value, unregister_token });
|
m_records.append({ &target, held_value, unregister_token });
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FinalizationRegistry::remove_by_token(Object& unregister_token)
|
bool FinalizationRegistry::remove_by_token(Cell& unregister_token)
|
||||||
{
|
{
|
||||||
auto removed = false;
|
auto removed = false;
|
||||||
for (auto it = m_records.begin(); it != m_records.end(); ++it) {
|
for (auto it = m_records.begin(); it != m_records.end(); ++it) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
* Copyright (c) 2021-2022, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -25,8 +25,8 @@ public:
|
||||||
explicit FinalizationRegistry(Realm&, JobCallback, Object& prototype);
|
explicit FinalizationRegistry(Realm&, JobCallback, Object& prototype);
|
||||||
virtual ~FinalizationRegistry() override = default;
|
virtual ~FinalizationRegistry() override = default;
|
||||||
|
|
||||||
void add_finalization_record(Cell& target, Value held_value, Object* unregister_token);
|
void add_finalization_record(Cell& target, Value held_value, Cell* unregister_token);
|
||||||
bool remove_by_token(Object& unregister_token);
|
bool remove_by_token(Cell& unregister_token);
|
||||||
ThrowCompletionOr<void> cleanup(Optional<JobCallback> = {});
|
ThrowCompletionOr<void> cleanup(Optional<JobCallback> = {});
|
||||||
|
|
||||||
virtual void remove_dead_cells(Badge<Heap>) override;
|
virtual void remove_dead_cells(Badge<Heap>) override;
|
||||||
|
@ -46,7 +46,7 @@ private:
|
||||||
struct FinalizationRecord {
|
struct FinalizationRecord {
|
||||||
Cell* target { nullptr };
|
Cell* target { nullptr };
|
||||||
Value held_value;
|
Value held_value;
|
||||||
Object* unregister_token { nullptr };
|
Cell* unregister_token { nullptr };
|
||||||
};
|
};
|
||||||
SinglyLinkedList<FinalizationRecord> m_records;
|
SinglyLinkedList<FinalizationRecord> m_records;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
* Copyright (c) 2021-2022, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -50,18 +50,18 @@ JS_DEFINE_NATIVE_FUNCTION(FinalizationRegistryPrototype::register_)
|
||||||
auto* finalization_registry = TRY(typed_this_object(global_object));
|
auto* finalization_registry = TRY(typed_this_object(global_object));
|
||||||
|
|
||||||
auto target = vm.argument(0);
|
auto target = vm.argument(0);
|
||||||
if (!target.is_object())
|
if (!can_be_held_weakly(target))
|
||||||
return vm.throw_completion<TypeError>(global_object, ErrorType::NotAnObject, target.to_string_without_side_effects());
|
return vm.throw_completion<TypeError>(global_object, ErrorType::CannotBeHeldWeakly, target.to_string_without_side_effects());
|
||||||
|
|
||||||
auto held_value = vm.argument(1);
|
auto held_value = vm.argument(1);
|
||||||
if (same_value(target, held_value))
|
if (same_value(target, held_value))
|
||||||
return vm.throw_completion<TypeError>(global_object, ErrorType::FinalizationRegistrySameTargetAndValue);
|
return vm.throw_completion<TypeError>(global_object, ErrorType::FinalizationRegistrySameTargetAndValue);
|
||||||
|
|
||||||
auto unregister_token = vm.argument(2);
|
auto unregister_token = vm.argument(2);
|
||||||
if (!unregister_token.is_object() && !unregister_token.is_undefined())
|
if (!can_be_held_weakly(unregister_token) && !unregister_token.is_undefined())
|
||||||
return vm.throw_completion<TypeError>(global_object, ErrorType::NotAnObject, unregister_token.to_string_without_side_effects());
|
return vm.throw_completion<TypeError>(global_object, ErrorType::CannotBeHeldWeakly, unregister_token.to_string_without_side_effects());
|
||||||
|
|
||||||
finalization_registry->add_finalization_record(target.as_cell(), held_value, unregister_token.is_undefined() ? nullptr : &unregister_token.as_object());
|
finalization_registry->add_finalization_record(target.as_cell(), held_value, unregister_token.is_undefined() ? nullptr : &unregister_token.as_cell());
|
||||||
|
|
||||||
return js_undefined();
|
return js_undefined();
|
||||||
}
|
}
|
||||||
|
@ -72,10 +72,10 @@ JS_DEFINE_NATIVE_FUNCTION(FinalizationRegistryPrototype::unregister)
|
||||||
auto* finalization_registry = TRY(typed_this_object(global_object));
|
auto* finalization_registry = TRY(typed_this_object(global_object));
|
||||||
|
|
||||||
auto unregister_token = vm.argument(0);
|
auto unregister_token = vm.argument(0);
|
||||||
if (!unregister_token.is_object())
|
if (!can_be_held_weakly(unregister_token))
|
||||||
return vm.throw_completion<TypeError>(global_object, ErrorType::NotAnObject, unregister_token.to_string_without_side_effects());
|
return vm.throw_completion<TypeError>(global_object, ErrorType::CannotBeHeldWeakly, unregister_token.to_string_without_side_effects());
|
||||||
|
|
||||||
return Value(finalization_registry->remove_by_token(unregister_token.as_object()));
|
return Value(finalization_registry->remove_by_token(unregister_token.as_cell()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,9 +12,15 @@ test("basic functionality", () => {
|
||||||
|
|
||||||
var target2 = {};
|
var target2 = {};
|
||||||
var heldValue2 = {};
|
var heldValue2 = {};
|
||||||
var token = {};
|
var token1 = {};
|
||||||
|
|
||||||
registry.register(target2, heldValue2, token);
|
registry.register(target2, heldValue2, token1);
|
||||||
|
|
||||||
|
var target3 = Symbol("target");
|
||||||
|
var heldValue3 = {};
|
||||||
|
var token2 = Symbol("token");
|
||||||
|
|
||||||
|
registry.register(target3, heldValue3, token2);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("errors", () => {
|
test("errors", () => {
|
||||||
|
@ -22,14 +28,19 @@ test("errors", () => {
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
registry.register(5, {});
|
registry.register(5, {});
|
||||||
}).toThrowWithMessage(TypeError, "is not an object");
|
}).toThrowWithMessage(TypeError, "cannot be held weakly");
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
var a = {};
|
var a = {};
|
||||||
registry.register(a, a);
|
registry.register(a, a);
|
||||||
}).toThrowWithMessage(TypeError, "Target and held value must not be the same");
|
}).toThrowWithMessage(TypeError, "Target and held value must not be the same");
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
var a = Symbol();
|
||||||
|
registry.register(a, a);
|
||||||
|
}).toThrowWithMessage(TypeError, "Target and held value must not be the same");
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
registry.register({}, {}, 5);
|
registry.register({}, {}, 5);
|
||||||
}).toThrowWithMessage(TypeError, "is not an object");
|
}).toThrowWithMessage(TypeError, "cannot be held weakly");
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,15 +5,25 @@ test("length is 2", () => {
|
||||||
test("basic functionality", () => {
|
test("basic functionality", () => {
|
||||||
var registry = new FinalizationRegistry(() => {});
|
var registry = new FinalizationRegistry(() => {});
|
||||||
|
|
||||||
var target = {};
|
var target1 = {};
|
||||||
var heldValue = {};
|
var heldValue1 = {};
|
||||||
var token = {};
|
var token1 = {};
|
||||||
|
|
||||||
registry.register(target, heldValue, token);
|
registry.register(target1, heldValue1, token1);
|
||||||
|
|
||||||
expect(registry.unregister({})).toBe(false);
|
expect(registry.unregister({})).toBe(false);
|
||||||
expect(registry.unregister(token)).toBe(true);
|
expect(registry.unregister(token1)).toBe(true);
|
||||||
expect(registry.unregister(token)).toBe(false);
|
expect(registry.unregister(token1)).toBe(false);
|
||||||
|
|
||||||
|
var target2 = Symbol("target");
|
||||||
|
var heldValue2 = Symbol("heldValue");
|
||||||
|
var token2 = Symbol("token");
|
||||||
|
|
||||||
|
registry.register(target2, heldValue2, token2);
|
||||||
|
|
||||||
|
expect(registry.unregister(Symbol("token"))).toBe(false);
|
||||||
|
expect(registry.unregister(token2)).toBe(true);
|
||||||
|
expect(registry.unregister(token2)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("errors", () => {
|
test("errors", () => {
|
||||||
|
@ -21,5 +31,5 @@ test("errors", () => {
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
registry.unregister(5);
|
registry.unregister(5);
|
||||||
}).toThrowWithMessage(TypeError, "is not an object");
|
}).toThrowWithMessage(TypeError, "cannot be held weakly");
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue