[weakrefs] Update FinalizationRegistry to take a per-item callback

Bug: v8:8179
Change-Id: I0cd43db6558db616690de2dd012bf7518c49345d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2138563
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67069}
This commit is contained in:
Shu-yu Guo 2020-04-07 15:07:13 -07:00 committed by Commit Bot
parent e2e42338a2
commit f902b9dd8d
45 changed files with 149 additions and 457 deletions

View File

@ -976,7 +976,6 @@ namespace internal {
CPP(Trace) \
\
/* Weak refs */ \
CPP(FinalizationRegistryCleanupIteratorNext) \
CPP(FinalizationRegistryCleanupSome) \
CPP(FinalizationRegistryConstructor) \
CPP(FinalizationRegistryRegister) \

View File

@ -159,24 +159,6 @@ BUILTIN(FinalizationRegistryCleanupSome) {
return ReadOnlyRoots(isolate).undefined_value();
}
BUILTIN(FinalizationRegistryCleanupIteratorNext) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSFinalizationRegistryCleanupIterator, iterator, "next");
Handle<JSFinalizationRegistry> finalization_registry(
iterator->finalization_registry(), isolate);
if (!finalization_registry->NeedsCleanup()) {
return *isolate->factory()->NewJSIteratorResult(
handle(ReadOnlyRoots(isolate).undefined_value(), isolate), true);
}
Handle<Object> holdings =
handle(JSFinalizationRegistry::PopClearedCellHoldings(
finalization_registry, isolate),
isolate);
return *isolate->factory()->NewJSIteratorResult(holdings, false);
}
BUILTIN(WeakRefConstructor) {
HandleScope scope(isolate);
Handle<JSFunction> target = args.target();

View File

@ -65,7 +65,6 @@ class JSSegmenter;
class JSV8BreakIterator;
class JSWeakCollection;
class JSFinalizationRegistry;
class JSFinalizationRegistryCleanupIterator;
class JSWeakMap;
class JSWeakRef;
class JSWeakSet;

View File

@ -240,7 +240,6 @@ Type::bitset BitsetType::Lub(const MapRefLike& map) {
case JS_STRING_ITERATOR_TYPE:
case JS_ASYNC_FROM_SYNC_ITERATOR_TYPE:
case JS_FINALIZATION_REGISTRY_TYPE:
case JS_FINALIZATION_REGISTRY_CLEANUP_ITERATOR_TYPE:
case JS_WEAK_MAP_TYPE:
case JS_WEAK_REF_TYPE:
case JS_WEAK_SET_TYPE:

View File

@ -1097,13 +1097,6 @@ void JSFinalizationRegistry::JSFinalizationRegistryVerify(Isolate* isolate) {
next_dirty().IsJSFinalizationRegistry());
}
void JSFinalizationRegistryCleanupIterator::
JSFinalizationRegistryCleanupIteratorVerify(Isolate* isolate) {
CHECK(IsJSFinalizationRegistryCleanupIterator());
JSObjectVerify(isolate);
VerifyHeapPointer(isolate, finalization_registry());
}
void JSWeakMap::JSWeakMapVerify(Isolate* isolate) {
TorqueGeneratedClassVerifiers::JSWeakMapVerify(*this, isolate);
CHECK(table().IsEphemeronHashTable() || table().IsUndefined(isolate));

View File

@ -1187,13 +1187,6 @@ void JSFinalizationRegistry::JSFinalizationRegistryPrint(std::ostream& os) {
JSObjectPrintBody(os, *this);
}
void JSFinalizationRegistryCleanupIterator::
JSFinalizationRegistryCleanupIteratorPrint(std::ostream& os) {
JSObjectPrintHeader(os, *this, "JSFinalizationRegistryCleanupIterator");
os << "\n - finalization_registry: " << Brief(finalization_registry());
JSObjectPrintBody(os, *this);
}
void JSWeakMap::JSWeakMapPrint(std::ostream& os) { // NOLINT
JSObjectPrintHeader(os, *this, "JSWeakMap");
os << "\n - table: " << Brief(table());

View File

@ -65,8 +65,21 @@ void FinalizationRegistryCleanupTask::RunInternal() {
// Exceptions are reported via the message handler. This is ensured by the
// verbose TryCatch.
//
// Cleanup is interrupted if there is an exception. The HTML spec calls for a
// microtask checkpoint after each cleanup task, so the task should return
// after an exception so the host can perform a microtask checkpoint. In case
// of exception, check if the FinalizationRegistry still needs cleanup
// and should be requeued.
//
// TODO(syg): Implement better scheduling for finalizers.
InvokeFinalizationRegistryCleanupFromTask(context, finalization_registry,
callback);
if (finalization_registry->NeedsCleanup() &&
!finalization_registry->scheduled_for_cleanup()) {
auto nop = [](HeapObject, ObjectSlot, Object) {};
heap_->EnqueueDirtyJSFinalizationRegistry(*finalization_registry, nop);
}
// Repost if there are remaining dirty FinalizationRegistries.
heap_->set_is_finalization_registry_cleanup_task_posted(false);

View File

@ -4384,30 +4384,6 @@ void Genesis::InitializeGlobal_harmony_weak_refs() {
JSObject::AddProperty(isolate(), global, weak_ref_name, weak_ref_fun,
DONT_ENUM);
}
{
// Create cleanup iterator for JSFinalizationRegistry.
Handle<JSObject> iterator_prototype(
native_context()->initial_iterator_prototype(), isolate());
Handle<JSObject> cleanup_iterator_prototype = factory->NewJSObject(
isolate()->object_function(), AllocationType::kOld);
JSObject::ForceSetPrototype(cleanup_iterator_prototype, iterator_prototype);
InstallToStringTag(isolate(), cleanup_iterator_prototype,
"FinalizationRegistry Cleanup Iterator");
SimpleInstallFunction(isolate(), cleanup_iterator_prototype, "next",
Builtins::kFinalizationRegistryCleanupIteratorNext, 0,
true);
Handle<Map> cleanup_iterator_map =
factory->NewMap(JS_FINALIZATION_REGISTRY_CLEANUP_ITERATOR_TYPE,
JSFinalizationRegistryCleanupIterator::kHeaderSize);
Map::SetPrototype(isolate(), cleanup_iterator_map,
cleanup_iterator_prototype);
native_context()->set_js_finalization_registry_cleanup_iterator_map(
*cleanup_iterator_map);
}
}
void Genesis::InitializeGlobal_harmony_promise_all_settled() {

View File

@ -191,8 +191,6 @@ enum ContextLookupFlags {
V(JS_SET_FUN_INDEX, JSFunction, js_set_fun) \
V(JS_SET_MAP_INDEX, Map, js_set_map) \
V(WEAK_CELL_MAP_INDEX, Map, weak_cell_map) \
V(JS_FINALIZATION_REGISTRY_CLEANUP_ITERATOR_MAP_INDEX, Map, \
js_finalization_registry_cleanup_iterator_map) \
V(JS_WEAK_MAP_FUN_INDEX, JSFunction, js_weak_map_fun) \
V(JS_WEAK_SET_FUN_INDEX, JSFunction, js_weak_set_fun) \
V(JS_WEAK_REF_FUNCTION_INDEX, JSFunction, js_weak_ref_fun) \

View File

@ -2140,8 +2140,6 @@ int JSObject::GetHeaderSize(InstanceType type,
return JSWeakRef::kHeaderSize;
case JS_FINALIZATION_REGISTRY_TYPE:
return JSFinalizationRegistry::kHeaderSize;
case JS_FINALIZATION_REGISTRY_CLEANUP_ITERATOR_TYPE:
return JSFinalizationRegistryCleanupIterator::kHeaderSize;
case JS_WEAK_MAP_TYPE:
return JSWeakMap::kHeaderSize;
case JS_WEAK_SET_TYPE:

View File

@ -20,7 +20,6 @@ namespace internal {
TQ_OBJECT_CONSTRUCTORS_IMPL(WeakCell)
TQ_OBJECT_CONSTRUCTORS_IMPL(JSWeakRef)
OBJECT_CONSTRUCTORS_IMPL(JSFinalizationRegistry, JSObject)
TQ_OBJECT_CONSTRUCTORS_IMPL(JSFinalizationRegistryCleanupIterator)
ACCESSORS(JSFinalizationRegistry, native_context, NativeContext,
kNativeContextOffset)

View File

@ -66,8 +66,7 @@ class JSFinalizationRegistry : public JSObject {
inline static Object PopClearedCellHoldings(
Handle<JSFinalizationRegistry> finalization_registry, Isolate* isolate);
// Constructs an iterator for the WeakCells in the cleared_cells list and
// calls the user's cleanup function.
// Call the user's cleanup function in a loop, once for each cleared cell.
//
// Returns Nothing<bool> if exception occurs, otherwise returns Just(true).
static V8_WARN_UNUSED_RESULT Maybe<bool> Cleanup(
@ -115,16 +114,6 @@ class JSWeakRef : public TorqueGeneratedJSWeakRef<JSWeakRef, JSObject> {
TQ_OBJECT_CONSTRUCTORS(JSWeakRef)
};
class JSFinalizationRegistryCleanupIterator
: public TorqueGeneratedJSFinalizationRegistryCleanupIterator<
JSFinalizationRegistryCleanupIterator, JSObject> {
public:
DECL_PRINTER(JSFinalizationRegistryCleanupIterator)
DECL_VERIFIER(JSFinalizationRegistryCleanupIterator)
TQ_OBJECT_CONSTRUCTORS(JSFinalizationRegistryCleanupIterator)
};
} // namespace internal
} // namespace v8

View File

@ -18,11 +18,6 @@ extern class JSFinalizationRegistry extends JSObject {
flags: SmiTagged<FinalizationRegistryFlags>;
}
@generateCppClass
extern class JSFinalizationRegistryCleanupIterator extends JSObject {
finalization_registry: JSFinalizationRegistry;
}
@generateCppClass
extern class WeakCell extends HeapObject {
finalization_registry: Undefined|JSFinalizationRegistry;

View File

@ -295,7 +295,6 @@ VisitorId Map::GetVisitorId(Map map) {
case JS_PROMISE_TYPE:
case JS_REG_EXP_TYPE:
case JS_REG_EXP_STRING_ITERATOR_TYPE:
case JS_FINALIZATION_REGISTRY_CLEANUP_ITERATOR_TYPE:
case JS_FINALIZATION_REGISTRY_TYPE:
#ifdef V8_INTL_SUPPORT
case JS_V8_BREAK_ITERATOR_TYPE:

View File

@ -143,7 +143,6 @@ class ZoneForwardList;
V(JSDate) \
V(JSError) \
V(JSFinalizationRegistry) \
V(JSFinalizationRegistryCleanupIterator) \
V(JSFunction) \
V(JSFunctionOrBoundFunction) \
V(JSGeneratorObject) \

View File

@ -979,7 +979,6 @@ ReturnType BodyDescriptorApply(InstanceType type, T1 p1, T2 p2, T3 p3, T4 p4) {
case JS_SPECIAL_API_OBJECT_TYPE:
case JS_MESSAGE_OBJECT_TYPE:
case JS_BOUND_FUNCTION_TYPE:
case JS_FINALIZATION_REGISTRY_CLEANUP_ITERATOR_TYPE:
case JS_FINALIZATION_REGISTRY_TYPE:
#ifdef V8_INTL_SUPPORT
case JS_V8_BREAK_ITERATOR_TYPE:

View File

@ -8316,9 +8316,9 @@ Maybe<bool> JSFinalizationRegistry::Cleanup(
// Attempt to shrink key_map now, as unregister tokens are held weakly and the
// map is not shrinkable when sweeping dead tokens during GC itself.
if (!finalization_registry->key_map().IsUndefined(isolate)) {
Handle<SimpleNumberDictionary> key_map =
handle(SimpleNumberDictionary::cast(finalization_registry->key_map()),
isolate);
Handle<SimpleNumberDictionary> key_map(
SimpleNumberDictionary::cast(finalization_registry->key_map()),
isolate);
key_map = SimpleNumberDictionary::Shrink(isolate, key_map);
finalization_registry->set_key_map(*key_map);
}
@ -8326,29 +8326,16 @@ Maybe<bool> JSFinalizationRegistry::Cleanup(
// It's possible that the cleared_cells list is empty, since
// FinalizationRegistry.unregister() removed all its elements before this task
// ran. In that case, don't call the cleanup function.
if (!finalization_registry->cleared_cells().IsUndefined(isolate)) {
// Construct the iterator.
Handle<JSFinalizationRegistryCleanupIterator> iterator;
{
Handle<Map> cleanup_iterator_map(
isolate->native_context()
->js_finalization_registry_cleanup_iterator_map(),
isolate);
iterator = Handle<JSFinalizationRegistryCleanupIterator>::cast(
isolate->factory()->NewJSObjectFromMap(
cleanup_iterator_map, AllocationType::kYoung,
Handle<AllocationSite>::null()));
iterator->set_finalization_registry(*finalization_registry);
}
Handle<Object> args[] = {iterator};
while (!finalization_registry->cleared_cells().IsUndefined(isolate)) {
Handle<Object> holding(
PopClearedCellHoldings(finalization_registry, isolate), isolate);
Handle<Object> args[] = {holding};
if (Execution::Call(
isolate, cleanup,
handle(ReadOnlyRoots(isolate).undefined_value(), isolate), 1, args)
.is_null()) {
return Nothing<bool>();
}
// TODO(marja): (spec): Should the iterator be invalidated after the
// function returns?
}
return Just(true);
}

View File

@ -8,8 +8,7 @@
// Since cleanup tasks are top-level tasks, errors thrown from them don't stop
// future cleanup tasks from running.
function callback(iter) {
[...iter];
function callback(holdings) {
throw new Error('callback');
};

View File

@ -1,93 +0,0 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking
let cleanup_call_count = 0;
let cleanup = function(iter) {
print("in cleanup");
if (cleanup_call_count == 0) {
// First call: iterate 2 of the 3 holdings
let holdings_list = [];
for (holdings of iter) {
holdings_list.push(holdings);
// Don't iterate the rest of the holdings
if (holdings_list.length == 2) {
break;
}
}
assertEquals(holdings_list.length, 2);
assertTrue(holdings_list[0] < 3);
assertTrue(holdings_list[1] < 3);
// Update call count only after the asserts; this ensures that the test
// fails even if the exceptions inside the cleanup function are swallowed.
cleanup_call_count++;
} else {
// Second call: iterate one leftover holdings and one holdings.
assertEquals(1, cleanup_call_count);
let holdings_list = [];
for (holdings of iter) {
holdings_list.push(holdings);
}
assertEquals(holdings_list.length, 2);
assertTrue((holdings_list[0] < 3 && holdings_list[1] == 100) ||
(holdings_list[1] < 3 && holdings_list[0] == 100));
// Update call count only after the asserts; this ensures that the test
// fails even if the exceptions inside the cleanup function are swallowed.
cleanup_call_count++;
}
}
let fg = new FinalizationRegistry(cleanup);
// Create 3 objects and register them in the FinalizationRegistry. The objects need
// to be inside a closure so that we can reliably kill them!
(function() {
let objects = [];
for (let i = 0; i < 3; ++i) {
objects[i] = {a: i};
fg.register(objects[i], i);
}
gc();
assertEquals(0, cleanup_call_count);
// Drop the references to the objects.
objects = [];
})();
// This GC will reclaim the targets.
gc();
assertEquals(0, cleanup_call_count);
let timeout_func_1 = function() {
assertEquals(1, cleanup_call_count);
// Assert that the cleanup function won't be called unless new targets appear.
setTimeout(timeout_func_2, 0);
}
setTimeout(timeout_func_1, 0);
let timeout_func_2 = function() {
assertEquals(1, cleanup_call_count);
// Create a new object and register it.
(function() {
let obj = {};
let wc = fg.register(obj, 100);
obj = null;
})();
// This GC will reclaim the targets.
gc();
assertEquals(1, cleanup_call_count);
setTimeout(timeout_func_3, 0);
}
let timeout_func_3 = function() {
assertEquals(2, cleanup_call_count);
}

View File

@ -19,9 +19,8 @@ function scheduleMicrotask(func) {
let log = [];
let cleanup = (iter) => {
let cleanup = (holdings) => {
cleanedUp = true;
for (holdings of iter) { }
}
let fg = new FinalizationRegistry(cleanup);

View File

@ -4,21 +4,11 @@
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking
let cleanup_called = false;
let cleanup = function(iter) {
assertFalse(cleanup_called);
let holdings_list = [];
for (holdings of iter) {
holdings_list.push(holdings);
}
assertEquals(holdings_list.length, 2);
if (holdings_list[0] == 1) {
assertEquals(holdings_list[1], 2);
} else {
assertEquals(holdings_list[0], 2);
assertEquals(holdings_list[1], 1);
}
cleanup_called = true;
let cleanup_called = 0;
let holdings_list = [];
let cleanup = function(holdings) {
holdings_list.push(holdings);
cleanup_called++;
}
let fg = new FinalizationRegistry(cleanup);
@ -34,7 +24,7 @@ let o2 = {};
})();
gc();
assertFalse(cleanup_called);
assertEquals(cleanup_called, 0);
// Drop the last references to o1 and o2.
(function() {
@ -45,10 +35,17 @@ assertFalse(cleanup_called);
// GC will reclaim the target objects; the cleanup function will be called the
// next time we enter the event loop.
gc();
assertFalse(cleanup_called);
assertEquals(cleanup_called, 0);
let timeout_func = function() {
assertTrue(cleanup_called);
assertEquals(cleanup_called, 2);
assertEquals(holdings_list.length, 2);
if (holdings_list[0] == 1) {
assertEquals(holdings_list[1], 2);
} else {
assertEquals(holdings_list[0], 2);
assertEquals(holdings_list[1], 1);
}
}
setTimeout(timeout_func, 0);

View File

@ -6,10 +6,8 @@
let cleanup_count = 0;
let cleanup_holdings = [];
let cleanup = function(iter) {
for (holdings of iter) {
cleanup_holdings.push(holdings);
}
let cleanup = function(holdings) {
cleanup_holdings.push(holdings);
++cleanup_count;
}

View File

@ -6,10 +6,8 @@
let cleanup_count = 0;
let cleanup_holdings = [];
let cleanup = function(iter) {
for (holdings of iter) {
cleanup_holdings.push(holdings);
}
let cleanup = function(holdings) {
cleanup_holdings.push(holdings);
++cleanup_count;
}

View File

@ -6,14 +6,12 @@
let cleanup_count = 0;
let cleanup_holdings = [];
let cleanup = function(iter) {
let cleanup = function(holdings) {
%AbortJS("shouldn't be called");
}
let cleanup2 = function(iter) {
for (holdings of iter) {
cleanup_holdings.push(holdings);
}
let cleanup2 = function(holdings) {
cleanup_holdings.push(holdings);
++cleanup_count;
}

View File

@ -5,12 +5,10 @@
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking
let cleanup_called = false;
let cleanup = function(iter) {
let cleanup = function(holdings) {
assertFalse(cleanup_called);
let holdings_list = [];
for (holdings of iter) {
holdings_list.push(holdings);
}
holdings_list.push(holdings);
assertEquals(1, holdings_list.length);
assertEquals("holdings", holdings_list[0]);
cleanup_called = true;

View File

@ -5,8 +5,7 @@
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking
let cleanup_called = false;
function cleanup(iter) {
[...iter];
function cleanup(holdings) {
cleanup_called = true;
};
(function() {

View File

@ -5,14 +5,10 @@
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking
let cleanup_called = false;
let cleanup = function(iter) {
let holdings_list = [];
let cleanup = function(holdings) {
assertFalse(cleanup_called);
let holdings_list = [];
for (holdings of iter) {
holdings_list.push(holdings);
}
assertEquals(holdings_list.length, 1);
assertEquals(holdings_list[0].a, "this is the holdings object");
holdings_list.push(holdings);
cleanup_called = true;
}
@ -40,6 +36,8 @@ assertFalse(cleanup_called);
let timeout_func = function() {
assertTrue(cleanup_called);
assertEquals(holdings_list.length, 1);
assertEquals(holdings_list[0].a, "this is the holdings object");
}
setTimeout(timeout_func, 0);

View File

@ -11,17 +11,13 @@ let cleanup0_holdings_count = 0;
let cleanup1_call_count = 0;
let cleanup1_holdings_count = 0;
let cleanup0 = function(iter) {
for (holdings of iter) {
++cleanup0_holdings_count;
}
let cleanup0 = function(holdings) {
++cleanup0_holdings_count;
++cleanup0_call_count;
}
let cleanup1 = function(iter) {
for (holdings of iter) {
++cleanup1_holdings_count;
}
let cleanup1 = function(holdings) {
++cleanup1_holdings_count;
++cleanup1_call_count;
}
@ -61,12 +57,11 @@ gc();
gc();
let timeout_func = function() {
assertEquals(1, cleanup0_call_count);
assertEquals(2, cleanup0_call_count);
assertEquals(2, cleanup0_holdings_count);
assertEquals(1, cleanup1_call_count);
assertEquals(2, cleanup1_call_count);
assertEquals(2, cleanup1_holdings_count);
}
// Give the cleanup task a chance to run. All holdings will be iterated during
// the same invocation of the cleanup function.
// Give the cleanup task a chance to run.
setTimeout(timeout_func, 0);

View File

@ -6,10 +6,8 @@
let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup = function(iter) {
for (holdings of iter) {
++cleanup_holdings_count;
}
let cleanup = function(holdings) {
++cleanup_holdings_count;
++cleanup_call_count;
}
@ -33,7 +31,7 @@ let fg2 = new FinalizationRegistry(cleanup);
gc();
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called and iterated the holdings.
// Assert that the cleanup function was called.
let timeout_func = function() {
assertEquals(2, cleanup_call_count);
assertEquals(2, cleanup_holdings_count);

View File

@ -5,9 +5,7 @@
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking
let call_count = 0;
let reentrant_gc =
function(iter) {
[...iter];
let reentrant_gc = function(holdings) {
gc();
call_count++;
}

View File

@ -6,11 +6,9 @@
let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup = function(iter) {
for (holdings of iter) {
assertEquals(holdings, undefined);
++cleanup_holdings_count;
}
let cleanup = function(holdings) {
assertEquals(holdings, undefined);
++cleanup_holdings_count;
++cleanup_call_count;
}
@ -30,7 +28,7 @@ let fg = new FinalizationRegistry(cleanup);
gc();
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called and iterated the holdings.
// Assert that the cleanup function was called.
let timeout_func = function() {
assertEquals(1, cleanup_call_count);
assertEquals(1, cleanup_holdings_count);

View File

@ -6,11 +6,9 @@
let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup = function(iter) {
for (holdings of iter) {
assertEquals("holdings", holdings);
++cleanup_holdings_count;
}
let cleanup = function(holdings) {
assertEquals("holdings", holdings);
++cleanup_holdings_count;
++cleanup_call_count;
}
@ -30,12 +28,12 @@ let key = {"k": "this is the key"};
gc();
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called and iterated the holdings.
// Assert that the cleanup function was called.
let timeout_func = function() {
assertEquals(1, cleanup_call_count);
assertEquals(1, cleanup_holdings_count);
// Unregister an already iterated over weak reference.
// Unregister an already cleaned-up weak reference.
let success = fg.unregister(key);
assertFalse(success);

View File

@ -5,7 +5,7 @@
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking --noincremental-marking
let cleanup_call_count = 0;
let cleanup = function(iter) {
let cleanup = function(holdings) {
++cleanup_call_count;
}

View File

@ -5,7 +5,7 @@
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking
let cleanup_call_count = 0;
let cleanup = function(iter) {
let cleanup = function(holdings) {
++cleanup_call_count;
}

View File

@ -6,18 +6,12 @@
let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup = function(iter) {
for (holdings of iter) {
assertEquals(holdings, "holdings");
let cleanup = function(holdings) {
assertEquals(holdings, "holdings");
let success = fg.unregister(key);
assertFalse(success);
// There's one more object with the same key that we haven't
// iterated over yet so we should be able to unregister the
// callback for that one.
let success = fg.unregister(key);
assertTrue(success);
++cleanup_holdings_count;
}
++cleanup_holdings_count;
++cleanup_call_count;
}
@ -28,9 +22,7 @@ let key = {"k": "this is the key"};
(function() {
let object = {};
let object2 = {};
fg.register(object, "holdings", key);
fg.register(object2, "holdings", key);
// object goes out of scope.
})();
@ -39,7 +31,7 @@ let key = {"k": "this is the key"};
gc();
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called and iterated the WeakCell.
// Assert that the cleanup function was called.
let timeout_func = function() {
assertEquals(1, cleanup_call_count);
assertEquals(1, cleanup_holdings_count);

View File

@ -1,42 +0,0 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking
let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup = function(iter) {
// Unregister before we've iterated through the holdings.
let success = fg.unregister(key);
assertTrue(success);
for (wc of iter) {
++cleanup_holdings_count;
}
++cleanup_call_count;
}
let fg = new FinalizationRegistry(cleanup);
let key = {"k": "the key"};
// Create an object and register it in the FinalizationRegistry. The object needs
// to be inside a closure so that we can reliably kill them!
(function() {
let object = {};
fg.register(object, "holdings", key);
// object goes out of scope.
})();
// This GC will discover unretained targets and schedule cleanup.
gc();
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called, but didn't iterate any holdings.
let timeout_func = function() {
assertEquals(1, cleanup_call_count);
assertEquals(0, cleanup_holdings_count);
}
setTimeout(timeout_func, 0);

View File

@ -6,34 +6,40 @@
let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup = function(iter) {
for (holdings of iter) {
assertEquals(holdings, "holdings");
let success = fg.unregister(key);
assertFalse(success);
++cleanup_holdings_count;
let cleanup = function(holdings) {
// See which target we're cleaning up and unregister the other one.
if (holdings == 1) {
let success = fg.unregister(key2);
assertTrue(success);
} else {
assertSame(holdings, 2);
let success = fg.unregister(key1);
assertTrue(success);
}
++cleanup_holdings_count;
++cleanup_call_count;
}
let fg = new FinalizationRegistry(cleanup);
// Create an object and register it in the FinalizationRegistry. The object needs to be inside
// a closure so that we can reliably kill them!
let key = {"k": "this is the key"};
let key1 = {"k": "first key"};
let key2 = {"k": "second key"};
// Create two objects and register them in the FinalizationRegistry. The objects
// need to be inside a closure so that we can reliably kill them!
(function() {
let object = {};
fg.register(object, "holdings", key);
let object1 = {};
fg.register(object1, 1, key1);
let object2 = {};
fg.register(object2, 2, key2);
// object goes out of scope.
// object1 and object2 go out of scope.
})();
// This GC will discover dirty WeakCells and schedule cleanup.
// This GC will reclaim target objects and schedule cleanup.
gc();
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called and iterated the WeakCell.
// Assert that the cleanup function was called and cleaned up one holdings (but not the other one).
let timeout_func = function() {
assertEquals(1, cleanup_call_count);
assertEquals(1, cleanup_holdings_count);

View File

@ -6,35 +6,39 @@
let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup = function(iter) {
for (holdings of iter) {
assertEquals(holdings, "holdings");
++cleanup_holdings_count;
}
// Unregister an already iterated over weak reference.
let cleanup = function(holdings) {
assertEquals(holdings, "holdings");
// There's one more object with the same key that we haven't
// cleaned up yet so we should be able to unregister the
// callback for that one.
let success = fg.unregister(key);
assertFalse(success);
assertTrue(success);
++cleanup_holdings_count;
++cleanup_call_count;
}
let fg = new FinalizationRegistry(cleanup);
let key = {"k": "this is the key"};
// Create an object and register it in the FinalizationRegistry. The object needs to be inside
// a closure so that we can reliably kill them!
let key = {"k": "this is the key"};
(function() {
let object = {};
let object2 = {};
fg.register(object, "holdings", key);
fg.register(object2, "holdings", key);
// object goes out of scope.
})();
// This GC will reclaim the target object and schedule cleanup.
// This GC will discover dirty WeakCells and schedule cleanup.
gc();
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called and iterated the holdings.
// Assert that the cleanup function was called.
let timeout_func = function() {
assertEquals(1, cleanup_call_count);
assertEquals(1, cleanup_holdings_count);

View File

@ -1,50 +0,0 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking
let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup = function(iter) {
for (holdings of iter) {
// See which target we're iterating over and unregister the other one.
if (holdings == 1) {
let success = fg.unregister(key2);
assertTrue(success);
} else {
assertSame(holdings, 2);
let success = fg.unregister(key1);
assertTrue(success);
}
++cleanup_holdings_count;
}
++cleanup_call_count;
}
let fg = new FinalizationRegistry(cleanup);
let key1 = {"k": "first key"};
let key2 = {"k": "second key"};
// Create two objects and register them in the FinalizationRegistry. The objects
// need to be inside a closure so that we can reliably kill them!
(function() {
let object1 = {};
fg.register(object1, 1, key1);
let object2 = {};
fg.register(object2, 2, key2);
// object1 and object2 go out of scope.
})();
// This GC will reclaim target objects and schedule cleanup.
gc();
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called and iterated one holdings (but not the other one).
let timeout_func = function() {
assertEquals(1, cleanup_call_count);
assertEquals(1, cleanup_holdings_count);
}
setTimeout(timeout_func, 0);

View File

@ -6,11 +6,9 @@
let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup = function(iter) {
for (holdings of iter) {
assertEquals("holdings2", holdings);
++cleanup_holdings_count;
}
let cleanup = function(holdings) {
assertEquals("holdings2", holdings);
++cleanup_holdings_count;
++cleanup_call_count;
}

View File

@ -5,7 +5,7 @@
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking
let cleanup_call_count = 0;
let cleanup = function(iter) {
let cleanup = function(holdings) {
++cleanup_call_count;
}

View File

@ -5,13 +5,9 @@
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking
let cleanup_called = false;
let cleanup = function(iter) {
let cleanup = function(holdings_arg) {
assertFalse(cleanup_called);
let result = iter.next();
assertEquals(result.value, holdings);
assertFalse(result.done);
result = iter.next();
assertTrue(result.done);
assertEquals(holdings_arg, holdings);
cleanup_called = true;
}

View File

@ -4,17 +4,17 @@
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking
var FG = new FinalizationRegistry (function (iter) { globalThis.FRRan = true; });
var FR = new FinalizationRegistry (function (holdings) { globalThis.FRRan = true; });
{
let obj = {};
// obj is its own unregister token and becomes unreachable after this
// block. If the unregister token is held strongly this test will not
// terminate.
FG.register(obj, 42, obj);
FR.register(obj, 42, obj);
}
function tryAgain() {
gc();
if (globalThis.FRRan || FG.cleanupSome()) {
if (globalThis.FRRan || FR.cleanupSome()) {
return;
}
setTimeout(tryAgain, 0);

View File

@ -599,12 +599,6 @@
# https://bugs.chromium.org/p/v8/issues/detail?id=9049
'language/comments/hashbang/use-strict': [SKIP],
# https://bugs.chromium.org/p/v8/issues/detail?id=8179
'built-ins/FinalizationRegistry/gc-has-one-chance-to-call-cleanupCallback': [FAIL],
'built-ins/FinalizationRegistry/prototype/cleanupSome/cleanup-prevented-with-reference': [FAIL],
'built-ins/FinalizationRegistry/prototype/cleanupSome/holdings-multiple-values': [FAIL],
'built-ins/FinalizationRegistry/prototype/unregister/unregister-cleaned-up-cell': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=10313
'built-ins/Date/parse/without-utc-offset': [SKIP],

View File

@ -172,29 +172,28 @@ INSTANCE_TYPES = {
1067: "JS_DISPLAY_NAMES_TYPE",
1068: "JS_ERROR_TYPE",
1069: "JS_FINALIZATION_REGISTRY_TYPE",
1070: "JS_FINALIZATION_REGISTRY_CLEANUP_ITERATOR_TYPE",
1071: "JS_LIST_FORMAT_TYPE",
1072: "JS_LOCALE_TYPE",
1073: "JS_MESSAGE_OBJECT_TYPE",
1074: "JS_NUMBER_FORMAT_TYPE",
1075: "JS_PLURAL_RULES_TYPE",
1076: "JS_PROMISE_TYPE",
1077: "JS_REG_EXP_TYPE",
1078: "JS_REG_EXP_STRING_ITERATOR_TYPE",
1079: "JS_RELATIVE_TIME_FORMAT_TYPE",
1080: "JS_SEGMENT_ITERATOR_TYPE",
1081: "JS_SEGMENTER_TYPE",
1082: "JS_STRING_ITERATOR_TYPE",
1083: "JS_V8_BREAK_ITERATOR_TYPE",
1084: "JS_WEAK_REF_TYPE",
1085: "WASM_EXCEPTION_OBJECT_TYPE",
1086: "WASM_GLOBAL_OBJECT_TYPE",
1087: "WASM_INSTANCE_OBJECT_TYPE",
1088: "WASM_MEMORY_OBJECT_TYPE",
1089: "WASM_MODULE_OBJECT_TYPE",
1090: "WASM_TABLE_OBJECT_TYPE",
1091: "JS_BOUND_FUNCTION_TYPE",
1092: "JS_FUNCTION_TYPE",
1070: "JS_LIST_FORMAT_TYPE",
1071: "JS_LOCALE_TYPE",
1072: "JS_MESSAGE_OBJECT_TYPE",
1073: "JS_NUMBER_FORMAT_TYPE",
1074: "JS_PLURAL_RULES_TYPE",
1075: "JS_PROMISE_TYPE",
1076: "JS_REG_EXP_TYPE",
1077: "JS_REG_EXP_STRING_ITERATOR_TYPE",
1078: "JS_RELATIVE_TIME_FORMAT_TYPE",
1079: "JS_SEGMENT_ITERATOR_TYPE",
1080: "JS_SEGMENTER_TYPE",
1081: "JS_STRING_ITERATOR_TYPE",
1082: "JS_V8_BREAK_ITERATOR_TYPE",
1083: "JS_WEAK_REF_TYPE",
1084: "WASM_EXCEPTION_OBJECT_TYPE",
1085: "WASM_GLOBAL_OBJECT_TYPE",
1086: "WASM_INSTANCE_OBJECT_TYPE",
1087: "WASM_MEMORY_OBJECT_TYPE",
1088: "WASM_MODULE_OBJECT_TYPE",
1089: "WASM_TABLE_OBJECT_TYPE",
1090: "JS_BOUND_FUNCTION_TYPE",
1091: "JS_FUNCTION_TYPE",
}
# List of known V8 maps.
@ -346,7 +345,7 @@ KNOWN_MAPS = {
("read_only_space", 0x03b2d): (77, "StoreHandler2Map"),
("read_only_space", 0x03b55): (77, "StoreHandler3Map"),
("map_space", 0x00121): (1057, "ExternalMap"),
("map_space", 0x00149): (1073, "JSMessageObjectMap"),
("map_space", 0x00149): (1072, "JSMessageObjectMap"),
}
# List of known V8 objects.