LCOV - code coverage report
Current view: top level - gi - cwrapper.h (source / functions) Coverage Total Hit
Test: gjs- Code Coverage Lines: 80.4 % 148 119
Test Date: 2024-09-12 04:39:42 Functions: 56.9 % 204 116
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 53.0 % 66 35

             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: 2020 Philip Chimento <philip.chimento@gmail.com>
       4                 :             : 
       5                 :             : #pragma once
       6                 :             : 
       7                 :             : #include <config.h>
       8                 :             : 
       9                 :             : #include <assert.h>
      10                 :             : #include <stddef.h>  // for size_t
      11                 :             : 
      12                 :             : #include <type_traits>  // for integral_constant
      13                 :             : 
      14                 :             : #include <glib-object.h>  // for GType
      15                 :             : 
      16                 :             : #include <js/CallArgs.h>
      17                 :             : #include <js/Class.h>
      18                 :             : #include <js/ErrorReport.h>  // for JSEXN_TYPEERR
      19                 :             : #include <js/GCVector.h>     // for MutableHandleIdVector
      20                 :             : #include <js/GlobalObject.h>  // for CurrentGlobalOrNull
      21                 :             : #include <js/Id.h>
      22                 :             : #include <js/Object.h>  // for GetClass
      23                 :             : #include <js/PropertyAndElement.h>
      24                 :             : #include <js/RootingAPI.h>
      25                 :             : #include <js/TypeDecls.h>
      26                 :             : #include <js/Value.h>
      27                 :             : #include <jsapi.h>  // for JSFUN_CONSTRUCTOR, JS_NewPlainObject, JS_GetFuncti...
      28                 :             : #include <jspubtd.h>  // for JSProto_Object, JSProtoKey
      29                 :             : 
      30                 :             : #include "gjs/jsapi-util.h"
      31                 :             : #include "gjs/macros.h"
      32                 :             : #include "util/log.h"
      33                 :             : 
      34                 :             : struct JSFunctionSpec;
      35                 :             : struct JSPropertySpec;
      36                 :             : 
      37                 :             : // gi/cwrapper.h - template implementing a JS object that wraps a C pointer.
      38                 :             : // This template is used for many of the special objects in GJS. It contains
      39                 :             : // functionality such as storing the class's prototype in a global slot, where
      40                 :             : // it can be easily retrieved in order to create new objects.
      41                 :             : 
      42                 :             : /*
      43                 :             :  * GJS_CHECK_WRAPPER_PRIV:
      44                 :             :  * @cx: JSContext pointer passed into JSNative function
      45                 :             :  * @argc: Number of arguments passed into JSNative function
      46                 :             :  * @vp: Argument value array passed into JSNative function
      47                 :             :  * @args: Name for JS::CallArgs variable defined by this code snippet
      48                 :             :  * @thisobj: Name for JS::RootedObject variable referring to function's this
      49                 :             :  * @type: Type of private data
      50                 :             :  * @priv: Name for private data variable defined by this code snippet
      51                 :             :  *
      52                 :             :  * A convenience macro for getting the private data from GJS classes using
      53                 :             :  * CWrapper or GIWrapper.
      54                 :             :  * Throws an error and returns false if the 'this' object is not the right type.
      55                 :             :  * Use in any JSNative function.
      56                 :             :  */
      57                 :             : #define GJS_CHECK_WRAPPER_PRIV(cx, argc, vp, args, thisobj, type, priv) \
      58                 :             :     GJS_GET_THIS(cx, argc, vp, args, thisobj);                          \
      59                 :             :     type* priv;                                                         \
      60                 :             :     if (!type::for_js_typecheck(cx, thisobj, &priv, &args))             \
      61                 :             :         return false;
      62                 :             : 
      63                 :             : GJS_JSAPI_RETURN_CONVENTION
      64                 :             : bool gjs_wrapper_define_gtype_prop(JSContext* cx, JS::HandleObject constructor,
      65                 :             :                                    GType gtype);
      66                 :             : 
      67                 :             : /*
      68                 :             :  * CWrapperPointerOps:
      69                 :             :  *
      70                 :             :  * This class contains methods that are common to both CWrapper and
      71                 :             :  * GIWrapperBase, for retrieving the wrapped C pointer out of the JS object.
      72                 :             :  */
      73                 :             : template <class Base, typename Wrapped = Base>
      74                 :             : class CWrapperPointerOps {
      75                 :             :  public:
      76                 :             :     /*
      77                 :             :      * CWrapperPointerOps::for_js:
      78                 :             :      *
      79                 :             :      * Gets the wrapped C pointer belonging to a particular JS object wrapper.
      80                 :             :      * Checks that the wrapper object has the right JSClass (Base::klass).
      81                 :             :      * A null return value means either that the object didn't have the right
      82                 :             :      * class, or that no private data has been set yet on the wrapper. To
      83                 :             :      * distinguish between these two cases, use for_js_typecheck().
      84                 :             :      */
      85                 :      108280 :     [[nodiscard]] static Wrapped* for_js(JSContext* cx,
      86                 :             :                                          JS::HandleObject wrapper) {
      87         [ +  + ]:      108280 :         if (!JS_InstanceOf(cx, wrapper, &Base::klass, nullptr))
      88                 :        1022 :             return nullptr;
      89                 :             : 
      90                 :      107258 :         return JS::GetMaybePtrFromReservedSlot<Wrapped>(wrapper, POINTER);
      91                 :             :     }
      92                 :             : 
      93                 :             :     /*
      94                 :             :      * CWrapperPointerOps::typecheck:
      95                 :             :      *
      96                 :             :      * Checks if the given wrapper object has the right JSClass (Base::klass).
      97                 :             :      */
      98                 :      132560 :     [[nodiscard]] static bool typecheck(JSContext* cx, JS::HandleObject wrapper,
      99                 :             :                                         JS::CallArgs* args = nullptr) {
     100                 :      132560 :         return JS_InstanceOf(cx, wrapper, &Base::klass, args);
     101                 :             :     }
     102                 :             : 
     103                 :             :     /*
     104                 :             :      * CWrapperPointerOps::for_js_typecheck:
     105                 :             :      *
     106                 :             :      * Like for_js(), only throws a JS exception if the wrapper object has the
     107                 :             :      * wrong class. Use in JSNative functions, where you have access to a
     108                 :             :      * JS::CallArgs. The exception message will mention args.callee.
     109                 :             :      *
     110                 :             :      * The second overload can be used when you don't have access to an
     111                 :             :      * instance of JS::CallArgs. The exception message will be generic.
     112                 :             :      */
     113                 :             :     GJS_JSAPI_RETURN_CONVENTION
     114                 :       54821 :     static bool for_js_typecheck(JSContext* cx, JS::HandleObject wrapper,
     115                 :             :                                  Wrapped** out, JS::CallArgs* args) {
     116         [ -  + ]:       54821 :         if (!typecheck(cx, wrapper, args))
     117                 :           0 :             return false;
     118                 :       54821 :         *out = for_js_nocheck(wrapper);
     119                 :       54821 :         return true;
     120                 :             :     }
     121                 :             :     GJS_JSAPI_RETURN_CONVENTION
     122                 :       77739 :     static bool for_js_typecheck(JSContext* cx, JS::HandleObject wrapper,
     123                 :             :                                  Wrapped** out) {
     124         [ +  + ]:       77739 :         if (!typecheck(cx, wrapper)) {
     125                 :           1 :             const JSClass* obj_class = JS::GetClass(wrapper);
     126                 :           1 :             gjs_throw_custom(cx, JSEXN_TYPEERR, nullptr,
     127                 :             :                              "Object %p is not a subclass of %s, it's a %s",
     128                 :           1 :                              wrapper.get(), Base::klass.name, obj_class->name);
     129                 :           1 :             return false;
     130                 :             :         }
     131                 :       77738 :         *out = for_js_nocheck(wrapper);
     132                 :       77738 :         return true;
     133                 :             :     }
     134                 :             : 
     135                 :             :     /*
     136                 :             :      * CWrapperPointerOps::for_js_nocheck:
     137                 :             :      *
     138                 :             :      * Use when you don't have a JSContext* available. This method is infallible
     139                 :             :      * and cannot trigger a GC, so it's safe to use from finalize() and trace().
     140                 :             :      * (It can return null if no private data has been set yet on the wrapper.)
     141                 :             :      */
     142                 :      168837 :     [[nodiscard]] static Wrapped* for_js_nocheck(JSObject* wrapper) {
     143                 :      168837 :         return JS::GetMaybePtrFromReservedSlot<Wrapped>(wrapper, POINTER);
     144                 :             :     }
     145                 :             : 
     146                 :             :  protected:
     147                 :             :     // The first reserved slot always stores the private pointer.
     148                 :             :     static const size_t POINTER = 0;
     149                 :             : 
     150                 :             :     /*
     151                 :             :      * CWrapperPointerOps::has_private:
     152                 :             :      *
     153                 :             :      * Returns true if a private C pointer has already been associated with the
     154                 :             :      * wrapper object.
     155                 :             :      */
     156                 :       31510 :     [[nodiscard]] static bool has_private(JSObject* wrapper) {
     157                 :       31510 :         return !!JS::GetMaybePtrFromReservedSlot<Wrapped>(wrapper, POINTER);
     158                 :             :     }
     159                 :             : 
     160                 :             :     /*
     161                 :             :      * CWrapperPointerOps::init_private:
     162                 :             :      *
     163                 :             :      * Call this to initialize the wrapper object's private C pointer. The
     164                 :             :      * pointer should not be null. This should not be called twice, without
     165                 :             :      * calling unset_private() in between.
     166                 :             :      */
     167                 :       31510 :     static void init_private(JSObject* wrapper, Wrapped* ptr) {
     168         [ +  - ]:       31510 :         assert(!has_private(wrapper) &&
     169                 :             :                "wrapper object should be a fresh object");
     170         [ -  + ]:       31510 :         assert(ptr && "private pointer should not be null, use unset_private");
     171                 :       31510 :         JS::SetReservedSlot(wrapper, POINTER, JS::PrivateValue(ptr));
     172                 :       31510 :     }
     173                 :             : 
     174                 :             :     /*
     175                 :             :      * CWrapperPointerOps::unset_private:
     176                 :             :      *
     177                 :             :      * Call this to remove the wrapper object's private C pointer. After calling
     178                 :             :      * this, it's okay to call init_private() again.
     179                 :             :      */
     180                 :       31264 :     static void unset_private(JSObject* wrapper) {
     181                 :       31264 :         JS::SetReservedSlot(wrapper, POINTER, JS::UndefinedValue());
     182                 :       31264 :     }
     183                 :             : };
     184                 :             : 
     185                 :             : /*
     186                 :             :  * CWrapper:
     187                 :             :  *
     188                 :             :  * This template implements a JS object that wraps a C pointer, stores its
     189                 :             :  * prototype in a global slot, and includes some optional functionality.
     190                 :             :  *
     191                 :             :  * If you derive from this class, you must implement:
     192                 :             :  *  - static constexpr GjsGlobalSlot PROTOTYPE_SLOT: global slot that the
     193                 :             :  *    prototype will be stored in
     194                 :             :  *  - static constexpr GjsDebugTopic DEBUG_TOPIC: debug log domain
     195                 :             :  *  - static constexpr JSClass klass: see documentation in SpiderMonkey; the
     196                 :             :  *    class may have JSClassOps (see below under CWrapper::class_ops) but must
     197                 :             :  *    at least have its js::ClassSpec member set. The members of js::ClassSpec
     198                 :             :  *    are createConstructor, createPrototype, constructorFunctions,
     199                 :             :  *    constructorProperties, prototypeFunctions, prototypeProperties,
     200                 :             :  *    finishInit, and flags.
     201                 :             :  *  - static Wrapped* constructor_impl(JSContext*, const JS::CallArgs&): custom
     202                 :             :  *    constructor functionality. If your JS object doesn't need a constructor
     203                 :             :  *    (i.e. user code can't use the `new` operator on it) then you can skip this
     204                 :             :  *    one, and include js::ClassSpec::DontDefineConstructor in your
     205                 :             :  *    class_spec's flags member.
     206                 :             :  *  - static constexpr unsigned constructor_nargs: number of arguments that the
     207                 :             :  *    constructor takes. If you implement constructor_impl() then also add this.
     208                 :             :  *  - void finalize_impl(JS::GCContext*, Wrapped*): called when the JS object is
     209                 :             :  *    garbage collected, use this to free the C pointer and do any other cleanup
     210                 :             :  *
     211                 :             :  * Add optional functionality by setting members of class_spec:
     212                 :             :  *  - createConstructor: the default is to create a constructor function that
     213                 :             :  *    calls constructor_impl(), unless flags includes DontDefineConstructor. If
     214                 :             :  *    you need something else, set this member.
     215                 :             :  *  - createPrototype: the default is to use a plain object as the prototype. If
     216                 :             :  *    you need something else, set this member.
     217                 :             :  *  - constructorFunctions: If the class has static methods, set this member.
     218                 :             :  *  - constructorProperties: If the class has static properties, set this
     219                 :             :  *    member.
     220                 :             :  *  - prototypeFunctions: If the class has methods, set this member.
     221                 :             :  *  - prototypeProperties: If the class has properties, set this member.
     222                 :             :  *  - finishInit: If you need to do any other initialization on the prototype or
     223                 :             :  *    the constructor object, set this member.
     224                 :             :  *  - flags: Specify DontDefineConstructor here if you don't want a user-visible
     225                 :             :  *    constructor.
     226                 :             :  *
     227                 :             :  * You may override CWrapper::class_ops if you want to opt in to more JSClass
     228                 :             :  * operations. In that case, CWrapper includes some optional functionality:
     229                 :             :  *  - resolve: include &resolve in your class_ops, and implement
     230                 :             :  *    bool resolve_impl(JSContext*, JS::HandleObject, JS::HandleId, bool*).
     231                 :             :  *  - new enumerate: include &new_enumerate in your class_ops, and implement
     232                 :             :  *    bool new_enumerate_impl(JSContext*, JS::HandleObject,
     233                 :             :  *    JS::MutableHandleIdVector, bool).
     234                 :             :  *
     235                 :             :  * This template uses the Curiously Recurring Template Pattern (CRTP), which
     236                 :             :  * requires inheriting classes to declare themselves friends of the parent
     237                 :             :  * class, so that the parent class can call their private methods.
     238                 :             :  *
     239                 :             :  * For more information about the CRTP, the Wikipedia article is informative:
     240                 :             :  * https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
     241                 :             :  */
     242                 :             : template <class Base, typename Wrapped = Base>
     243                 :             : class CWrapper : public CWrapperPointerOps<Base, Wrapped> {
     244                 :             :     GJS_JSAPI_RETURN_CONVENTION
     245                 :           2 :     static bool constructor(JSContext* cx, unsigned argc, JS::Value* vp) {
     246                 :           2 :         JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
     247                 :             : 
     248         [ -  + ]:           2 :         if (!args.isConstructing()) {
     249                 :           0 :             gjs_throw_constructor_error(cx);
     250                 :           0 :             return false;
     251                 :             :         }
     252                 :           2 :         JS::RootedObject object(
     253                 :           2 :             cx, JS_NewObjectForConstructor(cx, &Base::klass, args));
     254         [ -  + ]:           2 :         if (!object)
     255                 :           0 :             return false;
     256                 :             : 
     257                 :           2 :         Wrapped* priv = Base::constructor_impl(cx, args);
     258         [ -  + ]:           2 :         if (!priv)
     259                 :           0 :             return false;
     260                 :           2 :         CWrapperPointerOps<Base, Wrapped>::init_private(object, priv);
     261                 :             : 
     262                 :           2 :         args.rval().setObject(*object);
     263                 :           2 :         return true;
     264                 :           2 :     }
     265                 :             : 
     266                 :             :     GJS_JSAPI_RETURN_CONVENTION
     267                 :           0 :     static bool abstract_constructor(JSContext* cx, unsigned argc,
     268                 :             :                                      JS::Value* vp) {
     269                 :           0 :         JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
     270                 :           0 :         gjs_throw_abstract_constructor_error(cx, args);
     271                 :           0 :         return false;
     272                 :             :     }
     273                 :             : 
     274                 :             :     // Debug methods, no-op unless verbose logging is compiled in
     275                 :             : 
     276                 :             :  protected:
     277                 :       22748 :     static void debug_lifecycle(
     278                 :             :         const void* wrapped_ptr GJS_USED_VERBOSE_LIFECYCLE,
     279                 :             :         const void* obj GJS_USED_VERBOSE_LIFECYCLE,
     280                 :             :         const char* message GJS_USED_VERBOSE_LIFECYCLE) {
     281                 :             :         gjs_debug_lifecycle(Base::DEBUG_TOPIC, "[%p: JS wrapper %p] %s",
     282                 :             :                             wrapped_ptr, obj, message);
     283                 :       22748 :     }
     284                 :           4 :     void debug_jsprop(const char* message GJS_USED_VERBOSE_PROPS,
     285                 :             :                       const char* id GJS_USED_VERBOSE_PROPS,
     286                 :             :                       const void* obj GJS_USED_VERBOSE_PROPS) const {
     287                 :             :         gjs_debug_jsprop(Base::DEBUG_TOPIC, "[%p: JS wrapper %p] %s prop %s",
     288                 :             :                          this, obj, message, id);
     289                 :           4 :     }
     290                 :       12386 :     void debug_jsprop(const char* message, jsid id, const void* obj) const {
     291                 :             :         if constexpr (GJS_VERBOSE_ENABLE_PROPS)
     292                 :             :             debug_jsprop(message, gjs_debug_id(id).c_str(), obj);
     293                 :       12386 :     }
     294                 :             : 
     295                 :       12690 :     static void finalize(JS::GCContext* gcx, JSObject* obj) {
     296                 :       12690 :         Wrapped* priv = Base::for_js_nocheck(obj);
     297                 :             : 
     298                 :             :         // Call only CWrapper's original method here, not any overrides; e.g.,
     299                 :             :         // we don't want to deal with a read barrier.
     300                 :       12690 :         CWrapper::debug_lifecycle(priv, obj, "Finalize");
     301                 :             : 
     302                 :       12690 :         Base::finalize_impl(gcx, priv);
     303                 :             : 
     304                 :       12690 :         CWrapperPointerOps<Base, Wrapped>::unset_private(obj);
     305                 :       12690 :     }
     306                 :             : 
     307                 :             :     static constexpr JSClassOps class_ops = {
     308                 :             :         nullptr,  // addProperty
     309                 :             :         nullptr,  // deleteProperty
     310                 :             :         nullptr,  // enumerate
     311                 :             :         nullptr,  // newEnumerate
     312                 :             :         nullptr,  // resolve
     313                 :             :         nullptr,  // mayResolve
     314                 :             :         &CWrapper::finalize,
     315                 :             :     };
     316                 :             : 
     317                 :             :     /*
     318                 :             :      * CWrapper::create_abstract_constructor:
     319                 :             :      *
     320                 :             :      * This function can be used as the createConstructor member of class_ops.
     321                 :             :      * It creates a constructor that always throws if it is the new.target. Use
     322                 :             :      * it if you do need a constructor object to exist (for example, if it has
     323                 :             :      * static methods) but you don't want it to be able to be called.
     324                 :             :      */
     325                 :             :     GJS_JSAPI_RETURN_CONVENTION
     326                 :           5 :     static JSObject* create_abstract_constructor(JSContext* cx, JSProtoKey) {
     327                 :           5 :         return JS_GetFunctionObject(
     328                 :             :             JS_NewFunction(cx, &Base::abstract_constructor, 0,
     329                 :          10 :                            JSFUN_CONSTRUCTOR, Base::klass.name));
     330                 :             :     }
     331                 :             : 
     332                 :             :     /*
     333                 :             :      * CWrapper::define_gtype_prop:
     334                 :             :      *
     335                 :             :      * This function can be used as the finishInit member of class_ops. It
     336                 :             :      * defines a '$gtype' property on the constructor. If you use it, you must
     337                 :             :      * implement a gtype() static method that returns the GType to define.
     338                 :             :      */
     339                 :             :     GJS_JSAPI_RETURN_CONVENTION
     340                 :          13 :     static bool define_gtype_prop(JSContext* cx, JS::HandleObject ctor,
     341                 :             :                                   JS::HandleObject proto [[maybe_unused]]) {
     342                 :          13 :         return gjs_wrapper_define_gtype_prop(cx, ctor, Base::gtype());
     343                 :             :     }
     344                 :             : 
     345                 :             :     // Used to get the prototype when it is guaranteed to have already been
     346                 :             :     // created
     347                 :             :     GJS_JSAPI_RETURN_CONVENTION
     348                 :          10 :     static JSObject* prototype(JSContext* cx) {
     349                 :          10 :         JSObject* global = JS::CurrentGlobalOrNull(cx);
     350         [ -  + ]:          10 :         assert(global && "Must be in a realm to call prototype()");
     351                 :          10 :         JS::RootedValue v_proto(
     352                 :          10 :             cx, gjs_get_global_slot(global, Base::PROTOTYPE_SLOT));
     353         [ +  - ]:          10 :         assert(!v_proto.isUndefined() &&
     354                 :             :                "create_prototype() must be called before prototype()");
     355         [ +  - ]:          10 :         assert(v_proto.isObject() &&
     356                 :             :                "Someone stored some weird value in a global slot");
     357                 :          10 :         return &v_proto.toObject();
     358                 :          10 :     }
     359                 :             : 
     360                 :             :     GJS_JSAPI_RETURN_CONVENTION
     361                 :       12386 :     static bool resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
     362                 :             :                         bool* resolved) {
     363                 :       12386 :         Wrapped* priv = CWrapperPointerOps<Base, Wrapped>::for_js(cx, obj);
     364         [ -  + ]:       12386 :         assert(priv && "resolve called on wrong object");
     365                 :       12386 :         priv->debug_jsprop("Resolve hook", id, obj);
     366                 :       12386 :         return priv->resolve_impl(cx, obj, id, resolved);
     367                 :             :     }
     368                 :             : 
     369                 :             :     GJS_JSAPI_RETURN_CONVENTION
     370                 :           4 :     static bool new_enumerate(JSContext* cx, JS::HandleObject obj,
     371                 :             :                               JS::MutableHandleIdVector properties,
     372                 :             :                               bool only_enumerable) {
     373                 :           4 :         Wrapped* priv = CWrapperPointerOps<Base, Wrapped>::for_js(cx, obj);
     374         [ -  + ]:           4 :         assert(priv && "enumerate called on wrong object");
     375                 :           4 :         priv->debug_jsprop("Enumerate hook", "(all)", obj);
     376                 :           4 :         return priv->new_enumerate_impl(cx, obj, properties, only_enumerable);
     377                 :             :     }
     378                 :             : 
     379                 :             :  public:
     380                 :             :     /*
     381                 :             :      * CWrapper::create_prototype:
     382                 :             :      * @module: Object on which to define the constructor as a property, or
     383                 :             :      *   the global object if not given
     384                 :             :      *
     385                 :             :      * Create the class's prototype and store it in the global slot, or
     386                 :             :      * retrieve it if it has already been created.
     387                 :             :      *
     388                 :             :      * Unless DontDefineConstructor is in class_ops.flags, also create the
     389                 :             :      * class's constructor, and define it as a property on @module.
     390                 :             :      */
     391                 :             :     GJS_JSAPI_RETURN_CONVENTION
     392                 :       12924 :     static JSObject* create_prototype(JSContext* cx,
     393                 :             :                                       JS::HandleObject module = nullptr) {
     394                 :       12924 :         JSObject* global = JS::CurrentGlobalOrNull(cx);
     395         [ -  + ]:       12924 :         assert(global && "Must be in a realm to call create_prototype()");
     396                 :             : 
     397                 :             :         // If we've been here more than once, we already have the proto
     398                 :       12924 :         JS::RootedValue v_proto(
     399                 :       12924 :             cx, gjs_get_global_slot(global, Base::PROTOTYPE_SLOT));
     400         [ +  + ]:       12924 :         if (!v_proto.isUndefined()) {
     401         [ +  - ]:       12723 :             assert(v_proto.isObject() &&
     402                 :             :                    "Someone stored some weird value in a global slot");
     403                 :       12723 :             return &v_proto.toObject();
     404                 :             :         }
     405                 :             : 
     406                 :             :         // Workaround for bogus warning
     407                 :             :         // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94554
     408                 :             :         // Note that the corresponding function pointers in the js::ClassSpec
     409                 :             :         // must be initialized as nullptr, not the default initializer! (see
     410                 :             :         // e.g. CairoPath::class_spec.finishInit)
     411                 :             :         using NullOpType =
     412                 :             :             std::integral_constant<js::ClassObjectCreationOp, nullptr>;
     413                 :             :         using CreateConstructorType =
     414                 :             :             std::integral_constant<js::ClassObjectCreationOp,
     415                 :             :                                    Base::klass.spec->createConstructor>;
     416                 :             :         using CreatePrototypeType =
     417                 :             :             std::integral_constant<js::ClassObjectCreationOp,
     418                 :             :                                    Base::klass.spec->createPrototype>;
     419                 :             :         using NullFuncsType =
     420                 :             :             std::integral_constant<const JSFunctionSpec*, nullptr>;
     421                 :             :         using ConstructorFuncsType =
     422                 :             :             std::integral_constant<const JSFunctionSpec*,
     423                 :             :                                    Base::klass.spec->constructorFunctions>;
     424                 :             :         using PrototypeFuncsType =
     425                 :             :             std::integral_constant<const JSFunctionSpec*,
     426                 :             :                                    Base::klass.spec->prototypeFunctions>;
     427                 :             :         using NullPropsType =
     428                 :             :             std::integral_constant<const JSPropertySpec*, nullptr>;
     429                 :             :         using ConstructorPropsType =
     430                 :             :             std::integral_constant<const JSPropertySpec*,
     431                 :             :                                    Base::klass.spec->constructorProperties>;
     432                 :             :         using PrototypePropsType =
     433                 :             :             std::integral_constant<const JSPropertySpec*,
     434                 :             :                                    Base::klass.spec->prototypeProperties>;
     435                 :             :         using NullFinishOpType =
     436                 :             :             std::integral_constant<js::FinishClassInitOp, nullptr>;
     437                 :             :         using FinishInitType =
     438                 :             :             std::integral_constant<js::FinishClassInitOp,
     439                 :             :                                    Base::klass.spec->finishInit>;
     440                 :             : 
     441                 :             :         // Create the prototype. If no createPrototype function is provided,
     442                 :             :         // then the default is to create a plain object as the prototype.
     443                 :         201 :         JS::RootedObject proto(cx);
     444                 :             :         if constexpr (!std::is_same_v<CreatePrototypeType, NullOpType>) {
     445                 :          65 :             proto = Base::klass.spec->createPrototype(cx, JSProto_Object);
     446                 :             :         } else {
     447                 :         136 :             proto = JS_NewPlainObject(cx);
     448                 :             :         }
     449         [ -  + ]:         201 :         if (!proto)
     450                 :           0 :             return nullptr;
     451                 :             : 
     452                 :             :         if constexpr (!std::is_same_v<PrototypePropsType, NullPropsType>) {
     453         [ -  + ]:         201 :             if (!JS_DefineProperties(cx, proto,
     454                 :         201 :                                      Base::klass.spec->prototypeProperties))
     455                 :           0 :                 return nullptr;
     456                 :             :         }
     457                 :             :         if constexpr (!std::is_same_v<PrototypeFuncsType, NullFuncsType>) {
     458         [ -  + ]:         142 :             if (!JS_DefineFunctions(cx, proto,
     459                 :         142 :                                     Base::klass.spec->prototypeFunctions))
     460                 :           0 :                 return nullptr;
     461                 :             :         }
     462                 :             : 
     463                 :         201 :         gjs_set_global_slot(global, Base::PROTOTYPE_SLOT,
     464                 :         201 :                             JS::ObjectValue(*proto));
     465                 :             : 
     466                 :             :         // Create the constructor. If no createConstructor function is provided,
     467                 :             :         // then the default is to call CWrapper::constructor() which calls
     468                 :             :         // Base::constructor_impl().
     469                 :         201 :         JS::RootedObject ctor_obj(cx);
     470                 :             :         if constexpr (!(Base::klass.spec->flags &
     471                 :             :                         js::ClassSpec::DontDefineConstructor)) {
     472                 :             :             if constexpr (!std::is_same_v<CreateConstructorType, NullOpType>) {
     473                 :           5 :                 ctor_obj =
     474                 :           5 :                     Base::klass.spec->createConstructor(cx, JSProto_Object);
     475                 :             :             } else {
     476                 :           9 :                 JSFunction* ctor = JS_NewFunction(
     477                 :             :                     cx, &Base::constructor, Base::constructor_nargs,
     478                 :           9 :                     JSFUN_CONSTRUCTOR, Base::klass.name);
     479                 :           9 :                 ctor_obj = JS_GetFunctionObject(ctor);
     480                 :             :             }
     481         [ +  - ]:          28 :             if (!ctor_obj ||
     482   [ -  +  -  + ]:          28 :                 !JS_LinkConstructorAndPrototype(cx, ctor_obj, proto))
     483                 :           0 :                 return nullptr;
     484                 :             :             if constexpr (!std::is_same_v<ConstructorPropsType,
     485                 :             :                                           NullPropsType>) {
     486                 :             :                 if (!JS_DefineProperties(
     487                 :             :                         cx, ctor_obj, Base::klass.spec->constructorProperties))
     488                 :             :                     return nullptr;
     489                 :             :             }
     490                 :             :             if constexpr (!std::is_same_v<ConstructorFuncsType,
     491                 :             :                                           NullFuncsType>) {
     492         [ -  + ]:           2 :                 if (!JS_DefineFunctions(cx, ctor_obj,
     493                 :           2 :                                         Base::klass.spec->constructorFunctions))
     494                 :           0 :                     return nullptr;
     495                 :             :             }
     496                 :             :         }
     497                 :             : 
     498                 :             :         if constexpr (!std::is_same_v<FinishInitType, NullFinishOpType>) {
     499         [ -  + ]:          13 :             if (!Base::klass.spec->finishInit(cx, ctor_obj, proto))
     500                 :           0 :                 return nullptr;
     501                 :             :         }
     502                 :             : 
     503                 :             :         // Put the constructor, if one exists, as a property on the module
     504                 :             :         // object. If module is not given, we are defining a global class.
     505         [ +  + ]:         201 :         if (ctor_obj) {
     506                 :          14 :             JS::RootedObject in_obj(cx, module);
     507         [ -  + ]:          14 :             if (!in_obj)
     508                 :           0 :                 in_obj = global;
     509                 :          14 :             JS::RootedId class_name(
     510                 :          14 :                 cx, gjs_intern_string_to_id(cx, Base::klass.name));
     511         [ +  - ]:          28 :             if (class_name.isVoid() ||
     512   [ -  +  -  + ]:          28 :                 !JS_DefinePropertyById(cx, in_obj, class_name, ctor_obj,
     513                 :             :                                        GJS_MODULE_PROP_FLAGS))
     514                 :           0 :                 return nullptr;
     515   [ +  -  +  - ]:          14 :         }
     516                 :             : 
     517                 :         201 :         gjs_debug(GJS_DEBUG_CONTEXT, "Initialized class %s prototype %p",
     518                 :         201 :                   Base::klass.name, proto.get());
     519                 :         201 :         return proto;
     520                 :       12924 :     }
     521                 :             : 
     522                 :             :     /*
     523                 :             :      * CWrapper::from_c_ptr():
     524                 :             :      *
     525                 :             :      * Create a new CWrapper JS object from the given C pointer. The pointer
     526                 :             :      * is copied using copy_ptr(), so you must implement that if you use this
     527                 :             :      * function.
     528                 :             :      */
     529                 :             :     GJS_JSAPI_RETURN_CONVENTION
     530                 :           0 :     static JSObject* from_c_ptr(JSContext* cx, Wrapped* ptr) {
     531                 :           0 :         JS::RootedObject proto(cx, Base::prototype(cx));
     532         [ #  # ]:           0 :         if (!proto)
     533                 :           0 :             return nullptr;
     534                 :             : 
     535                 :           0 :         JS::RootedObject wrapper(
     536                 :           0 :             cx, JS_NewObjectWithGivenProto(cx, &Base::klass, proto));
     537         [ #  # ]:           0 :         if (!wrapper)
     538                 :           0 :             return nullptr;
     539                 :             : 
     540                 :           0 :         CWrapperPointerOps<Base, Wrapped>::init_private(wrapper,
     541                 :             :                                                         Base::copy_ptr(ptr));
     542                 :             : 
     543                 :           0 :         debug_lifecycle(ptr, wrapper, "from_c_ptr");
     544                 :             : 
     545                 :           0 :         return wrapper;
     546                 :           0 :     }
     547                 :             : };
        

Generated by: LCOV version 2.0-1