[weakrefs] Port FinalizationRegistry cleanup loop to Torque

To avoid shrinking the unregister token map on each pop of the cleared
cell list, the Torque implementation of the cleanup loop avoids
shrinking the map until the end of the loop.

To support that, PopClearedCellHoldings is refactored to the Torque
PopClearedCell which calls the
JSFinalization::RemoveCellFromUnregisterTokenMap and the runtime
ShrinkFinalizationRegistryUnregisterTokenMap. The former cannot GC is
and is implemented in CSA as a fast C call. The latter can GC and is a
runtime call.

This also incidentally makes uses of FinalizationRegistry without
unregister token a fast path that doesn't have to leave Torque.

Bug: v8:8179
Change-Id: Ia0c3c5800d26e31319a818f164f6bd3267355aa6
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2137950
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: Marja Hölttä <marja@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67161}
This commit is contained in:
Shu-yu Guo 2020-04-15 19:44:07 -07:00 committed by Commit Bot
parent 4821ca2cd1
commit dbbacccaa3
19 changed files with 280 additions and 153 deletions

View File

@ -1036,6 +1036,7 @@ torque_files = [
"src/builtins/convert.tq",
"src/builtins/console.tq",
"src/builtins/data-view.tq",
"src/builtins/finalization-registry.tq",
"src/builtins/frames.tq",
"src/builtins/frame-arguments.tq",
"src/builtins/growable-fixed-array.tq",
@ -2915,6 +2916,7 @@ v8_source_set("v8_base_without_compiler") {
"src/runtime/runtime-typedarray.cc",
"src/runtime/runtime-utils.h",
"src/runtime/runtime-wasm.cc",
"src/runtime/runtime-weak-refs.cc",
"src/runtime/runtime.cc",
"src/runtime/runtime.h",
"src/sanitizer/asan.h",

View File

@ -8429,9 +8429,13 @@ Maybe<bool> FinalizationGroup::Cleanup(
ENTER_V8(isolate, context, FinalizationGroup, Cleanup, Nothing<bool>(),
i::HandleScope);
i::Handle<i::Object> callback(fr->cleanup(), isolate);
i::Handle<i::Object> argv[] = {callback};
fr->set_scheduled_for_cleanup(false);
has_pending_exception =
i::JSFinalizationRegistry::Cleanup(isolate, fr, callback).IsNothing();
i::Execution::CallBuiltin(isolate,
isolate->finalization_registry_cleanup_some(),
fr, arraysize(argv), argv)
.is_null();
RETURN_ON_FAILED_EXECUTION_PRIMITIVE(bool);
return Just(true);
}
@ -11245,8 +11249,11 @@ void InvokeFinalizationRegistryCleanupFromTask(
Local<v8::Context> api_context = Utils::ToLocal(context);
CallDepthScope<true> call_depth_scope(isolate, api_context);
VMState<OTHER> state(isolate);
if (JSFinalizationRegistry::Cleanup(isolate, finalization_registry, callback)
.IsNothing()) {
Handle<Object> argv[] = {callback};
if (Execution::CallBuiltin(isolate,
isolate->finalization_registry_cleanup_some(),
finalization_registry, arraysize(argv), argv)
.is_null()) {
call_depth_scope.Escape();
}
}

View File

@ -303,6 +303,7 @@ extern enum MessageTemplate {
kProxyGetPrototypeOfNonExtensible,
kProxySetPrototypeOfNonExtensible,
kProxyDeletePropertyNonExtensible,
kWeakRefsCleanupMustBeCallable,
...
}
@ -701,7 +702,8 @@ macro Float64IsNaN(n: float64): bool {
}
// The type of all tagged values that can safely be compared with TaggedEqual.
type TaggedWithIdentity = JSReceiver|FixedArrayBase|Oddball|Map|EmptyString;
type TaggedWithIdentity =
JSReceiver|FixedArrayBase|Oddball|Map|WeakCell|EmptyString;
extern operator '==' macro TaggedEqual(TaggedWithIdentity, Object): bool;
extern operator '==' macro TaggedEqual(Object, TaggedWithIdentity): bool;

View File

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

View File

@ -122,43 +122,6 @@ BUILTIN(FinalizationRegistryUnregister) {
return *isolate->factory()->ToBoolean(success);
}
BUILTIN(FinalizationRegistryCleanupSome) {
HandleScope scope(isolate);
const char* method_name = "FinalizationRegistry.prototype.cleanupSome";
// 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.
CHECK_RECEIVER(JSFinalizationRegistry, finalization_registry, method_name);
Handle<Object> callback(finalization_registry->cleanup(), isolate);
Handle<Object> callback_obj = args.atOrUndefined(isolate, 1);
// 4. If callback is not undefined and IsCallable(callback) is
// false, throw a TypeError exception.
if (!callback_obj->IsUndefined(isolate)) {
if (!callback_obj->IsCallable()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewTypeError(MessageTemplate::kWeakRefsCleanupMustBeCallable));
}
callback = callback_obj;
}
// Don't do set_scheduled_for_cleanup(false); we still have the task
// scheduled.
if (JSFinalizationRegistry::Cleanup(isolate, finalization_registry, callback)
.IsNothing()) {
DCHECK(isolate->has_pending_exception());
return ReadOnlyRoots(isolate).exception();
}
return ReadOnlyRoots(isolate).undefined_value();
}
BUILTIN(WeakRefConstructor) {
HandleScope scope(isolate);
Handle<JSFunction> target = args.target();

View File

@ -0,0 +1,106 @@
// Copyright 2020 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.
namespace runtime {
extern runtime
ShrinkFinalizationRegistryUnregisterTokenMap(Context, JSFinalizationRegistry):
void;
}
namespace weakref {
extern transitioning macro
RemoveFinalizationRegistryCellFromUnregisterTokenMap(
JSFinalizationRegistry, WeakCell): void;
macro SplitOffTail(weakCell: WeakCell): WeakCell|Undefined {
const weakCellTail = weakCell.next;
weakCell.next = Undefined;
typeswitch (weakCellTail) {
case (Undefined): {
}
case (tailIsNowAHead: WeakCell): {
assert(tailIsNowAHead.prev == weakCell);
tailIsNowAHead.prev = Undefined;
}
}
return weakCellTail;
}
transitioning macro
PopClearedCell(finalizationRegistry: JSFinalizationRegistry): WeakCell
|Undefined {
typeswitch (finalizationRegistry.cleared_cells) {
case (Undefined): {
return Undefined;
}
case (weakCell: WeakCell): {
assert(weakCell.prev == Undefined);
finalizationRegistry.cleared_cells = SplitOffTail(weakCell);
// If the WeakCell has an unregister token, remove the cell from the
// unregister token linked lists and and the unregister token from
// key_map. This doesn't shrink key_map, which is done manually after
// the cleanup loop to avoid a runtime call.
if (weakCell.unregister_token != Undefined) {
RemoveFinalizationRegistryCellFromUnregisterTokenMap(
finalizationRegistry, weakCell);
}
return weakCell;
}
}
}
transitioning macro
FinalizationRegistryCleanupLoop(implicit context: Context)(
finalizationRegistry: JSFinalizationRegistry, callback: Callable) {
while (true) {
const weakCellHead = PopClearedCell(finalizationRegistry);
typeswitch (weakCellHead) {
case (Undefined): {
break;
}
case (weakCell: WeakCell): {
try {
Call(context, callback, Undefined, weakCell.holdings);
} catch (e) {
runtime::ShrinkFinalizationRegistryUnregisterTokenMap(
context, finalizationRegistry);
ReThrow(context, e);
}
}
}
}
runtime::ShrinkFinalizationRegistryUnregisterTokenMap(
context, finalizationRegistry);
}
transitioning javascript builtin
FinalizationRegistryPrototypeCleanupSome(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSAny {
// 1. Let finalizationRegistry be the this value.
//
// 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]).
const methodName: constexpr string =
'FinalizationRegistry.prototype.cleanupSome';
const finalizationRegistry =
Cast<JSFinalizationRegistry>(receiver) otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, methodName, receiver);
let callback: Callable;
if (arguments[0] != Undefined) {
// 4. If callback is not undefined and IsCallable(callback) is
// false, throw a TypeError exception.
callback = Cast<Callable>(arguments[0]) otherwise ThrowTypeError(
MessageTemplate::kWeakRefsCleanupMustBeCallable, arguments[0]);
} else {
callback = finalizationRegistry.cleanup;
}
FinalizationRegistryCleanupLoop(finalizationRegistry, callback);
return Undefined;
}
}

View File

@ -13250,6 +13250,21 @@ TNode<String> CodeStubAssembler::TaggedToDirectString(TNode<Object> value,
return CAST(value);
}
void CodeStubAssembler::RemoveFinalizationRegistryCellFromUnregisterTokenMap(
TNode<JSFinalizationRegistry> finalization_registry,
TNode<WeakCell> weak_cell) {
const TNode<ExternalReference> remove_cell = ExternalConstant(
ExternalReference::
js_finalization_registry_remove_cell_from_unregister_token_map());
const TNode<ExternalReference> isolate_ptr =
ExternalConstant(ExternalReference::isolate_address(isolate()));
CallCFunction(remove_cell, MachineType::Pointer(),
std::make_pair(MachineType::Pointer(), isolate_ptr),
std::make_pair(MachineType::AnyTagged(), finalization_registry),
std::make_pair(MachineType::AnyTagged(), weak_cell));
}
PrototypeCheckAssembler::PrototypeCheckAssembler(
compiler::CodeAssemblerState* state, Flags flags,
TNode<NativeContext> native_context, TNode<Map> initial_prototype_map,

View File

@ -3842,6 +3842,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<Smi> RefillMathRandom(TNode<NativeContext> native_context);
void RemoveFinalizationRegistryCellFromUnregisterTokenMap(
TNode<JSFinalizationRegistry> finalization_registry,
TNode<WeakCell> weak_cell);
private:
friend class CodeStubArguments;

View File

@ -902,6 +902,10 @@ static int EnterMicrotaskContextWrapper(HandleScopeImplementer* hsi,
FUNCTION_REFERENCE(call_enter_context_function, EnterMicrotaskContextWrapper)
FUNCTION_REFERENCE(
js_finalization_registry_remove_cell_from_unregister_token_map,
JSFinalizationRegistry::RemoveCellFromUnregisterTokenMap)
bool operator==(ExternalReference lhs, ExternalReference rhs) {
return lhs.address() == rhs.address();
}

View File

@ -215,6 +215,8 @@ class StatsCounter;
V(atomic_pair_exchange_function, "atomic_pair_exchange_function") \
V(atomic_pair_compare_exchange_function, \
"atomic_pair_compare_exchange_function") \
V(js_finalization_registry_remove_cell_from_unregister_token_map, \
"JSFinalizationRegistry::RemoveCellFromUnregisterTokenMap") \
EXTERNAL_REFERENCE_LIST_INTL(V)
#ifdef V8_INTL_SUPPORT

View File

@ -4338,6 +4338,15 @@ void Genesis::InitializeGlobal_harmony_weak_refs() {
SimpleInstallFunction(isolate(), finalization_registry_prototype,
"unregister",
Builtins::kFinalizationRegistryUnregister, 1, false);
// The cleanupSome function is created but not exposed, as it is used
// internally by InvokeFinalizationRegistryCleanupFromTask.
//
// It is exposed by FLAG_harmony_weak_refs_with_cleanup_some.
Handle<JSFunction> cleanup_some_fun = SimpleCreateFunction(
isolate(), factory->InternalizeUtf8String("cleanupSome"),
Builtins::kFinalizationRegistryPrototypeCleanupSome, 0, false);
native_context()->set_finalization_registry_cleanup_some(*cleanup_some_fun);
}
{
// Create %WeakRefPrototype%
@ -4386,9 +4395,10 @@ void Genesis::InitializeGlobal_harmony_weak_refs_with_cleanup_some() {
JSObject::cast(finalization_registry_fun->instance_prototype()),
isolate());
SimpleInstallFunction(isolate(), finalization_registry_prototype,
"cleanupSome",
Builtins::kFinalizationRegistryCleanupSome, 0, false);
JSObject::AddProperty(isolate(), finalization_registry_prototype,
factory()->InternalizeUtf8String("cleanupSome"),
isolate()->finalization_registry_cleanup_some(),
DONT_ENUM);
}
void Genesis::InitializeGlobal_harmony_promise_all_settled() {

View File

@ -346,6 +346,8 @@ enum ContextLookupFlags {
V(MAP_GET_INDEX, JSFunction, map_get) \
V(MAP_HAS_INDEX, JSFunction, map_has) \
V(MAP_SET_INDEX, JSFunction, map_set) \
V(FINALIZATION_REGISTRY_CLEANUP_SOME, JSFunction, \
finalization_registry_cleanup_some) \
V(FUNCTION_HAS_INSTANCE_INDEX, JSFunction, function_has_instance) \
V(OBJECT_TO_STRING, JSFunction, object_to_string) \
V(OBJECT_VALUE_OF_FUNCTION_INDEX, JSFunction, object_value_of_function) \

View File

@ -177,65 +177,6 @@ bool JSFinalizationRegistry::NeedsCleanup() const {
return cleared_cells().IsWeakCell();
}
Object JSFinalizationRegistry::PopClearedCellHoldings(
Handle<JSFinalizationRegistry> finalization_registry, Isolate* isolate) {
Handle<WeakCell> weak_cell =
handle(WeakCell::cast(finalization_registry->cleared_cells()), isolate);
DCHECK(weak_cell->prev().IsUndefined(isolate));
finalization_registry->set_cleared_cells(weak_cell->next());
weak_cell->set_next(ReadOnlyRoots(isolate).undefined_value());
if (finalization_registry->cleared_cells().IsWeakCell()) {
WeakCell cleared_cells_head =
WeakCell::cast(finalization_registry->cleared_cells());
DCHECK_EQ(cleared_cells_head.prev(), *weak_cell);
cleared_cells_head.set_prev(ReadOnlyRoots(isolate).undefined_value());
} else {
DCHECK(finalization_registry->cleared_cells().IsUndefined(isolate));
}
// Also remove the WeakCell from the key_map (if it's there).
if (!weak_cell->unregister_token().IsUndefined(isolate)) {
if (weak_cell->key_list_prev().IsUndefined(isolate)) {
Handle<SimpleNumberDictionary> key_map =
handle(SimpleNumberDictionary::cast(finalization_registry->key_map()),
isolate);
Handle<Object> unregister_token =
handle(weak_cell->unregister_token(), isolate);
uint32_t key = Smi::ToInt(unregister_token->GetHash());
InternalIndex entry = key_map->FindEntry(isolate, key);
if (weak_cell->key_list_next().IsUndefined(isolate)) {
// weak_cell is the only one associated with its key; remove the key
// from the hash table.
DCHECK(entry.is_found());
key_map = SimpleNumberDictionary::DeleteEntry(isolate, key_map, entry);
finalization_registry->set_key_map(*key_map);
} else {
// weak_cell is the list head for its key; we need to change the value
// of the key in the hash table.
Handle<WeakCell> next =
handle(WeakCell::cast(weak_cell->key_list_next()), isolate);
DCHECK_EQ(next->key_list_prev(), *weak_cell);
next->set_key_list_prev(ReadOnlyRoots(isolate).undefined_value());
weak_cell->set_key_list_next(ReadOnlyRoots(isolate).undefined_value());
key_map = SimpleNumberDictionary::Set(isolate, key_map, key, next);
finalization_registry->set_key_map(*key_map);
}
} else {
// weak_cell is somewhere in the middle of its key list.
WeakCell prev = WeakCell::cast(weak_cell->key_list_prev());
prev.set_key_list_next(weak_cell->key_list_next());
if (!weak_cell->key_list_next().IsUndefined()) {
WeakCell next = WeakCell::cast(weak_cell->key_list_next());
next.set_key_list_prev(weak_cell->key_list_prev());
}
}
}
return weak_cell->holdings();
}
template <typename GCNotifyUpdatedSlotCallback>
void WeakCell::Nullify(Isolate* isolate,
GCNotifyUpdatedSlotCallback gc_notify_updated_slot) {

View File

@ -61,17 +61,15 @@ class JSFinalizationRegistry : public JSObject {
// Returns true if the cleared_cells list is non-empty.
inline bool NeedsCleanup() const;
// Remove the first cleared WeakCell from the cleared_cells
// list (assumes there is one) and return its holdings.
inline static Object PopClearedCellHoldings(
Handle<JSFinalizationRegistry> finalization_registry, Isolate* isolate);
// Call the user's cleanup function in a loop, once for each cleared cell.
// Remove the already-popped weak_cell from its unregister token linked list,
// as well as removing the entry from the key map if it is the only WeakCell
// with its unregister token. This method cannot GC and does not shrink the
// key map. Asserts that weak_cell has a non-undefined unregister token.
//
// Returns Nothing<bool> if exception occurs, otherwise returns Just(true).
static V8_WARN_UNUSED_RESULT Maybe<bool> Cleanup(
Isolate* isolate, Handle<JSFinalizationRegistry> finalization_registry,
Handle<Object> callback);
// It takes raw Addresses because it is called from CSA and Torque.
V8_EXPORT_PRIVATE static void RemoveCellFromUnregisterTokenMap(
Isolate* isolate, Address raw_finalization_registry,
Address raw_weak_cell);
// Layout description.
DEFINE_FIELD_OFFSET_CONSTANTS(

View File

@ -8306,35 +8306,50 @@ EXTERN_DEFINE_BASE_NAME_DICTIONARY(GlobalDictionary, GlobalDictionaryShape)
#undef EXTERN_DEFINE_DICTIONARY
#undef EXTERN_DEFINE_BASE_NAME_DICTIONARY
Maybe<bool> JSFinalizationRegistry::Cleanup(
Isolate* isolate, Handle<JSFinalizationRegistry> finalization_registry,
Handle<Object> cleanup) {
DCHECK(cleanup->IsCallable());
// 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(
SimpleNumberDictionary::cast(finalization_registry->key_map()),
isolate);
key_map = SimpleNumberDictionary::Shrink(isolate, key_map);
finalization_registry->set_key_map(*key_map);
}
void JSFinalizationRegistry::RemoveCellFromUnregisterTokenMap(
Isolate* isolate, Address raw_finalization_registry,
Address raw_weak_cell) {
DisallowHeapAllocation no_gc;
JSFinalizationRegistry finalization_registry =
JSFinalizationRegistry::cast(Object(raw_finalization_registry));
WeakCell weak_cell = WeakCell::cast(Object(raw_weak_cell));
DCHECK(!weak_cell.unregister_token().IsUndefined(isolate));
// 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.
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>();
// Remove weak_cell from the linked list of other WeakCells with the same
// unregister token and remove its unregister token from key_map if necessary
// without shrinking it. Since shrinking may allocate, it is performed by the
// caller after looping, or on exception.
if (weak_cell.key_list_prev().IsUndefined(isolate)) {
SimpleNumberDictionary key_map =
SimpleNumberDictionary::cast(finalization_registry.key_map());
Object unregister_token = weak_cell.unregister_token();
uint32_t key = Smi::ToInt(unregister_token.GetHash());
InternalIndex entry = key_map.FindEntry(isolate, key);
DCHECK(entry.is_found());
if (weak_cell.key_list_next().IsUndefined(isolate)) {
// weak_cell is the only one associated with its key; remove the key
// from the hash table.
key_map.ClearEntry(entry);
key_map.ElementRemoved();
} else {
// weak_cell is the list head for its key; we need to change the value
// of the key in the hash table.
WeakCell next = WeakCell::cast(weak_cell.key_list_next());
DCHECK_EQ(next.key_list_prev(), weak_cell);
next.set_key_list_prev(ReadOnlyRoots(isolate).undefined_value());
weak_cell.set_key_list_next(ReadOnlyRoots(isolate).undefined_value());
key_map.ValueAtPut(entry, next);
}
} else {
// weak_cell is somewhere in the middle of its key list.
WeakCell prev = WeakCell::cast(weak_cell.key_list_prev());
prev.set_key_list_next(weak_cell.key_list_next());
if (!weak_cell.key_list_next().IsUndefined()) {
WeakCell next = WeakCell::cast(weak_cell.key_list_next());
next.set_key_list_prev(weak_cell.key_list_prev());
}
}
return Just(true);
}
} // namespace internal

View File

@ -0,0 +1,30 @@
// Copyright 2020 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.
#include "src/runtime/runtime-utils.h"
#include "src/execution/arguments-inl.h"
#include "src/objects/js-weak-refs-inl.h"
namespace v8 {
namespace internal {
RUNTIME_FUNCTION(Runtime_ShrinkFinalizationRegistryUnregisterTokenMap) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSFinalizationRegistry, finalization_registry, 0);
if (!finalization_registry->key_map().IsUndefined(isolate)) {
Handle<SimpleNumberDictionary> key_map =
handle(SimpleNumberDictionary::cast(finalization_registry->key_map()),
isolate);
key_map = SimpleNumberDictionary::Shrink(isolate, key_map);
finalization_registry->set_key_map(*key_map);
}
return ReadOnlyRoots(isolate).undefined_value();
}
} // namespace internal
} // namespace v8

View File

@ -578,6 +578,9 @@ namespace internal {
F(WasmNewMultiReturnJSArray, 1, 1) \
F(WasmDebugBreak, 0, 1)
#define FOR_EACH_INTRINSIC_WEAKREF(F, I) \
F(ShrinkFinalizationRegistryUnregisterTokenMap, 1, 1)
#define FOR_EACH_INTRINSIC_RETURN_PAIR_IMPL(F, I) \
F(DebugBreakOnBytecode, 1, 2) \
F(LoadLookupSlotForCall, 1, 2)
@ -636,7 +639,8 @@ namespace internal {
FOR_EACH_INTRINSIC_SYMBOL(F, I) \
FOR_EACH_INTRINSIC_TEST(F, I) \
FOR_EACH_INTRINSIC_TYPEDARRAY(F, I) \
FOR_EACH_INTRINSIC_WASM(F, I)
FOR_EACH_INTRINSIC_WASM(F, I) \
FOR_EACH_INTRINSIC_WEAKREF(F, I)
// Defines the list of all intrinsics, coming in 2 flavors, either returning an
// object or a pair.

View File

@ -97,6 +97,33 @@ void NullifyWeakCell(Handle<WeakCell> weak_cell, Isolate* isolate) {
#endif // VERIFY_HEAP
}
Object PopClearedCellHoldings(
Handle<JSFinalizationRegistry> finalization_registry, Isolate* isolate) {
// PopClearedCell is implemented in Torque. Reproduce that implementation here
// for testing.
Handle<WeakCell> weak_cell =
handle(WeakCell::cast(finalization_registry->cleared_cells()), isolate);
DCHECK(weak_cell->prev().IsUndefined(isolate));
finalization_registry->set_cleared_cells(weak_cell->next());
weak_cell->set_next(ReadOnlyRoots(isolate).undefined_value());
if (finalization_registry->cleared_cells().IsWeakCell()) {
WeakCell cleared_cells_head =
WeakCell::cast(finalization_registry->cleared_cells());
DCHECK_EQ(cleared_cells_head.prev(), *weak_cell);
cleared_cells_head.set_prev(ReadOnlyRoots(isolate).undefined_value());
} else {
DCHECK(finalization_registry->cleared_cells().IsUndefined(isolate));
}
if (!weak_cell->unregister_token().IsUndefined(isolate)) {
JSFinalizationRegistry::RemoveCellFromUnregisterTokenMap(
isolate, finalization_registry->ptr(), weak_cell->ptr());
}
return weak_cell->holdings();
}
// Usage: VerifyWeakCellChain(isolate, list_head, n, cell1, cell2, ..., celln);
// verifies that list_head == cell1 and cell1, cell2, ..., celln. form a list.
void VerifyWeakCellChain(Isolate* isolate, Object list_head, int n_args, ...) {
@ -361,15 +388,13 @@ TEST(TestJSFinalizationRegistryPopClearedCellHoldings1) {
NullifyWeakCell(weak_cell3, isolate);
CHECK(finalization_registry->NeedsCleanup());
Object cleared1 = JSFinalizationRegistry::PopClearedCellHoldings(
finalization_registry, isolate);
Object cleared1 = PopClearedCellHoldings(finalization_registry, isolate);
CHECK_EQ(cleared1, *holdings3);
CHECK(weak_cell3->prev().IsUndefined(isolate));
CHECK(weak_cell3->next().IsUndefined(isolate));
CHECK(finalization_registry->NeedsCleanup());
Object cleared2 = JSFinalizationRegistry::PopClearedCellHoldings(
finalization_registry, isolate);
Object cleared2 = PopClearedCellHoldings(finalization_registry, isolate);
CHECK_EQ(cleared2, *holdings2);
CHECK(weak_cell2->prev().IsUndefined(isolate));
CHECK(weak_cell2->next().IsUndefined(isolate));
@ -379,8 +404,7 @@ TEST(TestJSFinalizationRegistryPopClearedCellHoldings1) {
NullifyWeakCell(weak_cell1, isolate);
CHECK(finalization_registry->NeedsCleanup());
Object cleared3 = JSFinalizationRegistry::PopClearedCellHoldings(
finalization_registry, isolate);
Object cleared3 = PopClearedCellHoldings(finalization_registry, isolate);
CHECK_EQ(cleared3, *holdings1);
CHECK(weak_cell1->prev().IsUndefined(isolate));
CHECK(weak_cell1->next().IsUndefined(isolate));
@ -424,8 +448,7 @@ TEST(TestJSFinalizationRegistryPopClearedCellHoldings2) {
*weak_cell1);
}
Object cleared1 = JSFinalizationRegistry::PopClearedCellHoldings(
finalization_registry, isolate);
Object cleared1 = PopClearedCellHoldings(finalization_registry, isolate);
CHECK_EQ(cleared1, *holdings2);
{
@ -434,8 +457,7 @@ TEST(TestJSFinalizationRegistryPopClearedCellHoldings2) {
VerifyWeakCellKeyChain(isolate, key_map, *token1, 1, *weak_cell1);
}
Object cleared2 = JSFinalizationRegistry::PopClearedCellHoldings(
finalization_registry, isolate);
Object cleared2 = PopClearedCellHoldings(finalization_registry, isolate);
CHECK_EQ(cleared2, *holdings1);
{
@ -621,8 +643,7 @@ TEST(TestWeakCellUnregisterPopped) {
NullifyWeakCell(weak_cell1, isolate);
CHECK(finalization_registry->NeedsCleanup());
Object cleared1 = JSFinalizationRegistry::PopClearedCellHoldings(
finalization_registry, isolate);
Object cleared1 = PopClearedCellHoldings(finalization_registry, isolate);
CHECK_EQ(cleared1, *holdings1);
VerifyWeakCellChain(isolate, finalization_registry->active_cells(), 0);

View File

@ -3,10 +3,12 @@
^
Error: callback
at callback (*%(basename)s:{NUMBER}:{NUMBER})
at FinalizationRegistry.cleanupSome (<anonymous>)
*%(basename)s:{NUMBER}: Error: callback
throw new Error('callback');
^
Error: callback
at callback (*%(basename)s:{NUMBER}:{NUMBER})
at FinalizationRegistry.cleanupSome (<anonymous>)