mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 01:17:35 +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(ceil) \
|
||||||
P(charAt) \
|
P(charAt) \
|
||||||
P(charCodeAt) \
|
P(charCodeAt) \
|
||||||
|
P(cleanupSome) \
|
||||||
P(clear) \
|
P(clear) \
|
||||||
P(clz32) \
|
P(clz32) \
|
||||||
P(concat) \
|
P(concat) \
|
||||||
|
@ -310,6 +311,7 @@ namespace JS {
|
||||||
P(undefined) \
|
P(undefined) \
|
||||||
P(unescape) \
|
P(unescape) \
|
||||||
P(unicode) \
|
P(unicode) \
|
||||||
|
P(unregister) \
|
||||||
P(unshift) \
|
P(unshift) \
|
||||||
P(value) \
|
P(value) \
|
||||||
P(valueOf) \
|
P(valueOf) \
|
||||||
|
@ -321,6 +323,7 @@ struct CommonPropertyNames {
|
||||||
PropertyName catch_ { "catch", PropertyName::StringMayBeNumber::No };
|
PropertyName catch_ { "catch", PropertyName::StringMayBeNumber::No };
|
||||||
PropertyName delete_ { "delete", PropertyName::StringMayBeNumber::No };
|
PropertyName delete_ { "delete", PropertyName::StringMayBeNumber::No };
|
||||||
PropertyName for_ { "for", PropertyName::StringMayBeNumber::No };
|
PropertyName for_ { "for", PropertyName::StringMayBeNumber::No };
|
||||||
|
PropertyName register_ { "register", PropertyName::StringMayBeNumber::No };
|
||||||
PropertyName return_ { "return", PropertyName::StringMayBeNumber::No };
|
PropertyName return_ { "return", PropertyName::StringMayBeNumber::No };
|
||||||
PropertyName throw_ { "throw", PropertyName::StringMayBeNumber::No };
|
PropertyName throw_ { "throw", PropertyName::StringMayBeNumber::No };
|
||||||
#define __ENUMERATE(x) PropertyName x { #x, 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(DescWriteNonWritable, "Cannot write to non-writable property '{}'") \
|
||||||
M(DetachedArrayBuffer, "ArrayBuffer is detached") \
|
M(DetachedArrayBuffer, "ArrayBuffer is detached") \
|
||||||
M(DivisionByZero, "Division by zero") \
|
M(DivisionByZero, "Division by zero") \
|
||||||
|
M(FinalizationRegistrySameTargetAndValue, "Target and held value must not be the same") \
|
||||||
M(GetCapabilitiesExecutorCalledMultipleTimes, "GetCapabilitiesExecutor was called multiple times") \
|
M(GetCapabilitiesExecutorCalledMultipleTimes, "GetCapabilitiesExecutor was called multiple times") \
|
||||||
M(InOperatorWithObject, "'in' operator must be used on an object") \
|
M(InOperatorWithObject, "'in' operator must be used on an object") \
|
||||||
M(InstanceOfOperatorBadPrototype, "'prototype' property of {} is not 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();
|
auto& vm = this->vm();
|
||||||
Object::initialize(global_object);
|
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
|
// 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);
|
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);
|
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:
|
private:
|
||||||
static FinalizationRegistry* typed_this(VM&, GlobalObject&);
|
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