LCOV - code coverage report
Current view: top level - gjs - global.cpp (source / functions) Hit Total Coverage
Test: gjs- Code Coverage Lines: 158 202 78.2 %
Date: 2024-02-27 17:05:05 Functions: 19 23 82.6 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 41 78 52.6 %

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

Generated by: LCOV version 1.14