mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 19:37:36 +00:00
LibJS: Implement WeakSet changes from 'Symbol as WeakMap Keys Proposal'
This commit is contained in:
parent
a80d3fdf49
commit
dbd0110721
5 changed files with 32 additions and 16 deletions
|
@ -1,11 +1,12 @@
|
||||||
/*
|
/*
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/HashTable.h>
|
#include <AK/HashTable.h>
|
||||||
#include <AK/TypeCasts.h>
|
#include <AK/TypeCasts.h>
|
||||||
|
#include <LibJS/Runtime/AbstractOperations.h>
|
||||||
#include <LibJS/Runtime/WeakSetPrototype.h>
|
#include <LibJS/Runtime/WeakSetPrototype.h>
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
@ -34,9 +35,9 @@ JS_DEFINE_NATIVE_FUNCTION(WeakSetPrototype::add)
|
||||||
{
|
{
|
||||||
auto* weak_set = TRY(typed_this_object(global_object));
|
auto* weak_set = TRY(typed_this_object(global_object));
|
||||||
auto value = vm.argument(0);
|
auto value = vm.argument(0);
|
||||||
if (!value.is_object())
|
if (!can_be_held_weakly(value))
|
||||||
return vm.throw_completion<TypeError>(global_object, ErrorType::NotAnObject, value.to_string_without_side_effects());
|
return vm.throw_completion<TypeError>(global_object, ErrorType::CannotBeHeldWeakly, value.to_string_without_side_effects());
|
||||||
weak_set->values().set(&value.as_object(), AK::HashSetExistingEntryBehavior::Keep);
|
weak_set->values().set(&value.as_cell(), AK::HashSetExistingEntryBehavior::Keep);
|
||||||
return weak_set;
|
return weak_set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,9 +46,9 @@ JS_DEFINE_NATIVE_FUNCTION(WeakSetPrototype::delete_)
|
||||||
{
|
{
|
||||||
auto* weak_set = TRY(typed_this_object(global_object));
|
auto* weak_set = TRY(typed_this_object(global_object));
|
||||||
auto value = vm.argument(0);
|
auto value = vm.argument(0);
|
||||||
if (!value.is_object())
|
if (!can_be_held_weakly(value))
|
||||||
return Value(false);
|
return Value(false);
|
||||||
return Value(weak_set->values().remove(&value.as_object()));
|
return Value(weak_set->values().remove(&value.as_cell()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 24.4.3.4 WeakSet.prototype.has ( value ), https://tc39.es/ecma262/#sec-weakset.prototype.has
|
// 24.4.3.4 WeakSet.prototype.has ( value ), https://tc39.es/ecma262/#sec-weakset.prototype.has
|
||||||
|
@ -55,10 +56,10 @@ JS_DEFINE_NATIVE_FUNCTION(WeakSetPrototype::has)
|
||||||
{
|
{
|
||||||
auto* weak_set = TRY(typed_this_object(global_object));
|
auto* weak_set = TRY(typed_this_object(global_object));
|
||||||
auto value = vm.argument(0);
|
auto value = vm.argument(0);
|
||||||
if (!value.is_object())
|
if (!can_be_held_weakly(value))
|
||||||
return Value(false);
|
return Value(false);
|
||||||
auto& values = weak_set->values();
|
auto& values = weak_set->values();
|
||||||
return Value(values.find(&value.as_object()) != values.end());
|
return Value(values.find(&value.as_cell()) != values.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ describe("normal behavior", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("constructor with single array argument", () => {
|
test("constructor with single array argument", () => {
|
||||||
var a = new WeakSet([{ a: 1 }, { a: 2 }, { a: 3 }]);
|
var a = new WeakSet([{ a: 1 }, { a: 2 }, { a: 3 }, Symbol("foo")]);
|
||||||
expect(a instanceof WeakSet).toBeTrue();
|
expect(a instanceof WeakSet).toBeTrue();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
test("basic functionality", () => {
|
test("basic functionality", () => {
|
||||||
expect(WeakSet.prototype.add).toHaveLength(1);
|
expect(WeakSet.prototype.add).toHaveLength(1);
|
||||||
|
|
||||||
const weakSet = new WeakSet([{ a: 1 }, { a: 2 }, { a: 3 }]);
|
const weakSet = new WeakSet([{ a: 1 }, { a: 2 }, { a: 3 }, Symbol("foo")]);
|
||||||
expect(weakSet.add({ a: 4 })).toBe(weakSet);
|
expect(weakSet.add({ a: 4 })).toBe(weakSet);
|
||||||
expect(weakSet.add({ a: 1 })).toBe(weakSet);
|
expect(weakSet.add({ a: 1 })).toBe(weakSet);
|
||||||
|
expect(weakSet.add(Symbol("bar"))).toBe(weakSet);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("invalid values", () => {
|
test("invalid values", () => {
|
||||||
|
@ -11,18 +12,28 @@ test("invalid values", () => {
|
||||||
[-100, Infinity, NaN, "hello", 152n].forEach(value => {
|
[-100, Infinity, NaN, "hello", 152n].forEach(value => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
weakSet.add(value);
|
weakSet.add(value);
|
||||||
}).toThrowWithMessage(TypeError, "is not an object");
|
}).toThrowWithMessage(TypeError, "cannot be held weakly");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("automatic removal of garbage-collected values", () => {
|
test("automatic removal of garbage-collected values", () => {
|
||||||
const weakSet = new WeakSet();
|
const weakSet = new WeakSet();
|
||||||
const item = { a: 1 };
|
const objectItem = { a: 1 };
|
||||||
|
|
||||||
expect(weakSet.add(item)).toBe(weakSet);
|
expect(weakSet.add(objectItem)).toBe(weakSet);
|
||||||
expect(getWeakSetSize(weakSet)).toBe(1);
|
expect(getWeakSetSize(weakSet)).toBe(1);
|
||||||
|
|
||||||
markAsGarbage("item");
|
markAsGarbage("objectItem");
|
||||||
|
gc();
|
||||||
|
|
||||||
|
expect(getWeakSetSize(weakSet)).toBe(0);
|
||||||
|
|
||||||
|
const symbolItem = Symbol("foo");
|
||||||
|
|
||||||
|
expect(weakSet.add(symbolItem)).toBe(weakSet);
|
||||||
|
expect(getWeakSetSize(weakSet)).toBe(1);
|
||||||
|
|
||||||
|
markAsGarbage("symbolItem");
|
||||||
gc();
|
gc();
|
||||||
|
|
||||||
expect(getWeakSetSize(weakSet)).toBe(0);
|
expect(getWeakSetSize(weakSet)).toBe(0);
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
test("basic functionality", () => {
|
test("basic functionality", () => {
|
||||||
expect(WeakSet.prototype.delete).toHaveLength(1);
|
expect(WeakSet.prototype.delete).toHaveLength(1);
|
||||||
|
|
||||||
var original = [{ a: 1 }, { a: 2 }, { a: 3 }];
|
var original = [{ a: 1 }, { a: 2 }, { a: 3 }, Symbol("foo")];
|
||||||
const weakSet = new WeakSet(original);
|
const weakSet = new WeakSet(original);
|
||||||
expect(weakSet.delete(original[0])).toBeTrue();
|
expect(weakSet.delete(original[0])).toBeTrue();
|
||||||
expect(weakSet.delete(original[0])).toBeFalse();
|
expect(weakSet.delete(original[0])).toBeFalse();
|
||||||
|
expect(weakSet.delete(original[3])).toBeTrue();
|
||||||
|
expect(weakSet.delete(original[3])).toBeFalse();
|
||||||
expect(weakSet.delete(null)).toBeFalse();
|
expect(weakSet.delete(null)).toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,10 +3,12 @@ test("length is 1", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("basic functionality", () => {
|
test("basic functionality", () => {
|
||||||
var original = [{ a: 1 }, { a: 2 }, { a: 3 }];
|
var original = [{ a: 1 }, { a: 2 }, { a: 3 }, Symbol("foo")];
|
||||||
var weakSet = new WeakSet(original);
|
var weakSet = new WeakSet(original);
|
||||||
|
|
||||||
expect(new WeakSet().has()).toBeFalse();
|
expect(new WeakSet().has()).toBeFalse();
|
||||||
expect(weakSet.has(original[0])).toBeTrue();
|
expect(weakSet.has(original[0])).toBeTrue();
|
||||||
|
expect(weakSet.has(original[3])).toBeTrue();
|
||||||
expect(weakSet.has({ a: 1 })).toBeFalse();
|
expect(weakSet.has({ a: 1 })).toBeFalse();
|
||||||
|
expect(weakSet.has(Symbol("foo"))).toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue