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