Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/js/src/builtin/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 27 kB image not shown  

Quelle  FinalizationRegistryObject.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


// Implementation of JS FinalizationRegistry objects.

#include "builtin/FinalizationRegistryObject.h"

#include "mozilla/ScopeExit.h"

#include "jsapi.h"

#include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"

#include "gc/GCContext-inl.h"
#include "vm/JSObject-inl.h"
#include "vm/NativeObject-inl.h"

using namespace js;

///////////////////////////////////////////////////////////////////////////
// FinalizationRecordObject

const JSClass FinalizationRecordObject::class_ = {
    "FinalizationRecord",
    JSCLASS_HAS_RESERVED_SLOTS(SlotCount),
};

/* static */
FinalizationRecordObject* FinalizationRecordObject::create(
    JSContext* cx, HandleFinalizationQueueObject queue, HandleValue heldValue) {
  MOZ_ASSERT(queue);

  auto record = NewObjectWithGivenProto<FinalizationRecordObject>(cx, nullptr);
  if (!record) {
    return nullptr;
  }

  MOZ_ASSERT(queue->compartment() == record->compartment());

  record->initReservedSlot(QueueSlot, ObjectValue(*queue));
  record->initReservedSlot(HeldValueSlot, heldValue);
  record->initReservedSlot(InMapSlot, BooleanValue(false));

  return record;
}

FinalizationQueueObject* FinalizationRecordObject::queue() const {
  Value value = getReservedSlot(QueueSlot);
  if (value.isUndefined()) {
    return nullptr;
  }
  return &value.toObject().as<FinalizationQueueObject>();
}

Value FinalizationRecordObject::heldValue() const {
  return getReservedSlot(HeldValueSlot);
}

bool FinalizationRecordObject::isRegistered() const {
  MOZ_ASSERT_IF(!queue(), heldValue().isUndefined());
  return queue();
}

bool FinalizationRecordObject::isInRecordMap() const {
  return getReservedSlot(InMapSlot).toBoolean();
}

void FinalizationRecordObject::setInRecordMap(bool newValue) {
  MOZ_ASSERT(newValue != isInRecordMap());
  setReservedSlot(InMapSlot, BooleanValue(newValue));
}

void FinalizationRecordObject::clear() {
  MOZ_ASSERT(queue());
  setReservedSlot(QueueSlot, UndefinedValue());
  setReservedSlot(HeldValueSlot, UndefinedValue());
  MOZ_ASSERT(!isRegistered());
}

///////////////////////////////////////////////////////////////////////////
// FinalizationRegistrationsObject

const JSClass FinalizationRegistrationsObject::class_ = {
    "FinalizationRegistrations",
    JSCLASS_HAS_RESERVED_SLOTS(SlotCount) | JSCLASS_BACKGROUND_FINALIZE,
    &classOps_,
    JS_NULL_CLASS_SPEC,
};

const JSClassOps FinalizationRegistrationsObject::classOps_ = {
    nullptr,                                    // addProperty
    nullptr,                                    // delProperty
    nullptr,                                    // enumerate
    nullptr,                                    // newEnumerate
    nullptr,                                    // resolve
    nullptr,                                    // mayResolve
    FinalizationRegistrationsObject::finalize,  // finalize
    nullptr,                                    // call
    nullptr,                                    // construct
    FinalizationRegistrationsObject::trace,     // trace
};

/* static */
FinalizationRegistrationsObject* FinalizationRegistrationsObject::create(
    JSContext* cx) {
  auto records = cx->make_unique<WeakFinalizationRecordVector>(cx->zone());
  if (!records) {
    return nullptr;
  }

  auto object =
      NewObjectWithGivenProto<FinalizationRegistrationsObject>(cx, nullptr);
  if (!object) {
    return nullptr;
  }

  InitReservedSlot(object, RecordsSlot, records.release(),
                   MemoryUse::FinalizationRecordVector);

  return object;
}

/* static */
void FinalizationRegistrationsObject::trace(JSTracer* trc, JSObject* obj) {
  if (!trc->traceWeakEdges()) {
    return;
  }

  auto* self = &obj->as<FinalizationRegistrationsObject>();
  if (WeakFinalizationRecordVector* records = self->records()) {
    TraceRange(trc, records->length(), records->begin(),
               "FinalizationRegistrationsObject records");
  }
}

/* static */
void FinalizationRegistrationsObject::finalize(JS::GCContext* gcx,
                                               JSObject* obj) {
  auto* self = &obj->as<FinalizationRegistrationsObject>();
  gcx->delete_(obj, self->records(), MemoryUse::FinalizationRecordVector);
}

inline WeakFinalizationRecordVector*
FinalizationRegistrationsObject::records() {
  return static_cast<WeakFinalizationRecordVector*>(privatePtr());
}

inline const WeakFinalizationRecordVector*
FinalizationRegistrationsObject::records() const {
  return static_cast<const WeakFinalizationRecordVector*>(privatePtr());
}

inline void* FinalizationRegistrationsObject::privatePtr() const {
  Value value = getReservedSlot(RecordsSlot);
  if (value.isUndefined()) {
    return nullptr;
  }
  void* ptr = value.toPrivate();
  MOZ_ASSERT(ptr);
  return ptr;
}

inline bool FinalizationRegistrationsObject::isEmpty() const {
  MOZ_ASSERT(records());
  return records()->empty();
}

inline bool FinalizationRegistrationsObject::append(
    HandleFinalizationRecordObject record) {
  MOZ_ASSERT(records());
  return records()->append(record);
}

inline void FinalizationRegistrationsObject::remove(
    HandleFinalizationRecordObject record) {
  MOZ_ASSERT(records());
  records()->eraseIfEqual(record);
}

inline bool FinalizationRegistrationsObject::traceWeak(JSTracer* trc) {
  MOZ_ASSERT(records());
  return records()->traceWeak(trc);
}

///////////////////////////////////////////////////////////////////////////
// FinalizationRegistryObject

// Bug 1600300: FinalizationRegistryObject is foreground finalized so that
// HeapPtr destructors never see referents with released arenas. When this is
// fixed we may be able to make this background finalized again.
const JSClass FinalizationRegistryObject::class_ = {
    "FinalizationRegistry",
    JSCLASS_HAS_CACHED_PROTO(JSProto_FinalizationRegistry) |
        JSCLASS_HAS_RESERVED_SLOTS(SlotCount) | JSCLASS_FOREGROUND_FINALIZE,
    &classOps_,
    &classSpec_,
};

const JSClass FinalizationRegistryObject::protoClass_ = {
    "FinalizationRegistry.prototype",
    JSCLASS_HAS_CACHED_PROTO(JSProto_FinalizationRegistry),
    JS_NULL_CLASS_OPS,
    &classSpec_,
};

const JSClassOps FinalizationRegistryObject::classOps_ = {
    nullptr,                               // addProperty
    nullptr,                               // delProperty
    nullptr,                               // enumerate
    nullptr,                               // newEnumerate
    nullptr,                               // resolve
    nullptr,                               // mayResolve
    FinalizationRegistryObject::finalize,  // finalize
    nullptr,                               // call
    nullptr,                               // construct
    FinalizationRegistryObject::trace,     // trace
};

const ClassSpec FinalizationRegistryObject::classSpec_ = {
    GenericCreateConstructor<construct, 1, gc::AllocKind::FUNCTION>,
    GenericCreatePrototype<FinalizationRegistryObject>,
    nullptr,
    nullptr,
    methods_,
    properties_,
};

const JSFunctionSpec FinalizationRegistryObject::methods_[] = {
    JS_FN("register", register_, 2, 0),
    JS_FN("unregister", unregister, 1, 0),
    JS_FN("cleanupSome", cleanupSome, 0, 0),
    JS_FS_END,
};

const JSPropertySpec FinalizationRegistryObject::properties_[] = {
    JS_STRING_SYM_PS(toStringTag, "FinalizationRegistry", JSPROP_READONLY),
    JS_PS_END,
};

/* static */
bool FinalizationRegistryObject::construct(JSContext* cx, unsigned argc,
                                           Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  if (!ThrowIfNotConstructing(cx, args, "FinalizationRegistry")) {
    return false;
  }

  RootedObject cleanupCallback(
      cx, ValueToCallable(cx, args.get(0), 1, NO_CONSTRUCT));
  if (!cleanupCallback) {
    return false;
  }

  RootedObject proto(cx);
  if (!GetPrototypeFromBuiltinConstructor(
          cx, args, JSProto_FinalizationRegistry, &proto)) {
    return false;
  }

  Rooted<UniquePtr<ObjectWeakMap>> registrations(
      cx, cx->make_unique<ObjectWeakMap>(cx));
  if (!registrations) {
    return false;
  }

  RootedFinalizationQueueObject queue(
      cx, FinalizationQueueObject::create(cx, cleanupCallback));
  if (!queue) {
    return false;
  }

  RootedFinalizationRegistryObject registry(
      cx, NewObjectWithClassProto<FinalizationRegistryObject>(cx, proto));
  if (!registry) {
    return false;
  }

  registry->initReservedSlot(QueueSlot, ObjectValue(*queue));
  InitReservedSlot(registry, RegistrationsSlot, registrations.release(),
                   MemoryUse::FinalizationRegistryRegistrations);

  if (!cx->runtime()->gc.addFinalizationRegistry(cx, registry)) {
    return false;
  }

  queue->setHasRegistry(true);

  args.rval().setObject(*registry);
  return true;
}

/* static */
void FinalizationRegistryObject::trace(JSTracer* trc, JSObject* obj) {
  // Trace the registrations weak map. At most this traces the
  // FinalizationRegistrationsObject values of the map; the contents of those
  // objects are weakly held and are not traced by this method.

  auto* registry = &obj->as<FinalizationRegistryObject>();
  if (ObjectWeakMap* registrations = registry->registrations()) {
    registrations->trace(trc);
  }
}

void FinalizationRegistryObject::traceWeak(JSTracer* trc) {
  // Trace and update the contents of the registrations weak map's values, which
  // are weakly held.

  MOZ_ASSERT(registrations());
  for (ObjectValueWeakMap::Enum e(registrations()->valueMap()); !e.empty();
       e.popFront()) {
    auto* registrations =
        &e.front().value().toObject().as<FinalizationRegistrationsObject>();
    if (!registrations->traceWeak(trc)) {
      e.removeFront();
    }
  }
}

/* static */
void FinalizationRegistryObject::finalize(JS::GCContext* gcx, JSObject* obj) {
  auto registry = &obj->as<FinalizationRegistryObject>();

  // The queue's flag should have been updated by
  // GCRuntime::sweepFinalizationRegistries.
  MOZ_ASSERT_IF(registry->queue(), !registry->queue()->hasRegistry());

  gcx->delete_(obj, registry->registrations(),
               MemoryUse::FinalizationRegistryRegistrations);
}

FinalizationQueueObject* FinalizationRegistryObject::queue() const {
  Value value = getReservedSlot(QueueSlot);
  if (value.isUndefined()) {
    return nullptr;
  }
  return &value.toObject().as<FinalizationQueueObject>();
}

ObjectWeakMap* FinalizationRegistryObject::registrations() const {
  Value value = getReservedSlot(RegistrationsSlot);
  if (value.isUndefined()) {
    return nullptr;
  }
  return static_cast<ObjectWeakMap*>(value.toPrivate());
}

// FinalizationRegistry.prototype.register(target, heldValue [, unregisterToken
// ])
// https://tc39.es/proposal-weakrefs/#sec-finalization-registry.prototype.register
/* static */
bool FinalizationRegistryObject::register_(JSContext* cx, unsigned argc,
                                           Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  // 1. Let finalizationRegistry be the this value.
  // 2. If Type(finalizationRegistry) is not Object, throw a TypeError
  // exception.
  // 3. If finalizationRegistry does not have a [[Cells]] internal slot, throw a
  // TypeError exception.
  if (!args.thisv().isObject() ||
      !args.thisv().toObject().is<FinalizationRegistryObject>()) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_NOT_A_FINALIZATION_REGISTRY,
                              "Receiver of FinalizationRegistry.register call");
    return false;
  }

  RootedFinalizationRegistryObject registry(
      cx, &args.thisv().toObject().as<FinalizationRegistryObject>());

  // 4. If Type(target) is not Object, throw a TypeError exception.
  if (!args.get(0).isObject()) {
    JS_ReportErrorNumberASCII(
        cx, GetErrorMessage, nullptr, JSMSG_OBJECT_REQUIRED,
        "target argument to FinalizationRegistry.register");
    return false;
  }

  RootedObject target(cx, &args[0].toObject());

  // 5. If SameValue(target, heldValue), throw a TypeError exception.
  if (args.get(1).isObject() && &args.get(1).toObject() == target) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_BAD_HELD_VALUE);
    return false;
  }

  HandleValue heldValue = args.get(1);

  // 6. If Type(unregisterToken) is not Object,
  //    a. If unregisterToken is not undefined, throw a TypeError exception.
  if (!args.get(2).isUndefined() && !args.get(2).isObject()) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_BAD_UNREGISTER_TOKEN,
                              "FinalizationRegistry.register");
    return false;
  }

  RootedObject unregisterToken(cx);
  if (!args.get(2).isUndefined()) {
    unregisterToken = &args[2].toObject();
  }

  // Create the finalization record representing this target and heldValue.
  Rooted<FinalizationQueueObject*> queue(cx, registry->queue());
  Rooted<FinalizationRecordObject*> record(
      cx, FinalizationRecordObject::create(cx, queue, heldValue));
  if (!record) {
    return false;
  }

  // Add the record to the registrations if an unregister token was supplied.
  if (unregisterToken &&
      !addRegistration(cx, registry, unregisterToken, record)) {
    return false;
  }

  auto registrationsGuard = mozilla::MakeScopeExit([&] {
    if (unregisterToken) {
      removeRegistrationOnError(registry, unregisterToken, record);
    }
  });

  // Fully unwrap the target to pass it to the GC.
  RootedObject unwrappedTarget(cx);
  unwrappedTarget = CheckedUnwrapDynamic(target, cx);
  if (!unwrappedTarget) {
    ReportAccessDenied(cx);
    return false;
  }

  // If the target is a DOM wrapper, preserve it.
  if (!preserveDOMWrapper(cx, target)) {
    return false;
  }

  // Wrap the record into the compartment of the target.
  RootedObject wrappedRecord(cx, record);
  AutoRealm ar(cx, unwrappedTarget);
  if (!JS_WrapObject(cx, &wrappedRecord)) {
    return false;
  }

  if (JS_IsDeadWrapper(wrappedRecord)) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
    return false;
  }

  // Register the record with the target.
  gc::GCRuntime* gc = &cx->runtime()->gc;
  if (!gc->registerWithFinalizationRegistry(cx, unwrappedTarget,
                                            wrappedRecord)) {
    return false;
  }

  registrationsGuard.release();
  args.rval().setUndefined();
  return true;
}

/* static */
bool FinalizationRegistryObject::preserveDOMWrapper(JSContext* cx,
                                                    HandleObject obj) {
  if (!MaybePreserveDOMWrapper(cx, obj)) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_BAD_FINALIZATION_REGISTRY_OBJECT);
    return false;
  }

  return true;
}

/* static */
bool FinalizationRegistryObject::addRegistration(
    JSContext* cx, HandleFinalizationRegistryObject registry,
    HandleObject unregisterToken, HandleFinalizationRecordObject record) {
  // Add the record to the list of records associated with this unregister
  // token.

  MOZ_ASSERT(unregisterToken);
  MOZ_ASSERT(registry->registrations());

  auto& map = *registry->registrations();
  Rooted<FinalizationRegistrationsObject*> recordsObject(cx);
  JSObject* obj = map.lookup(unregisterToken);
  if (obj) {
    recordsObject = &obj->as<FinalizationRegistrationsObject>();
  } else {
    recordsObject = FinalizationRegistrationsObject::create(cx);
    if (!recordsObject || !map.add(cx, unregisterToken, recordsObject)) {
      return false;
    }
  }

  if (!recordsObject->append(record)) {
    ReportOutOfMemory(cx);
    return false;
  }

  return true;
}

/* static */ void FinalizationRegistryObject::removeRegistrationOnError(
    HandleFinalizationRegistryObject registry, HandleObject unregisterToken,
    HandleFinalizationRecordObject record) {
  // Remove a registration if something went wrong before we added it to the
  // target zone's map. Note that this can't remove a registration after that
  // point.

  MOZ_ASSERT(unregisterToken);
  MOZ_ASSERT(registry->registrations());
  JS::AutoAssertNoGC nogc;

  auto& map = *registry->registrations();
  JSObject* obj = map.lookup(unregisterToken);
  MOZ_ASSERT(obj);
  auto records = &obj->as<FinalizationRegistrationsObject>();
  records->remove(record);

  if (records->empty()) {
    map.remove(unregisterToken);
  }
}

// FinalizationRegistry.prototype.unregister ( unregisterToken )
// https://tc39.es/proposal-weakrefs/#sec-finalization-registry.prototype.unregister
/* static */
bool FinalizationRegistryObject::unregister(JSContext* cx, unsigned argc,
                                            Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  // 1. Let finalizationRegistry be the this value.
  // 2. If Type(finalizationRegistry) is not Object, throw a TypeError
  //    exception.
  // 3. If finalizationRegistry does not have a [[Cells]] internal slot, throw a
  //    TypeError exception.
  if (!args.thisv().isObject() ||
      !args.thisv().toObject().is<FinalizationRegistryObject>()) {
    JS_ReportErrorNumberASCII(
        cx, GetErrorMessage, nullptr, JSMSG_NOT_A_FINALIZATION_REGISTRY,
        "Receiver of FinalizationRegistry.unregister call");
    return false;
  }

  RootedFinalizationRegistryObject registry(
      cx, &args.thisv().toObject().as<FinalizationRegistryObject>());

  // 4. If Type(unregisterToken) is not Object, throw a TypeError exception.
  if (!args.get(0).isObject()) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_BAD_UNREGISTER_TOKEN,
                              "FinalizationRegistry.unregister");
    return false;
  }

  RootedObject unregisterToken(cx, &args[0].toObject());

  // 5. Let removed be false.
  bool removed = false;

  // 6. For each Record { [[Target]], [[HeldValue]], [[UnregisterToken]] } cell
  //    that is an element of finalizationRegistry.[[Cells]], do
  //    a. If SameValue(cell.[[UnregisterToken]], unregisterToken) is true, then
  //       i. Remove cell from finalizationRegistry.[[Cells]].
  //       ii. Set removed to true.

  RootedObject obj(cx, registry->registrations()->lookup(unregisterToken));
  if (obj) {
    auto* records = obj->as<FinalizationRegistrationsObject>().records();
    MOZ_ASSERT(records);
    MOZ_ASSERT(!records->empty());
    for (FinalizationRecordObject* record : *records) {
      if (unregisterRecord(record)) {
        removed = true;
      }
    }
    registry->registrations()->remove(unregisterToken);
  }

  // 7. Return removed.
  args.rval().setBoolean(removed);
  return true;
}

/* static */
bool FinalizationRegistryObject::unregisterRecord(
    FinalizationRecordObject* record) {
  if (!record->isRegistered()) {
    return false;
  }

  // Clear the fields of this record; it will be removed from the target's
  // list when it is next swept.
  record->clear();
  return true;
}

// FinalizationRegistry.prototype.cleanupSome ( [ callback ] )
// https://tc39.es/proposal-weakrefs/#sec-finalization-registry.prototype.cleanupSome
bool FinalizationRegistryObject::cleanupSome(JSContext* cx, unsigned argc,
                                             Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  // 1. Let finalizationRegistry be the this value.
  // 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]).
  if (!args.thisv().isObject() ||
      !args.thisv().toObject().is<FinalizationRegistryObject>()) {
    JS_ReportErrorNumberASCII(
        cx, GetErrorMessage, nullptr, JSMSG_NOT_A_FINALIZATION_REGISTRY,
        "Receiver of FinalizationRegistry.cleanupSome call");
    return false;
  }

  RootedFinalizationRegistryObject registry(
      cx, &args.thisv().toObject().as<FinalizationRegistryObject>());

  // 3. If callback is not undefined and IsCallable(callback) is false, throw a
  //    TypeError exception.
  RootedObject cleanupCallback(cx);
  if (!args.get(0).isUndefined()) {
    cleanupCallback = ValueToCallable(cx, args.get(0), -1, NO_CONSTRUCT);
    if (!cleanupCallback) {
      return false;
    }
  }

  RootedFinalizationQueueObject queue(cx, registry->queue());
  if (!FinalizationQueueObject::cleanupQueuedRecords(cx, queue,
                                                     cleanupCallback)) {
    return false;
  }

  args.rval().setUndefined();
  return true;
}

///////////////////////////////////////////////////////////////////////////
// FinalizationQueueObject

// Bug 1600300: FinalizationQueueObject is foreground finalized so that
// HeapPtr destructors never see referents with released arenas. When this is
// fixed we may be able to make this background finalized again.
const JSClass FinalizationQueueObject::class_ = {
    "FinalizationQueue",
    JSCLASS_HAS_RESERVED_SLOTS(SlotCount) | JSCLASS_FOREGROUND_FINALIZE,
    &classOps_,
};

const JSClassOps FinalizationQueueObject::classOps_ = {
    nullptr,                            // addProperty
    nullptr,                            // delProperty
    nullptr,                            // enumerate
    nullptr,                            // newEnumerate
    nullptr,                            // resolve
    nullptr,                            // mayResolve
    FinalizationQueueObject::finalize,  // finalize
    nullptr,                            // call
    nullptr,                            // construct
    FinalizationQueueObject::trace,     // trace
};

/* static */
FinalizationQueueObject* FinalizationQueueObject::create(
    JSContext* cx, HandleObject cleanupCallback) {
  MOZ_ASSERT(cleanupCallback);

  Rooted<UniquePtr<FinalizationRecordVector>> recordsToBeCleanedUp(
      cx, cx->make_unique<FinalizationRecordVector>(cx->zone()));
  if (!recordsToBeCleanedUp) {
    return nullptr;
  }

  Handle<PropertyName*> funName = cx->names().empty_;
  RootedFunction doCleanupFunction(
      cx, NewNativeFunction(cx, doCleanup, 0, funName,
                            gc::AllocKind::FUNCTION_EXTENDED));
  if (!doCleanupFunction) {
    return nullptr;
  }

  // It's problematic storing a CCW to a global in another compartment because
  // you don't know how far to unwrap it to get the original object
  // back. Instead store a CCW to a plain object in the same compartment as the
  // global (this uses Object.prototype).
  Rooted<JSObject*> hostDefinedData(cx);
  if (!GetObjectFromHostDefinedData(cx, &hostDefinedData)) {
    return nullptr;
  }

  FinalizationQueueObject* queue =
      NewObjectWithGivenProto<FinalizationQueueObject>(cx, nullptr);
  if (!queue) {
    return nullptr;
  }

  queue->initReservedSlot(CleanupCallbackSlot, ObjectValue(*cleanupCallback));
  queue->initReservedSlot(HostDefinedDataSlot,
                          JS::ObjectOrNullValue(hostDefinedData));
  InitReservedSlot(queue, RecordsToBeCleanedUpSlot,
                   recordsToBeCleanedUp.release(),
                   MemoryUse::FinalizationRegistryRecordVector);
  queue->initReservedSlot(IsQueuedForCleanupSlot, BooleanValue(false));
  queue->initReservedSlot(DoCleanupFunctionSlot,
                          ObjectValue(*doCleanupFunction));
  queue->initReservedSlot(HasRegistrySlot, BooleanValue(false));

  doCleanupFunction->setExtendedSlot(DoCleanupFunction_QueueSlot,
                                     ObjectValue(*queue));

  return queue;
}

/* static */
void FinalizationQueueObject::trace(JSTracer* trc, JSObject* obj) {
  auto queue = &obj->as<FinalizationQueueObject>();

  if (FinalizationRecordVector* records = queue->recordsToBeCleanedUp()) {
    records->trace(trc);
  }
}

/* static */
void FinalizationQueueObject::finalize(JS::GCContext* gcx, JSObject* obj) {
  auto queue = &obj->as<FinalizationQueueObject>();

  gcx->delete_(obj, queue->recordsToBeCleanedUp(),
               MemoryUse::FinalizationRegistryRecordVector);
}

void FinalizationQueueObject::setHasRegistry(bool newValue) {
  MOZ_ASSERT(hasRegistry() != newValue);

  // Suppress our assertions about touching grey things. It's OK for us to set a
  // boolean slot even if this object is gray.
  AutoTouchingGrayThings atgt;

  setReservedSlot(HasRegistrySlot, BooleanValue(newValue));
}

bool FinalizationQueueObject::hasRegistry() const {
  return getReservedSlot(HasRegistrySlot).toBoolean();
}

inline JSObject* FinalizationQueueObject::cleanupCallback() const {
  Value value = getReservedSlot(CleanupCallbackSlot);
  if (value.isUndefined()) {
    return nullptr;
  }
  return &value.toObject();
}

JSObject* FinalizationQueueObject::getHostDefinedData() const {
  Value value = getReservedSlot(HostDefinedDataSlot);
  if (value.isUndefined()) {
    return nullptr;
  }
  return value.toObjectOrNull();
}

FinalizationRecordVector* FinalizationQueueObject::recordsToBeCleanedUp()
    const {
  Value value = getReservedSlot(RecordsToBeCleanedUpSlot);
  if (value.isUndefined()) {
    return nullptr;
  }
  return static_cast<FinalizationRecordVector*>(value.toPrivate());
}

bool FinalizationQueueObject::isQueuedForCleanup() const {
  return getReservedSlot(IsQueuedForCleanupSlot).toBoolean();
}

JSFunction* FinalizationQueueObject::doCleanupFunction() const {
  Value value = getReservedSlot(DoCleanupFunctionSlot);
  if (value.isUndefined()) {
    return nullptr;
  }
  return &value.toObject().as<JSFunction>();
}

void FinalizationQueueObject::queueRecordToBeCleanedUp(
    FinalizationRecordObject* record) {
  AutoEnterOOMUnsafeRegion oomUnsafe;
  if (!recordsToBeCleanedUp()->append(record)) {
    oomUnsafe.crash("FinalizationQueueObject::queueRecordsToBeCleanedUp");
  }
}

void FinalizationQueueObject::setQueuedForCleanup(bool value) {
  MOZ_ASSERT(value != isQueuedForCleanup());
  setReservedSlot(IsQueuedForCleanupSlot, BooleanValue(value));
}

/* static */
bool FinalizationQueueObject::doCleanup(JSContext* cx, unsigned argc,
                                        Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  RootedFunction callee(cx, &args.callee().as<JSFunction>());

  Value value = callee->getExtendedSlot(DoCleanupFunction_QueueSlot);
  RootedFinalizationQueueObject queue(
      cx, &value.toObject().as<FinalizationQueueObject>());

  queue->setQueuedForCleanup(false);
  return cleanupQueuedRecords(cx, queue);
}

// CleanupFinalizationRegistry ( finalizationRegistry [ , callback ] )
// https://tc39.es/proposal-weakrefs/#sec-cleanup-finalization-registry
/* static */
bool FinalizationQueueObject::cleanupQueuedRecords(
    JSContext* cx, HandleFinalizationQueueObject queue,
    HandleObject callbackArg) {
  MOZ_ASSERT(cx->compartment() == queue->compartment());

  // 2. If callback is undefined, set callback to
  //    finalizationRegistry.[[CleanupCallback]].
  RootedValue callback(cx);
  if (callbackArg) {
    callback.setObject(*callbackArg);
  } else {
    JSObject* cleanupCallback = queue->cleanupCallback();
    MOZ_ASSERT(cleanupCallback);
    callback.setObject(*cleanupCallback);
  }

  // 3. While finalizationRegistry.[[Cells]] contains a Record cell such that
  //    cell.[[WeakRefTarget]] is empty, then an implementation may perform the
  //    following steps,
  //    a. Choose any such cell.
  //    b. Remove cell from finalizationRegistry.[[Cells]].
  //    c. Perform ? Call(callback, undefined, « cell.[[HeldValue]] »).

  RootedValue heldValue(cx);
  RootedValue rval(cx);
  FinalizationRecordVector* records = queue->recordsToBeCleanedUp();
  while (!records->empty()) {
    FinalizationRecordObject* record = records->popCopy();

    // Skip over records that have been unregistered.
    if (!record->isRegistered()) {
      continue;
    }

    heldValue.set(record->heldValue());

    record->clear();

    if (!Call(cx, callback, UndefinedHandleValue, heldValue, &rval)) {
      return false;
    }
  }

  return true;
}

Messung V0.5
C=91 H=98 G=94

¤ Dauer der Verarbeitung: 0.16 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.