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 : :
5 : : #ifndef GI_BOXED_H_
6 : : #define GI_BOXED_H_
7 : :
8 : : #include <config.h>
9 : :
10 : : #include <stddef.h> // for size_t
11 : : #include <stdint.h>
12 : :
13 : : #include <memory> // for unique_ptr
14 : :
15 : : #include <girepository.h>
16 : : #include <glib-object.h>
17 : : #include <glib.h>
18 : :
19 : : #include <js/AllocPolicy.h>
20 : : #include <js/GCHashTable.h> // for GCHashMap
21 : : #include <js/HashTable.h> // for DefaultHasher
22 : : #include <js/Id.h>
23 : : #include <js/RootingAPI.h>
24 : : #include <js/TypeDecls.h>
25 : :
26 : : #include "gi/cwrapper.h"
27 : : #include "gi/wrapperutils.h"
28 : : #include "gjs/jsapi-util.h"
29 : : #include "gjs/macros.h"
30 : : #include "util/log.h"
31 : :
32 : : class BoxedPrototype;
33 : : class BoxedInstance;
34 : : class JSTracer;
35 : : namespace JS {
36 : : class CallArgs;
37 : : }
38 : :
39 : : /* To conserve memory, we have two different kinds of private data for GBoxed
40 : : * JS wrappers: BoxedInstance, and BoxedPrototype. Both inherit from BoxedBase
41 : : * for their common functionality. For more information, see the notes in
42 : : * wrapperutils.h.
43 : : */
44 : :
45 : : class BoxedBase
46 : : : public GIWrapperBase<BoxedBase, BoxedPrototype, BoxedInstance> {
47 : : friend class CWrapperPointerOps<BoxedBase>;
48 : : friend class GIWrapperBase<BoxedBase, BoxedPrototype, BoxedInstance>;
49 : :
50 : : protected:
51 : 18694 : explicit BoxedBase(BoxedPrototype* proto = nullptr)
52 : 18694 : : GIWrapperBase(proto) {}
53 : :
54 : : static constexpr GjsDebugTopic DEBUG_TOPIC = GJS_DEBUG_GBOXED;
55 : : static constexpr const char* DEBUG_TAG = "boxed";
56 : :
57 : : static const struct JSClassOps class_ops;
58 : : static const struct JSClass klass;
59 : :
60 : : // JS property accessors
61 : :
62 : : GJS_JSAPI_RETURN_CONVENTION
63 : : static bool field_getter(JSContext* cx, unsigned argc, JS::Value* vp);
64 : : GJS_JSAPI_RETURN_CONVENTION
65 : : static bool field_setter(JSContext* cx, unsigned argc, JS::Value* vp);
66 : :
67 : : // Helper methods that work on either instances or prototypes
68 : :
69 : : GJS_JSAPI_RETURN_CONVENTION
70 : : GIFieldInfo* get_field_info(JSContext* cx, uint32_t id) const;
71 : :
72 : : public:
73 : : [[nodiscard]] BoxedBase* get_copy_source(JSContext* cx,
74 : : JS::Value value) const;
75 : : };
76 : :
77 : : class BoxedPrototype : public GIWrapperPrototype<BoxedBase, BoxedPrototype,
78 : : BoxedInstance, GIStructInfo> {
79 : : friend class GIWrapperPrototype<BoxedBase, BoxedPrototype, BoxedInstance,
80 : : GIStructInfo>;
81 : : friend class GIWrapperBase<BoxedBase, BoxedPrototype, BoxedInstance>;
82 : :
83 : : using FieldMap =
84 : : JS::GCHashMap<JS::Heap<JSString*>, GjsAutoFieldInfo,
85 : : js::DefaultHasher<JSString*>, js::SystemAllocPolicy>;
86 : :
87 : : int m_zero_args_constructor; // -1 if none
88 : : int m_default_constructor; // -1 if none
89 : : JS::Heap<jsid> m_default_constructor_name;
90 : : std::unique_ptr<FieldMap> m_field_map;
91 : : bool m_can_allocate_directly_without_pointers : 1;
92 : : bool m_can_allocate_directly : 1;
93 : :
94 : : explicit BoxedPrototype(GIStructInfo* info, GType gtype);
95 : : ~BoxedPrototype(void);
96 : :
97 : : GJS_JSAPI_RETURN_CONVENTION bool init(JSContext* cx);
98 : :
99 : : static constexpr InfoType::Tag info_type_tag = InfoType::Struct;
100 : :
101 : : // Accessors
102 : :
103 : : public:
104 : 1240 : [[nodiscard]] bool can_allocate_directly_without_pointers() const {
105 : 1240 : return m_can_allocate_directly_without_pointers;
106 : : }
107 : 304 : [[nodiscard]] bool can_allocate_directly() const {
108 : 304 : return m_can_allocate_directly;
109 : : }
110 : 1299 : [[nodiscard]] bool has_zero_args_constructor() const {
111 : 1299 : return m_zero_args_constructor >= 0;
112 : : }
113 : 988 : [[nodiscard]] bool has_default_constructor() const {
114 : 988 : return m_default_constructor >= 0;
115 : : }
116 : 59 : [[nodiscard]] GIFunctionInfo* zero_args_constructor_info() const {
117 : 59 : return g_struct_info_get_method(info(), m_zero_args_constructor);
118 : : }
119 : : // The ID is traced from the object, so it's OK to create a handle from it.
120 : 988 : [[nodiscard]] JS::HandleId default_constructor_name() const {
121 : 988 : return JS::HandleId::fromMarkedLocation(
122 : 988 : m_default_constructor_name.address());
123 : : }
124 : :
125 : : // JSClass operations
126 : :
127 : : private:
128 : : GJS_JSAPI_RETURN_CONVENTION
129 : : bool resolve_impl(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
130 : : bool* resolved);
131 : :
132 : : GJS_JSAPI_RETURN_CONVENTION
133 : : bool new_enumerate_impl(JSContext* cx, JS::HandleObject obj,
134 : : JS::MutableHandleIdVector properties,
135 : : bool only_enumerable);
136 : : void trace_impl(JSTracer* trc);
137 : :
138 : : // Helper methods
139 : :
140 : : GJS_JSAPI_RETURN_CONVENTION
141 : : static std::unique_ptr<FieldMap> create_field_map(
142 : : JSContext* cx, GIStructInfo* struct_info);
143 : : GJS_JSAPI_RETURN_CONVENTION
144 : : bool ensure_field_map(JSContext* cx);
145 : : GJS_JSAPI_RETURN_CONVENTION
146 : : bool define_boxed_class_fields(JSContext* cx, JS::HandleObject proto);
147 : :
148 : : public:
149 : : GJS_JSAPI_RETURN_CONVENTION
150 : : static bool define_class(JSContext* cx, JS::HandleObject in_object,
151 : : GIStructInfo* info);
152 : : GJS_JSAPI_RETURN_CONVENTION
153 : : GIFieldInfo* lookup_field(JSContext* cx, JSString* prop_name);
154 : : };
155 : :
156 : : class BoxedInstance
157 : : : public GIWrapperInstance<BoxedBase, BoxedPrototype, BoxedInstance> {
158 : : friend class GIWrapperInstance<BoxedBase, BoxedPrototype, BoxedInstance>;
159 : : friend class GIWrapperBase<BoxedBase, BoxedPrototype, BoxedInstance>;
160 : : friend class BoxedBase; // for field_getter, etc.
161 : :
162 : : // Reserved slots
163 : : static const size_t PARENT_OBJECT = 1;
164 : :
165 : : bool m_allocated_directly : 1;
166 : : bool m_owning_ptr : 1; // if set, the JS wrapper owns the C memory referred
167 : : // to by m_ptr.
168 : :
169 : : explicit BoxedInstance(BoxedPrototype* prototype, JS::HandleObject obj);
170 : : ~BoxedInstance(void);
171 : :
172 : : // Don't set GIWrapperBase::m_ptr directly. Instead, use one of these
173 : : // setters to express your intention to own the pointer or not.
174 : 16655 : void own_ptr(void* boxed_ptr) {
175 : 16655 : g_assert(!m_ptr);
176 : 16655 : m_ptr = boxed_ptr;
177 : 16655 : m_owning_ptr = true;
178 : 16655 : }
179 : 24 : void share_ptr(void* unowned_boxed_ptr) {
180 : 24 : g_assert(!m_ptr);
181 : 24 : m_ptr = unowned_boxed_ptr;
182 : 24 : m_owning_ptr = false;
183 : 24 : }
184 : :
185 : : // Methods for different ways to allocate the GBoxed pointer
186 : :
187 : : void allocate_directly(void);
188 : : void copy_boxed(void* boxed_ptr);
189 : : void copy_boxed(BoxedInstance* source);
190 : : void copy_memory(void* boxed_ptr);
191 : : void copy_memory(BoxedInstance* source);
192 : :
193 : : // Helper methods
194 : :
195 : : GJS_JSAPI_RETURN_CONVENTION
196 : : bool init_from_props(JSContext* cx, JS::Value props_value);
197 : :
198 : : GJS_JSAPI_RETURN_CONVENTION
199 : : bool get_nested_interface_object(JSContext* cx, JSObject* parent_obj,
200 : : GIFieldInfo* field_info,
201 : : GIBaseInfo* interface_info,
202 : : JS::MutableHandleValue value) const;
203 : : GJS_JSAPI_RETURN_CONVENTION
204 : : bool set_nested_interface_object(JSContext* cx, GIFieldInfo* field_info,
205 : : GIBaseInfo* interface_info,
206 : : JS::HandleValue value);
207 : :
208 : : GJS_JSAPI_RETURN_CONVENTION
209 : : static void* copy_ptr(JSContext* cx, GType gtype, void* ptr);
210 : :
211 : : // JS property accessors
212 : :
213 : : GJS_JSAPI_RETURN_CONVENTION
214 : : bool field_getter_impl(JSContext* cx, JSObject* obj, GIFieldInfo* info,
215 : : JS::MutableHandleValue rval) const;
216 : : GJS_JSAPI_RETURN_CONVENTION
217 : : bool field_setter_impl(JSContext* cx, GIFieldInfo* info,
218 : : JS::HandleValue value);
219 : :
220 : : // JS constructor
221 : :
222 : : GJS_JSAPI_RETURN_CONVENTION
223 : : bool constructor_impl(JSContext* cx, JS::HandleObject obj,
224 : : const JS::CallArgs& args);
225 : :
226 : : // Public API for initializing BoxedInstance JS object from C struct
227 : :
228 : : public:
229 : : struct NoCopy {};
230 : :
231 : : private:
232 : : GJS_JSAPI_RETURN_CONVENTION
233 : : bool init_from_c_struct(JSContext* cx, void* gboxed);
234 : : GJS_JSAPI_RETURN_CONVENTION
235 : : bool init_from_c_struct(JSContext* cx, void* gboxed, NoCopy);
236 : : template <typename... Args>
237 : : GJS_JSAPI_RETURN_CONVENTION static JSObject* new_for_c_struct_impl(
238 : : JSContext* cx, GIStructInfo* info, void* gboxed, Args&&... args);
239 : :
240 : : public:
241 : : GJS_JSAPI_RETURN_CONVENTION
242 : : static JSObject* new_for_c_struct(JSContext* cx, GIStructInfo* info,
243 : : void* gboxed);
244 : : GJS_JSAPI_RETURN_CONVENTION
245 : : static JSObject* new_for_c_struct(JSContext* cx, GIStructInfo* info,
246 : : void* gboxed, NoCopy);
247 : : };
248 : :
249 : : #endif // GI_BOXED_H_
|