tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

FinalizationRegistryObject.h (10884B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /*
      8 * FinalizationRegistry objects allow a program to register to receive a
      9 * callback after a 'target' object dies. The callback is passed a 'held value'
     10 * (that hopefully doesn't entrain the target). An 'unregister token' is an
     11 * object which can be used to remove multiple previous registrations in one go.
     12 *
     13 * To arrange this, the following data structures are used:
     14 *
     15 *   +---------------------------------------+-------------------------------+
     16 *   |   FinalizationRegistry compartment    |   Target zone / compartment   |
     17 *   |                                       |                               |
     18 *   |     +------------------------------+  |     +------------------+      |
     19 *   |  +--+    FinalizationRegistry      |  |     |       Zone       |      |
     20 *   |  |  +---+----------------+---------+  |     +---------+--------+      |
     21 *   |  |      |                |            |               |               |
     22 *   |  |      v                v            |               v               |
     23 *   |  |  +---+---+  +---------+---------+  |  +------------+------------+  |
     24 *   |  |  |Record |  |   Registrations   |  |  |  FinalizationObservers  |  |
     25 *   |  |  |Vector |  |        map        |  |  +------------+------------+  |
     26 *   |  |  +---+---+  +-------------------+  |               |               |
     27 *   |  |      |      |   Weak    :Records|  |               |               |
     28 *   |  |      |      | unregister:Vector |  |               v               |
     29 *   |  |      |      |   token   :       |  |  +------------+------------+  |
     30 *   |  |      |      +--------------+----+  |  |      RecordMap map      |  |
     31 *   |  |      |                     |       |  +-------------------------+  |
     32 *   |  |      |                     |       |  |  Target  : ObserverList |  |
     33 *   |  |      |                     |       |  |  object  :              |  |
     34 *   |  |    * v                   * v       |  +----+-------------+------+  |
     35 *   |  |  +-------------------------+-+ *   |       |             |         |
     36 *   |  |  | FinalizationRecordObject  +<--------------------------+         |
     37 *   |  |  +---------------------------+     |       |                       |
     38 *   |  |  | Queue                     +--+  |       v                       |
     39 *   |  |  +---------------------------+  |  |  +----+-----+                 |
     40 *   |  |  | Held value                |  |  |  |  Target  |                 |
     41 *   |  |  +---------------------------+  |  |  | GC thing |                 |
     42 *   |  |                                 |  |  +----------+                 |
     43 *   |  +--------------+   +--------------+  |                               |
     44 *   |                 |   |                 |                               |
     45 *   |                 v   v                 |                               |
     46 *   |      +----------+---+----------+      |                               |
     47 *   |      | FinalizationQueueObject |      |                               |
     48 *   |      +-------------------------+      |                               |
     49 *   |                                       |                               |
     50 *   +---------------------------------------+-------------------------------+
     51 *
     52 * A FinalizationRegistry consists of several parts:
     53 *  - the FinalizationRegistry object that consumers see
     54 *  - zero or more FinalizationRecordObjects representing registered targets
     55 *  - a FinalizationQueue containing records for targets that have died, used to
     56 *    queue and call the cleanup callbacks
     57 *  - a weakmap tracking unregister tokens and their associated records
     58 *
     59 * Registering a target with a FinalizationRegistry creates a FinalizationRecord
     60 * containing a pointer to the queue and the heldValue. This is added to the
     61 * registry's vector of registered targets and also to a linked list of
     62 * finalization observers which is used to actually track the target.
     63 *
     64 * When a target is registered an unregister token may be supplied. If so, this
     65 * is also recorded by the registry and is stored in a map of
     66 * registrations. They keys of this map are weakly held and do not keep the
     67 * unregister token alive.
     68 *
     69 * When targets are unregistered, the registration is looked up in the weakmap
     70 * and the corresponding records are cleared.
     71 
     72 * The finalization observer lists are swept during GC to check for records
     73 * associated with dying targets. For such targets the associated record list is
     74 * processed and each record is added to the FinalizationQueueObject. At a later
     75 * time this causes the client's cleanup callback to be run.
     76 */
     77 
     78 #ifndef builtin_FinalizationRegistryObject_h
     79 #define builtin_FinalizationRegistryObject_h
     80 
     81 #include "gc/Barrier.h"
     82 #include "gc/FinalizationObservers.h"
     83 #include "gc/WeakMap.h"
     84 #include "js/GCVector.h"
     85 #include "vm/NativeObject.h"
     86 
     87 namespace js {
     88 
     89 class FinalizationRegistryObject;
     90 class FinalizationRecordObject;
     91 class FinalizationQueueObject;
     92 
     93 using HandleFinalizationRegistryObject = Handle<FinalizationRegistryObject*>;
     94 using HandleFinalizationRecordObject = Handle<FinalizationRecordObject*>;
     95 using HandleFinalizationQueueObject = Handle<FinalizationQueueObject*>;
     96 using RootedFinalizationRegistryObject = Rooted<FinalizationRegistryObject*>;
     97 using RootedFinalizationRecordObject = Rooted<FinalizationRecordObject*>;
     98 using RootedFinalizationQueueObject = Rooted<FinalizationQueueObject*>;
     99 
    100 // A finalization record: a pair of finalization queue and held value.
    101 //
    102 // A finalization record represents the registered interest of a finalization
    103 // registry in a target's finalization.
    104 //
    105 // Finalization records created in the 'registered' state but may be
    106 // unregistered. This happens when:
    107 //  - the heldValue is passed to the registry's cleanup callback
    108 //  - the registry's unregister method removes the registration
    109 //
    110 // Finalization records are added to a per-zone record map. They are removed
    111 // when the record is queued for cleanup, or if the interest in finalization is
    112 // cancelled. See FinalizationObservers::shouldRemoveRecord for the possible
    113 // reasons.
    114 
    115 class FinalizationRecordObject : public gc::ObserverListObject {
    116  enum {
    117    QueueSlot = ObserverListObject::SlotCount,
    118    HeldValueSlot,
    119    DebugStateSlot,  // Used for assertions only.
    120    SlotCount
    121  };
    122 
    123 public:
    124  enum State { Unknown, InRecordMap, InQueue };
    125 
    126  static const JSClass class_;
    127 
    128  static FinalizationRecordObject* create(JSContext* cx,
    129                                          HandleFinalizationQueueObject queue,
    130                                          HandleValue heldValue);
    131 
    132  FinalizationQueueObject* queue() const;
    133  Value heldValue() const;
    134  bool isRegistered() const;
    135 
    136 #ifdef DEBUG
    137  void setState(State state);
    138  State getState() const;
    139  bool isInRecordMap() const { return getState() == InRecordMap; }
    140  bool isInQueue() const { return getState() == InQueue; }
    141 #endif
    142 
    143  void setInRecordMap(bool newValue);
    144  void setInQueue(bool newValue);
    145  void clear();
    146 
    147 private:
    148  static const JSClassOps classOps_;
    149 
    150  static void finalize(JS::GCContext* gcx, JSObject* obj);
    151 };
    152 
    153 using FinalizationRecordVector =
    154    GCVector<HeapPtr<FinalizationRecordObject*>, 1, js::CellAllocPolicy>;
    155 
    156 // The JS FinalizationRegistry object itself.
    157 class FinalizationRegistryObject : public NativeObject {
    158  enum { QueueSlot = 0, RegistrationsSlot, RecordsWithoutTokenSlot, SlotCount };
    159 
    160 public:
    161  using RegistrationsMap =
    162      GCHashMap<HeapPtr<Value>, FinalizationRecordVector, gc::WeakTargetHasher>;
    163 
    164  static const JSClass class_;
    165  static const JSClass protoClass_;
    166 
    167  FinalizationQueueObject* queue() const;
    168  RegistrationsMap* registrations() const;
    169  FinalizationRecordVector* recordsWithoutToken() const;
    170 
    171  void traceWeak(JSTracer* trc);
    172 
    173  static bool unregisterRecord(FinalizationRecordObject* record);
    174 
    175  static bool cleanupQueuedRecords(JSContext* cx,
    176                                   HandleFinalizationRegistryObject registry,
    177                                   HandleObject callback = nullptr);
    178 
    179 private:
    180  static const JSClassOps classOps_;
    181  static const ClassSpec classSpec_;
    182  static const JSFunctionSpec methods_[];
    183  static const JSPropertySpec properties_[];
    184 
    185  static bool construct(JSContext* cx, unsigned argc, Value* vp);
    186  static bool register_(JSContext* cx, unsigned argc, Value* vp);
    187  static bool unregister(JSContext* cx, unsigned argc, Value* vp);
    188  static bool cleanupSome(JSContext* cx, unsigned argc, Value* vp);
    189 
    190  static bool addRegistration(JSContext* cx,
    191                              HandleFinalizationRegistryObject registry,
    192                              HandleValue unregisterToken,
    193                              HandleFinalizationRecordObject record);
    194  static void removeRegistrationOnError(
    195      HandleFinalizationRegistryObject registry, HandleValue unregisterToken,
    196      HandleFinalizationRecordObject record);
    197 
    198  static bool preserveDOMWrapper(JSContext* cx, HandleObject obj);
    199 
    200  static void trace(JSTracer* trc, JSObject* obj);
    201  static void finalize(JS::GCContext* gcx, JSObject* obj);
    202 };
    203 
    204 // Contains information about the cleanup callback and the records queued to
    205 // be cleaned up. This is not exposed to content JS.
    206 class FinalizationQueueObject : public NativeObject {
    207  enum {
    208    CleanupCallbackSlot = 0,
    209    HostDefinedDataSlot,
    210    RecordsToBeCleanedUpSlot,
    211    IsQueuedForCleanupSlot,
    212    DoCleanupFunctionSlot,
    213    HasRegistrySlot,
    214    SlotCount
    215  };
    216 
    217  enum DoCleanupFunctionSlots {
    218    DoCleanupFunction_QueueSlot = 0,
    219  };
    220 
    221 public:
    222  static const JSClass class_;
    223 
    224  JSObject* cleanupCallback() const;
    225  JSObject* getHostDefinedData() const;
    226  bool hasRecordsToCleanUp() const;
    227  FinalizationRecordVector* recordsToBeCleanedUp() const;
    228  bool isQueuedForCleanup() const;
    229  JSFunction* doCleanupFunction() const;
    230  bool hasRegistry() const;
    231 
    232  void queueRecordToBeCleanedUp(FinalizationRecordObject* record);
    233  void setQueuedForCleanup(bool value);
    234 
    235  void setHasRegistry(bool newValue);
    236 
    237  static FinalizationQueueObject* create(JSContext* cx,
    238                                         HandleObject cleanupCallback);
    239 
    240  static bool cleanupQueuedRecords(JSContext* cx,
    241                                   HandleFinalizationQueueObject registry,
    242                                   HandleObject callback = nullptr);
    243 
    244 private:
    245  static const JSClassOps classOps_;
    246 
    247  static bool doCleanup(JSContext* cx, unsigned argc, Value* vp);
    248 
    249  static void trace(JSTracer* trc, JSObject* obj);
    250  static void finalize(JS::GCContext* gcx, JSObject* obj);
    251 };
    252 
    253 }  // namespace js
    254 
    255 #endif /* builtin_FinalizationRegistryObject_h */