mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 06:07:44 +00:00
LibJS: Add all of the FinalizationRegistry.prototype methods
More specifically: cleanupSome, register & unregister. FinalizationRegistery.prototype.cleanupSome is actually still a stage 2 proposal, but since test262 test cases already exist for it, i decided to go for it :)
This commit is contained in:
parent
de9fa6622a
commit
e1b0719435
7 changed files with 174 additions and 0 deletions
|
@ -75,6 +75,7 @@ namespace JS {
|
|||
P(ceil) \
|
||||
P(charAt) \
|
||||
P(charCodeAt) \
|
||||
P(cleanupSome) \
|
||||
P(clear) \
|
||||
P(clz32) \
|
||||
P(concat) \
|
||||
|
@ -310,6 +311,7 @@ namespace JS {
|
|||
P(undefined) \
|
||||
P(unescape) \
|
||||
P(unicode) \
|
||||
P(unregister) \
|
||||
P(unshift) \
|
||||
P(value) \
|
||||
P(valueOf) \
|
||||
|
@ -321,6 +323,7 @@ struct CommonPropertyNames {
|
|||
PropertyName catch_ { "catch", PropertyName::StringMayBeNumber::No };
|
||||
PropertyName delete_ { "delete", PropertyName::StringMayBeNumber::No };
|
||||
PropertyName for_ { "for", PropertyName::StringMayBeNumber::No };
|
||||
PropertyName register_ { "register", PropertyName::StringMayBeNumber::No };
|
||||
PropertyName return_ { "return", PropertyName::StringMayBeNumber::No };
|
||||
PropertyName throw_ { "throw", PropertyName::StringMayBeNumber::No };
|
||||
#define __ENUMERATE(x) PropertyName x { #x, PropertyName::StringMayBeNumber::No };
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
M(DescWriteNonWritable, "Cannot write to non-writable property '{}'") \
|
||||
M(DetachedArrayBuffer, "ArrayBuffer is detached") \
|
||||
M(DivisionByZero, "Division by zero") \
|
||||
M(FinalizationRegistrySameTargetAndValue, "Target and held value must not be the same") \
|
||||
M(GetCapabilitiesExecutorCalledMultipleTimes, "GetCapabilitiesExecutor was called multiple times") \
|
||||
M(InOperatorWithObject, "'in' operator must be used on an object") \
|
||||
M(InstanceOfOperatorBadPrototype, "'prototype' property of {} is not an object") \
|
||||
|
|
|
@ -17,6 +17,11 @@ void FinalizationRegistryPrototype::initialize(GlobalObject& global_object)
|
|||
{
|
||||
auto& vm = this->vm();
|
||||
Object::initialize(global_object);
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
|
||||
define_native_function(vm.names.cleanupSome, cleanup_some, 0, attr);
|
||||
define_native_function(vm.names.register_, register_, 2, attr);
|
||||
define_native_function(vm.names.unregister, unregister, 1, attr);
|
||||
|
||||
// 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);
|
||||
|
@ -38,4 +43,68 @@ FinalizationRegistry* FinalizationRegistryPrototype::typed_this(VM& vm, GlobalOb
|
|||
return static_cast<FinalizationRegistry*>(this_object);
|
||||
}
|
||||
|
||||
// @STAGE 2@ FinalizationRegistry.prototype.cleanupSome ( [ callback ] ), https://github.com/tc39/proposal-cleanup-some/blob/master/spec/finalization-registry.html
|
||||
JS_DEFINE_NATIVE_FUNCTION(FinalizationRegistryPrototype::cleanup_some)
|
||||
{
|
||||
auto* finalization_registry = typed_this(vm, global_object);
|
||||
if (!finalization_registry)
|
||||
return {};
|
||||
|
||||
auto callback = vm.argument(0);
|
||||
if (vm.argument_count() > 0 && !callback.is_function()) {
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::NotAFunction, callback.to_string_without_side_effects());
|
||||
return {};
|
||||
}
|
||||
|
||||
finalization_registry->cleanup(callback.is_undefined() ? nullptr : &callback.as_function());
|
||||
|
||||
return js_undefined();
|
||||
}
|
||||
|
||||
// 26.2.3.2 FinalizationRegistry.prototype.register ( target, heldValue [ , unregisterToken ] ), https://tc39.es/ecma262/#sec-finalization-registry.prototype.register
|
||||
JS_DEFINE_NATIVE_FUNCTION(FinalizationRegistryPrototype::register_)
|
||||
{
|
||||
auto* finalization_registry = typed_this(vm, global_object);
|
||||
if (!finalization_registry)
|
||||
return {};
|
||||
|
||||
auto target = vm.argument(0);
|
||||
if (!target.is_object()) {
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::NotAnObject, target.to_string_without_side_effects());
|
||||
return {};
|
||||
}
|
||||
|
||||
auto held_value = vm.argument(1);
|
||||
if (same_value(target, held_value)) {
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::FinalizationRegistrySameTargetAndValue);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto unregister_token = vm.argument(2);
|
||||
if (!unregister_token.is_object() && !unregister_token.is_undefined()) {
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::NotAnObject, unregister_token.to_string_without_side_effects());
|
||||
return {};
|
||||
}
|
||||
|
||||
finalization_registry->add_finalization_record(target.as_cell(), held_value, unregister_token.is_undefined() ? nullptr : &unregister_token.as_object());
|
||||
|
||||
return js_undefined();
|
||||
}
|
||||
|
||||
// 26.2.3.3 FinalizationRegistry.prototype.unregister ( unregisterToken ), https://tc39.es/ecma262/#sec-finalization-registry.prototype.unregister
|
||||
JS_DEFINE_NATIVE_FUNCTION(FinalizationRegistryPrototype::unregister)
|
||||
{
|
||||
auto* finalization_registry = typed_this(vm, global_object);
|
||||
if (!finalization_registry)
|
||||
return {};
|
||||
|
||||
auto unregister_token = vm.argument(0);
|
||||
if (!unregister_token.is_object()) {
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::NotAnObject, unregister_token.to_string_without_side_effects());
|
||||
return {};
|
||||
}
|
||||
|
||||
return Value(finalization_registry->remove_by_token(unregister_token.as_object()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,4 +20,10 @@ public:
|
|||
|
||||
private:
|
||||
static FinalizationRegistry* typed_this(VM&, GlobalObject&);
|
||||
|
||||
JS_DECLARE_NATIVE_FUNCTION(cleanup_some);
|
||||
JS_DECLARE_NATIVE_FUNCTION(register_);
|
||||
JS_DECLARE_NATIVE_FUNCTION(unregister);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
test("length is 0", () => {
|
||||
expect(FinalizationRegistry.prototype.cleanupSome).toHaveLength(0);
|
||||
});
|
||||
|
||||
function registerInDifferentScope(registry) {
|
||||
registry.register({}, {});
|
||||
}
|
||||
|
||||
test("basic functionality", () => {
|
||||
var registry = new FinalizationRegistry(() => {});
|
||||
|
||||
var count = 0;
|
||||
var increment = () => {
|
||||
count++;
|
||||
};
|
||||
|
||||
registry.cleanupSome(increment);
|
||||
|
||||
expect(count).toBe(0);
|
||||
|
||||
registerInDifferentScope(registry);
|
||||
gc();
|
||||
|
||||
registry.cleanupSome(increment);
|
||||
|
||||
expect(count).toBe(1);
|
||||
});
|
||||
|
||||
test("errors", () => {
|
||||
var registry = new FinalizationRegistry(() => {});
|
||||
|
||||
expect(() => {
|
||||
registry.cleanupSome(5);
|
||||
}).toThrowWithMessage(TypeError, "is not a function");
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
test("length is 2", () => {
|
||||
expect(FinalizationRegistry.prototype.register).toHaveLength(2);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
var registry = new FinalizationRegistry(() => {});
|
||||
|
||||
var target1 = {};
|
||||
var heldValue1 = {};
|
||||
|
||||
registry.register(target1, heldValue1);
|
||||
|
||||
var target2 = {};
|
||||
var heldValue2 = {};
|
||||
var token = {};
|
||||
|
||||
registry.register(target2, heldValue2, token);
|
||||
});
|
||||
|
||||
test("errors", () => {
|
||||
var registry = new FinalizationRegistry(() => {});
|
||||
|
||||
expect(() => {
|
||||
registry.register(5, {});
|
||||
}).toThrowWithMessage(TypeError, "is not an object");
|
||||
|
||||
expect(() => {
|
||||
var a = {};
|
||||
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");
|
||||
});
|
|
@ -0,0 +1,25 @@
|
|||
test("length is 2", () => {
|
||||
expect(FinalizationRegistry.prototype.unregister).toHaveLength(1);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
var registry = new FinalizationRegistry(() => {});
|
||||
|
||||
var target = {};
|
||||
var heldValue = {};
|
||||
var token = {};
|
||||
|
||||
registry.register(target, heldValue, token);
|
||||
|
||||
expect(registry.unregister({})).toBe(false);
|
||||
expect(registry.unregister(token)).toBe(true);
|
||||
expect(registry.unregister(token)).toBe(false);
|
||||
});
|
||||
|
||||
test("errors", () => {
|
||||
var registry = new FinalizationRegistry(() => {});
|
||||
|
||||
expect(() => {
|
||||
registry.unregister(5);
|
||||
}).toThrowWithMessage(TypeError, "is not an object");
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue