LCOV - code coverage report
Current view: top level - gi - wrapperutils.h (source / functions) Coverage Total Hit
Test: gjs- Code Coverage Lines: 90.4 % 312 282
Test Date: 2025-04-13 21:33:23 Functions: 96.1 % 304 292
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 72.4 % 152 110

             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: 2008 litl, LLC
       4                 :             : // SPDX-FileCopyrightText: 2018 Philip Chimento <philip.chimento@gmail.com>
       5                 :             : 
       6                 :             : #ifndef GI_WRAPPERUTILS_H_
       7                 :             : #define GI_WRAPPERUTILS_H_
       8                 :             : 
       9                 :             : #include <config.h>
      10                 :             : 
      11                 :             : #include <stdint.h>
      12                 :             : 
      13                 :             : #include <new>
      14                 :             : #include <string>
      15                 :             : 
      16                 :             : #include <girepository.h>
      17                 :             : #include <glib-object.h>
      18                 :             : #include <glib.h>
      19                 :             : 
      20                 :             : #include <js/CallArgs.h>
      21                 :             : #include <js/ComparisonOperators.h>
      22                 :             : #include <js/ErrorReport.h>  // for JSEXN_TYPEERR
      23                 :             : #include <js/GCVector.h>     // for MutableHandleIdVector
      24                 :             : #include <js/Id.h>
      25                 :             : #include <js/MemoryFunctions.h>
      26                 :             : #include <js/Object.h>
      27                 :             : #include <js/PropertyAndElement.h>  // for JS_DefineFunctionById
      28                 :             : #include <js/RootingAPI.h>
      29                 :             : #include <js/TypeDecls.h>
      30                 :             : #include <js/Value.h>
      31                 :             : #include <jsapi.h>  // for JS_GetPrototype
      32                 :             : 
      33                 :             : #include "gi/arg-inl.h"
      34                 :             : #include "gi/cwrapper.h"
      35                 :             : #include "gi/info.h"
      36                 :             : #include "gjs/atoms.h"
      37                 :             : #include "gjs/auto.h"
      38                 :             : #include "gjs/context-private.h"
      39                 :             : #include "gjs/jsapi-class.h"
      40                 :             : #include "gjs/jsapi-util.h"
      41                 :             : #include "gjs/macros.h"
      42                 :             : #include "gjs/profiler-private.h"
      43                 :             : #include "util/log.h"
      44                 :             : 
      45                 :             : struct JSFunctionSpec;
      46                 :             : struct JSPropertySpec;
      47                 :             : class JSTracer;
      48                 :             : 
      49                 :             : GJS_JSAPI_RETURN_CONVENTION
      50                 :             : bool gjs_wrapper_to_string_func(JSContext* cx, JSObject* this_obj,
      51                 :             :                                 const char* objtype, GIBaseInfo* info,
      52                 :             :                                 GType gtype, const void* native_address,
      53                 :             :                                 JS::MutableHandleValue ret);
      54                 :             : 
      55                 :             : bool gjs_wrapper_throw_nonexistent_field(JSContext* cx, GType gtype,
      56                 :             :                                          const char* field_name);
      57                 :             : 
      58                 :             : bool gjs_wrapper_throw_readonly_field(JSContext* cx, GType gtype,
      59                 :             :                                       const char* field_name);
      60                 :             : 
      61                 :             : namespace InfoType {
      62                 :             : enum Tag { Enum, Interface, Object, Struct, Union };
      63                 :             : }
      64                 :             : 
      65                 :             : namespace MemoryUse {
      66                 :             : constexpr JS::MemoryUse GObjectInstanceStruct = JS::MemoryUse::Embedding1;
      67                 :             : }
      68                 :             : 
      69                 :             : struct GjsTypecheckNoThrow {};
      70                 :             : 
      71                 :             : /*
      72                 :             :  * gjs_define_static_methods:
      73                 :             :  *
      74                 :             :  * Defines all static methods from @info on @constructor. Also includes class
      75                 :             :  * methods for GIObjectInfo, and interface methods for GIInterfaceInfo.
      76                 :             :  */
      77                 :             : template <InfoType::Tag>
      78                 :             : GJS_JSAPI_RETURN_CONVENTION bool gjs_define_static_methods(
      79                 :             :     JSContext* cx, JS::HandleObject constructor, GType gtype, GIBaseInfo* info);
      80                 :             : 
      81                 :             : /*
      82                 :             :  * GIWrapperBase:
      83                 :             :  *
      84                 :             :  * In most different kinds of C pointer that we expose to JS through GObject
      85                 :             :  * Introspection (boxed, fundamental, gerror, interface, object, union), we want
      86                 :             :  * to have different private structures for the prototype JS object and the JS
      87                 :             :  * objects representing instances. Both should inherit from a base structure for
      88                 :             :  * their common functionality.
      89                 :             :  *
      90                 :             :  * This is mainly for memory reasons. We need to keep track of the GIBaseInfo*
      91                 :             :  * and GType for each dynamically created class, but we don't need to duplicate
      92                 :             :  * that information (16 bytes on x64 systems) for every instance. In some cases
      93                 :             :  * there can also be other information that's only used on the prototype.
      94                 :             :  *
      95                 :             :  * So, to conserve memory, we split the private structures in FooInstance and
      96                 :             :  * FooPrototype, which both inherit from FooBase. All the repeated code in these
      97                 :             :  * structures lives in GIWrapperBase, GIWrapperPrototype, and GIWrapperInstance.
      98                 :             :  *
      99                 :             :  * The m_proto member needs a bit of explanation, as this is used to implement
     100                 :             :  * an unusual form of polymorphism. Sadly, we cannot have virtual methods in
     101                 :             :  * FooBase, because SpiderMonkey can be compiled with or without RTTI, so we
     102                 :             :  * cannot count on being able to cast FooBase to FooInstance or FooPrototype
     103                 :             :  * with dynamic_cast<>, and the vtable would take up just as much space anyway.
     104                 :             :  * Instead, we use the CRTP technique, and distinguish between FooInstance and
     105                 :             :  * FooPrototype using the m_proto member, which will be null for FooPrototype.
     106                 :             :  * Instead of casting, we have the to_prototype() and to_instance() methods
     107                 :             :  * which will give you a pointer if the FooBase is of the correct type (and
     108                 :             :  * assert if not.)
     109                 :             :  *
     110                 :             :  * The CRTP requires inheriting classes to declare themselves friends of the
     111                 :             :  * parent class, so that the parent class can call their private methods.
     112                 :             :  *
     113                 :             :  * For more information about the CRTP, the Wikipedia article is informative:
     114                 :             :  * https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
     115                 :             :  */
     116                 :             : template <class Base, class Prototype, class Instance>
     117                 :             : class GIWrapperBase : public CWrapperPointerOps<Base> {
     118                 :             :  protected:
     119                 :             :     // nullptr if this Base is a Prototype; points to the corresponding
     120                 :             :     // Prototype if this Base is an Instance.
     121                 :             :     Prototype* m_proto;
     122                 :             : 
     123                 :       24685 :     explicit GIWrapperBase(Prototype* proto = nullptr) : m_proto(proto) {}
     124                 :             : 
     125                 :             :     // These three can be overridden in subclasses. See define_jsclass().
     126                 :             :     static constexpr JSPropertySpec* proto_properties = nullptr;
     127                 :             :     static constexpr JSPropertySpec* static_properties = nullptr;
     128                 :             :     static constexpr JSFunctionSpec* proto_methods = nullptr;
     129                 :             :     static constexpr JSFunctionSpec* static_methods = nullptr;
     130                 :             : 
     131                 :             :  public:
     132                 :             :     // Methods implementing our CRTP polymorphism scheme follow below. We don't
     133                 :             :     // use standard C++ polymorphism because that would occupy another 8 bytes
     134                 :             :     // for a vtable.
     135                 :             : 
     136                 :             :     /*
     137                 :             :      * GIWrapperBase::is_prototype:
     138                 :             :      *
     139                 :             :      * Returns whether this Base is actually a Prototype (true) or an Instance
     140                 :             :      * (false).
     141                 :             :      */
     142                 :      741070 :     [[nodiscard]] bool is_prototype() const { return !m_proto; }
     143                 :             : 
     144                 :             :     /*
     145                 :             :      * GIWrapperBase::to_prototype:
     146                 :             :      * GIWrapperBase::to_instance:
     147                 :             :      *
     148                 :             :      * These methods assert that this Base is of the correct subclass. If you
     149                 :             :      * don't want to assert, then either check beforehand with is_prototype(),
     150                 :             :      * or use get_prototype().
     151                 :             :      */
     152                 :       53089 :     [[nodiscard]] Prototype* to_prototype() {
     153                 :       53089 :         g_assert(is_prototype());
     154                 :       53089 :         return reinterpret_cast<Prototype*>(this);
     155                 :             :     }
     156                 :       27757 :     [[nodiscard]] const Prototype* to_prototype() const {
     157                 :       27757 :         g_assert(is_prototype());
     158                 :       27757 :         return reinterpret_cast<const Prototype*>(this);
     159                 :             :     }
     160                 :      144952 :     [[nodiscard]] Instance* to_instance() {
     161                 :      144952 :         g_assert(!is_prototype());
     162                 :      144952 :         return reinterpret_cast<Instance*>(this);
     163                 :             :     }
     164                 :        7239 :     [[nodiscard]] const Instance* to_instance() const {
     165                 :        7239 :         g_assert(!is_prototype());
     166                 :        7239 :         return reinterpret_cast<const Instance*>(this);
     167                 :             :     }
     168                 :             : 
     169                 :             :     /*
     170                 :             :      * GIWrapperBase::get_prototype:
     171                 :             :      *
     172                 :             :      * get_prototype() doesn't assert. If you call it on a Prototype, it returns
     173                 :             :      * you the same object cast to the correct type; if you call it on an
     174                 :             :      * Instance, it returns you the Prototype belonging to the corresponding JS
     175                 :             :      * prototype.
     176                 :             :      */
     177                 :        2160 :     [[nodiscard]] [[gnu::const]] Prototype* get_prototype() {
     178         [ -  + ]:        2160 :         return is_prototype() ? to_prototype() : m_proto;
     179                 :             :     }
     180                 :      259129 :     [[nodiscard]] const Prototype* get_prototype() const {
     181         [ +  + ]:      259129 :         return is_prototype() ? to_prototype() : m_proto;
     182                 :             :     }
     183                 :             : 
     184                 :             :     // Accessors for Prototype members follow below. Both Instance and Prototype
     185                 :             :     // should be able to access the GIFooInfo and the GType, but for space
     186                 :             :     // reasons we store them only on Prototype.
     187                 :             : 
     188                 :       26853 :     [[nodiscard]] GIBaseInfo* info() const { return get_prototype()->info(); }
     189                 :      232233 :     [[nodiscard]] GType gtype() const { return get_prototype()->gtype(); }
     190                 :             : 
     191                 :             :     // The next three methods are operations derived from the GIFooInfo.
     192                 :             : 
     193                 :        3976 :     [[nodiscard]] const char* type_name() const { return g_type_name(gtype()); }
     194                 :        4509 :     [[nodiscard]] const char* ns() const {
     195         [ +  + ]:        4509 :         return info() ? g_base_info_get_namespace(info()) : "";
     196                 :             :     }
     197                 :        6475 :     [[nodiscard]] const char* name() const {
     198         [ +  + ]:        6475 :         return info() ? g_base_info_get_name(info()) : type_name();
     199                 :             :     }
     200                 :             : 
     201                 :        2843 :     [[nodiscard]] std::string format_name() const {
     202                 :        2843 :         std::string retval = ns();
     203         [ +  + ]:        2843 :         if (!retval.empty())
     204                 :        2842 :             retval += '.';
     205                 :        2843 :         retval += name();
     206                 :        2843 :         return retval;
     207                 :             :     }
     208                 :             : 
     209                 :             :  private:
     210                 :             :     // Accessor for Instance member. Used only in debug methods and toString().
     211                 :          59 :     [[nodiscard]] const void* ptr_addr() const {
     212         [ -  + ]:          59 :         return is_prototype() ? nullptr : to_instance()->ptr();
     213                 :             :     }
     214                 :             : 
     215                 :             :     // Debug methods
     216                 :             : 
     217                 :             :  protected:
     218                 :       22150 :     void debug_lifecycle(const char* message GJS_USED_VERBOSE_LIFECYCLE) const {
     219                 :             :         gjs_debug_lifecycle(Base::DEBUG_TOPIC,
     220                 :             :                             "[%p: %s pointer %p - %s (%s)] %s", this,
     221                 :             :                             Base::DEBUG_TAG, ptr_addr(), format_name().c_str(),
     222                 :             :                             type_name(), message);
     223                 :       22150 :     }
     224                 :       58454 :     void debug_lifecycle(const void* obj GJS_USED_VERBOSE_LIFECYCLE,
     225                 :             :                          const char* message GJS_USED_VERBOSE_LIFECYCLE) const {
     226                 :             :         gjs_debug_lifecycle(Base::DEBUG_TOPIC,
     227                 :             :                             "[%p: %s pointer %p - JS wrapper %p - %s (%s)] %s",
     228                 :             :                             this, Base::DEBUG_TAG, ptr_addr(), obj,
     229                 :             :                             format_name().c_str(), type_name(), message);
     230                 :       58454 :     }
     231                 :        1103 :     void debug_jsprop(const char* message GJS_USED_VERBOSE_PROPS,
     232                 :             :                       const char* id GJS_USED_VERBOSE_PROPS,
     233                 :             :                       const void* obj GJS_USED_VERBOSE_PROPS) const {
     234                 :             :         gjs_debug_jsprop(
     235                 :             :             Base::DEBUG_TOPIC,
     236                 :             :             "[%p: %s pointer %p - JS wrapper %p - %s (%s)] %s '%s'", this,
     237                 :             :             Base::DEBUG_TAG, ptr_addr(), obj, format_name().c_str(),
     238                 :             :             type_name(), message, id);
     239                 :        1103 :     }
     240                 :       83447 :     void debug_jsprop(const char* message, jsid id, const void* obj) const {
     241                 :             :         if constexpr (GJS_VERBOSE_ENABLE_PROPS)
     242                 :             :             debug_jsprop(message, gjs_debug_id(id).c_str(), obj);
     243                 :       83447 :     }
     244                 :             :     void debug_jsprop(const char* message, JSString* id,
     245                 :             :                       const void* obj) const {
     246                 :             :         if constexpr (GJS_VERBOSE_ENABLE_PROPS)
     247                 :             :             debug_jsprop(message, gjs_debug_string(id).c_str(), obj);
     248                 :             :     }
     249                 :        4423 :     static void debug_jsprop_static(const char* message GJS_USED_VERBOSE_PROPS,
     250                 :             :                                     jsid id GJS_USED_VERBOSE_PROPS,
     251                 :             :                                     const void* obj GJS_USED_VERBOSE_PROPS) {
     252                 :             :         gjs_debug_jsprop(Base::DEBUG_TOPIC,
     253                 :             :                          "[%s JS wrapper %p] %s '%s', no instance associated",
     254                 :             :                          Base::DEBUG_TAG, obj, message,
     255                 :             :                          gjs_debug_id(id).c_str());
     256                 :        4423 :     }
     257                 :             : 
     258                 :             :     // JS class operations, used only in the JSClassOps struct
     259                 :             : 
     260                 :             :     /*
     261                 :             :      * GIWrapperBase::new_enumerate:
     262                 :             :      *
     263                 :             :      * Include this in the Base::klass vtable if the class should support
     264                 :             :      * lazy enumeration (listing all of the lazy properties that can be defined
     265                 :             :      * in resolve().) If it is included, then there must be a corresponding
     266                 :             :      * Prototype::new_enumerate_impl() method.
     267                 :             :      */
     268                 :             :     GJS_JSAPI_RETURN_CONVENTION
     269                 :         229 :     static bool new_enumerate(JSContext* cx, JS::HandleObject obj,
     270                 :             :                               JS::MutableHandleIdVector properties,
     271                 :             :                               bool only_enumerable) {
     272                 :         229 :         Base* priv = Base::for_js(cx, obj);
     273                 :             : 
     274                 :         229 :         priv->debug_jsprop("Enumerate hook", "(all)", obj);
     275                 :             : 
     276         [ +  + ]:         229 :         if (!priv->is_prototype()) {
     277                 :             :             // Instances don't have any methods or properties.
     278                 :             :             // Spidermonkey will call new_enumerate on the prototype next.
     279                 :         150 :             return true;
     280                 :             :         }
     281                 :             : 
     282                 :          79 :         return priv->to_prototype()->new_enumerate_impl(cx, obj, properties,
     283                 :          79 :                                                         only_enumerable);
     284                 :             :     }
     285                 :             : 
     286                 :             :  private:
     287                 :             :     /*
     288                 :             :      * GIWrapperBase::id_is_never_lazy:
     289                 :             :      *
     290                 :             :      * Returns true if @id should never be treated as a lazy property. The
     291                 :             :      * JSResolveOp for an instance is called for every property not defined,
     292                 :             :      * even if it's one of the functions or properties we're adding to the
     293                 :             :      * prototype manually, such as toString().
     294                 :             :      *
     295                 :             :      * Override this and chain up if you have Base::resolve in your JSClassOps
     296                 :             :      * vtable, and have overridden Base::proto_properties or
     297                 :             :      * Base::proto_methods. You should add any identifiers in the override that
     298                 :             :      * you have added to the prototype object.
     299                 :             :      */
     300                 :       26218 :     [[nodiscard]] static bool id_is_never_lazy(jsid id, const GjsAtoms& atoms) {
     301                 :             :         // toString() is always defined somewhere on the prototype chain, so it
     302                 :             :         // is never a lazy property.
     303                 :       26218 :         return id == atoms.to_string();
     304                 :             :     }
     305                 :             : 
     306                 :             :  protected:
     307                 :             :     /**
     308                 :             :      * GIWrapperBase::resolve_prototype:
     309                 :             :      */
     310                 :        3745 :     [[nodiscard]] static Prototype* resolve_prototype(JSContext* cx,
     311                 :             :                                                       JS::HandleObject proto) {
     312         [ +  + ]:        3745 :         if (JS::GetClass(proto) == &Base::klass)
     313                 :        3299 :             return Prototype::for_js(cx, proto);
     314                 :             : 
     315                 :         446 :         const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
     316                 :             : 
     317                 :         446 :         bool has_property = false;
     318         [ -  + ]:         446 :         if (!JS_HasOwnPropertyById(cx, proto, atoms.gobject_prototype(),
     319                 :             :                                    &has_property))
     320                 :           0 :             return nullptr;
     321                 :             : 
     322         [ +  + ]:         446 :         if (!has_property) {
     323                 :           2 :             gjs_throw(cx, "Tried to construct an object without a GType");
     324                 :           2 :             return nullptr;
     325                 :             :         }
     326                 :             : 
     327                 :         444 :         JS::RootedValue gobject_proto(cx);
     328         [ -  + ]:         444 :         if (!JS_GetPropertyById(cx, proto, atoms.gobject_prototype(),
     329                 :             :                                 &gobject_proto))
     330                 :           0 :             return nullptr;
     331                 :             : 
     332         [ -  + ]:         444 :         if (!gobject_proto.isObject()) {
     333                 :           0 :             gjs_throw(cx, "Tried to construct an object without a GType");
     334                 :           0 :             return nullptr;
     335                 :             :         }
     336                 :             : 
     337                 :         444 :         JS::RootedObject obj(cx, &gobject_proto.toObject());
     338                 :             :         // gobject_prototype is an internal symbol so we can assert that it is
     339                 :             :         // only assigned to objects with &Base::klass definitions
     340                 :         444 :         g_assert(JS::GetClass(obj) == &Base::klass);
     341                 :             : 
     342                 :         444 :         return Prototype::for_js(cx, obj);
     343                 :         444 :     }
     344                 :             : 
     345                 :             :     /*
     346                 :             :      * GIWrapperBase::resolve:
     347                 :             :      *
     348                 :             :      * Include this in the Base::klass vtable if the class should support lazy
     349                 :             :      * properties. If it is included, then there must be a corresponding
     350                 :             :      * Prototype::resolve_impl() method.
     351                 :             :      *
     352                 :             :      * The *resolved out parameter, on success, should be false to indicate that
     353                 :             :      * id was not resolved; and true if id was resolved.
     354                 :             :      */
     355                 :             :     GJS_JSAPI_RETURN_CONVENTION
     356                 :       83990 :     static bool resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
     357                 :             :                         bool* resolved) {
     358                 :       83990 :         Base* priv = Base::for_js(cx, obj);
     359                 :             : 
     360         [ +  + ]:       83990 :         if (!priv) {
     361                 :             :             // This catches a case in Object where the private struct isn't set
     362                 :             :             // until the initializer is called, so just defer to prototype
     363                 :             :             // chains in this case.
     364                 :             :             //
     365                 :             :             // This isn't too bad: either you get undefined if the field doesn't
     366                 :             :             // exist on any of the prototype chains, or whatever code will run
     367                 :             :             // afterwards will fail because of the "!priv" check there.
     368                 :        2768 :             debug_jsprop_static("Resolve hook", id, obj);
     369                 :        2768 :             *resolved = false;
     370                 :        2768 :             return true;
     371                 :             :         }
     372                 :             : 
     373                 :       81222 :         priv->debug_jsprop("Resolve hook", id, obj);
     374                 :             : 
     375         [ +  + ]:       81222 :         if (!priv->is_prototype()) {
     376                 :             :             // We are an instance, not a prototype, so look for per-instance
     377                 :             :             // props that we want to define on the JSObject. Generally we do not
     378                 :             :             // want to cache these in JS, we want to always pull them from the C
     379                 :             :             // object, or JS would not see any changes made from C. So we use
     380                 :             :             // the property accessors, not this resolve hook.
     381                 :       55004 :             *resolved = false;
     382                 :       55004 :             return true;
     383                 :             :         }
     384                 :             : 
     385                 :       26218 :         const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
     386         [ +  + ]:       26218 :         if (id_is_never_lazy(id, atoms)) {
     387                 :        2251 :             *resolved = false;
     388                 :        2251 :             return true;
     389                 :             :         }
     390                 :             : 
     391                 :       23967 :         return priv->to_prototype()->resolve_impl(cx, obj, id, resolved);
     392                 :             :     }
     393                 :             : 
     394                 :             :     /*
     395                 :             :      * GIWrapperBase::finalize:
     396                 :             :      *
     397                 :             :      * This should always be included in the Base::klass vtable. The destructors
     398                 :             :      * of Prototype and Instance will be called in the finalize hook. It is not
     399                 :             :      * necessary to include a finalize_impl() function in Prototype or Instance.
     400                 :             :      * Any needed finalization should be done in ~Prototype() and ~Instance().
     401                 :             :      */
     402                 :       24662 :     static void finalize(JS::GCContext* gcx, JSObject* obj) {
     403                 :       24662 :         Base* priv = Base::for_js_nocheck(obj);
     404         [ +  + ]:       24662 :         if (!priv)
     405                 :           2 :             return;  // construction didn't finish
     406                 :             : 
     407                 :             :         // Call only GIWrapperBase's original method here, not any overrides;
     408                 :             :         // e.g., we don't want to deal with a read barrier in ObjectInstance.
     409                 :       24660 :         static_cast<GIWrapperBase*>(priv)->debug_lifecycle(obj, "Finalize");
     410                 :             : 
     411         [ +  + ]:       24660 :         if (priv->is_prototype())
     412                 :        1843 :             priv->to_prototype()->finalize_impl(gcx, obj);
     413                 :             :         else
     414                 :       22817 :             priv->to_instance()->finalize_impl(gcx, obj);
     415                 :             : 
     416                 :       24660 :         Base::unset_private(obj);
     417                 :             :     }
     418                 :             : 
     419                 :             :     /*
     420                 :             :      * GIWrapperBase::trace:
     421                 :             :      *
     422                 :             :      * This should be included in the Base::klass vtable if any of the Base,
     423                 :             :      * Prototype or Instance structures contain any members that the JS garbage
     424                 :             :      * collector must trace. Each struct containing such members must override
     425                 :             :      * GIWrapperBase::trace_impl(), GIWrapperPrototype::trace_impl(), and/or
     426                 :             :      * GIWrapperInstance::trace_impl() in order to perform the trace.
     427                 :             :      */
     428                 :        4816 :     static void trace(JSTracer* trc, JSObject* obj) {
     429                 :        4816 :         Base* priv = Base::for_js_nocheck(obj);
     430         [ -  + ]:        4816 :         if (!priv)
     431                 :           0 :             return;
     432                 :             : 
     433                 :             :         // Don't log in trace(). That would overrun even the most verbose logs.
     434                 :             : 
     435         [ +  + ]:        4816 :         if (priv->is_prototype())
     436                 :        4224 :             priv->to_prototype()->trace_impl(trc);
     437                 :             :         else
     438                 :         592 :             priv->to_instance()->trace_impl(trc);
     439                 :             : 
     440                 :        4816 :         priv->trace_impl(trc);
     441                 :             :     }
     442                 :             : 
     443                 :             :     /*
     444                 :             :      * GIWrapperBase::trace_impl:
     445                 :             :      * Override if necessary. See trace().
     446                 :             :      */
     447                 :        4816 :     void trace_impl(JSTracer*) {}
     448                 :             : 
     449                 :             :     // JSNative methods
     450                 :             : 
     451                 :             :     /*
     452                 :             :      * GIWrapperBase::constructor:
     453                 :             :      *
     454                 :             :      * C++ implementation of the JS constructor passed to JS_InitClass(). Only
     455                 :             :      * called on instances, never on prototypes. This method contains the
     456                 :             :      * functionality common to all GI wrapper classes. There must be a
     457                 :             :      * corresponding Instance::constructor_impl method containing the rest of
     458                 :             :      * the functionality.
     459                 :             :      */
     460                 :             :     GJS_JSAPI_RETURN_CONVENTION
     461                 :        3062 :     static bool constructor(JSContext* cx, unsigned argc, JS::Value* vp) {
     462                 :        3062 :         JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
     463                 :             : 
     464         [ +  + ]:        3062 :         if (!args.isConstructing()) {
     465                 :           1 :             gjs_throw_constructor_error(cx);
     466                 :           1 :             return false;
     467                 :             :         }
     468                 :        3061 :         JS::RootedObject obj(
     469                 :        3061 :             cx, JS_NewObjectForConstructor(cx, &Base::klass, args));
     470         [ -  + ]:        3061 :         if (!obj)
     471                 :           0 :             return false;
     472                 :             : 
     473                 :        3061 :         JS::RootedObject proto(cx);
     474         [ -  + ]:        3061 :         if (!JS_GetPrototype(cx, obj, &proto))
     475                 :           0 :             return false;
     476                 :             : 
     477                 :        3061 :         Prototype* prototype = resolve_prototype(cx, proto);
     478         [ +  + ]:        3061 :         if (!prototype)
     479                 :           2 :             return false;
     480                 :             : 
     481                 :        3059 :         args.rval().setUndefined();
     482                 :             : 
     483                 :        3059 :         Instance* priv = Instance::new_for_js_object(prototype, obj);
     484                 :             : 
     485                 :             :         {
     486                 :        3059 :             std::string full_name{
     487   [ -  +  +  - ]:        6118 :                 GJS_PROFILER_DYNAMIC_STRING(cx, priv->format_name())};
     488                 :        3059 :             AutoProfilerLabel label{cx, "constructor", full_name};
     489                 :             : 
     490         [ +  + ]:        3059 :             if (!priv->constructor_impl(cx, obj, args))
     491                 :          25 :                 return false;
     492   [ +  +  +  + ]:        3084 :         }
     493                 :             : 
     494                 :        3034 :         static_cast<GIWrapperBase*>(priv)->debug_lifecycle(obj,
     495                 :             :                                                            "JSObject created");
     496                 :             :         gjs_debug_lifecycle(Base::DEBUG_TOPIC, "m_proto is %p",
     497                 :             :                             priv->get_prototype());
     498                 :             : 
     499                 :             :         // We may need to return a value different from obj (for example because
     500                 :             :         // we delegate to another constructor)
     501         [ +  + ]:        3034 :         if (args.rval().isUndefined())
     502                 :         714 :             args.rval().setObject(*obj);
     503                 :        3034 :         return true;
     504                 :        3061 :     }
     505                 :             : 
     506                 :             :     /*
     507                 :             :      * GIWrapperBase::to_string:
     508                 :             :      *
     509                 :             :      * JSNative method connected to the toString() method in JS.
     510                 :             :      */
     511                 :             :     GJS_JSAPI_RETURN_CONVENTION
     512                 :          59 :     static bool to_string(JSContext* cx, unsigned argc, JS::Value* vp) {
     513   [ -  +  -  + ]:          59 :         GJS_CHECK_WRAPPER_PRIV(cx, argc, vp, args, obj, Base, priv);
     514                 :          59 :         return gjs_wrapper_to_string_func(cx, obj, Base::DEBUG_TAG,
     515                 :             :                                           priv->info(), priv->gtype(),
     516                 :          59 :                                           priv->ptr_addr(), args.rval());
     517                 :          59 :     }
     518                 :             : 
     519                 :             :     // Helper methods
     520                 :             : 
     521                 :             :  public:
     522                 :             :     /*
     523                 :             :      * GIWrapperBase::check_is_instance:
     524                 :             :      * @for_what: string used in the exception message if an exception is thrown
     525                 :             :      *
     526                 :             :      * Used in JSNative methods to ensure the passed-in JS object is an instance
     527                 :             :      * and not the prototype. Throws a JS exception if the prototype is passed
     528                 :             :      * in.
     529                 :             :      */
     530                 :             :     GJS_JSAPI_RETURN_CONVENTION
     531                 :      115356 :     bool check_is_instance(JSContext* cx, const char* for_what) const {
     532         [ +  - ]:      115356 :         if (!is_prototype())
     533                 :      115356 :             return true;
     534                 :           0 :         gjs_throw(cx, "Can't %s on %s.prototype; only on instances", for_what,
     535                 :           0 :                   format_name().c_str());
     536                 :           0 :         return false;
     537                 :             :     }
     538                 :             : 
     539                 :             :     /*
     540                 :             :      * GIWrapperBase::to_c_ptr:
     541                 :             :      *
     542                 :             :      * Returns the underlying C pointer of the wrapped object, or throws a JS
     543                 :             :      * exception if that is not possible (for example, the passed-in JS object
     544                 :             :      * is the prototype.)
     545                 :             :      *
     546                 :             :      * Includes a JS typecheck (but without any extra typecheck of the GType or
     547                 :             :      * introspection info that you would get from GIWrapperBase::typecheck(), so
     548                 :             :      * if you want that you still have to do the typecheck before calling this
     549                 :             :      * method.)
     550                 :             :      */
     551                 :             :     template <typename T = void>
     552                 :       54660 :     GJS_JSAPI_RETURN_CONVENTION static T* to_c_ptr(JSContext* cx,
     553                 :             :                                                    JS::HandleObject obj) {
     554                 :             :         Base* priv;
     555   [ +  -  -  + ]:      109320 :         if (!Base::for_js_typecheck(cx, obj, &priv) ||
     556         [ -  + ]:       54660 :             !priv->check_is_instance(cx, "get a C pointer"))
     557                 :           0 :             return nullptr;
     558                 :             : 
     559                 :       54660 :         return static_cast<T*>(priv->to_instance()->ptr());
     560                 :             :     }
     561                 :             : 
     562                 :             :     /*
     563                 :             :      * GIWrapperBase::transfer_to_gi_argument:
     564                 :             :      * @arg: #GIArgument to fill with the value from @obj
     565                 :             :      * @transfer_direction: Either %GI_DIRECTION_IN or %GI_DIRECTION_OUT
     566                 :             :      * @transfer_ownership: #GITransfer value specifying whether @arg should
     567                 :             :      *   copy or acquire a reference to the value or not
     568                 :             :      * @expected_gtype: #GType to perform a typecheck with
     569                 :             :      * @expected_info: Introspection info to perform a typecheck with
     570                 :             :      *
     571                 :             :      * Prepares @arg for passing the value from @obj into C code. It will get a
     572                 :             :      * C pointer from @obj and assign it to @arg's pointer field, taking a
     573                 :             :      * reference with GIWrapperInstance::copy_ptr() if @transfer_direction and
     574                 :             :      * @transfer_ownership indicate that it should.
     575                 :             :      *
     576                 :             :      * Includes a typecheck using GIWrapperBase::typecheck(), to which
     577                 :             :      * @expected_gtype and @expected_info are passed.
     578                 :             :      *
     579                 :             :      * If returning false, then @arg's pointer field is null.
     580                 :             :      */
     581                 :             :     GJS_JSAPI_RETURN_CONVENTION
     582                 :       54052 :     static bool transfer_to_gi_argument(JSContext* cx, JS::HandleObject obj,
     583                 :             :                                         GIArgument* arg,
     584                 :             :                                         GIDirection transfer_direction,
     585                 :             :                                         GITransfer transfer_ownership,
     586                 :             :                                         GType expected_gtype) {
     587                 :       54052 :         g_assert(transfer_direction != GI_DIRECTION_INOUT &&
     588                 :             :                  "transfer_to_gi_argument() must choose between in or out");
     589                 :             : 
     590   [ +  +  +  + ]:      108085 :         if (expected_gtype != G_TYPE_NONE &&
     591         [ +  + ]:       54033 :             !Base::typecheck(cx, obj, expected_gtype)) {
     592                 :           4 :             gjs_arg_unset(arg);
     593                 :           4 :             return false;
     594                 :             :         }
     595                 :             : 
     596                 :       54048 :         gjs_arg_set(arg, Base::to_c_ptr(cx, obj));
     597         [ -  + ]:       54048 :         if (!gjs_arg_get<void*>(arg))
     598                 :           0 :             return false;
     599                 :             : 
     600   [ +  -  +  + ]:       54048 :         if ((transfer_direction == GI_DIRECTION_IN &&
     601         [ -  + ]:       53938 :              transfer_ownership != GI_TRANSFER_NOTHING) ||
     602         [ #  # ]:           0 :             (transfer_direction == GI_DIRECTION_OUT &&
     603                 :             :              transfer_ownership == GI_TRANSFER_EVERYTHING)) {
     604                 :         110 :             gjs_arg_set(arg, Instance::copy_ptr(cx, expected_gtype,
     605                 :             :                                                 gjs_arg_get<void*>(arg)));
     606         [ -  + ]:         110 :             if (!gjs_arg_get<void*>(arg))
     607                 :           0 :                 return false;
     608                 :             :         }
     609                 :             : 
     610                 :       54048 :         return true;
     611                 :             :     }
     612                 :             : 
     613                 :             :     // Public typecheck API
     614                 :             : 
     615                 :             :     /*
     616                 :             :      * GIWrapperBase::typecheck:
     617                 :             :      * @expected_info: (nullable): GI info to check
     618                 :             :      * @expected_type: (nullable): GType to check
     619                 :             :      *
     620                 :             :      * Checks not only that the JS object is of the correct JSClass (like
     621                 :             :      * CWrapperPointerOps::typecheck() does); but also that the object is an
     622                 :             :      * instance, not the prototype; and that the instance's wrapped pointer is
     623                 :             :      * of the correct GType or GI info.
     624                 :             :      *
     625                 :             :      * The overload with a GjsTypecheckNoThrow parameter will not throw a JS
     626                 :             :      * exception if the prototype is passed in or the typecheck fails.
     627                 :             :      */
     628                 :             :     GJS_JSAPI_RETURN_CONVENTION
     629                 :         424 :     static bool typecheck(JSContext* cx, JS::HandleObject object,
     630                 :             :                           GIBaseInfo* expected_info) {
     631                 :             :         Base* priv;
     632   [ +  -  -  + ]:         848 :         if (!Base::for_js_typecheck(cx, object, &priv) ||
     633         [ -  + ]:         424 :             !priv->check_is_instance(cx, "convert to pointer"))
     634                 :           0 :             return false;
     635                 :             : 
     636         [ +  - ]:         424 :         if (priv->to_instance()->typecheck_impl(expected_info))
     637                 :         424 :             return true;
     638                 :             : 
     639                 :           0 :         gjs_throw_custom(cx, JSEXN_TYPEERR, nullptr,
     640                 :             :                          "Object is of type %s - cannot convert to %s.%s",
     641                 :           0 :                          priv->format_name().c_str(),
     642                 :             :                          g_base_info_get_namespace(expected_info),
     643                 :             :                          g_base_info_get_name(expected_info));
     644                 :           0 :         return false;
     645                 :             :     }
     646                 :             :     GJS_JSAPI_RETURN_CONVENTION
     647                 :       56582 :     static bool typecheck(JSContext* cx, JS::HandleObject object,
     648                 :             :                           GType expected_gtype) {
     649                 :             :         Base* priv;
     650   [ +  +  +  + ]:      113160 :         if (!Base::for_js_typecheck(cx, object, &priv) ||
     651         [ -  + ]:       56578 :             !priv->check_is_instance(cx, "convert to pointer"))
     652                 :           4 :             return false;
     653                 :             : 
     654         [ +  + ]:       56578 :         if (priv->to_instance()->typecheck_impl(expected_gtype))
     655                 :       56573 :             return true;
     656                 :             : 
     657                 :          10 :         gjs_throw_custom(cx, JSEXN_TYPEERR, nullptr,
     658                 :             :                          "Object is of type %s - cannot convert to %s",
     659                 :          10 :                          priv->format_name().c_str(),
     660                 :             :                          g_type_name(expected_gtype));
     661                 :           5 :         return false;
     662                 :             :     }
     663                 :             :     template <typename T>
     664                 :             :     [[nodiscard]]
     665                 :         204 :     static bool typecheck(JSContext* cx, JS::HandleObject object,
     666                 :             :                           T expected, GjsTypecheckNoThrow) {
     667                 :         204 :         Base* priv = Base::for_js(cx, object);
     668   [ +  +  -  +  :         204 :         if (!priv || priv->is_prototype())
                   +  + ]
     669                 :          98 :             return false;
     670                 :             : 
     671                 :         106 :         return priv->to_instance()->typecheck_impl(expected);
     672                 :             :     }
     673                 :             : 
     674                 :             :     // Deleting these constructors and assignment operators will also delete
     675                 :             :     // them from derived classes.
     676                 :             :     GIWrapperBase(const GIWrapperBase& other) = delete;
     677                 :             :     GIWrapperBase(GIWrapperBase&& other) = delete;
     678                 :             :     GIWrapperBase& operator=(const GIWrapperBase& other) = delete;
     679                 :             :     GIWrapperBase& operator=(GIWrapperBase&& other) = delete;
     680                 :             : };
     681                 :             : 
     682                 :             : /*
     683                 :             :  * GIWrapperPrototype:
     684                 :             :  *
     685                 :             :  * The specialization of GIWrapperBase which becomes the private data of JS
     686                 :             :  * prototype objects. For example, it is the parent class of BoxedPrototype.
     687                 :             :  *
     688                 :             :  * Classes inheriting from GIWrapperPrototype must declare "friend class
     689                 :             :  * GIWrapperBase" as well as the normal CRTP requirement of "friend class
     690                 :             :  * GIWrapperPrototype", because of the unusual polymorphism scheme, in order for
     691                 :             :  * Base to call methods such as trace_impl().
     692                 :             :  */
     693                 :             : template <class Base, class Prototype, class Instance,
     694                 :             :           typename Info = GIObjectInfo>
     695                 :             : class GIWrapperPrototype : public Base {
     696                 :             :     using GjsAutoPrototype =
     697                 :           0 :         Gjs::AutoPointer<Prototype, void, g_atomic_rc_box_release>;
     698                 :             : 
     699                 :             :  protected:
     700                 :             :     // m_info may be null in the case of JS-defined types, or internal types
     701                 :             :     // not exposed through introspection, such as GLocalFile. Not all subclasses
     702                 :             :     // of GIWrapperPrototype support this. Object and Interface support it in
     703                 :             :     // any case.
     704                 :             :     GI::AutoBaseInfo m_info;
     705                 :             :     GType m_gtype;
     706                 :             : 
     707                 :        1864 :     explicit GIWrapperPrototype(Info* info, GType gtype)
     708                 :        1864 :         : Base(), m_info(info, Gjs::TakeOwnership{}), m_gtype(gtype) {
     709                 :        1864 :         Base::debug_lifecycle("Prototype constructor");
     710                 :        1864 :     }
     711                 :             : 
     712                 :             :     /*
     713                 :             :      * GIWrapperPrototype::init:
     714                 :             :      *
     715                 :             :      * Performs any initialization that cannot be done in the constructor of
     716                 :             :      * GIWrapperPrototype, either because it can fail, or because it can cause a
     717                 :             :      * garbage collection.
     718                 :             :      *
     719                 :             :      * This default implementation does nothing. Override in a subclass if
     720                 :             :      * necessary.
     721                 :             :      */
     722                 :             :     GJS_JSAPI_RETURN_CONVENTION
     723                 :         985 :     bool init(JSContext*) { return true; }
     724                 :             : 
     725                 :             :     // The following four methods are private because they are used only in
     726                 :             :     // create_class().
     727                 :             : 
     728                 :             :  private:
     729                 :             :     /*
     730                 :             :      * GIWrapperPrototype::parent_proto:
     731                 :             :      *
     732                 :             :      * Returns in @proto the parent class's prototype object, or nullptr if
     733                 :             :      * there is none.
     734                 :             :      *
     735                 :             :      * This default implementation is for GObject introspection types that can't
     736                 :             :      * inherit in JS, like Boxed and Union. Override this if the type can
     737                 :             :      * inherit in JS.
     738                 :             :      */
     739                 :             :     GJS_JSAPI_RETURN_CONVENTION
     740                 :        1106 :     bool get_parent_proto(JSContext*, JS::MutableHandleObject proto) const {
     741                 :        1106 :         proto.set(nullptr);
     742                 :        1106 :         return true;
     743                 :             :     }
     744                 :             : 
     745                 :             :     /*
     746                 :             :      * GIWrapperPrototype::constructor_nargs:
     747                 :             :      *
     748                 :             :      * Override this if the type's constructor takes other than 1 argument.
     749                 :             :      */
     750                 :        1746 :     [[nodiscard]] unsigned constructor_nargs() const { return 1; }
     751                 :             : 
     752                 :             :     /*
     753                 :             :      * GIWrapperPrototype::define_jsclass:
     754                 :             :      * @in_object: JSObject on which to define the class constructor as a
     755                 :             :      *   property
     756                 :             :      * @parent_proto: (nullable): prototype of the prototype
     757                 :             :      * @constructor: return location for the constructor function object
     758                 :             :      * @prototype: return location for the prototype object
     759                 :             :      *
     760                 :             :      * Defines a JS class with constructor and prototype, and optionally defines
     761                 :             :      * properties and methods on the prototype object, and methods on the
     762                 :             :      * constructor object.
     763                 :             :      *
     764                 :             :      * By default no properties or methods are defined, but derived classes can
     765                 :             :      * override the GIWrapperBase::proto_properties,
     766                 :             :      * GIWrapperBase::proto_methods, and GIWrapperBase::static_methods members.
     767                 :             :      * Static properties would also be possible but are not used anywhere in GJS
     768                 :             :      * so are not implemented yet.
     769                 :             :      *
     770                 :             :      * Note: no prototype methods are defined if @parent_proto is null.
     771                 :             :      *
     772                 :             :      * Here is a refresher comment on the difference between __proto__ and
     773                 :             :      * prototype that has been in the GJS codebase since forever:
     774                 :             :      *
     775                 :             :      * https://web.archive.org/web/20100716231157/http://egachine.berlios.de/embedding-sm-best-practice/apa.html
     776                 :             :      * https://www.sitepoint.com/javascript-inheritance/
     777                 :             :      * http://www.cs.rit.edu/~atk/JavaScript/manuals/jsobj/
     778                 :             :      *
     779                 :             :      * What we want is: repoobj.Gtk.Window is constructor for a GtkWindow
     780                 :             :      * wrapper JSObject (gjs_define_object_class() is supposed to define Window
     781                 :             :      * in Gtk.)
     782                 :             :      *
     783                 :             :      * Window.prototype contains the methods on Window, e.g. set_default_size()
     784                 :             :      * mywindow.__proto__ is Window.prototype
     785                 :             :      * mywindow.__proto__.__proto__ is Bin.prototype
     786                 :             :      * mywindow.__proto__.__proto__.__proto__ is Container.prototype
     787                 :             :      *
     788                 :             :      * Because Window.prototype is an instance of Window in a sense,
     789                 :             :      * Window.prototype.__proto__ is Window.prototype, just as
     790                 :             :      * mywindow.__proto__ is Window.prototype
     791                 :             :      *
     792                 :             :      * If we do "mywindow = new Window()" then we should get:
     793                 :             :      *     mywindow.__proto__ == Window.prototype
     794                 :             :      * which means "mywindow instanceof Window" is true.
     795                 :             :      *
     796                 :             :      * Remember "Window.prototype" is "the __proto__ of stuff constructed with
     797                 :             :      * new Window()"
     798                 :             :      *
     799                 :             :      * __proto__ is used to search for properties if you do "this.foo", while
     800                 :             :      * .prototype is only relevant for constructors and is used to set __proto__
     801                 :             :      * on new'd objects. So .prototype only makes sense on constructors.
     802                 :             :      *
     803                 :             :      * JS_SetPrototype() and JS_GetPrototype() are for __proto__. To set/get
     804                 :             :      * .prototype, just use the normal property accessors, or JS_InitClass()
     805                 :             :      * sets it up automatically.
     806                 :             :      */
     807                 :             :     GJS_JSAPI_RETURN_CONVENTION
     808                 :        1761 :     bool define_jsclass(JSContext* cx, JS::HandleObject in_object,
     809                 :             :                         JS::HandleObject parent_proto,
     810                 :             :                         JS::MutableHandleObject constructor,
     811                 :             :                         JS::MutableHandleObject prototype) {
     812                 :             :         // The GI namespace is only used to set the JSClass->name field (exposed
     813                 :             :         // by Object.prototype.toString, for example). We can safely set
     814                 :             :         // "unknown" if this is a custom or internal JS class with no GI
     815                 :             :         // namespace, as in that case the name is already globally unique (it's
     816                 :             :         // a GType name).
     817         [ +  + ]:        1761 :         const char* gi_namespace = Base::info() ? Base::ns() : "unknown";
     818                 :             : 
     819                 :        1761 :         unsigned nargs = static_cast<Prototype*>(this)->constructor_nargs();
     820                 :             : 
     821         [ -  + ]:        1761 :         if (!gjs_init_class_dynamic(
     822                 :             :                 cx, in_object, parent_proto, gi_namespace, Base::name(),
     823                 :             :                 &Base::klass, &Base::constructor, nargs, Base::proto_properties,
     824         [ +  + ]:        1761 :                 parent_proto ? nullptr : Base::proto_methods,
     825                 :             :                 Base::static_properties, Base::static_methods, prototype,
     826                 :             :                 constructor))
     827                 :           0 :             return false;
     828                 :             : 
     829                 :        3522 :         gjs_debug(Base::DEBUG_TOPIC,
     830                 :             :                   "Defined class for %s (%s), prototype %p, "
     831                 :             :                   "JSClass %p, in object %p",
     832                 :        1761 :                   Base::name(), Base::type_name(), prototype.get(),
     833                 :        1761 :                   JS::GetClass(prototype), in_object.get());
     834                 :             : 
     835                 :        1761 :         return true;
     836                 :             :     }
     837                 :             : 
     838                 :             :     /*
     839                 :             :      * GIWrapperPrototype::define_static_methods:
     840                 :             :      *
     841                 :             :      * Defines all introspectable static methods on @constructor, including
     842                 :             :      * class methods for objects, and interface methods for interfaces. See
     843                 :             :      * gjs_define_static_methods() for details.
     844                 :             :      *
     845                 :             :      * It requires Prototype to have an info_type_tag member to indicate
     846                 :             :      * the correct template specialization of gjs_define_static_methods().
     847                 :             :      */
     848                 :             :     GJS_JSAPI_RETURN_CONVENTION
     849                 :        1864 :     bool define_static_methods(JSContext* cx, JS::HandleObject constructor) {
     850         [ +  + ]:        1864 :         if (!info())
     851                 :         198 :             return true;  // no introspection means no methods to define
     852                 :        1666 :         return gjs_define_static_methods<Prototype::info_type_tag>(
     853                 :        1666 :             cx, constructor, m_gtype, m_info);
     854                 :             :     }
     855                 :             : 
     856                 :             :     GJS_JSAPI_RETURN_CONVENTION
     857                 :        1864 :     static Prototype* create_prototype(Info* info, GType gtype) {
     858                 :        1864 :         g_assert(gtype != G_TYPE_INVALID);
     859                 :             : 
     860                 :             :         // We have to keep the Prototype in an arcbox because some of its
     861                 :             :         // members are needed in some Instance destructors, e.g. m_gtype to
     862                 :             :         // figure out how to free the Instance's m_ptr, and m_info to figure out
     863                 :             :         // how many bytes to free if it is allocated directly. Storing a
     864                 :             :         // refcount on the prototype is cheaper than storing pointers to m_info
     865                 :             :         // and m_gtype on each instance.
     866                 :        1864 :         Prototype* priv = g_atomic_rc_box_new0(Prototype);
     867                 :        1864 :         new (priv) Prototype(info, gtype);
     868                 :             : 
     869                 :        1864 :         return priv;
     870                 :             :     }
     871                 :             : 
     872                 :             :  public:
     873                 :             :     /**
     874                 :             :      * GIWrapperPrototype::create_class:
     875                 :             :      * @in_object: JSObject on which to define the class constructor as a
     876                 :             :      *   property
     877                 :             :      * @info: (nullable): Introspection info for the class, or null if the class
     878                 :             :      *   has been defined in JS
     879                 :             :      * @gtype: GType for the class
     880                 :             :      * @constructor: return location for the constructor function object
     881                 :             :      * @prototype: return location for the prototype object
     882                 :             :      *
     883                 :             :      * Creates a JS class that wraps a GI pointer, by defining its constructor
     884                 :             :      * function and prototype object. The prototype object is given an instance
     885                 :             :      * of GIWrapperPrototype as its private data, which is also returned.
     886                 :             :      * Basically treat this method as the public constructor.
     887                 :             :      *
     888                 :             :      * Also defines all the requested methods and properties on the prototype
     889                 :             :      * and constructor objects (see define_jsclass()), as well as a `$gtype`
     890                 :             :      * property and a toString() method.
     891                 :             :      *
     892                 :             :      * This method can be overridden and chained up to if the derived class
     893                 :             :      * needs to define more properties on the constructor or prototype objects,
     894                 :             :      * e.g. eager GI properties.
     895                 :             :      */
     896                 :             :     GJS_JSAPI_RETURN_CONVENTION
     897                 :        1761 :     static Prototype* create_class(JSContext* cx, JS::HandleObject in_object,
     898                 :             :                                    Info* info, GType gtype,
     899                 :             :                                    JS::MutableHandleObject constructor,
     900                 :             :                                    JS::MutableHandleObject prototype) {
     901                 :        1761 :         g_assert(in_object);
     902                 :             : 
     903                 :        1761 :         GjsAutoPrototype priv = create_prototype(info, gtype);
     904         [ -  + ]:        1761 :         if (!priv->init(cx))
     905                 :           0 :             return nullptr;
     906                 :             : 
     907                 :        1761 :         JS::RootedObject parent_proto(cx);
     908         [ +  - ]:        3522 :         if (!priv->get_parent_proto(cx, &parent_proto) ||
     909   [ -  +  -  + ]:        3522 :             !priv->define_jsclass(cx, in_object, parent_proto, constructor,
     910                 :             :                                   prototype))
     911                 :           0 :             return nullptr;
     912                 :             : 
     913                 :             :         // Init the private variable of @private before we do anything else. If
     914                 :             :         // a garbage collection or error happens subsequently, then this object
     915                 :             :         // might be traced and we would end up dereferencing a null pointer.
     916                 :        1761 :         Prototype* proto = priv.release();
     917                 :        1761 :         Prototype::init_private(prototype, proto);
     918                 :             : 
     919         [ -  + ]:        1761 :         if (!gjs_wrapper_define_gtype_prop(cx, constructor, gtype))
     920                 :           0 :             return nullptr;
     921                 :             : 
     922                 :             :         // Every class has a toString() with C++ implementation, so define that
     923                 :             :         // without requiring it to be listed in Base::proto_methods
     924         [ +  + ]:        1761 :         if (!parent_proto) {
     925                 :        1186 :             const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
     926         [ -  + ]:        1186 :             if (!JS_DefineFunctionById(cx, prototype, atoms.to_string(),
     927                 :             :                                        &Base::to_string, 0,
     928                 :             :                                        GJS_MODULE_PROP_FLAGS))
     929                 :           0 :                 return nullptr;
     930                 :             :         }
     931                 :             : 
     932         [ -  + ]:        1761 :         if (!proto->define_static_methods(cx, constructor))
     933                 :           0 :             return nullptr;
     934                 :             : 
     935                 :        1761 :         return proto;
     936                 :        1761 :     }
     937                 :             : 
     938                 :             :     GJS_JSAPI_RETURN_CONVENTION
     939                 :         103 :     static Prototype* wrap_class(JSContext* cx, JS::HandleObject in_object,
     940                 :             :                                  Info* info, GType gtype,
     941                 :             :                                  JS::HandleObject constructor,
     942                 :             :                                  JS::MutableHandleObject prototype) {
     943                 :         103 :         g_assert(in_object);
     944                 :             : 
     945                 :         103 :         GjsAutoPrototype priv = create_prototype(info, gtype);
     946         [ -  + ]:         103 :         if (!priv->init(cx))
     947                 :           0 :             return nullptr;
     948                 :             : 
     949                 :         103 :         JS::RootedObject parent_proto(cx);
     950         [ -  + ]:         103 :         if (!priv->get_parent_proto(cx, &parent_proto))
     951                 :           0 :             return nullptr;
     952                 :             : 
     953         [ +  + ]:         103 :         if (parent_proto) {
     954                 :         100 :             prototype.set(
     955                 :         100 :                 JS_NewObjectWithGivenProto(cx, &Base::klass, parent_proto));
     956                 :             :         } else {
     957                 :           3 :             prototype.set(JS_NewObject(cx, &Base::klass));
     958                 :             :         }
     959                 :             : 
     960         [ -  + ]:         103 :         if (!prototype)
     961                 :           0 :             return nullptr;
     962                 :             : 
     963                 :         103 :         Prototype* proto = priv.release();
     964                 :         103 :         Prototype::init_private(prototype, proto);
     965                 :             : 
     966         [ -  + ]:         103 :         if (!proto->define_static_methods(cx, constructor))
     967                 :           0 :             return nullptr;
     968                 :             : 
     969                 :         103 :         Gjs::AutoChar class_name{g_strdup_printf("%s", proto->name())};
     970         [ -  + ]:         103 :         if (!JS_DefineProperty(cx, in_object, class_name, constructor,
     971                 :             :                                GJS_MODULE_PROP_FLAGS))
     972                 :           0 :             return nullptr;
     973                 :             : 
     974                 :         103 :         return proto;
     975                 :         103 :     }
     976                 :             : 
     977                 :             :     // Methods to get an existing Prototype
     978                 :             : 
     979                 :             :     /*
     980                 :             :      * GIWrapperPrototype::for_js:
     981                 :             :      *
     982                 :             :      * Like Base::for_js(), but asserts that the returned private struct is a
     983                 :             :      * Prototype and not an Instance.
     984                 :             :      */
     985                 :        3807 :     [[nodiscard]] static Prototype* for_js(JSContext* cx,
     986                 :             :                                            JS::HandleObject wrapper) {
     987                 :        3807 :         return Base::for_js(cx, wrapper)->to_prototype();
     988                 :             :     }
     989                 :             : 
     990                 :             :     /*
     991                 :             :      * GIWrapperPrototype::for_js_prototype:
     992                 :             :      *
     993                 :             :      * Gets the Prototype private data from to @wrapper.prototype. Cannot return
     994                 :             :      * null, and asserts so.
     995                 :             :      */
     996                 :       19078 :     [[nodiscard]] static Prototype* for_js_prototype(JSContext* cx,
     997                 :             :                                                      JS::HandleObject wrapper) {
     998                 :       19078 :         JS::RootedObject proto(cx);
     999                 :       19078 :         JS_GetPrototype(cx, wrapper, &proto);
    1000                 :       19078 :         Base* retval = Base::for_js(cx, proto);
    1001                 :       19078 :         g_assert(retval);
    1002                 :       19078 :         return retval->to_prototype();
    1003                 :       19078 :     }
    1004                 :             : 
    1005                 :             :     // Accessors
    1006                 :             : 
    1007                 :       46391 :     [[nodiscard]] Info* info() const { return m_info; }
    1008                 :      234578 :     [[nodiscard]] GType gtype() const { return m_gtype; }
    1009                 :             : 
    1010                 :             :     // Helper methods
    1011                 :             : 
    1012                 :             :  private:
    1013                 :        1843 :     static void destroy_notify(void* ptr) {
    1014                 :        1843 :         static_cast<Prototype*>(ptr)->~Prototype();
    1015                 :        1843 :     }
    1016                 :             : 
    1017                 :             :  public:
    1018                 :       22821 :     Prototype* acquire(void) {
    1019                 :       22821 :         g_atomic_rc_box_acquire(this);
    1020                 :       22821 :         return static_cast<Prototype*>(this);
    1021                 :             :     }
    1022                 :             : 
    1023                 :       24660 :     void release(void) { g_atomic_rc_box_release_full(this, &destroy_notify); }
    1024                 :             : 
    1025                 :             :     // JSClass operations
    1026                 :             : 
    1027                 :             :  protected:
    1028                 :        1843 :     void finalize_impl(JS::GCContext*, JSObject*) { release(); }
    1029                 :             : 
    1030                 :             :     // Override if necessary
    1031                 :          14 :     void trace_impl(JSTracer*) {}
    1032                 :             : };
    1033                 :             : 
    1034                 :             : using GIWrappedUnowned = void;
    1035                 :             : namespace Gjs {
    1036                 :             : template <>
    1037                 :             : struct SmartPointer<GIWrappedUnowned>
    1038                 :             :     : AutoPointer<GIWrappedUnowned, void, nullptr> {
    1039                 :             :     using AutoPointer::AutoPointer;
    1040                 :             : };
    1041                 :             : }  // namespace Gjs
    1042                 :             : 
    1043                 :             : /*
    1044                 :             :  * GIWrapperInstance:
    1045                 :             :  *
    1046                 :             :  * The specialization of GIWrapperBase which becomes the private data of JS
    1047                 :             :  * instance objects. For example, it is the parent class of BoxedInstance.
    1048                 :             :  *
    1049                 :             :  * Classes inheriting from GIWrapperInstance must declare "friend class
    1050                 :             :  * GIWrapperBase" as well as the normal CRTP requirement of "friend class
    1051                 :             :  * GIWrapperInstance", because of the unusual polymorphism scheme, in order for
    1052                 :             :  * Base to call methods such as trace_impl().
    1053                 :             :  */
    1054                 :             : template <class Base, class Prototype, class Instance,
    1055                 :             :           typename Wrapped = GIWrappedUnowned>
    1056                 :             : class GIWrapperInstance : public Base {
    1057                 :             :  protected:
    1058                 :             :     Gjs::SmartPointer<Wrapped> m_ptr;
    1059                 :             : 
    1060                 :       22821 :     explicit GIWrapperInstance(Prototype* prototype, JS::HandleObject obj)
    1061                 :       22821 :         : Base(prototype), m_ptr(nullptr) {
    1062                 :       22821 :         Base::m_proto->acquire();
    1063                 :       22821 :         Base::GIWrapperBase::debug_lifecycle(obj, "Instance constructor");
    1064                 :       22821 :     }
    1065                 :             : 
    1066                 :       22817 :     ~GIWrapperInstance(void) { Base::m_proto->release(); }
    1067                 :             : 
    1068                 :             :  public:
    1069                 :             :     /*
    1070                 :             :      * GIWrapperInstance::new_for_js_object:
    1071                 :             :      *
    1072                 :             :      * Creates a GIWrapperInstance and associates it with @obj as its private
    1073                 :             :      * data. This is called by the JS constructor.
    1074                 :             :      */
    1075                 :       19078 :     [[nodiscard]] static Instance* new_for_js_object(JSContext* cx,
    1076                 :             :                                                      JS::HandleObject obj) {
    1077                 :       19078 :         Prototype* prototype = Prototype::for_js_prototype(cx, obj);
    1078                 :       19078 :         auto* priv = new Instance(prototype, obj);
    1079                 :             : 
    1080                 :             :         // Init the private variable before we do anything else. If a garbage
    1081                 :             :         // collection happens when calling the constructor, then this object
    1082                 :             :         // might be traced and we would end up dereferencing a null pointer.
    1083                 :       19078 :         Instance::init_private(obj, priv);
    1084                 :             : 
    1085                 :       19078 :         return priv;
    1086                 :             :     }
    1087                 :             : 
    1088                 :        3059 :     [[nodiscard]] static Instance* new_for_js_object(Prototype* prototype,
    1089                 :             :                                                      JS::HandleObject obj) {
    1090                 :        3059 :         auto* priv = new Instance(prototype, obj);
    1091                 :             : 
    1092                 :        3059 :         Instance::init_private(obj, priv);
    1093                 :             : 
    1094                 :        3059 :         return priv;
    1095                 :             :     }
    1096                 :             : 
    1097                 :             :     // Method to get an existing Instance
    1098                 :             : 
    1099                 :             :     /*
    1100                 :             :      * GIWrapperInstance::for_js:
    1101                 :             :      *
    1102                 :             :      * Like Base::for_js(), but asserts that the returned private struct is an
    1103                 :             :      * Instance and not a Prototype.
    1104                 :             :      */
    1105                 :          45 :     [[nodiscard]] static Instance* for_js(JSContext* cx,
    1106                 :             :                                           JS::HandleObject wrapper) {
    1107                 :          45 :         return Base::for_js(cx, wrapper)->to_instance();
    1108                 :             :     }
    1109                 :             : 
    1110                 :             :     // Accessors
    1111                 :             : 
    1112                 :       57137 :     [[nodiscard]] Wrapped* ptr() const { return m_ptr; }
    1113                 :             :     /*
    1114                 :             :      * GIWrapperInstance::raw_ptr:
    1115                 :             :      *
    1116                 :             :      * Like ptr(), but returns a byte pointer for use in byte arithmetic.
    1117                 :             :      */
    1118                 :          19 :     [[nodiscard]] uint8_t* raw_ptr() const {
    1119                 :          19 :         return reinterpret_cast<uint8_t*>(ptr());
    1120                 :             :     }
    1121                 :             : 
    1122                 :             :     // JSClass operations
    1123                 :             : 
    1124                 :             :  protected:
    1125                 :       22817 :     void finalize_impl(JS::GCContext*, JSObject*) {
    1126         [ +  - ]:       22817 :         delete static_cast<Instance*>(this);
    1127                 :       22817 :     }
    1128                 :             : 
    1129                 :             :     // Override if necessary
    1130                 :         332 :     void trace_impl(JSTracer*) {}
    1131                 :             : 
    1132                 :             :     // Helper methods
    1133                 :             : 
    1134                 :             :     /*
    1135                 :             :      * GIWrapperInstance::typecheck_impl:
    1136                 :             :      *
    1137                 :             :      * See GIWrapperBase::typecheck(). Checks that the instance's wrapped
    1138                 :             :      * pointer is of the correct GType or GI info. Does not throw a JS
    1139                 :             :      * exception.
    1140                 :             :      *
    1141                 :             :      * It's possible to override typecheck_impl() if you need an extra step in
    1142                 :             :      * the check.
    1143                 :             :      */
    1144                 :             :     [[nodiscard]]
    1145                 :         466 :     bool typecheck_impl(GIBaseInfo* expected_info) const {
    1146                 :         466 :         g_assert(expected_info &&
    1147                 :             :                  "should not call typecheck_impl() without introspection info");
    1148                 :         466 :         return g_base_info_equal(Base::info(), expected_info);
    1149                 :             :     }
    1150                 :             :     [[nodiscard]]
    1151                 :       56642 :     bool typecheck_impl(GType expected_gtype) const {
    1152                 :       56642 :         g_assert(expected_gtype != G_TYPE_NONE &&
    1153                 :             :                  "should not call typecheck_impl() without a real GType");
    1154   [ +  +  +  + ]:       56642 :         return g_type_is_a(Base::gtype(), expected_gtype);
    1155                 :             :     }
    1156                 :             : };
    1157                 :             : 
    1158                 :             : #endif  // GI_WRAPPERUTILS_H_
        

Generated by: LCOV version 2.0-1