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