LCOV - code coverage report
Current view: top level - gjs - global.cpp (source / functions) Coverage Total Hit
Test: gjs-1.89.90 Code Coverage Lines: 80.4 % 225 181
Test Date: 2026-09-04 06:14:55 Functions: 83.3 % 24 20
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 53.5 % 86 46

             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                 :             : }
        

Generated by: LCOV version 2.0-1