LCOV - code coverage report
Current view: top level - gi - boxed.h (source / functions) Coverage Total Hit
Test: gjs-1.87.1 Code Coverage Lines: 93.1 % 29 27
Test Date: 2026-01-05 03:41:30 Functions: 88.9 % 18 16
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 50.0 % 6 3

             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: 2022 Marco Trevisan <marco.trevisan@canonical.com>
       5                 :             : // SPDX-FileCopyrightText: 2025 Philip Chimento <philip.chimento@gmail.com>
       6                 :             : 
       7                 :             : #pragma once
       8                 :             : 
       9                 :             : #include <config.h>
      10                 :             : 
      11                 :             : #include <stddef.h>  // for size_t
      12                 :             : #include <stdint.h>
      13                 :             : 
      14                 :             : #include <memory>  // for unique_ptr
      15                 :             : #include <string>
      16                 :             : 
      17                 :             : #include <glib-object.h>
      18                 :             : #include <glib.h>
      19                 :             : 
      20                 :             : #include <js/AllocPolicy.h>
      21                 :             : #include <js/GCHashTable.h>  // for GCHashMap
      22                 :             : #include <js/HashTable.h>    // for DefaultHasher
      23                 :             : #include <js/Id.h>
      24                 :             : #include <js/RootingAPI.h>
      25                 :             : #include <js/TypeDecls.h>
      26                 :             : #include <mozilla/Maybe.h>
      27                 :             : 
      28                 :             : #include "gi/info.h"
      29                 :             : #include "gi/wrapperutils.h"
      30                 :             : #include "gjs/jsapi-util.h"
      31                 :             : #include "gjs/macros.h"
      32                 :             : #include "util/log.h"
      33                 :             : 
      34                 :             : class JSTracer;
      35                 :             : namespace JS {
      36                 :             : class CallArgs;
      37                 :             : }
      38                 :             : 
      39                 :             : namespace Boxed {
      40                 :             : struct NoCopy {};
      41                 :             : 
      42                 :             : using FieldMap =
      43                 :             :     JS::GCHashMap<JS::Heap<JSString*>, GI::AutoFieldInfo,
      44                 :             :                   js::DefaultHasher<JSString*>, js::SystemAllocPolicy>;
      45                 :             : }  // namespace Boxed
      46                 :             : 
      47                 :             : /* To conserve memory, we have two different kinds of private data for GBoxed
      48                 :             :  * JS wrappers: BoxedInstance, and BoxedPrototype. Both inherit from BoxedBase
      49                 :             :  * for their common functionality. For more information, see the notes in
      50                 :             :  * wrapperutils.h.
      51                 :             :  */
      52                 :             : 
      53                 :             : template <class Base, class Prototype, class Instance>
      54                 :             : class BoxedBase : public GIWrapperBase<Base, Prototype, Instance> {
      55                 :             :     using BaseClass = GIWrapperBase<Base, Prototype, Instance>;
      56                 :             : 
      57                 :             :  protected:
      58                 :             :     using BaseClass::BaseClass;
      59                 :             : 
      60                 :             :     static constexpr GjsDebugTopic DEBUG_TOPIC = GJS_DEBUG_GBOXED;
      61                 :             : 
      62                 :             :     // JS property accessors
      63                 :             : 
      64                 :             :     GJS_JSAPI_RETURN_CONVENTION
      65                 :             :     static bool field_getter(JSContext*, unsigned, JS::Value*);
      66                 :             :     GJS_JSAPI_RETURN_CONVENTION
      67                 :             :     static bool field_setter(JSContext*, unsigned, JS::Value*);
      68                 :             : 
      69                 :             :     // Helper methods that work on either instances or prototypes
      70                 :             : 
      71                 :             :     GJS_JSAPI_RETURN_CONVENTION
      72                 :             :     mozilla::Maybe<GI::AutoFieldInfo> get_field_info(JSContext*,
      73                 :             :                                                      uint32_t id) const;
      74                 :             : 
      75                 :             :  public:
      76                 :             :     [[nodiscard]] Base* get_copy_source(JSContext*, JS::Value) const;
      77                 :             :     using BaseClass::info;
      78                 :             :     using BaseClass::name;
      79                 :             : };
      80                 :             : 
      81                 :             : template <class Base, class Prototype, class Instance>
      82                 :             : class BoxedPrototype : public GIWrapperPrototype<Base, Prototype, Instance,
      83                 :             :                                                  GI::OwnedInfo<Base::TAG>,
      84                 :             :                                                  GI::UnownedInfo<Base::TAG>> {
      85                 :             :     using BoxedInfo = GI::UnownedInfo<Base::TAG>;
      86                 :             :     using BaseClass = GIWrapperPrototype<Base, Prototype, Instance,
      87                 :             :                                          GI::OwnedInfo<Base::TAG>, BoxedInfo>;
      88                 :             :     friend class GIWrapperBase<Base, Prototype, Instance>;
      89                 :             : 
      90                 :             :     int m_zero_args_constructor;  // -1 if none
      91                 :             :     int m_default_constructor;  // -1 if none
      92                 :             :     JS::Heap<jsid> m_default_constructor_name;
      93                 :             :     std::unique_ptr<Boxed::FieldMap> m_field_map;
      94                 :             :     bool m_can_allocate_directly_without_pointers : 1;
      95                 :             :     bool m_can_allocate_directly : 1;
      96                 :             : 
      97                 :             :  protected:
      98                 :             :     explicit BoxedPrototype(const BoxedInfo, GType);
      99                 :             : 
     100                 :             :     GJS_JSAPI_RETURN_CONVENTION bool init(JSContext* cx);
     101                 :             : 
     102                 :             :     // Accessors
     103                 :             : 
     104                 :             :  public:
     105                 :             :     GJS_JSAPI_RETURN_CONVENTION
     106                 :             :     mozilla::Maybe<const GI::FieldInfo> lookup_field(JSContext*,
     107                 :             :                                                      JSString* prop_name);
     108                 :             : 
     109                 :             :     [[nodiscard]]
     110                 :        1583 :     bool can_allocate_directly_without_pointers() const {
     111                 :        1583 :         return m_can_allocate_directly_without_pointers;
     112                 :             :     }
     113                 :             :     [[nodiscard]]
     114                 :        1678 :     bool can_allocate_directly() const {
     115                 :        1678 :         return m_can_allocate_directly;
     116                 :             :     }
     117                 :             :     [[nodiscard]]
     118                 :        1757 :     bool has_zero_args_constructor() const {
     119                 :        1757 :         return m_zero_args_constructor >= 0;
     120                 :             :     }
     121                 :             :     [[nodiscard]]
     122                 :        1168 :     bool has_default_constructor() const {
     123                 :        1168 :         return m_default_constructor >= 0;
     124                 :             :     }
     125                 :             :     [[nodiscard]]
     126                 :          87 :     GI::AutoFunctionInfo zero_args_constructor_info() const {
     127                 :          87 :         g_assert(has_zero_args_constructor());
     128                 :          87 :         return *info().methods()[m_zero_args_constructor];
     129                 :             :     }
     130                 :             :     // The ID is traced from the object, so it's OK to create a handle from it.
     131                 :             :     [[nodiscard]]
     132                 :        1123 :     JS::HandleId default_constructor_name() const {
     133                 :        1123 :         return JS::HandleId::fromMarkedLocation(
     134                 :        1123 :             m_default_constructor_name.unsafeAddress());
     135                 :             :     }
     136                 :             : 
     137                 :             :     using BaseClass::format_name;
     138                 :             :     using BaseClass::gtype;
     139                 :             :     using BaseClass::info;
     140                 :             :     using BaseClass::name;
     141                 :             : 
     142                 :             :     // JSClass operations
     143                 :             : 
     144                 :             :  private:
     145                 :             :     GJS_JSAPI_RETURN_CONVENTION
     146                 :             :     bool resolve_impl(JSContext*, JS::HandleObject, JS::HandleId,
     147                 :             :                       bool* resolved);
     148                 :             : 
     149                 :             :     GJS_JSAPI_RETURN_CONVENTION
     150                 :             :     bool new_enumerate_impl(JSContext*, JS::HandleObject,
     151                 :             :                             JS::MutableHandleIdVector properties,
     152                 :             :                             bool only_enumerable);
     153                 :             :     void trace_impl(JSTracer* trc);
     154                 :             : 
     155                 :             :     // Helper methods
     156                 :             : 
     157                 :             :     GJS_JSAPI_RETURN_CONVENTION
     158                 :             :     static std::unique_ptr<Boxed::FieldMap> create_field_map(JSContext*,
     159                 :             :                                                              const BoxedInfo);
     160                 :             :     GJS_JSAPI_RETURN_CONVENTION
     161                 :             :     bool ensure_field_map(JSContext*);
     162                 :             :     GJS_JSAPI_RETURN_CONVENTION
     163                 :             :     bool define_boxed_class_fields(JSContext*, JS::HandleObject proto);
     164                 :             : 
     165                 :             :  protected:
     166                 :             :     GJS_JSAPI_RETURN_CONVENTION
     167                 :             :     static bool define_class_impl(JSContext*, JS::HandleObject in_object,
     168                 :             :                                   const BoxedInfo,
     169                 :             :                                   JS::MutableHandleObject prototype);
     170                 :             :     static std::string find_unique_js_field_name(const BoxedInfo,
     171                 :             :                                                  const std::string& field_name);
     172                 :             : };
     173                 :             : 
     174                 :             : template <class Base, class Prototype, class Instance>
     175                 :             : class BoxedInstance : public GIWrapperInstance<Base, Prototype, Instance> {
     176                 :             :     using BaseClass = GIWrapperInstance<Base, Prototype, Instance>;
     177                 :             :     friend class GIWrapperBase<Base, Prototype, Instance>;
     178                 :             :     friend class BoxedBase<Base, Prototype, Instance>;  // for field_getter, etc
     179                 :             :     template <class OtherInstance>
     180                 :             :     friend void adopt_nested_ptr(OtherInstance*, void*);
     181                 :             : 
     182                 :             :     using BoxedInfo = GI::UnownedInfo<Base::TAG>;
     183                 :             : 
     184                 :             :     // Reserved slots
     185                 :             :     static const size_t PARENT_OBJECT = 1;
     186                 :             : 
     187                 :             :  protected:
     188                 :             :     bool m_allocated_directly : 1;
     189                 :             :     bool m_owning_ptr : 1;  // if set, the JS wrapper owns the C memory referred
     190                 :             :                             // to by m_ptr.
     191                 :             : 
     192                 :             :     explicit BoxedInstance(Prototype*, JS::HandleObject);
     193                 :             :     ~BoxedInstance();
     194                 :             : 
     195                 :             :     // Don't set GIWrapperBase::m_ptr directly. Instead, use one of these
     196                 :             :     // setters to express your intention to own the pointer or not.
     197                 :       20713 :     void own_ptr(void* boxed_ptr) {
     198                 :       20713 :         g_assert(!m_ptr);
     199                 :       20713 :         m_ptr = boxed_ptr;
     200                 :       20713 :         m_owning_ptr = true;
     201                 :       20713 :     }
     202                 :         129 :     void share_ptr(void* unowned_boxed_ptr) {
     203                 :         129 :         g_assert(!m_ptr);
     204                 :         129 :         m_ptr = unowned_boxed_ptr;
     205                 :         129 :         m_owning_ptr = false;
     206                 :         129 :     }
     207                 :             : 
     208                 :             :     // Methods for different ways to allocate the GBoxed pointer
     209                 :             : 
     210                 :             :     void allocate_directly();
     211                 :             :     void copy_boxed(void* boxed_ptr);
     212                 :             :     void copy_boxed(Instance* source);
     213                 :             :     void copy_memory(void* boxed_ptr);
     214                 :             :     void copy_memory(Instance* source);
     215                 :             : 
     216                 :             :     // Helper methods
     217                 :             : 
     218                 :             :     GJS_JSAPI_RETURN_CONVENTION
     219                 :             :     bool invoke_static_method(JSContext*, JS::HandleObject,
     220                 :             :                               JS::HandleId method_name, const JS::CallArgs&);
     221                 :             :     GJS_JSAPI_RETURN_CONVENTION
     222                 :             :     bool init_from_props(JSContext*, JS::Value props_value);
     223                 :             : 
     224                 :             :     template <class FieldInstance>
     225                 :             :     GJS_JSAPI_RETURN_CONVENTION
     226                 :             :     bool get_nested_interface_object(JSContext*, JSObject* parent_obj,
     227                 :             :                                      const GI::FieldInfo,
     228                 :             :                                      const GI::UnownedInfo<FieldInstance::TAG>,
     229                 :             :                                      JS::MutableHandleValue) const;
     230                 :             :     template <class FieldBase>
     231                 :             :     GJS_JSAPI_RETURN_CONVENTION
     232                 :             :     bool set_nested_interface_object(JSContext*, const GI::FieldInfo,
     233                 :             :                                      const GI::UnownedInfo<FieldBase::TAG>,
     234                 :             :                                      JS::HandleValue);
     235                 :             : 
     236                 :             :     GJS_JSAPI_RETURN_CONVENTION
     237                 :         116 :     static void* copy_ptr(JSContext* cx, GType gtype, void* ptr) {
     238   [ +  -  +  -  :         116 :         if (g_type_is_a(gtype, G_TYPE_BOXED))
                   +  - ]
     239                 :         116 :             return g_boxed_copy(gtype, ptr);
     240                 :             : 
     241                 :           0 :         gjs_throw(cx,
     242                 :             :                   "Can't transfer ownership of a %s type not registered as "
     243                 :             :                   "boxed",
     244                 :             :                   Base::DEBUG_TAG);
     245                 :           0 :         return nullptr;
     246                 :             :     }
     247                 :             : 
     248                 :             :     // JS property accessors
     249                 :             : 
     250                 :             :     GJS_JSAPI_RETURN_CONVENTION
     251                 :             :     bool field_getter_impl(JSContext*, JSObject*, const GI::FieldInfo,
     252                 :             :                            JS::MutableHandleValue rval) const;
     253                 :             :     GJS_JSAPI_RETURN_CONVENTION
     254                 :             :     bool field_setter_impl(JSContext*, const GI::FieldInfo, JS::HandleValue);
     255                 :             : 
     256                 :             :     // JS constructor
     257                 :             : 
     258                 :             :     GJS_JSAPI_RETURN_CONVENTION
     259                 :             :     bool constructor_impl(JSContext*, JS::HandleObject, const JS::CallArgs&);
     260                 :             : 
     261                 :             :     // Public API for initializing BoxedInstance JS object from C struct
     262                 :             : 
     263                 :             :  private:
     264                 :             :     GJS_JSAPI_RETURN_CONVENTION
     265                 :             :     bool init_from_c_struct(JSContext*, void* gboxed);
     266                 :             :     GJS_JSAPI_RETURN_CONVENTION
     267                 :             :     bool init_from_c_struct(JSContext*, void* gboxed, Boxed::NoCopy);
     268                 :             : 
     269                 :             :  protected:
     270                 :             :     template <typename... Args>
     271                 :             :     GJS_JSAPI_RETURN_CONVENTION
     272                 :             :     static JSObject* new_for_c_struct_impl(JSContext*, const BoxedInfo,
     273                 :             :                                            void* gboxed, Args&&...);
     274                 :             : 
     275                 :             :  protected:
     276                 :             :     using BaseClass::debug_lifecycle;
     277                 :             :     using BaseClass::get_copy_source;
     278                 :             :     using BaseClass::get_field_info;
     279                 :             :     using BaseClass::get_prototype;
     280                 :             :     using BaseClass::m_ptr;
     281                 :             :     using BaseClass::raw_ptr;
     282                 :             : 
     283                 :             :  public:
     284                 :             :     using BaseClass::format_name;
     285                 :             :     using BaseClass::gtype;
     286                 :             :     using BaseClass::info;
     287                 :             :     using BaseClass::name;
     288                 :             : };
        

Generated by: LCOV version 2.0-1