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

Generated by: LCOV version 2.0-1