зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1629796: Replace finalization iterator with multiple callback calls. r=jonco
Implements the spec changes from: https://github.com/tc39/proposal-weakrefs/pull/187 The spec change removes the `FinalizationRegistryCleanupIterator` in favour of calling the clean-up callback for each finalised value. It also allows to call `cleanupSome()` within the callback function. `FinalizationRegistryObject::cleanupQueuedRecords()` has been changed to iterate from back to front, because this allows us to call `GCVector::popCopy()`, which makes it more efficient to remove entries from the `records` vector. Differential Revision: https://phabricator.services.mozilla.com/D70821
This commit is contained in:
Родитель
4333cb7419
Коммит
8642ae3db5
|
@ -700,7 +700,7 @@ static const uint32_t JSCLASS_FOREGROUND_FINALIZE =
|
|||
// application.
|
||||
static const uint32_t JSCLASS_GLOBAL_APPLICATION_SLOTS = 5;
|
||||
static const uint32_t JSCLASS_GLOBAL_SLOT_COUNT =
|
||||
JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 26;
|
||||
JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 25;
|
||||
|
||||
static constexpr uint32_t JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(uint32_t n) {
|
||||
return JSCLASS_IS_GLOBAL |
|
||||
|
|
|
@ -326,7 +326,6 @@ bool FinalizationRegistryObject::construct(JSContext* cx, unsigned argc,
|
|||
recordsToBeCleanedUp.release(),
|
||||
MemoryUse::FinalizationRegistryRecordVector);
|
||||
registry->initReservedSlot(IsQueuedForCleanupSlot, BooleanValue(false));
|
||||
registry->initReservedSlot(IsCleanupJobActiveSlot, BooleanValue(false));
|
||||
|
||||
if (!cx->runtime()->gc.addFinalizationRegistry(cx, registry)) {
|
||||
return false;
|
||||
|
@ -439,10 +438,6 @@ bool FinalizationRegistryObject::isQueuedForCleanup() const {
|
|||
return getReservedSlot(IsQueuedForCleanupSlot).toBoolean();
|
||||
}
|
||||
|
||||
bool FinalizationRegistryObject::isCleanupJobActive() const {
|
||||
return getReservedSlot(IsCleanupJobActiveSlot).toBoolean();
|
||||
}
|
||||
|
||||
void FinalizationRegistryObject::queueRecordToBeCleanedUp(
|
||||
FinalizationRecordObject* record) {
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
|
@ -456,11 +451,6 @@ void FinalizationRegistryObject::setQueuedForCleanup(bool value) {
|
|||
setReservedSlot(IsQueuedForCleanupSlot, BooleanValue(value));
|
||||
}
|
||||
|
||||
void FinalizationRegistryObject::setCleanupJobActive(bool value) {
|
||||
MOZ_ASSERT(value != isCleanupJobActive());
|
||||
setReservedSlot(IsCleanupJobActiveSlot, BooleanValue(value));
|
||||
}
|
||||
|
||||
// FinalizationRegistry.prototype.register(target, heldValue [, unregisterToken
|
||||
// ])
|
||||
// https://tc39.es/proposal-weakrefs/#sec-finalization-registry.prototype.register
|
||||
|
@ -705,11 +695,7 @@ bool FinalizationRegistryObject::cleanupSome(JSContext* cx, unsigned argc,
|
|||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
// 1. Let finalizationRegistry be the this value.
|
||||
// 2. If Type(finalizationRegistry) is not Object, throw a TypeError
|
||||
// exception.
|
||||
// 3. If finalizationRegistry does not have [[Cells]] and
|
||||
// [[IsFinalizationRegistryCleanupJobActive]] internal slots, throw a
|
||||
// TypeError exception.
|
||||
// 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]).
|
||||
if (!args.thisv().isObject() ||
|
||||
!args.thisv().toObject().is<FinalizationRegistryObject>()) {
|
||||
JS_ReportErrorNumberASCII(
|
||||
|
@ -718,17 +704,10 @@ bool FinalizationRegistryObject::cleanupSome(JSContext* cx, unsigned argc,
|
|||
return false;
|
||||
}
|
||||
|
||||
// 4. If finalizationRegistry.[[IsFinalizationRegistryCleanupJobActive]] is
|
||||
// true, throw a TypeError exception.
|
||||
RootedFinalizationRegistryObject registry(
|
||||
cx, &args.thisv().toObject().as<FinalizationRegistryObject>());
|
||||
if (registry->isCleanupJobActive()) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_BAD_CLEANUP_STATE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 5. If callback is not undefined and IsCallable(callback) is false, throw a
|
||||
// 3. If callback is not undefined and IsCallable(callback) is false, throw a
|
||||
// TypeError exception.
|
||||
RootedObject cleanupCallback(cx);
|
||||
if (!args.get(0).isUndefined()) {
|
||||
|
@ -746,24 +725,6 @@ bool FinalizationRegistryObject::cleanupSome(JSContext* cx, unsigned argc,
|
|||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool FinalizationRegistryObject::hasRegisteredRecordsToBeCleanedUp(
|
||||
HandleFinalizationRegistryObject registry) {
|
||||
FinalizationRecordVector* records = registry->recordsToBeCleanedUp();
|
||||
size_t initialLength = records->length();
|
||||
if (initialLength == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (FinalizationRecordObject* record : *records) {
|
||||
if (record->isActive()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// CleanupFinalizationRegistry ( finalizationRegistry [ , callback ] )
|
||||
// https://tc39.es/proposal-weakrefs/#sec-cleanup-finalization-registry
|
||||
/* static */
|
||||
|
@ -772,20 +733,7 @@ bool FinalizationRegistryObject::cleanupQueuedRecords(
|
|||
HandleObject callbackArg) {
|
||||
MOZ_ASSERT(cx->compartment() == registry->compartment());
|
||||
|
||||
// 2. If CheckForEmptyCells(finalizationRegistry) is false, return.
|
||||
if (!hasRegisteredRecordsToBeCleanedUp(registry)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3. Let iterator be
|
||||
// !CreateFinalizationRegistryCleanupIterator(finalizationRegistry).
|
||||
Rooted<FinalizationIteratorObject*> iterator(
|
||||
cx, FinalizationIteratorObject::create(cx, registry));
|
||||
if (!iterator) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. If callback is undefined, set callback to
|
||||
// 2. If callback is undefined, set callback to
|
||||
// finalizationRegistry.[[CleanupCallback]].
|
||||
RootedValue callback(cx);
|
||||
if (callbackArg) {
|
||||
|
@ -796,193 +744,34 @@ bool FinalizationRegistryObject::cleanupQueuedRecords(
|
|||
callback.setObject(*cleanupCallback);
|
||||
}
|
||||
|
||||
// 5. Set finalizationRegistry.[[IsFinalizationRegistryCleanupJobActive]] to
|
||||
// true.
|
||||
registry->setCleanupJobActive(true);
|
||||
|
||||
FinalizationRecordVector* records = registry->recordsToBeCleanedUp();
|
||||
#ifdef DEBUG
|
||||
size_t initialLength = records->length();
|
||||
#endif
|
||||
|
||||
// 6. Let result be Call(callback, undefined, iterator).
|
||||
RootedValue iteratorVal(cx, ObjectValue(*iterator));
|
||||
RootedValue rval(cx);
|
||||
bool ok = Call(cx, callback, UndefinedHandleValue, iteratorVal, &rval);
|
||||
|
||||
// Remove records that were iterated over.
|
||||
size_t index = iterator->index();
|
||||
MOZ_ASSERT(index <= records->length());
|
||||
MOZ_ASSERT(initialLength <= records->length());
|
||||
if (index > 0) {
|
||||
records->erase(records->begin(), records->begin() + index);
|
||||
}
|
||||
|
||||
// 7. Set finalizationRegistry.[[IsFinalizationRegistryCleanupJobActive]] to
|
||||
// false.
|
||||
registry->setCleanupJobActive(false);
|
||||
|
||||
// 8. Set iterator.[[FinalizationRegistry]] to empty.
|
||||
iterator->clearFinalizationRegistry();
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// FinalizationIteratorObject
|
||||
|
||||
const JSClass FinalizationIteratorObject::class_ = {
|
||||
"FinalizationRegistryCleanupIterator",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(SlotCount), JS_NULL_CLASS_OPS,
|
||||
JS_NULL_CLASS_SPEC};
|
||||
|
||||
const JSFunctionSpec FinalizationIteratorObject::methods_[] = {
|
||||
JS_FN(js_next_str, next, 0, 0), JS_FS_END};
|
||||
|
||||
const JSPropertySpec FinalizationIteratorObject::properties_[] = {
|
||||
JS_STRING_SYM_PS(toStringTag, "FinalizationRegistry Cleanup Iterator",
|
||||
JSPROP_READONLY),
|
||||
JS_PS_END};
|
||||
|
||||
/* static */
|
||||
bool GlobalObject::initFinalizationIteratorProto(JSContext* cx,
|
||||
Handle<GlobalObject*> global) {
|
||||
Rooted<JSObject*> base(
|
||||
cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
|
||||
if (!base) {
|
||||
return false;
|
||||
}
|
||||
RootedPlainObject proto(
|
||||
cx, GlobalObject::createBlankPrototypeInheriting<PlainObject>(cx, base));
|
||||
if (!proto) {
|
||||
return false;
|
||||
}
|
||||
if (!JS_DefineFunctions(cx, proto, FinalizationIteratorObject::methods_) ||
|
||||
!JS_DefineProperties(cx, proto,
|
||||
FinalizationIteratorObject::properties_)) {
|
||||
return false;
|
||||
}
|
||||
global->setReservedSlot(FINALIZATION_ITERATOR_PROTO, ObjectValue(*proto));
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ FinalizationIteratorObject* FinalizationIteratorObject::create(
|
||||
JSContext* cx, HandleFinalizationRegistryObject registry) {
|
||||
MOZ_ASSERT(registry);
|
||||
|
||||
RootedObject proto(cx, GlobalObject::getOrCreateFinalizationIteratorPrototype(
|
||||
cx, cx->global()));
|
||||
if (!proto) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FinalizationIteratorObject* iterator =
|
||||
NewObjectWithGivenProto<FinalizationIteratorObject>(cx, proto);
|
||||
if (!iterator) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
iterator->initReservedSlot(FinalizationRegistrySlot, ObjectValue(*registry));
|
||||
iterator->initReservedSlot(IndexSlot, Int32Value(0));
|
||||
|
||||
return iterator;
|
||||
}
|
||||
|
||||
FinalizationRegistryObject* FinalizationIteratorObject::finalizationRegistry()
|
||||
const {
|
||||
Value value = getReservedSlot(FinalizationRegistrySlot);
|
||||
if (value.isUndefined()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &value.toObject().as<FinalizationRegistryObject>();
|
||||
}
|
||||
|
||||
size_t FinalizationIteratorObject::index() const {
|
||||
int32_t i = getReservedSlot(IndexSlot).toInt32();
|
||||
MOZ_ASSERT(i >= 0);
|
||||
return size_t(i);
|
||||
}
|
||||
|
||||
void FinalizationIteratorObject::setIndex(size_t i) {
|
||||
MOZ_ASSERT(i <= INT32_MAX);
|
||||
setReservedSlot(IndexSlot, Int32Value(int32_t(i)));
|
||||
}
|
||||
|
||||
void FinalizationIteratorObject::clearFinalizationRegistry() {
|
||||
MOZ_ASSERT(finalizationRegistry());
|
||||
setReservedSlot(FinalizationRegistrySlot, UndefinedValue());
|
||||
}
|
||||
|
||||
// %FinalizationRegistryCleanupIteratorPrototype%.next()
|
||||
// https://tc39.es/proposal-weakrefs/#sec-%finalizationregistrycleanupiterator%.next
|
||||
/* static */
|
||||
bool FinalizationIteratorObject::next(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
// 1. Let iterator be the this value.
|
||||
// 2. If Type(iterator) is not Object, throw a TypeError exception.
|
||||
// 3. If iterator does not have a [[FinalizationRegistry]] internal slot,
|
||||
// throw a TypeError exception.
|
||||
if (!args.thisv().isObject() ||
|
||||
!args.thisv().toObject().is<FinalizationIteratorObject>()) {
|
||||
JS_ReportErrorNumberASCII(
|
||||
cx, GetErrorMessage, nullptr, JSMSG_NOT_A_FINALIZATION_ITERATOR,
|
||||
"Receiver of FinalizationRegistryCleanupIterator.next call");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedFinalizationIteratorObject iterator(
|
||||
cx, &args.thisv().toObject().as<FinalizationIteratorObject>());
|
||||
|
||||
// 4. If iterator.[[FinalizationRegistry]] is empty, throw a TypeError
|
||||
// exception.
|
||||
// 5. Let finalizationRegistry be iterator.[[FinalizationRegistry]].
|
||||
RootedFinalizationRegistryObject registry(cx,
|
||||
iterator->finalizationRegistry());
|
||||
if (!registry) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_STALE_FINALIZATION_REGISTRY_ITERATOR);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 8. If finalizationRegistry.[[Cells]] contains a Record cell such that
|
||||
// cell.[[Target]] is empty,
|
||||
// 3. While finalizationRegistry.[[Cells]] contains a Record cell such that
|
||||
// cell.[[WeakRefTarget]] is empty, then an implementation may perform the
|
||||
// following steps,
|
||||
// a. Choose any such cell.
|
||||
// b. Remove cell from finalizationRegistry.[[Cells]].
|
||||
// c. Return CreateIterResultObject(cell.[[HeldValue]], false).
|
||||
// c. Perform ? Call(callback, undefined, « cell.[[HeldValue]] »).
|
||||
|
||||
RootedValue heldValue(cx);
|
||||
RootedValue rval(cx);
|
||||
FinalizationRecordVector* records = registry->recordsToBeCleanedUp();
|
||||
size_t index = iterator->index();
|
||||
MOZ_ASSERT(index <= records->length());
|
||||
FinalizationRecordSet* activeRecords = registry->activeRecords();
|
||||
while (!records->empty()) {
|
||||
FinalizationRecordObject* record = records->popCopy();
|
||||
|
||||
// Advance until we find a record that hasn't been unregistered.
|
||||
while (index < records->length() && index < INT32_MAX &&
|
||||
!(*records)[index]->isActive()) {
|
||||
index++;
|
||||
iterator->setIndex(index);
|
||||
}
|
||||
|
||||
if (index < records->length() && index < INT32_MAX) {
|
||||
RootedFinalizationRecordObject record(cx, (*records)[index]);
|
||||
RootedValue heldValue(cx, record->heldValue());
|
||||
PlainObject* result = CreateIterResultObject(cx, heldValue, false);
|
||||
if (!result) {
|
||||
return false;
|
||||
// Skip over records that have been unregistered.
|
||||
if (!record->isActive()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
registry->activeRecords()->remove(record);
|
||||
heldValue.set(record->heldValue());
|
||||
|
||||
activeRecords->remove(record);
|
||||
record->clear();
|
||||
iterator->setIndex(index + 1);
|
||||
|
||||
args.rval().setObject(*result);
|
||||
return true;
|
||||
if (!Call(cx, callback, UndefinedHandleValue, heldValue, &rval)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 9. Otherwise, return CreateIterResultObject(undefined, true).
|
||||
PlainObject* result = CreateIterResultObject(cx, UndefinedHandleValue, true);
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setObject(*result);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -181,7 +181,6 @@ class FinalizationRegistryObject : public NativeObject {
|
|||
ActiveRecords,
|
||||
RecordsToBeCleanedUpSlot,
|
||||
IsQueuedForCleanupSlot,
|
||||
IsCleanupJobActiveSlot,
|
||||
SlotCount
|
||||
};
|
||||
|
||||
|
@ -194,11 +193,9 @@ class FinalizationRegistryObject : public NativeObject {
|
|||
FinalizationRecordSet* activeRecords() const;
|
||||
FinalizationRecordVector* recordsToBeCleanedUp() const;
|
||||
bool isQueuedForCleanup() const;
|
||||
bool isCleanupJobActive() const;
|
||||
|
||||
void queueRecordToBeCleanedUp(FinalizationRecordObject* record);
|
||||
void setQueuedForCleanup(bool value);
|
||||
void setCleanupJobActive(bool value);
|
||||
|
||||
void sweep();
|
||||
|
||||
|
@ -227,9 +224,6 @@ class FinalizationRegistryObject : public NativeObject {
|
|||
|
||||
static void trace(JSTracer* trc, JSObject* obj);
|
||||
static void finalize(JSFreeOp* fop, JSObject* obj);
|
||||
|
||||
static bool hasRegisteredRecordsToBeCleanedUp(
|
||||
HandleFinalizationRegistryObject registry);
|
||||
};
|
||||
|
||||
// An iterator over a finalization registry's queued held values. In the spec
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
gczeal(4);
|
||||
let heldValues = [];
|
||||
registry = new FinalizationRegistry(iterator => {
|
||||
heldValues.push(...iterator);
|
||||
registry = new FinalizationRegistry(value => {
|
||||
heldValues.push(value);
|
||||
});
|
||||
registry.register({}, 42);
|
||||
gc();
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// |jit-test| --enable-weak-refs
|
||||
|
||||
const token = {};
|
||||
let iterated;
|
||||
const finalizationRegistry = new FinalizationRegistry(items => {
|
||||
iterated = items.next().value;
|
||||
let cleanedUpValue;
|
||||
const finalizationRegistry = new FinalizationRegistry(value => {
|
||||
cleanedUpValue = value;
|
||||
});
|
||||
{
|
||||
let object = {};
|
||||
|
@ -12,5 +12,5 @@ const finalizationRegistry = new FinalizationRegistry(items => {
|
|||
}
|
||||
gc();
|
||||
finalizationRegistry.cleanupSome();
|
||||
assertEq(iterated, token);
|
||||
assertEq(cleanedUpValue, token);
|
||||
assertEq(finalizationRegistry.unregister(token), false);
|
||||
|
|
|
@ -11,8 +11,8 @@ function ccwToObject() {
|
|||
}
|
||||
|
||||
function newRegistry() {
|
||||
return new FinalizationRegistry(iterator => {
|
||||
heldValues.push(...iterator);
|
||||
return new FinalizationRegistry(value => {
|
||||
heldValues.push(value);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ function ccwToRegistry() {
|
|||
let global = newGlobal({newCompartment: true});
|
||||
global.heldValues = heldValues;
|
||||
return global.eval(
|
||||
`new FinalizationRegistry(iterator => heldValues.push(...iterator))`);
|
||||
`new FinalizationRegistry(value => heldValues.push(value))`);
|
||||
}
|
||||
|
||||
function incrementalGC() {
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
// |jit-test| --enable-weak-refs
|
||||
|
||||
// Test trying to call cleanupSome recursively in callback.
|
||||
|
||||
// 0: Initial state.
|
||||
// 1: Attempt recursive calls.
|
||||
// 2: After recursive calls.
|
||||
let state = 0;
|
||||
|
||||
let registry = new FinalizationRegistry(x => {
|
||||
if (state === 0) {
|
||||
state = 1;
|
||||
try {
|
||||
registry.cleanupSome();
|
||||
} catch (e) {
|
||||
// Pass the test if any error was thrown.
|
||||
return;
|
||||
} finally {
|
||||
state = 2;
|
||||
}
|
||||
throw new Error("expected stack overflow error");
|
||||
}
|
||||
|
||||
if (state === 1) {
|
||||
registry.cleanupSome();
|
||||
}
|
||||
});
|
||||
|
||||
// Attempt to find the maximum supported stack depth.
|
||||
function findStackSize(i) {
|
||||
try {
|
||||
return findStackSize(i + 1);
|
||||
} catch {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
const stackSize = findStackSize(0);
|
||||
|
||||
// Multiply the calculated stack size by some factor just to be on the safe side.
|
||||
const exceedStackDepthLimit = stackSize * 5;
|
||||
|
||||
let values = [];
|
||||
for (let i = 0; i < exceedStackDepthLimit; ++i) {
|
||||
let v = {};
|
||||
registry.register(v, i);
|
||||
values.push(v);
|
||||
}
|
||||
values.length = 0;
|
||||
|
||||
gc();
|
||||
drainJobQueue();
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
// Test gray finalization registry is correctly barrired.
|
||||
target = {};
|
||||
registry = new FinalizationRegistry(iterator => undefined);
|
||||
registry = new FinalizationRegistry(value => undefined);
|
||||
registry.register(target, 1);
|
||||
grayRoot()[0] = registry;
|
||||
registry = undefined;
|
||||
|
|
|
@ -61,36 +61,9 @@ let registry = new FinalizationRegistry(x => 0);
|
|||
assertEq(Object.getPrototypeOf(registry), proto);
|
||||
assertEq(Object.getOwnPropertyNames(registry).length, 0);
|
||||
|
||||
// Get a cleanup iterator.
|
||||
let iterator;
|
||||
registry = new FinalizationRegistry(it => iterator = it);
|
||||
registry.register({}, 0);
|
||||
gc();
|
||||
drainJobQueue();
|
||||
assertEq(typeof registry, 'object');
|
||||
assertEq(typeof iterator, 'object');
|
||||
|
||||
// 3.5.2 The %FinalizationRegistryCleanupIteratorPrototype% Object
|
||||
let arrayIterator = [][Symbol.iterator]();
|
||||
let iteratorProto = arrayIterator.__proto__.__proto__;
|
||||
proto = iterator.__proto__;
|
||||
assertEq(typeof proto, "object");
|
||||
assertEq(proto.__proto__, iteratorProto);
|
||||
|
||||
// 3.5.2.1 %FinalizationRegistryCleanupIteratorPrototype%.next()
|
||||
assertEq(proto.hasOwnProperty("next"), true);
|
||||
assertEq(typeof proto.next, "function");
|
||||
|
||||
// 3.5.2.2 %FinalizationRegistryCleanupIteratorPrototype% [ @@toStringTag ]
|
||||
assertEq(proto[Symbol.toStringTag], "FinalizationRegistry Cleanup Iterator");
|
||||
checkPropertyDescriptor(proto, Symbol.toStringTag, false, false, true);
|
||||
|
||||
// 3.5.3 Properties of FinalizationRegistry Cleanup Iterator Instances
|
||||
assertEq(Object.getOwnPropertyNames(iterator).length, 0);
|
||||
|
||||
let heldValues = [];
|
||||
registry = new FinalizationRegistry(iterator => {
|
||||
heldValues.push(...iterator);
|
||||
registry = new FinalizationRegistry(value => {
|
||||
heldValues.push(value);
|
||||
});
|
||||
|
||||
// Test a single target.
|
||||
|
@ -117,8 +90,8 @@ for (let i = 0; i < 100; i++) {
|
|||
// Test a single object in multiple registries
|
||||
heldValues = [];
|
||||
let heldValues2 = [];
|
||||
let registry2 = new FinalizationRegistry(iterator => {
|
||||
heldValues2.push(...iterator);
|
||||
let registry2 = new FinalizationRegistry(value => {
|
||||
heldValues2.push(value);
|
||||
});
|
||||
{
|
||||
let object = {};
|
||||
|
@ -196,8 +169,8 @@ class MyRegistry extends FinalizationRegistry {
|
|||
super(callback);
|
||||
}
|
||||
}
|
||||
let r2 = new MyRegistry(iterator => {
|
||||
heldValues.push(...iterator);
|
||||
let r2 = new MyRegistry(value => {
|
||||
heldValues.push(value);
|
||||
});
|
||||
heldValues = [];
|
||||
r2.register({}, 42);
|
||||
|
@ -206,26 +179,9 @@ drainJobQueue();
|
|||
assertEq(heldValues.length, 1);
|
||||
assertEq(heldValues[0], 42);
|
||||
|
||||
// Test trying to use iterator after the callback.
|
||||
iterator = undefined;
|
||||
let r3 = new FinalizationRegistry(i => iterator = i);
|
||||
r3.register({}, 1);
|
||||
gc();
|
||||
drainJobQueue();
|
||||
assertEq(typeof iterator, 'object');
|
||||
assertThrowsTypeError(() => iterator.next());
|
||||
|
||||
// Test trying to use the wrong iterator inside the callback.
|
||||
let r4 = new FinalizationRegistry(x => {
|
||||
assertThrowsTypeError(() => iterator.next());
|
||||
});
|
||||
r4.register({}, 1);
|
||||
gc();
|
||||
drainJobQueue();
|
||||
|
||||
// Test cleanupSome.
|
||||
heldValues = [];
|
||||
let r5 = new FinalizationRegistry(i => heldValues = [...i]);
|
||||
let r5 = new FinalizationRegistry(v => heldValues.push(v));
|
||||
r5.register({}, 1);
|
||||
r5.register({}, 2);
|
||||
r5.register({}, 3);
|
||||
|
@ -239,15 +195,29 @@ assertEq(heldValues[2], 3);
|
|||
|
||||
// Test trying to call cleanupSome in callback.
|
||||
let r6 = new FinalizationRegistry(x => {
|
||||
assertThrowsTypeError(() => r6.cleanupSome());
|
||||
r6.cleanupSome();
|
||||
});
|
||||
r6.register({}, 1);
|
||||
gc();
|
||||
drainJobQueue();
|
||||
|
||||
// Test trying to call cleanupSome in callback with multiple values.
|
||||
let callbackCounter7 = 0;
|
||||
let r7 = new FinalizationRegistry(x => {
|
||||
callbackCounter7++;
|
||||
r7.cleanupSome();
|
||||
});
|
||||
r7.register({}, 1);
|
||||
r7.register({}, 2);
|
||||
r7.register({}, 3);
|
||||
r7.register({}, 4);
|
||||
gc();
|
||||
drainJobQueue();
|
||||
assertEq(callbackCounter7, 4);
|
||||
|
||||
// Test that targets don't keep the finalization registry alive.
|
||||
let target = {};
|
||||
registry = new FinalizationRegistry(iterator => undefined);
|
||||
registry = new FinalizationRegistry(value => undefined);
|
||||
registry.register(target, 1);
|
||||
let weakRef = new WeakRef(registry);
|
||||
registry = undefined;
|
||||
|
@ -259,7 +229,7 @@ assertEq(typeof target, 'object');
|
|||
|
||||
// Test that targets don't keep the finalization registry alive when also
|
||||
// used as the unregister token.
|
||||
registry = new FinalizationRegistry(iterator => undefined);
|
||||
registry = new FinalizationRegistry(value => undefined);
|
||||
registry.register(target, 1, target);
|
||||
weakRef = new WeakRef(registry);
|
||||
registry = undefined;
|
||||
|
@ -271,8 +241,8 @@ assertEq(typeof target, 'object');
|
|||
|
||||
// Test that cleanup doesn't happen if the finalization registry dies.
|
||||
heldValues = [];
|
||||
new FinalizationRegistry(iterator => {
|
||||
heldValues.push(...iterator);
|
||||
new FinalizationRegistry(value => {
|
||||
heldValues.push(value);
|
||||
}).register({}, 1);
|
||||
gc();
|
||||
drainJobQueue();
|
||||
|
|
|
@ -412,14 +412,6 @@ skip script test262/intl402/ListFormat/constructor/constructor/proto-from-ctor-r
|
|||
# https://bugzilla.mozilla.org/show_bug.cgi?id=1362154
|
||||
skip script test262/built-ins/String/prototype/replaceAll/searchValue-replacer-RegExp-call.js
|
||||
|
||||
# https://github.com/tc39/proposal-weakrefs/pull/187
|
||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=1629796
|
||||
skip script test262/built-ins/FinalizationRegistry/gc-has-one-chance-to-call-cleanupCallback.js
|
||||
skip script test262/built-ins/FinalizationRegistry/prototype/cleanupSome/holdings-multiple-values.js
|
||||
skip script test262/built-ins/FinalizationRegistry/prototype/cleanupSome/cleanup-prevented-with-reference.js
|
||||
skip script test262/built-ins/FinalizationRegistry/prototype/cleanupSome/reentrancy.js
|
||||
skip script test262/built-ins/FinalizationRegistry/prototype/unregister/unregister-cleaned-up-cell.js
|
||||
|
||||
# Depends upon the SharedArrayBuffer constructor being defined as a global
|
||||
# property -- and right now, it's only defined for cross-site-isolated pages
|
||||
# that request it using COOP/COEP.
|
||||
|
|
|
@ -115,7 +115,6 @@ class GlobalObject : public NativeObject {
|
|||
IMPORT_ENTRY_PROTO,
|
||||
EXPORT_ENTRY_PROTO,
|
||||
REQUESTED_MODULE_PROTO,
|
||||
FINALIZATION_ITERATOR_PROTO,
|
||||
REGEXP_STATICS,
|
||||
RUNTIME_CODEGEN_ENABLED,
|
||||
INTRINSICS,
|
||||
|
@ -548,12 +547,6 @@ class GlobalObject : public NativeObject {
|
|||
return &global->getPrototype(JSProto_TypedArray).toObject();
|
||||
}
|
||||
|
||||
static JSObject* getOrCreateFinalizationIteratorPrototype(
|
||||
JSContext* cx, Handle<GlobalObject*> global) {
|
||||
return getOrCreateObject(cx, global, FINALIZATION_ITERATOR_PROTO,
|
||||
initFinalizationIteratorProto);
|
||||
}
|
||||
|
||||
private:
|
||||
using ObjectInitOp = bool (*)(JSContext*, Handle<GlobalObject*>);
|
||||
|
||||
|
@ -853,10 +846,6 @@ class GlobalObject : public NativeObject {
|
|||
static bool initTypedObjectModule(JSContext* cx,
|
||||
Handle<GlobalObject*> global);
|
||||
|
||||
// Implemented in builtin/FinalizationRegistry.cpp
|
||||
static bool initFinalizationIteratorProto(JSContext* cx,
|
||||
Handle<GlobalObject*> global);
|
||||
|
||||
static bool initStandardClasses(JSContext* cx, Handle<GlobalObject*> global);
|
||||
static bool initSelfHostingBuiltins(JSContext* cx,
|
||||
Handle<GlobalObject*> global,
|
||||
|
|
|
@ -19,27 +19,27 @@ onmessage = (event) => {
|
|||
|
||||
function startTest() {
|
||||
// Registry with no registered objects.
|
||||
let registry1 = new FinalizationRegistry(i => holdings1 = [...i]);
|
||||
let registry1 = new FinalizationRegistry(v => { holdings1.push(v); });
|
||||
|
||||
// Registry with three registered objects.
|
||||
let registry2 = new FinalizationRegistry(i => holdings2 = [...i]);
|
||||
let registry2 = new FinalizationRegistry(v => { holdings2.push(v); });
|
||||
registry2.register({}, 1);
|
||||
registry2.register({}, 2);
|
||||
registry2.register({}, 3);
|
||||
|
||||
// Registry with registered object that is then unregistered.
|
||||
let registry3 = new FinalizationRegistry(i => holdings3 = [...i]);
|
||||
let registry3 = new FinalizationRegistry(v => { holdings3.push(v); });
|
||||
let token3 = {}
|
||||
registry3.register({}, 1, token3);
|
||||
registry3.unregister(token3);
|
||||
|
||||
// Registry with registered object that doesn't die.
|
||||
let registry4 = new FinalizationRegistry(i => holdings4 = [...i]);
|
||||
let registry4 = new FinalizationRegistry(v => { holdings4.push(v); });
|
||||
let object4 = {};
|
||||
registry4.register(object4, 1);
|
||||
|
||||
// Registry observing cyclic JS data structure.
|
||||
let registry5 = new FinalizationRegistry(i => holdings5 = [...i]);
|
||||
let registry5 = new FinalizationRegistry(v => { holdings5.push(v); });
|
||||
registry5.register(makeJSCycle(4), 5);
|
||||
|
||||
const { gc } = getJSTestingFunctions();
|
||||
|
|
|
@ -12,35 +12,35 @@
|
|||
|
||||
// Registry with no registered objects.
|
||||
let holdings1 = [];
|
||||
let registry1 = new FinalizationRegistry(i => holdings1 = [...i]);
|
||||
let registry1 = new FinalizationRegistry(v => { holdings1.push(v); });
|
||||
|
||||
// Registry with three registered objects.
|
||||
let holdings2 = [];
|
||||
let registry2 = new FinalizationRegistry(i => holdings2 = [...i]);
|
||||
let registry2 = new FinalizationRegistry(v => { holdings2.push(v); });
|
||||
registry2.register({}, 1);
|
||||
registry2.register({}, 2);
|
||||
registry2.register({}, 3);
|
||||
|
||||
// Registry with registered object that is then unregistered.
|
||||
let holdings3 = [];
|
||||
let registry3 = new FinalizationRegistry(i => holdings3 = [...i]);
|
||||
let registry3 = new FinalizationRegistry(v => { holdings3.push(v); });
|
||||
let token3 = {}
|
||||
registry3.register({}, 1, token3);
|
||||
registry3.unregister(token3);
|
||||
|
||||
// Registry with registered object that doesn't die.
|
||||
let holdings4 = [];
|
||||
let registry4 = new FinalizationRegistry(i => holdings4 = [...i]);
|
||||
let registry4 = new FinalizationRegistry(v => { holdings4.push(v); });
|
||||
registry4.register(object4, 1);
|
||||
|
||||
// Registry observing cyclic JS data structure.
|
||||
let holdings5 = [];
|
||||
let registry5 = new FinalizationRegistry(i => holdings5 = [...i]);
|
||||
let registry5 = new FinalizationRegistry(v => { holdings5.push(v); });
|
||||
registry5.register(makeJSCycle(4), 5);
|
||||
|
||||
// Registry observing cyclic DOM/JS data structure.
|
||||
let holdings6 = [];
|
||||
let registry6 = new FinalizationRegistry(i => holdings6 = [...i]);
|
||||
let registry6 = new FinalizationRegistry(v => { holdings6.push(v); });
|
||||
registry6.register(makeDOMCycle(4), 6);
|
||||
|
||||
// Need to run full GC/CC/GC cycle to collect cyclic garbage through DOM
|
||||
|
|
Загрузка…
Ссылка в новой задаче