Files
UXP/js/xpconnect/tests/unit/test_finalization_registry.js
T
2026-05-19 09:39:19 +08:00

109 lines
3.9 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function assertThrowsTypeError(fn) {
try {
fn();
} catch (e) {
do_check_true(e instanceof TypeError);
return;
}
do_throw("expected TypeError");
}
add_task(function* test_finalization_registry_api_surface() {
do_check_eq(typeof FinalizationRegistry, "function");
do_check_eq(FinalizationRegistry.name, "FinalizationRegistry");
do_check_eq(FinalizationRegistry.length, 1);
assertThrowsTypeError(() => FinalizationRegistry(function() {}));
assertThrowsTypeError(() => new FinalizationRegistry(1));
let registry = new FinalizationRegistry(function() {});
do_check_eq(Object.prototype.toString.call(registry), "[object FinalizationRegistry]");
do_check_eq(typeof FinalizationRegistry.prototype.register, "function");
do_check_eq(FinalizationRegistry.prototype.register.length, 2);
do_check_eq(typeof FinalizationRegistry.prototype.unregister, "function");
do_check_eq(FinalizationRegistry.prototype.unregister.length, 1);
do_check_eq(FinalizationRegistry.prototype.cleanupSome, undefined);
let desc = Object.getOwnPropertyDescriptor(FinalizationRegistry.prototype,
Symbol.toStringTag);
do_check_eq(desc.value, "FinalizationRegistry");
do_check_eq(desc.writable, false);
do_check_eq(desc.enumerable, false);
do_check_eq(desc.configurable, true);
});
add_task(function* test_register_validation_and_unregister() {
let registry = new FinalizationRegistry(function() {});
let target = {};
let token = {};
do_check_eq(registry.register(target, "held"), undefined);
do_check_eq(registry.register({}, "held", token), undefined);
do_check_eq(registry.unregister(token), true);
do_check_eq(registry.unregister({}), false);
assertThrowsTypeError(() => registry.register(1, "held"));
assertThrowsTypeError(() => registry.register(target, target));
assertThrowsTypeError(() => registry.register(target, "held", 1));
assertThrowsTypeError(() => registry.register(Symbol.for("registered"), "held"));
assertThrowsTypeError(() => registry.unregister(undefined));
do_check_eq(registry.register(Symbol("target"), "held"), undefined);
let symbolToken = Symbol("token");
do_check_eq(registry.register({}, "held", symbolToken), undefined);
do_check_eq(registry.unregister(symbolToken), true);
});
add_task(function* test_proto_from_constructor_realm() {
let other = new Components.utils.Sandbox("http://example.com", { freshZone: true });
Components.utils.evalInSandbox("var newTarget = new Function();", other);
for (let proto of [undefined, null, true, "", Symbol(), 1]) {
other.newTarget.prototype = proto;
let registry = Reflect.construct(FinalizationRegistry, [function() {}],
other.newTarget);
do_check_true(Object.getPrototypeOf(registry) ===
other.FinalizationRegistry.prototype);
}
});
add_task(function* test_cleanup_callback_after_gc() {
let cleaned = [];
let registry = new FinalizationRegistry(value => cleaned.push(value));
let token = {};
(function() {
let target = {};
registry.register(target, "held", token);
})();
for (let i = 0; i < 4 && cleaned.length == 0; i++) {
Components.utils.forceGC();
yield Promise.resolve();
}
do_check_eq(cleaned.length, 1);
do_check_eq(cleaned[0], "held");
do_check_eq(registry.unregister(token), false);
});
add_task(function* test_unregister_prevents_cleanup() {
let cleaned = [];
let registry = new FinalizationRegistry(value => cleaned.push(value));
let token = {};
(function() {
let target = {};
registry.register(target, "held", token);
})();
do_check_eq(registry.unregister(token), true);
Components.utils.forceGC();
yield Promise.resolve();
do_check_eq(cleaned.length, 0);
});