소스 검색

LibJS: Implement stage 3 proposal FinalizationRegistry changes

Specifically the 'Symbol as WeakMap Keys Proposal'.
Idan Horowitz 3 년 전
부모
커밋
a79796ea4a

+ 3 - 3
Userland/Libraries/LibJS/Runtime/FinalizationRegistry.cpp

@@ -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
  */
@@ -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());
     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;
     for (auto it = m_records.begin(); it != m_records.end(); ++it) {

+ 4 - 4
Userland/Libraries/LibJS/Runtime/FinalizationRegistry.h

@@ -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
  */
@@ -25,8 +25,8 @@ public:
     explicit FinalizationRegistry(Realm&, JobCallback, Object& prototype);
     virtual ~FinalizationRegistry() override = default;
 
-    void add_finalization_record(Cell& target, Value held_value, Object* unregister_token);
-    bool remove_by_token(Object& unregister_token);
+    void add_finalization_record(Cell& target, Value held_value, Cell* unregister_token);
+    bool remove_by_token(Cell& unregister_token);
     ThrowCompletionOr<void> cleanup(Optional<JobCallback> = {});
 
     virtual void remove_dead_cells(Badge<Heap>) override;
@@ -46,7 +46,7 @@ private:
     struct FinalizationRecord {
         Cell* target { nullptr };
         Value held_value;
-        Object* unregister_token { nullptr };
+        Cell* unregister_token { nullptr };
     };
     SinglyLinkedList<FinalizationRecord> m_records;
 };

+ 9 - 9
Userland/Libraries/LibJS/Runtime/FinalizationRegistryPrototype.cpp

@@ -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
  */
@@ -50,18 +50,18 @@ JS_DEFINE_NATIVE_FUNCTION(FinalizationRegistryPrototype::register_)
     auto* finalization_registry = TRY(typed_this_object(global_object));
 
     auto target = vm.argument(0);
-    if (!target.is_object())
-        return vm.throw_completion<TypeError>(global_object, ErrorType::NotAnObject, target.to_string_without_side_effects());
+    if (!can_be_held_weakly(target))
+        return vm.throw_completion<TypeError>(global_object, ErrorType::CannotBeHeldWeakly, target.to_string_without_side_effects());
 
     auto held_value = vm.argument(1);
     if (same_value(target, held_value))
         return vm.throw_completion<TypeError>(global_object, ErrorType::FinalizationRegistrySameTargetAndValue);
 
     auto unregister_token = vm.argument(2);
-    if (!unregister_token.is_object() && !unregister_token.is_undefined())
-        return vm.throw_completion<TypeError>(global_object, ErrorType::NotAnObject, unregister_token.to_string_without_side_effects());
+    if (!can_be_held_weakly(unregister_token) && !unregister_token.is_undefined())
+        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();
 }
@@ -72,10 +72,10 @@ JS_DEFINE_NATIVE_FUNCTION(FinalizationRegistryPrototype::unregister)
     auto* finalization_registry = TRY(typed_this_object(global_object));
 
     auto unregister_token = vm.argument(0);
-    if (!unregister_token.is_object())
-        return vm.throw_completion<TypeError>(global_object, ErrorType::NotAnObject, unregister_token.to_string_without_side_effects());
+    if (!can_be_held_weakly(unregister_token))
+        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()));
 }
 
 }

+ 15 - 4
Userland/Libraries/LibJS/Tests/builtins/FinalizationRegistry/FinalizationRegistry.prototype.register.js

@@ -12,9 +12,15 @@ test("basic functionality", () => {
 
     var target2 = {};
     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", () => {
@@ -22,14 +28,19 @@ test("errors", () => {
 
     expect(() => {
         registry.register(5, {});
-    }).toThrowWithMessage(TypeError, "is not an object");
+    }).toThrowWithMessage(TypeError, "cannot be held weakly");
 
     expect(() => {
         var a = {};
         registry.register(a, a);
     }).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(() => {
         registry.register({}, {}, 5);
-    }).toThrowWithMessage(TypeError, "is not an object");
+    }).toThrowWithMessage(TypeError, "cannot be held weakly");
 });

+ 17 - 7
Userland/Libraries/LibJS/Tests/builtins/FinalizationRegistry/FinalizationRegistry.prototype.unregister.js

@@ -5,15 +5,25 @@ test("length is 2", () => {
 test("basic functionality", () => {
     var registry = new FinalizationRegistry(() => {});
 
-    var target = {};
-    var heldValue = {};
-    var token = {};
+    var target1 = {};
+    var heldValue1 = {};
+    var token1 = {};
 
-    registry.register(target, heldValue, token);
+    registry.register(target1, heldValue1, token1);
 
     expect(registry.unregister({})).toBe(false);
-    expect(registry.unregister(token)).toBe(true);
-    expect(registry.unregister(token)).toBe(false);
+    expect(registry.unregister(token1)).toBe(true);
+    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", () => {
@@ -21,5 +31,5 @@ test("errors", () => {
 
     expect(() => {
         registry.unregister(5);
-    }).toThrowWithMessage(TypeError, "is not an object");
+    }).toThrowWithMessage(TypeError, "cannot be held weakly");
 });