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