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:
André Bargull 2020-05-13 15:25:24 +00:00
Родитель 4333cb7419
Коммит 8642ae3db5
13 изменённых файлов: 121 добавлений и 336 удалений

Просмотреть файл

@ -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