LCOV - code coverage report
Current view: top level - gjs - jsapi-util-root.h (source / functions) Coverage Total Hit
Test: gjs- Code Coverage Lines: 94.3 % 87 82
Test Date: 2024-09-12 04:39:42 Functions: 95.5 % 22 21
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 83.3 % 18 15

             Branch data     Line data    Source code
       1                 :             : /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
       2                 :             : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
       3                 :             : // SPDX-FileCopyrightText: 2017 Endless Mobile, Inc.
       4                 :             : // SPDX-FileCopyrightText: 2019 Canonical, Ltd.
       5                 :             : 
       6                 :             : #ifndef GJS_JSAPI_UTIL_ROOT_H_
       7                 :             : #define GJS_JSAPI_UTIL_ROOT_H_
       8                 :             : 
       9                 :             : #include <config.h>
      10                 :             : 
      11                 :             : #include <cstddef>  // for nullptr_t
      12                 :             : #include <memory>
      13                 :             : #include <new>
      14                 :             : 
      15                 :             : #include <glib.h>
      16                 :             : 
      17                 :             : #include <js/ComparisonOperators.h>
      18                 :             : #include <js/GCAPI.h>
      19                 :             : #include <js/HeapAPI.h>     // for ExposeObjectToActiveJS, GetGCThingZone
      20                 :             : #include <js/RootingAPI.h>  // for SafelyInitialized
      21                 :             : #include <js/TracingAPI.h>
      22                 :             : #include <js/TypeDecls.h>
      23                 :             : 
      24                 :             : #include "util/log.h"
      25                 :             : 
      26                 :             : namespace JS { template <typename T> struct GCPolicy; }
      27                 :             : 
      28                 :             : /* jsapi-util-root.h - Utilities for dealing with the lifetime and ownership of
      29                 :             :  * JS Objects and other things that can be collected by the garbage collector
      30                 :             :  * (collectively called "GC things.")
      31                 :             :  *
      32                 :             :  * GjsMaybeOwned is a multi-purpose wrapper for a JSObject. You can
      33                 :             :  * wrap a thing in one of three ways:
      34                 :             :  *
      35                 :             :  * - trace the object (tie it to the lifetime of another GC thing),
      36                 :             :  * - root the object (keep it alive as long as the wrapper is in existence),
      37                 :             :  * - maintain a weak pointer to the object (not keep it alive at all and have it
      38                 :             :  *   possibly be finalized out from under you).
      39                 :             :  *
      40                 :             :  * To trace or maintain a weak pointer, simply assign an object to the
      41                 :             :  * GjsMaybeOwned wrapper. For tracing, you must call the trace() method when
      42                 :             :  * your other GC thing is traced.
      43                 :             :  *
      44                 :             :  * Rooting requires a JSContext so can't just assign a thing of type T. Instead
      45                 :             :  * you need to call the root() method to set up rooting.
      46                 :             :  *
      47                 :             :  * If the thing is rooted, it will be unrooted when the GjsMaybeOwned is
      48                 :             :  * destroyed.
      49                 :             :  *
      50                 :             :  * To switch between one of the three modes, you must first call reset(). This
      51                 :             :  * drops all references to any object and leaves the GjsMaybeOwned in the
      52                 :             :  * same state as if it had just been constructed.
      53                 :             :  */
      54                 :             : 
      55                 :             : /* GjsMaybeOwned is intended for use as a member of classes that are allocated
      56                 :             :  * on the heap. Do not allocate GjsMaybeOwned on the stack, and do not allocate
      57                 :             :  * any instances of classes that have it as a member on the stack either. */
      58                 :             : class GjsMaybeOwned {
      59                 :             :  private:
      60                 :             :     /* m_root value controls which of these members we can access. When switching
      61                 :             :      * from one to the other, be careful to call the constructor and destructor
      62                 :             :      * of JS::Heap, since they use post barriers. */
      63                 :             :     JS::Heap<JSObject*> m_heap;
      64                 :             :     std::unique_ptr<JS::PersistentRootedObject> m_root;
      65                 :             : 
      66                 :             :     /* No-op unless GJS_VERBOSE_ENABLE_LIFECYCLE is defined to 1. */
      67                 :       61510 :     inline void debug(const char* what GJS_USED_VERBOSE_LIFECYCLE) {
      68                 :             :         gjs_debug_lifecycle(GJS_DEBUG_KEEP_ALIVE, "GjsMaybeOwned %p %s", this,
      69                 :             :                             what);
      70                 :       61510 :     }
      71                 :             : 
      72                 :             :     void
      73                 :       10500 :     teardown_rooting()
      74                 :             :     {
      75                 :       10500 :         debug("teardown_rooting()");
      76                 :       10500 :         g_assert(m_root);
      77                 :             : 
      78                 :       10500 :         m_root.reset();
      79                 :             : 
      80                 :       10500 :         new (&m_heap) JS::Heap<JSObject*>();
      81                 :       10500 :     }
      82                 :             : 
      83                 :             :  public:
      84                 :       10770 :     GjsMaybeOwned() {
      85                 :       10770 :         debug("created");
      86                 :       10770 :     }
      87                 :             : 
      88                 :       10743 :     ~GjsMaybeOwned() {
      89                 :       10743 :         debug("destroyed");
      90                 :       10743 :     }
      91                 :             : 
      92                 :             :     // COMPAT: constexpr in C++23
      93                 :       23770 :     [[nodiscard]] JSObject* get() const {
      94         [ +  + ]:       23770 :         return m_root ? m_root->get() : m_heap.get();
      95                 :             :     }
      96                 :             : 
      97                 :             :     // Use debug_addr() only for debug logging, because it is unbarriered.
      98                 :             :     // COMPAT: constexpr in C++23
      99                 :        6629 :     [[nodiscard]] const void* debug_addr() const {
     100         [ +  + ]:        6629 :         return m_root ? m_root->get() : m_heap.unbarrieredGet();
     101                 :             :     }
     102                 :             : 
     103                 :             :     // COMPAT: constexpr in C++23
     104                 :         477 :     bool operator==(JSObject* other) const {
     105         [ +  + ]:         477 :         if (m_root)
     106                 :          43 :             return m_root->get() == other;
     107                 :         434 :         return m_heap == other;
     108                 :             :     }
     109                 :         474 :     bool operator!=(JSObject* other) const { return !(*this == other); }
     110                 :             : 
     111                 :             :     // We can access the pointer without a read barrier if the only thing we are
     112                 :             :     // are doing with it is comparing it to nullptr.
     113                 :             :     // COMPAT: constexpr in C++23
     114                 :       28462 :     bool operator==(std::nullptr_t) const {
     115         [ +  + ]:       28462 :         if (m_root)
     116                 :       24363 :             return m_root->get() == nullptr;
     117                 :        4099 :         return m_heap.unbarrieredGet() == nullptr;
     118                 :             :     }
     119                 :       28462 :     bool operator!=(std::nullptr_t) const { return !(*this == nullptr); }
     120                 :             : 
     121                 :             :     // Likewise the truth value does not require a read barrier
     122                 :             :     // COMPAT: constexpr in C++23
     123                 :       28462 :     explicit operator bool() const { return *this != nullptr; }
     124                 :             : 
     125                 :             :     // You can get a Handle<T> if the thing is rooted, so that you can use this
     126                 :             :     // wrapper with stack rooting. However, you must not do this if the
     127                 :             :     // JSContext can be destroyed while the Handle is live. */
     128                 :             :     // COMPAT: constexpr in C++23
     129                 :             :     [[nodiscard]] JS::HandleObject handle() {
     130                 :             :         g_assert(m_root);
     131                 :             :         return *m_root;
     132                 :             :     }
     133                 :             : 
     134                 :             :     /* Roots the GC thing. You must not use this if you're already using the
     135                 :             :      * wrapper to store a non-rooted GC thing. */
     136                 :       10508 :     void root(JSContext* cx, JSObject* thing) {
     137                 :       10508 :         debug("root()");
     138                 :       10508 :         g_assert(!m_root);
     139                 :       10508 :         g_assert(!m_heap);
     140                 :       10508 :         m_heap.~Heap();
     141                 :       10508 :         m_root = std::make_unique<JS::PersistentRootedObject>(cx, thing);
     142                 :       10508 :     }
     143                 :             : 
     144                 :             :     /* You can only assign directly to the GjsMaybeOwned wrapper in the
     145                 :             :      * non-rooted case. */
     146                 :        1808 :     void operator=(JSObject* thing) {
     147                 :        1808 :         g_assert(!m_root);
     148                 :        1808 :         m_heap = thing;
     149                 :        1808 :     }
     150                 :             : 
     151                 :             :     /* Marks an object as reachable for one GC with ExposeObjectToActiveJS().
     152                 :             :      * Use to avoid stopping tracing an object during GC. This makes no sense
     153                 :             :      * in the rooted case. */
     154                 :         390 :     void prevent_collection() {
     155                 :         390 :         debug("prevent_collection()");
     156                 :         390 :         g_assert(!m_root);
     157                 :         390 :         JSObject* obj = m_heap.unbarrieredGet();
     158                 :             :         // If the object has been swept already, then the zone is nullptr
     159   [ +  -  -  +  :         390 :         if (!obj || !JS::GetGCThingZone(JS::GCCellPtr(obj)))
                   -  + ]
     160                 :           0 :             return;
     161         [ +  + ]:         390 :         if (!JS::RuntimeHeapIsCollecting())
     162                 :         143 :             JS::ExposeObjectToActiveJS(obj);
     163                 :             :     }
     164                 :             : 
     165                 :       14019 :     void reset() {
     166                 :       14019 :         debug("reset()");
     167         [ +  + ]:       14019 :         if (!m_root) {
     168                 :        3519 :             m_heap = nullptr;
     169                 :        3519 :             return;
     170                 :             :         }
     171                 :             : 
     172                 :       10500 :         teardown_rooting();
     173                 :             :     }
     174                 :             : 
     175                 :        1563 :     void switch_to_rooted(JSContext* cx) {
     176                 :        1563 :         debug("switch to rooted");
     177                 :        1563 :         g_assert(!m_root);
     178                 :             : 
     179                 :             :         /* Prevent the thing from being garbage collected while it is in neither
     180                 :             :          * m_heap nor m_root */
     181                 :        1563 :         JS::RootedObject thing{cx, m_heap};
     182                 :             : 
     183                 :        1563 :         reset();
     184                 :        1563 :         root(cx, thing);
     185                 :        1563 :         g_assert(m_root);
     186                 :        1563 :     }
     187                 :             : 
     188                 :        1422 :     void switch_to_unrooted(JSContext* cx) {
     189                 :        1422 :         debug("switch to unrooted");
     190                 :        1422 :         g_assert(m_root);
     191                 :             : 
     192                 :             :         /* Prevent the thing from being garbage collected while it is in neither
     193                 :             :          * m_heap nor m_root */
     194                 :        1422 :         JS::RootedObject thing{cx, *m_root};
     195                 :             : 
     196                 :        1422 :         reset();
     197                 :        1422 :         m_heap = thing;
     198                 :        1422 :         g_assert(!m_root);
     199                 :        1422 :     }
     200                 :             : 
     201                 :             :     /* Tracing makes no sense in the rooted case, because JS::PersistentRooted
     202                 :             :      * already takes care of that. */
     203                 :             :     void
     204                 :         178 :     trace(JSTracer   *tracer,
     205                 :             :           const char *name)
     206                 :             :     {
     207                 :         178 :         debug("trace()");
     208                 :         178 :         g_assert(!m_root);
     209                 :         178 :         JS::TraceEdge(tracer, &m_heap, name);
     210                 :         178 :     }
     211                 :             : 
     212                 :             :     /* If not tracing, then you must call this method during GC in order to
     213                 :             :      * update the object's location if it was moved, or null it out if it was
     214                 :             :      * finalized. If the object was finalized, returns true. */
     215                 :        1417 :     bool update_after_gc(JSTracer* trc) {
     216                 :        1417 :         debug("update_after_gc()");
     217                 :        1417 :         g_assert(!m_root);
     218                 :        1417 :         JS_UpdateWeakPointerAfterGC(trc, &m_heap);
     219                 :        1417 :         return !m_heap;
     220                 :             :     }
     221                 :             : 
     222                 :             :     // COMPAT: constexpr in C++23
     223                 :       17617 :     [[nodiscard]] bool rooted() const { return m_root != nullptr; }
     224                 :             : };
     225                 :             : 
     226                 :             : namespace Gjs {
     227                 :             : 
     228                 :             : template <typename T>
     229                 :             : class WeakPtr : public JS::Heap<T> {
     230                 :             :  public:
     231                 :             :     using JS::Heap<T>::Heap;
     232                 :             :     using JS::Heap<T>::operator=;
     233                 :             : };
     234                 :             : 
     235                 :             : }  // namespace Gjs
     236                 :             : 
     237                 :             : namespace JS {
     238                 :             : 
     239                 :             : template <typename T>
     240                 :             : struct GCPolicy<Gjs::WeakPtr<T>> {
     241                 :             :     static void trace(JSTracer* trc, Gjs::WeakPtr<T>* thingp,
     242                 :             :                       const char* name) {
     243                 :             :         return JS::TraceEdge(trc, thingp, name);
     244                 :             :     }
     245                 :             : 
     246                 :        4702 :     static bool traceWeak(JSTracer* trc, Gjs::WeakPtr<T>* thingp) {
     247                 :        4702 :         return js::gc::TraceWeakEdge(trc, thingp);
     248                 :             :     }
     249                 :             : 
     250                 :           0 :     static bool needsSweep(JSTracer* trc, const Gjs::WeakPtr<T>* thingp) {
     251                 :           0 :         Gjs::WeakPtr<T> thing{*thingp};
     252                 :           0 :         return !js::gc::TraceWeakEdge(trc, &thing);
     253                 :           0 :     }
     254                 :             : };
     255                 :             : 
     256                 :             : }  // namespace JS
     257                 :             : 
     258                 :             : #endif  // GJS_JSAPI_UTIL_ROOT_H_
        

Generated by: LCOV version 2.0-1