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