LCOV - code coverage report
Current view: top level - gjs - context.cpp (source / functions) Coverage Total Hit
Test: gjs- Code Coverage Lines: 82.2 % 894 735
Test Date: 2025-02-09 21:27:27 Functions: 87.2 % 94 82
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 65.4 % 347 227

             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                 :             : 
       5                 :             : #include <config.h>
       6                 :             : 
       7                 :             : #include <signal.h>  // for sigaction, SIGUSR1, sa_handler
       8                 :             : #include <stdint.h>
       9                 :             : #include <stdio.h>   // for FILE, fclose, size_t
      10                 :             : #include <stdlib.h>  // for exit
      11                 :             : #include <string.h>  // for memset
      12                 :             : 
      13                 :             : #ifdef HAVE_UNISTD_H
      14                 :             : #    include <unistd.h>  // for getpid
      15                 :             : #endif
      16                 :             : 
      17                 :             : #ifdef G_OS_WIN32
      18                 :             : #    include <process.h>
      19                 :             : #    include <windows.h>
      20                 :             : #endif
      21                 :             : 
      22                 :             : #ifdef HAVE_READLINE_READLINE_H
      23                 :             : #    include <readline/history.h>
      24                 :             : #endif
      25                 :             : 
      26                 :             : #include <new>
      27                 :             : #include <string>       // for u16string
      28                 :             : #include <thread>       // for get_id
      29                 :             : #include <unordered_map>
      30                 :             : #include <utility>  // for move
      31                 :             : #include <vector>
      32                 :             : 
      33                 :             : #include <gio/gio.h>
      34                 :             : #include <girepository.h>
      35                 :             : #include <glib-object.h>
      36                 :             : #include <glib.h>
      37                 :             : 
      38                 :             : #include <js/AllocPolicy.h>       // for SystemAllocPolicy
      39                 :             : #include <js/CallAndConstruct.h>  // for Call, JS_CallFunctionValue
      40                 :             : #include <js/CallArgs.h>          // for UndefinedHandleValue
      41                 :             : #include <js/CharacterEncoding.h>
      42                 :             : #include <js/CompilationAndEvaluation.h>
      43                 :             : #include <js/CompileOptions.h>
      44                 :             : #include <js/Context.h>
      45                 :             : #include <js/ErrorReport.h>
      46                 :             : #include <js/Exception.h>     // for StealPendingExceptionStack
      47                 :             : #include <js/GCAPI.h>         // for JS_GC, JS_AddExtraGCRootsTr...
      48                 :             : #include <js/GCHashTable.h>   // for WeakCache
      49                 :             : #include <js/GCVector.h>      // for RootedVector
      50                 :             : #include <js/GlobalObject.h>  // for CurrentGlobalOrNull
      51                 :             : #include <js/HeapAPI.h>       // for ExposeObjectToActiveJS
      52                 :             : #include <js/Id.h>
      53                 :             : #include <js/Modules.h>
      54                 :             : #include <js/Promise.h>  // for JobQueue::SavedJobQueue
      55                 :             : #include <js/PropertyAndElement.h>
      56                 :             : #include <js/PropertyDescriptor.h>  // for JSPROP_PERMANENT, JSPROP_RE...
      57                 :             : #include <js/Realm.h>
      58                 :             : #include <js/RootingAPI.h>
      59                 :             : #include <js/ScriptPrivate.h>
      60                 :             : #include <js/SourceText.h>
      61                 :             : #include <js/String.h>  // for JS_NewStringCopyZ
      62                 :             : #include <js/TracingAPI.h>
      63                 :             : #include <js/TypeDecls.h>
      64                 :             : #include <js/UniquePtr.h>
      65                 :             : #include <js/Utility.h>  // for DeletePolicy via WeakCache
      66                 :             : #include <js/Value.h>
      67                 :             : #include <js/ValueArray.h>
      68                 :             : #include <js/friend/DumpFunctions.h>
      69                 :             : #include <jsapi.h>              // for JS_GetFunctionObject, JS_Ge...
      70                 :             : #include <jsfriendapi.h>        // for ScriptEnvironmentPreparer
      71                 :             : #include <mozilla/Result.h>
      72                 :             : #include <mozilla/UniquePtr.h>  // for UniquePtr::get
      73                 :             : 
      74                 :             : #include "gi/closure.h"  // for Closure::Ptr, Closure
      75                 :             : #include "gi/function.h"
      76                 :             : #include "gi/object.h"
      77                 :             : #include "gi/private.h"
      78                 :             : #include "gi/repo.h"
      79                 :             : #include "gi/utils-inl.h"
      80                 :             : #include "gjs/atoms.h"
      81                 :             : #include "gjs/auto.h"
      82                 :             : #include "gjs/byteArray.h"
      83                 :             : #include "gjs/context-private.h"
      84                 :             : #include "gjs/context.h"
      85                 :             : #include "gjs/engine.h"
      86                 :             : #include "gjs/error-types.h"
      87                 :             : #include "gjs/gerror-result.h"
      88                 :             : #include "gjs/global.h"
      89                 :             : #include "gjs/importer.h"
      90                 :             : #include "gjs/internal.h"
      91                 :             : #include "gjs/jsapi-util.h"
      92                 :             : #include "gjs/mainloop.h"
      93                 :             : #include "gjs/mem.h"
      94                 :             : #include "gjs/module.h"
      95                 :             : #include "gjs/native.h"
      96                 :             : #include "gjs/objectbox.h"
      97                 :             : #include "gjs/profiler-private.h"
      98                 :             : #include "gjs/profiler.h"
      99                 :             : #include "gjs/promise.h"
     100                 :             : #include "gjs/text-encoding.h"
     101                 :             : #include "modules/cairo-module.h"
     102                 :             : #include "modules/console.h"
     103                 :             : #include "modules/print.h"
     104                 :             : #include "modules/system.h"
     105                 :             : #include "util/log.h"
     106                 :             : 
     107                 :             : namespace mozilla {
     108                 :             : union Utf8Unit;
     109                 :             : }
     110                 :             : 
     111                 :             : using Gjs::GErrorResult;
     112                 :             : using mozilla::Err, mozilla::Ok;
     113                 :             : 
     114                 :             : static void     gjs_context_dispose           (GObject               *object);
     115                 :             : static void     gjs_context_finalize          (GObject               *object);
     116                 :             : static void     gjs_context_constructed       (GObject               *object);
     117                 :             : static void     gjs_context_get_property      (GObject               *object,
     118                 :             :                                                   guint                  prop_id,
     119                 :             :                                                   GValue                *value,
     120                 :             :                                                   GParamSpec            *pspec);
     121                 :             : static void     gjs_context_set_property      (GObject               *object,
     122                 :             :                                                   guint                  prop_id,
     123                 :             :                                                   const GValue          *value,
     124                 :             :                                                   GParamSpec            *pspec);
     125                 :             : 
     126                 :           0 : void GjsContextPrivate::EnvironmentPreparer::invoke(JS::HandleObject scope,
     127                 :             :                                                     Closure& closure) {
     128                 :           0 :     g_assert(!JS_IsExceptionPending(m_cx));
     129                 :             : 
     130                 :           0 :     JSAutoRealm ar(m_cx, scope);
     131         [ #  # ]:           0 :     if (!closure(m_cx))
     132                 :           0 :         gjs_log_exception(m_cx);
     133                 :           0 : }
     134                 :             : 
     135                 :             : struct _GjsContext {
     136                 :             :     GObject parent;
     137                 :             : };
     138                 :             : 
     139   [ +  +  +  -  :       25996 : G_DEFINE_TYPE_WITH_PRIVATE(GjsContext, gjs_context, G_TYPE_OBJECT);
                   +  + ]
     140                 :             : 
     141                 :        2556 : GjsContextPrivate* GjsContextPrivate::from_object(GObject* js_context) {
     142                 :        2556 :     g_return_val_if_fail(GJS_IS_CONTEXT(js_context), nullptr);
     143                 :             :     return static_cast<GjsContextPrivate*>(
     144                 :        2556 :         gjs_context_get_instance_private(GJS_CONTEXT(js_context)));
     145                 :             : }
     146                 :             : 
     147                 :        7613 : GjsContextPrivate* GjsContextPrivate::from_object(GjsContext* js_context) {
     148                 :        7613 :     g_return_val_if_fail(GJS_IS_CONTEXT(js_context), nullptr);
     149                 :             :     return static_cast<GjsContextPrivate*>(
     150                 :        7613 :         gjs_context_get_instance_private(js_context));
     151                 :             : }
     152                 :             : 
     153                 :        5117 : GjsContextPrivate* GjsContextPrivate::from_current_context() {
     154                 :        5117 :     return from_object(gjs_context_get_current());
     155                 :             : }
     156                 :             : 
     157                 :             : enum {
     158                 :             :     PROP_CONTEXT_0,
     159                 :             :     PROP_PROGRAM_PATH,
     160                 :             :     PROP_SEARCH_PATH,
     161                 :             :     PROP_PROGRAM_NAME,
     162                 :             :     PROP_PROFILER_ENABLED,
     163                 :             :     PROP_PROFILER_SIGUSR2,
     164                 :             :     PROP_EXEC_AS_MODULE,
     165                 :             :     PROP_REPL_HISTORY_PATH
     166                 :             : };
     167                 :             : 
     168                 :             : static GMutex contexts_lock;
     169                 :             : static GList *all_contexts = NULL;
     170                 :             : 
     171                 :             : static Gjs::AutoChar dump_heap_output;
     172                 :             : static unsigned dump_heap_idle_id = 0;
     173                 :             : 
     174                 :             : #ifdef G_OS_UNIX
     175                 :             : // Currently heap dumping via SIGUSR1 is only supported on UNIX platforms!
     176                 :             : // This can reduce performance. See note in system.cpp on System.dumpHeap().
     177                 :             : static void
     178                 :           0 : gjs_context_dump_heaps(void)
     179                 :             : {
     180                 :             :     static unsigned counter = 0;
     181                 :             : 
     182                 :           0 :     gjs_memory_report("signal handler", false);
     183                 :             : 
     184                 :             :     /* dump to sequential files to allow easier comparisons */
     185                 :             :     Gjs::AutoChar filename{g_strdup_printf("%s.%jd.%u", dump_heap_output.get(),
     186                 :           0 :                                            intmax_t(getpid()), counter)};
     187                 :           0 :     ++counter;
     188                 :             : 
     189                 :           0 :     FILE *fp = fopen(filename, "w");
     190         [ #  # ]:           0 :     if (!fp)
     191                 :           0 :         return;
     192                 :             : 
     193   [ #  #  #  # ]:           0 :     for (GList *l = all_contexts; l; l = g_list_next(l)) {
     194                 :           0 :         auto* gjs = static_cast<GjsContextPrivate*>(l->data);
     195                 :           0 :         js::DumpHeap(gjs->context(), fp, js::CollectNurseryBeforeDump);
     196                 :             :     }
     197                 :             : 
     198                 :           0 :     fclose(fp);
     199         [ #  # ]:           0 : }
     200                 :             : 
     201                 :           0 : static gboolean dump_heap_idle(void*) {
     202                 :           0 :     dump_heap_idle_id = 0;
     203                 :             : 
     204                 :           0 :     gjs_context_dump_heaps();
     205                 :             : 
     206                 :           0 :     return false;
     207                 :             : }
     208                 :             : 
     209                 :           0 : static void dump_heap_signal_handler(int signum [[maybe_unused]]) {
     210         [ #  # ]:           0 :     if (dump_heap_idle_id == 0)
     211                 :           0 :         dump_heap_idle_id = g_idle_add_full(G_PRIORITY_HIGH_IDLE,
     212                 :             :                                             dump_heap_idle, nullptr, nullptr);
     213                 :           0 : }
     214                 :             : #endif
     215                 :             : 
     216                 :             : static void
     217                 :         256 : setup_dump_heap(void)
     218                 :             : {
     219                 :             :     static bool dump_heap_initialized = false;
     220         [ +  + ]:         256 :     if (!dump_heap_initialized) {
     221                 :         111 :         dump_heap_initialized = true;
     222                 :             : 
     223                 :             :         /* install signal handler only if environment variable is set */
     224                 :         111 :         const char *heap_output = g_getenv("GJS_DEBUG_HEAP_OUTPUT");
     225         [ -  + ]:         111 :         if (heap_output) {
     226                 :             : #ifdef G_OS_UNIX
     227                 :             :             struct sigaction sa;
     228                 :             : 
     229                 :           0 :             dump_heap_output = g_strdup(heap_output);
     230                 :             : 
     231                 :           0 :             memset(&sa, 0, sizeof(sa));
     232                 :           0 :             sa.sa_handler = dump_heap_signal_handler;
     233                 :           0 :             sigaction(SIGUSR1, &sa, nullptr);
     234                 :             : #else
     235                 :             :             g_message(
     236                 :             :                 "heap dump is currently only supported on UNIX platforms");
     237                 :             : #endif
     238                 :             :         }
     239                 :             :     }
     240                 :         256 : }
     241                 :             : 
     242                 :             : static void
     243                 :         256 : gjs_context_init(GjsContext *js_context)
     244                 :             : {
     245                 :         256 :     gjs_log_init();
     246                 :         256 :     gjs_context_make_current(js_context);
     247                 :         256 : }
     248                 :             : 
     249                 :             : static void
     250                 :         111 : gjs_context_class_init(GjsContextClass *klass)
     251                 :             : {
     252                 :         111 :     GObjectClass *object_class = G_OBJECT_CLASS (klass);
     253                 :             :     GParamSpec *pspec;
     254                 :             : 
     255                 :         111 :     gjs_log_init();
     256                 :             : 
     257                 :         111 :     object_class->dispose = gjs_context_dispose;
     258                 :         111 :     object_class->finalize = gjs_context_finalize;
     259                 :             : 
     260                 :         111 :     object_class->constructed = gjs_context_constructed;
     261                 :         111 :     object_class->get_property = gjs_context_get_property;
     262                 :         111 :     object_class->set_property = gjs_context_set_property;
     263                 :             : 
     264                 :         111 :     pspec = g_param_spec_boxed("search-path",
     265                 :             :                                "Search path",
     266                 :             :                                "Path where modules to import should reside",
     267                 :             :                                G_TYPE_STRV,
     268                 :             :                                (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
     269                 :             : 
     270                 :         111 :     g_object_class_install_property(object_class,
     271                 :             :                                     PROP_SEARCH_PATH,
     272                 :             :                                     pspec);
     273                 :         111 :     g_param_spec_unref(pspec);
     274                 :             : 
     275                 :         111 :     pspec = g_param_spec_string("program-name",
     276                 :             :                                 "Program Name",
     277                 :             :                                 "The filename of the launched JS program",
     278                 :             :                                 "",
     279                 :             :                                 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
     280                 :             : 
     281                 :         111 :     g_object_class_install_property(object_class,
     282                 :             :                                     PROP_PROGRAM_NAME,
     283                 :             :                                     pspec);
     284                 :         111 :     g_param_spec_unref(pspec);
     285                 :             : 
     286                 :         111 :     pspec = g_param_spec_string(
     287                 :             :         "program-path", "Executed File Path",
     288                 :             :         "The full path of the launched file or NULL if GJS was launched from "
     289                 :             :         "the C API or interactive console.",
     290                 :             :         nullptr, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
     291                 :             : 
     292                 :         111 :     g_object_class_install_property(object_class, PROP_PROGRAM_PATH, pspec);
     293                 :         111 :     g_param_spec_unref(pspec);
     294                 :             : 
     295                 :             :     /**
     296                 :             :      * GjsContext:profiler-enabled:
     297                 :             :      *
     298                 :             :      * Set this property to profile any JS code run by this context. By
     299                 :             :      * default, the profiler is started and stopped when you call
     300                 :             :      * gjs_context_eval().
     301                 :             :      *
     302                 :             :      * The value of this property is superseded by the GJS_ENABLE_PROFILER
     303                 :             :      * environment variable.
     304                 :             :      *
     305                 :             :      * You may only have one context with the profiler enabled at a time.
     306                 :             :      */
     307                 :         111 :     pspec = g_param_spec_boolean("profiler-enabled", "Profiler enabled",
     308                 :             :                                  "Whether to profile JS code run by this context",
     309                 :             :                                  FALSE,
     310                 :             :                                  GParamFlags(G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
     311                 :         111 :     g_object_class_install_property(object_class, PROP_PROFILER_ENABLED, pspec);
     312                 :         111 :     g_param_spec_unref(pspec);
     313                 :             : 
     314                 :             :     /**
     315                 :             :      * GjsContext:profiler-sigusr2:
     316                 :             :      *
     317                 :             :      * Set this property to install a SIGUSR2 signal handler that starts and
     318                 :             :      * stops the profiler. This property also implies that
     319                 :             :      * #GjsContext:profiler-enabled is set.
     320                 :             :      */
     321                 :         111 :     pspec = g_param_spec_boolean("profiler-sigusr2", "Profiler SIGUSR2",
     322                 :             :                                  "Whether to activate the profiler on SIGUSR2",
     323                 :             :                                  FALSE,
     324                 :             :                                  GParamFlags(G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
     325                 :         111 :     g_object_class_install_property(object_class, PROP_PROFILER_SIGUSR2, pspec);
     326                 :         111 :     g_param_spec_unref(pspec);
     327                 :             : 
     328                 :         111 :     pspec = g_param_spec_boolean(
     329                 :             :         "exec-as-module", "Execute as module",
     330                 :             :         "Whether to execute the file as a module", FALSE,
     331                 :             :         GParamFlags(G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
     332                 :         111 :     g_object_class_install_property(object_class, PROP_EXEC_AS_MODULE, pspec);
     333                 :         111 :     g_param_spec_unref(pspec);
     334                 :             : 
     335                 :             :     /**
     336                 :             :      * GjsContext:repl-history-path:
     337                 :             :      *
     338                 :             :      * Set this property to persist repl command history in the console or
     339                 :             :      * debugger. If NULL, then command history will not be persisted.
     340                 :             :      */
     341                 :         111 :     pspec = g_param_spec_string(
     342                 :             :         "repl-history-path", "REPL History Path",
     343                 :             :         "The writable path to persist repl history", nullptr,
     344                 :             :         (GParamFlags)(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
     345                 :             :                       G_PARAM_STATIC_STRINGS));
     346                 :         111 :     g_object_class_install_property(object_class, PROP_REPL_HISTORY_PATH,
     347                 :             :                                     pspec);
     348                 :         111 :     g_param_spec_unref(pspec);
     349                 :             : 
     350                 :             :     /* For GjsPrivate */
     351         [ -  + ]:         111 :     if (!g_getenv("GJS_USE_UNINSTALLED_FILES")) {
     352                 :             : #ifdef G_OS_WIN32
     353                 :             :         extern HMODULE gjs_dll;
     354                 :             :         Gjs::AutoChar basedir{
     355                 :             :             g_win32_get_package_installation_directory_of_module(gjs_dll)};
     356                 :             :         Gjs::AutoChar priv_typelib_dir{g_build_filename(
     357                 :             :             basedir, "lib", "gjs", "girepository-1.0", nullptr)};
     358                 :             : #else
     359                 :             :         Gjs::AutoChar priv_typelib_dir{
     360                 :           0 :             g_build_filename(PKGLIBDIR, "girepository-1.0", nullptr)};
     361                 :             : #endif
     362                 :           0 :         g_irepository_prepend_search_path(priv_typelib_dir);
     363                 :           0 :     }
     364                 :         111 :     auto& registry = Gjs::NativeModuleDefineFuncs::get();
     365                 :         111 :     registry.add("_promiseNative", gjs_define_native_promise_stuff);
     366                 :         111 :     registry.add("_byteArrayNative", gjs_define_byte_array_stuff);
     367                 :         111 :     registry.add("_encodingNative", gjs_define_text_encoding_stuff);
     368                 :         111 :     registry.add("_gi", gjs_define_private_gi_stuff);
     369                 :         111 :     registry.add("gi", gjs_define_repo);
     370                 :         111 :     registry.add("cairoNative", gjs_js_define_cairo_stuff);
     371                 :         111 :     registry.add("system", gjs_js_define_system_stuff);
     372                 :         111 :     registry.add("console", gjs_define_console_stuff);
     373                 :         111 :     registry.add("_print", gjs_define_print_stuff);
     374                 :         111 : }
     375                 :             : 
     376                 :         347 : void GjsContextPrivate::trace(JSTracer* trc, void* data) {
     377                 :         347 :     auto* gjs = static_cast<GjsContextPrivate*>(data);
     378                 :         347 :     JS::TraceEdge<JSObject*>(trc, &gjs->m_global, "GJS global object");
     379                 :         347 :     JS::TraceEdge<JSObject*>(trc, &gjs->m_internal_global,
     380                 :             :                              "GJS internal global object");
     381                 :         347 :     JS::TraceEdge<JSObject*>(trc, &gjs->m_main_loop_hook, "GJS main loop hook");
     382                 :         347 :     gjs->m_atoms->trace(trc);
     383                 :         347 :     gjs->m_job_queue.trace(trc);
     384                 :         347 :     gjs->m_cleanup_tasks.trace(trc);
     385                 :         347 :     gjs->m_object_init_list.trace(trc);
     386                 :         347 : }
     387                 :             : 
     388                 :       10083 : void GjsContextPrivate::warn_about_unhandled_promise_rejections(void) {
     389         [ +  + ]:       10084 :     for (auto& kv : m_unhandled_rejection_stacks) {
     390                 :           1 :         const char* stack = kv.second.get();
     391   [ +  -  +  - ]:           1 :         g_warning("Unhandled promise rejection. To suppress this warning, add "
     392                 :             :                   "an error handler to your promise chain with .catch() or a "
     393                 :             :                   "try-catch block around your await expression. %s%s",
     394                 :             :                   stack ? "Stack trace of the failed promise:\n" :
     395                 :             :                     "Unfortunately there is no stack trace of the failed promise.",
     396                 :             :                   stack ? stack : "");
     397                 :             :     }
     398                 :       10083 :     m_unhandled_rejection_stacks.clear();
     399                 :       10083 : }
     400                 :             : 
     401                 :             : static void
     402                 :         254 : gjs_context_dispose(GObject *object)
     403                 :             : {
     404                 :         254 :     gjs_debug(GJS_DEBUG_CONTEXT, "JS shutdown sequence");
     405                 :             : 
     406                 :         254 :     GjsContextPrivate* gjs = GjsContextPrivate::from_object(object);
     407                 :             : 
     408                 :         254 :     g_assert(gjs->is_owner_thread() &&
     409                 :             :              "Gjs Context disposed from another thread");
     410                 :             : 
     411                 :             :     /* Profiler must be stopped and freed before context is shut down */
     412                 :         254 :     gjs->free_profiler();
     413                 :             : 
     414                 :             :     /* Stop accepting entries in the toggle queue before running dispose
     415                 :             :      * notifications, which causes all GjsMaybeOwned instances to unroot.
     416                 :             :      * We don't want any objects to toggle down after that. */
     417                 :         254 :     gjs_debug(GJS_DEBUG_CONTEXT, "Shutting down toggle queue");
     418                 :         254 :     gjs_object_clear_toggles();
     419                 :         254 :     gjs_object_shutdown_toggle_queue();
     420                 :             : 
     421         [ +  - ]:         254 :     if (gjs->context())
     422                 :         254 :         ObjectInstance::context_dispose_notify(nullptr, object);
     423                 :             : 
     424                 :         254 :     gjs_debug(GJS_DEBUG_CONTEXT,
     425                 :             :               "Notifying external reference holders of GjsContext dispose");
     426                 :         254 :     G_OBJECT_CLASS(gjs_context_parent_class)->dispose(object);
     427                 :             : 
     428                 :         254 :     gjs->dispose();
     429                 :         254 : }
     430                 :             : 
     431                 :         254 : void GjsContextPrivate::free_profiler(void) {
     432                 :         254 :     gjs_debug(GJS_DEBUG_CONTEXT, "Stopping profiler");
     433         [ +  + ]:         254 :     if (m_profiler)
     434         [ +  - ]:           5 :         g_clear_pointer(&m_profiler, _gjs_profiler_free);
     435                 :         254 : }
     436                 :             : 
     437                 :       13689 : void GjsContextPrivate::register_notifier(DestroyNotify notify_func,
     438                 :             :                                           void* data) {
     439                 :       13689 :     m_destroy_notifications.push_back({notify_func, data});
     440                 :       13689 : }
     441                 :             : 
     442                 :       13498 : void GjsContextPrivate::unregister_notifier(DestroyNotify notify_func,
     443                 :             :                                             void* data) {
     444                 :       13498 :     auto target = std::make_pair(notify_func, data);
     445                 :       13498 :     Gjs::remove_one_from_unsorted_vector(&m_destroy_notifications, target);
     446                 :       13498 : }
     447                 :             : 
     448                 :         254 : void GjsContextPrivate::dispose(void) {
     449         [ +  - ]:         254 :     if (m_cx) {
     450                 :         254 :         stop_draining_job_queue();
     451                 :             : 
     452                 :         254 :         gjs_debug(GJS_DEBUG_CONTEXT,
     453                 :             :                   "Notifying reference holders of GjsContext dispose");
     454                 :             : 
     455         [ +  + ]:         442 :         for (auto const& destroy_notify : m_destroy_notifications)
     456                 :         188 :             destroy_notify.first(m_cx, destroy_notify.second);
     457                 :             : 
     458                 :         254 :         gjs_debug(GJS_DEBUG_CONTEXT,
     459                 :             :                   "Checking unhandled promise rejections");
     460                 :         254 :         warn_about_unhandled_promise_rejections();
     461                 :             : 
     462                 :         254 :         gjs_debug(GJS_DEBUG_CONTEXT, "Releasing cached JS wrappers");
     463                 :         254 :         m_fundamental_table->clear();
     464                 :         254 :         m_gtype_table->clear();
     465                 :             : 
     466                 :             :         /* Do a full GC here before tearing down, since once we do
     467                 :             :          * that we may not have the JS::GetReservedSlot(, 0) to access the
     468                 :             :          * context
     469                 :             :          */
     470                 :         254 :         gjs_debug(GJS_DEBUG_CONTEXT, "Final triggered GC");
     471                 :         254 :         JS_GC(m_cx, Gjs::GCReason::GJS_CONTEXT_DISPOSE);
     472                 :             : 
     473                 :         254 :         gjs_debug(GJS_DEBUG_CONTEXT, "Destroying JS context");
     474                 :         254 :         m_destroying.store(true);
     475                 :             : 
     476                 :             :         /* Now, release all native objects, to avoid recursion between
     477                 :             :          * the JS teardown and the C teardown.  The JSObject proxies
     478                 :             :          * still exist, but point to NULL.
     479                 :             :          */
     480                 :         254 :         gjs_debug(GJS_DEBUG_CONTEXT, "Releasing all native objects");
     481                 :         254 :         ObjectInstance::prepare_shutdown();
     482                 :         254 :         GjsCallbackTrampoline::prepare_shutdown();
     483                 :             : 
     484                 :         254 :         gjs_debug(GJS_DEBUG_CONTEXT, "Disabling auto GC");
     485         [ +  + ]:         254 :         if (m_auto_gc_id > 0) {
     486                 :         141 :             g_source_remove(m_auto_gc_id);
     487                 :         141 :             m_auto_gc_id = 0;
     488                 :             :         }
     489                 :             : 
     490                 :         254 :         gjs_debug(GJS_DEBUG_CONTEXT, "Ending trace on global object");
     491                 :         254 :         JS_RemoveExtraGCRootsTracer(m_cx, &GjsContextPrivate::trace, this);
     492                 :         254 :         m_global = nullptr;
     493                 :         254 :         m_internal_global = nullptr;
     494                 :         254 :         m_main_loop_hook = nullptr;
     495                 :             : 
     496                 :         254 :         gjs_debug(GJS_DEBUG_CONTEXT, "Freeing allocated resources");
     497         [ +  - ]:         254 :         delete m_fundamental_table;
     498         [ +  - ]:         254 :         delete m_gtype_table;
     499         [ +  - ]:         254 :         delete m_atoms;
     500                 :             : 
     501                 :         254 :         m_job_queue.clear();
     502                 :         254 :         m_object_init_list.clear();
     503                 :             : 
     504                 :             :         /* Tear down JS */
     505                 :         254 :         JS_DestroyContext(m_cx);
     506                 :         254 :         m_cx = nullptr;
     507                 :             :         // don't use g_clear_pointer() as we want the pointer intact while we
     508                 :             :         // destroy the context in case we dump stack
     509                 :         254 :         gjs_debug(GJS_DEBUG_CONTEXT, "JS context destroyed");
     510                 :             :     }
     511                 :         254 : }
     512                 :             : 
     513                 :         254 : GjsContextPrivate::~GjsContextPrivate(void) {
     514         [ +  + ]:         254 :     g_clear_pointer(&m_search_path, g_strfreev);
     515         [ +  + ]:         254 :     g_clear_pointer(&m_program_path, g_free);
     516         [ +  - ]:         254 :     g_clear_pointer(&m_program_name, g_free);
     517         [ +  + ]:         254 :     g_clear_pointer(&m_repl_history_path, g_free);
     518                 :         254 : }
     519                 :             : 
     520                 :             : static void
     521                 :         254 : gjs_context_finalize(GObject *object)
     522                 :             : {
     523         [ +  - ]:         254 :     if (gjs_context_get_current() == (GjsContext*)object)
     524                 :         254 :         gjs_context_make_current(NULL);
     525                 :             : 
     526                 :         254 :     g_mutex_lock(&contexts_lock);
     527                 :         254 :     all_contexts = g_list_remove(all_contexts, object);
     528                 :         254 :     g_mutex_unlock(&contexts_lock);
     529                 :             : 
     530                 :         254 :     GjsContextPrivate* gjs = GjsContextPrivate::from_object(object);
     531                 :         254 :     gjs->~GjsContextPrivate();
     532                 :         254 :     G_OBJECT_CLASS(gjs_context_parent_class)->finalize(object);
     533                 :             : 
     534                 :         254 :     g_mutex_lock(&contexts_lock);
     535         [ +  - ]:         254 :     if (!all_contexts)
     536                 :         254 :         gjs_log_cleanup();
     537                 :         254 :     g_mutex_unlock(&contexts_lock);
     538                 :         254 : }
     539                 :             : 
     540                 :             : static void
     541                 :         256 : gjs_context_constructed(GObject *object)
     542                 :             : {
     543                 :         256 :     GjsContext *js_context = GJS_CONTEXT(object);
     544                 :             : 
     545                 :         256 :     G_OBJECT_CLASS(gjs_context_parent_class)->constructed(object);
     546                 :             : 
     547                 :         256 :     GjsContextPrivate* gjs_location = GjsContextPrivate::from_object(object);
     548                 :         256 :     JSContext* cx = gjs_create_js_context(gjs_location);
     549         [ -  + ]:         256 :     if (!cx)
     550                 :           0 :         g_error("Failed to create javascript context");
     551                 :             : 
     552                 :         256 :     new (gjs_location) GjsContextPrivate(cx, js_context);
     553                 :             : 
     554                 :         256 :     g_mutex_lock(&contexts_lock);
     555                 :         256 :     all_contexts = g_list_prepend(all_contexts, object);
     556                 :         256 :     g_mutex_unlock(&contexts_lock);
     557                 :             : 
     558                 :         256 :     setup_dump_heap();
     559                 :             : 
     560                 :             : #ifdef HAVE_READLINE_READLINE_H
     561                 :         256 :     const char* path = gjs_context_get_repl_history_path(js_context);
     562                 :             :     // Populate command history from persisted file
     563         [ +  + ]:         256 :     if (path) {
     564                 :          61 :         int err = read_history(path);
     565   [ +  +  -  +  :          61 :         if (err != 0 && g_getenv("GJS_REPL_HISTORY"))
                   -  + ]
     566                 :           0 :             g_warning("Could not read REPL history file %s: %s", path,
     567                 :             :                       g_strerror(err));
     568                 :             :     }
     569                 :             : #endif
     570                 :         256 : }
     571                 :             : 
     572                 :           3 : static bool on_context_module_rejected_log_exception(JSContext* cx,
     573                 :             :                                                      unsigned argc,
     574                 :             :                                                      JS::Value* vp) {
     575                 :           3 :     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
     576                 :             : 
     577                 :           3 :     gjs_debug(GJS_DEBUG_IMPORTER, "Module evaluation promise rejected: %s",
     578                 :           6 :               gjs_debug_callable(&args.callee()).c_str());
     579                 :             : 
     580                 :           3 :     JS::HandleValue error = args.get(0);
     581                 :             : 
     582                 :           3 :     GjsContextPrivate* gjs_cx = GjsContextPrivate::from_cx(cx);
     583                 :           3 :     gjs_cx->report_unhandled_exception();
     584                 :             : 
     585                 :           3 :     gjs_log_exception_full(cx, error, nullptr, G_LOG_LEVEL_CRITICAL);
     586                 :             : 
     587                 :           3 :     gjs_cx->main_loop_release();
     588                 :             : 
     589                 :           3 :     args.rval().setUndefined();
     590                 :           3 :     return true;
     591                 :             : }
     592                 :             : 
     593                 :         624 : static bool on_context_module_resolved(JSContext* cx, unsigned argc,
     594                 :             :                                        JS::Value* vp) {
     595                 :         624 :     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
     596                 :             : 
     597                 :         624 :     gjs_debug(GJS_DEBUG_IMPORTER, "Module evaluation promise resolved: %s",
     598                 :        1248 :               gjs_debug_callable(&args.callee()).c_str());
     599                 :             : 
     600                 :         624 :     args.rval().setUndefined();
     601                 :             : 
     602                 :         624 :     GjsContextPrivate::from_cx(cx)->main_loop_release();
     603                 :             : 
     604                 :         624 :     return true;
     605                 :             : }
     606                 :             : 
     607                 :         632 : static bool add_promise_reactions(JSContext* cx, JS::HandleValue promise,
     608                 :             :                                   JSNative resolve, JSNative reject,
     609                 :             :                                   const std::string& debug_tag) {
     610                 :         632 :     g_assert(promise.isObject() && "got weird value from JS::ModuleEvaluate");
     611                 :         632 :     JS::RootedObject promise_object(cx, &promise.toObject());
     612                 :             : 
     613                 :         632 :     std::string resolved_tag = debug_tag + " async resolved";
     614                 :         632 :     std::string rejected_tag = debug_tag + " async rejected";
     615                 :             : 
     616                 :             :     JS::RootedFunction on_rejected(
     617                 :             :         cx,
     618                 :         632 :         js::NewFunctionWithReserved(cx, reject, 1, 0, rejected_tag.c_str()));
     619         [ -  + ]:         632 :     if (!on_rejected)
     620                 :           0 :         return false;
     621                 :             :     JS::RootedFunction on_resolved(
     622                 :             :         cx,
     623                 :         632 :         js::NewFunctionWithReserved(cx, resolve, 1, 0, resolved_tag.c_str()));
     624         [ -  + ]:         632 :     if (!on_resolved)
     625                 :           0 :         return false;
     626                 :             : 
     627                 :         632 :     JS::RootedObject resolved(cx, JS_GetFunctionObject(on_resolved));
     628                 :         632 :     JS::RootedObject rejected(cx, JS_GetFunctionObject(on_rejected));
     629                 :             : 
     630                 :         632 :     return JS::AddPromiseReactions(cx, promise_object, resolved, rejected);
     631                 :         632 : }
     632                 :             : 
     633                 :         512 : static void load_context_module(JSContext* cx, const char* uri,
     634                 :             :                                 const char* debug_identifier) {
     635                 :         512 :     JS::RootedObject loader(cx, gjs_module_load(cx, uri, uri));
     636                 :             : 
     637         [ -  + ]:         512 :     if (!loader) {
     638                 :           0 :         gjs_log_exception(cx);
     639                 :           0 :         g_error("Failed to load %s module.", debug_identifier);
     640                 :             :     }
     641                 :             : 
     642         [ -  + ]:         512 :     if (!JS::ModuleLink(cx, loader)) {
     643                 :           0 :         gjs_log_exception(cx);
     644                 :           0 :         g_error("Failed to instantiate %s module.", debug_identifier);
     645                 :             :     }
     646                 :             : 
     647                 :         512 :     JS::RootedValue evaluation_promise(cx);
     648         [ -  + ]:         512 :     if (!JS::ModuleEvaluate(cx, loader, &evaluation_promise)) {
     649                 :           0 :         gjs_log_exception(cx);
     650                 :           0 :         g_error("Failed to evaluate %s module.", debug_identifier);
     651                 :             :     }
     652                 :             : 
     653                 :         512 :     GjsContextPrivate::from_cx(cx)->main_loop_hold();
     654                 :         512 :     bool ok = add_promise_reactions(
     655                 :             :         cx, evaluation_promise, on_context_module_resolved,
     656                 :         512 :         [](JSContext* cx, unsigned argc, JS::Value* vp) {
     657                 :           0 :             JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
     658                 :             : 
     659                 :           0 :             gjs_debug(GJS_DEBUG_IMPORTER,
     660                 :             :                       "Module evaluation promise rejected: %s",
     661                 :           0 :                       gjs_debug_callable(&args.callee()).c_str());
     662                 :             : 
     663                 :           0 :             JS::HandleValue error = args.get(0);
     664                 :             :             // Abort because this module is required.
     665                 :           0 :             gjs_log_exception_full(cx, error, nullptr, G_LOG_LEVEL_ERROR);
     666                 :             : 
     667                 :           0 :             GjsContextPrivate::from_cx(cx)->main_loop_release();
     668                 :           0 :             return false;
     669                 :             :         },
     670                 :             :         debug_identifier);
     671                 :             : 
     672         [ -  + ]:         512 :     if (!ok) {
     673                 :           0 :         gjs_log_exception(cx);
     674                 :           0 :         g_error("Failed to load %s module.", debug_identifier);
     675                 :             :     }
     676                 :         512 : }
     677                 :             : 
     678                 :         256 : GjsContextPrivate::GjsContextPrivate(JSContext* cx, GjsContext* public_context)
     679                 :         256 :     : m_public_context(public_context),
     680                 :         256 :       m_cx(cx),
     681                 :         256 :       m_owner_thread(std::this_thread::get_id()),
     682                 :         256 :       m_dispatcher(this),
     683                 :         256 :       m_memory_monitor(g_memory_monitor_dup_default()),
     684                 :        1024 :       m_environment_preparer(cx) {
     685                 :         256 :     JS_SetGCCallback(
     686                 :             :         cx,
     687                 :        1266 :         [](JSContext*, JSGCStatus status, JS::GCReason reason, void* data) {
     688                 :        1010 :             static_cast<GjsContextPrivate*>(data)->on_garbage_collection(
     689                 :             :                 status, reason);
     690                 :        1010 :         },
     691                 :             :         this);
     692                 :             : 
     693                 :         256 :     const char *env_profiler = g_getenv("GJS_ENABLE_PROFILER");
     694   [ +  +  -  + ]:         256 :     if (env_profiler || m_should_listen_sigusr2)
     695                 :           1 :         m_should_profile = true;
     696                 :             : 
     697         [ +  + ]:         256 :     if (m_should_profile) {
     698                 :           5 :         m_profiler = _gjs_profiler_new(public_context);
     699                 :             : 
     700         [ -  + ]:           5 :         if (!m_profiler) {
     701                 :           0 :             m_should_profile = false;
     702                 :             :         } else {
     703         [ -  + ]:           5 :             if (m_should_listen_sigusr2)
     704                 :           0 :                 _gjs_profiler_setup_signals(m_profiler, public_context);
     705                 :             :         }
     706                 :             :     }
     707                 :             : 
     708                 :         256 :     JSRuntime* rt = JS_GetRuntime(m_cx);
     709                 :         256 :     m_fundamental_table = new JS::WeakCache<FundamentalTable>(rt);
     710                 :         256 :     m_gtype_table = new JS::WeakCache<GTypeTable>(rt);
     711                 :             : 
     712                 :         256 :     m_atoms = new GjsAtoms();
     713                 :             : 
     714         [ -  + ]:         256 :     if (ObjectBox::gtype() == 0)
     715                 :           0 :         g_error("Failed to initialize JSObject GType");
     716                 :             : 
     717                 :             :     JS::RootedObject internal_global(
     718                 :         256 :         m_cx, gjs_create_global_object(cx, GjsGlobalType::INTERNAL));
     719                 :             : 
     720         [ -  + ]:         256 :     if (!internal_global) {
     721                 :           0 :         gjs_log_exception(m_cx);
     722                 :           0 :         g_error("Failed to initialize internal global object");
     723                 :             :     }
     724                 :             : 
     725                 :         256 :     m_internal_global = internal_global;
     726                 :         256 :     Gjs::AutoInternalRealm ar{this};
     727                 :         256 :     JS_AddExtraGCRootsTracer(m_cx, &GjsContextPrivate::trace, this);
     728                 :             : 
     729         [ -  + ]:         256 :     if (!m_atoms->init_atoms(m_cx)) {
     730                 :           0 :         gjs_log_exception(m_cx);
     731                 :           0 :         g_error("Failed to initialize global strings");
     732                 :             :     }
     733                 :             : 
     734         [ -  + ]:         256 :     if (!gjs_define_global_properties(m_cx, internal_global,
     735                 :             :                                       GjsGlobalType::INTERNAL,
     736                 :             :                                       "GJS internal global", "nullptr")) {
     737                 :           0 :         gjs_log_exception(m_cx);
     738                 :           0 :         g_error("Failed to define properties on internal global object");
     739                 :             :     }
     740                 :             : 
     741                 :             :     JS::RootedObject global(
     742                 :         256 :         m_cx,
     743                 :         256 :         gjs_create_global_object(cx, GjsGlobalType::DEFAULT, internal_global));
     744                 :             : 
     745         [ -  + ]:         256 :     if (!global) {
     746                 :           0 :         gjs_log_exception(m_cx);
     747                 :           0 :         g_error("Failed to initialize global object");
     748                 :             :     }
     749                 :             : 
     750                 :         256 :     m_global = global;
     751                 :             : 
     752                 :             :     {
     753                 :         256 :         Gjs::AutoMainRealm ar{this};
     754                 :             : 
     755                 :         256 :         std::vector<std::string> paths;
     756         [ +  + ]:         256 :         if (m_search_path)
     757                 :             :             paths = {m_search_path,
     758                 :         231 :                      m_search_path + g_strv_length(m_search_path)};
     759                 :         256 :         JS::RootedObject importer(m_cx, gjs_create_root_importer(m_cx, paths));
     760         [ -  + ]:         256 :         if (!importer) {
     761                 :           0 :             gjs_log_exception(cx);
     762                 :           0 :             g_error("Failed to create root importer");
     763                 :             :         }
     764                 :             : 
     765                 :         256 :         g_assert(
     766                 :             :             gjs_get_global_slot(global, GjsGlobalSlot::IMPORTS).isUndefined() &&
     767                 :             :             "Someone else already created root importer");
     768                 :             : 
     769                 :         256 :         gjs_set_global_slot(global, GjsGlobalSlot::IMPORTS,
     770                 :         256 :                             JS::ObjectValue(*importer));
     771                 :             : 
     772         [ -  + ]:         256 :         if (!gjs_define_global_properties(m_cx, global, GjsGlobalType::DEFAULT,
     773                 :             :                                           "GJS", "default")) {
     774                 :           0 :             gjs_log_exception(m_cx);
     775                 :           0 :             g_error("Failed to define properties on global object");
     776                 :             :         }
     777                 :         256 :     }
     778                 :             : 
     779                 :         256 :     JS::SetModuleResolveHook(rt, gjs_module_resolve);
     780                 :         256 :     JS::SetModuleDynamicImportHook(rt, gjs_dynamic_module_resolve);
     781                 :         256 :     JS::SetModuleMetadataHook(rt, gjs_populate_module_meta);
     782                 :             : 
     783         [ -  + ]:         256 :     if (!JS_DefineProperty(m_cx, internal_global, "moduleGlobalThis", global,
     784                 :             :                            JSPROP_PERMANENT)) {
     785                 :           0 :         gjs_log_exception(m_cx);
     786                 :           0 :         g_error("Failed to define module global in internal global.");
     787                 :             :     }
     788                 :             : 
     789         [ -  + ]:         256 :     if (!gjs_load_internal_module(cx, "internalLoader")) {
     790                 :           0 :         gjs_log_exception(cx);
     791                 :           0 :         g_error("Failed to load internal module loaders.");
     792                 :             :     }
     793                 :             : 
     794                 :         256 :     load_context_module(cx,
     795                 :             :                         "resource:///org/gnome/gjs/modules/internal/loader.js",
     796                 :             :                         "module loader");
     797                 :             : 
     798                 :             :     {
     799                 :         256 :         Gjs::AutoMainRealm ar{this};
     800                 :         256 :         load_context_module(
     801                 :             :             cx, "resource:///org/gnome/gjs/modules/esm/_bootstrap/default.js",
     802                 :             :             "ESM bootstrap");
     803                 :         256 :     }
     804                 :             : 
     805                 :         256 :     [[maybe_unused]] bool success = m_main_loop.spin(this);
     806                 :         256 :     g_assert(success && "bootstrap should not call system.exit()");
     807                 :             : 
     808                 :         256 :     g_signal_connect_object(
     809                 :             :         m_memory_monitor, "low-memory-warning",
     810         [ #  # ]:           0 :         G_CALLBACK(+[](GjsContext* js_cx, GMemoryMonitorWarningLevel level) {
     811                 :             :             auto* cx =
     812                 :             :                 static_cast<JSContext*>(gjs_context_get_native_context(js_cx));
     813                 :             :             JS::PrepareForFullGC(cx);
     814                 :             :             JS::GCOptions gc_strength = JS::GCOptions::Normal;
     815                 :             :             if (level > G_MEMORY_MONITOR_WARNING_LEVEL_LOW)
     816                 :             :                 gc_strength = JS::GCOptions::Shrink;
     817                 :             :             JS::NonIncrementalGC(cx, gc_strength, Gjs::GCReason::LOW_MEMORY);
     818                 :             :         }),
     819                 :         256 :         m_public_context, G_CONNECT_SWAPPED);
     820                 :             : 
     821                 :         256 :     start_draining_job_queue();
     822                 :         256 : }
     823                 :             : 
     824                 :          62 : void GjsContextPrivate::set_args(std::vector<std::string>&& args) {
     825                 :          62 :     m_args = args;
     826                 :          62 : }
     827                 :             : 
     828                 :          64 : JSObject* GjsContextPrivate::build_args_array() {
     829                 :          64 :     return gjs_build_string_array(m_cx, m_args);
     830                 :             : }
     831                 :             : 
     832                 :             : static void
     833                 :           0 : gjs_context_get_property (GObject     *object,
     834                 :             :                           guint        prop_id,
     835                 :             :                           GValue      *value,
     836                 :             :                           GParamSpec  *pspec)
     837                 :             : {
     838                 :           0 :     GjsContextPrivate* gjs = GjsContextPrivate::from_object(object);
     839                 :             : 
     840   [ #  #  #  # ]:           0 :     switch (prop_id) {
     841                 :           0 :     case PROP_PROGRAM_NAME:
     842                 :           0 :         g_value_set_string(value, gjs->program_name());
     843                 :           0 :         break;
     844                 :           0 :     case PROP_PROGRAM_PATH:
     845                 :           0 :         g_value_set_string(value, gjs->program_path());
     846                 :           0 :         break;
     847                 :           0 :     case PROP_REPL_HISTORY_PATH:
     848                 :           0 :         g_value_set_string(value, gjs->repl_history_path());
     849                 :           0 :         break;
     850                 :           0 :     default:
     851                 :           0 :         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     852                 :           0 :         break;
     853                 :             :     }
     854                 :           0 : }
     855                 :             : 
     856                 :             : static void
     857                 :        1792 : gjs_context_set_property (GObject      *object,
     858                 :             :                           guint         prop_id,
     859                 :             :                           const GValue *value,
     860                 :             :                           GParamSpec   *pspec)
     861                 :             : {
     862                 :        1792 :     GjsContextPrivate* gjs = GjsContextPrivate::from_object(object);
     863                 :             : 
     864   [ +  +  +  +  :        1792 :     switch (prop_id) {
             +  +  +  - ]
     865                 :         256 :     case PROP_SEARCH_PATH:
     866                 :         256 :         gjs->set_search_path(static_cast<char**>(g_value_dup_boxed(value)));
     867                 :         256 :         break;
     868                 :         256 :     case PROP_PROGRAM_NAME:
     869                 :         256 :         gjs->set_program_name(g_value_dup_string(value));
     870                 :         256 :         break;
     871                 :         256 :     case PROP_PROGRAM_PATH:
     872                 :         256 :         gjs->set_program_path(g_value_dup_string(value));
     873                 :         256 :         break;
     874                 :         256 :     case PROP_PROFILER_ENABLED:
     875                 :         256 :         gjs->set_should_profile(g_value_get_boolean(value));
     876                 :         256 :         break;
     877                 :         256 :     case PROP_PROFILER_SIGUSR2:
     878                 :         256 :         gjs->set_should_listen_sigusr2(g_value_get_boolean(value));
     879                 :         256 :         break;
     880                 :         256 :     case PROP_EXEC_AS_MODULE:
     881                 :         256 :         gjs->set_execute_as_module(g_value_get_boolean(value));
     882                 :         256 :         break;
     883                 :         256 :     case PROP_REPL_HISTORY_PATH:
     884                 :         256 :         gjs->set_repl_history_path(g_value_dup_string(value));
     885                 :         256 :         break;
     886                 :           0 :     default:
     887                 :           0 :         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     888                 :           0 :         break;
     889                 :             :     }
     890                 :        1792 : }
     891                 :             : 
     892                 :             : 
     893                 :             : GjsContext*
     894                 :         120 : gjs_context_new(void)
     895                 :             : {
     896                 :         120 :     return (GjsContext*) g_object_new (GJS_TYPE_CONTEXT, NULL);
     897                 :             : }
     898                 :             : 
     899                 :             : GjsContext*
     900                 :          74 : gjs_context_new_with_search_path(char** search_path)
     901                 :             : {
     902                 :          74 :     return (GjsContext*) g_object_new (GJS_TYPE_CONTEXT,
     903                 :             :                          "search-path", search_path,
     904                 :          74 :                          NULL);
     905                 :             : }
     906                 :             : 
     907                 :           0 : gboolean GjsContextPrivate::trigger_gc_if_needed(void* data) {
     908                 :           0 :     auto* gjs = static_cast<GjsContextPrivate*>(data);
     909                 :           0 :     gjs->m_auto_gc_id = 0;
     910                 :             : 
     911         [ #  # ]:           0 :     if (gjs->m_force_gc) {
     912                 :             :         gjs_debug_lifecycle(GJS_DEBUG_CONTEXT, "Big Hammer hit");
     913                 :           0 :         JS_GC(gjs->m_cx, Gjs::GCReason::BIG_HAMMER);
     914                 :             :     } else {
     915                 :           0 :         gjs_gc_if_needed(gjs->m_cx);
     916                 :             :     }
     917                 :           0 :     gjs->m_force_gc = false;
     918                 :             : 
     919                 :           0 :     return G_SOURCE_REMOVE;
     920                 :             : }
     921                 :             : 
     922                 :       15581 : void GjsContextPrivate::schedule_gc_internal(bool force_gc) {
     923                 :       15581 :     m_force_gc |= force_gc;
     924                 :             : 
     925         [ +  + ]:       15581 :     if (m_auto_gc_id > 0)
     926                 :       15438 :         return;
     927                 :             : 
     928                 :             :     if (force_gc)
     929                 :             :         gjs_debug_lifecycle(GJS_DEBUG_CONTEXT, "Big Hammer scheduled");
     930                 :             : 
     931                 :         143 :     m_auto_gc_id = g_timeout_add_seconds_full(G_PRIORITY_LOW, 10,
     932                 :             :                                               trigger_gc_if_needed, this,
     933                 :             :                                               nullptr);
     934                 :             : 
     935         [ -  + ]:         143 :     if (force_gc)
     936                 :           0 :         g_source_set_name_by_id(m_auto_gc_id, "[gjs] Garbage Collection (Big Hammer)");
     937                 :             :     else
     938                 :         143 :         g_source_set_name_by_id(m_auto_gc_id, "[gjs] Garbage Collection");
     939                 :             : }
     940                 :             : 
     941                 :             : /*
     942                 :             :  * GjsContextPrivate::schedule_gc_if_needed:
     943                 :             :  *
     944                 :             :  * Does a minor GC immediately if the JS engine decides one is needed, but also
     945                 :             :  * schedules a full GC in the next idle time.
     946                 :             :  */
     947                 :       14085 : void GjsContextPrivate::schedule_gc_if_needed(void) {
     948                 :             :     // We call JS_MaybeGC immediately, but defer a check for a full GC cycle
     949                 :             :     // to an idle handler.
     950                 :       14085 :     JS_MaybeGC(m_cx);
     951                 :             : 
     952                 :       14085 :     schedule_gc_internal(false);
     953                 :       14085 : }
     954                 :             : 
     955                 :        1010 : void GjsContextPrivate::on_garbage_collection(JSGCStatus status, JS::GCReason reason) {
     956         [ -  + ]:        1010 :     if (m_profiler)
     957                 :           0 :         _gjs_profiler_set_gc_status(m_profiler, status, reason);
     958                 :             : 
     959      [ +  +  - ]:        1010 :     switch (status) {
     960                 :         505 :         case JSGC_BEGIN:
     961                 :             :             gjs_debug_lifecycle(GJS_DEBUG_CONTEXT,
     962                 :             :                                 "Begin garbage collection because of %s",
     963                 :             :                                 gjs_explain_gc_reason(reason));
     964                 :             : 
     965                 :             :             // We finalize any pending toggle refs before doing any garbage
     966                 :             :             // collection, so that we can collect the JS wrapper objects, and in
     967                 :             :             // order to minimize the chances of objects having a pending toggle
     968                 :             :             // up queued when they are garbage collected.
     969                 :         505 :             gjs_object_clear_toggles();
     970                 :             : 
     971                 :         505 :             m_async_closures.clear();
     972                 :         505 :             m_async_closures.shrink_to_fit();
     973                 :         505 :             break;
     974                 :         505 :         case JSGC_END:
     975                 :         505 :             m_destroy_notifications.shrink_to_fit();
     976                 :             :             gjs_debug_lifecycle(GJS_DEBUG_CONTEXT, "End garbage collection");
     977                 :         505 :             break;
     978                 :           0 :         default:
     979                 :             :             g_assert_not_reached();
     980                 :             :     }
     981                 :        1010 : }
     982                 :             : 
     983                 :          34 : void GjsContextPrivate::exit(uint8_t exit_code) {
     984                 :          34 :     g_assert(!m_should_exit);
     985                 :          34 :     m_should_exit = true;
     986                 :          34 :     m_exit_code = exit_code;
     987                 :          34 : }
     988                 :             : 
     989                 :        1833 : bool GjsContextPrivate::should_exit(uint8_t* exit_code_p) const {
     990         [ +  + ]:        1833 :     if (exit_code_p != NULL)
     991                 :         376 :         *exit_code_p = m_exit_code;
     992                 :        1833 :     return m_should_exit;
     993                 :             : }
     994                 :             : 
     995                 :           2 : void GjsContextPrivate::exit_immediately(uint8_t exit_code) {
     996                 :           2 :     warn_about_unhandled_promise_rejections();
     997                 :             : 
     998                 :           2 :     ::exit(exit_code);
     999                 :             : }
    1000                 :             : 
    1001                 :        3324 : void GjsContextPrivate::start_draining_job_queue(void) { m_dispatcher.start(); }
    1002                 :             : 
    1003                 :        3322 : void GjsContextPrivate::stop_draining_job_queue(void) {
    1004                 :        3322 :     m_draining_job_queue = false;
    1005                 :        3322 :     m_dispatcher.stop();
    1006                 :        3322 : }
    1007                 :             : 
    1008                 :        1296 : JSObject* GjsContextPrivate::getIncumbentGlobal(JSContext* cx) {
    1009                 :             :     // This is equivalent to SpiderMonkey's behavior.
    1010                 :        1296 :     return JS::CurrentGlobalOrNull(cx);
    1011                 :             : }
    1012                 :             : 
    1013                 :             : // See engine.cpp and JS::SetJobQueue().
    1014                 :        1290 : bool GjsContextPrivate::enqueuePromiseJob(JSContext* cx [[maybe_unused]],
    1015                 :             :                                           JS::HandleObject promise,
    1016                 :             :                                           JS::HandleObject job,
    1017                 :             :                                           JS::HandleObject allocation_site,
    1018                 :             :                                           JS::HandleObject incumbent_global
    1019                 :             :                                           [[maybe_unused]]) {
    1020                 :        1290 :     g_assert(cx == m_cx);
    1021                 :        1290 :     g_assert(from_cx(cx) == this);
    1022                 :             : 
    1023                 :        3870 :     gjs_debug(GJS_DEBUG_MAINLOOP,
    1024                 :             :               "Enqueue job %s, promise=%s, allocation site=%s",
    1025                 :        3870 :               gjs_debug_object(job).c_str(), gjs_debug_object(promise).c_str(),
    1026                 :        2580 :               gjs_debug_object(allocation_site).c_str());
    1027                 :             : 
    1028         [ -  + ]:        1290 :     if (!m_job_queue.append(job)) {
    1029                 :           0 :         JS_ReportOutOfMemory(m_cx);
    1030                 :           0 :         return false;
    1031                 :             :     }
    1032                 :             : 
    1033                 :        1290 :     JS::JobQueueMayNotBeEmpty(m_cx);
    1034                 :        1290 :     m_dispatcher.start();
    1035                 :        1290 :     return true;
    1036                 :             : }
    1037                 :             : 
    1038                 :             : // Override of JobQueue::runJobs(). Called by js::RunJobs(), and when execution
    1039                 :             : // of the job queue was interrupted by the debugger and is resuming.
    1040                 :        9832 : void GjsContextPrivate::runJobs(JSContext* cx) {
    1041                 :        9832 :     g_assert(cx == m_cx);
    1042                 :        9832 :     g_assert(from_cx(cx) == this);
    1043         [ +  + ]:        9832 :     if (!run_jobs_fallible())
    1044                 :           3 :         gjs_log_exception(cx);
    1045                 :        9832 : }
    1046                 :             : 
    1047                 :             : /*
    1048                 :             :  * GjsContext::run_jobs_fallible:
    1049                 :             :  *
    1050                 :             :  * Drains the queue of promise callbacks that the JS engine has reported
    1051                 :             :  * finished, calling each one and logging any exceptions that it throws.
    1052                 :             :  *
    1053                 :             :  * Adapted from js::RunJobs() in SpiderMonkey's default job queue
    1054                 :             :  * implementation.
    1055                 :             :  *
    1056                 :             :  * Returns: false if one of the jobs threw an uncatchable exception;
    1057                 :             :  * otherwise true.
    1058                 :             :  */
    1059                 :        9861 : bool GjsContextPrivate::run_jobs_fallible() {
    1060                 :        9861 :     bool retval = true;
    1061                 :             : 
    1062   [ +  -  +  + ]:        9861 :     if (m_draining_job_queue || m_should_exit)
    1063                 :          34 :         return true;
    1064                 :             : 
    1065                 :        9827 :     m_draining_job_queue = true;  // Ignore reentrant calls
    1066                 :             : 
    1067                 :        9827 :     JS::RootedObject job(m_cx);
    1068                 :        9827 :     JS::HandleValueArray args(JS::HandleValueArray::empty());
    1069                 :        9827 :     JS::RootedValue rval(m_cx);
    1070                 :             : 
    1071         [ +  + ]:        9827 :     if (m_job_queue.length() == 0) {
    1072                 :             :         // Check FinalizationRegistry cleanup tasks at least once if there are
    1073                 :             :         // no microtasks queued. This may enqueue more microtasks, which will be
    1074                 :             :         // appended to m_job_queue.
    1075         [ -  + ]:        9306 :         if (!run_finalization_registry_cleanup())
    1076                 :           0 :             retval = false;
    1077                 :             :     }
    1078                 :             : 
    1079                 :             :     /* Execute jobs in a loop until we've reached the end of the queue.
    1080                 :             :      * Since executing a job can trigger enqueueing of additional jobs,
    1081                 :             :      * it's crucial to recheck the queue length during each iteration. */
    1082         [ +  + ]:       11112 :     for (size_t ix = 0; ix < m_job_queue.length(); ix++) {
    1083                 :             :         /* A previous job might have set this flag. e.g., System.exit(). */
    1084   [ +  +  -  +  :        1286 :         if (m_should_exit || !m_dispatcher.is_running()) {
                   +  + ]
    1085                 :           1 :             gjs_debug(GJS_DEBUG_MAINLOOP, "Stopping jobs because of %s",
    1086         [ +  - ]:           1 :                       m_should_exit ? "exit" : "main loop cancel");
    1087                 :           1 :             break;
    1088                 :             :         }
    1089                 :             : 
    1090                 :        1285 :         job = m_job_queue[ix];
    1091                 :             : 
    1092                 :             :         /* It's possible that job draining was interrupted prematurely,
    1093                 :             :          * leaving the queue partly processed. In that case, slots for
    1094                 :             :          * already-executed entries will contain nullptrs, which we should
    1095                 :             :          * just skip. */
    1096         [ -  + ]:        1285 :         if (!job)
    1097                 :           0 :             continue;
    1098                 :             : 
    1099                 :        1285 :         m_job_queue[ix] = nullptr;
    1100                 :             :         {
    1101                 :        1285 :             JSAutoRealm ar(m_cx, job);
    1102                 :        1285 :             gjs_debug(GJS_DEBUG_MAINLOOP, "handling job %zu, %s", ix,
    1103                 :        2570 :                       gjs_debug_object(job).c_str());
    1104         [ +  + ]:        1285 :             if (!JS::Call(m_cx, JS::UndefinedHandleValue, job, args, &rval)) {
    1105                 :             :                 /* Uncatchable exception - return false so that
    1106                 :             :                  * System.exit() works in the interactive shell and when
    1107                 :             :                  * exiting the interpreter. */
    1108         [ +  - ]:           3 :                 if (!JS_IsExceptionPending(m_cx)) {
    1109                 :             :                     /* System.exit() is an uncatchable exception, but does not
    1110                 :             :                      * indicate a bug. Log everything else. */
    1111         [ -  + ]:           3 :                     if (!should_exit(nullptr))
    1112                 :           0 :                         g_critical("Promise callback terminated with uncatchable exception");
    1113                 :           3 :                     retval = false;
    1114                 :           3 :                     continue;
    1115                 :             :                 }
    1116                 :             : 
    1117                 :             :                 /* There's nowhere for the exception to go at this point */
    1118                 :           0 :                 gjs_log_exception_uncaught(m_cx);
    1119                 :             :             }
    1120         [ +  + ]:        1285 :         }
    1121                 :        1282 :         gjs_debug(GJS_DEBUG_MAINLOOP, "Completed job %zu", ix);
    1122                 :             : 
    1123                 :             :         // Run FinalizationRegistry cleanup tasks after each job. Cleanup tasks
    1124                 :             :         // may enqueue more microtasks, which will be appended to m_job_queue.
    1125         [ -  + ]:        1282 :         if (!run_finalization_registry_cleanup())
    1126                 :           0 :             retval = false;
    1127                 :             :     }
    1128                 :             : 
    1129                 :        9827 :     m_draining_job_queue = false;
    1130                 :        9827 :     m_job_queue.clear();
    1131                 :        9827 :     warn_about_unhandled_promise_rejections();
    1132                 :        9827 :     JS::JobQueueIsEmpty(m_cx);
    1133                 :        9827 :     return retval;
    1134                 :        9827 : }
    1135                 :             : 
    1136                 :       10588 : bool GjsContextPrivate::run_finalization_registry_cleanup() {
    1137                 :       10588 :     bool retval = true;
    1138                 :             : 
    1139                 :       10588 :     JS::Rooted<FunctionVector> tasks{m_cx};
    1140                 :       10588 :     std::swap(tasks.get(), m_cleanup_tasks);
    1141                 :       10588 :     g_assert(m_cleanup_tasks.empty());
    1142                 :             : 
    1143                 :       10588 :     JS::RootedFunction task{m_cx};
    1144                 :       10588 :     JS::RootedValue unused_rval{m_cx};
    1145         [ +  + ]:       10593 :     for (JSFunction* func : tasks) {
    1146                 :           5 :         gjs_debug(GJS_DEBUG_MAINLOOP,
    1147                 :             :                   "Running FinalizationRegistry cleanup callback");
    1148                 :             : 
    1149                 :           5 :         task.set(func);
    1150                 :           5 :         JS::ExposeObjectToActiveJS(JS_GetFunctionObject(func));
    1151                 :             : 
    1152                 :           5 :         JSAutoRealm ar{m_cx, JS_GetFunctionObject(func)};
    1153         [ -  + ]:           5 :         if (!JS_CallFunction(m_cx, nullptr, task, JS::HandleValueArray::empty(),
    1154                 :             :                              &unused_rval)) {
    1155                 :             :             // Same logic as above
    1156         [ #  # ]:           0 :             if (!JS_IsExceptionPending(m_cx)) {
    1157         [ #  # ]:           0 :                 if (!should_exit(nullptr))
    1158                 :           0 :                     g_critical(
    1159                 :             :                         "FinalizationRegistry callback terminated with "
    1160                 :             :                         "uncatchable exception");
    1161                 :           0 :                 retval = false;
    1162                 :           0 :                 continue;
    1163                 :             :             }
    1164                 :           0 :             gjs_log_exception_uncaught(m_cx);
    1165                 :             :         }
    1166                 :             : 
    1167                 :           5 :         gjs_debug(GJS_DEBUG_MAINLOOP,
    1168                 :             :                   "Completed FinalizationRegistry cleanup callback");
    1169         [ +  - ]:           5 :     }
    1170                 :             : 
    1171                 :       21176 :     return retval;
    1172                 :       10588 : }
    1173                 :             : 
    1174                 :             : class GjsContextPrivate::SavedQueue : public JS::JobQueue::SavedJobQueue {
    1175                 :             :  private:
    1176                 :             :     GjsContextPrivate* m_gjs;
    1177                 :             :     JS::PersistentRooted<JobQueueStorage> m_queue;
    1178                 :             :     bool m_was_draining : 1;
    1179                 :             : 
    1180                 :             :  public:
    1181                 :        3068 :     explicit SavedQueue(GjsContextPrivate* gjs)
    1182                 :        6136 :         : m_gjs(gjs),
    1183                 :        3068 :           m_queue(gjs->m_cx, std::move(gjs->m_job_queue)),
    1184                 :        3068 :           m_was_draining(gjs->m_draining_job_queue) {
    1185                 :        3068 :         gjs_debug(GJS_DEBUG_CONTEXT, "Pausing job queue");
    1186                 :        3068 :         gjs->stop_draining_job_queue();
    1187                 :        3068 :     }
    1188                 :             : 
    1189                 :        3068 :     ~SavedQueue(void) {
    1190                 :        3068 :         gjs_debug(GJS_DEBUG_CONTEXT, "Unpausing job queue");
    1191                 :        3068 :         m_gjs->m_job_queue = std::move(m_queue.get());
    1192                 :        3068 :         m_gjs->m_draining_job_queue = m_was_draining;
    1193                 :        3068 :         m_gjs->start_draining_job_queue();
    1194                 :        3068 :     }
    1195                 :             : };
    1196                 :             : 
    1197                 :        3068 : js::UniquePtr<JS::JobQueue::SavedJobQueue> GjsContextPrivate::saveJobQueue(
    1198                 :             :     JSContext* cx) {
    1199                 :        3068 :     g_assert(cx == m_cx);
    1200                 :        3068 :     g_assert(from_cx(cx) == this);
    1201                 :             : 
    1202                 :        3068 :     auto saved_queue = js::MakeUnique<SavedQueue>(this);
    1203         [ -  + ]:        3068 :     if (!saved_queue) {
    1204                 :           0 :         JS_ReportOutOfMemory(cx);
    1205                 :           0 :         return nullptr;
    1206                 :             :     }
    1207                 :             : 
    1208                 :        3068 :     g_assert(m_job_queue.empty());
    1209                 :        3068 :     return saved_queue;
    1210                 :        3068 : }
    1211                 :             : 
    1212                 :          18 : void GjsContextPrivate::register_unhandled_promise_rejection(
    1213                 :             :     uint64_t id, JS::UniqueChars&& stack) {
    1214                 :          18 :     m_unhandled_rejection_stacks[id] = std::move(stack);
    1215                 :          18 : }
    1216                 :             : 
    1217                 :          17 : void GjsContextPrivate::unregister_unhandled_promise_rejection(uint64_t id) {
    1218                 :          17 :     size_t erased = m_unhandled_rejection_stacks.erase(id);
    1219         [ -  + ]:          17 :     if (erased != 1) {
    1220                 :           0 :         g_critical("Promise %" G_GUINT64_FORMAT
    1221                 :             :                    " Handler attached to rejected promise that wasn't "
    1222                 :             :                    "previously marked as unhandled or that we wrongly reported "
    1223                 :             :                    "as unhandled",
    1224                 :             :                    id);
    1225                 :             :     }
    1226                 :          17 : }
    1227                 :             : 
    1228                 :           5 : bool GjsContextPrivate::queue_finalization_registry_cleanup(
    1229                 :             :     JSFunction* cleanup_task) {
    1230                 :           5 :     return m_cleanup_tasks.append(cleanup_task);
    1231                 :             : }
    1232                 :             : 
    1233                 :         118 : void GjsContextPrivate::async_closure_enqueue_for_gc(Gjs::Closure* trampoline) {
    1234                 :             :     //  Because we can't free the mmap'd data for a callback
    1235                 :             :     //  while it's in use, this list keeps track of ones that
    1236                 :             :     //  will be freed the next time gc happens
    1237                 :         118 :     g_assert(!trampoline->context() || trampoline->context() == m_cx);
    1238                 :         118 :     m_async_closures.emplace_back(trampoline);
    1239                 :         118 : }
    1240                 :             : 
    1241                 :             : /**
    1242                 :             :  * gjs_context_maybe_gc:
    1243                 :             :  * @context: a #GjsContext
    1244                 :             :  *
    1245                 :             :  * Similar to the Spidermonkey JS_MaybeGC() call which
    1246                 :             :  * heuristically looks at JS runtime memory usage and
    1247                 :             :  * may initiate a garbage collection.
    1248                 :             :  *
    1249                 :             :  * This function always unconditionally invokes JS_MaybeGC(), but
    1250                 :             :  * additionally looks at memory usage from the system malloc()
    1251                 :             :  * when available, and if the delta has grown since the last run
    1252                 :             :  * significantly, also initiates a full JavaScript garbage
    1253                 :             :  * collection.  The idea is that since GJS is a bridge between
    1254                 :             :  * JavaScript and system libraries, and JS objects act as proxies
    1255                 :             :  * for these system memory objects, GJS consumers need a way to
    1256                 :             :  * hint to the runtime that it may be a good idea to try a
    1257                 :             :  * collection.
    1258                 :             :  *
    1259                 :             :  * A good time to call this function is when your application
    1260                 :             :  * transitions to an idle state.
    1261                 :             :  */
    1262                 :             : void
    1263                 :           0 : gjs_context_maybe_gc (GjsContext  *context)
    1264                 :             : {
    1265                 :           0 :     GjsContextPrivate* gjs = GjsContextPrivate::from_object(context);
    1266                 :           0 :     gjs_maybe_gc(gjs->context());
    1267                 :           0 : }
    1268                 :             : 
    1269                 :             : /**
    1270                 :             :  * gjs_context_gc:
    1271                 :             :  * @context: a #GjsContext
    1272                 :             :  *
    1273                 :             :  * Initiate a full GC; may or may not block until complete.  This
    1274                 :             :  * function just calls Spidermonkey JS_GC().
    1275                 :             :  */
    1276                 :             : void
    1277                 :           0 : gjs_context_gc (GjsContext  *context)
    1278                 :             : {
    1279                 :           0 :     GjsContextPrivate* gjs = GjsContextPrivate::from_object(context);
    1280                 :           0 :     JS_GC(gjs->context(), Gjs::GCReason::GJS_API_CALL);
    1281                 :           0 : }
    1282                 :             : 
    1283                 :             : /**
    1284                 :             :  * gjs_context_get_all:
    1285                 :             :  *
    1286                 :             :  * Returns a newly-allocated list containing all known instances of #GjsContext.
    1287                 :             :  * This is useful for operating on the contexts from a process-global situation
    1288                 :             :  * such as a debugger.
    1289                 :             :  *
    1290                 :             :  * Return value: (element-type GjsContext) (transfer full): Known #GjsContext instances
    1291                 :             :  */
    1292                 :             : GList*
    1293                 :          44 : gjs_context_get_all(void)
    1294                 :             : {
    1295                 :             :   GList *result;
    1296                 :             :   GList *iter;
    1297                 :          44 :   g_mutex_lock (&contexts_lock);
    1298                 :          44 :   result = g_list_copy(all_contexts);
    1299         [ +  + ]:          88 :   for (iter = result; iter; iter = iter->next)
    1300                 :          44 :     g_object_ref((GObject*)iter->data);
    1301                 :          44 :   g_mutex_unlock (&contexts_lock);
    1302                 :          44 :   return result;
    1303                 :             : }
    1304                 :             : 
    1305                 :             : /**
    1306                 :             :  * gjs_context_get_native_context:
    1307                 :             :  *
    1308                 :             :  * Returns a pointer to the underlying native context.  For SpiderMonkey, this
    1309                 :             :  * is a JSContext *
    1310                 :             :  */
    1311                 :             : void*
    1312                 :        1561 : gjs_context_get_native_context (GjsContext *js_context)
    1313                 :             : {
    1314                 :        1561 :     g_return_val_if_fail(GJS_IS_CONTEXT(js_context), NULL);
    1315                 :        1561 :     GjsContextPrivate* gjs = GjsContextPrivate::from_object(js_context);
    1316                 :        1561 :     return gjs->context();
    1317                 :             : }
    1318                 :             : 
    1319                 :         505 : static inline bool result_to_c(GErrorResult<> result, GError** error_out) {
    1320         [ +  + ]:         505 :     if (result.isOk())
    1321                 :         461 :         return true;
    1322                 :          44 :     *error_out = result.unwrapErr().release();
    1323                 :          44 :     return false;
    1324                 :             : }
    1325                 :             : 
    1326                 :             : bool
    1327                 :         255 : gjs_context_eval(GjsContext   *js_context,
    1328                 :             :                  const char   *script,
    1329                 :             :                  gssize        script_len,
    1330                 :             :                  const char   *filename,
    1331                 :             :                  int          *exit_status_p,
    1332                 :             :                  GError      **error)
    1333                 :             : {
    1334                 :         255 :     g_return_val_if_fail(GJS_IS_CONTEXT(js_context), false);
    1335                 :             : 
    1336         [ +  + ]:         255 :     size_t real_len = script_len < 0 ? strlen(script) : script_len;
    1337                 :             : 
    1338                 :         255 :     Gjs::AutoUnref<GjsContext> js_context_ref{js_context, Gjs::TakeOwnership{}};
    1339                 :         255 :     GjsContextPrivate* gjs = GjsContextPrivate::from_object(js_context);
    1340                 :             : 
    1341                 :         255 :     gjs->register_non_module_sourcemap(script, filename);
    1342                 :         509 :     return result_to_c(gjs->eval(script, real_len, filename, exit_status_p),
    1343                 :         254 :                        error);
    1344                 :         254 : }
    1345                 :             : 
    1346                 :         125 : bool gjs_context_eval_module(GjsContext* js_context, const char* identifier,
    1347                 :             :                              uint8_t* exit_code, GError** error) {
    1348                 :         125 :     g_return_val_if_fail(GJS_IS_CONTEXT(js_context), false);
    1349                 :             : 
    1350                 :         125 :     Gjs::AutoUnref<GjsContext> js_context_ref{js_context, Gjs::TakeOwnership{}};
    1351                 :             : 
    1352                 :         125 :     GjsContextPrivate* gjs = GjsContextPrivate::from_object(js_context);
    1353                 :         125 :     return result_to_c(gjs->eval_module(identifier, exit_code), error);
    1354                 :         124 : }
    1355                 :             : 
    1356                 :         127 : bool gjs_context_register_module(GjsContext* js_context, const char* identifier,
    1357                 :             :                                  const char* uri, GError** error) {
    1358                 :         127 :     g_return_val_if_fail(GJS_IS_CONTEXT(js_context), false);
    1359                 :             : 
    1360                 :         127 :     GjsContextPrivate* gjs = GjsContextPrivate::from_object(js_context);
    1361                 :             : 
    1362                 :         127 :     return result_to_c(gjs->register_module(identifier, uri), error);
    1363                 :             : }
    1364                 :             : 
    1365                 :         380 : bool GjsContextPrivate::auto_profile_enter() {
    1366                 :         380 :     bool auto_profile = m_should_profile;
    1367   [ +  +  +  +  :         488 :     if (auto_profile &&
                   +  + ]
    1368         [ -  + ]:         108 :         (_gjs_profiler_is_running(m_profiler) || m_should_listen_sigusr2))
    1369                 :         100 :         auto_profile = false;
    1370                 :             : 
    1371                 :         380 :     Gjs::AutoMainRealm ar{this};
    1372                 :             : 
    1373         [ +  + ]:         380 :     if (auto_profile)
    1374                 :           4 :         gjs_profiler_start(m_profiler);
    1375                 :             : 
    1376                 :         760 :     return auto_profile;
    1377                 :         380 : }
    1378                 :             : 
    1379                 :         374 : void GjsContextPrivate::auto_profile_exit(bool auto_profile) {
    1380         [ +  + ]:         374 :     if (auto_profile)
    1381                 :           4 :         gjs_profiler_stop(m_profiler);
    1382                 :         374 : }
    1383                 :             : 
    1384                 :         374 : GErrorResult<> GjsContextPrivate::handle_exit_code(bool no_sync_error_pending,
    1385                 :             :                                                    const char* source_type,
    1386                 :             :                                                    const char* identifier,
    1387                 :             :                                                    uint8_t* exit_code) {
    1388                 :             :     uint8_t code;
    1389         [ +  + ]:         374 :     if (should_exit(&code)) {
    1390                 :             :         /* exit_status_p is public API so can't be changed, but should be
    1391                 :             :          * uint8_t, not int */
    1392                 :          32 :         Gjs::AutoError error;
    1393                 :          32 :         g_set_error(error.out(), GJS_ERROR, GJS_ERROR_SYSTEM_EXIT,
    1394                 :             :                     "Exit with code %d", code);
    1395                 :             : 
    1396                 :          32 :         *exit_code = code;
    1397                 :          32 :         return Err(error.release());  // Don't log anything
    1398                 :          32 :     }
    1399                 :             : 
    1400                 :             :     // Once the main loop exits an exception could
    1401                 :             :     // be pending even if the script returned
    1402                 :             :     // true synchronously
    1403         [ +  + ]:         342 :     if (JS_IsExceptionPending(m_cx)) {
    1404                 :           4 :         Gjs::AutoError error;
    1405                 :           4 :         g_set_error(error.out(), GJS_ERROR, GJS_ERROR_FAILED,
    1406                 :             :                     "%s %s threw an exception", source_type, identifier);
    1407                 :           4 :         gjs_log_exception_uncaught(m_cx);
    1408                 :             : 
    1409                 :           4 :         *exit_code = 1;
    1410                 :           4 :         return Err(error.release());
    1411                 :           4 :     }
    1412                 :             : 
    1413         [ +  + ]:         338 :     if (m_unhandled_exception) {
    1414                 :           3 :         Gjs::AutoError error;
    1415                 :           3 :         g_set_error(error.out(), GJS_ERROR, GJS_ERROR_FAILED,
    1416                 :             :                     "%s %s threw an exception", source_type, identifier);
    1417                 :           3 :         *exit_code = 1;
    1418                 :           3 :         return Err(error.release());
    1419                 :           3 :     }
    1420                 :             : 
    1421                 :             :     // Assume success if no error was thrown and should exit isn't
    1422                 :             :     // set
    1423         [ +  - ]:         335 :     if (no_sync_error_pending) {
    1424                 :         335 :         *exit_code = 0;
    1425                 :         335 :         return Ok{};
    1426                 :             :     }
    1427                 :             : 
    1428                 :           0 :     g_critical("%s %s terminated with an uncatchable exception", source_type,
    1429                 :             :                identifier);
    1430                 :           0 :     Gjs::AutoError error;
    1431                 :           0 :     g_set_error(error.out(), GJS_ERROR, GJS_ERROR_FAILED,
    1432                 :             :                 "%s %s terminated with an uncatchable exception", source_type,
    1433                 :             :                 identifier);
    1434                 :             : 
    1435                 :           0 :     gjs_log_exception_uncaught(m_cx);
    1436                 :             :     /* No exit code from script, but we don't want to exit(0) */
    1437                 :           0 :     *exit_code = 1;
    1438                 :           0 :     return Err(error.release());
    1439                 :           0 : }
    1440                 :             : 
    1441                 :          50 : bool GjsContextPrivate::set_main_loop_hook(JSObject* callable) {
    1442                 :          50 :     g_assert(JS::IsCallable(callable) && "main loop hook must be a callable object");
    1443                 :             : 
    1444         [ -  + ]:          50 :     if (!callable) {
    1445                 :           0 :         m_main_loop_hook = nullptr;
    1446                 :           0 :         return true;
    1447                 :             :     }
    1448                 :             : 
    1449         [ -  + ]:          50 :     if (m_main_loop_hook)
    1450                 :           0 :         return false;
    1451                 :             : 
    1452                 :          50 :     m_main_loop_hook = callable;
    1453                 :          50 :     return true;
    1454                 :             : }
    1455                 :             : 
    1456                 :          50 : bool GjsContextPrivate::run_main_loop_hook() {
    1457                 :          50 :     JS::RootedObject hook(m_cx, m_main_loop_hook.get());
    1458                 :          50 :     m_main_loop_hook = nullptr;
    1459                 :          50 :     gjs_debug(GJS_DEBUG_MAINLOOP, "Running and clearing main loop hook");
    1460                 :          50 :     JS::RootedValue ignored_rval(m_cx);
    1461                 :         100 :     return JS::Call(m_cx, JS::NullHandleValue, hook,
    1462                 :         100 :                     JS::HandleValueArray::empty(), &ignored_rval);
    1463                 :          50 : }
    1464                 :             : 
    1465                 :             : // source maps parsing is built upon hooks for resolving or loading modules
    1466                 :             : // for non-module runs we need to invoke this logic manually
    1467                 :         255 : void GjsContextPrivate::register_non_module_sourcemap(const char* script,
    1468                 :             :                                                       const char* filename) {
    1469                 :       20645 :     using AutoURI = Gjs::AutoPointer<GUri, GUri, g_uri_unref>;
    1470                 :         255 :     Gjs::AutoMainRealm ar{this};
    1471                 :             : 
    1472                 :         255 :     JS::RootedObject global{m_cx, JS::CurrentGlobalOrNull(m_cx)};
    1473                 :             :     JS::RootedValue v_loader{
    1474                 :         255 :         m_cx, gjs_get_global_slot(global, GjsGlobalSlot::MODULE_LOADER)};
    1475                 :         255 :     g_assert(v_loader.isObject());
    1476                 :         255 :     JS::RootedObject v_loader_obj{m_cx, &v_loader.toObject()};
    1477                 :             : 
    1478                 :         255 :     JS::RootedValueArray<3> args{m_cx};
    1479                 :         255 :     JS::RootedString script_str{m_cx, JS_NewStringCopyZ(m_cx, script)};
    1480                 :         255 :     JS::RootedString file_name_str{m_cx, JS_NewStringCopyZ(m_cx, filename)};
    1481                 :         255 :     args[0].setString(script_str);
    1482                 :         255 :     args[1].setString(file_name_str);
    1483                 :             : 
    1484                 :             :     // if the file uri is not an absolute uri with a scheme, build one assuming
    1485                 :             :     // file:// this parameter is used to help locate non-inlined source map file
    1486                 :         255 :     AutoURI uri = g_uri_parse(filename, G_URI_FLAGS_NONE, nullptr);
    1487         [ +  + ]:         255 :     if (!uri) {
    1488                 :         250 :         Gjs::AutoUnref<GFile> file = g_file_new_for_path(filename);
    1489                 :         250 :         Gjs::AutoChar file_uri = g_file_get_uri(file);
    1490                 :             :         JS::RootedString abs_filename_scheme_str{
    1491                 :         250 :             m_cx, JS_NewStringCopyZ(m_cx, file_uri.get())};
    1492                 :         250 :         args[2].setString(abs_filename_scheme_str);
    1493                 :         250 :     }
    1494                 :             : 
    1495                 :         255 :     JS::RootedValue ignored{m_cx};
    1496                 :         255 :     JS::Call(m_cx, v_loader_obj, "populateSourceMap", args, &ignored);
    1497                 :         255 : }
    1498                 :             : 
    1499                 :         255 : GErrorResult<> GjsContextPrivate::eval(const char* script, size_t script_len,
    1500                 :             :                                        const char* filename,
    1501                 :             :                                        int* exit_status_p) {
    1502                 :         255 :     AutoResetExit reset(this);
    1503                 :             : 
    1504                 :         255 :     bool auto_profile = auto_profile_enter();
    1505                 :             : 
    1506                 :         255 :     Gjs::AutoMainRealm ar{this};
    1507                 :             : 
    1508                 :         255 :     JS::RootedValue retval(m_cx);
    1509                 :         255 :     bool ok = eval_with_scope(nullptr, script, script_len, filename, &retval);
    1510                 :             : 
    1511                 :             :     // If there are no errors and the mainloop hook is set, call it.
    1512   [ +  +  -  +  :         254 :     if (ok && m_main_loop_hook)
                   -  + ]
    1513                 :           0 :         ok = run_main_loop_hook();
    1514                 :             : 
    1515                 :         254 :     bool exiting = false;
    1516                 :             : 
    1517                 :             :     // Spin the internal loop until the main loop hook is set or no holds
    1518                 :             :     // remain.
    1519                 :             :     // If the main loop returns false we cannot guarantee the state of our
    1520                 :             :     // promise queue (a module promise could be pending) so instead of draining
    1521                 :             :     // draining the queue we instead just exit.
    1522   [ +  +  +  +  :         254 :     if (ok && !m_main_loop.spin(this)) {
                   +  + ]
    1523                 :           2 :         exiting = true;
    1524                 :             :     }
    1525                 :             : 
    1526                 :             :     // If the hook has been set again, enter a loop until an error is
    1527                 :             :     // encountered or the main loop is quit.
    1528   [ +  +  +  +  :         254 :     while (ok && !exiting && m_main_loop_hook) {
             -  +  -  + ]
    1529                 :           0 :         ok = run_main_loop_hook();
    1530                 :             : 
    1531                 :             :         // Additional jobs could have been enqueued from the
    1532                 :             :         // main loop hook
    1533   [ #  #  #  #  :           0 :         if (ok && !m_main_loop.spin(this)) {
                   #  # ]
    1534                 :           0 :             exiting = true;
    1535                 :             :         }
    1536                 :             :     }
    1537                 :             : 
    1538                 :             :     // The promise job queue should be drained even on error, to finish
    1539                 :             :     // outstanding async tasks before the context is torn down. Drain after
    1540                 :             :     // uncaught exceptions have been reported since draining runs callbacks.
    1541                 :             :     // We do not drain if we are exiting.
    1542   [ +  +  +  - ]:         254 :     if (!ok && !exiting) {
    1543                 :          29 :         JS::AutoSaveExceptionState saved_exc(m_cx);
    1544   [ +  -  -  + ]:          29 :         ok = run_jobs_fallible() && ok;
    1545                 :          29 :     }
    1546                 :             : 
    1547                 :         254 :     auto_profile_exit(auto_profile);
    1548                 :             : 
    1549                 :             :     uint8_t out_code;
    1550                 :         254 :     GErrorResult<> result = handle_exit_code(ok, "Script", filename, &out_code);
    1551                 :             : 
    1552         [ +  + ]:         254 :     if (exit_status_p) {
    1553   [ +  +  +  +  :         229 :         if (result.isOk() && retval.isInt32()) {
                   +  + ]
    1554                 :           5 :             int code = retval.toInt32();
    1555                 :           5 :             gjs_debug(GJS_DEBUG_CONTEXT,
    1556                 :             :                       "Script returned integer code %d", code);
    1557                 :           5 :             *exit_status_p = code;
    1558                 :             :         } else {
    1559                 :         224 :             *exit_status_p = out_code;
    1560                 :             :         }
    1561                 :             :     }
    1562                 :             : 
    1563                 :         508 :     return result;
    1564                 :         254 : }
    1565                 :             : 
    1566                 :         125 : GErrorResult<> GjsContextPrivate::eval_module(const char* identifier,
    1567                 :             :                                               uint8_t* exit_status_p) {
    1568                 :         125 :     AutoResetExit reset(this);
    1569                 :             : 
    1570                 :         125 :     bool auto_profile = auto_profile_enter();
    1571                 :             : 
    1572                 :         125 :     Gjs::AutoMainRealm ar{this};
    1573                 :             : 
    1574                 :         125 :     JS::RootedObject registry(m_cx, gjs_get_module_registry(m_global));
    1575                 :         125 :     JS::RootedId key(m_cx, gjs_intern_string_to_id(m_cx, identifier));
    1576                 :         125 :     JS::RootedObject obj(m_cx);
    1577   [ +  -  +  +  :         125 :     if (!gjs_global_registry_get(m_cx, registry, key, &obj) || !obj) {
                   +  + ]
    1578                 :           2 :         Gjs::AutoError error;
    1579                 :           2 :         g_set_error(error.out(), GJS_ERROR, GJS_ERROR_FAILED,
    1580                 :             :                     "Cannot load module with identifier: '%s'", identifier);
    1581                 :             : 
    1582         [ +  + ]:           2 :         if (exit_status_p)
    1583                 :           1 :             *exit_status_p = 1;
    1584                 :           2 :         return Err(error.release());
    1585                 :           2 :     }
    1586                 :             : 
    1587         [ +  + ]:         123 :     if (!JS::ModuleLink(m_cx, obj)) {
    1588                 :           2 :         gjs_log_exception(m_cx);
    1589                 :           2 :         Gjs::AutoError error;
    1590                 :           2 :         g_set_error(error.out(), GJS_ERROR, GJS_ERROR_FAILED,
    1591                 :             :                     "Failed to resolve imports for module: '%s'", identifier);
    1592                 :             : 
    1593         [ +  + ]:           2 :         if (exit_status_p)
    1594                 :           1 :             *exit_status_p = 1;
    1595                 :           2 :         return Err(error.release());
    1596                 :           2 :     }
    1597                 :             : 
    1598                 :         121 :     JS::RootedValue evaluation_promise(m_cx);
    1599                 :         121 :     bool ok = JS::ModuleEvaluate(m_cx, obj, &evaluation_promise);
    1600                 :             : 
    1601         [ +  - ]:         120 :     if (ok) {
    1602                 :         120 :         GjsContextPrivate::from_cx(m_cx)->main_loop_hold();
    1603                 :             : 
    1604                 :         240 :         ok = add_promise_reactions(
    1605                 :             :             m_cx, evaluation_promise, on_context_module_resolved,
    1606                 :             :             on_context_module_rejected_log_exception, identifier);
    1607                 :             :     }
    1608                 :             : 
    1609                 :         120 :     bool exiting = false;
    1610                 :             : 
    1611                 :             :     do {
    1612                 :             :         // If there are no errors and the mainloop hook is set, call it.
    1613   [ +  -  +  +  :         122 :         if (ok && m_main_loop_hook)
                   +  + ]
    1614                 :          50 :             ok = run_main_loop_hook();
    1615                 :             : 
    1616                 :             :         // Spin the internal loop until the main loop hook is set or no holds
    1617                 :             :         // remain.
    1618                 :             :         //
    1619                 :             :         // If the main loop returns false we cannot guarantee the state of our
    1620                 :             :         // promise queue (a module promise could be pending) so instead of
    1621                 :             :         // draining the queue we instead just exit.
    1622                 :             :         //
    1623                 :             :         // Additional jobs could have been enqueued from the
    1624                 :             :         // main loop hook
    1625   [ +  -  +  +  :         122 :         if (ok && !m_main_loop.spin(this)) {
                   +  + ]
    1626                 :           5 :             exiting = true;
    1627                 :             :         }
    1628   [ +  -  +  +  :         122 :     } while (ok && !exiting && m_main_loop_hook);
             +  +  +  + ]
    1629                 :             : 
    1630                 :             :     // The promise job queue should be drained even on error, to finish
    1631                 :             :     // outstanding async tasks before the context is torn down. Drain after
    1632                 :             :     // uncaught exceptions have been reported since draining runs callbacks.
    1633                 :             :     // We do not drain if we are exiting.
    1634   [ -  +  -  - ]:         120 :     if (!ok && !exiting) {
    1635                 :           0 :         JS::AutoSaveExceptionState saved_exc(m_cx);
    1636   [ #  #  #  # ]:           0 :         ok = run_jobs_fallible() && ok;
    1637                 :           0 :     }
    1638                 :             : 
    1639                 :         120 :     auto_profile_exit(auto_profile);
    1640                 :             : 
    1641                 :             :     uint8_t out_code;
    1642                 :             :     GErrorResult<> result =
    1643                 :         120 :         handle_exit_code(ok, "Module", identifier, &out_code);
    1644         [ +  + ]:         120 :     if (exit_status_p)
    1645                 :         118 :         *exit_status_p = out_code;
    1646                 :             : 
    1647                 :         120 :     return result;
    1648                 :         124 : }
    1649                 :             : 
    1650                 :         127 : GErrorResult<> GjsContextPrivate::register_module(const char* identifier,
    1651                 :             :                                                   const char* uri) {
    1652                 :         127 :     Gjs::AutoMainRealm ar{this};
    1653                 :             : 
    1654         [ +  + ]:         127 :     if (gjs_module_load(m_cx, identifier, uri))
    1655                 :         126 :         return Ok{};
    1656                 :             : 
    1657                 :           1 :     const char* msg = "unknown";
    1658                 :           1 :     JS::ExceptionStack exn_stack(m_cx);
    1659                 :           1 :     JS::ErrorReportBuilder builder(m_cx);
    1660   [ +  -  +  -  :           2 :     if (JS::StealPendingExceptionStack(m_cx, &exn_stack) &&
                   +  - ]
    1661                 :           1 :         builder.init(m_cx, exn_stack,
    1662                 :             :                      JS::ErrorReportBuilder::WithSideEffects)) {
    1663                 :           1 :         msg = builder.toStringResult().c_str();
    1664                 :             :     } else {
    1665                 :           0 :         JS_ClearPendingException(m_cx);
    1666                 :             :     }
    1667                 :             : 
    1668                 :           1 :     Gjs::AutoError error;
    1669         [ +  - ]:           1 :     g_set_error(error.out(), GJS_ERROR, GJS_ERROR_FAILED,
    1670                 :             :                 "Failed to parse module '%s': %s", identifier,
    1671                 :             :                 msg ? msg : "unknown");
    1672                 :             : 
    1673                 :           1 :     return Err(error.release());
    1674                 :         127 : }
    1675                 :             : 
    1676                 :             : bool
    1677                 :          63 : gjs_context_eval_file(GjsContext    *js_context,
    1678                 :             :                       const char    *filename,
    1679                 :             :                       int           *exit_status_p,
    1680                 :             :                       GError       **error)
    1681                 :             : {
    1682                 :          63 :     Gjs::AutoChar script;
    1683                 :             :     size_t script_len;
    1684                 :          63 :     Gjs::AutoUnref<GFile> file{g_file_new_for_commandline_arg(filename)};
    1685                 :             : 
    1686         [ +  + ]:          63 :     if (!g_file_load_contents(file, nullptr, script.out(), &script_len, nullptr,
    1687                 :             :                               error))
    1688                 :           1 :         return false;
    1689                 :             : 
    1690                 :          62 :     return gjs_context_eval(js_context, script, script_len, filename,
    1691                 :          62 :                             exit_status_p, error);
    1692                 :          63 : }
    1693                 :             : 
    1694                 :         115 : bool gjs_context_eval_module_file(GjsContext* js_context, const char* filename,
    1695                 :             :                                   uint8_t* exit_status_p, GError** error) {
    1696                 :         115 :     Gjs::AutoUnref<GFile> file{g_file_new_for_commandline_arg(filename)};
    1697                 :         115 :     Gjs::AutoChar uri{g_file_get_uri(file)};
    1698                 :             : 
    1699   [ +  -  +  + ]:         230 :     return gjs_context_register_module(js_context, uri, uri, error) &&
    1700                 :         115 :            gjs_context_eval_module(js_context, uri, exit_status_p, error);
    1701                 :         115 : }
    1702                 :             : 
    1703                 :             : /*
    1704                 :             :  * GjsContextPrivate::eval_with_scope:
    1705                 :             :  * @scope_object: an object to use as the global scope, or nullptr
    1706                 :             :  * @source: JavaScript program encoded in UTF-8
    1707                 :             :  * @source_len: length of @source, or -1 if @source is 0-terminated
    1708                 :             :  * @filename: filename to use as the origin of @source
    1709                 :             :  * @retval: location for the return value of @source
    1710                 :             :  *
    1711                 :             :  * Executes @source with a local scope so that nothing from the source code
    1712                 :             :  * leaks out into the global scope.
    1713                 :             :  * If @scope_object is given, then everything that @source placed in the global
    1714                 :             :  * namespace is defined on @scope_object.
    1715                 :             :  * Otherwise, the global definitions are just discarded.
    1716                 :             :  */
    1717                 :         258 : bool GjsContextPrivate::eval_with_scope(JS::HandleObject scope_object,
    1718                 :             :                                         const char* source, size_t source_len,
    1719                 :             :                                         const char* filename,
    1720                 :             :                                         JS::MutableHandleValue retval) {
    1721                 :             :     /* log and clear exception if it's set (should not be, normally...) */
    1722         [ -  + ]:         258 :     if (JS_IsExceptionPending(m_cx)) {
    1723                 :           0 :         g_warning("eval_with_scope() called with a pending exception");
    1724                 :           0 :         return false;
    1725                 :             :     }
    1726                 :             : 
    1727                 :         258 :     JS::RootedObject eval_obj(m_cx, scope_object);
    1728         [ +  + ]:         258 :     if (!eval_obj)
    1729                 :         255 :         eval_obj = JS_NewPlainObject(m_cx);
    1730                 :             : 
    1731                 :         258 :     JS::SourceText<mozilla::Utf8Unit> buf;
    1732         [ -  + ]:         258 :     if (!buf.init(m_cx, source, source_len, JS::SourceOwnership::Borrowed))
    1733                 :           0 :         return false;
    1734                 :             : 
    1735                 :         258 :     JS::RootedObjectVector scope_chain(m_cx);
    1736         [ -  + ]:         258 :     if (!scope_chain.append(eval_obj)) {
    1737                 :           0 :         JS_ReportOutOfMemory(m_cx);
    1738                 :           0 :         return false;
    1739                 :             :     }
    1740                 :             : 
    1741                 :         258 :     JS::CompileOptions options(m_cx);
    1742                 :         258 :     options.setFileAndLine(filename, 1).setNonSyntacticScope(true);
    1743                 :             : 
    1744                 :         258 :     Gjs::AutoUnref<GFile> file{g_file_new_for_commandline_arg(filename)};
    1745                 :         258 :     Gjs::AutoChar uri{g_file_get_uri(file)};
    1746                 :         258 :     JS::RootedObject priv(m_cx, gjs_script_module_build_private(m_cx, uri));
    1747         [ -  + ]:         258 :     if (!priv)
    1748                 :           0 :         return false;
    1749                 :             : 
    1750                 :         258 :     JS::RootedScript script(m_cx);
    1751                 :         258 :     script.set(JS::Compile(m_cx, options, buf));
    1752         [ +  + ]:         258 :     if (!script)
    1753                 :           1 :         return false;
    1754                 :             : 
    1755                 :         257 :     JS::SetScriptPrivate(script, JS::ObjectValue(*priv));
    1756         [ +  + ]:         257 :     if (!JS_ExecuteScript(m_cx, scope_chain, script, retval))
    1757                 :          30 :         return false;
    1758                 :             : 
    1759                 :         226 :     schedule_gc_if_needed();
    1760                 :             : 
    1761         [ -  + ]:         226 :     if (JS_IsExceptionPending(m_cx)) {
    1762                 :           0 :         g_warning(
    1763                 :             :             "JS::Evaluate() returned true but exception was pending; "
    1764                 :             :             "did somebody call gjs_throw() without returning false?");
    1765                 :           0 :         return false;
    1766                 :             :     }
    1767                 :             : 
    1768                 :         226 :     gjs_debug(GJS_DEBUG_CONTEXT, "Script evaluation succeeded");
    1769                 :             : 
    1770                 :         226 :     return true;
    1771                 :         257 : }
    1772                 :             : 
    1773                 :             : /*
    1774                 :             :  * GjsContextPrivate::call_function:
    1775                 :             :  * @this_obj: Object to use as the 'this' for the function call
    1776                 :             :  * @func_val: Callable to call, as a JS value
    1777                 :             :  * @args: Arguments to pass to the callable
    1778                 :             :  * @rval: Location for the return value
    1779                 :             :  *
    1780                 :             :  * Use this instead of JS_CallFunctionValue(), because it schedules a GC if
    1781                 :             :  * one is needed. It's good practice to check if a GC should be run every time
    1782                 :             :  * we return from JS back into C++.
    1783                 :             :  */
    1784                 :        2532 : bool GjsContextPrivate::call_function(JS::HandleObject this_obj,
    1785                 :             :                                       JS::HandleValue func_val,
    1786                 :             :                                       const JS::HandleValueArray& args,
    1787                 :             :                                       JS::MutableHandleValue rval) {
    1788         [ +  + ]:        2532 :     if (!JS_CallFunctionValue(m_cx, this_obj, func_val, args, rval))
    1789                 :          19 :         return false;
    1790                 :             : 
    1791                 :        2513 :     schedule_gc_if_needed();
    1792                 :             : 
    1793                 :        2513 :     return true;
    1794                 :             : }
    1795                 :             : 
    1796                 :             : bool
    1797                 :           1 : gjs_context_define_string_array(GjsContext  *js_context,
    1798                 :             :                                 const char    *array_name,
    1799                 :             :                                 gssize         array_length,
    1800                 :             :                                 const char   **array_values,
    1801                 :             :                                 GError       **error)
    1802                 :             : {
    1803                 :           1 :     g_return_val_if_fail(GJS_IS_CONTEXT(js_context), false);
    1804                 :           1 :     GjsContextPrivate* gjs = GjsContextPrivate::from_object(js_context);
    1805                 :             : 
    1806                 :           1 :     Gjs::AutoMainRealm ar{gjs};
    1807                 :             : 
    1808                 :           1 :     std::vector<std::string> strings;
    1809         [ +  - ]:           1 :     if (array_values) {
    1810         [ -  + ]:           1 :         if (array_length < 0)
    1811                 :           0 :             array_length = g_strv_length(const_cast<char**>(array_values));
    1812                 :           3 :         strings = {array_values, array_values + array_length};
    1813                 :             :     }
    1814                 :             : 
    1815                 :             :     // ARGV is a special case to preserve backwards compatibility.
    1816         [ +  - ]:           1 :     if (strcmp(array_name, "ARGV") == 0) {
    1817                 :           1 :         gjs->set_args(std::move(strings));
    1818                 :             : 
    1819                 :           1 :         return true;
    1820                 :             :     }
    1821                 :             : 
    1822                 :           0 :     JS::RootedObject global_root(gjs->context(), gjs->global());
    1823         [ #  # ]:           0 :     if (!gjs_define_string_array(gjs->context(), global_root, array_name,
    1824                 :             :                                  strings, JSPROP_READONLY | JSPROP_PERMANENT)) {
    1825                 :           0 :         gjs_log_exception(gjs->context());
    1826                 :           0 :         g_set_error(error,
    1827                 :             :                     GJS_ERROR,
    1828                 :             :                     GJS_ERROR_FAILED,
    1829                 :             :                     "gjs_define_string_array() failed");
    1830                 :           0 :         return false;
    1831                 :             :     }
    1832                 :             : 
    1833                 :           0 :     return true;
    1834                 :           1 : }
    1835                 :             : 
    1836                 :          61 : void gjs_context_set_argv(GjsContext* js_context, ssize_t array_length,
    1837                 :             :                           const char** array_values) {
    1838                 :          61 :     g_return_if_fail(GJS_IS_CONTEXT(js_context));
    1839                 :          61 :     GjsContextPrivate* gjs = GjsContextPrivate::from_object(js_context);
    1840                 :          61 :     std::vector<std::string> args(array_values, array_values + array_length);
    1841                 :          61 :     gjs->set_args(std::move(args));
    1842                 :          61 : }
    1843                 :             : 
    1844                 :             : static GjsContext *current_context;
    1845                 :             : 
    1846                 :             : GjsContext *
    1847                 :        6639 : gjs_context_get_current (void)
    1848                 :             : {
    1849                 :        6639 :     return current_context;
    1850                 :             : }
    1851                 :             : 
    1852                 :             : void
    1853                 :         510 : gjs_context_make_current (GjsContext *context)
    1854                 :             : {
    1855                 :         510 :     g_assert (context == NULL || current_context == NULL);
    1856                 :             : 
    1857                 :         510 :     current_context = context;
    1858                 :         510 : }
    1859                 :             : 
    1860                 :             : namespace Gjs {
    1861                 :             : /*
    1862                 :             :  * Gjs::AutoMainRealm:
    1863                 :             :  * @gjs: a #GjsContextPrivate
    1864                 :             :  *
    1865                 :             :  * Enters the realm of the "main global" for the context, and leaves when the
    1866                 :             :  * object is destructed at the end of the scope. The main global is the global
    1867                 :             :  * object under which all user JS code is executed. It is used as the root
    1868                 :             :  * object for the scope of modules loaded by GJS in this context.
    1869                 :             :  *
    1870                 :             :  * Only code in a different realm, such as the debugger code or the module
    1871                 :             :  * loader code, uses a different global object.
    1872                 :             :  */
    1873                 :       23619 : AutoMainRealm::AutoMainRealm(GjsContextPrivate* gjs)
    1874                 :       23619 :     : JSAutoRealm(gjs->context(), gjs->global()) {}
    1875                 :             : 
    1876                 :       21854 : AutoMainRealm::AutoMainRealm(JSContext* cx)
    1877                 :       21854 :     : AutoMainRealm(static_cast<GjsContextPrivate*>(JS_GetContextPrivate(cx))) {
    1878                 :       21854 : }
    1879                 :             : 
    1880                 :             : /*
    1881                 :             :  * Gjs::AutoInternalRealm:
    1882                 :             :  * @gjs: a #GjsContextPrivate
    1883                 :             :  *
    1884                 :             :  * Enters the realm of the "internal global" for the context, and leaves when
    1885                 :             :  * the object is destructed at the end of the scope. The internal global is only
    1886                 :             :  * used for executing the module loader code, to keep it separate from user
    1887                 :             :  * code.
    1888                 :             :  */
    1889                 :        2560 : AutoInternalRealm::AutoInternalRealm(GjsContextPrivate* gjs)
    1890                 :        2560 :     : JSAutoRealm(gjs->context(), gjs->internal_global()) {}
    1891                 :             : 
    1892                 :        2304 : AutoInternalRealm::AutoInternalRealm(JSContext* cx)
    1893                 :             :     : AutoInternalRealm(
    1894                 :        2304 :           static_cast<GjsContextPrivate*>(JS_GetContextPrivate(cx))) {}
    1895                 :             : 
    1896                 :             : }  // namespace Gjs
    1897                 :             : 
    1898                 :             : /**
    1899                 :             :  * gjs_context_get_profiler:
    1900                 :             :  * @self: the #GjsContext
    1901                 :             :  *
    1902                 :             :  * Returns the profiler's internal instance of #GjsProfiler for you to
    1903                 :             :  * customize, or %NULL if profiling is not enabled on this #GjsContext.
    1904                 :             :  *
    1905                 :             :  * Returns: (transfer none) (nullable): a #GjsProfiler
    1906                 :             :  */
    1907                 :             : GjsProfiler *
    1908                 :           7 : gjs_context_get_profiler(GjsContext *self)
    1909                 :             : {
    1910                 :           7 :     return GjsContextPrivate::from_object(self)->profiler();
    1911                 :             : }
    1912                 :             : 
    1913                 :             : /**
    1914                 :             :  * gjs_get_js_version:
    1915                 :             :  *
    1916                 :             :  * Returns the underlying version of the JS engine.
    1917                 :             :  *
    1918                 :             :  * Returns: a string
    1919                 :             :  */
    1920                 :             : const char *
    1921                 :           2 : gjs_get_js_version(void)
    1922                 :             : {
    1923                 :           2 :     return JS_GetImplementationVersion();
    1924                 :             : }
    1925                 :             : 
    1926                 :             : /**
    1927                 :             :  * gjs_context_get_repl_history_path:
    1928                 :             :  *
    1929                 :             :  * Returns the path property for persisting REPL command history
    1930                 :             :  *
    1931                 :             :  * Returns: a string
    1932                 :             :  */
    1933                 :         256 : const char* gjs_context_get_repl_history_path(GjsContext* self) {
    1934                 :         256 :     return GjsContextPrivate::from_object(self)->repl_history_path();
    1935                 :             : }
    1936                 :             : 
    1937                 :             : /**
    1938                 :             :  * gjs_context_run_in_realm:
    1939                 :             :  * @self: the #GjsContext
    1940                 :             :  * @func: (scope call): function to run
    1941                 :             :  * @user_data: (closure func): pointer to pass to @func
    1942                 :             :  *
    1943                 :             :  * Runs @func immediately, not asynchronously, after entering the JS context's
    1944                 :             :  * main realm. After @func completes, leaves the realm again.
    1945                 :             :  *
    1946                 :             :  * You only need this if you are using JSAPI calls from the SpiderMonkey API
    1947                 :             :  * directly.
    1948                 :             :  */
    1949                 :           1 : void gjs_context_run_in_realm(GjsContext* self, GjsContextInRealmFunc func,
    1950                 :             :                               void* user_data) {
    1951                 :           1 :     g_return_if_fail(GJS_IS_CONTEXT(self));
    1952                 :           1 :     g_return_if_fail(func);
    1953                 :             : 
    1954                 :           1 :     GjsContextPrivate* gjs = GjsContextPrivate::from_object(self);
    1955                 :           1 :     Gjs::AutoMainRealm ar{gjs};
    1956                 :           1 :     func(self, user_data);
    1957                 :           1 : }
        

Generated by: LCOV version 2.0-1