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 : : };
|