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: 2018 Philip Chimento <philip.chimento@gmail.com>
5 : :
6 : : #include <config.h>
7 : :
8 : : #include <stdint.h>
9 : :
10 : : #include <glib-object.h>
11 : : #include <glib.h>
12 : :
13 : : #include <js/Array.h> // for JS::GetArrayLength
14 : : #include <js/CallArgs.h>
15 : : #include <js/PropertyAndElement.h>
16 : : #include <js/PropertySpec.h>
17 : : #include <js/RootingAPI.h>
18 : : #include <js/TypeDecls.h>
19 : : #include <js/Utility.h> // for UniqueChars
20 : : #include <js/Value.h>
21 : : #include <js/ValueArray.h>
22 : : #include <jsapi.h> // for JS_NewPlainObject
23 : : #include <mozilla/Maybe.h>
24 : :
25 : : #ifndef G_DISABLE_ASSERT
26 : : # include <js/CallAndConstruct.h> // for IsCallable
27 : : #endif
28 : :
29 : : #include "gi/closure.h"
30 : : #include "gi/gobject.h"
31 : : #include "gi/gtype.h"
32 : : #include "gi/interface.h"
33 : : #include "gi/object.h"
34 : : #include "gi/param.h"
35 : : #include "gi/private.h"
36 : : #include "gi/repo.h"
37 : : #include "gi/value.h"
38 : : #include "gjs/atoms.h"
39 : : #include "gjs/auto.h"
40 : : #include "gjs/context-private.h"
41 : : #include "gjs/jsapi-util-args.h"
42 : : #include "gjs/jsapi-util.h"
43 : : #include "gjs/macros.h"
44 : :
45 : : using mozilla::Nothing;
46 : :
47 : : /* gi/private.cpp - private "imports._gi" module with operations that we need to
48 : : * use from JS in order to create GObject classes, but should not be exposed to
49 : : * client code.
50 : : */
51 : :
52 : : GJS_JSAPI_RETURN_CONVENTION
53 : 25 : static bool gjs_override_property(JSContext* cx, unsigned argc, JS::Value* vp) {
54 : 25 : JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
55 : 25 : JS::UniqueChars name;
56 : 25 : JS::RootedObject type(cx);
57 : :
58 [ - + ]: 25 : if (!gjs_parse_call_args(cx, "override_property", args, "so", "name", &name,
59 : : "type", &type))
60 : 0 : return false;
61 : :
62 : : GType gtype;
63 [ - + ]: 25 : if (!gjs_gtype_get_actual_gtype(cx, type, >ype))
64 : 0 : return false;
65 [ - + ]: 25 : if (gtype == G_TYPE_INVALID) {
66 : 0 : gjs_throw(cx, "Invalid parameter type was not a GType");
67 : 0 : return false;
68 : : }
69 : :
70 : : GParamSpec* pspec;
71 [ + - + + : 25 : if (g_type_is_a(gtype, G_TYPE_INTERFACE)) {
+ + ]
72 : : auto* interface_type =
73 : 19 : static_cast<GTypeInterface*>(g_type_default_interface_ref(gtype));
74 : 19 : pspec = g_object_interface_find_property(interface_type, name.get());
75 : 19 : g_type_default_interface_unref(interface_type);
76 : : } else {
77 : 6 : Gjs::AutoTypeClass<GObjectClass> class_type{gtype};
78 : 6 : pspec = g_object_class_find_property(class_type, name.get());
79 : 6 : }
80 : :
81 [ + + ]: 25 : if (!pspec) {
82 : 2 : gjs_throw(cx, "No such property '%s' to override on type '%s'",
83 : : name.get(), g_type_name(gtype));
84 : 2 : return false;
85 : : }
86 : :
87 : 23 : Gjs::AutoParam new_pspec{g_param_spec_override(name.get(), pspec)};
88 : :
89 : 23 : g_param_spec_set_qdata(new_pspec, ObjectBase::custom_property_quark(),
90 : : GINT_TO_POINTER(1));
91 : :
92 : 23 : JSObject* obj = gjs_param_from_g_param(cx, new_pspec);
93 [ - + ]: 23 : if (!obj)
94 : 0 : return false;
95 : :
96 : 23 : args.rval().setObject(*obj);
97 : 23 : return true;
98 : 25 : }
99 : :
100 : : GJS_JSAPI_RETURN_CONVENTION
101 : 139 : static bool validate_interfaces_and_properties_args(JSContext* cx,
102 : : JS::HandleObject interfaces,
103 : : JS::HandleObject properties,
104 : : uint32_t* n_interfaces,
105 : : uint32_t* n_properties) {
106 : : bool is_array;
107 [ - + ]: 139 : if (!JS::IsArrayObject(cx, interfaces, &is_array))
108 : 0 : return false;
109 [ - + ]: 139 : if (!is_array) {
110 : 0 : gjs_throw(cx, "Invalid parameter interfaces (expected Array)");
111 : 0 : return false;
112 : : }
113 : :
114 : : uint32_t n_int;
115 [ - + ]: 139 : if (!JS::GetArrayLength(cx, interfaces, &n_int))
116 : 0 : return false;
117 : :
118 [ - + ]: 139 : if (!JS::IsArrayObject(cx, properties, &is_array))
119 : 0 : return false;
120 [ - + ]: 139 : if (!is_array) {
121 : 0 : gjs_throw(cx, "Invalid parameter properties (expected Array)");
122 : 0 : return false;
123 : : }
124 : :
125 : : uint32_t n_prop;
126 [ - + ]: 139 : if (!JS::GetArrayLength(cx, properties, &n_prop))
127 : 0 : return false;
128 : :
129 [ + - ]: 139 : if (n_interfaces)
130 : 139 : *n_interfaces = n_int;
131 [ + - ]: 139 : if (n_properties)
132 : 139 : *n_properties = n_prop;
133 : 139 : return true;
134 : : }
135 : :
136 : : GJS_JSAPI_RETURN_CONVENTION
137 : 138 : static bool save_properties_for_class_init(JSContext* cx,
138 : : JS::HandleObject properties,
139 : : uint32_t n_properties, GType gtype) {
140 : 138 : AutoParamArray properties_native;
141 : 138 : JS::RootedValue prop_val(cx);
142 : 138 : JS::RootedObject prop_obj(cx);
143 [ + + ]: 206 : for (uint32_t i = 0; i < n_properties; i++) {
144 [ - + ]: 68 : if (!JS_GetElement(cx, properties, i, &prop_val))
145 : 0 : return false;
146 : :
147 [ - + ]: 68 : if (!prop_val.isObject()) {
148 : 0 : gjs_throw(cx, "Invalid parameter, expected object");
149 : 0 : return false;
150 : : }
151 : :
152 : 68 : prop_obj = &prop_val.toObject();
153 [ - + ]: 68 : if (!gjs_typecheck_param(cx, prop_obj, G_TYPE_NONE, true))
154 : 0 : return false;
155 : :
156 : 68 : properties_native.emplace_back(
157 : 68 : g_param_spec_ref(gjs_g_param_from_param(cx, prop_obj)));
158 : : }
159 : 138 : push_class_init_properties(gtype, &properties_native);
160 : 138 : return true;
161 : 138 : }
162 : :
163 : : GJS_JSAPI_RETURN_CONVENTION
164 : 139 : static bool get_interface_gtypes(JSContext* cx, JS::HandleObject interfaces,
165 : : uint32_t n_interfaces, GType* iface_types) {
166 [ + + ]: 195 : for (uint32_t ix = 0; ix < n_interfaces; ix++) {
167 : 56 : JS::RootedValue iface_val(cx);
168 [ - + ]: 56 : if (!JS_GetElement(cx, interfaces, ix, &iface_val))
169 : 0 : return false;
170 : :
171 [ - + ]: 56 : if (!iface_val.isObject()) {
172 : 0 : gjs_throw(
173 : : cx, "Invalid parameter interfaces (element %d was not a GType)",
174 : : ix);
175 : 0 : return false;
176 : : }
177 : :
178 : 56 : JS::RootedObject iface(cx, &iface_val.toObject());
179 : : GType iface_type;
180 [ - + ]: 56 : if (!gjs_gtype_get_actual_gtype(cx, iface, &iface_type))
181 : 0 : return false;
182 [ - + ]: 56 : if (iface_type == G_TYPE_INVALID) {
183 : 0 : gjs_throw(
184 : : cx, "Invalid parameter interfaces (element %d was not a GType)",
185 : : ix);
186 : 0 : return false;
187 : : }
188 : :
189 : 56 : iface_types[ix] = iface_type;
190 [ + - + - ]: 56 : }
191 : 139 : return true;
192 : : }
193 : :
194 : : GJS_JSAPI_RETURN_CONVENTION
195 : 105 : static bool create_wrapper_array(JSContext* cx, JS::HandleObject prototype,
196 : : GType type, JS::MutableHandleValue rval) {
197 : : JS::RootedObject gtype_wrapper(cx,
198 : 105 : gjs_gtype_create_gtype_wrapper(cx, type));
199 [ - + ]: 105 : if (!gtype_wrapper)
200 : 0 : return false;
201 : :
202 : 105 : JS::RootedValueArray<2> tuple(cx);
203 : 105 : tuple[0].setObject(*prototype);
204 : 105 : tuple[1].setObject(*gtype_wrapper);
205 : :
206 : 105 : JS::RootedObject array(cx, JS::NewArrayObject(cx, tuple));
207 [ - + ]: 105 : if (!array)
208 : 0 : return false;
209 : :
210 : 105 : rval.setObject(*array);
211 : 105 : return true;
212 : 105 : }
213 : :
214 : : GJS_JSAPI_RETURN_CONVENTION
215 : 9 : static bool gjs_register_interface_impl(JSContext* cx, const char* name,
216 : : JS::HandleObject interfaces,
217 : : JS::HandleObject properties,
218 : : GType* gtype) {
219 : : uint32_t n_interfaces, n_properties;
220 [ - + ]: 9 : if (!validate_interfaces_and_properties_args(cx, interfaces, properties,
221 : : &n_interfaces, &n_properties))
222 : 0 : return false;
223 : :
224 : 9 : Gjs::AutoPointer<GType> iface_types{g_new(GType, n_interfaces)};
225 : :
226 : : // We do interface addition in two passes so that any failure is caught
227 : : // early, before registering the GType (which we can't undo)
228 [ - + ]: 9 : if (!get_interface_gtypes(cx, interfaces, n_interfaces, iface_types))
229 : 0 : return false;
230 : :
231 [ - + ]: 9 : if (g_type_from_name(name) != G_TYPE_INVALID) {
232 : 0 : gjs_throw(cx, "Type name %s is already registered", name);
233 : 0 : return false;
234 : : }
235 : :
236 : 9 : GTypeInfo type_info = gjs_gobject_interface_info;
237 : 9 : GType interface_type = g_type_register_static(G_TYPE_INTERFACE, name,
238 : : &type_info, GTypeFlags(0));
239 : :
240 : 9 : g_type_set_qdata(interface_type, ObjectBase::custom_type_quark(),
241 : : GINT_TO_POINTER(1));
242 : :
243 [ - + ]: 9 : if (!save_properties_for_class_init(cx, properties, n_properties,
244 : : interface_type))
245 : 0 : return false;
246 : :
247 [ + + ]: 20 : for (uint32_t ix = 0; ix < n_interfaces; ix++)
248 : 11 : g_type_interface_add_prerequisite(interface_type, iface_types[ix]);
249 : :
250 : 9 : *gtype = interface_type;
251 : 9 : return true;
252 : 9 : }
253 : :
254 : : GJS_JSAPI_RETURN_CONVENTION
255 : 6 : static bool gjs_register_interface(JSContext* cx, unsigned argc,
256 : : JS::Value* vp) {
257 : 6 : JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
258 : :
259 : 6 : JS::UniqueChars name;
260 : 6 : JS::RootedObject interfaces(cx), properties(cx);
261 [ - + ]: 6 : if (!gjs_parse_call_args(cx, "register_interface", args, "soo", "name",
262 : : &name, "interfaces", &interfaces, "properties",
263 : : &properties))
264 : 0 : return false;
265 : :
266 : : GType interface_type;
267 [ - + ]: 6 : if (!gjs_register_interface_impl(cx, name.get(), interfaces, properties,
268 : : &interface_type))
269 : 0 : return false;
270 : :
271 : : // create a custom JSClass
272 : 6 : JS::RootedObject module(cx, gjs_lookup_private_namespace(cx));
273 [ - + ]: 6 : if (!module)
274 : 0 : return false; // error will have been thrown already
275 : :
276 : 6 : JS::RootedObject constructor(cx), ignored_prototype(cx);
277 [ - + ]: 6 : if (!InterfacePrototype::create_class(cx, module, Nothing{}, interface_type,
278 : 6 : &constructor, &ignored_prototype))
279 : 0 : return false;
280 : :
281 : 6 : args.rval().setObject(*constructor);
282 : 6 : return true;
283 : 6 : }
284 : :
285 : : GJS_JSAPI_RETURN_CONVENTION
286 : 3 : static bool gjs_register_interface_with_class(JSContext* cx, unsigned argc,
287 : : JS::Value* vp) {
288 : 3 : JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
289 : :
290 : 3 : JS::UniqueChars name;
291 : 3 : JS::RootedObject klass(cx), interfaces(cx), properties(cx);
292 [ - + ]: 3 : if (!gjs_parse_call_args(cx, "register_interface_with_class", args, "osoo",
293 : : "class", &klass, "name", &name, "interfaces",
294 : : &interfaces, "properties", &properties))
295 : 0 : return false;
296 : :
297 : : GType interface_type;
298 [ - + ]: 3 : if (!gjs_register_interface_impl(cx, name.get(), interfaces, properties,
299 : : &interface_type))
300 : 0 : return false;
301 : :
302 : : // create a custom JSClass
303 : 3 : JS::RootedObject module(cx, gjs_lookup_private_namespace(cx));
304 [ - + ]: 3 : if (!module)
305 : 0 : return false; // error will have been thrown already
306 : :
307 : 3 : JS::RootedObject prototype(cx);
308 [ - + ]: 3 : if (!InterfacePrototype::wrap_class(cx, module, Nothing{}, interface_type,
309 : 3 : klass, &prototype))
310 : 0 : return false;
311 : :
312 : 3 : return create_wrapper_array(cx, prototype, interface_type, args.rval());
313 : 3 : }
314 : :
315 : 45 : static inline void gjs_add_interface(GType instance_type,
316 : : GType interface_type) {
317 : : static GInterfaceInfo interface_vtable{nullptr, nullptr, nullptr};
318 : 45 : g_type_add_interface_static(instance_type, interface_type,
319 : : &interface_vtable);
320 : 45 : }
321 : :
322 : : GJS_JSAPI_RETURN_CONVENTION
323 : 131 : static bool gjs_register_type_impl(JSContext* cx, const char* name,
324 : : GTypeFlags type_flags,
325 : : JS::HandleObject parent,
326 : : JS::HandleObject interfaces,
327 : : JS::HandleObject properties,
328 : : GType** iface_types_out,
329 : : uint32_t* n_interfaces_out, GType* gtype) {
330 [ - + ]: 131 : if (!parent)
331 : 0 : return false;
332 : :
333 : : /* Don't pass the argv to it, as otherwise we will log about the callee
334 : : * while we only care about the parent object type. */
335 : : ObjectBase* parent_priv;
336 [ + + ]: 131 : if (!ObjectBase::for_js_typecheck(cx, parent, &parent_priv))
337 : 1 : return false;
338 : :
339 : : uint32_t n_interfaces, n_properties;
340 [ - + ]: 130 : if (!validate_interfaces_and_properties_args(cx, interfaces, properties,
341 : : &n_interfaces, &n_properties))
342 : 0 : return false;
343 : :
344 : 130 : Gjs::AutoPointer<GType> iface_types{g_new(GType, n_interfaces)};
345 : :
346 : : // We do interface addition in two passes so that any failure is caught
347 : : // early, before registering the GType (which we can't undo)
348 [ - + ]: 130 : if (!get_interface_gtypes(cx, interfaces, n_interfaces, iface_types))
349 : 0 : return false;
350 : :
351 [ - + ]: 130 : if (g_type_from_name(name) != G_TYPE_INVALID) {
352 : 0 : gjs_throw(cx, "Type name %s is already registered", name);
353 : 0 : return false;
354 : : }
355 : :
356 : : // We checked parent above, in ObjectBase::for_js_typecheck()
357 : 130 : g_assert(parent_priv);
358 : :
359 : : GTypeQuery query;
360 : 130 : g_type_query(parent_priv->gtype(), &query);
361 : :
362 [ + + ]: 130 : if (G_UNLIKELY(
363 : : g_type_test_flags(parent_priv->gtype(), G_TYPE_FLAG_FINAL))) {
364 : 1 : gjs_throw(cx, "Cannot inherit from a final type");
365 : 1 : return false;
366 : : }
367 : :
368 : 129 : GTypeInfo type_info = gjs_gobject_class_info;
369 : 129 : type_info.class_size = query.class_size;
370 : 129 : type_info.instance_size = query.instance_size;
371 : :
372 : 129 : GType instance_type = g_type_register_static(parent_priv->gtype(), name,
373 : : &type_info, type_flags);
374 : :
375 : 129 : g_type_set_qdata(instance_type, ObjectBase::custom_type_quark(),
376 : : GINT_TO_POINTER(1));
377 : :
378 [ - + ]: 129 : if (!save_properties_for_class_init(cx, properties, n_properties,
379 : : instance_type))
380 : 0 : return false;
381 : :
382 [ + + ]: 174 : for (uint32_t ix = 0; ix < n_interfaces; ix++)
383 : 45 : gjs_add_interface(instance_type, iface_types[ix]);
384 : :
385 : 129 : *gtype = instance_type;
386 : 129 : *n_interfaces_out = n_interfaces;
387 : 129 : *iface_types_out = iface_types.release();
388 : 129 : return true;
389 : 130 : }
390 : :
391 : : GJS_JSAPI_RETURN_CONVENTION
392 : 27 : static bool gjs_register_type(JSContext* cx, unsigned argc, JS::Value* vp) {
393 : 27 : JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
394 : :
395 : 27 : JS::UniqueChars name;
396 : : GTypeFlags type_flags;
397 : 27 : JS::RootedObject parent(cx), interfaces(cx), properties(cx);
398 [ - + ]: 27 : if (!gjs_parse_call_args(cx, "register_type", args, "osioo", "parent",
399 : : &parent, "name", &name, "flags", &type_flags,
400 : : "interfaces", &interfaces, "properties",
401 : : &properties))
402 : 0 : return false;
403 : :
404 : : GType instance_type;
405 : 27 : Gjs::AutoPointer<GType> iface_types;
406 : : uint32_t n_interfaces;
407 [ - + ]: 54 : if (!gjs_register_type_impl(cx, name.get(), type_flags, parent, interfaces,
408 : 27 : properties, iface_types.out(), &n_interfaces,
409 : : &instance_type))
410 : 0 : return false;
411 : :
412 : : // create a custom JSClass
413 : 27 : JS::RootedObject module(cx, gjs_lookup_private_namespace(cx));
414 : 27 : JS::RootedObject constructor(cx), prototype(cx);
415 [ - + ]: 27 : if (!ObjectPrototype::define_class(cx, module, Nothing{}, instance_type,
416 : 27 : iface_types, n_interfaces, &constructor,
417 : 27 : &prototype))
418 : 0 : return false;
419 : :
420 : 27 : auto* priv = ObjectPrototype::for_js(cx, prototype);
421 : 27 : priv->set_type_qdata();
422 : :
423 : 27 : args.rval().setObject(*constructor);
424 : :
425 : 27 : return true;
426 : 27 : }
427 : :
428 : : GJS_JSAPI_RETURN_CONVENTION
429 : 104 : static bool gjs_register_type_with_class(JSContext* cx, unsigned argc,
430 : : JS::Value* vp) {
431 : 104 : JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
432 : :
433 : 104 : JS::UniqueChars name;
434 : : GTypeFlags type_flags;
435 : 104 : JS::RootedObject klass(cx), parent(cx), interfaces(cx), properties(cx);
436 [ - + ]: 104 : if (!gjs_parse_call_args(cx, "register_type_with_class", args, "oosioo",
437 : : "class", &klass, "parent", &parent, "name", &name,
438 : : "flags", &type_flags, "interfaces", &interfaces,
439 : : "properties", &properties))
440 : 0 : return false;
441 : :
442 : : GType instance_type;
443 : : uint32_t n_interfaces;
444 : 104 : Gjs::AutoPointer<GType> iface_types;
445 [ + + ]: 208 : if (!gjs_register_type_impl(cx, name.get(), type_flags, parent, interfaces,
446 : 104 : properties, iface_types.out(), &n_interfaces,
447 : : &instance_type))
448 : 2 : return false;
449 : :
450 : : // create a custom JSClass
451 : 102 : JS::RootedObject module(cx, gjs_lookup_private_namespace(cx));
452 [ - + ]: 102 : if (!module)
453 : 0 : return false;
454 : :
455 : 102 : JS::RootedObject prototype(cx);
456 : 102 : ObjectPrototype* priv = ObjectPrototype::wrap_class(
457 : 204 : cx, module, Nothing{}, instance_type, klass, &prototype);
458 [ - + ]: 102 : if (!priv)
459 : 0 : return false;
460 : :
461 : 102 : priv->set_interfaces(iface_types, n_interfaces);
462 : 102 : priv->set_type_qdata();
463 : :
464 : 102 : return create_wrapper_array(cx, prototype, instance_type, args.rval());
465 : 104 : }
466 : :
467 : : GJS_JSAPI_RETURN_CONVENTION
468 : 33 : static bool gjs_signal_new(JSContext* cx, unsigned argc, JS::Value* vp) {
469 : 33 : JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
470 : :
471 : 33 : JS::UniqueChars signal_name;
472 : : int32_t flags, accumulator_enum;
473 : 33 : JS::RootedObject gtype_obj(cx), return_gtype_obj(cx), params_obj(cx);
474 [ - + ]: 33 : if (!gjs_parse_call_args(cx, "signal_new", args, "osiioo", "gtype",
475 : : >ype_obj, "signal name", &signal_name, "flags",
476 : : &flags, "accumulator", &accumulator_enum,
477 : : "return gtype", &return_gtype_obj, "params",
478 : : ¶ms_obj))
479 : 0 : return false;
480 : :
481 : : // we only support standard accumulators for now
482 : : GSignalAccumulator accumulator;
483 [ + - + ]: 33 : switch (accumulator_enum) {
484 : 4 : case 1:
485 : 4 : accumulator = g_signal_accumulator_first_wins;
486 : 4 : break;
487 : 0 : case 2:
488 : 0 : accumulator = g_signal_accumulator_true_handled;
489 : 0 : break;
490 : 29 : case 0:
491 : : default:
492 : 29 : accumulator = nullptr;
493 : : }
494 : :
495 : : GType return_type;
496 [ - + ]: 33 : if (!gjs_gtype_get_actual_gtype(cx, return_gtype_obj, &return_type))
497 : 0 : return false;
498 : :
499 [ - + ]: 33 : if (accumulator == g_signal_accumulator_true_handled &&
500 [ # # ]: 0 : return_type != G_TYPE_BOOLEAN) {
501 : 0 : gjs_throw(cx,
502 : : "GObject.SignalAccumulator.TRUE_HANDLED can only be used "
503 : : "with boolean signals");
504 : 0 : return false;
505 : : }
506 : :
507 : : uint32_t n_parameters;
508 [ - + ]: 33 : if (!JS::GetArrayLength(cx, params_obj, &n_parameters))
509 : 0 : return false;
510 : :
511 : 66 : Gjs::AutoPointer<GType> params{g_new(GType, n_parameters)};
512 : 66 : JS::RootedValue gtype_val(cx);
513 [ + + ]: 53 : for (uint32_t ix = 0; ix < n_parameters; ix++) {
514 [ + - - + ]: 40 : if (!JS_GetElement(cx, params_obj, ix, >ype_val) ||
515 [ - + ]: 20 : !gtype_val.isObject()) {
516 : 0 : gjs_throw(cx, "Invalid signal parameter number %d", ix);
517 : 0 : return false;
518 : : }
519 : :
520 : 20 : JS::RootedObject gjs_gtype(cx, >ype_val.toObject());
521 [ - + ]: 20 : if (!gjs_gtype_get_actual_gtype(cx, gjs_gtype, ¶ms[ix]))
522 : 0 : return false;
523 [ + - ]: 20 : }
524 : :
525 : : GType gtype;
526 [ - + ]: 33 : if (!gjs_gtype_get_actual_gtype(cx, gtype_obj, >ype))
527 : 0 : return false;
528 : :
529 : 66 : unsigned signal_id = g_signal_newv(
530 : 33 : signal_name.get(), gtype, GSignalFlags(flags),
531 : : /* class closure = */ nullptr, accumulator, /* accu_data = */ nullptr,
532 : : /* c_marshaller = */ nullptr, return_type, n_parameters, params);
533 : :
534 : : // FIXME: what if ID is greater than int32 max?
535 : 33 : args.rval().setInt32(signal_id);
536 : 33 : return true;
537 : 33 : }
538 : :
539 : : GJS_JSAPI_RETURN_CONVENTION
540 : 11 : static bool gjs_lookup_constructor(JSContext* cx, unsigned argc,
541 : : JS::Value* vp) {
542 : 11 : JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
543 : :
544 : 11 : JS::RootedObject gtype_obj(cx);
545 [ - + ]: 11 : if (!gjs_parse_call_args(cx, "lookupConstructor", args, "o", "gtype",
546 : : >ype_obj))
547 : 0 : return false;
548 : :
549 : : GType gtype;
550 [ - + ]: 11 : if (!gjs_gtype_get_actual_gtype(cx, gtype_obj, >ype))
551 : 0 : return false;
552 : :
553 [ - + ]: 11 : if (gtype == G_TYPE_NONE) {
554 : 0 : gjs_throw(cx, "Invalid GType for constructor lookup");
555 : 0 : return false;
556 : : }
557 : :
558 : 11 : return gjs_lookup_object_constructor(cx, gtype, args.rval());
559 : 11 : }
560 : :
561 : : template <GjsSymbolAtom GjsAtoms::* member>
562 : : GJS_JSAPI_RETURN_CONVENTION
563 : 312 : static bool symbol_getter(JSContext* cx, unsigned argc, JS::Value* vp) {
564 : 312 : JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
565 : 312 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
566 : 312 : args.rval().setSymbol((atoms.*member)().toSymbol());
567 : 312 : return true;
568 : : }
569 : :
570 : : GJS_JSAPI_RETURN_CONVENTION
571 : 55 : static bool gjs_associate_closure(JSContext* cx, unsigned argc, JS::Value* vp) {
572 : 55 : JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
573 : 55 : JS::RootedObject func_obj{cx};
574 : 55 : JS::RootedObject target_obj{cx};
575 : 55 : Gjs::AutoGValue value(G_TYPE_CLOSURE);
576 : :
577 [ - + ]: 55 : if (!gjs_parse_call_args(cx, "associateClosure", args, "oo", "object",
578 : : &target_obj, "func", &func_obj))
579 : 0 : return false;
580 : :
581 : 55 : g_assert(JS::IsCallable(func_obj) &&
582 : : "associateClosure's function must be callable");
583 : :
584 : 55 : ObjectInstance* obj = ObjectInstance::for_js(cx, target_obj);
585 [ - + ]: 55 : if (!obj)
586 : 0 : return false;
587 : :
588 : : Gjs::Closure::Ptr closure =
589 : 55 : Gjs::Closure::create_marshaled(cx, func_obj, "wrapped", false);
590 : :
591 [ - + ]: 55 : if (!obj->associate_closure(cx, closure))
592 : 0 : return false;
593 : :
594 : 55 : g_value_set_boxed(&value, closure);
595 : 55 : return gjs_value_from_g_value(cx, args.rval(), &value);
596 : 55 : }
597 : :
598 : : static JSFunctionSpec private_module_funcs[] = {
599 : : JS_FN("override_property", gjs_override_property, 2, GJS_MODULE_PROP_FLAGS),
600 : : JS_FN("register_interface", gjs_register_interface, 3,
601 : : GJS_MODULE_PROP_FLAGS),
602 : : JS_FN("register_interface_with_class", gjs_register_interface_with_class, 4,
603 : : GJS_MODULE_PROP_FLAGS),
604 : : JS_FN("register_type", gjs_register_type, 4, GJS_MODULE_PROP_FLAGS),
605 : : JS_FN("register_type_with_class", gjs_register_type_with_class, 5,
606 : : GJS_MODULE_PROP_FLAGS),
607 : : JS_FN("signal_new", gjs_signal_new, 6, GJS_MODULE_PROP_FLAGS),
608 : : JS_FN("lookupConstructor", gjs_lookup_constructor, 1, 0),
609 : : JS_FN("associateClosure", gjs_associate_closure, 2, GJS_MODULE_PROP_FLAGS),
610 : : JS_FS_END,
611 : : };
612 : :
613 : : static JSPropertySpec private_module_props[] = {
614 : : JS_PSG("gobject_prototype_symbol",
615 : : symbol_getter<&GjsAtoms::gobject_prototype>, GJS_MODULE_PROP_FLAGS),
616 : : JS_PSG("hook_up_vfunc_symbol", symbol_getter<&GjsAtoms::hook_up_vfunc>,
617 : : GJS_MODULE_PROP_FLAGS),
618 : : JS_PSG("signal_find_symbol", symbol_getter<&GjsAtoms::signal_find>,
619 : : GJS_MODULE_PROP_FLAGS),
620 : : JS_PSG("signals_block_symbol", symbol_getter<&GjsAtoms::signals_block>,
621 : : GJS_MODULE_PROP_FLAGS),
622 : : JS_PSG("signals_unblock_symbol", symbol_getter<&GjsAtoms::signals_unblock>,
623 : : GJS_MODULE_PROP_FLAGS),
624 : : JS_PSG("signals_disconnect_symbol",
625 : : symbol_getter<&GjsAtoms::signals_disconnect>, GJS_MODULE_PROP_FLAGS),
626 : : JS_PS_END};
627 : :
628 : 59 : bool gjs_define_private_gi_stuff(JSContext* cx,
629 : : JS::MutableHandleObject module) {
630 : 59 : module.set(JS_NewPlainObject(cx));
631 [ + - + - ]: 118 : return JS_DefineFunctions(cx, module, private_module_funcs) &&
632 : 118 : JS_DefineProperties(cx, module, private_module_props);
633 : : }
|