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 : 623 : static JSObject* base(JSContext* cx, const JSClass* clasp,
52 : : const JS::RealmCreationOptions& options,
53 : : JSPrincipals* principals = nullptr) {
54 : 623 : JS::RealmBehaviors behaviors;
55 : 623 : JS::RealmOptions compartment_options(options, behaviors);
56 : :
57 : 623 : JS::RootedObject global{cx, JS_NewGlobalObject(cx, clasp, principals,
58 : : JS::FireOnNewGlobalHook,
59 : 623 : compartment_options)};
60 [ - + ]: 623 : if (!global)
61 : 0 : return nullptr;
62 : :
63 : 623 : JSAutoRealm ac(cx, global);
64 : :
65 [ + - ]: 1246 : if (!JS_InitReflectParse(cx, global) ||
66 [ - + - + ]: 1246 : !JS_DefineDebuggerObject(cx, global))
67 : 0 : return nullptr;
68 : :
69 : 623 : return global;
70 : 623 : }
71 : :
72 : : protected:
73 : : GJS_JSAPI_RETURN_CONVENTION
74 : 364 : static JSObject* create(
75 : : JSContext* cx, const JSClass* clasp,
76 : : JS::RealmCreationOptions options = JS::RealmCreationOptions(),
77 : : JSPrincipals* principals = nullptr) {
78 : 364 : options.setNewCompartmentAndZone();
79 : 364 : return base(cx, clasp, options, principals);
80 : : }
81 : :
82 : : GJS_JSAPI_RETURN_CONVENTION
83 : 259 : 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 : 259 : options.setExistingCompartment(existing);
88 : 259 : return base(cx, clasp, options, principals);
89 : : }
90 : :
91 : : GJS_JSAPI_RETURN_CONVENTION
92 : 364 : static bool run_bootstrap(JSContext* cx, const char* bootstrap_script,
93 : : JS::HandleObject global) {
94 : 364 : Gjs::AutoChar uri{g_strdup_printf(
95 : : "resource:///org/gnome/gjs/modules/script/_bootstrap/%s.js",
96 : 364 : bootstrap_script)};
97 : :
98 : 364 : JSAutoRealm ar(cx, global);
99 : :
100 : 364 : JS::CompileOptions options(cx);
101 : 364 : options.setFileAndLine(uri, 1).setSourceIsLazy(true);
102 : :
103 : : char* script;
104 : : size_t script_len;
105 [ - + ]: 364 : if (!gjs_load_internal_source(cx, uri, &script, &script_len))
106 : 0 : return false;
107 : :
108 : 364 : JS::SourceText<mozilla::Utf8Unit> source;
109 [ - + ]: 364 : if (!source.init(cx, script, script_len,
110 : : JS::SourceOwnership::TakeOwnership))
111 : 0 : return false;
112 : :
113 : 364 : JS::RootedValue ignored(cx);
114 : 364 : return JS::Evaluate(cx, options, source, &ignored);
115 : 364 : }
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 args = 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(args[0].isString());
126 : :
127 : 25 : JS::RootedString str{m_cx, args[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 [ - + ]: 50 : if (!Gjs::NativeModuleDefineFuncs::get().define(m_cx, id.get(),
136 : 25 : &native_obj)) {
137 : 0 : gjs_throw(m_cx, "Failed to load native module: %s", id.get());
138 : 0 : return false;
139 : : }
140 : :
141 : 25 : args.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 : : .name = "GjsGlobal",
153 : : .flags = JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(
154 : : static_cast<uint32_t>(GjsGlobalSlot::LAST)),
155 : : .cOps = &defaultclassops};
156 : :
157 : : // clang-format off
158 : : static constexpr JSPropertySpec static_props[] = {
159 : : JS_STRING_SYM_PS(toStringTag, "GjsGlobal", JSPROP_READONLY),
160 : : JS_PS_END};
161 : : // clang-format on
162 : :
163 : : static constexpr JSFunctionSpec static_funcs[] = {
164 : : JS_FS_END};
165 : :
166 : : public:
167 : : GJS_JSAPI_RETURN_CONVENTION
168 : 0 : static JSObject* create(JSContext* cx) {
169 : 0 : return GjsBaseGlobal::create(cx, &klass);
170 : : }
171 : :
172 : : GJS_JSAPI_RETURN_CONVENTION
173 : 259 : static JSObject* create_with_compartment(JSContext* cx,
174 : : JS::HandleObject cmp_global) {
175 : 259 : return GjsBaseGlobal::create_with_compartment(cx, cmp_global, &klass);
176 : : }
177 : :
178 : : GJS_JSAPI_RETURN_CONVENTION
179 : 259 : static bool define_properties(JSContext* cx, JS::HandleObject global,
180 : : const char* realm_name,
181 : : const char* bootstrap_script) {
182 : 259 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
183 : 259 : if (!JS_DefinePropertyById(cx, global, atoms.window(), global,
184 : 259 : JSPROP_READONLY | JSPROP_PERMANENT) ||
185 [ + - + - : 518 : !JS_DefineFunctions(cx, global, GjsGlobal::static_funcs) ||
- + ]
186 [ - + ]: 259 : !JS_DefineProperties(cx, global, GjsGlobal::static_props))
187 : 0 : return false;
188 : :
189 : 259 : JS::Realm* realm = JS::GetObjectRealmOrNull(global);
190 : 259 : g_assert(realm && "Global object must be associated with a realm");
191 : : // const_cast is allowed here if we never free the realm data
192 : 259 : JS::SetRealmPrivate(realm, const_cast<char*>(realm_name));
193 : :
194 : 259 : JS::RootedObject native_registry(cx, JS::NewMapObject(cx));
195 [ - + ]: 259 : if (!native_registry)
196 : 0 : return false;
197 : :
198 : 259 : gjs_set_global_slot(global, GjsGlobalSlot::NATIVE_REGISTRY,
199 : 259 : JS::ObjectValue(*native_registry));
200 : :
201 : 259 : JS::RootedObject module_registry(cx, JS::NewMapObject(cx));
202 [ - + ]: 259 : if (!module_registry)
203 : 0 : return false;
204 : :
205 : 259 : gjs_set_global_slot(global, GjsGlobalSlot::MODULE_REGISTRY,
206 : 259 : JS::ObjectValue(*module_registry));
207 : :
208 : 259 : JS::RootedObject source_map_registry{cx, JS::NewMapObject(cx)};
209 [ - + ]: 259 : if (!source_map_registry)
210 : 0 : return false;
211 : :
212 : 259 : gjs_set_global_slot(global, GjsGlobalSlot::SOURCE_MAP_REGISTRY,
213 : 259 : JS::ObjectValue(*source_map_registry));
214 : :
215 : 259 : JS::Value v_importer =
216 : 259 : gjs_get_global_slot(global, GjsGlobalSlot::IMPORTS);
217 : 259 : g_assert(v_importer.isObject() &&
218 : : "importer should be defined before passing null importer to "
219 : : "GjsGlobal::define_properties");
220 : 259 : JS::RootedObject root_importer(cx, &v_importer.toObject());
221 : :
222 : : // Wrapping is a no-op if the importer is already in the same realm.
223 [ + - ]: 518 : if (!JS_WrapObject(cx, &root_importer) ||
224 [ - + - + ]: 518 : !JS_DefinePropertyById(cx, global, atoms.imports(), root_importer,
225 : : GJS_MODULE_PROP_FLAGS))
226 : 0 : return false;
227 : :
228 [ + - ]: 259 : if (bootstrap_script) {
229 [ - + ]: 259 : if (!run_bootstrap(cx, bootstrap_script, global))
230 : 0 : return false;
231 : : }
232 : :
233 : 259 : return true;
234 : 259 : }
235 : : };
236 : :
237 : : class GjsDebuggerGlobal : GjsBaseGlobal {
238 : : static constexpr JSClass klass = {
239 : : .name = "GjsDebuggerGlobal",
240 : : .flags = JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(
241 : : static_cast<uint32_t>(GjsDebuggerGlobalSlot::LAST)),
242 : : .cOps = &defaultclassops};
243 : :
244 : : static constexpr JSFunctionSpec static_funcs[] = {
245 : : JS_FN("loadNative", &load_native_module, 1, 0), JS_FS_END};
246 : :
247 : : public:
248 : : GJS_JSAPI_RETURN_CONVENTION
249 : 105 : static JSObject* create(JSContext* cx) {
250 : 105 : JS::RealmCreationOptions options;
251 : 105 : options.setToSourceEnabled(true); // debugger uses uneval()
252 : 105 : return GjsBaseGlobal::create(cx, &klass, options);
253 : 105 : }
254 : :
255 : : GJS_JSAPI_RETURN_CONVENTION
256 : 0 : static JSObject* create_with_compartment(JSContext* cx,
257 : : JS::HandleObject cmp_global) {
258 : 0 : return GjsBaseGlobal::create_with_compartment(cx, cmp_global, &klass);
259 : : }
260 : :
261 : : GJS_JSAPI_RETURN_CONVENTION
262 : 105 : static bool define_properties(JSContext* cx, JS::HandleObject global,
263 : : const char* realm_name,
264 : : const char* bootstrap_script) {
265 : 105 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
266 : 105 : if (!JS_DefinePropertyById(cx, global, atoms.window(), global,
267 [ + - - + ]: 210 : JSPROP_READONLY | JSPROP_PERMANENT) ||
268 [ - + ]: 105 : !JS_DefineFunctions(cx, global, GjsDebuggerGlobal::static_funcs))
269 : 0 : return false;
270 : :
271 : 105 : JS::Realm* realm = JS::GetObjectRealmOrNull(global);
272 : 105 : g_assert(realm && "Global object must be associated with a realm");
273 : : // const_cast is allowed here if we never free the realm data
274 : 105 : JS::SetRealmPrivate(realm, const_cast<char*>(realm_name));
275 : :
276 [ + - ]: 105 : if (bootstrap_script) {
277 [ - + ]: 105 : if (!run_bootstrap(cx, bootstrap_script, global))
278 : 0 : return false;
279 : : }
280 : :
281 : 105 : return true;
282 : : }
283 : : };
284 : :
285 : : class GjsInternalGlobal : GjsBaseGlobal {
286 : : static constexpr JSFunctionSpec static_funcs[] = {
287 : : JS_FN("compileModule", gjs_internal_compile_module, 2, 0),
288 : : JS_FN("compileInternalModule", gjs_internal_compile_internal_module, 2,
289 : : 0),
290 : : JS_FN("getRegistry", gjs_internal_get_registry, 1, 0),
291 : : JS_FN("getSourceMapRegistry", gjs_internal_get_source_map_registry, 1,
292 : : 0),
293 : : JS_FN("loadResourceOrFile", gjs_internal_load_resource_or_file, 1, 0),
294 : : JS_FN("loadResourceOrFileAsync",
295 : : gjs_internal_load_resource_or_file_async, 1, 0),
296 : : JS_FN("parseURI", gjs_internal_parse_uri, 1, 0),
297 : : JS_FN("resolveRelativeResourceOrFile",
298 : : gjs_internal_resolve_relative_resource_or_file, 2, 0),
299 : : JS_FN("setGlobalModuleLoader", gjs_internal_set_global_module_loader, 2,
300 : : 0),
301 : : JS_FN("setModulePrivate", gjs_internal_set_module_private, 2, 0),
302 : : JS_FN("uriExists", gjs_internal_uri_exists, 1, 0),
303 : : JS_FN("atob", gjs_internal_atob, 1, 0),
304 : : JS_FS_END};
305 : :
306 : : static constexpr JSClass klass = {
307 : : .name = "GjsInternalGlobal",
308 : : .flags = JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(
309 : : static_cast<uint32_t>(GjsInternalGlobalSlot::LAST)),
310 : : .cOps = &defaultclassops};
311 : :
312 : : public:
313 : : GJS_JSAPI_RETURN_CONVENTION
314 : 259 : static JSObject* create(JSContext* cx) {
315 : 259 : return GjsBaseGlobal::create(cx, &klass, {}, get_internal_principals());
316 : : }
317 : :
318 : : GJS_JSAPI_RETURN_CONVENTION
319 : 0 : static JSObject* create_with_compartment(JSContext* cx,
320 : : JS::HandleObject cmp_global) {
321 : 0 : return GjsBaseGlobal::create_with_compartment(
322 : 0 : cx, cmp_global, &klass, {}, get_internal_principals());
323 : : }
324 : :
325 : : GJS_JSAPI_RETURN_CONVENTION
326 : 259 : static bool define_properties(JSContext* cx, JS::HandleObject global,
327 : : const char* realm_name,
328 : : const char* bootstrap_script
329 : : [[maybe_unused]]) {
330 : 259 : JS::Realm* realm = JS::GetObjectRealmOrNull(global);
331 : 259 : g_assert(realm && "Global object must be associated with a realm");
332 : : // const_cast is allowed here if we never free the realm data
333 : 259 : JS::SetRealmPrivate(realm, const_cast<char*>(realm_name));
334 : :
335 : 259 : JSAutoRealm ar(cx, global);
336 : 259 : JS::RootedObject native_registry(cx, JS::NewMapObject(cx));
337 [ - + ]: 259 : if (!native_registry)
338 : 0 : return false;
339 : :
340 : 259 : gjs_set_global_slot(global, GjsGlobalSlot::NATIVE_REGISTRY,
341 : 259 : JS::ObjectValue(*native_registry));
342 : :
343 : 259 : JS::RootedObject module_registry(cx, JS::NewMapObject(cx));
344 [ - + ]: 259 : if (!module_registry)
345 : 0 : return false;
346 : :
347 : 259 : gjs_set_global_slot(global, GjsGlobalSlot::MODULE_REGISTRY,
348 : 259 : JS::ObjectValue(*module_registry));
349 : :
350 : 259 : JS::RootedObject source_map_registry{cx, JS::NewMapObject(cx)};
351 [ - + ]: 259 : if (!source_map_registry)
352 : 0 : return false;
353 : :
354 : 259 : gjs_set_global_slot(global, GjsGlobalSlot::SOURCE_MAP_REGISTRY,
355 : 259 : JS::ObjectValue(*source_map_registry));
356 : :
357 : 259 : return JS_DefineFunctions(cx, global, static_funcs);
358 : 259 : }
359 : : };
360 : :
361 : : /**
362 : : * gjs_create_global_object:
363 : : * @cx: a #JSContext
364 : : * @global_type: Type of global object, influences what APIs get defined
365 : : * @existing_global: An existing global object to share a compartment with
366 : : *
367 : : * Creates a global object, and initializes it with the default API.
368 : : *
369 : : * Returns: the created global object on success, nullptr otherwise, in which
370 : : * case an exception is pending on @cx
371 : : */
372 : 623 : JSObject* gjs_create_global_object(JSContext* cx, GjsGlobalType global_type,
373 : : JS::HandleObject existing_global) {
374 [ + + ]: 623 : if (existing_global) {
375 [ + - - - ]: 259 : switch (global_type) {
376 : 259 : case GjsGlobalType::DEFAULT:
377 : 259 : return GjsGlobal::create_with_compartment(cx, existing_global);
378 : 0 : case GjsGlobalType::DEBUGGER:
379 : 0 : return GjsDebuggerGlobal::create_with_compartment(
380 : 0 : cx, existing_global);
381 : 0 : case GjsGlobalType::INTERNAL:
382 : 0 : return GjsInternalGlobal::create_with_compartment(
383 : 0 : cx, existing_global);
384 : : }
385 : : g_assert_not_reached();
386 : : }
387 : :
388 [ - + + - ]: 364 : switch (global_type) {
389 : 0 : case GjsGlobalType::DEFAULT:
390 : 0 : return GjsGlobal::create(cx);
391 : 105 : case GjsGlobalType::DEBUGGER:
392 : 105 : return GjsDebuggerGlobal::create(cx);
393 : 259 : case GjsGlobalType::INTERNAL:
394 : 259 : return GjsInternalGlobal::create(cx);
395 : : }
396 : : g_assert_not_reached();
397 : : }
398 : :
399 : : /**
400 : : * gjs_global_is_type:
401 : : * @cx: the current #JSContext
402 : : * @type: the global type to test for
403 : : *
404 : : * Returns: whether the current global is the same type as @type
405 : : */
406 : 20716 : bool gjs_global_is_type(JSContext* cx, GjsGlobalType type) {
407 : 20716 : JSObject* global = JS::CurrentGlobalOrNull(cx);
408 : :
409 : 20716 : g_assert(global && "gjs_global_is_type called before a realm was entered.");
410 : :
411 : 20716 : JS::Value global_type =
412 : 20716 : gjs_get_global_slot(global, GjsBaseGlobalSlot::GLOBAL_TYPE);
413 : :
414 : 20716 : g_assert(global_type.isInt32());
415 : :
416 : 20716 : return static_cast<GjsGlobalType>(global_type.toInt32()) == type;
417 : : }
418 : :
419 : 0 : GjsGlobalType gjs_global_get_type(JSContext* cx) {
420 : 0 : JSObject* global = JS::CurrentGlobalOrNull(cx);
421 : :
422 : 0 : g_assert(global &&
423 : : "gjs_global_get_type called before a realm was entered.");
424 : :
425 : 0 : JS::Value global_type =
426 : 0 : gjs_get_global_slot(global, GjsBaseGlobalSlot::GLOBAL_TYPE);
427 : :
428 : 0 : g_assert(global_type.isInt32());
429 : :
430 : 0 : return static_cast<GjsGlobalType>(global_type.toInt32());
431 : : }
432 : :
433 : 25531 : GjsGlobalType gjs_global_get_type(JSObject* global) {
434 : 25531 : JS::Value global_type =
435 : 25531 : gjs_get_global_slot(global, GjsBaseGlobalSlot::GLOBAL_TYPE);
436 : :
437 : 25531 : g_assert(global_type.isInt32());
438 : :
439 : 25531 : return static_cast<GjsGlobalType>(global_type.toInt32());
440 : : }
441 : :
442 : : /**
443 : : * gjs_global_registry_set:
444 : : * @cx: the current #JSContext
445 : : * @registry: a JS Map object
446 : : * @key: a module identifier, typically a string or symbol
447 : : * @module: a module object
448 : : *
449 : : * This function inserts a module object into a global registry. Global
450 : : * registries are JS Map objects for easy reuse and access within internal JS.
451 : : * This function will assert if a module has already been inserted at the given
452 : : * key.
453 : : *
454 : : * Returns: false if an exception is pending, otherwise true.
455 : : */
456 : 832 : bool gjs_global_registry_set(JSContext* cx, JS::HandleObject registry,
457 : : JS::PropertyKey key, JS::HandleObject module) {
458 : 832 : JS::RootedValue v_key(cx);
459 [ - + ]: 832 : if (!JS_IdToValue(cx, key, &v_key))
460 : 0 : return false;
461 : :
462 : : bool has_key;
463 [ - + ]: 832 : if (!JS::MapHas(cx, registry, v_key, &has_key))
464 : 0 : return false;
465 : :
466 : 832 : g_assert(!has_key && "Module key already exists in the registry");
467 : :
468 : 832 : JS::RootedValue v_value(cx, JS::ObjectValue(*module));
469 : :
470 : 832 : return JS::MapSet(cx, registry, v_key, v_value);
471 : 832 : }
472 : :
473 : : /**
474 : : * gjs_global_registry_get:
475 : : * @cx: the current #JSContext
476 : : * @registry: a JS Map object
477 : : * @key: a module identifier, typically a string or symbol
478 : : * @module_out: (out): handle where a module object will be stored
479 : : *
480 : : * This function retrieves a module record from the global registry, or null if
481 : : * the module record is not present. Global registries are JS Map objects for
482 : : * easy reuse and access within internal JS.
483 : : *
484 : : * Returns: false if an exception is pending, otherwise true.
485 : : */
486 : 36636 : bool gjs_global_registry_get(JSContext* cx, JS::HandleObject registry,
487 : : JS::PropertyKey key,
488 : : JS::MutableHandleObject module_out) {
489 : 36636 : JS::RootedValue v_key(cx), v_value(cx);
490 [ + - ]: 73272 : if (!JS_IdToValue(cx, key, &v_key) ||
491 [ - + - + ]: 73272 : !JS::MapGet(cx, registry, v_key, &v_value))
492 : 0 : return false;
493 : :
494 : 36636 : g_assert((v_value.isUndefined() || v_value.isObject()) &&
495 : : "Invalid value in module registry");
496 : :
497 [ + + ]: 36636 : if (v_value.isObject()) {
498 : 36061 : module_out.set(&v_value.toObject());
499 : 36061 : return true;
500 : : }
501 : :
502 : 575 : module_out.set(nullptr);
503 : 575 : return true;
504 : 36636 : }
505 : :
506 : : /**
507 : : * gjs_global_source_map_get:
508 : : * @cx: the current #JSContext
509 : : * @registry: a JS Map object
510 : : * @key: a source string, such as retrieved from a stack frame
511 : : * @source_map_consumer_obj: handle where a source map consumer object will be
512 : : * stored
513 : : *
514 : : * This function retrieves a source map consumer from the source map registry,
515 : : * or null if the source does not have a source map consumer.
516 : : *
517 : : * Returns: false if an exception is pending, otherwise true.
518 : : */
519 : 1097 : bool gjs_global_source_map_get(
520 : : JSContext* cx, JS::HandleObject registry, JS::HandleString key,
521 : : JS::MutableHandleObject source_map_consumer_obj) {
522 : 1097 : JS::RootedValue v_key{cx, JS::StringValue(key)};
523 : 1097 : JS::RootedValue v_value{cx};
524 [ - + ]: 1097 : if (!JS::MapGet(cx, registry, v_key, &v_value))
525 : 0 : return false;
526 : :
527 : 1097 : g_assert((v_value.isUndefined() || v_value.isObject()) &&
528 : : "Invalid value in source map registry");
529 : :
530 [ + + ]: 1097 : if (v_value.isObject()) {
531 : 6 : source_map_consumer_obj.set(&v_value.toObject());
532 : 6 : return true;
533 : : }
534 : :
535 : 1091 : source_map_consumer_obj.set(nullptr);
536 : 1091 : return true;
537 : 1097 : }
538 : :
539 : : /**
540 : : * gjs_define_global_properties:
541 : : * @cx: a #JSContext
542 : : * @global: a JS global object that has not yet been passed to this function
543 : : * @realm_name: (nullable): name of the realm, for debug output
544 : : * @bootstrap_script: (nullable): name of a bootstrap script (found at
545 : : * resource://org/gnome/gjs/modules/script/_bootstrap/@bootstrap_script) or
546 : : * nullptr for none
547 : : *
548 : : * Defines properties on the global object such as 'window' and 'imports', and
549 : : * runs a bootstrap JS script on the global object to define any properties
550 : : * that can be defined from JS.
551 : : * This function completes the initialization of a new global object, but it
552 : : * is separate from gjs_create_global_object() because all globals share the
553 : : * same root importer.
554 : : * The code creating the main global for the JS context needs to create the
555 : : * root importer in between calling gjs_create_global_object() and
556 : : * gjs_define_global_properties().
557 : : *
558 : : * The caller of this function should be in the realm for @global.
559 : : * If the root importer object belongs to a different realm, this function will
560 : : * create a wrapper for it.
561 : : *
562 : : * Returns: true on success, false otherwise, in which case an exception is
563 : : * pending on @cx
564 : : */
565 : 623 : bool gjs_define_global_properties(JSContext* cx, JS::HandleObject global,
566 : : GjsGlobalType global_type,
567 : : const char* realm_name,
568 : : const char* bootstrap_script) {
569 : 623 : gjs_set_global_slot(global.get(), GjsBaseGlobalSlot::GLOBAL_TYPE,
570 : : JS::Int32Value(static_cast<uint32_t>(global_type)));
571 : :
572 [ + + + - ]: 623 : switch (global_type) {
573 : 259 : case GjsGlobalType::DEFAULT:
574 : 259 : return GjsGlobal::define_properties(cx, global, realm_name,
575 : 259 : bootstrap_script);
576 : 105 : case GjsGlobalType::DEBUGGER:
577 : 105 : return GjsDebuggerGlobal::define_properties(cx, global, realm_name,
578 : 105 : bootstrap_script);
579 : 259 : case GjsGlobalType::INTERNAL:
580 : 259 : return GjsInternalGlobal::define_properties(cx, global, realm_name,
581 : 259 : bootstrap_script);
582 : : }
583 : :
584 : : // Global type does not handle define_properties
585 : : g_assert_not_reached();
586 : : }
587 : :
588 : 3714 : void detail::set_global_slot(JSObject* global, uint32_t slot, JS::Value value) {
589 : 3714 : JS::SetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot, value);
590 : 3714 : }
591 : :
592 : 129469 : JS::Value detail::get_global_slot(JSObject* global, uint32_t slot) {
593 : 129469 : return JS::GetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot);
594 : : }
|