Update tests for GC hook, apply async

- Fix incorrect tests for WeakRefs

Ref #2239
Fixes #2260
Fixes #2256
This commit is contained in:
Leo Balter 2019-09-04 13:51:41 -03:00 committed by Rick Waldron
parent 7134634aa4
commit 08844700fe
21 changed files with 494 additions and 508 deletions

71
harness/async-gc.js Normal file
View File

@ -0,0 +1,71 @@
// Copyright (C) 2019 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
Collection of functions used to capture references cleanup from garbage collectors
features: [Symbol, async-functions]
flags: [non-deterministic]
features: [FinalizationGroup]
---*/
function asyncGC(...targets) {
var fg = new FinalizationGroup(() => {});
var length = targets.length;
for (let target of targets) {
fg.register(target, 'target');
target = null;
}
targets = null;
return Promise.resolve('tick').then(() => asyncGCDeref()).then(() => {
var names;
// consume iterator to capture names
fg.cleanupSome(iter => { names = [...iter]; });
if (!names || names.length != length) {
throw asyncGC.notCollected;
}
});
}
asyncGC.notCollected = Symbol('Object was not collected');
async function asyncGCDeref() {
var trigger;
// TODO: Remove this when $262.clearKeptObject becomes documented and required
if ($262.clearKeptObjects) {
trigger = $262.clearKeptObjects();
}
await $262.gc();
return Promise.resolve(trigger);
}
function resolveAsyncGC(err) {
if (err === asyncGC.notCollected) {
// Do not fail as GC can't provide necessary resources.
$DONE();
}
$DONE(err);
}
// function emptyCells() {
// var target = {};
// var wr = new WeakRef(target);
// var collected = asyncGC(target, clearKeptObjects());
// target = null;
// return collected.then(() => {
// return wr.deref();
// });
// }
// emptyCells().then((derefAsync) => {
// assert.sameValue(derefAsync, undefined);
// });

View File

@ -26,8 +26,9 @@ info: |
The initial value of the @@toStringTag property is the String value "FinalizationGroup Cleanup Iterator". The initial value of the @@toStringTag property is the String value "FinalizationGroup Cleanup Iterator".
This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
includes: [propertyHelper.js]
features: [FinalizationGroup, host-gc-required, Symbol, Symbol.toStringTag] features: [FinalizationGroup, host-gc-required, Symbol, Symbol.toStringTag]
includes: [async-gc.js, propertyHelper.js]
flags: [async, non-deterministic]
---*/ ---*/
var FinalizationGroupCleanupIteratorPrototype; var FinalizationGroupCleanupIteratorPrototype;
@ -39,20 +40,25 @@ function callback(iterator) {
FinalizationGroupCleanupIteratorPrototype = Object.getPrototypeOf(iterator); FinalizationGroupCleanupIteratorPrototype = Object.getPrototypeOf(iterator);
} }
(function() { function emptyCells() {
var o = {}; var target = {};
fg.register(o); fg.register(target);
})();
$262.gc(); var prom = asyncGC(target);
target = null;
fg.cleanupSome(callback); return prom;
}
assert.sameValue(called, 1, 'cleanup successful'); emptyCells().then(function() {
fg.cleanupSome(callback);
verifyProperty(FinalizationGroupCleanupIteratorPrototype, Symbol.toStringTag, { assert.sameValue(called, 1, 'cleanup successful');
verifyProperty(FinalizationGroupCleanupIteratorPrototype, Symbol.toStringTag, {
value: 'FinalizationGroup Cleanup Iterator', value: 'FinalizationGroup Cleanup Iterator',
writable: false, writable: false,
enumerable: false, enumerable: false,
configurable: true configurable: true
}); });
}).then($DONE, resolveAsyncGC);

View File

@ -29,6 +29,8 @@ info: |
2. If Type(iterator) is not Object, throw a TypeError exception. 2. If Type(iterator) is not Object, throw a TypeError exception.
3. If iterator does not have a [[FinalizationGroup]] internal slot, throw a TypeError exception. 3. If iterator does not have a [[FinalizationGroup]] internal slot, throw a TypeError exception.
features: [FinalizationGroup, WeakRef, host-gc-required, Symbol] features: [FinalizationGroup, WeakRef, host-gc-required, Symbol]
includes: [async-gc.js]
flags: [async, non-deterministic]
---*/ ---*/
var iter; var iter;
@ -42,21 +44,26 @@ function callback(iterator) {
FinalizationGroupCleanupIteratorPrototype = Object.getPrototypeOf(iterator); FinalizationGroupCleanupIteratorPrototype = Object.getPrototypeOf(iterator);
} }
(function() { function emptyCells() {
var o = {}; var target = {};
fg.register(o); fg.register(target, 'target');
})();
$262.gc(); var prom = asyncGC(target);
target = null;
fg.cleanupSome(callback); return prom;
}
// Make sure everything is set emptyCells().then(function() {
assert.sameValue(called, 1, 'cleanup successful'); fg.cleanupSome(callback);
assert.sameValue(typeof iter, 'object');
assert.sameValue(Object.getPrototypeOf(iter), FinalizationGroupCleanupIteratorPrototype);
// To the actual assertion // Make sure everything is set
assert.throws(TypeError, function() { assert.sameValue(called, 1, 'cleanup successful');
assert.sameValue(typeof iter, 'object');
assert.sameValue(Object.getPrototypeOf(iter), FinalizationGroupCleanupIteratorPrototype);
// To the actual assertion
assert.throws(TypeError, function() {
iter.next(); iter.next();
}, 'Iter should fail if not called during the cleanupSome call'); }, 'Iter should fail if not called during the cleanupSome call');
}).then($DONE, resolveAsyncGC);

View File

@ -34,7 +34,8 @@ info: |
Unless otherwise specified, the length property of a built-in Unless otherwise specified, the length property of a built-in
function object has the attributes { [[Writable]]: false, function object has the attributes { [[Writable]]: false,
[[Enumerable]]: false, [[Configurable]]: true }. [[Enumerable]]: false, [[Configurable]]: true }.
includes: [propertyHelper.js] includes: [async-gc.js, propertyHelper.js]
flags: [async, non-deterministic]
features: [FinalizationGroup, host-gc-required, Symbol] features: [FinalizationGroup, host-gc-required, Symbol]
---*/ ---*/
@ -47,24 +48,27 @@ function callback(iterator) {
FinalizationGroupCleanupIteratorPrototype = Object.getPrototypeOf(iterator); FinalizationGroupCleanupIteratorPrototype = Object.getPrototypeOf(iterator);
} }
(function() { function emptyCells() {
var o = {}; var target = {};
fg.register(o); fg.register(target);
})();
$262.gc(); var prom = asyncGC(target);
target = null;
fg.cleanupSome(callback); return prom;
}
assert.sameValue(called, 1, 'cleanup successful'); emptyCells().then(function() {
fg.cleanupSome(callback);
assert.sameValue(typeof FinalizationGroupCleanupIteratorPrototype.next, 'function'); assert.sameValue(called, 1, 'cleanup successful');
var next = FinalizationGroupCleanupIteratorPrototype.next; assert.sameValue(typeof FinalizationGroupCleanupIteratorPrototype.next, 'function');
verifyProperty(next, 'length', { verifyProperty(FinalizationGroupCleanupIteratorPrototype.next, 'length', {
value: 0, value: 0,
enumerable: false, enumerable: false,
writable: false, writable: false,
configurable: true, configurable: true,
}); });
}).then($DONE, resolveAsyncGC);

View File

@ -27,6 +27,8 @@ info: |
2. If Type(iterator) is not Object, throw a TypeError exception. 2. If Type(iterator) is not Object, throw a TypeError exception.
3. If iterator does not have a [[FinalizationGroup]] internal slot, throw a TypeError exception. 3. If iterator does not have a [[FinalizationGroup]] internal slot, throw a TypeError exception.
features: [FinalizationGroup, WeakRef, host-gc-required, Symbol] features: [FinalizationGroup, WeakRef, host-gc-required, Symbol]
includes: [async-gc.js]
flags: [async, non-deterministic]
---*/ ---*/
var FinalizationGroupCleanupIteratorPrototype; var FinalizationGroupCleanupIteratorPrototype;
@ -70,14 +72,19 @@ function callback(iterator) {
endOfCall += 1; endOfCall += 1;
} }
(function() { function emptyCells() {
var o = {}; var target = {};
fg.register(o); fg.register(target);
})();
$262.gc(); var prom = asyncGC(target);
target = null;
fg.cleanupSome(callback); return prom;
}
assert.sameValue(called, 1, 'cleanup successful'); emptyCells().then(function() {
assert.sameValue(endOfCall, 1, 'Abrupt completions are not directly returned.'); fg.cleanupSome(callback);
assert.sameValue(called, 1, 'cleanup successful');
assert.sameValue(endOfCall, 1, 'Abrupt completions are not directly returned.');
}).then($DONE, resolveAsyncGC);

View File

@ -33,7 +33,8 @@ info: |
Unless otherwise specified, the name property of a built-in function Unless otherwise specified, the name property of a built-in function
object, if it exists, has the attributes { [[Writable]]: false, object, if it exists, has the attributes { [[Writable]]: false,
[[Enumerable]]: false, [[Configurable]]: true }. [[Enumerable]]: false, [[Configurable]]: true }.
includes: [propertyHelper.js] includes: [async-gc.js, propertyHelper.js]
flags: [async, non-deterministic]
features: [FinalizationGroup, host-gc-required, Symbol] features: [FinalizationGroup, host-gc-required, Symbol]
---*/ ---*/
@ -46,24 +47,29 @@ function callback(iterator) {
FinalizationGroupCleanupIteratorPrototype = Object.getPrototypeOf(iterator); FinalizationGroupCleanupIteratorPrototype = Object.getPrototypeOf(iterator);
} }
(function() { function emptyCells() {
var o = {}; var target = {};
fg.register(o); fg.register(target);
})();
$262.gc(); var prom = asyncGC(target);
target = null;
fg.cleanupSome(callback); return prom;
}
assert.sameValue(called, 1, 'cleanup successful'); emptyCells().then(function() {
fg.cleanupSome(callback);
assert.sameValue(typeof FinalizationGroupCleanupIteratorPrototype.next, 'function'); assert.sameValue(called, 1, 'cleanup successful');
var next = FinalizationGroupCleanupIteratorPrototype.next; assert.sameValue(typeof FinalizationGroupCleanupIteratorPrototype.next, 'function');
verifyProperty(next, 'name', { var next = FinalizationGroupCleanupIteratorPrototype.next;
verifyProperty(next, 'name', {
value: 'next', value: 'next',
enumerable: false, enumerable: false,
writable: false, writable: false,
configurable: true, configurable: true,
}); });
}).then($DONE, resolveAsyncGC);

View File

@ -26,6 +26,8 @@ info: |
1. Let iterator be the this value. 1. Let iterator be the this value.
2. If Type(iterator) is not Object, throw a TypeError exception. 2. If Type(iterator) is not Object, throw a TypeError exception.
features: [FinalizationGroup, host-gc-required, Symbol] features: [FinalizationGroup, host-gc-required, Symbol]
includes: [async-gc.js]
flags: [async, non-deterministic]
---*/ ---*/
var FinalizationGroupCleanupIteratorPrototype; var FinalizationGroupCleanupIteratorPrototype;
@ -37,46 +39,51 @@ function callback(iterator) {
FinalizationGroupCleanupIteratorPrototype = Object.getPrototypeOf(iterator); FinalizationGroupCleanupIteratorPrototype = Object.getPrototypeOf(iterator);
} }
(function() { function emptyCells() {
var o = {}; var target = {};
fg.register(o); fg.register(target);
})();
$262.gc(); var prom = asyncGC(target);
target = null;
fg.cleanupSome(callback); return prom;
}
assert.sameValue(called, 1, 'cleanup successful'); emptyCells().then(function() {
fg.cleanupSome(callback);
assert.sameValue(typeof FinalizationGroupCleanupIteratorPrototype.next, 'function'); assert.sameValue(called, 1, 'cleanup successful');
var next = FinalizationGroupCleanupIteratorPrototype.next; assert.sameValue(typeof FinalizationGroupCleanupIteratorPrototype.next, 'function');
assert.throws(TypeError, function() { var next = FinalizationGroupCleanupIteratorPrototype.next;
assert.throws(TypeError, function() {
next.call(undefined); next.call(undefined);
}, 'undefined'); }, 'undefined');
assert.throws(TypeError, function() { assert.throws(TypeError, function() {
next.call(null); next.call(null);
}, 'null'); }, 'null');
assert.throws(TypeError, function() { assert.throws(TypeError, function() {
next.call(true); next.call(true);
}, 'true'); }, 'true');
assert.throws(TypeError, function() { assert.throws(TypeError, function() {
next.call(false); next.call(false);
}, 'false'); }, 'false');
assert.throws(TypeError, function() { assert.throws(TypeError, function() {
next.call(1); next.call(1);
}, '1'); }, '1');
assert.throws(TypeError, function() { assert.throws(TypeError, function() {
next.call('string'); next.call('string');
}, 'string'); }, 'string');
var symbol = Symbol(); var symbol = Symbol();
assert.throws(TypeError, function() { assert.throws(TypeError, function() {
next.call(symbol); next.call(symbol);
}, 'symbol'); }, 'symbol');
}).then($DONE, resolveAsyncGC);

View File

@ -26,7 +26,8 @@ info: |
Every other data property described in clauses 18 through 26 and in Annex B.2 Every other data property described in clauses 18 through 26 and in Annex B.2
has the attributes { [[Writable]]: true, [[Enumerable]]: false, has the attributes { [[Writable]]: true, [[Enumerable]]: false,
[[Configurable]]: true } unless otherwise specified. [[Configurable]]: true } unless otherwise specified.
includes: [propertyHelper.js] includes: [propertyHelper.js, async-gc.js]
flags: [async, non-deterministic]
features: [FinalizationGroup, host-gc-required, Symbol] features: [FinalizationGroup, host-gc-required, Symbol]
---*/ ---*/
@ -39,23 +40,28 @@ function callback(iterator) {
FinalizationGroupCleanupIteratorPrototype = Object.getPrototypeOf(iterator); FinalizationGroupCleanupIteratorPrototype = Object.getPrototypeOf(iterator);
} }
(function() { function emptyCells() {
var o = {}; var target = {};
fg.register(o); fg.register(target);
})();
$262.gc(); var prom = asyncGC(target);
target = null;
fg.cleanupSome(callback); return prom;
}
assert.sameValue(called, 1, 'cleanup successful'); emptyCells().then(function() {
fg.cleanupSome(callback);
assert.sameValue(typeof FinalizationGroupCleanupIteratorPrototype.next, 'function'); assert.sameValue(called, 1, 'cleanup successful');
var next = FinalizationGroupCleanupIteratorPrototype.next; assert.sameValue(typeof FinalizationGroupCleanupIteratorPrototype.next, 'function');
verifyProperty(FinalizationGroupCleanupIteratorPrototype, 'next', { var next = FinalizationGroupCleanupIteratorPrototype.next;
verifyProperty(FinalizationGroupCleanupIteratorPrototype, 'next', {
enumerable: false, enumerable: false,
writable: true, writable: true,
configurable: true, configurable: true,
}); });
}).then($DONE, resolveAsyncGC);

View File

@ -49,6 +49,8 @@ info: |
6. Set iterator.[[FinalizationGroup]] to finalizationGroup. 6. Set iterator.[[FinalizationGroup]] to finalizationGroup.
7. Return iterator. 7. Return iterator.
features: [FinalizationGroup, host-gc-required] features: [FinalizationGroup, host-gc-required]
includes: [async-gc.js]
flags: [async, non-deterministic]
---*/ ---*/
var IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())); var IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));
@ -64,21 +66,26 @@ function callback(iterator) {
var fg = new FinalizationGroup(function() {}); var fg = new FinalizationGroup(function() {});
(function() { function emptyCells() {
var o = {}; var target = {};
fg.register(o); fg.register(target);
})();
$262.gc(); var prom = asyncGC(target);
target = null;
fg.cleanupSome(callback); return prom;
}
assert.sameValue(called, 1, 'cleanup successful'); emptyCells().then(function() {
fg.cleanupSome(callback);
var proto = Object.getPrototypeOf(FinalizationGroupCleanupIteratorPrototype); assert.sameValue(called, 1, 'cleanup successful');
assert.sameValue(
var proto = Object.getPrototypeOf(FinalizationGroupCleanupIteratorPrototype);
assert.sameValue(
proto, IteratorPrototype, proto, IteratorPrototype,
'[[Prototype]] internal slot whose value is the intrinsic object %IteratorPrototype%' '[[Prototype]] internal slot whose value is the intrinsic object %IteratorPrototype%'
); );
assert.sameValue(cleanupCallbackCalled, 0, 'if a callback is given, do not call cleanupCallback'); assert.sameValue(cleanupCallbackCalled, 0, 'if a callback is given, do not call cleanupCallback');
}).then($DONE, resolveAsyncGC);

View File

@ -26,7 +26,8 @@ info: |
a. Set cell.[[Target]] to empty. a. Set cell.[[Target]] to empty.
b. Optionally, perform ! HostCleanupFinalizationGroup(fg). b. Optionally, perform ! HostCleanupFinalizationGroup(fg).
features: [FinalizationGroup, async-functions, host-gc-required] features: [FinalizationGroup, async-functions, host-gc-required]
flags: [async] flags: [async, non-deterministic]
includes: [async-gc.js]
---*/ ---*/
var cleanupCallback = 0; var cleanupCallback = 0;
@ -44,17 +45,15 @@ var fg = new FinalizationGroup(function() {
}); });
function emptyCells() { function emptyCells() {
(function() { var target = {};
var a = {}; fg.register(target, 'a');
fg.register(a, 'a');
})();
// GC is called here var prom = asyncGC(target);
$262.gc(); target = null;
return prom;
} }
emptyCells();
// Let's add some async ticks, as the cleanupCallback is not meant to interrupt // Let's add some async ticks, as the cleanupCallback is not meant to interrupt
// synchronous operations. // synchronous operations.
async function fn() { async function fn() {
@ -76,7 +75,7 @@ async function fn() {
// callback is not called again. // callback is not called again.
cleanupCallback = 0; cleanupCallback = 0;
$262.gc(); await $262.gc();
await Promise.resolve(2); // tick await Promise.resolve(2); // tick
fg.cleanupSome(cb); fg.cleanupSome(cb);
@ -86,7 +85,7 @@ async function fn() {
assert.sameValue(called, 2, 'cleanupSome callback for the second time'); assert.sameValue(called, 2, 'cleanupSome callback for the second time');
assert.sameValue(cleanupCallback, 0, 'cleanupCallback is not called again #1'); assert.sameValue(cleanupCallback, 0, 'cleanupCallback is not called again #1');
$262.gc(); await $262.gc();
await Promise.resolve(3); // tick await Promise.resolve(3); // tick
fg.cleanupSome(cb); fg.cleanupSome(cb);
@ -94,11 +93,12 @@ async function fn() {
assert.sameValue(called, 3, 'cleanupSome callback for the third time'); assert.sameValue(called, 3, 'cleanupSome callback for the third time');
assert.sameValue(cleanupCallback, 0, 'cleanupCallback is not called again #2'); assert.sameValue(cleanupCallback, 0, 'cleanupCallback is not called again #2');
$262.gc(); await $262.gc();
} }
fn() emptyCells()
.then(async function() { // tick .then(async function() {
await fn();// tick
await Promise.resolve(4); // tick await Promise.resolve(4); // tick
assert.sameValue(cleanupCallback, 0, 'cleanupCallback is not called again #3'); assert.sameValue(cleanupCallback, 0, 'cleanupCallback is not called again #3');
@ -108,7 +108,7 @@ fn()
assert.sameValue(called, 4, 'cleanupSome callback for the fourth time'); assert.sameValue(called, 4, 'cleanupSome callback for the fourth time');
assert.sameValue(cleanupCallback, 0, 'cleanupCallback is not called again #4'); assert.sameValue(cleanupCallback, 0, 'cleanupCallback is not called again #4');
$262.gc(); await $262.gc();
// Now we are exhausting the iterator, so cleanupSome callback will also not be called. // Now we are exhausting the iterator, so cleanupSome callback will also not be called.
fg.cleanupSome(iterator => { fg.cleanupSome(iterator => {
@ -121,7 +121,7 @@ fn()
assert.sameValue(called, 5, 'cleanupSome callback for the fifth time'); assert.sameValue(called, 5, 'cleanupSome callback for the fifth time');
assert.sameValue(cleanupCallback, 0, 'cleanupCallback is not called again #4'); assert.sameValue(cleanupCallback, 0, 'cleanupCallback is not called again #4');
$262.gc(); await $262.gc();
await Promise.resolve(5); // tick await Promise.resolve(5); // tick
await await Promise.resolve(6); // more ticks await await Promise.resolve(6); // more ticks
@ -132,4 +132,4 @@ fn()
assert.sameValue(called, 5, 'cleanupSome callback is not called anymore, no empty cells'); assert.sameValue(called, 5, 'cleanupSome callback is not called anymore, no empty cells');
assert.sameValue(cleanupCallback, 0, 'cleanupCallback is not called again #5'); assert.sameValue(cleanupCallback, 0, 'cleanupCallback is not called again #5');
}) })
.then($DONE, $DONE); .then($DONE, resolveAsyncGC);

View File

@ -48,6 +48,8 @@ info: |
6. Set iterator.[[FinalizationGroup]] to finalizationGroup. 6. Set iterator.[[FinalizationGroup]] to finalizationGroup.
7. Return iterator. 7. Return iterator.
features: [FinalizationGroup, host-gc-required] features: [FinalizationGroup, host-gc-required]
includes: [async-gc.js]
flags: [async, non-deterministic]
---*/ ---*/
var IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())); var IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));
@ -65,23 +67,32 @@ var fg = new FinalizationGroup(function() {
cleanupCallbackCalled += 1; cleanupCallbackCalled += 1;
}); });
(function() { async function register() {
let o = {}; var target = {};
fg.register(o); fg.register(target);
})(); var prom = asyncGC(target);
assert.sameValue(called, 0); target = null;
assert.sameValue(called, 0); // never called in sync execution
$262.gc(); return prom;
fg.cleanupSome(callback); }
assert.sameValue(called, 1); register()
.then(function() {
// We can't observe if the cleanupCallback was called, but we can make sure cleanupSome won't call it.
cleanupCallbackCalled = 0;
var proto = Object.getPrototypeOf(FinalizationGroupCleanupIteratorPrototype); fg.cleanupSome(callback);
assert.sameValue(
assert.sameValue(called, 1);
var proto = Object.getPrototypeOf(FinalizationGroupCleanupIteratorPrototype);
assert.sameValue(
proto, IteratorPrototype, proto, IteratorPrototype,
'[[Prototype]] internal slot whose value is the intrinsic object %IteratorPrototype%' '[[Prototype]] internal slot whose value is the intrinsic object %IteratorPrototype%'
); );
assert.sameValue(cleanupCallbackCalled, 0, 'if a callback is given, do not call cleanupCallback');
assert.sameValue(cleanupCallbackCalled, 0, 'if a callback is given, do not call cleanupCallback'); })
.then($DONE, resolveAsyncGC);

View File

@ -14,6 +14,8 @@ info: |
5. Perform ? CleanupFinalizationGroup(finalizationGroup, callback). 5. Perform ? CleanupFinalizationGroup(finalizationGroup, callback).
6. Return undefined. 6. Return undefined.
features: [FinalizationGroup, host-gc-required] features: [FinalizationGroup, host-gc-required]
includes: [async-gc.js]
flags: [async, non-deterministic]
---*/ ---*/
var holdingsList; var holdingsList;
@ -22,27 +24,24 @@ function cb(iterator) {
}; };
var fg = new FinalizationGroup(function() {}); var fg = new FinalizationGroup(function() {});
var referenced = {};
function emptyCells() { function emptyCells() {
var x; var target = {};
(function() { fg.register(target, 'target!');
var a = {}; fg.register(referenced, 'referenced');
b = {};
x = {}; var prom = asyncGC(target, referenced);
var y = {}; target = null;
fg.register(x, 'x');
fg.register(a, 'a'); return prom;
fg.register(y, y);
fg.register(b, 'b');
var b;
})();
$262.gc();
x; // This is an intentional reference to x, please leave it here, after the GC
} }
emptyCells(); emptyCells().then(function() {
fg.cleanupSome(cb); fg.cleanupSome(cb);
// The order is per implementation so we can't use compareArray without sorting assert.sameValue(holdingsList.length, 1);
assert.sameValue(holdingsList.length, 2); assert.sameValue(holdingsList[0], 'target!');
assert(holdingsList.includes('a'));
assert(holdingsList.includes('b')); assert.sameValue(typeof referenced, 'object', 'referenced preserved');
}).then($DONE, resolveAsyncGC);

View File

@ -23,66 +23,31 @@ info: |
ii. Set removed to true. ii. Set removed to true.
3. Return removed. 3. Return removed.
features: [FinalizationGroup, host-gc-required] features: [FinalizationGroup, host-gc-required]
includes: [async-gc.js]
flags: [async, non-deterministic]
---*/ ---*/
var holdingsList; var called = 0;
var fg = new FinalizationGroup(function() {}); var fg = new FinalizationGroup(function() {});
var unregA = {};
var unregB = {};
var unregC = {};
var unregDE = {}; // Use the same unregister token for D and E
function emptyCells() { function emptyCells() {
(function() { var target = {};
var a = {}; var token = {};
var b = {}; fg.register(target, 'target!', token);
var c = {};
var d = {};
var e = {};
fg.register(a, 'a', unregA);
fg.register(b, 'b', unregB);
fg.register(c, 'c', unregC);
fg.register(d, 'd', unregDE);
fg.register(e, 'e', unregDE);
})();
var res = fg.unregister(unregC); // unregister 'c' before GC var prom = asyncGC(target);
assert.sameValue(res, true, 'unregister c before GC'); target = null;
$262.gc(); var res = fg.unregister(token);
assert.sameValue(res, true, 'unregister target before iterating over it in cleanup');
return prom;
} }
emptyCells(); emptyCells().then(function() {
fg.cleanupSome(function cb(iterator) {
called += 1;
});
var res = fg.unregister(unregDE); assert.sameValue(called, 0, 'callback was not called');
assert.sameValue(res, true, 'unregister d and e after GC'); }).then($DONE, resolveAsyncGC);
fg.cleanupSome(function cb(iterator) {
var res = fb.unregister(unregA);
assert.sameValue(res, true, 'unregister a before the iterator is consumed.');
holdingsList = [...iterator];
// It's not possible to verify an unregister during the iterator consumption
// as it's .next() does not have any specific order to follow for each item.
});
assert.sameValue(holdingsList[0], 'b');
assert.sameValue(holdingsList.length, 1);
// Second run
res = fg.unregister(unregB); // let's empty the cells
assert.sameValue(res, true, 'unregister B for cleanup');
holdingsList = undefined;
emptyCells();
fg.cleanupSome(function cb(iterator) {
var res = fb.unregister(unregDE);
assert.sameValue(res, true, 'unregister d and e before the iterator is consumed.');
holdingsList = [...iterator];
});
assert(holdingsList.includes('b'));
assert(holdingsList.includes('a'), 'just like the first run, now without removing a');
assert.sameValue(holdingsList.length, 2);

View File

@ -48,6 +48,8 @@ info: |
6. Set iterator.[[FinalizationGroup]] to finalizationGroup. 6. Set iterator.[[FinalizationGroup]] to finalizationGroup.
7. Return iterator. 7. Return iterator.
features: [FinalizationGroup, host-gc-required] features: [FinalizationGroup, host-gc-required]
includes: [async-gc.js]
flags: [async, non-deterministic]
---*/ ---*/
var IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())); var IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));
@ -60,23 +62,25 @@ function callback(iterator) {
FinalizationGroupCleanupIteratorPrototype = Object.getPrototypeOf(iterator); FinalizationGroupCleanupIteratorPrototype = Object.getPrototypeOf(iterator);
} }
var fg = new FinalizationGroup(callback); var fg = new FinalizationGroup(function() {});
(function() { function emptyCells() {
let o = {}; var target = {};
fg.register(o); fg.register(target);
})();
assert.sameValue(called, 0); var prom = asyncGC(target);
target = null;
$262.gc(); return prom;
fg.cleanupSome(); }
assert.sameValue(called, 1); emptyCells().then(function() {
fg.cleanupSome(callback);
assert.sameValue(called, 1);
var proto = Object.getPrototypeOf(FinalizationGroupCleanupIteratorPrototype); var proto = Object.getPrototypeOf(FinalizationGroupCleanupIteratorPrototype);
assert.sameValue( assert.sameValue(
proto, IteratorPrototype, proto, IteratorPrototype,
'[[Prototype]] internal slot whose value is the intrinsic object %IteratorPrototype%' '[[Prototype]] internal slot whose value is the intrinsic object %IteratorPrototype%'
); );
}).then($DONE, resolveAsyncGC);

View File

@ -1,51 +0,0 @@
// Copyright (C) 2019 Leo Balter. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-finalization-group.prototype.cleanupSome
description: WeakRef deref should not prevent a GC event
info: |
FinalizationGroup.prototype.cleanupSome ( [ callback ] )
...
4. If callback is not undefined and IsCallable(callback) is false, throw a TypeError exception.
5. Perform ? CleanupFinalizationGroup(finalizationGroup, callback).
6. Return undefined.
WeakRef.prototype.deref ( )
...
4. Let target be the value of weakRef.[[Target]].
5. If target is not empty,
a. Perform ! KeepDuringJob(target).
b. Return target.
6. Return undefined.
features: [FinalizationGroup, WeakRef, host-gc-required]
---*/
var holdingsList;
function cb(iterator) {
holdingsList = [...iterator];
}
var fg = new FinalizationGroup(function() {});
var deref = false;
function emptyCells() {
var wr;
(function() {
var a = {};
wr = new WeakRef(a);
fg.register(a, 'a');
})();
$262.gc();
deref = wr.deref();
}
emptyCells();
fg.cleanupSome(cb);
assert.sameValue(holdingsList.length, 1);
assert.sameValue(holdingsList[0], 'a');
assert.sameValue(deref, undefined, 'deref handles an empty wearRef.[[Target]] returning undefined');

View File

@ -29,6 +29,8 @@ info: |
c. Return CreateIterResultObject(cell.[[Holdings]], false). c. Return CreateIterResultObject(cell.[[Holdings]], false).
9. Otherwise, return CreateIterResultObject(undefined, true). 9. Otherwise, return CreateIterResultObject(undefined, true).
features: [FinalizationGroup, host-gc-required, Symbol] features: [FinalizationGroup, host-gc-required, Symbol]
includes: [async-gc.js]
flags: [async, non-deterministic]
---*/ ---*/
var called = 0; var called = 0;
@ -67,32 +69,38 @@ function callback(iterator) {
endOfCall += 1; endOfCall += 1;
} }
(function() { function emptyCells() {
var o1 = {}; var o1 = {};
var o2 = {}; var o2 = {};
fg.register(o1, 'holdings 1'); fg.register(o1, 'holdings 1');
fg.register(o2, 'holdings 2'); fg.register(o2, 'holdings 2');
})();
$262.gc(); var prom = asyncGC(o1, o2);
o1 = null;
o2 = null;
fg.cleanupSome(callback); return prom;
}
// Make sure everything is set emptyCells().then(function() {
assert.sameValue(called, 4, 'cleanup successfully'); fg.cleanupSome(callback);
assert.sameValue(endOfCall, 4, 'cleanup ended successfully');
assert.notSameValue(firstIter, secondIter, 'callback is not called with the same iterator #1'); // Make sure everything is set
assert.notSameValue(firstIter, thirdIter, 'callback is not called with the same iterator #2'); assert.sameValue(called, 4, 'cleanup successfully');
assert.notSameValue(secondIter, thirdIter, 'callback is not called with the same iterator #3'); assert.sameValue(endOfCall, 4, 'cleanup ended successfully');
assert.sameValue(first.value, undefined, 'iterator is already consumed'); assert.notSameValue(firstIter, secondIter, 'callback is not called with the same iterator #1');
assert.sameValue(first.done, true, 'first callback will find no empty Targets'); assert.notSameValue(firstIter, thirdIter, 'callback is not called with the same iterator #2');
assert.sameValue(second.done, false, 'second callback will find an empty Target'); assert.notSameValue(secondIter, thirdIter, 'callback is not called with the same iterator #3');
assert.sameValue(third.done, false, 'third callback will find an empty Target');
// 8.a. Choose any such cell. assert.sameValue(first.value, undefined, 'iterator is already consumed');
var holdings = [second.value, third.value]; assert.sameValue(first.done, true, 'first callback will find no empty Targets');
assert.sameValue(second.done, false, 'second callback will find an empty Target');
assert.sameValue(third.done, false, 'third callback will find an empty Target');
assert(holdings.includes('holdings 1'), 'iterators consume emptied cells #1'); // 8.a. Choose any such cell.
assert(holdings.includes('holdings 2'), 'iterators consume emptied cells #2'); var holdings = [second.value, third.value];
assert(holdings.includes('holdings 1'), 'iterators consume emptied cells #1');
assert(holdings.includes('holdings 2'), 'iterators consume emptied cells #2');
}).then($DONE, resolveAsyncGC);

View File

@ -29,88 +29,46 @@ info: |
c. Return CreateIterResultObject(cell.[[Holdings]], false). c. Return CreateIterResultObject(cell.[[Holdings]], false).
9. Otherwise, return CreateIterResultObject(undefined, true). 9. Otherwise, return CreateIterResultObject(undefined, true).
features: [FinalizationGroup, Symbol, host-gc-required] features: [FinalizationGroup, Symbol, host-gc-required]
includes: [compareArray.js] includes: [compareArray.js, async-gc.js]
flags: [async, non-deterministic]
---*/ ---*/
var holdings; function check(value, expectedName) {
var fg = new FinalizationGroup(function() {}); var holdings;
var called = 0;
var fg = new FinalizationGroup(function() {});
function callback(iterator) { function callback(iterator) {
called += 1;
holdings = [...iterator]; holdings = [...iterator];
}
// This is internal to avoid conflicts
function emptyCells(value) {
var target = {};
fg.register(target, value);
var prom = asyncGC(target);
target = null;
return prom;
}
return emptyCells(value).then(function() {
fg.cleanupSome(callback);
assert.sameValue(called, 1, expectedName);
assert.sameValue(holdings.length, 1, expectedName);
assert.sameValue(holdings[0], value, expectedName);
});
} }
(function() { Promise.all([
var o = {}; check(undefined, 'undefined'),
fg.register(o); check(null, 'null'),
fg.register(o, undefined); check('', 'the empty string'),
})(); check({}, 'object'),
check(42, 'number'),
$262.gc(); check(true, 'true'),
fg.cleanupSome(callback); check(false, 'false'),
check(Symbol(1), 'symbol'),
assert.compareArray(holdings, [undefined, undefined], 'undefined'); ]).then(() => $DONE(), resolveAsyncGC);
(function() {
var o = {};
fg.register(o, null);
})();
$262.gc();
fg.cleanupSome(callback);
assert.compareArray(holdings, [null], 'null');
(function() {
var o = {};
fg.register(o, '');
})();
$262.gc();
fg.cleanupSome(callback);
assert.compareArray(holdings, [''], 'the empty string');
var other = {};
(function() {
var o = {};
fg.register(o, other);
})();
$262.gc();
fg.cleanupSome(callback);
assert.compareArray(holdings, [other], '{}');
(function() {
var o = {};
fg.register(o, 42);
})();
$262.gc();
fg.cleanupSome(callback);
assert.compareArray(holdings, [42], '42');
(function() {
var o = {};
fg.register(o, true);
})();
$262.gc();
fg.cleanupSome(callback);
assert.compareArray(holdings, [true], 'true');
(function() {
var o = {};
fg.register(o, false);
})();
$262.gc();
fg.cleanupSome(callback);
assert.compareArray(holdings, [false], 'false');
var s = Symbol();
(function() {
var o = {};
fg.register(o, s);
})();
$262.gc();
fg.cleanupSome(callback);
assert.compareArray(holdings, [s], 'symbol');

View File

@ -22,37 +22,41 @@ info: |
7. Set finalizationGroup.[[IsFinalizationGroupCleanupJobActive]] to false. 7. Set finalizationGroup.[[IsFinalizationGroupCleanupJobActive]] to false.
8. If result is an abrupt completion, return result. 8. If result is an abrupt completion, return result.
features: [FinalizationGroup, arrow-function, async-functions, async-iteration, class, host-gc-required] features: [FinalizationGroup, arrow-function, async-functions, async-iteration, class, host-gc-required]
includes: [async-gc.js]
flags: [async, non-deterministic]
---*/ ---*/
var called = 0; var called = 0;
var iterator; var iterator = false;
function poisoned(iter) { function poisoned(iter) {
called += 1;
iterator = iter; iterator = iter;
iter.next(); // Won't throw iter.next(); // Won't throw
throw new Test262Error(); throw new Test262Error();
} }
var fg = new FinalizationGroup(function() { var fg = new FinalizationGroup(function() {});
called += 1;
});
function emptyCells() { function emptyCells() {
(function() { var target = {};
var o = {}; fg.register(target);
fg.register(o);
})(); var prom = asyncGC(target);
$262.gc(); target = null;
return prom;
} }
emptyCells(); emptyCells().then(function() {
assert.throws(Test262Error, function() {
assert.throws(Test262Error, function() {
fg.cleanupSome(poisoned); fg.cleanupSome(poisoned);
}); });
assert.sameValue(called, 0); assert.sameValue(called, 1);
assert.sameValue(typeof iteraror.next, 'function'); assert.sameValue(typeof iterator, 'object');
assert.throws(TypeError, function() { assert.sameValue(typeof iteraror.next, 'function');
assert.throws(TypeError, function() {
iterator.next(); iterator.next();
}, 'iterator.next throws a TypeError if IsFinalizationGroupCleanupJobActive is false'); }, 'iterator.next throws a TypeError if IsFinalizationGroupCleanupJobActive is false');
}).then($DONE, resolveAsyncGC);

View File

@ -1,52 +0,0 @@
// Copyright (C) 2019 Leo Balter. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-finalization-group.prototype.cleanupSome
description: Return abrupt completion from CleanupFinalizationGroup (using CleanupCallback)
info: |
FinalizationGroup.prototype.cleanupSome ( [ callback ] )
1. Let finalizationGroup be the this value.
2. If Type(finalizationGroup) is not Object, throw a TypeError exception.
3. If finalizationGroup does not have a [[Cells]] internal slot, throw a TypeError exception.
4. If callback is not undefined and IsCallable(callback) is false, throw a TypeError exception.
5. Perform ? CleanupFinalizationGroup(finalizationGroup, callback).
6. Return undefined.
CleanupFinalizationGroup ( finalizationGroup [ , callback ] )
4. If callback is undefined, set callback to finalizationGroup.[[CleanupCallback]].
5. Set finalizationGroup.[[IsFinalizationGroupCleanupJobActive]] to true.
6. Let result be Call(callback, undefined, « iterator »).
7. Set finalizationGroup.[[IsFinalizationGroupCleanupJobActive]] to false.
8. If result is an abrupt completion, return result.
features: [FinalizationGroup, arrow-function, async-functions, async-iteration, class, host-gc-required]
---*/
var iterator;
function poisoned(iter) {
iterator = iter;
iter.next(); // Won't throw
throw new Test262Error();
}
var fg = new FinalizationGroup(poisoned);
function emptyCells() {
(function() {
var o = {};
fg.register(o);
})();
$262.gc();
}
emptyCells();
assert.throws(Test262Error, function() {
fg.cleanupSome();
});
assert.sameValue(typeof iteraror.next, 'function');
assert.throws(TypeError, function() {
iterator.next();
}, 'iterator.next throws a TypeError if IsFinalizationGroupCleanupJobActive is false');

View File

@ -14,6 +14,8 @@ info: |
5. Perform ? CleanupFinalizationGroup(finalizationGroup, callback). 5. Perform ? CleanupFinalizationGroup(finalizationGroup, callback).
6. Return undefined. 6. Return undefined.
features: [FinalizationGroup, arrow-function, async-functions, async-iteration, class, host-gc-required] features: [FinalizationGroup, arrow-function, async-functions, async-iteration, class, host-gc-required]
includes: [async-gc.js]
flags: [async, non-deterministic]
---*/ ---*/
var called; var called;
@ -28,42 +30,55 @@ var cb = function() {
var fg = new FinalizationGroup(fn); var fg = new FinalizationGroup(fn);
function emptyCells() { function emptyCells() {
called = 0; var target = {};
(function() { fg.register(target);
var o = {};
fg.register(o); var prom = asyncGC(target);
})(); target = null;
$262.gc();
return prom;
} }
emptyCells(); var tests = [];
assert.sameValue(fg.cleanupSome(cb), undefined, 'regular callback');
assert.sameValue(called, 1);
emptyCells(); tests.push(emptyCells().then(function() {
assert.sameValue(fg.cleanupSome(fn), undefined, 'regular callback, same FG cleanup function'); called = 0;
assert.sameValue(called, 1); assert.sameValue(fg.cleanupSome(cb), undefined, 'regular callback');
assert.sameValue(called, 1);
}));
emptyCells(); tests.push(emptyCells().then(function() {
assert.sameValue(fg.cleanupSome(() => 1), undefined, 'arrow function'); called = 0;
assert.sameValue(fg.cleanupSome(fn), undefined, 'regular callback, same FG cleanup function');
assert.sameValue(called, 1);
}));
emptyCells(); tests.push(emptyCells().then(function() {
assert.sameValue(fg.cleanupSome(fg.cleanupSome), undefined, 'cleanupSome itself'); called = 0;
assert.sameValue(fg.cleanupSome(), undefined, 'undefined (implicit) callback, defer to FB callback');
assert.sameValue(called, 1);
}));
emptyCells(); tests.push(emptyCells().then(function() {
assert.sameValue(fg.cleanupSome(class {}), undefined, 'class expression'); called = 0;
assert.sameValue(fg.cleanupSome(undefined), undefined, 'undefined (explicit) callback, defer to FB callback');
assert.sameValue(called, 1);
}));
emptyCells(); tests.push(emptyCells().then(function() {
assert.sameValue(fg.cleanupSome(async function() {}), undefined, 'async function'); assert.sameValue(fg.cleanupSome(() => 1), undefined, 'arrow function');
}));
emptyCells(); tests.push(emptyCells().then(function() {
assert.sameValue(fg.cleanupSome(function *() {}), undefined, 'generator'); assert.sameValue(fg.cleanupSome(async function() {}), undefined, 'async function');
}));
emptyCells(); tests.push(emptyCells().then(function() {
assert.sameValue(fg.cleanupSome(async function *() {}), undefined, 'async generator'); assert.sameValue(fg.cleanupSome(function *() {}), undefined, 'generator');
}));
emptyCells(); tests.push(emptyCells().then(function() {
assert.sameValue(fg.cleanupSome(), undefined, 'undefined, implicit'); assert.sameValue(fg.cleanupSome(async function *() {}), undefined, 'async generator');
}));
emptyCells(); Promise.all(tests).then(() => { $DONE(); }, resolveAsyncGC);
assert.sameValue(fg.cleanupSome(undefined), undefined, 'undefined, explicit');

View File

@ -14,20 +14,24 @@ info: |
b. Return target. b. Return target.
6. Return undefined. 6. Return undefined.
features: [WeakRef, host-gc-required] features: [WeakRef, host-gc-required]
includes: [async-gc.js]
flags: [async, non-deterministic]
---*/ ---*/
var deref = false; var deref = false;
function emptyCells() { function emptyCells() {
var wr; var wr;
(function() { var target = {};
var a = {}; wr = new WeakRef(target);
wr = new WeakRef(a);
})(); var prom = asyncGC(target);
$262.gc(); target = null;
deref = wr.deref();
return prom;
} }
emptyCells(); emptyCells().then(function() {
deref = wr.deref();
assert.sameValue(deref, undefined); assert.sameValue(deref, undefined);
}).then($DONE, resolveAsyncGC);