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 : : #include <girepository/girepository.h>
10 : : #include <glib-object.h>
11 : : #include <glib.h>
12 : :
13 : : #include <js/CallAndConstruct.h> // for JS_CallFunctionValue
14 : : #include <js/Class.h>
15 : : #include <js/ComparisonOperators.h>
16 : : #include <js/Exception.h>
17 : : #include <js/GlobalObject.h> // for CurrentGlobalOrNull
18 : : #include <js/Id.h> // for PropertyKey
19 : : #include <js/Object.h> // for GetClass
20 : : #include <js/PropertyAndElement.h>
21 : : #include <js/PropertyDescriptor.h> // for JSPROP_PERMANENT, JSPROP_RESOLVING
22 : : #include <js/RootingAPI.h>
23 : : #include <js/String.h>
24 : : #include <js/TypeDecls.h>
25 : : #include <js/Utility.h> // for UniqueChars
26 : : #include <js/Value.h>
27 : : #include <js/ValueArray.h>
28 : : #include <js/Warnings.h>
29 : : #include <jsapi.h> // for JS_NewPlainObject, JS_NewObject
30 : : #include <mozilla/Maybe.h>
31 : : #include <mozilla/ScopeExit.h>
32 : : #include <mozilla/Unused.h>
33 : :
34 : : #include "gi/arg.h"
35 : : #include "gi/enumeration.h"
36 : : #include "gi/function.h"
37 : : #include "gi/fundamental.h"
38 : : #include "gi/gerror.h"
39 : : #include "gi/info.h"
40 : : #include "gi/interface.h"
41 : : #include "gi/ns.h"
42 : : #include "gi/object.h"
43 : : #include "gi/param.h"
44 : : #include "gi/repo.h"
45 : : #include "gi/struct.h"
46 : : #include "gi/union.h"
47 : : #include "gjs/atoms.h"
48 : : #include "gjs/auto.h"
49 : : #include "gjs/context-private.h"
50 : : #include "gjs/gerror-result.h"
51 : : #include "gjs/global.h"
52 : : #include "gjs/jsapi-util.h"
53 : : #include "gjs/macros.h"
54 : : #include "gjs/module.h"
55 : : #include "util/log.h"
56 : :
57 : : using mozilla::Maybe;
58 : :
59 : : GJS_JSAPI_RETURN_CONVENTION
60 : : static bool lookup_override_function(JSContext*, JS::HandleId,
61 : : JS::MutableHandleValue);
62 : :
63 : : GJS_JSAPI_RETURN_CONVENTION
64 : 353 : static bool get_version_for_ns(JSContext* cx, JS::HandleObject repo_obj,
65 : : JS::HandleId ns_id, JS::UniqueChars* version) {
66 : 353 : JS::RootedObject versions{cx};
67 : : bool found;
68 : 353 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
69 : :
70 [ - + ]: 353 : if (!gjs_object_require_property(cx, repo_obj, "GI repository object",
71 : 353 : atoms.versions(), &versions))
72 : 0 : return false;
73 : :
74 [ - + ]: 353 : if (!JS_AlreadyHasOwnPropertyById(cx, versions, ns_id, &found))
75 : 0 : return false;
76 : :
77 [ + + ]: 353 : if (!found)
78 : 98 : return true;
79 : :
80 : 255 : return gjs_object_require_property(cx, versions, nullptr, ns_id, version);
81 : 353 : }
82 : :
83 : : GJS_JSAPI_RETURN_CONVENTION
84 : 353 : static bool resolve_namespace_object(JSContext* cx, JS::HandleObject repo_obj,
85 : : JS::HandleId ns_id) {
86 : 353 : JS::UniqueChars version;
87 [ - + ]: 353 : if (!get_version_for_ns(cx, repo_obj, ns_id, &version))
88 : 0 : return false;
89 : :
90 : 353 : JS::UniqueChars ns_name;
91 [ - + ]: 353 : if (!gjs_get_string_id(cx, ns_id, &ns_name))
92 : 0 : return false;
93 [ - + ]: 353 : if (!ns_name) {
94 : 0 : gjs_throw(cx, "Requiring invalid namespace on imports.gi");
95 : 0 : return false;
96 : : }
97 : :
98 : 353 : GI::Repository repo;
99 : : size_t nversions;
100 : 706 : mozilla::Unused << repo.enumerate_versions(ns_name.get(), &nversions);
101 [ - + ]: 16 : if (nversions > 1 && !version &&
102 [ + + - - : 369 : !repo.is_registered(ns_name.get(), nullptr) &&
- + ]
103 [ # # ]: 0 : !JS::WarnUTF8(cx,
104 : : "Requiring %s but it has %zu versions available; use "
105 : : "imports.gi.versions to pick one",
106 : : ns_name.get(), nversions))
107 : 0 : return false;
108 : :
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 (defined(G_OS_UNIX) || defined(G_OS_WIN32))
113 [ + + ]: 353 : if (strcmp(ns_name.get(), "Gio") == 0) {
114 : : # ifdef G_OS_UNIX
115 : 25 : const char* platform = "Unix";
116 : : # else // G_OS_WIN32
117 : : const char* platform = "Win32";
118 : : # endif // G_OS_UNIX/G_OS_WIN32
119 : : Gjs::AutoChar platform_specific{
120 : 25 : g_strconcat(ns_name.get(), platform, nullptr)};
121 : 25 : auto required = repo.require(platform_specific, version.get());
122 [ - + ]: 25 : if (!required.isOk()) {
123 : 0 : gjs_throw(cx, "Failed to require %s %s: %s",
124 : : platform_specific.get(), version.get(),
125 : 0 : required.inspectErr()->message);
126 : 0 : return false;
127 : : }
128 [ + - + - ]: 25 : }
129 : : #endif // (defined(G_OS_UNIX) || defined(G_OS_WIN32))
130 : :
131 : 353 : auto required = repo.require(ns_name.get(), version.get());
132 [ + + ]: 353 : if (!required.isOk()) {
133 [ + + ]: 5 : gjs_throw(cx, "Requiring %s, version %s: %s", ns_name.get(),
134 : 2 : version ? version.get() : "none",
135 : 3 : required.inspectErr()->message);
136 : 3 : return false;
137 : : }
138 : :
139 : : /* Defines a property on "obj" (the javascript repo object) with the given
140 : : * namespace name, pointing to that namespace in the repo.
141 : : */
142 : 350 : JS::RootedObject gi_namespace{cx, gjs_create_ns(cx, ns_name.get())};
143 : :
144 : 350 : JS::RootedValue override{cx};
145 [ + + ]: 693 : if (!lookup_override_function(cx, ns_id, &override) ||
146 : : // Define the property early, to avoid reentrancy issues if the override
147 : : // module looks for namespaces that import this
148 [ - + + + ]: 693 : !JS_DefinePropertyById(cx, repo_obj, ns_id, gi_namespace,
149 : : GJS_MODULE_PROP_FLAGS))
150 : 7 : return false;
151 : :
152 : 343 : JS::RootedValue result{cx};
153 [ + + ]: 503 : if (!override.isUndefined() &&
154 [ + + ]: 160 : !JS_CallFunctionValue(cx, /* this_obj = */ gi_namespace, override,
155 [ + + ]: 503 : JS::HandleValueArray::empty(), &result))
156 : 1 : return false;
157 : :
158 : 342 : gjs_debug(GJS_DEBUG_GNAMESPACE,
159 : : "Defined namespace '%s' %p in GIRepository %p", ns_name.get(),
160 : 342 : gi_namespace.get(), repo_obj.get());
161 : :
162 : 342 : GjsContextPrivate* gjs = GjsContextPrivate::from_cx(cx);
163 : 342 : gjs->schedule_gc_if_needed();
164 : 342 : return true;
165 : 353 : }
166 : :
167 : : /*
168 : : * The *resolved out parameter, on success, should be false to indicate that id
169 : : * was not resolved; and true if id was resolved.
170 : : */
171 : : GJS_JSAPI_RETURN_CONVENTION
172 : 353 : static bool repo_resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
173 : : bool* resolved) {
174 [ - + ]: 353 : if (!id.isString()) {
175 : 0 : *resolved = false;
176 : 0 : return true; // not resolved, but no error
177 : : }
178 : :
179 : : // let Object.prototype resolve these
180 : 353 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
181 [ + - - + : 353 : if (id == atoms.to_string() || id == atoms.value_of()) {
- + ]
182 : 0 : *resolved = false;
183 : 0 : return true;
184 : : }
185 : :
186 : : gjs_debug_jsprop(GJS_DEBUG_GREPO, "Resolve prop '%s' hook, obj %s",
187 : : gjs_debug_id(id).c_str(), gjs_debug_object(obj).c_str());
188 : :
189 [ + + ]: 353 : if (!resolve_namespace_object(cx, obj, id))
190 : 11 : return false;
191 : :
192 : 342 : *resolved = true;
193 : 342 : return true;
194 : : }
195 : :
196 : : static const struct JSClassOps gjs_repo_class_ops = {
197 : : nullptr, // addProperty
198 : : nullptr, // deleteProperty
199 : : nullptr, // enumerate
200 : : nullptr, // newEnumerate
201 : : repo_resolve,
202 : : };
203 : :
204 : : struct JSClass gjs_repo_class = {
205 : : "GIRepository",
206 : : 0,
207 : : &gjs_repo_class_ops,
208 : : };
209 : :
210 : : GJS_JSAPI_RETURN_CONVENTION
211 : 88 : static JSObject* repo_new(JSContext* cx) {
212 : 88 : JS::RootedObject repo{cx, JS_NewObject(cx, &gjs_repo_class)};
213 [ - + ]: 88 : if (repo == nullptr)
214 : 0 : return nullptr;
215 : :
216 : : gjs_debug_lifecycle(GJS_DEBUG_GREPO, "repo constructor, obj %p",
217 : : repo.get());
218 : :
219 : 88 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
220 : 88 : JS::RootedObject versions{cx, JS_NewPlainObject(cx)};
221 [ - + ]: 88 : if (!JS_DefinePropertyById(cx, repo, atoms.versions(), versions,
222 : : JSPROP_PERMANENT | JSPROP_RESOLVING))
223 : 0 : return nullptr;
224 : :
225 : : // GLib/GObject/Gio are fixed at 2.0, since we depend on them internally.
226 : 88 : JS::RootedString two_point_oh{cx, JS_NewStringCopyZ(cx, "2.0")};
227 : 88 : if (!JS_DefinePropertyById(cx, versions, atoms.glib(), two_point_oh,
228 : 88 : JSPROP_PERMANENT) ||
229 [ + - ]: 88 : !JS_DefinePropertyById(cx, versions, atoms.gobject(), two_point_oh,
230 [ + - ]: 176 : JSPROP_PERMANENT) ||
231 [ - + - + ]: 176 : !JS_DefinePropertyById(cx, versions, atoms.gio(), two_point_oh,
232 : : JSPROP_PERMANENT))
233 : 0 : return nullptr;
234 : :
235 : : #ifdef G_OS_UNIX
236 : 88 : if (!JS_DefineProperty(cx, versions, "GLibUnix", two_point_oh,
237 [ + - ]: 176 : JSPROP_PERMANENT) ||
238 [ - + - + ]: 176 : !JS_DefineProperty(cx, versions, "GioUnix", two_point_oh,
239 : : JSPROP_PERMANENT))
240 : 0 : return nullptr;
241 : : #elif defined(G_OS_WIN32)
242 : : if (!JS_DefineProperty(cx, versions, "GLibWin32", two_point_oh,
243 : : JSPROP_PERMANENT) ||
244 : : !JS_DefineProperty(cx, versions, "GioWin32", two_point_oh,
245 : : JSPROP_PERMANENT))
246 : : return nullptr;
247 : : #endif // G_OS_UNIX/G_OS_WIN32
248 : :
249 : 88 : JS::RootedObject private_ns{cx, JS_NewPlainObject(cx)};
250 [ - + ]: 88 : if (!JS_DefinePropertyById(cx, repo, atoms.private_ns_marker(), private_ns,
251 : : JSPROP_PERMANENT | JSPROP_RESOLVING))
252 : 0 : return nullptr;
253 : :
254 : 88 : return repo;
255 : 88 : }
256 : :
257 : 88 : bool gjs_define_repo(JSContext* cx, JS::MutableHandleObject repo) {
258 : 88 : repo.set(repo_new(cx));
259 : 88 : return true;
260 : : }
261 : :
262 : : GJS_JSAPI_RETURN_CONVENTION
263 : 4872 : static bool gjs_value_from_constant_info(JSContext* cx,
264 : : const GI::ConstantInfo info,
265 : : JS::MutableHandleValue value) {
266 : : GIArgument garg;
267 : 4872 : info.load_value(&garg);
268 : : auto guard =
269 : 9744 : mozilla::MakeScopeExit([&info, &garg]() { info.free_value(&garg); });
270 : :
271 : 4872 : return gjs_value_from_gi_argument(cx, value, info.type_info(), &garg, true);
272 : 4872 : }
273 : :
274 : : GJS_JSAPI_RETURN_CONVENTION
275 : 4872 : static bool gjs_define_constant(JSContext* cx, JS::HandleObject in_object,
276 : : const GI::ConstantInfo& info) {
277 : 4872 : JS::RootedValue value{cx};
278 : :
279 [ - + ]: 4872 : if (!gjs_value_from_constant_info(cx, info, &value))
280 : 0 : return false;
281 : :
282 : 9744 : return JS_DefineProperty(cx, in_object, info.name(), value,
283 : 4872 : GJS_MODULE_PROP_FLAGS);
284 : 4872 : }
285 : :
286 : 10949 : bool gjs_define_info(JSContext* cx, JS::HandleObject in_object,
287 : : const GI::BaseInfo& info, bool* defined) {
288 : 10949 : info.log_usage();
289 : :
290 : 10949 : *defined = true;
291 : :
292 [ + + ]: 10949 : if (auto func_info = info.as<GI::InfoTag::FUNCTION>())
293 [ + + ]: 10949 : return gjs_define_function(cx, in_object, 0, func_info.value());
294 : :
295 [ + + ]: 7382 : if (auto object_info = info.as<GI::InfoTag::OBJECT>()) {
296 : 797 : GType gtype = object_info->gtype();
297 : :
298 [ + + - + : 797 : if (g_type_is_a(gtype, G_TYPE_PARAM))
+ + ]
299 : 59 : return gjs_define_param_class(cx, in_object);
300 : :
301 [ + + + + : 738 : if (g_type_is_a(gtype, G_TYPE_OBJECT)) {
+ + ]
302 : 728 : JS::RootedObject ignored1{cx}, ignored2{cx};
303 : 728 : return ObjectPrototype::define_class(cx, in_object, object_info,
304 : 728 : gtype, nullptr, 0, &ignored1,
305 : 1456 : &ignored2);
306 : 728 : }
307 : :
308 [ + - ]: 10 : if (G_TYPE_IS_INSTANTIATABLE(gtype)) {
309 : 10 : JS::RootedObject ignored{cx};
310 : 10 : return FundamentalPrototype::define_class(
311 : 20 : cx, in_object, object_info.value(), &ignored);
312 : 10 : }
313 : :
314 : 0 : gjs_throw(cx, "Unsupported type %s, deriving from fundamental %s",
315 : : g_type_name(gtype), g_type_name(g_type_fundamental(gtype)));
316 : 0 : return false;
317 [ + + ]: 7382 : }
318 : :
319 : 6585 : auto struct_info = info.as<GI::InfoTag::STRUCT>();
320 : : // We don't want GType structures in the namespace, we expose their fields
321 : : // as vfuncs and their methods as static methods
322 [ + + + + : 6585 : if (struct_info && struct_info->is_gtype_struct()) {
+ + ]
323 : 171 : *defined = false;
324 : 171 : return true;
325 : : }
326 : :
327 [ + + ]: 6414 : if (struct_info)
328 : 1040 : return StructPrototype::define_class(cx, in_object,
329 : 2080 : struct_info.value());
330 : :
331 [ + + ]: 5374 : if (auto union_info = info.as<GI::InfoTag::UNION>())
332 [ + + ]: 5374 : return UnionPrototype::define_class(cx, in_object, union_info.value());
333 : :
334 [ + + ]: 5366 : if (auto enum_info = info.as<GI::InfoTag::ENUM>()) {
335 [ + + + + : 239 : if (!info.is_flags() && enum_info->error_domain()) {
+ + ]
336 : : // define as GError subclass
337 : 14 : return ErrorPrototype::define_class(cx, in_object,
338 : 28 : enum_info.value());
339 : : }
340 : :
341 : 225 : return gjs_define_enumeration(cx, in_object, enum_info.value());
342 [ + + ]: 5366 : }
343 : :
344 [ + + ]: 5127 : if (auto constant_info = info.as<GI::InfoTag::CONSTANT>())
345 [ + + ]: 5127 : return gjs_define_constant(cx, in_object, constant_info.value());
346 : :
347 [ + - ]: 255 : if (auto interface_info = info.as<GI::InfoTag::INTERFACE>()) {
348 : 255 : JS::RootedObject ignored1{cx}, ignored2{cx};
349 : 255 : return InterfacePrototype::create_class(cx, in_object, interface_info,
350 : 255 : interface_info->gtype(),
351 : 510 : &ignored1, &ignored2);
352 [ - + ]: 510 : }
353 : :
354 : 0 : gjs_throw(cx, "API of type %s not implemented, cannot define %s.%s",
355 : : info.type_string(), info.ns(), info.name());
356 : 0 : return false;
357 : 6585 : }
358 : :
359 : : // Get the "unknown namespace", which should be used for unnamespaced types
360 : 329 : JSObject* gjs_lookup_private_namespace(JSContext* cx) {
361 : 329 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
362 : 329 : return gjs_lookup_namespace_object_by_name(cx, atoms.private_ns_marker());
363 : : }
364 : :
365 : : // Get the namespace object that the GIBaseInfo should be inside
366 : 24486 : JSObject* gjs_lookup_namespace_object(JSContext* cx, const GI::BaseInfo& info) {
367 : 24486 : const char* ns = info.ns();
368 [ - + ]: 24486 : if (ns == nullptr) {
369 : 0 : gjs_throw(cx, "%s '%s' does not have a namespace", info.type_string(),
370 : : info.name());
371 : :
372 : 0 : return nullptr;
373 : : }
374 : :
375 : 24486 : JS::RootedId ns_name{cx, gjs_intern_string_to_id(cx, ns)};
376 [ - + ]: 24486 : if (ns_name.isVoid())
377 : 0 : return nullptr;
378 : 24486 : return gjs_lookup_namespace_object_by_name(cx, ns_name);
379 : 24486 : }
380 : :
381 : : /* Check if an exception's 'name' property is equal to ImportError. Ignores all
382 : : * errors that might arise. */
383 : : [[nodiscard]]
384 : 185 : static bool is_import_error(JSContext* cx, JS::HandleValue thrown_value) {
385 [ + + ]: 185 : if (!thrown_value.isObject())
386 : 1 : return false;
387 : :
388 : 184 : JS::AutoSaveExceptionState saved_exc(cx);
389 : 184 : JS::RootedObject exc(cx, &thrown_value.toObject());
390 : 184 : JS::RootedValue exc_name(cx);
391 : 184 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
392 : : auto cleanup =
393 : 368 : mozilla::MakeScopeExit([&saved_exc]() { saved_exc.restore(); });
394 : : bool eq;
395 [ + - ]: 368 : return JS_GetPropertyById(cx, exc, atoms.name(), &exc_name) &&
396 : 184 : JS_StringEqualsLiteral(cx, exc_name.toString(), "ImportError",
397 [ + - + + ]: 368 : &eq) &&
398 : 184 : eq;
399 : 184 : }
400 : :
401 : : GJS_JSAPI_RETURN_CONVENTION
402 : 350 : static bool lookup_override_function(JSContext* cx, JS::HandleId ns_name,
403 : : JS::MutableHandleValue function) {
404 : 350 : JS::AutoSaveExceptionState saved_exc(cx);
405 : :
406 : 350 : JS::RootedObject global{cx, JS::CurrentGlobalOrNull(cx)};
407 : : JS::RootedValue importer(
408 : 350 : cx, gjs_get_global_slot(global, GjsGlobalSlot::IMPORTS));
409 : 350 : g_assert(importer.isObject());
410 : :
411 : 350 : JS::RootedObject overridespkg(cx), module(cx);
412 : 350 : JS::RootedObject importer_obj(cx, &importer.toObject());
413 : 350 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
414 [ - + ]: 350 : if (!gjs_object_require_property(cx, importer_obj, "importer",
415 : 350 : atoms.overrides(), &overridespkg))
416 : 0 : return false;
417 : :
418 [ + + ]: 350 : if (!gjs_object_require_property(cx, overridespkg,
419 : : "GI repository object", ns_name,
420 : 350 : &module)) {
421 : 185 : JS::RootedValue exc(cx);
422 : 185 : JS_GetPendingException(cx, &exc);
423 : :
424 : : /* If the exception was an ImportError (i.e., module not found) then we
425 : : * simply didn't have an override, don't throw an exception */
426 [ + + ]: 185 : if (is_import_error(cx, exc)) {
427 : 183 : saved_exc.restore();
428 : 183 : return true;
429 : : }
430 : :
431 : 2 : return false;
432 : 185 : }
433 : :
434 : : // If the override module is present, it must have a callable _init(). An
435 : : // override module without _init() is probably unintentional. (function
436 : : // being undefined means there was no override module.)
437 : 165 : if (!gjs_object_require_property(cx, module, "override module",
438 : 163 : atoms.init(), function) ||
439 [ + + + + : 165 : !function.isObject() || !JS::IsCallable(&function.toObject())) {
+ + + + ]
440 : 5 : gjs_throw(cx, "Unexpected value for _init in overrides module");
441 : 5 : return false;
442 : : }
443 : 160 : return true;
444 : 350 : }
445 : :
446 : : GJS_JSAPI_RETURN_CONVENTION
447 : 25113 : static JSObject* lookup_namespace(JSContext* cx, JSObject* global,
448 : : JS::HandleId ns_name) {
449 : 25113 : JS::RootedObject native_registry(cx, gjs_get_native_registry(global));
450 : 25113 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
451 : 25113 : JS::RootedObject gi(cx);
452 : :
453 [ - + ]: 25113 : if (!gjs_global_registry_get(cx, native_registry, atoms.gi(), &gi))
454 : 0 : return nullptr;
455 : :
456 [ - + ]: 25113 : if (!gi) {
457 : 0 : gjs_throw(cx, "No gi property in native registry");
458 : 0 : return nullptr;
459 : : }
460 : :
461 : 25113 : JS::RootedObject retval(cx);
462 [ - + ]: 25113 : if (!gjs_object_require_property(cx, gi, "GI repository object", ns_name,
463 : 25113 : &retval))
464 : 0 : return nullptr;
465 : :
466 : 25113 : return retval;
467 : 25113 : }
468 : :
469 : 25113 : JSObject* gjs_lookup_namespace_object_by_name(JSContext* cx,
470 : : JS::HandleId ns_name) {
471 : 25113 : JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
472 : :
473 : 25113 : g_assert(gjs_global_get_type(global) == GjsGlobalType::DEFAULT);
474 : 25113 : return lookup_namespace(cx, global, ns_name);
475 : 25113 : }
476 : :
477 : 5005 : char* gjs_hyphen_from_camel(const char* camel_name) {
478 : : // four hyphens should be reasonable guess
479 : 5005 : GString* s = g_string_sized_new(strlen(camel_name) + 4 + 1);
480 : :
481 [ + + ]: 60502 : for (const char* p = camel_name; *p; ++p) {
482 [ + + ]: 55497 : if (g_ascii_isupper(*p)) {
483 : : g_string_append_c(s, '-');
484 [ + - ]: 1164 : g_string_append_c(s, g_ascii_tolower(*p));
485 : : } else {
486 [ + - ]: 54333 : g_string_append_c(s, *p);
487 : : }
488 : : }
489 : :
490 : 5005 : return g_string_free(s, false);
491 : : }
492 : :
493 : 20417 : JSObject* gjs_lookup_generic_constructor(JSContext* cx,
494 : : const GI::BaseInfo& info) {
495 : 20417 : JS::RootedObject in_object{cx, gjs_lookup_namespace_object(cx, info)};
496 : 20417 : const char* constructor_name = info.name();
497 : :
498 [ - + ]: 20417 : if (G_UNLIKELY (!in_object))
499 : 0 : return nullptr;
500 : :
501 : 20417 : JS::RootedValue value{cx};
502 [ - + ]: 20417 : if (!JS_GetProperty(cx, in_object, constructor_name, &value))
503 : 0 : return nullptr;
504 : :
505 [ - + ]: 20417 : if (G_UNLIKELY(!value.isObject())) {
506 : 0 : gjs_throw(cx,
507 : : "Constructor of %s.%s was the wrong type, expected an object",
508 : : info.ns(), constructor_name);
509 : 0 : return nullptr;
510 : : }
511 : :
512 : 20417 : return &value.toObject();
513 : 20417 : }
514 : :
515 : 20417 : JSObject* gjs_lookup_generic_prototype(JSContext* cx,
516 : : const GI::BaseInfo& info) {
517 : 20417 : JS::RootedObject constructor{cx, gjs_lookup_generic_constructor(cx, info)};
518 [ - + ]: 20417 : if (G_UNLIKELY(!constructor))
519 : 0 : return nullptr;
520 : :
521 : 20417 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
522 : 20417 : JS::RootedValue value{cx};
523 [ - + ]: 20417 : if (!JS_GetPropertyById(cx, constructor, atoms.prototype(), &value))
524 : 0 : return nullptr;
525 : :
526 [ - + ]: 20417 : if (G_UNLIKELY(!value.isObject())) {
527 : 0 : gjs_throw(cx,
528 : : "Prototype of %s.%s was the wrong type, expected an object",
529 : : info.ns(), info.name());
530 : 0 : return nullptr;
531 : : }
532 : :
533 : 20417 : return &value.toObject();
534 : 20417 : }
535 : :
536 : 20401 : JSObject* gjs_new_object_with_generic_prototype(JSContext* cx,
537 : : const GI::BaseInfo& info) {
538 : 20401 : JS::RootedObject proto(cx, gjs_lookup_generic_prototype(cx, info));
539 [ - + ]: 20401 : if (!proto)
540 : 0 : return nullptr;
541 : :
542 : 20401 : return JS_NewObjectWithGivenProto(cx, JS::GetClass(proto), proto);
543 : 20401 : }
|