Branch data Line data Source code
1 : : /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 : : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
3 : : // SPDX-FileCopyrightText: 2008 litl, LLC
4 : : // SPDX-FileCopyrightText: 2009 Red Hat, Inc.
5 : : // SPDX-FileCopyrightText: 2017 Philip Chimento <philip.chimento@gmail.com>
6 : : // SPDX-FileCopyrightText: 2020 Evan Welsh <contact@evanwelsh.com>
7 : :
8 : : #include <config.h>
9 : :
10 : : #include <stddef.h> // for size_t
11 : :
12 : : #include <glib.h>
13 : :
14 : : #include <js/CallArgs.h> // for CallArgs, CallArgsFromVp
15 : : #include <js/CharacterEncoding.h> // for JS_EncodeStringToUTF8
16 : : #include <js/Class.h>
17 : : #include <js/CompilationAndEvaluation.h>
18 : : #include <js/CompileOptions.h>
19 : : #include <js/Debug.h> // for JS_DefineDebuggerObject
20 : : #include <js/GlobalObject.h> // for CurrentGlobalOrNull, JS_NewGlobalObject
21 : : #include <js/Id.h>
22 : : #include <js/MapAndSet.h>
23 : : #include <js/Object.h>
24 : : #include <js/PropertyAndElement.h>
25 : : #include <js/PropertyDescriptor.h> // for JSPROP_PERMANENT, JSPROP_RE...
26 : : #include <js/PropertySpec.h>
27 : : #include <js/Realm.h> // for GetObjectRealmOrNull, SetRealmPrivate
28 : : #include <js/RealmOptions.h>
29 : : #include <js/RootingAPI.h>
30 : : #include <js/SourceText.h>
31 : : #include <js/TypeDecls.h>
32 : : #include <js/Utility.h> // for UniqueChars
33 : : #include <jsapi.h> // for JS_IdToValue, JS_InitReflectParse
34 : :
35 : : #include "gjs/atoms.h"
36 : : #include "gjs/auto.h"
37 : : #include "gjs/context-private.h"
38 : : #include "gjs/engine.h"
39 : : #include "gjs/global.h"
40 : : #include "gjs/internal.h"
41 : : #include "gjs/jsapi-util.h"
42 : : #include "gjs/macros.h"
43 : : #include "gjs/native.h"
44 : :
45 : : namespace mozilla {
46 : : union Utf8Unit;
47 : : }
48 : :
49 : : class GjsBaseGlobal {
50 : : GJS_JSAPI_RETURN_CONVENTION
51 : 614 : static JSObject* base(JSContext* cx, const JSClass* clasp,
52 : : JS::RealmCreationOptions options,
53 : : JSPrincipals* principals = nullptr) {
54 : 614 : JS::RealmBehaviors behaviors;
55 : 614 : JS::RealmOptions compartment_options(options, behaviors);
56 : :
57 : 614 : JS::RootedObject global{cx, JS_NewGlobalObject(cx, clasp, principals,
58 : : JS::FireOnNewGlobalHook,
59 : 614 : compartment_options)};
60 [ - + ]: 614 : if (!global)
61 : 0 : return nullptr;
62 : :
63 : 614 : JSAutoRealm ac(cx, global);
64 : :
65 [ + - ]: 1228 : if (!JS_InitReflectParse(cx, global) ||
66 [ - + - + ]: 1228 : !JS_DefineDebuggerObject(cx, global))
67 : 0 : return nullptr;
68 : :
69 : 614 : return global;
70 : 614 : }
71 : :
72 : : protected:
73 : : GJS_JSAPI_RETURN_CONVENTION
74 : 358 : static JSObject* create(
75 : : JSContext* cx, const JSClass* clasp,
76 : : JS::RealmCreationOptions options = JS::RealmCreationOptions(),
77 : : JSPrincipals* principals = nullptr) {
78 : 358 : options.setNewCompartmentAndZone();
79 : 358 : return base(cx, clasp, options, principals);
80 : : }
81 : :
82 : : GJS_JSAPI_RETURN_CONVENTION
83 : 256 : static JSObject* create_with_compartment(
84 : : JSContext* cx, JS::HandleObject existing, const JSClass* clasp,
85 : : JS::RealmCreationOptions options = JS::RealmCreationOptions(),
86 : : JSPrincipals* principals = nullptr) {
87 : 256 : options.setExistingCompartment(existing);
88 : 256 : return base(cx, clasp, options, principals);
89 : : }
90 : :
91 : : GJS_JSAPI_RETURN_CONVENTION
92 : 358 : static bool run_bootstrap(JSContext* cx, const char* bootstrap_script,
93 : : JS::HandleObject global) {
94 : : Gjs::AutoChar uri{g_strdup_printf(
95 : : "resource:///org/gnome/gjs/modules/script/_bootstrap/%s.js",
96 : 358 : bootstrap_script)};
97 : :
98 : 358 : JSAutoRealm ar(cx, global);
99 : :
100 : 358 : JS::CompileOptions options(cx);
101 : 358 : options.setFileAndLine(uri, 1).setSourceIsLazy(true);
102 : :
103 : : char* script;
104 : : size_t script_len;
105 [ - + ]: 358 : if (!gjs_load_internal_source(cx, uri, &script, &script_len))
106 : 0 : return false;
107 : :
108 : 358 : JS::SourceText<mozilla::Utf8Unit> source;
109 [ - + ]: 358 : if (!source.init(cx, script, script_len,
110 : : JS::SourceOwnership::TakeOwnership))
111 : 0 : return false;
112 : :
113 : 358 : JS::RootedValue ignored(cx);
114 : 358 : return JS::Evaluate(cx, options, source, &ignored);
115 : 358 : }
116 : :
117 : : GJS_JSAPI_RETURN_CONVENTION
118 : 24 : static bool load_native_module(JSContext* m_cx, unsigned argc,
119 : : JS::Value* vp) {
120 : 24 : JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);
121 : :
122 : : // This function should never be directly exposed to user code, so we
123 : : // can be strict.
124 : 24 : g_assert(argc == 1);
125 : 24 : g_assert(argv[0].isString());
126 : :
127 : 24 : JS::RootedString str(m_cx, argv[0].toString());
128 : 24 : JS::UniqueChars id(JS_EncodeStringToUTF8(m_cx, str));
129 : :
130 [ - + ]: 24 : if (!id)
131 : 0 : return false;
132 : :
133 : 24 : JS::RootedObject native_obj(m_cx);
134 : :
135 [ - + ]: 24 : if (!Gjs::NativeModuleDefineFuncs::get().define(m_cx, id.get(),
136 : : &native_obj)) {
137 : 0 : gjs_throw(m_cx, "Failed to load native module: %s", id.get());
138 : 0 : return false;
139 : : }
140 : :
141 : 24 : argv.rval().setObject(*native_obj);
142 : 24 : return true;
143 : 24 : }
144 : : };
145 : :
146 : : const JSClassOps defaultclassops = JS::DefaultGlobalClassOps;
147 : :
148 : : class GjsGlobal : GjsBaseGlobal {
149 : : static constexpr JSClass klass = {
150 : : // Jasmine depends on the class name "GjsGlobal" to detect GJS' global
151 : : // object.
152 : : "GjsGlobal",
153 : : JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(
154 : : static_cast<uint32_t>(GjsGlobalSlot::LAST)),
155 : : &defaultclassops,
156 : : };
157 : :
158 : : // clang-format off
159 : : static constexpr JSPropertySpec static_props[] = {
160 : : JS_STRING_SYM_PS(toStringTag, "GjsGlobal", JSPROP_READONLY),
161 : : JS_PS_END};
162 : : // clang-format on
163 : :
164 : : static constexpr JSFunctionSpec static_funcs[] = {
165 : : JS_FS_END};
166 : :
167 : : public:
168 : : GJS_JSAPI_RETURN_CONVENTION
169 : 0 : static JSObject* create(JSContext* cx) {
170 : 0 : return GjsBaseGlobal::create(cx, &klass);
171 : : }
172 : :
173 : : GJS_JSAPI_RETURN_CONVENTION
174 : 256 : static JSObject* create_with_compartment(JSContext* cx,
175 : : JS::HandleObject cmp_global) {
176 : 256 : return GjsBaseGlobal::create_with_compartment(cx, cmp_global, &klass);
177 : : }
178 : :
179 : : GJS_JSAPI_RETURN_CONVENTION
180 : 256 : static bool define_properties(JSContext* cx, JS::HandleObject global,
181 : : const char* realm_name,
182 : : const char* bootstrap_script) {
183 : 256 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
184 : 256 : if (!JS_DefinePropertyById(cx, global, atoms.window(), global,
185 : 256 : JSPROP_READONLY | JSPROP_PERMANENT) ||
186 [ + - + - : 512 : !JS_DefineFunctions(cx, global, GjsGlobal::static_funcs) ||
- + ]
187 [ - + ]: 256 : !JS_DefineProperties(cx, global, GjsGlobal::static_props))
188 : 0 : return false;
189 : :
190 : 256 : JS::Realm* realm = JS::GetObjectRealmOrNull(global);
191 : 256 : g_assert(realm && "Global object must be associated with a realm");
192 : : // const_cast is allowed here if we never free the realm data
193 : 256 : JS::SetRealmPrivate(realm, const_cast<char*>(realm_name));
194 : :
195 : 256 : JS::RootedObject native_registry(cx, JS::NewMapObject(cx));
196 [ - + ]: 256 : if (!native_registry)
197 : 0 : return false;
198 : :
199 : 256 : gjs_set_global_slot(global, GjsGlobalSlot::NATIVE_REGISTRY,
200 : 256 : JS::ObjectValue(*native_registry));
201 : :
202 : 256 : JS::RootedObject module_registry(cx, JS::NewMapObject(cx));
203 [ - + ]: 256 : if (!module_registry)
204 : 0 : return false;
205 : :
206 : 256 : gjs_set_global_slot(global, GjsGlobalSlot::MODULE_REGISTRY,
207 : 256 : JS::ObjectValue(*module_registry));
208 : :
209 : 256 : JS::RootedObject source_map_registry{cx, JS::NewMapObject(cx)};
210 [ - + ]: 256 : if (!source_map_registry)
211 : 0 : return false;
212 : :
213 : 256 : gjs_set_global_slot(global, GjsGlobalSlot::SOURCE_MAP_REGISTRY,
214 : 256 : JS::ObjectValue(*source_map_registry));
215 : :
216 : : JS::Value v_importer =
217 : 256 : gjs_get_global_slot(global, GjsGlobalSlot::IMPORTS);
218 : 256 : g_assert(((void) "importer should be defined before passing null "
219 : : "importer to GjsGlobal::define_properties",
220 : : v_importer.isObject()));
221 : 256 : JS::RootedObject root_importer(cx, &v_importer.toObject());
222 : :
223 : : // Wrapping is a no-op if the importer is already in the same realm.
224 [ + - ]: 512 : if (!JS_WrapObject(cx, &root_importer) ||
225 [ - + - + ]: 512 : !JS_DefinePropertyById(cx, global, atoms.imports(), root_importer,
226 : : GJS_MODULE_PROP_FLAGS))
227 : 0 : return false;
228 : :
229 [ + - ]: 256 : if (bootstrap_script) {
230 [ - + ]: 256 : if (!run_bootstrap(cx, bootstrap_script, global))
231 : 0 : return false;
232 : : }
233 : :
234 : 256 : return true;
235 : 256 : }
236 : : };
237 : :
238 : : class GjsDebuggerGlobal : GjsBaseGlobal {
239 : : static constexpr JSClass klass = {
240 : : "GjsDebuggerGlobal",
241 : : JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(
242 : : static_cast<uint32_t>(GjsDebuggerGlobalSlot::LAST)),
243 : : &defaultclassops,
244 : : };
245 : :
246 : : static constexpr JSFunctionSpec static_funcs[] = {
247 : : JS_FN("loadNative", &load_native_module, 1, 0), JS_FS_END};
248 : :
249 : : public:
250 : : GJS_JSAPI_RETURN_CONVENTION
251 : 102 : static JSObject* create(JSContext* cx) {
252 : 102 : JS::RealmCreationOptions options;
253 : 102 : options.setToSourceEnabled(true); // debugger uses uneval()
254 : 102 : return GjsBaseGlobal::create(cx, &klass, options);
255 : 102 : }
256 : :
257 : : GJS_JSAPI_RETURN_CONVENTION
258 : 0 : static JSObject* create_with_compartment(JSContext* cx,
259 : : JS::HandleObject cmp_global) {
260 : 0 : return GjsBaseGlobal::create_with_compartment(cx, cmp_global, &klass);
261 : : }
262 : :
263 : : GJS_JSAPI_RETURN_CONVENTION
264 : 102 : static bool define_properties(JSContext* cx, JS::HandleObject global,
265 : : const char* realm_name,
266 : : const char* bootstrap_script) {
267 : 102 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
268 : 102 : if (!JS_DefinePropertyById(cx, global, atoms.window(), global,
269 [ + - - + ]: 204 : JSPROP_READONLY | JSPROP_PERMANENT) ||
270 [ - + ]: 102 : !JS_DefineFunctions(cx, global, GjsDebuggerGlobal::static_funcs))
271 : 0 : return false;
272 : :
273 : 102 : JS::Realm* realm = JS::GetObjectRealmOrNull(global);
274 : 102 : g_assert(realm && "Global object must be associated with a realm");
275 : : // const_cast is allowed here if we never free the realm data
276 : 102 : JS::SetRealmPrivate(realm, const_cast<char*>(realm_name));
277 : :
278 [ + - ]: 102 : if (bootstrap_script) {
279 [ - + ]: 102 : if (!run_bootstrap(cx, bootstrap_script, global))
280 : 0 : return false;
281 : : }
282 : :
283 : 102 : return true;
284 : : }
285 : : };
286 : :
287 : : class GjsInternalGlobal : GjsBaseGlobal {
288 : : static constexpr JSFunctionSpec static_funcs[] = {
289 : : JS_FN("compileModule", gjs_internal_compile_module, 2, 0),
290 : : JS_FN("compileInternalModule", gjs_internal_compile_internal_module, 2,
291 : : 0),
292 : : JS_FN("getRegistry", gjs_internal_get_registry, 1, 0),
293 : : JS_FN("getSourceMapRegistry", gjs_internal_get_source_map_registry, 1,
294 : : 0),
295 : : JS_FN("loadResourceOrFile", gjs_internal_load_resource_or_file, 1, 0),
296 : : JS_FN("loadResourceOrFileAsync",
297 : : gjs_internal_load_resource_or_file_async, 1, 0),
298 : : JS_FN("parseURI", gjs_internal_parse_uri, 1, 0),
299 : : JS_FN("resolveRelativeResourceOrFile",
300 : : gjs_internal_resolve_relative_resource_or_file, 2, 0),
301 : : JS_FN("setGlobalModuleLoader", gjs_internal_set_global_module_loader, 2,
302 : : 0),
303 : : JS_FN("setModulePrivate", gjs_internal_set_module_private, 2, 0),
304 : : JS_FN("uriExists", gjs_internal_uri_exists, 1, 0),
305 : : JS_FN("atob", gjs_internal_atob, 1, 0),
306 : : JS_FS_END};
307 : :
308 : : static constexpr JSClass klass = {
309 : : "GjsInternalGlobal",
310 : : JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(
311 : : static_cast<uint32_t>(GjsInternalGlobalSlot::LAST)),
312 : : &defaultclassops,
313 : : };
314 : :
315 : : public:
316 : : GJS_JSAPI_RETURN_CONVENTION
317 : 256 : static JSObject* create(JSContext* cx) {
318 : 256 : return GjsBaseGlobal::create(cx, &klass, {}, get_internal_principals());
319 : : }
320 : :
321 : : GJS_JSAPI_RETURN_CONVENTION
322 : 0 : static JSObject* create_with_compartment(JSContext* cx,
323 : : JS::HandleObject cmp_global) {
324 : 0 : return GjsBaseGlobal::create_with_compartment(
325 : 0 : cx, cmp_global, &klass, {}, get_internal_principals());
326 : : }
327 : :
328 : : GJS_JSAPI_RETURN_CONVENTION
329 : 256 : static bool define_properties(JSContext* cx, JS::HandleObject global,
330 : : const char* realm_name,
331 : : const char* bootstrap_script
332 : : [[maybe_unused]]) {
333 : 256 : JS::Realm* realm = JS::GetObjectRealmOrNull(global);
334 : 256 : g_assert(realm && "Global object must be associated with a realm");
335 : : // const_cast is allowed here if we never free the realm data
336 : 256 : JS::SetRealmPrivate(realm, const_cast<char*>(realm_name));
337 : :
338 : 256 : JSAutoRealm ar(cx, global);
339 : 256 : JS::RootedObject native_registry(cx, JS::NewMapObject(cx));
340 [ - + ]: 256 : if (!native_registry)
341 : 0 : return false;
342 : :
343 : 256 : gjs_set_global_slot(global, GjsGlobalSlot::NATIVE_REGISTRY,
344 : 256 : JS::ObjectValue(*native_registry));
345 : :
346 : 256 : JS::RootedObject module_registry(cx, JS::NewMapObject(cx));
347 [ - + ]: 256 : if (!module_registry)
348 : 0 : return false;
349 : :
350 : 256 : gjs_set_global_slot(global, GjsGlobalSlot::MODULE_REGISTRY,
351 : 256 : JS::ObjectValue(*module_registry));
352 : :
353 : 256 : JS::RootedObject source_map_registry{cx, JS::NewMapObject(cx)};
354 [ - + ]: 256 : if (!source_map_registry)
355 : 0 : return false;
356 : :
357 : 256 : gjs_set_global_slot(global, GjsGlobalSlot::SOURCE_MAP_REGISTRY,
358 : 256 : JS::ObjectValue(*source_map_registry));
359 : :
360 : 256 : return JS_DefineFunctions(cx, global, static_funcs);
361 : 256 : }
362 : : };
363 : :
364 : : /**
365 : : * gjs_create_global_object:
366 : : * @cx: a #JSContext
367 : : *
368 : : * Creates a global object, and initializes it with the default API.
369 : : *
370 : : * Returns: the created global object on success, nullptr otherwise, in which
371 : : * case an exception is pending on @cx
372 : : */
373 : 614 : JSObject* gjs_create_global_object(JSContext* cx, GjsGlobalType global_type,
374 : : JS::HandleObject current_global) {
375 [ + + ]: 614 : if (current_global) {
376 [ + - - - ]: 256 : switch (global_type) {
377 : 256 : case GjsGlobalType::DEFAULT:
378 : 256 : return GjsGlobal::create_with_compartment(cx, current_global);
379 : 0 : case GjsGlobalType::DEBUGGER:
380 : 0 : return GjsDebuggerGlobal::create_with_compartment(
381 : 0 : cx, current_global);
382 : 0 : case GjsGlobalType::INTERNAL:
383 : 0 : return GjsInternalGlobal::create_with_compartment(
384 : 0 : cx, current_global);
385 : 0 : default:
386 : 0 : return nullptr;
387 : : }
388 : : }
389 : :
390 [ - + + - ]: 358 : switch (global_type) {
391 : 0 : case GjsGlobalType::DEFAULT:
392 : 0 : return GjsGlobal::create(cx);
393 : 102 : case GjsGlobalType::DEBUGGER:
394 : 102 : return GjsDebuggerGlobal::create(cx);
395 : 256 : case GjsGlobalType::INTERNAL:
396 : 256 : return GjsInternalGlobal::create(cx);
397 : 0 : default:
398 : 0 : return nullptr;
399 : : }
400 : : }
401 : :
402 : : /**
403 : : * gjs_global_is_type:
404 : : *
405 : : * @param cx the current #JSContext
406 : : * @param type the global type to test for
407 : : *
408 : : * @returns whether the current global is the same type as #type
409 : : */
410 : 19946 : bool gjs_global_is_type(JSContext* cx, GjsGlobalType type) {
411 : 19946 : JSObject* global = JS::CurrentGlobalOrNull(cx);
412 : :
413 : 19946 : g_assert(global && "gjs_global_is_type called before a realm was entered.");
414 : :
415 : : JS::Value global_type =
416 : 19946 : gjs_get_global_slot(global, GjsBaseGlobalSlot::GLOBAL_TYPE);
417 : :
418 : 19946 : g_assert(global_type.isInt32());
419 : :
420 : 19946 : return static_cast<GjsGlobalType>(global_type.toInt32()) == type;
421 : : }
422 : :
423 : 0 : GjsGlobalType gjs_global_get_type(JSContext* cx) {
424 : 0 : auto global = JS::CurrentGlobalOrNull(cx);
425 : :
426 : 0 : g_assert(global &&
427 : : "gjs_global_get_type called before a realm was entered.");
428 : :
429 : : JS::Value global_type =
430 : 0 : gjs_get_global_slot(global, GjsBaseGlobalSlot::GLOBAL_TYPE);
431 : :
432 : 0 : g_assert(global_type.isInt32());
433 : :
434 : 0 : return static_cast<GjsGlobalType>(global_type.toInt32());
435 : : }
436 : :
437 : 22285 : GjsGlobalType gjs_global_get_type(JSObject* global) {
438 : : JS::Value global_type =
439 : 22285 : gjs_get_global_slot(global, GjsBaseGlobalSlot::GLOBAL_TYPE);
440 : :
441 : 22285 : g_assert(global_type.isInt32());
442 : :
443 : 22285 : return static_cast<GjsGlobalType>(global_type.toInt32());
444 : : }
445 : :
446 : : /**
447 : : * gjs_global_registry_set:
448 : : *
449 : : * @brief This function inserts a module object into a global registry.
450 : : * Global registries are JS Map objects for easy reuse and access
451 : : * within internal JS. This function will assert if a module has
452 : : * already been inserted at the given key.
453 : :
454 : : * @param cx the current #JSContext
455 : : * @param registry a JS Map object
456 : : * @param key a module identifier, typically a string or symbol
457 : : * @param module a module object
458 : : */
459 : 818 : bool gjs_global_registry_set(JSContext* cx, JS::HandleObject registry,
460 : : JS::PropertyKey key, JS::HandleObject module) {
461 : 818 : JS::RootedValue v_key(cx);
462 [ - + ]: 818 : if (!JS_IdToValue(cx, key, &v_key))
463 : 0 : return false;
464 : :
465 : : bool has_key;
466 [ - + ]: 818 : if (!JS::MapHas(cx, registry, v_key, &has_key))
467 : 0 : return false;
468 : :
469 : 818 : g_assert(!has_key && "Module key already exists in the registry");
470 : :
471 : 818 : JS::RootedValue v_value(cx, JS::ObjectValue(*module));
472 : :
473 : 818 : return JS::MapSet(cx, registry, v_key, v_value);
474 : 818 : }
475 : :
476 : : /**
477 : : * gjs_global_registry_get:
478 : : *
479 : : * @brief This function retrieves a module record from the global registry,
480 : : * or %NULL if the module record is not present.
481 : : * Global registries are JS Map objects for easy reuse and access
482 : : * within internal JS.
483 : :
484 : : * @param cx the current #JSContext
485 : : * @param registry a JS Map object
486 : : * @param key a module identifier, typically a string or symbol
487 : : * @param module a module object
488 : : */
489 : 32606 : bool gjs_global_registry_get(JSContext* cx, JS::HandleObject registry,
490 : : JS::PropertyKey key,
491 : : JS::MutableHandleObject module_out) {
492 : 32606 : JS::RootedValue v_key(cx), v_value(cx);
493 [ + - ]: 65212 : if (!JS_IdToValue(cx, key, &v_key) ||
494 [ - + - + ]: 65212 : !JS::MapGet(cx, registry, v_key, &v_value))
495 : 0 : return false;
496 : :
497 : 32606 : g_assert((v_value.isUndefined() || v_value.isObject()) &&
498 : : "Invalid value in module registry");
499 : :
500 [ + + ]: 32606 : if (v_value.isObject()) {
501 : 32042 : module_out.set(&v_value.toObject());
502 : 32042 : return true;
503 : : }
504 : :
505 : 564 : module_out.set(nullptr);
506 : 564 : return true;
507 : 32606 : }
508 : :
509 : : /**
510 : : * gjs_global_source_map_get:
511 : : *
512 : : * @brief This function retrieves a source map consumer from the source map
513 : : * registry, or %NULL if the source does not have a source map consumer.
514 : : */
515 : 907 : bool gjs_global_source_map_get(
516 : : JSContext* cx, JS::HandleObject registry, JS::Handle<JS::Value> key,
517 : : JS::MutableHandleObject source_map_consumer_obj) {
518 : 907 : JS::RootedValue v_value{cx};
519 [ - + ]: 907 : if (!JS::MapGet(cx, registry, key, &v_value))
520 : 0 : return false;
521 : 907 : JS::RootedString str{cx, key.toString()};
522 : :
523 : 907 : g_assert((v_value.isUndefined() || v_value.isObject()) &&
524 : : "Invalid value in source map registry");
525 : :
526 [ + + ]: 907 : if (v_value.isObject()) {
527 : 6 : source_map_consumer_obj.set(&v_value.toObject());
528 : 6 : return true;
529 : : }
530 : :
531 : 901 : source_map_consumer_obj.set(nullptr);
532 : 901 : return true;
533 : 907 : }
534 : :
535 : : /**
536 : : * gjs_define_global_properties:
537 : : * @cx: a #JSContext
538 : : * @global: a JS global object that has not yet been passed to this function
539 : : * @realm_name: (nullable): name of the realm, for debug output
540 : : * @bootstrap_script: (nullable): name of a bootstrap script (found at
541 : : * resource://org/gnome/gjs/modules/script/_bootstrap/@bootstrap_script) or
542 : : * %NULL for none
543 : : *
544 : : * Defines properties on the global object such as 'window' and 'imports', and
545 : : * runs a bootstrap JS script on the global object to define any properties
546 : : * that can be defined from JS.
547 : : * This function completes the initialization of a new global object, but it
548 : : * is separate from gjs_create_global_object() because all globals share the
549 : : * same root importer.
550 : : * The code creating the main global for the JS context needs to create the
551 : : * root importer in between calling gjs_create_global_object() and
552 : : * gjs_define_global_properties().
553 : : *
554 : : * The caller of this function should be in the realm for @global.
555 : : * If the root importer object belongs to a different realm, this function will
556 : : * create a wrapper for it.
557 : : *
558 : : * Returns: true on success, false otherwise, in which case an exception is
559 : : * pending on @cx
560 : : */
561 : 614 : bool gjs_define_global_properties(JSContext* cx, JS::HandleObject global,
562 : : GjsGlobalType global_type,
563 : : const char* realm_name,
564 : : const char* bootstrap_script) {
565 : 614 : gjs_set_global_slot(global.get(), GjsBaseGlobalSlot::GLOBAL_TYPE,
566 : : JS::Int32Value(static_cast<uint32_t>(global_type)));
567 : :
568 [ + + + - ]: 614 : switch (global_type) {
569 : 256 : case GjsGlobalType::DEFAULT:
570 : 256 : return GjsGlobal::define_properties(cx, global, realm_name,
571 : 256 : bootstrap_script);
572 : 102 : case GjsGlobalType::DEBUGGER:
573 : 102 : return GjsDebuggerGlobal::define_properties(cx, global, realm_name,
574 : 102 : bootstrap_script);
575 : 256 : case GjsGlobalType::INTERNAL:
576 : 256 : return GjsInternalGlobal::define_properties(cx, global, realm_name,
577 : 256 : bootstrap_script);
578 : : }
579 : :
580 : : // Global type does not handle define_properties
581 : : g_assert_not_reached();
582 : : }
583 : :
584 : 3666 : void detail::set_global_slot(JSObject* global, uint32_t slot, JS::Value value) {
585 : 3666 : JS::SetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot, value);
586 : 3666 : }
587 : :
588 : 117475 : JS::Value detail::get_global_slot(JSObject* global, uint32_t slot) {
589 : 117475 : return JS::GetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot);
590 : : }
591 : :
592 : : decltype(GjsGlobal::klass) constexpr GjsGlobal::klass;
593 : : decltype(GjsGlobal::static_funcs) constexpr GjsGlobal::static_funcs;
594 : : decltype(GjsGlobal::static_props) constexpr GjsGlobal::static_props;
595 : :
596 : : decltype(GjsDebuggerGlobal::klass) constexpr GjsDebuggerGlobal::klass;
597 : : decltype(
598 : : GjsDebuggerGlobal::static_funcs) constexpr GjsDebuggerGlobal::static_funcs;
599 : :
600 : : decltype(GjsInternalGlobal::klass) constexpr GjsInternalGlobal::klass;
601 : : decltype(
602 : : GjsInternalGlobal::static_funcs) constexpr GjsInternalGlobal::static_funcs;
|