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