LCOV - code coverage report
Current view: top level - gjs - importer.cpp (source / functions) Coverage Total Hit
Test: gjs- Code Coverage Lines: 80.5 % 426 343
Test Date: 2024-12-07 00:45:03 Functions: 100.0 % 21 21
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 63.7 % 303 193

             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-2010 litl, LLC
       4                 :             : 
       5                 :             : #include <config.h>
       6                 :             : 
       7                 :             : #include <string.h>  // for size_t, strcmp, strlen
       8                 :             : 
       9                 :             : #ifdef _WIN32
      10                 :             : #    include <windows.h>
      11                 :             : #endif
      12                 :             : 
      13                 :             : #include <string>
      14                 :             : #include <vector>   // for vector
      15                 :             : 
      16                 :             : #include <gio/gio.h>
      17                 :             : #include <glib-object.h>
      18                 :             : #include <glib.h>
      19                 :             : 
      20                 :             : #include <js/Array.h>
      21                 :             : #include <js/CallArgs.h>
      22                 :             : #include <js/CharacterEncoding.h>
      23                 :             : #include <js/Class.h>
      24                 :             : #include <js/ComparisonOperators.h>
      25                 :             : #include <js/ErrorReport.h>  // for JS_ReportOutOfMemory, JSEXN_ERR
      26                 :             : #include <js/Exception.h>
      27                 :             : #include <js/GCVector.h>      // for StackGCVector
      28                 :             : #include <js/GlobalObject.h>  // for CurrentGlobalOrNull
      29                 :             : #include <js/Id.h>            // for PropertyKey
      30                 :             : #include <js/Object.h>        // for GetClass
      31                 :             : #include <js/PropertyAndElement.h>
      32                 :             : #include <js/PropertyDescriptor.h>
      33                 :             : #include <js/PropertySpec.h>
      34                 :             : #include <js/RootingAPI.h>
      35                 :             : #include <js/String.h>
      36                 :             : #include <js/Symbol.h>
      37                 :             : #include <js/TypeDecls.h>
      38                 :             : #include <js/Utility.h>  // for UniqueChars
      39                 :             : #include <js/Value.h>
      40                 :             : #include <jsapi.h>  // for JS_NewPlainObject, IdVector, JS_...
      41                 :             : #include <mozilla/Maybe.h>
      42                 :             : #include <mozilla/UniquePtr.h>
      43                 :             : 
      44                 :             : #include "gjs/atoms.h"
      45                 :             : #include "gjs/auto.h"
      46                 :             : #include "gjs/context-private.h"
      47                 :             : #include "gjs/gerror-result.h"
      48                 :             : #include "gjs/global.h"
      49                 :             : #include "gjs/importer.h"
      50                 :             : #include "gjs/jsapi-util.h"
      51                 :             : #include "gjs/macros.h"
      52                 :             : #include "gjs/module.h"
      53                 :             : #include "gjs/native.h"
      54                 :             : #include "util/log.h"
      55                 :             : 
      56                 :             : #define MODULE_INIT_FILENAME "__init__.js"
      57                 :             : 
      58                 :             : extern const JSClass gjs_importer_class;
      59                 :             : 
      60                 :             : GJS_JSAPI_RETURN_CONVENTION
      61                 :             : static JSObject* gjs_define_importer(JSContext*, JS::HandleObject, const char*,
      62                 :             :                                      const std::vector<std::string>&, bool);
      63                 :             : 
      64                 :             : GJS_JSAPI_RETURN_CONVENTION
      65                 :             : static bool
      66                 :          15 : importer_to_string(JSContext *cx,
      67                 :             :                    unsigned   argc,
      68                 :             :                    JS::Value *vp)
      69                 :             : {
      70         [ -  + ]:          15 :     GJS_GET_THIS(cx, argc, vp, args, importer);
      71                 :             : 
      72                 :          15 :     Gjs::AutoChar output;
      73                 :             : 
      74                 :          15 :     const JSClass* klass = JS::GetClass(importer);
      75                 :          15 :     const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
      76                 :          15 :     JS::RootedValue module_path(cx);
      77         [ -  + ]:          15 :     if (!JS_GetPropertyById(cx, importer, atoms.module_path(), &module_path))
      78                 :           0 :         return false;
      79                 :             : 
      80         [ +  + ]:          15 :     if (module_path.isNull()) {
      81                 :           8 :         output = g_strdup_printf("[%s root]", klass->name);
      82                 :             :     } else {
      83                 :           7 :         g_assert(module_path.isString() && "Bad importer.__modulePath__");
      84                 :           7 :         JS::UniqueChars path = gjs_string_to_utf8(cx, module_path);
      85         [ -  + ]:           7 :         if (!path)
      86                 :           0 :             return false;
      87                 :           7 :         output = g_strdup_printf("[%s %s]", klass->name, path.get());
      88         [ +  - ]:           7 :     }
      89                 :             : 
      90                 :          15 :     args.rval().setString(JS_NewStringCopyZ(cx, output));
      91                 :          15 :     return true;
      92                 :          15 : }
      93                 :             : 
      94                 :             : GJS_JSAPI_RETURN_CONVENTION
      95                 :             : static bool
      96                 :        1170 : define_meta_properties(JSContext       *context,
      97                 :             :                        JS::HandleObject module_obj,
      98                 :             :                        const char      *parse_name,
      99                 :             :                        const char      *module_name,
     100                 :             :                        JS::HandleObject parent)
     101                 :             : {
     102                 :             :     bool parent_is_module;
     103                 :        1170 :     const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
     104                 :             : 
     105                 :             :     /* For these meta-properties, don't set ENUMERATE since we wouldn't want to
     106                 :             :      * copy these symbols to any other object for example. RESOLVING is used to
     107                 :             :      * make sure we don't try to invoke a "resolve" operation, since this
     108                 :             :      * function may be called from inside one. */
     109                 :        1170 :     unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_RESOLVING;
     110                 :             : 
     111                 :             :     /* We define both __moduleName__ and __parentModule__ to null
     112                 :             :      * on the root importer
     113                 :             :      */
     114         [ +  - ]:        1170 :     parent_is_module = parent && JS_InstanceOf(context, parent,
     115         [ +  + ]:        1170 :                                                &gjs_importer_class, nullptr);
     116                 :             : 
     117         [ +  - ]:        2340 :     gjs_debug(GJS_DEBUG_IMPORTER, "Defining parent %p of %p '%s' is mod %d",
     118                 :        1170 :               parent.get(), module_obj.get(),
     119                 :             :               module_name ? module_name : "<root>", parent_is_module);
     120                 :             : 
     121         [ +  + ]:        1170 :     if (parse_name != nullptr) {
     122                 :         393 :         JS::RootedValue file(context);
     123         [ -  + ]:         393 :         if (!gjs_string_from_utf8(context, parse_name, &file))
     124                 :           0 :             return false;
     125         [ -  + ]:         393 :         if (!JS_DefinePropertyById(context, module_obj, atoms.file(), file,
     126                 :             :                                    attrs))
     127                 :           0 :             return false;
     128         [ +  - ]:         393 :     }
     129                 :             : 
     130                 :             :     /* Null is used instead of undefined for backwards compatibility with code
     131                 :             :      * that explicitly checks for null. */
     132                 :        1170 :     JS::RootedValue module_name_val(context, JS::NullValue());
     133                 :        1170 :     JS::RootedValue parent_module_val(context, JS::NullValue());
     134                 :        1170 :     JS::RootedValue module_path(context, JS::NullValue());
     135                 :        1170 :     JS::RootedValue to_string_tag(context);
     136         [ +  + ]:        1170 :     if (parent_is_module) {
     137         [ -  + ]:         929 :         if (!gjs_string_from_utf8(context, module_name, &module_name_val))
     138                 :           0 :             return false;
     139                 :         929 :         parent_module_val.setObject(*parent);
     140                 :             : 
     141                 :         929 :         JS::RootedValue parent_module_path(context);
     142         [ -  + ]:         929 :         if (!JS_GetPropertyById(context, parent, atoms.module_path(),
     143                 :             :                                 &parent_module_path))
     144                 :           0 :             return false;
     145                 :             : 
     146                 :         929 :         Gjs::AutoChar module_path_buf;
     147         [ +  + ]:         929 :         if (parent_module_path.isNull()) {
     148                 :         779 :             module_path_buf = g_strdup(module_name);
     149                 :             :         } else {
     150                 :             :             JS::UniqueChars parent_path =
     151                 :         150 :                 gjs_string_to_utf8(context, parent_module_path);
     152         [ -  + ]:         150 :             if (!parent_path)
     153                 :           0 :                 return false;
     154                 :         150 :             module_path_buf = g_strdup_printf("%s.%s", parent_path.get(), module_name);
     155         [ +  - ]:         150 :         }
     156         [ -  + ]:         929 :         if (!gjs_string_from_utf8(context, module_path_buf, &module_path))
     157                 :           0 :             return false;
     158                 :             : 
     159                 :             :         Gjs::AutoChar to_string_tag_buf{
     160                 :         929 :             g_strdup_printf("GjsModule %s", module_path_buf.get())};
     161         [ -  + ]:         929 :         if (!gjs_string_from_utf8(context, to_string_tag_buf, &to_string_tag))
     162                 :           0 :             return false;
     163   [ +  -  +  -  :         929 :     } else {
                   +  - ]
     164                 :         241 :         to_string_tag.setString(JS_AtomizeString(context, "GjsModule"));
     165                 :             :     }
     166                 :             : 
     167         [ -  + ]:        1170 :     if (!JS_DefinePropertyById(context, module_obj, atoms.module_name(),
     168                 :             :                                module_name_val, attrs))
     169                 :           0 :         return false;
     170                 :             : 
     171         [ -  + ]:        1170 :     if (!JS_DefinePropertyById(context, module_obj, atoms.parent_module(),
     172                 :             :                                parent_module_val, attrs))
     173                 :           0 :         return false;
     174                 :             : 
     175         [ -  + ]:        1170 :     if (!JS_DefinePropertyById(context, module_obj, atoms.module_path(),
     176                 :             :                                module_path, attrs))
     177                 :           0 :         return false;
     178                 :             : 
     179                 :             :     JS::RootedId to_string_tag_name(
     180                 :        1170 :         context, JS::PropertyKey::Symbol(JS::GetWellKnownSymbol(
     181                 :        1170 :                      context, JS::SymbolCode::toStringTag)));
     182                 :        1170 :     return JS_DefinePropertyById(context, module_obj, to_string_tag_name,
     183                 :        1170 :                                  to_string_tag, attrs);
     184                 :        1170 : }
     185                 :             : 
     186                 :             : GJS_JSAPI_RETURN_CONVENTION
     187                 :          62 : static bool import_directory(JSContext* context, JS::HandleObject obj,
     188                 :             :                              const char* name,
     189                 :             :                              const std::vector<std::string>& full_paths) {
     190                 :          62 :     gjs_debug(GJS_DEBUG_IMPORTER,
     191                 :             :               "Importing directory '%s'",
     192                 :             :               name);
     193                 :             : 
     194                 :             :     // We define a sub-importer that has only the given directories on its
     195                 :             :     // search path.
     196                 :          62 :     return !!gjs_define_importer(context, obj, name, full_paths, false);
     197                 :             : }
     198                 :             : 
     199                 :             : /* Make the property we set in gjs_module_import() permanent;
     200                 :             :  * we do this after the import successfully completes.
     201                 :             :  */
     202                 :             : GJS_JSAPI_RETURN_CONVENTION
     203                 :             : static bool
     204                 :         393 : seal_import(JSContext       *cx,
     205                 :             :             JS::HandleObject obj,
     206                 :             :             JS::HandleId     id,
     207                 :             :             const char      *name)
     208                 :             : {
     209                 :         393 :     JS::Rooted<mozilla::Maybe<JS::PropertyDescriptor>> maybe_descr(cx);
     210                 :             : 
     211   [ +  -  -  +  :         786 :     if (!JS_GetOwnPropertyDescriptorById(cx, obj, id, &maybe_descr) ||
                   -  + ]
     212                 :         393 :         maybe_descr.isNothing()) {
     213                 :           0 :         gjs_debug(GJS_DEBUG_IMPORTER,
     214                 :             :                   "Failed to get attributes to seal '%s' in importer",
     215                 :             :                   name);
     216                 :           0 :         return false;
     217                 :             :     }
     218                 :             : 
     219                 :         393 :     JS::Rooted<JS::PropertyDescriptor> descr(cx, maybe_descr.value());
     220                 :             : 
     221                 :         393 :     descr.setConfigurable(false);
     222                 :             : 
     223         [ -  + ]:         393 :     if (!JS_DefinePropertyById(cx, obj, id, descr)) {
     224                 :           0 :         gjs_debug(GJS_DEBUG_IMPORTER,
     225                 :             :                   "Failed to redefine attributes to seal '%s' in importer",
     226                 :             :                   name);
     227                 :           0 :         return false;
     228                 :             :     }
     229                 :             : 
     230                 :         393 :     return true;
     231                 :         393 : }
     232                 :             : 
     233                 :             : /* An import failed. Delete the property pointing to the import
     234                 :             :  * from the parent namespace. In complicated situations this might
     235                 :             :  * not be sufficient to get us fully back to a sane state. If:
     236                 :             :  *
     237                 :             :  *  - We import module A
     238                 :             :  *  - module A imports module B
     239                 :             :  *  - module B imports module A, storing a reference to the current
     240                 :             :  *    module A module object
     241                 :             :  *  - module A subsequently throws an exception
     242                 :             :  *
     243                 :             :  * Then module B is left imported, but the imported module B has
     244                 :             :  * a reference to the failed module A module object. To handle this
     245                 :             :  * we could could try to track the entire "import operation" and
     246                 :             :  * roll back *all* modifications made to the namespace objects.
     247                 :             :  * It's not clear that the complexity would be worth the small gain
     248                 :             :  * in robustness. (You can still come up with ways of defeating
     249                 :             :  * the attempt to clean up.)
     250                 :             :  */
     251                 :             : static void
     252                 :           4 : cancel_import(JSContext       *context,
     253                 :             :               JS::HandleObject obj,
     254                 :             :               const char      *name)
     255                 :             : {
     256                 :           4 :     gjs_debug(GJS_DEBUG_IMPORTER,
     257                 :             :               "Cleaning up from failed import of '%s'",
     258                 :             :               name);
     259                 :             : 
     260         [ -  + ]:           4 :     if (!JS_DeleteProperty(context, obj, name)) {
     261                 :           0 :         gjs_debug(GJS_DEBUG_IMPORTER,
     262                 :             :                   "Failed to delete '%s' in importer",
     263                 :             :                   name);
     264                 :             :     }
     265                 :           4 : }
     266                 :             : 
     267                 :             : /*
     268                 :             :  * gjs_import_native_module:
     269                 :             :  * @cx: the #JSContext
     270                 :             :  * @importer: the root importer
     271                 :             :  * @id_str: Name under which the module was registered with add()
     272                 :             :  *
     273                 :             :  * Imports a builtin native-code module so that it is available to JS code as
     274                 :             :  * `imports[id_str]`.
     275                 :             :  *
     276                 :             :  * Returns: true on success, false if an exception was thrown.
     277                 :             :  */
     278                 :         474 : bool gjs_import_native_module(JSContext* cx, JS::HandleObject importer,
     279                 :             :                               const char* id_str) {
     280                 :         474 :     gjs_debug(GJS_DEBUG_IMPORTER, "Importing '%s'", id_str);
     281                 :             : 
     282                 :             :     JS::RootedObject native_registry(
     283                 :         474 :         cx, gjs_get_native_registry(JS::CurrentGlobalOrNull(cx)));
     284                 :             : 
     285                 :         474 :     JS::RootedId id(cx, gjs_intern_string_to_id(cx, id_str));
     286         [ -  + ]:         474 :     if (id.isVoid())
     287                 :           0 :         return false;
     288                 :             : 
     289                 :         474 :     JS::RootedObject module(cx);
     290         [ -  + ]:         474 :     if (!gjs_global_registry_get(cx, native_registry, id, &module))
     291                 :           0 :         return false;
     292                 :             : 
     293         [ +  + ]:         905 :     if (!module &&
     294         [ +  - ]:         431 :         (!Gjs::NativeModuleDefineFuncs::get().define(cx, id_str, &module) ||
     295   [ -  +  -  + ]:         905 :          !gjs_global_registry_set(cx, native_registry, id, module)))
     296                 :           0 :         return false;
     297                 :             : 
     298   [ +  -  +  - ]:         948 :     return define_meta_properties(cx, module, nullptr, id_str, importer) &&
     299                 :         474 :            JS_DefineProperty(cx, importer, id_str, module,
     300                 :         474 :                              GJS_MODULE_PROP_FLAGS);
     301                 :         474 : }
     302                 :             : 
     303                 :             : GJS_JSAPI_RETURN_CONVENTION
     304                 :             : static bool
     305                 :         121 : import_module_init(JSContext       *context,
     306                 :             :                    GFile           *file,
     307                 :             :                    JS::HandleObject module_obj)
     308                 :             : {
     309                 :         121 :     gsize script_len = 0;
     310                 :         121 :     Gjs::AutoError error;
     311                 :             : 
     312                 :         121 :     GjsContextPrivate* gjs = GjsContextPrivate::from_cx(context);
     313                 :         121 :     JS::RootedValue ignored(context);
     314                 :             : 
     315                 :         121 :     Gjs::AutoChar script;
     316         [ +  + ]:         121 :     if (!g_file_load_contents(file, nullptr, script.out(), &script_len, nullptr,
     317                 :             :                               &error)) {
     318         [ +  - ]:         236 :         if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY) &&
     319   [ +  -  -  +  :         236 :             !g_error_matches(error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY) &&
                   -  + ]
     320                 :         118 :             !g_error_matches(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
     321                 :           0 :             gjs_throw_gerror_message(context, error);
     322                 :           0 :             return false;
     323                 :             :         }
     324                 :             : 
     325                 :         118 :         return true;
     326                 :             :     }
     327                 :           3 :     g_assert(script);
     328                 :             : 
     329                 :           3 :     Gjs::AutoChar full_path{g_file_get_parse_name(file)};
     330                 :             : 
     331                 :           3 :     return gjs->eval_with_scope(module_obj, script, script_len, full_path,
     332                 :           3 :                                 &ignored);
     333                 :         121 : }
     334                 :             : 
     335                 :             : GJS_JSAPI_RETURN_CONVENTION
     336                 :        1049 : static JSObject* load_module_init(JSContext* cx, JS::HandleObject in_object,
     337                 :             :                                   GFile* file) {
     338                 :             :     bool found;
     339                 :        1049 :     const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
     340                 :             : 
     341                 :             :     /* First we check if js module has already been loaded  */
     342         [ -  + ]:        1049 :     if (!JS_HasPropertyById(cx, in_object, atoms.module_init(), &found))
     343                 :           0 :         return nullptr;
     344         [ +  + ]:        1049 :     if (found) {
     345                 :         928 :         JS::RootedValue v_module(cx);
     346         [ -  + ]:         928 :         if (!JS_GetPropertyById(cx, in_object, atoms.module_init(),
     347                 :             :                                 &v_module))
     348                 :           0 :             return nullptr;
     349         [ +  - ]:         928 :         if (v_module.isObject())
     350                 :         928 :             return &v_module.toObject();
     351                 :             : 
     352                 :           0 :         Gjs::AutoChar full_path{g_file_get_parse_name(file)};
     353                 :           0 :         gjs_throw(cx, "Unexpected non-object module __init__ imported from %s",
     354                 :             :                   full_path.get());
     355                 :           0 :         return nullptr;
     356                 :         928 :     }
     357                 :             : 
     358                 :         121 :     JS::RootedObject module_obj(cx, JS_NewPlainObject(cx));
     359         [ -  + ]:         121 :     if (!module_obj)
     360                 :           0 :         return nullptr;
     361                 :             : 
     362         [ +  + ]:         121 :     if (!import_module_init(cx, file, module_obj))
     363                 :           2 :         return nullptr;
     364                 :             : 
     365         [ -  + ]:         119 :     if (!JS_DefinePropertyById(cx, in_object, atoms.module_init(), module_obj,
     366                 :             :                                GJS_MODULE_PROP_FLAGS & ~JSPROP_PERMANENT))
     367                 :           0 :         return nullptr;
     368                 :             : 
     369                 :         119 :     return module_obj;
     370                 :         121 : }
     371                 :             : 
     372                 :             : GJS_JSAPI_RETURN_CONVENTION
     373                 :           3 : static bool load_module_elements(JSContext* cx, JS::HandleObject in_object,
     374                 :             :                                  JS::MutableHandleIdVector prop_ids,
     375                 :             :                                  GFile* file) {
     376                 :           3 :     JS::RootedObject module_obj(cx, load_module_init(cx, in_object, file));
     377         [ -  + ]:           3 :     if (!module_obj)
     378                 :           0 :         return false;
     379                 :             : 
     380                 :           3 :     JS::Rooted<JS::IdVector> ids(cx, cx);
     381         [ -  + ]:           3 :     if (!JS_Enumerate(cx, module_obj, &ids))
     382                 :           0 :         return false;
     383                 :             : 
     384         [ -  + ]:           3 :     if (!prop_ids.appendAll(ids)) {
     385                 :           0 :         JS_ReportOutOfMemory(cx);
     386                 :           0 :         return false;
     387                 :             :     }
     388                 :             : 
     389                 :           3 :     return true;
     390                 :           3 : }
     391                 :             : 
     392                 :             : /* If error, returns false. If not found, returns true but does not touch
     393                 :             :  * the value at *result. If found, returns true and sets *result = true.
     394                 :             :  */
     395                 :             : GJS_JSAPI_RETURN_CONVENTION
     396                 :        1046 : static bool import_symbol_from_init_js(JSContext* cx, JS::HandleObject importer,
     397                 :             :                                        GFile* directory, const char* name,
     398                 :             :                                        bool* result) {
     399                 :             :     bool found;
     400                 :             :     Gjs::AutoUnref<GFile> file{
     401                 :        1046 :         g_file_get_child(directory, MODULE_INIT_FILENAME)};
     402                 :             : 
     403                 :        1046 :     JS::RootedObject module_obj(cx, load_module_init(cx, importer, file));
     404   [ +  +  -  +  :        1046 :     if (!module_obj || !JS_AlreadyHasOwnProperty(cx, module_obj, name, &found))
                   +  + ]
     405                 :           2 :         return false;
     406                 :             : 
     407         [ +  + ]:        1044 :     if (!found)
     408                 :        1042 :         return true;
     409                 :             : 
     410                 :           2 :     JS::RootedValue obj_val(cx);
     411         [ -  + ]:           2 :     if (!JS_GetProperty(cx, module_obj, name, &obj_val))
     412                 :           0 :         return false;
     413                 :             : 
     414         [ -  + ]:           2 :     if (obj_val.isUndefined())
     415                 :           0 :         return true;
     416                 :             : 
     417         [ -  + ]:           2 :     if (!JS_DefineProperty(cx, importer, name, obj_val,
     418                 :             :                            GJS_MODULE_PROP_FLAGS & ~JSPROP_PERMANENT))
     419                 :           0 :         return false;
     420                 :             : 
     421                 :           2 :     *result = true;
     422                 :           2 :     return true;
     423                 :        1046 : }
     424                 :             : 
     425                 :             : GJS_JSAPI_RETURN_CONVENTION
     426                 :         397 : static bool attempt_import(JSContext* cx, JS::HandleObject obj,
     427                 :             :                            JS::HandleId module_id, const char* module_name,
     428                 :             :                            GFile* file) {
     429                 :             :     JS::RootedObject module_obj(
     430                 :         397 :         cx, gjs_module_import(cx, obj, module_id, module_name, file));
     431         [ +  + ]:         397 :     if (!module_obj)
     432                 :           4 :         return false;
     433                 :             : 
     434                 :         393 :     Gjs::AutoChar full_path{g_file_get_parse_name(file)};
     435                 :             : 
     436                 :         393 :     return define_meta_properties(cx, module_obj, full_path, module_name,
     437   [ +  -  +  - ]:         786 :                                   obj) &&
     438                 :         393 :            seal_import(cx, obj, module_id, module_name);
     439                 :         397 : }
     440                 :             : 
     441                 :             : GJS_JSAPI_RETURN_CONVENTION
     442                 :             : static bool
     443                 :         397 : import_file_on_module(JSContext       *context,
     444                 :             :                       JS::HandleObject obj,
     445                 :             :                       JS::HandleId     id,
     446                 :             :                       const char      *name,
     447                 :             :                       GFile           *file)
     448                 :             : {
     449         [ +  + ]:         397 :     if (!attempt_import(context, obj, id, name, file)) {
     450                 :           4 :         cancel_import(context, obj, name);
     451                 :           4 :         return false;
     452                 :             :     }
     453                 :             : 
     454                 :         393 :     return true;
     455                 :             : }
     456                 :             : 
     457                 :             : GJS_JSAPI_RETURN_CONVENTION
     458                 :        1003 : static bool do_import(JSContext* context, JS::HandleObject obj,
     459                 :             :                       JS::HandleId id) {
     460                 :        1003 :     JS::RootedObject search_path(context);
     461                 :             :     guint32 search_path_len;
     462                 :             :     guint32 i;
     463                 :             :     bool exists, is_array;
     464                 :        1003 :     const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
     465                 :             : 
     466         [ -  + ]:        1003 :     if (!gjs_object_require_property(context, obj, "importer",
     467                 :             :                                      atoms.search_path(), &search_path))
     468                 :           0 :         return false;
     469                 :             : 
     470         [ -  + ]:        1003 :     if (!JS::IsArrayObject(context, search_path, &is_array))
     471                 :           0 :         return false;
     472         [ -  + ]:        1003 :     if (!is_array) {
     473                 :           0 :         gjs_throw(context, "searchPath property on importer is not an array");
     474                 :           0 :         return false;
     475                 :             :     }
     476                 :             : 
     477         [ -  + ]:        1003 :     if (!JS::GetArrayLength(context, search_path, &search_path_len)) {
     478                 :           0 :         gjs_throw(context, "searchPath array has no length");
     479                 :           0 :         return false;
     480                 :             :     }
     481                 :             : 
     482                 :        1003 :     JS::UniqueChars name;
     483         [ -  + ]:        1003 :     if (!gjs_get_string_id(context, id, &name))
     484                 :           0 :         return false;
     485         [ -  + ]:        1003 :     if (!name) {
     486                 :           0 :         gjs_throw(context, "Importing invalid module name");
     487                 :           0 :         return false;
     488                 :             :     }
     489                 :             : 
     490                 :             :     // null if this is the root importer
     491                 :        1003 :     JS::RootedValue parent(context);
     492         [ -  + ]:        1003 :     if (!JS_GetPropertyById(context, obj, atoms.parent_module(), &parent))
     493                 :           0 :         return false;
     494                 :             : 
     495                 :             :     /* First try importing an internal module like gi */
     496   [ +  +  +  +  :        1785 :     if (parent.isNull() &&
                   +  + ]
     497                 :         782 :         Gjs::NativeModuleDefineFuncs::get().is_registered(name.get())) {
     498         [ -  + ]:         474 :         if (!gjs_import_native_module(context, obj, name.get()))
     499                 :           0 :             return false;
     500                 :             : 
     501                 :         474 :         gjs_debug(GJS_DEBUG_IMPORTER, "successfully imported module '%s'",
     502                 :             :                   name.get());
     503                 :         474 :         return true;
     504                 :             :     }
     505                 :             : 
     506                 :         529 :     Gjs::AutoChar filename{g_strdup_printf("%s.js", name.get())};
     507                 :         529 :     std::vector<std::string> directories;
     508                 :         529 :     JS::RootedValue elem(context);
     509                 :         529 :     JS::RootedString str(context);
     510                 :             : 
     511         [ +  + ]:        1174 :     for (i = 0; i < search_path_len; ++i) {
     512                 :        1046 :         elem.setUndefined();
     513         [ -  + ]:        1046 :         if (!JS_GetElement(context, search_path, i, &elem)) {
     514                 :             :             /* this means there was an exception, while elem.isUndefined()
     515                 :             :              * means no element found
     516                 :             :              */
     517                 :         401 :             return false;
     518                 :             :         }
     519                 :             : 
     520         [ -  + ]:        1046 :         if (elem.isUndefined())
     521                 :         645 :             continue;
     522                 :             : 
     523         [ -  + ]:        1046 :         if (!elem.isString()) {
     524                 :           0 :             gjs_throw(context, "importer searchPath contains non-string");
     525                 :           0 :             return false;
     526                 :             :         }
     527                 :             : 
     528                 :        1046 :         str = elem.toString();
     529                 :        1046 :         JS::UniqueChars dirname(JS_EncodeStringToUTF8(context, str));
     530         [ -  + ]:        1046 :         if (!dirname)
     531                 :           0 :             return false;
     532                 :             : 
     533                 :             :         /* Ignore empty path elements */
     534         [ -  + ]:        1046 :         if (dirname[0] == '\0')
     535                 :           0 :             continue;
     536                 :             : 
     537                 :             :         Gjs::AutoUnref<GFile> directory{
     538                 :        1046 :             g_file_new_for_commandline_arg(dirname.get())};
     539                 :             : 
     540                 :             :         /* Try importing __init__.js and loading the symbol from it */
     541                 :        1046 :         bool found = false;
     542         [ +  + ]:        1046 :         if (!import_symbol_from_init_js(context, obj, directory, name.get(),
     543                 :             :                                         &found))
     544                 :           2 :             return false;
     545         [ +  + ]:        1044 :         if (found)
     546                 :           2 :             return true;
     547                 :             : 
     548                 :             :         /* Second try importing a directory (a sub-importer) */
     549                 :        1042 :         Gjs::AutoUnref<GFile> file{g_file_get_child(directory, name.get())};
     550                 :             : 
     551         [ +  + ]:        1042 :         if (g_file_query_file_type(file, GFileQueryInfoFlags(0), nullptr) ==
     552                 :             :             G_FILE_TYPE_DIRECTORY) {
     553                 :          62 :             Gjs::AutoChar full_path{g_file_get_parse_name(file)};
     554                 :          62 :             gjs_debug(GJS_DEBUG_IMPORTER,
     555                 :             :                       "Adding directory '%s' to child importer '%s'",
     556                 :             :                       full_path.get(), name.get());
     557                 :          62 :             directories.push_back(full_path.get());
     558                 :          62 :         }
     559                 :             : 
     560                 :             :         /* If we just added to directories, we know we don't need to
     561                 :             :          * check for a file.  If we added to directories on an earlier
     562                 :             :          * iteration, we want to ignore any files later in the
     563                 :             :          * path. So, always skip the rest of the loop block if we have
     564                 :             :          * directories.
     565                 :             :          */
     566         [ +  + ]:        1042 :         if (!directories.empty())
     567                 :         234 :             continue;
     568                 :             : 
     569                 :             :         /* Third, if it's not a directory, try importing a file */
     570                 :         808 :         file = g_file_get_child(directory, filename.get());
     571                 :         808 :         exists = g_file_query_exists(file, nullptr);
     572                 :             : 
     573         [ +  + ]:         808 :         if (!exists) {
     574                 :         411 :             Gjs::AutoChar full_path{g_file_get_parse_name(file)};
     575                 :         411 :             gjs_debug(GJS_DEBUG_IMPORTER,
     576                 :             :                       "JS import '%s' not found in %s at %s", name.get(),
     577                 :             :                       dirname.get(), full_path.get());
     578                 :         411 :             continue;
     579                 :         411 :         }
     580                 :             : 
     581         [ +  + ]:         397 :         if (import_file_on_module(context, obj, id, name.get(), file)) {
     582                 :         393 :             gjs_debug(GJS_DEBUG_IMPORTER, "successfully imported module '%s'",
     583                 :             :                       name.get());
     584                 :         393 :             return true;
     585                 :             :         }
     586                 :             : 
     587                 :             :         /* Don't keep searching path if we fail to load the file for
     588                 :             :          * reasons other than it doesn't exist... i.e. broken files
     589                 :             :          * block searching for nonbroken ones
     590                 :             :          */
     591                 :           4 :         return false;
     592   [ +  +  +  +  :        3134 :     }
                   +  + ]
     593                 :             : 
     594         [ +  + ]:         128 :     if (!directories.empty()) {
     595         [ -  + ]:          62 :         if (!import_directory(context, obj, name.get(), directories))
     596                 :           0 :             return false;
     597                 :             : 
     598                 :          62 :         gjs_debug(GJS_DEBUG_IMPORTER, "successfully imported directory '%s'",
     599                 :             :                   name.get());
     600                 :          62 :         return true;
     601                 :             :     }
     602                 :             : 
     603                 :             :     /* If no exception occurred, the problem is just that we got to the
     604                 :             :      * end of the path. Be sure an exception is set. */
     605                 :          66 :     g_assert(!JS_IsExceptionPending(context));
     606                 :          66 :     gjs_throw_custom(context, JSEXN_ERR, "ImportError",
     607                 :             :                      "No JS module '%s' found in search path", name.get());
     608                 :          66 :     return false;
     609                 :        1003 : }
     610                 :             : 
     611                 :             : GJS_JSAPI_RETURN_CONVENTION
     612                 :           3 : static bool importer_new_enumerate(JSContext* context, JS::HandleObject object,
     613                 :             :                                    JS::MutableHandleIdVector properties,
     614                 :             :                                    bool enumerable_only [[maybe_unused]]) {
     615                 :             :     guint32 search_path_len;
     616                 :             :     guint32 i;
     617                 :             :     bool is_array;
     618                 :           3 :     const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
     619                 :             : 
     620                 :           3 :     JS::RootedObject search_path(context);
     621         [ -  + ]:           3 :     if (!gjs_object_require_property(context, object, "importer",
     622                 :             :                                      atoms.search_path(), &search_path))
     623                 :           0 :         return false;
     624                 :             : 
     625         [ -  + ]:           3 :     if (!JS::IsArrayObject(context, search_path, &is_array))
     626                 :           0 :         return false;
     627         [ -  + ]:           3 :     if (!is_array) {
     628                 :           0 :         gjs_throw(context, "searchPath property on importer is not an array");
     629                 :           0 :         return false;
     630                 :             :     }
     631                 :             : 
     632         [ -  + ]:           3 :     if (!JS::GetArrayLength(context, search_path, &search_path_len)) {
     633                 :           0 :         gjs_throw(context, "searchPath array has no length");
     634                 :           0 :         return false;
     635                 :             :     }
     636                 :             : 
     637                 :           3 :     JS::RootedValue elem(context);
     638                 :           3 :     JS::RootedString str(context);
     639         [ +  + ]:           6 :     for (i = 0; i < search_path_len; ++i) {
     640                 :           3 :         elem.setUndefined();
     641         [ -  + ]:           3 :         if (!JS_GetElement(context, search_path, i, &elem)) {
     642                 :             :             /* this means there was an exception, while elem.isUndefined()
     643                 :             :              * means no element found
     644                 :             :              */
     645                 :           0 :             return false;
     646                 :             :         }
     647                 :             : 
     648         [ -  + ]:           3 :         if (elem.isUndefined())
     649                 :           0 :             continue;
     650                 :             : 
     651         [ -  + ]:           3 :         if (!elem.isString()) {
     652                 :           0 :             gjs_throw(context, "importer searchPath contains non-string");
     653                 :           0 :             return false;
     654                 :             :         }
     655                 :             : 
     656                 :           3 :         str = elem.toString();
     657                 :           3 :         JS::UniqueChars dirname(JS_EncodeStringToUTF8(context, str));
     658         [ -  + ]:           3 :         if (!dirname)
     659                 :           0 :             return false;
     660                 :             : 
     661                 :             :         Gjs::AutoUnref<GFile> directory{
     662                 :           3 :             g_file_new_for_commandline_arg(dirname.get())};
     663                 :             :         Gjs::AutoUnref<GFile> file{
     664                 :           3 :             g_file_get_child(directory, MODULE_INIT_FILENAME)};
     665                 :             : 
     666         [ -  + ]:           3 :         if (!load_module_elements(context, object, properties, file))
     667                 :           0 :             return false;
     668                 :             : 
     669                 :             :         /* new_for_commandline_arg handles resource:/// paths */
     670                 :             :         Gjs::AutoUnref<GFileEnumerator> direnum{g_file_enumerate_children(
     671                 :             :             directory, "standard::name,standard::type", G_FILE_QUERY_INFO_NONE,
     672                 :           3 :             nullptr, nullptr)};
     673                 :             : 
     674                 :             :         while (true) {
     675                 :             :             GFileInfo *info;
     676                 :             :             GFile *file;
     677   [ +  -  -  +  :         138 :             if (!direnum ||
                   -  + ]
     678                 :          69 :                 !g_file_enumerator_iterate(direnum, &info, &file, NULL, NULL))
     679                 :           0 :                 break;
     680   [ +  +  +  - ]:          69 :             if (info == NULL || file == NULL)
     681                 :             :                 break;
     682                 :             : 
     683                 :          66 :             Gjs::AutoChar filename{g_file_get_basename(file)};
     684                 :             : 
     685                 :             :             /* skip hidden files and directories (.svn, .git, ...) */
     686         [ -  + ]:          66 :             if (filename.get()[0] == '.')
     687                 :           0 :                 continue;
     688                 :             : 
     689                 :             :             /* skip module init file */
     690         [ -  + ]:          66 :             if (strcmp(filename, MODULE_INIT_FILENAME) == 0)
     691                 :           0 :                 continue;
     692                 :             : 
     693         [ +  + ]:          66 :             if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY) {
     694                 :          21 :                 jsid id = gjs_intern_string_to_id(context, filename);
     695         [ -  + ]:          21 :                 if (id.isVoid())
     696                 :           0 :                     return false;
     697         [ -  + ]:          21 :                 if (!properties.append(id)) {
     698                 :           0 :                     JS_ReportOutOfMemory(context);
     699                 :           0 :                     return false;
     700                 :             :                 }
     701   [ -  +  +  -  :          45 :             } else if (g_str_has_suffix(filename, ".js")) {
                   +  + ]
     702                 :             :                 Gjs::AutoChar filename_noext{
     703                 :          39 :                     g_strndup(filename, strlen(filename) - 3)};
     704                 :          39 :                 jsid id = gjs_intern_string_to_id(context, filename_noext);
     705         [ -  + ]:          39 :                 if (id.isVoid())
     706                 :           0 :                     return false;
     707         [ -  + ]:          39 :                 if (!properties.append(id)) {
     708                 :           0 :                     JS_ReportOutOfMemory(context);
     709                 :           0 :                     return false;
     710                 :             :                 }
     711         [ +  - ]:          39 :             }
     712      [ +  -  - ]:         132 :         }
     713   [ +  -  +  -  :           3 :     }
             +  -  +  - ]
     714                 :           3 :     return true;
     715                 :           3 : }
     716                 :             : 
     717                 :             : /* The *resolved out parameter, on success, should be false to indicate that id
     718                 :             :  * was not resolved; and true if id was resolved. */
     719                 :             : GJS_JSAPI_RETURN_CONVENTION
     720                 :             : static bool
     721                 :        1266 : importer_resolve(JSContext        *context,
     722                 :             :                  JS::HandleObject  obj,
     723                 :             :                  JS::HandleId      id,
     724                 :             :                  bool             *resolved)
     725                 :             : {
     726         [ +  + ]:        1266 :     if (!id.isString()) {
     727                 :           1 :         *resolved = false;
     728                 :           1 :         return true;
     729                 :             :     }
     730                 :             : 
     731                 :        1265 :     const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
     732   [ +  +  +  +  :        2268 :     if (id == atoms.module_init() || id == atoms.to_string() ||
                   -  + ]
     733         [ +  + ]:        2268 :         id == atoms.value_of()) {
     734                 :         262 :         *resolved = false;
     735                 :         262 :         return true;
     736                 :             :     }
     737                 :             : 
     738                 :             :     gjs_debug_jsprop(GJS_DEBUG_IMPORTER, "Resolve prop '%s' hook, obj %s",
     739                 :             :                      gjs_debug_id(id).c_str(), gjs_debug_object(obj).c_str());
     740                 :             : 
     741         [ -  + ]:        1003 :     if (!id.isString()) {
     742                 :           0 :         *resolved = false;
     743                 :           0 :         return true;
     744                 :             :     }
     745                 :             : 
     746         [ +  + ]:        1003 :     if (!do_import(context, obj, id))
     747                 :          72 :         return false;
     748                 :             : 
     749                 :         931 :     *resolved = true;
     750                 :         931 :     return true;
     751                 :             : }
     752                 :             : 
     753                 :             : static const JSClassOps gjs_importer_class_ops = {
     754                 :             :     nullptr,  // addProperty
     755                 :             :     nullptr,  // deleteProperty
     756                 :             :     nullptr,  // enumerate
     757                 :             :     importer_new_enumerate,
     758                 :             :     importer_resolve,
     759                 :             : };
     760                 :             : 
     761                 :             : const JSClass gjs_importer_class = {
     762                 :             :     "GjsFileImporter",
     763                 :             :     0,
     764                 :             :     &gjs_importer_class_ops,
     765                 :             : };
     766                 :             : 
     767                 :             : static const JSPropertySpec gjs_importer_proto_props[] = {
     768                 :             :     JS_STRING_SYM_PS(toStringTag, "GjsFileImporter", JSPROP_READONLY),
     769                 :             :     JS_PS_END};
     770                 :             : 
     771                 :             : JSFunctionSpec gjs_importer_proto_funcs[] = {
     772                 :             :     JS_FN("toString", importer_to_string, 0, 0),
     773                 :             :     JS_FS_END};
     774                 :             : 
     775                 :         241 : [[nodiscard]] static const std::vector<std::string>& gjs_get_search_path() {
     776   [ +  +  +  - ]:         241 :     static std::vector<std::string> gjs_search_path;
     777                 :             :     static bool search_path_initialized = false;
     778                 :             : 
     779                 :             :     /* not thread safe */
     780                 :             : 
     781         [ +  + ]:         241 :     if (!search_path_initialized) {
     782                 :             :         const char* const* system_data_dirs;
     783                 :             :         const char *envstr;
     784                 :             :         gsize i;
     785                 :             : 
     786                 :             :         /* in order of priority */
     787                 :             : 
     788                 :             :         /* $GJS_PATH */
     789                 :         100 :         envstr = g_getenv("GJS_PATH");
     790         [ +  - ]:         100 :         if (envstr) {
     791                 :             :             char **dirs, **d;
     792                 :         100 :             dirs = g_strsplit(envstr, G_SEARCHPATH_SEPARATOR_S, 0);
     793         [ -  + ]:         100 :             for (d = dirs; *d != NULL; d++)
     794                 :           0 :                 gjs_search_path.push_back(*d);
     795                 :             :             /* we assume the array and strings are allocated separately */
     796                 :         100 :             g_free(dirs);
     797                 :             :         }
     798                 :             : 
     799                 :         200 :         gjs_search_path.push_back("resource:///org/gnome/gjs/modules/script/");
     800                 :         100 :         gjs_search_path.push_back("resource:///org/gnome/gjs/modules/core/");
     801                 :             : 
     802                 :             :         /* $XDG_DATA_DIRS /gjs-1.0 */
     803                 :         100 :         system_data_dirs = g_get_system_data_dirs();
     804         [ +  + ]:         300 :         for (i = 0; system_data_dirs[i] != NULL; ++i) {
     805                 :             :             Gjs::AutoChar s{
     806                 :         200 :                 g_build_filename(system_data_dirs[i], "gjs-1.0", nullptr)};
     807                 :         200 :             gjs_search_path.push_back(s.get());
     808                 :         200 :         }
     809                 :             : 
     810                 :             :         /* ${datadir}/share/gjs-1.0 */
     811                 :             : #ifdef G_OS_WIN32
     812                 :             :         extern HMODULE gjs_dll;
     813                 :             :         char *basedir = g_win32_get_package_installation_directory_of_module (gjs_dll);
     814                 :             :         Gjs::AutoChar gjs_data_dir{
     815                 :             :             g_build_filename(basedir, "share", "gjs-1.0", nullptr)};
     816                 :             :         gjs_search_path.push_back(gjs_data_dir.get());
     817                 :             :         g_free (basedir);
     818                 :             : #else
     819                 :         100 :         gjs_search_path.push_back(GJS_JS_DIR);
     820                 :             : #endif
     821                 :             : 
     822                 :         100 :         search_path_initialized = true;
     823                 :             :     }
     824                 :             : 
     825                 :         241 :     return gjs_search_path;
     826                 :             : }
     827                 :             : 
     828                 :             : GJS_JSAPI_RETURN_CONVENTION
     829                 :           1 : static bool no_construct(JSContext* cx, unsigned argc, JS::Value* vp) {
     830                 :           1 :     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
     831                 :           1 :     gjs_throw_abstract_constructor_error(cx, args);
     832                 :           1 :     return false;
     833                 :             : }
     834                 :             : 
     835                 :             : GJS_JSAPI_RETURN_CONVENTION
     836                 :         303 : static JSObject* gjs_importer_define_proto(JSContext* cx) {
     837                 :         303 :     JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
     838                 :         303 :     g_assert(global && "Must enter a realm before defining importer");
     839                 :             : 
     840                 :             :     // If we've been here more than once, we already have the proto
     841                 :             :     JS::Value v_proto =
     842                 :         303 :         gjs_get_global_slot(global, GjsGlobalSlot::PROTOTYPE_importer);
     843         [ +  + ]:         303 :     if (!v_proto.isUndefined()) {
     844                 :          62 :         g_assert(v_proto.isObject() &&
     845                 :             :                  "Someone stored some weird value in a global slot");
     846                 :          62 :         return &v_proto.toObject();
     847                 :             :     }
     848                 :             : 
     849                 :         241 :     JS::RootedObject proto(cx, JS_NewPlainObject(cx));
     850   [ +  -  +  - ]:         482 :     if (!proto || !JS_DefineFunctions(cx, proto, gjs_importer_proto_funcs) ||
     851   [ -  +  -  + ]:         482 :         !JS_DefineProperties(cx, proto, gjs_importer_proto_props))
     852                 :           0 :         return nullptr;
     853                 :         241 :     gjs_set_global_slot(global, GjsGlobalSlot::PROTOTYPE_importer,
     854                 :         241 :                         JS::ObjectValue(*proto));
     855                 :             : 
     856                 :             :     // For backwards compatibility
     857                 :         241 :     JSFunction* constructor = JS_NewFunction(
     858                 :             :         cx, no_construct, 0, JSFUN_CONSTRUCTOR, "GjsFileImporter");
     859                 :         241 :     JS::RootedObject ctor_obj(cx, JS_GetFunctionObject(constructor));
     860         [ +  - ]:         482 :     if (!JS_LinkConstructorAndPrototype(cx, ctor_obj, proto) ||
     861   [ -  +  -  + ]:         482 :         !JS_DefineProperty(cx, global, "GjsFileImporter", ctor_obj, 0))
     862                 :           0 :         return nullptr;
     863                 :             : 
     864                 :         241 :     gjs_debug(GJS_DEBUG_CONTEXT, "Initialized class %s prototype %p",
     865                 :         241 :               gjs_importer_class.name, proto.get());
     866                 :         241 :     return proto;
     867                 :         303 : }
     868                 :             : 
     869                 :             : GJS_JSAPI_RETURN_CONVENTION
     870                 :         303 : static JSObject* gjs_create_importer(
     871                 :             :     JSContext* context, const char* importer_name,
     872                 :             :     const std::vector<std::string>& initial_search_path,
     873                 :             :     bool add_standard_search_path, JS::HandleObject in_object) {
     874                 :         303 :     std::vector<std::string> search_paths = initial_search_path;
     875         [ +  + ]:         303 :     if (add_standard_search_path) {
     876                 :             :         /* Stick the "standard" shared search path after the provided one. */
     877                 :         241 :         const std::vector<std::string>& gjs_search_path = gjs_get_search_path();
     878                 :         241 :         search_paths.insert(search_paths.end(), gjs_search_path.begin(),
     879                 :             :                             gjs_search_path.end());
     880                 :             :     }
     881                 :             : 
     882                 :         303 :     JS::RootedObject proto(context, gjs_importer_define_proto(context));
     883         [ -  + ]:         303 :     if (!proto)
     884                 :           0 :         return nullptr;
     885                 :             : 
     886                 :             :     JS::RootedObject importer(
     887                 :             :         context,
     888                 :         303 :         JS_NewObjectWithGivenProto(context, &gjs_importer_class, proto));
     889         [ -  + ]:         303 :     if (!importer)
     890                 :           0 :         return nullptr;
     891                 :             : 
     892                 :             :     gjs_debug_lifecycle(GJS_DEBUG_IMPORTER, "importer constructor, obj %p",
     893                 :             :                         importer.get());
     894                 :             : 
     895                 :             :     /* API users can replace this property from JS, is the idea */
     896         [ -  + ]:         303 :     if (!gjs_define_string_array(
     897                 :             :             context, importer, "searchPath", search_paths,
     898                 :             :             // settable (no READONLY) but not deletable (PERMANENT)
     899                 :             :             JSPROP_PERMANENT | JSPROP_RESOLVING))
     900                 :           0 :         return nullptr;
     901                 :             : 
     902         [ -  + ]:         303 :     if (!define_meta_properties(context, importer, NULL, importer_name, in_object))
     903                 :           0 :         return nullptr;
     904                 :             : 
     905                 :         303 :     return importer;
     906                 :         303 : }
     907                 :             : 
     908                 :             : GJS_JSAPI_RETURN_CONVENTION
     909                 :          62 : static JSObject* gjs_define_importer(
     910                 :             :     JSContext* context, JS::HandleObject in_object, const char* importer_name,
     911                 :             :     const std::vector<std::string>& initial_search_path,
     912                 :             :     bool add_standard_search_path) {
     913                 :             :     JS::RootedObject importer(
     914                 :             :         context,
     915                 :          62 :         gjs_create_importer(context, importer_name, initial_search_path,
     916                 :          62 :                             add_standard_search_path, in_object));
     917                 :             : 
     918         [ -  + ]:          62 :     if (!JS_DefineProperty(context, in_object, importer_name, importer,
     919                 :             :                            GJS_MODULE_PROP_FLAGS))
     920                 :           0 :         return nullptr;
     921                 :             : 
     922                 :          62 :     gjs_debug(GJS_DEBUG_IMPORTER,
     923                 :          62 :               "Defined importer '%s' %p in %p", importer_name, importer.get(),
     924                 :          62 :               in_object.get());
     925                 :             : 
     926                 :          62 :     return importer;
     927                 :          62 : }
     928                 :             : 
     929                 :         241 : JSObject* gjs_create_root_importer(
     930                 :             :     JSContext* cx, const std::vector<std::string>& search_path) {
     931                 :         241 :     return gjs_create_importer(cx, "imports", search_path, true, nullptr);
     932                 :             : }
        

Generated by: LCOV version 2.0-1