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 : :
5 : : #include <config.h>
6 : :
7 : : #include <string.h> // for strlen
8 : :
9 : : #if GJS_VERBOSE_ENABLE_GI_USAGE
10 : : # include <string>
11 : : #endif
12 : :
13 : : #include <girepository.h>
14 : : #include <glib-object.h>
15 : : #include <glib.h>
16 : :
17 : : #include <js/CallAndConstruct.h> // for JS_CallFunctionValue
18 : : #include <js/Class.h>
19 : : #include <js/ComparisonOperators.h>
20 : : #include <js/Exception.h>
21 : : #include <js/GlobalObject.h> // for CurrentGlobalOrNull
22 : : #include <js/Id.h> // for PropertyKey
23 : : #include <js/Object.h> // for GetClass
24 : : #include <js/PropertyAndElement.h>
25 : : #include <js/PropertyDescriptor.h> // for JSPROP_PERMANENT, JSPROP_RESOLVING
26 : : #include <js/RootingAPI.h>
27 : : #include <js/String.h>
28 : : #include <js/TypeDecls.h>
29 : : #include <js/Utility.h> // for UniqueChars
30 : : #include <js/Value.h>
31 : : #include <js/ValueArray.h>
32 : : #include <js/Warnings.h>
33 : : #include <jsapi.h> // for JS_NewPlainObject, JS_NewObject
34 : :
35 : : #include "gi/arg.h"
36 : : #include "gi/boxed.h"
37 : : #include "gi/enumeration.h"
38 : : #include "gi/function.h"
39 : : #include "gi/fundamental.h"
40 : : #include "gi/gerror.h"
41 : : #include "gi/interface.h"
42 : : #include "gi/ns.h"
43 : : #include "gi/object.h"
44 : : #include "gi/param.h"
45 : : #include "gi/repo.h"
46 : : #include "gi/union.h"
47 : : #include "gjs/atoms.h"
48 : : #include "gjs/context-private.h"
49 : : #include "gjs/global.h"
50 : : #include "gjs/jsapi-util.h"
51 : : #include "gjs/macros.h"
52 : : #include "gjs/module.h"
53 : : #include "util/log.h"
54 : :
55 : : GJS_JSAPI_RETURN_CONVENTION
56 : : static bool lookup_override_function(JSContext *, JS::HandleId,
57 : : JS::MutableHandleValue);
58 : :
59 : : GJS_JSAPI_RETURN_CONVENTION
60 : 230 : static bool get_version_for_ns(JSContext* context, JS::HandleObject repo_obj,
61 : : JS::HandleId ns_id, JS::UniqueChars* version) {
62 : 230 : JS::RootedObject versions(context);
63 : : bool found;
64 : 230 : const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
65 : :
66 [ - + ]: 230 : if (!gjs_object_require_property(context, repo_obj, "GI repository object",
67 : : atoms.versions(), &versions))
68 : 0 : return false;
69 : :
70 [ - + ]: 230 : if (!JS_AlreadyHasOwnPropertyById(context, versions, ns_id, &found))
71 : 0 : return false;
72 : :
73 [ + + ]: 230 : if (!found)
74 : 67 : return true;
75 : :
76 : 163 : return gjs_object_require_property(context, versions, NULL, ns_id, version);
77 : 230 : }
78 : :
79 : 228 : static void strlist_free(GList* l) { g_list_free_full(l, g_free); }
80 : :
81 : : GJS_JSAPI_RETURN_CONVENTION
82 : 230 : static bool resolve_namespace_object(JSContext* context,
83 : : JS::HandleObject repo_obj,
84 : : JS::HandleId ns_id) {
85 : 230 : JS::UniqueChars version;
86 [ - + ]: 230 : if (!get_version_for_ns(context, repo_obj, ns_id, &version))
87 : 0 : return false;
88 : :
89 : 230 : JS::UniqueChars ns_name;
90 [ - + ]: 230 : if (!gjs_get_string_id(context, ns_id, &ns_name))
91 : 0 : return false;
92 [ - + ]: 230 : if (!ns_name) {
93 : 0 : gjs_throw(context, "Requiring invalid namespace on imports.gi");
94 : 0 : return false;
95 : : }
96 : :
97 : 228 : GjsAutoPointer<GList, GList, strlist_free> versions =
98 : 230 : g_irepository_enumerate_versions(nullptr, ns_name.get());
99 : 230 : unsigned nversions = g_list_length(versions);
100 [ + + - + ]: 72 : if (nversions > 1 && !version &&
101 [ + + - + ]: 302 : !g_irepository_is_registered(nullptr, ns_name.get(), nullptr) &&
102 [ # # ]: 0 : !JS::WarnUTF8(context,
103 : : "Requiring %s but it has %u versions available; use "
104 : : "imports.gi.versions to pick one",
105 : : ns_name.get(), nversions))
106 : 0 : return false;
107 : :
108 : 230 : GjsAutoError error;
109 : : // If resolving Gio, load the platform-specific typelib first, so that
110 : : // GioUnix/GioWin32 GTypes get looked up in there with higher priority,
111 : : // instead of in Gio.
112 : : #if GLIB_CHECK_VERSION(2, 79, 2) && (defined(G_OS_UNIX) || defined(G_OS_WIN32))
113 [ + + ]: 230 : if (strcmp(ns_name.get(), "Gio") == 0) {
114 : : # ifdef G_OS_UNIX
115 : 23 : const char* platform = "Unix";
116 : : # else // G_OS_WIN32
117 : : const char* platform = "Win32";
118 : : # endif // G_OS_UNIX/G_OS_WIN32
119 : : GjsAutoChar platform_specific =
120 : 23 : g_strconcat(ns_name.get(), platform, nullptr);
121 [ - + ]: 23 : if (!g_irepository_require(nullptr, platform_specific, version.get(),
122 : : GIRepositoryLoadFlags(0), &error)) {
123 : 0 : gjs_throw(context, "Failed to require %s %s: %s",
124 : 0 : platform_specific.get(), version.get(), error->message);
125 : 0 : return false;
126 : : }
127 [ + - ]: 23 : }
128 : : #endif // GLib >= 2.79.2
129 : :
130 : 230 : g_irepository_require(nullptr, ns_name.get(), version.get(),
131 : : GIRepositoryLoadFlags(0), &error);
132 [ + + ]: 230 : if (error) {
133 [ + + ]: 37 : gjs_throw(context, "Requiring %s, version %s: %s", ns_name.get(),
134 : 37 : version ? version.get() : "none", error->message);
135 : 20 : return false;
136 : : }
137 : :
138 : : /* Defines a property on "obj" (the javascript repo object)
139 : : * with the given namespace name, pointing to that namespace
140 : : * in the repo.
141 : : */
142 : : JS::RootedObject gi_namespace(context,
143 : 210 : gjs_create_ns(context, ns_name.get()));
144 : :
145 : 210 : JS::RootedValue override(context);
146 [ + + ]: 414 : if (!lookup_override_function(context, ns_id, &override) ||
147 : : // Define the property early, to avoid reentrancy issues if the override
148 : : // module looks for namespaces that import this
149 [ - + + + ]: 414 : !JS_DefinePropertyById(context, repo_obj, ns_id, gi_namespace,
150 : : GJS_MODULE_PROP_FLAGS))
151 : 6 : return false;
152 : :
153 : 204 : JS::RootedValue result(context);
154 [ + + ]: 343 : if (!override.isUndefined() &&
155 [ - + ]: 278 : !JS_CallFunctionValue (context, gi_namespace, /* thisp */
156 : : override, /* callee */
157 [ - + ]: 343 : JS::HandleValueArray::empty(), &result))
158 : 0 : return false;
159 : :
160 : 204 : gjs_debug(GJS_DEBUG_GNAMESPACE,
161 : : "Defined namespace '%s' %p in GIRepository %p", ns_name.get(),
162 : 204 : gi_namespace.get(), repo_obj.get());
163 : :
164 : 204 : GjsContextPrivate* gjs = GjsContextPrivate::from_cx(context);
165 : 204 : gjs->schedule_gc_if_needed();
166 : 204 : return true;
167 : 230 : }
168 : :
169 : : /*
170 : : * The *resolved out parameter, on success, should be false to indicate that id
171 : : * was not resolved; and true if id was resolved.
172 : : */
173 : : GJS_JSAPI_RETURN_CONVENTION
174 : : static bool
175 : 230 : repo_resolve(JSContext *context,
176 : : JS::HandleObject obj,
177 : : JS::HandleId id,
178 : : bool *resolved)
179 : : {
180 [ - + ]: 230 : if (!id.isString()) {
181 : 0 : *resolved = false;
182 : 0 : return true; /* not resolved, but no error */
183 : : }
184 : :
185 : : /* let Object.prototype resolve these */
186 : 230 : const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
187 [ + - - + : 230 : if (id == atoms.to_string() || id == atoms.value_of()) {
- + ]
188 : 0 : *resolved = false;
189 : 0 : return true;
190 : : }
191 : :
192 : : gjs_debug_jsprop(GJS_DEBUG_GREPO, "Resolve prop '%s' hook, obj %s",
193 : : gjs_debug_id(id).c_str(), gjs_debug_object(obj).c_str());
194 : :
195 [ + + ]: 230 : if (!resolve_namespace_object(context, obj, id))
196 : 26 : return false;
197 : :
198 : 204 : *resolved = true;
199 : 204 : return true;
200 : : }
201 : :
202 : : static const struct JSClassOps gjs_repo_class_ops = {
203 : : nullptr, // addProperty
204 : : nullptr, // deleteProperty
205 : : nullptr, // enumerate
206 : : nullptr, // newEnumerate
207 : : repo_resolve,
208 : : };
209 : :
210 : : struct JSClass gjs_repo_class = {
211 : : "GIRepository",
212 : : 0,
213 : : &gjs_repo_class_ops,
214 : : };
215 : :
216 : : GJS_JSAPI_RETURN_CONVENTION
217 : : static JSObject*
218 : 80 : repo_new(JSContext *context)
219 : : {
220 : 80 : JS::RootedObject repo(context, JS_NewObject(context, &gjs_repo_class));
221 [ - + ]: 80 : if (repo == nullptr)
222 : 0 : return nullptr;
223 : :
224 : : gjs_debug_lifecycle(GJS_DEBUG_GREPO, "repo constructor, obj %p",
225 : : repo.get());
226 : :
227 : 80 : const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
228 : 80 : JS::RootedObject versions(context, JS_NewPlainObject(context));
229 [ - + ]: 80 : if (!JS_DefinePropertyById(context, repo, atoms.versions(), versions,
230 : : JSPROP_PERMANENT | JSPROP_RESOLVING))
231 : 0 : return nullptr;
232 : :
233 : : /* GLib/GObject/Gio are fixed at 2.0, since we depend on them
234 : : * internally.
235 : : */
236 : 80 : JS::RootedString two_point_oh(context, JS_NewStringCopyZ(context, "2.0"));
237 : 80 : if (!JS_DefinePropertyById(context, versions, atoms.glib(), two_point_oh,
238 : 80 : JSPROP_PERMANENT) ||
239 [ + - ]: 80 : !JS_DefinePropertyById(context, versions, atoms.gobject(), two_point_oh,
240 [ + - ]: 160 : JSPROP_PERMANENT) ||
241 [ - + - + ]: 160 : !JS_DefinePropertyById(context, versions, atoms.gio(), two_point_oh,
242 : : JSPROP_PERMANENT))
243 : 0 : return nullptr;
244 : :
245 : : #if GLIB_CHECK_VERSION(2, 79, 2)
246 : : # if defined(G_OS_UNIX)
247 : 80 : if (!JS_DefineProperty(context, versions, "GLibUnix", two_point_oh,
248 [ + - ]: 160 : JSPROP_PERMANENT) ||
249 [ - + - + ]: 160 : !JS_DefineProperty(context, versions, "GioUnix", two_point_oh,
250 : : JSPROP_PERMANENT))
251 : 0 : return nullptr;
252 : : # elif defined(G_OS_WIN32)
253 : : if (!JS_DefineProperty(context, versions, "GLibWin32", two_point_oh,
254 : : JSPROP_PERMANENT) ||
255 : : !JS_DefineProperty(context, versions, "GioWin32", two_point_oh,
256 : : JSPROP_PERMANENT))
257 : : return nullptr;
258 : : # endif // G_OS_UNIX/G_OS_WIN32
259 : : #endif // GLib >= 2.79.2
260 : :
261 : 80 : JS::RootedObject private_ns(context, JS_NewPlainObject(context));
262 [ - + ]: 80 : if (!JS_DefinePropertyById(context, repo, atoms.private_ns_marker(),
263 : : private_ns, JSPROP_PERMANENT | JSPROP_RESOLVING))
264 : 0 : return nullptr;
265 : :
266 : 80 : return repo;
267 : 80 : }
268 : :
269 : : bool
270 : 80 : gjs_define_repo(JSContext *cx,
271 : : JS::MutableHandleObject repo)
272 : : {
273 : 80 : repo.set(repo_new(cx));
274 : 80 : return true;
275 : : }
276 : :
277 : : GJS_JSAPI_RETURN_CONVENTION
278 : 4694 : static bool gjs_value_from_constant_info(JSContext* cx, GIConstantInfo* info,
279 : : JS::MutableHandleValue value) {
280 : : GIArgument garg;
281 : 4694 : g_constant_info_get_value(info, &garg);
282 : :
283 : 4694 : GjsAutoTypeInfo type_info = g_constant_info_get_type(info);
284 : :
285 : 4694 : bool ok = gjs_value_from_gi_argument(cx, value, type_info, &garg, true);
286 : :
287 : 4694 : g_constant_info_free_value(info, &garg);
288 : 9388 : return ok;
289 : 4694 : }
290 : :
291 : : GJS_JSAPI_RETURN_CONVENTION
292 : : static bool
293 : 4694 : gjs_define_constant(JSContext *context,
294 : : JS::HandleObject in_object,
295 : : GIConstantInfo *info)
296 : : {
297 : 4694 : JS::RootedValue value(context);
298 : : const char *name;
299 : :
300 [ - + ]: 4694 : if (!gjs_value_from_constant_info(context, info, &value))
301 : 0 : return false;
302 : :
303 : 4694 : name = g_base_info_get_name((GIBaseInfo*) info);
304 : :
305 : 9388 : return JS_DefineProperty(context, in_object, name, value,
306 : 4694 : GJS_MODULE_PROP_FLAGS);
307 : 4694 : }
308 : :
309 : : #if GJS_VERBOSE_ENABLE_GI_USAGE
310 : : void
311 : : _gjs_log_info_usage(GIBaseInfo *info)
312 : : {
313 : : #define DIRECTION_STRING(d) ( ((d) == GI_DIRECTION_IN) ? "IN" : ((d) == GI_DIRECTION_OUT) ? "OUT" : "INOUT" )
314 : : #define TRANSFER_STRING(t) ( ((t) == GI_TRANSFER_NOTHING) ? "NOTHING" : ((t) == GI_TRANSFER_CONTAINER) ? "CONTAINER" : "EVERYTHING" )
315 : :
316 : : {
317 : : char *details;
318 : : GIInfoType info_type;
319 : : GIBaseInfo *container;
320 : :
321 : : info_type = g_base_info_get_type(info);
322 : :
323 : : if (info_type == GI_INFO_TYPE_FUNCTION) {
324 : : std::string args("{ ");
325 : : int n_args;
326 : : int i;
327 : : GITransfer retval_transfer;
328 : :
329 : : n_args = g_callable_info_get_n_args((GICallableInfo*) info);
330 : : for (i = 0; i < n_args; ++i) {
331 : : GIArgInfo *arg;
332 : : GIDirection direction;
333 : : GITransfer transfer;
334 : :
335 : : arg = g_callable_info_get_arg((GICallableInfo*)info, i);
336 : : direction = g_arg_info_get_direction(arg);
337 : : transfer = g_arg_info_get_ownership_transfer(arg);
338 : :
339 : : if (i > 0)
340 : : args += ", ";
341 : :
342 : : args += std::string("{ GI_DIRECTION_") +
343 : : DIRECTION_STRING(direction) + ", GI_TRANSFER_" +
344 : : TRANSFER_STRING(transfer) + " }";
345 : :
346 : : g_base_info_unref((GIBaseInfo*) arg);
347 : : }
348 : :
349 : : args += " }";
350 : :
351 : : retval_transfer = g_callable_info_get_caller_owns((GICallableInfo*) info);
352 : :
353 : : details = g_strdup_printf(
354 : : ".details = { .func = { .retval_transfer = GI_TRANSFER_%s, "
355 : : ".n_args = %d, .args = %s } }",
356 : : TRANSFER_STRING(retval_transfer), n_args, args.c_str());
357 : : } else {
358 : : details = g_strdup_printf(".details = { .nothing = {} }");
359 : : }
360 : :
361 : : container = g_base_info_get_container(info);
362 : :
363 : : gjs_debug_gi_usage("{ GI_INFO_TYPE_%s, \"%s\", \"%s\", \"%s\", %s },",
364 : : gjs_info_type_name(info_type),
365 : : g_base_info_get_namespace(info),
366 : : container ? g_base_info_get_name(container) : "",
367 : : g_base_info_get_name(info),
368 : : details);
369 : : g_free(details);
370 : : }
371 : : }
372 : : #endif /* GJS_VERBOSE_ENABLE_GI_USAGE */
373 : :
374 : : bool
375 : 8429 : gjs_define_info(JSContext *context,
376 : : JS::HandleObject in_object,
377 : : GIBaseInfo *info,
378 : : bool *defined)
379 : : {
380 : : #if GJS_VERBOSE_ENABLE_GI_USAGE
381 : : _gjs_log_info_usage(info);
382 : : #endif
383 : :
384 : 8429 : *defined = true;
385 : :
386 [ + + + + : 8429 : switch (g_base_info_get_type(info)) {
+ + + + +
- ]
387 : 2158 : case GI_INFO_TYPE_FUNCTION:
388 : : {
389 : : JSObject *f;
390 : 2158 : f = gjs_define_function(context, in_object, 0, (GICallableInfo*) info);
391 [ - + ]: 2158 : if (f == NULL)
392 : 0 : return false;
393 : : }
394 : 2158 : break;
395 : 530 : case GI_INFO_TYPE_OBJECT:
396 : : {
397 : : GType gtype;
398 : 530 : gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)info);
399 : :
400 [ + + - + : 530 : if (g_type_is_a (gtype, G_TYPE_PARAM)) {
+ + ]
401 [ - + ]: 51 : if (!gjs_define_param_class(context, in_object))
402 : 0 : return false;
403 [ + + + + : 479 : } else if (g_type_is_a (gtype, G_TYPE_OBJECT)) {
+ + ]
404 : 478 : JS::RootedObject ignored1(context), ignored2(context);
405 [ - + ]: 478 : if (!ObjectPrototype::define_class(context, in_object, info,
406 : : gtype, nullptr, 0, &ignored1,
407 : : &ignored2))
408 : 0 : return false;
409 [ + - + - : 479 : } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) {
+ - ]
410 : 1 : JS::RootedObject ignored(context);
411 [ - + ]: 1 : if (!FundamentalPrototype::define_class(context, in_object,
412 : : info, &ignored))
413 : 0 : return false;
414 [ + - ]: 1 : } else {
415 : 0 : gjs_throw (context,
416 : : "Unsupported type %s, deriving from fundamental %s",
417 : : g_type_name(gtype), g_type_name(g_type_fundamental(gtype)));
418 : 0 : return false;
419 : : }
420 : : }
421 : 530 : break;
422 : 676 : case GI_INFO_TYPE_STRUCT:
423 : : /* We don't want GType structures in the namespace,
424 : : we expose their fields as vfuncs and their methods
425 : : as static methods
426 : : */
427 [ + + ]: 676 : if (g_struct_info_is_gtype_struct((GIStructInfo*) info)) {
428 : 10 : *defined = false;
429 : 10 : break;
430 : : }
431 : : /* Fall through */
432 : :
433 : : case GI_INFO_TYPE_BOXED:
434 [ - + ]: 717 : if (!BoxedPrototype::define_class(context, in_object, info))
435 : 0 : return false;
436 : 717 : break;
437 : 3 : case GI_INFO_TYPE_UNION:
438 [ - + ]: 3 : if (!UnionPrototype::define_class(context, in_object,
439 : : (GIUnionInfo*)info))
440 : 0 : return false;
441 : 3 : break;
442 : 109 : case GI_INFO_TYPE_ENUM:
443 [ + + ]: 109 : if (g_enum_info_get_error_domain((GIEnumInfo*) info)) {
444 : : /* define as GError subclass */
445 [ - + ]: 11 : if (!ErrorPrototype::define_class(context, in_object, info))
446 : 0 : return false;
447 : 11 : break;
448 : : }
449 : : [[fallthrough]];
450 : :
451 : : case GI_INFO_TYPE_FLAGS:
452 [ - + ]: 174 : if (!gjs_define_enumeration(context, in_object, (GIEnumInfo*) info))
453 : 0 : return false;
454 : 174 : break;
455 : 4694 : case GI_INFO_TYPE_CONSTANT:
456 [ - + ]: 4694 : if (!gjs_define_constant(context, in_object, (GIConstantInfo*) info))
457 : 0 : return false;
458 : 4694 : break;
459 : 132 : case GI_INFO_TYPE_INTERFACE:
460 : : {
461 : 132 : JS::RootedObject ignored1(context), ignored2(context);
462 [ - + ]: 132 : if (!InterfacePrototype::create_class(
463 : : context, in_object, info,
464 : : g_registered_type_info_get_g_type(info), &ignored1,
465 : : &ignored2))
466 : 0 : return false;
467 [ + - + - ]: 132 : }
468 : 132 : break;
469 : 0 : case GI_INFO_TYPE_INVALID:
470 : : case GI_INFO_TYPE_INVALID_0:
471 : : case GI_INFO_TYPE_CALLBACK:
472 : : case GI_INFO_TYPE_VALUE:
473 : : case GI_INFO_TYPE_SIGNAL:
474 : : case GI_INFO_TYPE_VFUNC:
475 : : case GI_INFO_TYPE_PROPERTY:
476 : : case GI_INFO_TYPE_FIELD:
477 : : case GI_INFO_TYPE_ARG:
478 : : case GI_INFO_TYPE_TYPE:
479 : : case GI_INFO_TYPE_UNRESOLVED:
480 : : default:
481 : 0 : gjs_throw(context, "API of type %s not implemented, cannot define %s.%s",
482 : : gjs_info_type_name(g_base_info_get_type(info)),
483 : : g_base_info_get_namespace(info),
484 : : g_base_info_get_name(info));
485 : 0 : return false;
486 : : }
487 : :
488 : 8429 : return true;
489 : : }
490 : :
491 : : /* Get the "unknown namespace", which should be used for unnamespaced types */
492 : : JSObject*
493 : 295 : gjs_lookup_private_namespace(JSContext *context)
494 : : {
495 : 295 : const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
496 : 295 : return gjs_lookup_namespace_object_by_name(context,
497 : 295 : atoms.private_ns_marker());
498 : : }
499 : :
500 : : /* Get the namespace object that the GIBaseInfo should be inside */
501 : : JSObject*
502 : 16449 : gjs_lookup_namespace_object(JSContext *context,
503 : : GIBaseInfo *info)
504 : : {
505 : : const char *ns;
506 : :
507 : 16449 : ns = g_base_info_get_namespace(info);
508 [ - + ]: 16449 : if (ns == NULL) {
509 : 0 : gjs_throw(context, "%s '%s' does not have a namespace",
510 : : gjs_info_type_name(g_base_info_get_type(info)),
511 : : g_base_info_get_name(info));
512 : :
513 : 0 : return NULL;
514 : : }
515 : :
516 : 16449 : JS::RootedId ns_name(context, gjs_intern_string_to_id(context, ns));
517 [ - + ]: 16449 : if (ns_name.isVoid())
518 : 0 : return nullptr;
519 : 16449 : return gjs_lookup_namespace_object_by_name(context, ns_name);
520 : 16449 : }
521 : :
522 : : /* Check if an exception's 'name' property is equal to ImportError. Ignores
523 : : * all errors that might arise. */
524 : 67 : [[nodiscard]] static bool is_import_error(JSContext* cx,
525 : : JS::HandleValue thrown_value) {
526 [ + + ]: 67 : if (!thrown_value.isObject())
527 : 1 : return false;
528 : :
529 : 66 : JS::AutoSaveExceptionState saved_exc(cx);
530 : 66 : JS::RootedObject exc(cx, &thrown_value.toObject());
531 : 66 : JS::RootedValue exc_name(cx);
532 : 66 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
533 : : bool eq;
534 : : bool retval =
535 [ + - ]: 132 : JS_GetPropertyById(cx, exc, atoms.name(), &exc_name) &&
536 [ + - + + ]: 132 : JS_StringEqualsLiteral(cx, exc_name.toString(), "ImportError", &eq) &&
537 : 66 : eq;
538 : :
539 : 66 : saved_exc.restore();
540 : 66 : return retval;
541 : 66 : }
542 : :
543 : : GJS_JSAPI_RETURN_CONVENTION
544 : : static bool
545 : 210 : lookup_override_function(JSContext *cx,
546 : : JS::HandleId ns_name,
547 : : JS::MutableHandleValue function)
548 : : {
549 : 210 : JS::AutoSaveExceptionState saved_exc(cx);
550 : :
551 : 210 : JS::RootedObject global{cx, JS::CurrentGlobalOrNull(cx)};
552 : : JS::RootedValue importer(
553 : 210 : cx, gjs_get_global_slot(global, GjsGlobalSlot::IMPORTS));
554 : 210 : g_assert(importer.isObject());
555 : :
556 : 210 : JS::RootedObject overridespkg(cx), module(cx);
557 : 210 : JS::RootedObject importer_obj(cx, &importer.toObject());
558 : 210 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
559 [ - + ]: 210 : if (!gjs_object_require_property(cx, importer_obj, "importer",
560 : : atoms.overrides(), &overridespkg))
561 : 0 : return false;
562 : :
563 [ + + ]: 210 : if (!gjs_object_require_property(cx, overridespkg,
564 : : "GI repository object", ns_name,
565 : : &module)) {
566 : 67 : JS::RootedValue exc(cx);
567 : 67 : JS_GetPendingException(cx, &exc);
568 : :
569 : : /* If the exception was an ImportError (i.e., module not found) then
570 : : * we simply didn't have an override, don't throw an exception */
571 [ + + ]: 67 : if (is_import_error(cx, exc)) {
572 : 65 : saved_exc.restore();
573 : 65 : return true;
574 : : }
575 : :
576 : 2 : return false;
577 : 67 : }
578 : :
579 : : // If the override module is present, it must have a callable _init(). An
580 : : // override module without _init() is probably unintentional. (function
581 : : // being undefined means there was no override module.)
582 : 143 : if (!gjs_object_require_property(cx, module, "override module",
583 : 142 : atoms.init(), function) ||
584 [ + + + + : 143 : !function.isObject() || !JS::IsCallable(&function.toObject())) {
+ + + + ]
585 : 4 : gjs_throw(cx, "Unexpected value for _init in overrides module");
586 : 4 : return false;
587 : : }
588 : 139 : return true;
589 : 210 : }
590 : :
591 : : GJS_JSAPI_RETURN_CONVENTION
592 : 16997 : static JSObject* lookup_namespace(JSContext* cx, JSObject* global,
593 : : JS::HandleId ns_name) {
594 : 16997 : JS::RootedObject native_registry(cx, gjs_get_native_registry(global));
595 : 16997 : auto priv = GjsContextPrivate::from_cx(cx);
596 : 16997 : const GjsAtoms& atoms = priv->atoms();
597 : 16997 : JS::RootedObject gi(cx);
598 : :
599 [ - + ]: 16997 : if (!gjs_global_registry_get(cx, native_registry, atoms.gi(), &gi))
600 : 0 : return nullptr;
601 : :
602 [ - + ]: 16997 : if (!gi) {
603 : 0 : gjs_throw(cx, "No gi property in native registry");
604 : 0 : return nullptr;
605 : : }
606 : :
607 : 16997 : JS::RootedObject retval(cx);
608 [ - + ]: 16997 : if (!gjs_object_require_property(cx, gi, "GI repository object", ns_name,
609 : : &retval))
610 : 0 : return NULL;
611 : :
612 : 16997 : return retval;
613 : 16997 : }
614 : :
615 : 16997 : JSObject* gjs_lookup_namespace_object_by_name(JSContext* cx,
616 : : JS::HandleId ns_name) {
617 : 16997 : JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
618 : :
619 : 16997 : g_assert(gjs_global_get_type(global) == GjsGlobalType::DEFAULT);
620 : 16997 : return lookup_namespace(cx, global, ns_name);
621 : 16997 : }
622 : :
623 : : const char*
624 : 8429 : gjs_info_type_name(GIInfoType type)
625 : : {
626 [ - + - + : 8429 : switch (type) {
+ + + + +
+ + - - -
- - - - -
- - ]
627 : 0 : case GI_INFO_TYPE_INVALID:
628 : 0 : return "INVALID";
629 : 2158 : case GI_INFO_TYPE_FUNCTION:
630 : 2158 : return "FUNCTION";
631 : 0 : case GI_INFO_TYPE_CALLBACK:
632 : 0 : return "CALLBACK";
633 : 676 : case GI_INFO_TYPE_STRUCT:
634 : 676 : return "STRUCT";
635 : 51 : case GI_INFO_TYPE_BOXED:
636 : 51 : return "BOXED";
637 : 109 : case GI_INFO_TYPE_ENUM:
638 : 109 : return "ENUM";
639 : 76 : case GI_INFO_TYPE_FLAGS:
640 : 76 : return "FLAGS";
641 : 530 : case GI_INFO_TYPE_OBJECT:
642 : 530 : return "OBJECT";
643 : 132 : case GI_INFO_TYPE_INTERFACE:
644 : 132 : return "INTERFACE";
645 : 4694 : case GI_INFO_TYPE_CONSTANT:
646 : 4694 : return "CONSTANT";
647 : 3 : case GI_INFO_TYPE_UNION:
648 : 3 : return "UNION";
649 : 0 : case GI_INFO_TYPE_VALUE:
650 : 0 : return "VALUE";
651 : 0 : case GI_INFO_TYPE_SIGNAL:
652 : 0 : return "SIGNAL";
653 : 0 : case GI_INFO_TYPE_VFUNC:
654 : 0 : return "VFUNC";
655 : 0 : case GI_INFO_TYPE_PROPERTY:
656 : 0 : return "PROPERTY";
657 : 0 : case GI_INFO_TYPE_FIELD:
658 : 0 : return "FIELD";
659 : 0 : case GI_INFO_TYPE_ARG:
660 : 0 : return "ARG";
661 : 0 : case GI_INFO_TYPE_TYPE:
662 : 0 : return "TYPE";
663 : 0 : case GI_INFO_TYPE_UNRESOLVED:
664 : 0 : return "UNRESOLVED";
665 : 0 : case GI_INFO_TYPE_INVALID_0:
666 : : g_assert_not_reached();
667 : : return "----INVALID0----";
668 : 0 : default:
669 : 0 : return "???";
670 : : }
671 : : }
672 : :
673 : : char*
674 : 3382 : gjs_hyphen_from_camel(const char *camel_name)
675 : : {
676 : : GString *s;
677 : : const char *p;
678 : :
679 : : /* four hyphens should be reasonable guess */
680 : 3382 : s = g_string_sized_new(strlen(camel_name) + 4 + 1);
681 : :
682 [ + + ]: 40289 : for (p = camel_name; *p; ++p) {
683 [ + + ]: 36907 : if (g_ascii_isupper(*p)) {
684 : : g_string_append_c(s, '-');
685 [ + - ]: 897 : g_string_append_c(s, g_ascii_tolower(*p));
686 : : } else {
687 [ + - ]: 36010 : g_string_append_c(s, *p);
688 : : }
689 : : }
690 : :
691 : 3382 : return g_string_free(s, false);
692 : : }
693 : :
694 : : JSObject *
695 : 14150 : gjs_lookup_generic_constructor(JSContext *context,
696 : : GIBaseInfo *info)
697 : : {
698 : : JS::RootedObject in_object{context,
699 : 14150 : gjs_lookup_namespace_object(context, info)};
700 : 14150 : const char* constructor_name = g_base_info_get_name(info);
701 : :
702 [ - + ]: 14150 : if (G_UNLIKELY (!in_object))
703 : 0 : return NULL;
704 : :
705 : 14150 : JS::RootedValue value(context);
706 [ - + ]: 14150 : if (!JS_GetProperty(context, in_object, constructor_name, &value))
707 : 0 : return NULL;
708 : :
709 [ - + ]: 14150 : if (G_UNLIKELY(!value.isObject())) {
710 : 0 : gjs_throw(context,
711 : : "Constructor of %s.%s was the wrong type, expected an object",
712 : : g_base_info_get_namespace(info), constructor_name);
713 : 0 : return NULL;
714 : : }
715 : :
716 : 14150 : return &value.toObject();
717 : 14150 : }
718 : :
719 : : JSObject *
720 : 14150 : gjs_lookup_generic_prototype(JSContext *context,
721 : : GIBaseInfo *info)
722 : : {
723 : : JS::RootedObject constructor(context,
724 : 14150 : gjs_lookup_generic_constructor(context, info));
725 [ - + ]: 14150 : if (G_UNLIKELY(!constructor))
726 : 0 : return NULL;
727 : :
728 : 14150 : const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
729 : 14150 : JS::RootedValue value(context);
730 [ - + ]: 14150 : if (!JS_GetPropertyById(context, constructor, atoms.prototype(), &value))
731 : 0 : return NULL;
732 : :
733 [ - + ]: 14150 : if (G_UNLIKELY(!value.isObject())) {
734 : 0 : gjs_throw(context,
735 : : "Prototype of %s.%s was the wrong type, expected an object",
736 : : g_base_info_get_namespace(info), g_base_info_get_name(info));
737 : 0 : return NULL;
738 : : }
739 : :
740 : 14150 : return &value.toObject();
741 : 14150 : }
742 : :
743 : 14139 : JSObject* gjs_new_object_with_generic_prototype(JSContext* cx,
744 : : GIBaseInfo* info) {
745 : 14139 : JS::RootedObject proto(cx, gjs_lookup_generic_prototype(cx, info));
746 [ - + ]: 14139 : if (!proto)
747 : 0 : return nullptr;
748 : :
749 : 14139 : return JS_NewObjectWithGivenProto(cx, JS::GetClass(proto), proto);
750 : 14139 : }
751 : :
752 : : // Handle the case where g_irepository_find_by_gtype() returns a type in Gio
753 : : // that should be in GioUnix or GioWin32. This may be an interface, class, or
754 : : // boxed. This function only needs to be called if you are going to do something
755 : : // with the GIBaseInfo that involves handing a JS object to the user. Otherwise,
756 : : // use g_irepository_find_by_gtype() directly.
757 : 1890 : GIBaseInfo* gjs_lookup_gtype(GIRepository* repo, GType gtype) {
758 : 1890 : GjsAutoBaseInfo retval = g_irepository_find_by_gtype(repo, gtype);
759 [ + + ]: 1890 : if (!retval)
760 : 170 : return nullptr;
761 : :
762 : : #if GLIB_CHECK_VERSION(2, 79, 2) && (defined(G_OS_UNIX) || defined(G_OS_WIN32))
763 : : # ifdef G_OS_UNIX
764 : : static const char* c_prefix = "GUnix";
765 : : static const char* new_ns = "GioUnix";
766 : : # else // G_OS_WIN32
767 : : static const char* c_prefix = "GWin32";
768 : : static const char* new_ns = "GioWin32";
769 : : # endif
770 : :
771 : 1720 : const char* ns = g_base_info_get_namespace(retval);
772 [ + + ]: 1720 : if (strcmp(ns, "Gio") != 0)
773 : 1270 : return retval.release();
774 : :
775 : 450 : const char* gtype_name = g_type_name(gtype);
776 [ + + ]: 450 : if (!g_str_has_prefix(gtype_name, c_prefix))
777 : 433 : return retval.release();
778 : :
779 : 17 : const char* new_name = gtype_name + strlen(c_prefix);
780 : 17 : GIBaseInfo* new_info = g_irepository_find_by_name(repo, new_ns, new_name);
781 [ - + ]: 17 : if (new_info)
782 : 0 : return new_info;
783 : : #endif // GLib >= 2.79.2
784 : :
785 : 17 : return retval.release();
786 : 1890 : }
|