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 : 617 : static JSObject* base(JSContext* cx, const JSClass* clasp,
52 : : JS::RealmCreationOptions options,
53 : : JSPrincipals* principals = nullptr) {
54 : 617 : JS::RealmBehaviors behaviors;
55 : 617 : JS::RealmOptions compartment_options(options, behaviors);
56 : :
57 : 617 : JS::RootedObject global{cx, JS_NewGlobalObject(cx, clasp, principals,
58 : : JS::FireOnNewGlobalHook,
59 : 617 : compartment_options)};
60 [ - + ]: 617 : if (!global)
61 : 0 : return nullptr;
62 : :
63 : 617 : JSAutoRealm ac(cx, global);
64 : :
65 [ + - ]: 1234 : if (!JS_InitReflectParse(cx, global) ||
66 [ - + - + ]: 1234 : !JS_DefineDebuggerObject(cx, global))
67 : 0 : return nullptr;
68 : :
69 : 617 : return global;
70 : 617 : }
71 : :
72 : : protected:
73 : : GJS_JSAPI_RETURN_CONVENTION
74 : 360 : static JSObject* create(
75 : : JSContext* cx, const JSClass* clasp,
76 : : JS::RealmCreationOptions options = JS::RealmCreationOptions(),
77 : : JSPrincipals* principals = nullptr) {
78 : 360 : options.setNewCompartmentAndZone();
79 : 360 : return base(cx, clasp, options, principals);
80 : : }
81 : :
82 : : GJS_JSAPI_RETURN_CONVENTION
83 : 257 : 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 : 257 : options.setExistingCompartment(existing);
88 : 257 : return base(cx, clasp, options, principals);
89 : : }
90 : :
91 : : GJS_JSAPI_RETURN_CONVENTION
92 : 360 : 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 : 360 : bootstrap_script)};
97 : :
98 : 360 : JSAutoRealm ar(cx, global);
99 : :
100 : 360 : JS::CompileOptions options(cx);
101 : 360 : options.setFileAndLine(uri, 1).setSourceIsLazy(true);
102 : :
103 : : char* script;
104 : : size_t script_len;
105 [ - + ]: 360 : if (!gjs_load_internal_source(cx, uri, &script, &script_len))
106 : 0 : return false;
107 : :
108 : 360 : JS::SourceText<mozilla::Utf8Unit> source;
109 [ - + ]: 360 : if (!source.init(cx, script, script_len,
110 : : JS::SourceOwnership::TakeOwnership))
111 : 0 : return false;
112 : :
113 : 360 : JS::RootedValue ignored(cx);
114 : 360 : return JS::Evaluate(cx, options, source, &ignored);
115 : 360 : }
116 : :
117 : : GJS_JSAPI_RETURN_CONVENTION
118 : 25 : static bool load_native_module(JSContext* m_cx, unsigned argc,
119 : : JS::Value* vp) {
120 : 25 : 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 : 25 : g_assert(argc == 1);
125 : 25 : g_assert(argv[0].isString());
126 : :
127 : 25 : JS::RootedString str(m_cx, argv[0].toString());
128 : 25 : JS::UniqueChars id(JS_EncodeStringToUTF8(m_cx, str));
129 : :
130 [ - + ]: 25 : if (!id)
131 : 0 : return false;
132 : :
133 : 25 : JS::RootedObject native_obj(m_cx);
134 : :
135 [ - + ]: 25 : 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 : 25 : argv.rval().setObject(*native_obj);
142 : 25 : return true;
143 : 25 : }
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 : 257 : static JSObject* create_with_compartment(JSContext* cx,
175 : : JS::HandleObject cmp_global) {
176 : 257 : return GjsBaseGlobal::create_with_compartment(cx, cmp_global, &klass);
177 : : }
178 : :
179 : : GJS_JSAPI_RETURN_CONVENTION
180 : 257 : static bool define_properties(JSContext* cx, JS::HandleObject global,
181 : : const char* realm_name,
182 : : const char* bootstrap_script) {
183 : 257 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
184 : 257 : if (!JS_DefinePropertyById(cx, global, atoms.window(), global,
185 : 257 : JSPROP_READONLY | JSPROP_PERMANENT) ||
186 [ + - + - : 514 : !JS_DefineFunctions(cx, global, GjsGlobal::static_funcs) ||
- + ]
187 [ - + ]: 257 : !JS_DefineProperties(cx, global, GjsGlobal::static_props))
188 : 0 : return false;
189 : :
190 : 257 : JS::Realm* realm = JS::GetObjectRealmOrNull(global);
191 : 257 : 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 : 257 : JS::SetRealmPrivate(realm, const_cast<char*>(realm_name));
194 : :
195 : 257 : JS::RootedObject native_registry(cx, JS::NewMapObject(cx));
196 [ - + ]: 257 : if (!native_registry)
197 : 0 : return false;
198 : :
199 : 257 : gjs_set_global_slot(global, GjsGlobalSlot::NATIVE_REGISTRY,
200 : 257 : JS::ObjectValue(*native_registry));
201 : :
202 : 257 : JS::RootedObject module_registry(cx, JS::NewMapObject(cx));
203 [ - + ]: 257 : if (!module_registry)
204 : 0 : return false;
205 : :
206 : 257 : gjs_set_global_slot(global, GjsGlobalSlot::MODULE_REGISTRY,
207 : 257 : JS::ObjectValue(*module_registry));
208 : :
209 : 257 : JS::RootedObject source_map_registry{cx, JS::NewMapObject(cx)};
210 [ - + ]: 257 : if (!source_map_registry)
211 : 0 : return false;
212 : :
213 : 257 : gjs_set_global_slot(global, GjsGlobalSlot::SOURCE_MAP_REGISTRY,
214 : 257 : JS::ObjectValue(*source_map_registry));
215 : :
216 : : JS::Value v_importer =
217 : 257 : gjs_get_global_slot(global, GjsGlobalSlot::IMPORTS);
218 : 257 : g_assert(((void) "importer should be defined before passing null "
219 : : "importer to GjsGlobal::define_properties",
220 : : v_importer.isObject()));
221 : 257 : 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 [ + - ]: 514 : if (!JS_WrapObject(cx, &root_importer) ||
225 [ - + - + ]: 514 : !JS_DefinePropertyById(cx, global, atoms.imports(), root_importer,
226 : : GJS_MODULE_PROP_FLAGS))
227 : 0 : return false;
228 : :
229 [ + - ]: 257 : if (bootstrap_script) {
230 [ - + ]: 257 : if (!run_bootstrap(cx, bootstrap_script, global))
231 : 0 : return false;
232 : : }
233 : :
234 : 257 : return true;
235 : 257 : }
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 : 103 : static JSObject* create(JSContext* cx) {
252 : 103 : JS::RealmCreationOptions options;
253 : 103 : options.setToSourceEnabled(true); // debugger uses uneval()
254 : 103 : return GjsBaseGlobal::create(cx, &klass, options);
255 : 103 : }
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 : 103 : static bool define_properties(JSContext* cx, JS::HandleObject global,
265 : : const char* realm_name,
266 : : const char* bootstrap_script) {
267 : 103 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
268 : 103 : if (!JS_DefinePropertyById(cx, global, atoms.window(), global,
269 [ + - - + ]: 206 : JSPROP_READONLY | JSPROP_PERMANENT) ||
270 [ - + ]: 103 : !JS_DefineFunctions(cx, global, GjsDebuggerGlobal::static_funcs))
271 : 0 : return false;
272 : :
273 : 103 : JS::Realm* realm = JS::GetObjectRealmOrNull(global);
274 : 103 : 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 : 103 : JS::SetRealmPrivate(realm, const_cast<char*>(realm_name));
277 : :
278 [ + - ]: 103 : if (bootstrap_script) {
279 [ - + ]: 103 : if (!run_bootstrap(cx, bootstrap_script, global))
280 : 0 : return false;
281 : : }
282 : :
283 : 103 : 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 : 257 : static JSObject* create(JSContext* cx) {
318 : 257 : 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 : 257 : static bool define_properties(JSContext* cx, JS::HandleObject global,
330 : : const char* realm_name,
331 : : const char* bootstrap_script
332 : : [[maybe_unused]]) {
333 : 257 : JS::Realm* realm = JS::GetObjectRealmOrNull(global);
334 : 257 : 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 : 257 : JS::SetRealmPrivate(realm, const_cast<char*>(realm_name));
337 : :
338 : 257 : JSAutoRealm ar(cx, global);
339 : 257 : JS::RootedObject native_registry(cx, JS::NewMapObject(cx));
340 [ - + ]: 257 : if (!native_registry)
341 : 0 : return false;
342 : :
343 : 257 : gjs_set_global_slot(global, GjsGlobalSlot::NATIVE_REGISTRY,
344 : 257 : JS::ObjectValue(*native_registry));
345 : :
346 : 257 : JS::RootedObject module_registry(cx, JS::NewMapObject(cx));
347 [ - + ]: 257 : if (!module_registry)
348 : 0 : return false;
349 : :
350 : 257 : gjs_set_global_slot(global, GjsGlobalSlot::MODULE_REGISTRY,
351 : 257 : JS::ObjectValue(*module_registry));
352 : :
353 : 257 : JS::RootedObject source_map_registry{cx, JS::NewMapObject(cx)};
354 [ - + ]: 257 : if (!source_map_registry)
355 : 0 : return false;
356 : :
357 : 257 : gjs_set_global_slot(global, GjsGlobalSlot::SOURCE_MAP_REGISTRY,
358 : 257 : JS::ObjectValue(*source_map_registry));
359 : :
360 : 257 : return JS_DefineFunctions(cx, global, static_funcs);
361 : 257 : }
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 : 617 : JSObject* gjs_create_global_object(JSContext* cx, GjsGlobalType global_type,
374 : : JS::HandleObject current_global) {
375 [ + + ]: 617 : if (current_global) {
376 [ + - - - ]: 257 : switch (global_type) {
377 : 257 : case GjsGlobalType::DEFAULT:
378 : 257 : 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 [ - + + - ]: 360 : switch (global_type) {
391 : 0 : case GjsGlobalType::DEFAULT:
392 : 0 : return GjsGlobal::create(cx);
393 : 103 : case GjsGlobalType::DEBUGGER:
394 : 103 : return GjsDebuggerGlobal::create(cx);
395 : 257 : case GjsGlobalType::INTERNAL:
396 : 257 : return GjsInternalGlobal::create(cx);
397 : 0 : default:
398 : 0 : return nullptr;
399 : : }
400 : : }
401 : :
402 : : /**
403 : : * gjs_global_is_type:
404 : : * @cx: the current #JSContext
405 : : * @type: the global type to test for
406 : : *
407 : : * Returns: whether the current global is the same type as @type
408 : : */
409 : 20032 : bool gjs_global_is_type(JSContext* cx, GjsGlobalType type) {
410 : 20032 : JSObject* global = JS::CurrentGlobalOrNull(cx);
411 : :
412 : 20032 : g_assert(global && "gjs_global_is_type called before a realm was entered.");
413 : :
414 : : JS::Value global_type =
415 : 20032 : gjs_get_global_slot(global, GjsBaseGlobalSlot::GLOBAL_TYPE);
416 : :
417 : 20032 : g_assert(global_type.isInt32());
418 : :
419 : 20032 : return static_cast<GjsGlobalType>(global_type.toInt32()) == type;
420 : : }
421 : :
422 : 0 : GjsGlobalType gjs_global_get_type(JSContext* cx) {
423 : 0 : auto global = JS::CurrentGlobalOrNull(cx);
424 : :
425 : 0 : g_assert(global &&
426 : : "gjs_global_get_type called before a realm was entered.");
427 : :
428 : : JS::Value global_type =
429 : 0 : gjs_get_global_slot(global, GjsBaseGlobalSlot::GLOBAL_TYPE);
430 : :
431 : 0 : g_assert(global_type.isInt32());
432 : :
433 : 0 : return static_cast<GjsGlobalType>(global_type.toInt32());
434 : : }
435 : :
436 : 23174 : GjsGlobalType gjs_global_get_type(JSObject* global) {
437 : : JS::Value global_type =
438 : 23174 : gjs_get_global_slot(global, GjsBaseGlobalSlot::GLOBAL_TYPE);
439 : :
440 : 23174 : g_assert(global_type.isInt32());
441 : :
442 : 23174 : return static_cast<GjsGlobalType>(global_type.toInt32());
443 : : }
444 : :
445 : : /**
446 : : * gjs_global_registry_set:
447 : : * @cx: the current #JSContext
448 : : * @registry: a JS Map object
449 : : * @key: a module identifier, typically a string or symbol
450 : : * @module: a module object
451 : : *
452 : : * This function inserts a module object into a global registry. Global
453 : : * registries are JS Map objects for easy reuse and access within internal JS.
454 : : * This function will assert if a module has already been inserted at the given
455 : : * key.
456 : : *
457 : : * Returns: false if an exception is pending, otherwise true.
458 : : */
459 : 820 : bool gjs_global_registry_set(JSContext* cx, JS::HandleObject registry,
460 : : JS::PropertyKey key, JS::HandleObject module) {
461 : 820 : JS::RootedValue v_key(cx);
462 [ - + ]: 820 : if (!JS_IdToValue(cx, key, &v_key))
463 : 0 : return false;
464 : :
465 : : bool has_key;
466 [ - + ]: 820 : if (!JS::MapHas(cx, registry, v_key, &has_key))
467 : 0 : return false;
468 : :
469 : 820 : g_assert(!has_key && "Module key already exists in the registry");
470 : :
471 : 820 : JS::RootedValue v_value(cx, JS::ObjectValue(*module));
472 : :
473 : 820 : return JS::MapSet(cx, registry, v_key, v_value);
474 : 820 : }
475 : :
476 : : /**
477 : : * gjs_global_registry_get:
478 : : * @cx: the current #JSContext
479 : : * @registry: a JS Map object
480 : : * @key: a module identifier, typically a string or symbol
481 : : * @module_out: (out): handle where a module object will be stored
482 : : *
483 : : * This function retrieves a module record from the global registry, or null if
484 : : * the module record is not present. Global registries are JS Map objects for
485 : : * easy reuse and access within internal JS.
486 : : *
487 : : * Returns: false if an exception is pending, otherwise true.
488 : : */
489 : 33524 : bool gjs_global_registry_get(JSContext* cx, JS::HandleObject registry,
490 : : JS::PropertyKey key,
491 : : JS::MutableHandleObject module_out) {
492 : 33524 : JS::RootedValue v_key(cx), v_value(cx);
493 [ + - ]: 67048 : if (!JS_IdToValue(cx, key, &v_key) ||
494 [ - + - + ]: 67048 : !JS::MapGet(cx, registry, v_key, &v_value))
495 : 0 : return false;
496 : :
497 : 33524 : g_assert((v_value.isUndefined() || v_value.isObject()) &&
498 : : "Invalid value in module registry");
499 : :
500 [ + + ]: 33524 : if (v_value.isObject()) {
501 : 32959 : module_out.set(&v_value.toObject());
502 : 32959 : return true;
503 : : }
504 : :
505 : 565 : module_out.set(nullptr);
506 : 565 : return true;
507 : 33524 : }
508 : :
509 : : /**
510 : : * gjs_global_source_map_get:
511 : : * @cx: the current #JSContext
512 : : * @registry: a JS Map object
513 : : * @key: a source string, such as retrieved from a stack frame
514 : : * @source_map_consumer_obj: handle where a source map consumer object will be
515 : : * stored
516 : : *
517 : : * This function retrieves a source map consumer from the source map registry,
518 : : * or null if the source does not have a source map consumer.
519 : : *
520 : : * Returns: false if an exception is pending, otherwise true.
521 : : */
522 : 907 : bool gjs_global_source_map_get(
523 : : JSContext* cx, JS::HandleObject registry, JS::HandleString key,
524 : : JS::MutableHandleObject source_map_consumer_obj) {
525 : 907 : JS::RootedValue v_key{cx, JS::StringValue(key)};
526 : 907 : JS::RootedValue v_value{cx};
527 [ - + ]: 907 : if (!JS::MapGet(cx, registry, v_key, &v_value))
528 : 0 : return false;
529 : :
530 : 907 : g_assert((v_value.isUndefined() || v_value.isObject()) &&
531 : : "Invalid value in source map registry");
532 : :
533 [ + + ]: 907 : if (v_value.isObject()) {
534 : 6 : source_map_consumer_obj.set(&v_value.toObject());
535 : 6 : return true;
536 : : }
537 : :
538 : 901 : source_map_consumer_obj.set(nullptr);
539 : 901 : return true;
540 : 907 : }
541 : :
542 : : /**
543 : : * gjs_define_global_properties:
544 : : * @cx: a #JSContext
545 : : * @global: a JS global object that has not yet been passed to this function
546 : : * @realm_name: (nullable): name of the realm, for debug output
547 : : * @bootstrap_script: (nullable): name of a bootstrap script (found at
548 : : * resource://org/gnome/gjs/modules/script/_bootstrap/@bootstrap_script) or
549 : : * %NULL for none
550 : : *
551 : : * Defines properties on the global object such as 'window' and 'imports', and
552 : : * runs a bootstrap JS script on the global object to define any properties
553 : : * that can be defined from JS.
554 : : * This function completes the initialization of a new global object, but it
555 : : * is separate from gjs_create_global_object() because all globals share the
556 : : * same root importer.
557 : : * The code creating the main global for the JS context needs to create the
558 : : * root importer in between calling gjs_create_global_object() and
559 : : * gjs_define_global_properties().
560 : : *
561 : : * The caller of this function should be in the realm for @global.
562 : : * If the root importer object belongs to a different realm, this function will
563 : : * create a wrapper for it.
564 : : *
565 : : * Returns: true on success, false otherwise, in which case an exception is
566 : : * pending on @cx
567 : : */
568 : 617 : bool gjs_define_global_properties(JSContext* cx, JS::HandleObject global,
569 : : GjsGlobalType global_type,
570 : : const char* realm_name,
571 : : const char* bootstrap_script) {
572 : 617 : gjs_set_global_slot(global.get(), GjsBaseGlobalSlot::GLOBAL_TYPE,
573 : : JS::Int32Value(static_cast<uint32_t>(global_type)));
574 : :
575 [ + + + - ]: 617 : switch (global_type) {
576 : 257 : case GjsGlobalType::DEFAULT:
577 : 257 : return GjsGlobal::define_properties(cx, global, realm_name,
578 : 257 : bootstrap_script);
579 : 103 : case GjsGlobalType::DEBUGGER:
580 : 103 : return GjsDebuggerGlobal::define_properties(cx, global, realm_name,
581 : 103 : bootstrap_script);
582 : 257 : case GjsGlobalType::INTERNAL:
583 : 257 : return GjsInternalGlobal::define_properties(cx, global, realm_name,
584 : 257 : bootstrap_script);
585 : : }
586 : :
587 : : // Global type does not handle define_properties
588 : : g_assert_not_reached();
589 : : }
590 : :
591 : 3680 : void detail::set_global_slot(JSObject* global, uint32_t slot, JS::Value value) {
592 : 3680 : JS::SetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot, value);
593 : 3680 : }
594 : :
595 : 118740 : JS::Value detail::get_global_slot(JSObject* global, uint32_t slot) {
596 : 118740 : return JS::GetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot);
597 : : }
598 : :
599 : : decltype(GjsGlobal::klass) constexpr GjsGlobal::klass;
600 : : decltype(GjsGlobal::static_funcs) constexpr GjsGlobal::static_funcs;
601 : : decltype(GjsGlobal::static_props) constexpr GjsGlobal::static_props;
602 : :
603 : : decltype(GjsDebuggerGlobal::klass) constexpr GjsDebuggerGlobal::klass;
604 : : decltype(
605 : : GjsDebuggerGlobal::static_funcs) constexpr GjsDebuggerGlobal::static_funcs;
606 : :
607 : : decltype(GjsInternalGlobal::klass) constexpr GjsInternalGlobal::klass;
608 : : decltype(
609 : : GjsInternalGlobal::static_funcs) constexpr GjsInternalGlobal::static_funcs;
|