LCOV - code coverage report
Current view: top level - modules/core/overrides - Gio.js (source / functions) Coverage Total Hit
Test: gjs- Code Coverage Lines: 88.0 % 552 486
Test Date: 2024-03-26 02:45:07 Functions: 94.5 % 73 69
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 71.0 % 310 220

             Branch data     Line data    Source code
       1                 :          96 : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
       2                 :             : // SPDX-FileCopyrightText: 2011 Giovanni Campagna
       3                 :             : 
       4                 :          20 : var GLib = imports.gi.GLib;
       5                 :          20 : var GjsPrivate = imports.gi.GjsPrivate;
       6                 :          20 : var Signals = imports.signals;
       7                 :             : var Gio;
       8                 :             : 
       9                 :             : // Ensures that a Gio.UnixFDList being passed into or out of a DBus method with
      10                 :             : // a parameter type that includes 'h' somewhere, actually has entries in it for
      11                 :             : // each of the indices being passed as an 'h' parameter.
      12                 :          12 : function _validateFDVariant(variant, fdList) {
      13 [ -  + ][ -  + ]:          12 :     switch (String.fromCharCode(variant.classify())) {
         [ -  + ][ -  + ]
         [ -  + ][ -  + ]
         [ -  + ][ -  + ]
         [ -  + ][ -  + ]
         [ -  + ][ -  + ]
         [ +  + ][ -  + ]
         [ -  + ][ -  + ]
         [ +  - ][ #  # ]
      14                 :             :     case 'b':
      15                 :             :     case 'y':
      16                 :             :     case 'n':
      17                 :             :     case 'q':
      18                 :             :     case 'i':
      19                 :             :     case 'u':
      20                 :             :     case 'x':
      21                 :             :     case 't':
      22                 :             :     case 'd':
      23                 :             :     case 'o':
      24                 :             :     case 'g':
      25                 :             :     case 's':
      26                 :           0 :         return;
      27                 :           6 :     case 'h': {
      28                 :           6 :         const val = variant.get_handle();
      29                 :           6 :         const numFds = fdList.get_length();
      30         [ +  + ]:           6 :         if (val >= numFds) {
      31                 :           4 :             throw new Error(`handle ${val} is out of range of Gio.UnixFDList ` +
      32                 :           2 :                 `containing ${numFds} FDs`);
      33                 :             :         }
      34                 :           4 :         return;
      35                 :             :     }
      36                 :             :     case 'v':
      37                 :           0 :         _validateFDVariant(variant.get_variant(), fdList);
      38                 :           0 :         return;
      39                 :           0 :     case 'm': {
      40                 :           0 :         let val = variant.get_maybe();
      41         [ #  # ]:           0 :         if (val)
      42                 :           0 :             _validateFDVariant(val, fdList);
      43                 :           0 :         return;
      44                 :             :     }
      45                 :             :     case 'a':
      46                 :             :     case '(':
      47                 :           6 :     case '{': {
      48                 :           6 :         let nElements = variant.n_children();
      49         [ +  + ]:          10 :         for (let ix = 0; ix < nElements; ix++)
      50                 :           6 :             _validateFDVariant(variant.get_child_value(ix), fdList);
      51                 :           4 :         return;
      52                 :             :     }
      53                 :             :     }
      54                 :             : 
      55                 :           0 :     throw new Error('Assertion failure: this code should not be reached');
      56                 :           8 : }
      57                 :             : 
      58                 :          53 : function _proxyInvoker(methodName, sync, inSignature, argArray) {
      59                 :             :     var replyFunc;
      60                 :          53 :     var flags = 0;
      61                 :          53 :     var cancellable = null;
      62                 :          53 :     let fdList = null;
      63                 :             : 
      64                 :             :     /* Convert argArray to a *real* array */
      65                 :          53 :     argArray = Array.prototype.slice.call(argArray);
      66                 :             : 
      67                 :             :     /* The default replyFunc only logs the responses */
      68                 :          53 :     replyFunc = _logReply;
      69                 :             : 
      70                 :          53 :     var signatureLength = inSignature.length;
      71                 :          53 :     var minNumberArgs = signatureLength;
      72                 :          53 :     var maxNumberArgs = signatureLength + 4;
      73                 :             : 
      74         [ +  - ]:          53 :     if (argArray.length < minNumberArgs) {
      75                 :           0 :         throw new Error(`Not enough arguments passed for method: ${
      76                 :           0 :             methodName}. Expected ${minNumberArgs}, got ${argArray.length}`);
      77         [ +  - ]:          53 :     } else if (argArray.length > maxNumberArgs) {
      78                 :           0 :         throw new Error(`Too many arguments passed for method ${methodName}. ` +
      79                 :           0 :             `Maximum is ${maxNumberArgs} including one callback, ` +
      80                 :           0 :             'Gio.Cancellable, Gio.UnixFDList, and/or flags');
      81                 :             :     }
      82                 :             : 
      83         [ +  + ]:         113 :     while (argArray.length > signatureLength) {
      84                 :          59 :         var argNum = argArray.length - 1;
      85                 :          59 :         var arg = argArray.pop();
      86 [ +  + ][ +  + ]:          59 :         if (typeof arg === 'function' && !sync) {
      87                 :          53 :             replyFunc = arg;
      88         [ +  - ]:           6 :         } else if (typeof arg === 'number') {
      89                 :           0 :             flags = arg;
      90         [ +  - ]:           6 :         } else if (arg instanceof Gio.Cancellable) {
      91                 :           0 :             cancellable = arg;
      92         [ -  + ]:           6 :         } else if (arg instanceof Gio.UnixFDList) {
      93                 :           6 :             fdList = arg;
      94                 :             :         } else {
      95                 :           0 :             throw new Error(`Argument ${argNum} of method ${methodName} is ` +
      96                 :           0 :                 `${typeof arg}. It should be a callback, flags, ` +
      97                 :           0 :                 'Gio.UnixFDList, or a Gio.Cancellable');
      98                 :             :         }
      99                 :             :     }
     100                 :             : 
     101                 :          53 :     const inTypeString = `(${inSignature.join('')})`;
     102                 :          53 :     const inVariant = new GLib.Variant(inTypeString, argArray);
     103         [ +  + ]:          53 :     if (inTypeString.includes('h')) {
     104         [ +  + ]:           8 :         if (!fdList) {
     105                 :           4 :             throw new Error(`Method ${methodName} with input type containing ` +
     106                 :           2 :                 '\'h\' must have a Gio.UnixFDList as an argument');
     107                 :             :         }
     108                 :           6 :         _validateFDVariant(inVariant, fdList);
     109                 :             :     }
     110                 :             : 
     111                 :          49 :     var asyncCallback = (proxy, result) => {
     112                 :          49 :         try {
     113 [ +  - ][ +  - ]:          42 :             const [outVariant, outFdList] =
         [ +  - ][ +  - ]
     114                 :          49 :                 proxy.call_with_unix_fd_list_finish(result);
     115                 :          42 :             replyFunc(outVariant.deepUnpack(), null, outFdList);
     116                 :           7 :         } catch (e) {
     117                 :           7 :             replyFunc([], e, null);
     118                 :             :         }
     119                 :             :     };
     120                 :             : 
     121         [ +  - ]:          49 :     if (sync) {
     122 [ #  # ][ #  # ]:           0 :         const [outVariant, outFdList] = this.call_with_unix_fd_list_sync(
         [ #  # ][ #  # ]
     123                 :           0 :             methodName, inVariant, flags, -1, fdList, cancellable);
     124         [ #  # ]:           0 :         if (fdList)
     125                 :           0 :             return [outVariant.deepUnpack(), outFdList];
     126                 :           0 :         return outVariant.deepUnpack();
     127                 :             :     }
     128                 :             : 
     129                 :          98 :     return this.call_with_unix_fd_list(methodName, inVariant, flags, -1, fdList,
     130                 :          49 :         cancellable, asyncCallback);
     131                 :          49 : }
     132                 :             : 
     133                 :           0 : function _logReply(result, exc) {
     134         [ #  # ]:           0 :     if (exc !== null)
     135                 :           0 :         log(`Ignored exception from dbus method: ${exc}`);
     136                 :             : }
     137                 :             : 
     138                 :         352 : function _makeProxyMethod(method, sync) {
     139                 :             :     var i;
     140                 :         352 :     var name = method.name;
     141                 :         352 :     var inArgs = method.in_args;
     142                 :         352 :     var inSignature = [];
     143         [ +  + ]:         624 :     for (i = 0; i < inArgs.length; i++)
     144                 :         272 :         inSignature.push(inArgs[i].signature);
     145                 :             : 
     146                 :         405 :     return function (...args) {
     147                 :          53 :         return _proxyInvoker.call(this, name, sync, inSignature, args);
     148                 :             :     };
     149                 :             : }
     150                 :             : 
     151                 :           6 : function _convertToNativeSignal(proxy, senderName, signalName, parameters) {
     152                 :           6 :     Signals._emit.call(proxy, signalName, senderName, parameters.deepUnpack());
     153                 :             : }
     154                 :             : 
     155                 :           6 : function _propertyGetter(name) {
     156                 :           6 :     let value = this.get_cached_property(name);
     157         [ -  + ]:           6 :     return value ? value.deepUnpack() : null;
     158                 :           6 : }
     159                 :             : 
     160                 :           2 : function _propertySetter(name, signature, value) {
     161                 :           2 :     let variant = new GLib.Variant(signature, value);
     162                 :           2 :     this.set_cached_property(name, variant);
     163                 :             : 
     164                 :           4 :     this.call('org.freedesktop.DBus.Properties.Set',
     165                 :           2 :         new GLib.Variant('(ssv)', [this.g_interface_name, name, variant]),
     166                 :           2 :         Gio.DBusCallFlags.NONE, -1, null,
     167                 :           2 :         (proxy, result) => {
     168                 :           2 :             try {
     169                 :           2 :                 this.call_finish(result);
     170                 :           0 :             } catch (e) {
     171                 :           0 :                 log(`Could not set property ${name} on remote object ${
     172                 :           0 :                     this.g_object_path}: ${e.message}`);
     173                 :             :             }
     174                 :             :         });
     175                 :             : }
     176                 :             : 
     177                 :           8 : function _addDBusConvenience() {
     178                 :           8 :     let info = this.g_interface_info;
     179         [ +  - ]:           8 :     if (!info)
     180                 :           0 :         return;
     181                 :             : 
     182         [ -  + ]:           8 :     if (info.signals.length > 0)
     183                 :           8 :         this.connect('g-signal', _convertToNativeSignal);
     184                 :             : 
     185                 :           8 :     let i, methods = info.methods;
     186         [ +  + ]:         184 :     for (i = 0; i < methods.length; i++) {
     187                 :         176 :         var method = methods[i];
     188                 :         176 :         let remoteMethod = _makeProxyMethod(methods[i], false);
     189                 :         176 :         this[`${method.name}Remote`] = remoteMethod;
     190                 :         176 :         this[`${method.name}Sync`] = _makeProxyMethod(methods[i], true);
     191                 :         202 :         this[`${method.name}Async`] = function (...args) {
     192                 :          26 :             return new Promise((resolve, reject) => {
     193                 :          26 :                 args.push((result, error, fdList) => {
     194         [ +  + ]:          24 :                     if (error)
     195                 :           3 :                         reject(error);
     196         [ +  + ]:          21 :                     else if (fdList)
     197                 :           2 :                         resolve([result, fdList]);
     198                 :             :                     else
     199                 :          19 :                         resolve(result);
     200                 :             :                 });
     201         [ +  + ]:          26 :                 remoteMethod.call(this, ...args);
     202                 :             :             });
     203                 :             :         };
     204                 :             :     }
     205                 :             : 
     206                 :           8 :     let properties = info.properties;
     207         [ +  + ]:          40 :     for (i = 0; i < properties.length; i++) {
     208                 :          32 :         let name = properties[i].name;
     209                 :          32 :         let signature = properties[i].signature;
     210                 :          32 :         let flags = properties[i].flags;
     211                 :          32 :         let getter = () => {
     212                 :           2 :             throw new Error(`Property ${name} is not readable`);
     213                 :             :         };
     214                 :          32 :         let setter = () => {
     215                 :           1 :             throw new Error(`Property ${name} is not writable`);
     216                 :             :         };
     217                 :             : 
     218         [ +  + ]:          32 :         if (flags & Gio.DBusPropertyInfoFlags.READABLE)
     219                 :          24 :             getter = _propertyGetter.bind(this, name);
     220                 :             : 
     221         [ +  + ]:          32 :         if (flags & Gio.DBusPropertyInfoFlags.WRITABLE)
     222                 :          16 :             setter = _propertySetter.bind(this, name, signature);
     223                 :             : 
     224                 :          64 :         Object.defineProperty(this, name, {
     225                 :          32 :             get: getter,
     226                 :          32 :             set: setter,
     227                 :          32 :             configurable: false,
     228                 :          32 :             enumerable: true,
     229                 :             :         });
     230                 :             :     }
     231                 :           0 : }
     232                 :             : 
     233                 :           1 : function _makeProxyWrapper(interfaceXml) {
     234                 :           1 :     var info = _newInterfaceInfo(interfaceXml);
     235                 :           1 :     var iname = info.name;
     236         [ +  + ]:           4 :     function wrapper(bus, name, object, asyncCallback, cancellable,
     237                 :           3 :         flags = Gio.DBusProxyFlags.NONE) {
     238                 :           8 :         var obj = new Gio.DBusProxy({
     239                 :           4 :             g_connection: bus,
     240                 :           4 :             g_interface_name: iname,
     241                 :           4 :             g_interface_info: info,
     242                 :           4 :             g_name: name,
     243                 :           4 :             g_flags: flags,
     244                 :           4 :             g_object_path: object,
     245                 :             :         });
     246                 :             : 
     247         [ +  + ]:           4 :         if (!cancellable)
     248                 :           2 :             cancellable = null;
     249         [ +  + ]:           4 :         if (asyncCallback) {
     250                 :           3 :             obj.init_async(GLib.PRIORITY_DEFAULT, cancellable).then(
     251                 :           6 :                 () => asyncCallback(obj, null)).catch(e => asyncCallback(null, e));
     252                 :             :         } else {
     253                 :           1 :             obj.init(cancellable);
     254                 :             :         }
     255                 :           3 :         return obj;
     256                 :             :     }
     257         [ -  + ]:           4 :     wrapper.newAsync = function newAsync(bus, name, object, cancellable,
     258                 :           3 :         flags = Gio.DBusProxyFlags.NONE) {
     259                 :           6 :         const obj = new Gio.DBusProxy({
     260                 :           3 :             g_connection: bus,
     261                 :           3 :             g_interface_name: info.name,
     262                 :           3 :             g_interface_info: info,
     263                 :           3 :             g_name: name,
     264                 :           3 :             g_flags: flags,
     265                 :           3 :             g_object_path: object,
     266                 :             :         });
     267                 :             : 
     268                 :           3 :         return new Promise((resolve, reject) =>
     269         [ +  + ]:           3 :             obj.init_async(GLib.PRIORITY_DEFAULT, cancellable ?? null).then(
     270                 :           5 :                 () => resolve(obj)).catch(reject));
     271                 :           3 :     };
     272                 :           1 :     return wrapper;
     273                 :             : }
     274                 :             : 
     275                 :             : 
     276                 :           3 : function _newNodeInfo(constructor, value) {
     277         [ -  + ]:           3 :     if (typeof value === 'string')
     278                 :           3 :         return constructor(value);
     279                 :           0 :     throw TypeError(`Invalid type ${Object.prototype.toString.call(value)}`);
     280                 :             : }
     281                 :             : 
     282                 :           2 : function _newInterfaceInfo(value) {
     283                 :           2 :     var nodeInfo = Gio.DBusNodeInfo.new_for_xml(value);
     284                 :           2 :     return nodeInfo.interfaces[0];
     285                 :             : }
     286                 :             : 
     287                 :          40 : function _injectToMethod(klass, method, addition) {
     288                 :          40 :     var previous = klass[method];
     289                 :             : 
     290                 :          47 :     klass[method] = function (...args) {
     291                 :           7 :         addition.apply(this, args);
     292                 :           7 :         return previous.apply(this, args);
     293                 :             :     };
     294                 :             : }
     295                 :             : 
     296                 :          80 : function _injectToStaticMethod(klass, method, addition) {
     297                 :          80 :     var previous = klass[method];
     298                 :             : 
     299                 :          81 :     klass[method] = function (...parameters) {
     300                 :           1 :         let obj = previous.apply(this, parameters);
     301                 :           1 :         addition.apply(obj, parameters);
     302                 :           1 :         return obj;
     303                 :           1 :     };
     304                 :             : }
     305                 :             : 
     306                 :          20 : function _wrapFunction(klass, method, addition) {
     307                 :          20 :     var previous = klass[method];
     308                 :             : 
     309                 :          23 :     klass[method] = function (...args) {
     310                 :           3 :         args.unshift(previous);
     311                 :           3 :         return addition.apply(this, args);
     312                 :             :     };
     313                 :             : }
     314                 :             : 
     315                 :          34 : function _makeOutSignature(args) {
     316                 :          34 :     var ret = '(';
     317         [ +  + ]:          74 :     for (var i = 0; i < args.length; i++)
     318                 :          40 :         ret += args[i].signature;
     319                 :             : 
     320                 :          34 :     return `${ret})`;
     321                 :             : }
     322                 :             : 
     323                 :          49 : function _handleMethodCall(info, impl, methodName, parameters, invocation) {
     324                 :             :     // prefer a sync version if available
     325         [ +  + ]:          49 :     if (this[methodName]) {
     326                 :          41 :         let retval;
     327                 :          41 :         try {
     328                 :          41 :             const fdList = invocation.get_message().get_unix_fd_list();
     329         [ +  + ]:          41 :             retval = this[methodName](...parameters.deepUnpack(), fdList);
     330                 :           3 :         } catch (e) {
     331         [ +  - ]:           3 :             if (e instanceof GLib.Error) {
     332                 :           0 :                 invocation.return_gerror(e);
     333                 :           3 :             } else {
     334                 :           3 :                 let name = e.name;
     335         [ -  + ]:           3 :                 if (!name.includes('.')) {
     336                 :             :                     // likely to be a normal JS error
     337                 :           3 :                     name = `org.gnome.gjs.JSError.${name}`;
     338                 :             :                 }
     339                 :           3 :                 logError(e, `Exception in method call: ${methodName}`);
     340                 :           3 :                 invocation.return_dbus_error(name, e.message);
     341                 :             :             }
     342                 :           3 :             return;
     343                 :             :         }
     344         [ +  + ]:          38 :         if (retval === undefined) {
     345                 :             :             // undefined (no return value) is the empty tuple
     346                 :           4 :             retval = new GLib.Variant('()', []);
     347                 :             :         }
     348                 :          38 :         try {
     349                 :          38 :             let outFdList = null;
     350         [ +  + ]:          38 :             if (!(retval instanceof GLib.Variant)) {
     351                 :             :                 // attempt packing according to out signature
     352                 :          34 :                 let methodInfo = info.lookup_method(methodName);
     353                 :          34 :                 let outArgs = methodInfo.out_args;
     354                 :          34 :                 let outSignature = _makeOutSignature(outArgs);
     355         [ +  + ]:          34 :                 if (outSignature.includes('h') &&
     356         [ +  + ]:           2 :                     retval[retval.length - 1] instanceof Gio.UnixFDList) {
     357                 :           2 :                     outFdList = retval.pop();
     358         [ +  + ]:          32 :                 } else if (outArgs.length === 1) {
     359                 :             :                     // if one arg, we don't require the handler wrapping it
     360                 :             :                     // into an Array
     361                 :          28 :                     retval = [retval];
     362                 :             :                 }
     363                 :          34 :                 retval = new GLib.Variant(outSignature, retval);
     364                 :             :             }
     365                 :          36 :             invocation.return_value_with_unix_fd_list(retval, outFdList);
     366                 :           2 :         } catch (e) {
     367                 :             :             // if we don't do this, the other side will never see a reply
     368                 :           4 :             invocation.return_dbus_error('org.gnome.gjs.JSError.ValueError',
     369                 :           2 :                 'Service implementation returned an incorrect value type');
     370                 :             :         }
     371         [ +  + ]:           8 :     } else if (this[`${methodName}Async`]) {
     372                 :           6 :         const fdList = invocation.get_message().get_unix_fd_list();
     373                 :           6 :         this[`${methodName}Async`](parameters.deepUnpack(), invocation, fdList);
     374                 :             :     } else {
     375                 :           2 :         log(`Missing handler for DBus method ${methodName}`);
     376                 :           4 :         invocation.return_gerror(new Gio.DBusError({
     377                 :           2 :             code: Gio.DBusError.UNKNOWN_METHOD,
     378                 :           2 :             message: `Method ${methodName} is not implemented`,
     379                 :             :         }));
     380                 :             :     }
     381                 :           3 : }
     382                 :             : 
     383                 :           9 : function _handlePropertyGet(info, impl, propertyName) {
     384                 :           9 :     let propInfo = info.lookup_property(propertyName);
     385                 :           9 :     let jsval = this[propertyName];
     386 [ -  + ][ +  + ]:           9 :     if (jsval?.get_type_string?.() === propInfo.signature)
                 [ +  + ]
     387                 :           3 :         return jsval;
     388         [ -  + ]:           6 :     else if (jsval !== undefined)
     389                 :           6 :         return new GLib.Variant(propInfo.signature, jsval);
     390                 :             :     else
     391                 :           0 :         return null;
     392                 :           9 : }
     393                 :             : 
     394                 :           2 : function _handlePropertySet(info, impl, propertyName, newValue) {
     395                 :           2 :     this[propertyName] = newValue.deepUnpack();
     396                 :             : }
     397                 :             : 
     398                 :           1 : function _wrapJSObject(interfaceInfo, jsObj) {
     399                 :             :     var info;
     400         [ +  - ]:           1 :     if (interfaceInfo instanceof Gio.DBusInterfaceInfo)
     401                 :           0 :         info = interfaceInfo;
     402                 :             :     else
     403                 :           1 :         info = Gio.DBusInterfaceInfo.new_for_xml(interfaceInfo);
     404                 :           1 :     info.cache_build();
     405                 :             : 
     406                 :           1 :     var impl = new GjsPrivate.DBusImplementation({g_interface_info: info});
     407                 :           1 :     impl.connect('handle-method-call', function (self, methodName, parameters, invocation) {
     408                 :          49 :         return _handleMethodCall.call(jsObj, info, self, methodName, parameters, invocation);
     409                 :             :     });
     410                 :           1 :     impl.connect('handle-property-get', function (self, propertyName) {
     411                 :           9 :         return _handlePropertyGet.call(jsObj, info, self, propertyName);
     412                 :             :     });
     413                 :           1 :     impl.connect('handle-property-set', function (self, propertyName, value) {
     414                 :           2 :         return _handlePropertySet.call(jsObj, info, self, propertyName, value);
     415                 :             :     });
     416                 :             : 
     417                 :           1 :     return impl;
     418                 :             : }
     419                 :             : 
     420                 :           3 : function* _listModelIterator() {
     421                 :           3 :     let _index = 0;
     422                 :           3 :     const _len = this.get_n_items();
     423         [ +  + ]:         124 :     while (_index < _len)
     424                 :         120 :         yield this.get_item(_index++);
     425                 :           3 : }
     426                 :             : 
     427         [ +  + ]:          27 : function _promisify(proto, asyncFunc, finishFunc = undefined) {
     428         [ +  + ]:          27 :     if (proto[asyncFunc] === undefined)
     429                 :           1 :         throw new Error(`${proto} has no method named ${asyncFunc}`);
     430                 :             : 
     431         [ +  + ]:          26 :     if (finishFunc === undefined) {
     432 [ -  + ][ +  + ]:          25 :         if (asyncFunc.endsWith('_begin') || asyncFunc.endsWith('_async'))
     433                 :          24 :             finishFunc = `${asyncFunc.slice(0, -5)}finish`;
     434                 :             :         else
     435                 :           1 :             finishFunc = `${asyncFunc}_finish`;
     436                 :             :     }
     437                 :             : 
     438         [ +  + ]:          26 :     if (proto[finishFunc] === undefined)
     439                 :           1 :         throw new Error(`${proto} has no method named ${finishFunc}`);
     440                 :             : 
     441                 :          25 :     const originalFuncName = `_original_${asyncFunc}`;
     442         [ +  + ]:          25 :     if (proto[originalFuncName] !== undefined)
     443                 :           2 :         return;
     444                 :          23 :     proto[originalFuncName] = proto[asyncFunc];
     445                 :          31 :     proto[asyncFunc] = function (...args) {
     446         [ +  + ]:           8 :         if (args.length === this[originalFuncName].length)
     447 [ +  - ][ #  # ]:           1 :             return this[originalFuncName](...args);
     448                 :          14 :         return new Promise((resolve, reject) => {
     449                 :           7 :             let {stack: callStack} = new Error();
     450         [ +  + ]:           7 :             this[originalFuncName](...args, function (source, res) {
     451                 :           7 :                 try {
     452 [ -  + ][ -  + ]:           7 :                     const result = source !== null && source[finishFunc] !== undefined
     453                 :           7 :                         ? source[finishFunc](res)
     454                 :           0 :                         : proto[finishFunc](res);
     455 [ +  - ][ #  # ]:           5 :                     if (Array.isArray(result) && result.length > 1 && result[0] === true)
                 [ +  - ]
     456                 :           0 :                         result.shift();
     457                 :           5 :                     resolve(result);
     458                 :           2 :                 } catch (error) {
     459                 :           2 :                     callStack = callStack.split('\n').filter(line =>
     460                 :          31 :                         line.indexOf('_promisify/') === -1).join('\n');
     461         [ -  + ]:           2 :                     if (error.stack)
     462                 :           2 :                         error.stack += `### Promise created here: ###\n${callStack}`;
     463                 :             :                     else
     464                 :           0 :                         error.stack = callStack;
     465                 :           2 :                     reject(error);
     466                 :             :                 }
     467                 :             :             });
     468                 :             :         });
     469                 :             :     };
     470                 :           2 : }
     471                 :             : 
     472                 :          13 : function _notIntrospectableError(funcName, replacement) {
     473                 :          13 :     return new Error(`${funcName} is not introspectable. Use ${replacement} instead.`);
     474                 :             : }
     475                 :             : 
     476                 :          10 : function _warnNotIntrospectable(funcName, replacement) {
     477                 :          10 :     logError(_notIntrospectableError(funcName, replacement));
     478                 :             : }
     479                 :             : 
     480                 :          20 : function _init() {
     481                 :          20 :     Gio = this;
     482                 :             : 
     483                 :          20 :     Gio.Application.prototype.runAsync = GLib.MainLoop.prototype.runAsync;
     484                 :             : 
     485                 :          20 :     Gio.DBus = {
     486                 :             :         // Namespace some functions
     487                 :          20 :         get: Gio.bus_get,
     488                 :          20 :         get_finish: Gio.bus_get_finish,
     489                 :          20 :         get_sync: Gio.bus_get_sync,
     490                 :             : 
     491                 :          20 :         own_name: Gio.bus_own_name,
     492                 :          20 :         own_name_on_connection: Gio.bus_own_name_on_connection,
     493                 :          20 :         unown_name: Gio.bus_unown_name,
     494                 :             : 
     495                 :          20 :         watch_name: Gio.bus_watch_name,
     496                 :          20 :         watch_name_on_connection: Gio.bus_watch_name_on_connection,
     497                 :          20 :         unwatch_name: Gio.bus_unwatch_name,
     498                 :             :     };
     499                 :             : 
     500                 :          40 :     Object.defineProperties(Gio.DBus, {
     501                 :          20 :         'session': {
     502                 :          20 :             get() {
     503                 :          10 :                 return Gio.bus_get_sync(Gio.BusType.SESSION, null);
     504                 :             :             },
     505                 :          20 :             enumerable: false,
     506                 :             :         },
     507                 :          20 :         'system': {
     508                 :          20 :             get() {
     509                 :           0 :                 return Gio.bus_get_sync(Gio.BusType.SYSTEM, null);
     510                 :             :             },
     511                 :          20 :             enumerable: false,
     512                 :             :         },
     513                 :             :     });
     514                 :             : 
     515                 :          20 :     Gio.DBusConnection.prototype.watch_name = function (name, flags, appeared, vanished) {
     516                 :           0 :         return Gio.bus_watch_name_on_connection(this, name, flags, appeared, vanished);
     517                 :             :     };
     518                 :          20 :     Gio.DBusConnection.prototype.unwatch_name = function (id) {
     519                 :           0 :         return Gio.bus_unwatch_name(id);
     520                 :             :     };
     521                 :          20 :     Gio.DBusConnection.prototype.own_name = function (name, flags, acquired, lost) {
     522                 :           1 :         return Gio.bus_own_name_on_connection(this, name, flags, acquired, lost);
     523                 :             :     };
     524                 :          20 :     Gio.DBusConnection.prototype.unown_name = function (id) {
     525                 :           1 :         return Gio.bus_unown_name(id);
     526                 :             :     };
     527                 :             : 
     528                 :          20 :     _injectToMethod(Gio.DBusProxy.prototype, 'init', _addDBusConvenience);
     529                 :          20 :     _promisify(Gio.DBusProxy.prototype, 'init_async');
     530                 :          20 :     _injectToMethod(Gio.DBusProxy.prototype, 'init_async', _addDBusConvenience);
     531                 :          20 :     _injectToStaticMethod(Gio.DBusProxy, 'new_sync', _addDBusConvenience);
     532                 :          20 :     _injectToStaticMethod(Gio.DBusProxy, 'new_finish', _addDBusConvenience);
     533                 :          20 :     _injectToStaticMethod(Gio.DBusProxy, 'new_for_bus_sync', _addDBusConvenience);
     534                 :          20 :     _injectToStaticMethod(Gio.DBusProxy, 'new_for_bus_finish', _addDBusConvenience);
     535                 :          20 :     Gio.DBusProxy.prototype.connectSignal = Signals._connect;
     536                 :          20 :     Gio.DBusProxy.prototype.disconnectSignal = Signals._disconnect;
     537                 :             : 
     538                 :          20 :     Gio.DBusProxy.makeProxyWrapper = _makeProxyWrapper;
     539                 :             : 
     540                 :             :     // Some helpers
     541                 :          20 :     _wrapFunction(Gio.DBusNodeInfo, 'new_for_xml', _newNodeInfo);
     542                 :          20 :     Gio.DBusInterfaceInfo.new_for_xml = _newInterfaceInfo;
     543                 :             : 
     544                 :          20 :     Gio.DBusExportedObject = GjsPrivate.DBusImplementation;
     545                 :          20 :     Gio.DBusExportedObject.wrapJSObject = _wrapJSObject;
     546                 :             : 
     547                 :             :     // ListStore
     548                 :          20 :     Gio.ListStore.prototype[Symbol.iterator] = _listModelIterator;
     549                 :          20 :     Gio.ListStore.prototype.insert_sorted = function (item, compareFunc) {
     550                 :          10 :         return GjsPrivate.list_store_insert_sorted(this, item, compareFunc);
     551                 :             :     };
     552                 :          20 :     Gio.ListStore.prototype.sort = function (compareFunc) {
     553                 :           1 :         return GjsPrivate.list_store_sort(this, compareFunc);
     554                 :             :     };
     555                 :             : 
     556                 :             :     // Promisify
     557                 :          20 :     Gio._promisify = _promisify;
     558                 :             : 
     559                 :             :     // Temporary Gio.File.prototype fix
     560                 :          20 :     Gio._LocalFilePrototype = Gio.File.new_for_path('/').constructor.prototype;
     561                 :             : 
     562                 :          20 :     Gio.File.prototype.replace_contents_async = function replace_contents_async(contents, etag, make_backup, flags, cancellable, callback) {
     563                 :           0 :         return this.replace_contents_bytes_async(contents, etag, make_backup, flags, cancellable, callback);
     564                 :             :     };
     565                 :             : 
     566                 :             :     // Best-effort attempt to replace set_attribute(), which is not
     567                 :             :     // introspectable due to the pointer argument
     568                 :          20 :     Gio.File.prototype.set_attribute = function set_attribute(attribute, type, value, flags, cancellable) {
     569                 :           5 :         _warnNotIntrospectable('Gio.File.prototype.set_attribute', 'set_attribute_{type}');
     570                 :             : 
     571                 :           5 :         switch (type) {
     572         [ -  + ]:           5 :         case Gio.FileAttributeType.STRING:
     573                 :           0 :             return this.set_attribute_string(attribute, value, flags, cancellable);
     574         [ -  + ]:           5 :         case Gio.FileAttributeType.BYTE_STRING:
     575                 :           0 :             return this.set_attribute_byte_string(attribute, value, flags, cancellable);
     576         [ +  + ]:           5 :         case Gio.FileAttributeType.UINT32:
     577                 :           1 :             return this.set_attribute_uint32(attribute, value, flags, cancellable);
     578         [ -  + ]:           4 :         case Gio.FileAttributeType.INT32:
     579                 :           0 :             return this.set_attribute_int32(attribute, value, flags, cancellable);
     580         [ +  + ]:           4 :         case Gio.FileAttributeType.UINT64:
     581                 :           1 :             return this.set_attribute_uint64(attribute, value, flags, cancellable);
     582         [ -  + ]:           3 :         case Gio.FileAttributeType.INT64:
     583                 :           0 :             return this.set_attribute_int64(attribute, value, flags, cancellable);
     584         [ +  + ]:           3 :         case Gio.FileAttributeType.INVALID:
     585         [ +  + ]:           2 :         case Gio.FileAttributeType.BOOLEAN:
     586         [ +  - ]:           1 :         case Gio.FileAttributeType.OBJECT:
     587         [ #  # ]:           0 :         case Gio.FileAttributeType.STRINGV:
     588                 :           3 :             throw _notIntrospectableError('This attribute type', 'Gio.FileInfo');
     589                 :             :         }
     590                 :             :     };
     591                 :             : 
     592                 :          20 :     Gio.FileInfo.prototype.set_attribute = function set_attribute(attribute, type, value) {
     593                 :           5 :         _warnNotIntrospectable('Gio.FileInfo.prototype.set_attribute', 'set_attribute_{type}');
     594                 :             : 
     595                 :           5 :         switch (type) {
     596         [ +  + ]:           5 :         case Gio.FileAttributeType.INVALID:
     597                 :           1 :             return this.remove_attribute(attribute);
     598         [ -  + ]:           4 :         case Gio.FileAttributeType.STRING:
     599                 :           0 :             return this.set_attribute_string(attribute, value);
     600         [ -  + ]:           4 :         case Gio.FileAttributeType.BYTE_STRING:
     601                 :           0 :             return this.set_attribute_byte_string(attribute, value);
     602         [ +  + ]:           4 :         case Gio.FileAttributeType.BOOLEAN:
     603                 :           1 :             return this.set_attribute_boolean(attribute, value);
     604         [ +  + ]:           3 :         case Gio.FileAttributeType.UINT32:
     605                 :           1 :             return this.set_attribute_uint32(attribute, value);
     606         [ -  + ]:           2 :         case Gio.FileAttributeType.INT32:
     607                 :           0 :             return this.set_attribute_int32(attribute, value);
     608         [ +  + ]:           2 :         case Gio.FileAttributeType.UINT64:
     609                 :           1 :             return this.set_attribute_uint64(attribute, value);
     610         [ -  + ]:           1 :         case Gio.FileAttributeType.INT64:
     611                 :           0 :             return this.set_attribute_int64(attribute, value);
     612         [ +  - ]:           1 :         case Gio.FileAttributeType.OBJECT:
     613                 :           1 :             return this.set_attribute_object(attribute, value);
     614         [ #  # ]:           0 :         case Gio.FileAttributeType.STRINGV:
     615                 :           0 :             return this.set_attribute_stringv(attribute, value);
     616                 :             :         }
     617                 :             :     };
     618                 :             : 
     619                 :          21 :     Gio.InputStream.prototype.createSyncIterator = function* createSyncIterator(count) {
     620         [ -  + ]:           9 :         while (true) {
     621                 :           9 :             const bytes = this.read_bytes(count, null);
     622         [ +  + ]:           9 :             if (bytes.get_size() === 0)
     623                 :           1 :                 return;
     624                 :           8 :             yield bytes;
     625                 :             :         }
     626                 :           1 :     };
     627                 :             : 
     628         [ -  + ]:          21 :     Gio.InputStream.prototype.createAsyncIterator = async function* createAsyncIterator(
     629                 :           1 :         count, ioPriority = GLib.PRIORITY_DEFAULT) {
     630                 :           1 :         const self = this;
     631                 :             : 
     632                 :             :         function next() {
     633                 :           9 :             return new Promise((resolve, reject) => {
     634                 :           9 :                 self.read_bytes_async(count, ioPriority, null, (_self, res) => {
     635                 :           9 :                     try {
     636                 :           9 :                         const bytes = self.read_bytes_finish(res);
     637                 :           9 :                         resolve(bytes);
     638                 :           0 :                     } catch (err) {
     639                 :           0 :                         reject(err);
     640                 :             :                     }
     641                 :             :                 });
     642                 :             :             });
     643                 :             :         }
     644                 :             : 
     645         [ -  + ]:           9 :         while (true) {
     646                 :             :             // eslint-disable-next-line no-await-in-loop
     647         [ -  + ]:           9 :             const bytes = await next(count);
     648         [ +  + ]:           9 :             if (bytes.get_size() === 0)
     649                 :           1 :                 return;
     650         [ -  + ]:           8 :             yield bytes;
     651                 :             :         }
     652                 :           1 :     };
     653                 :             : 
     654                 :          21 :     Gio.FileEnumerator.prototype[Symbol.iterator] = function* FileEnumeratorIterator() {
     655         [ +  + ]:          33 :         while (true) {
     656                 :          32 :             try {
     657                 :          32 :                 const info = this.next_file(null);
     658         [ +  + ]:          32 :                 if (info === null)
     659                 :           1 :                     break;
     660                 :          31 :                 yield info;
     661                 :           0 :             } catch (err) {
     662                 :           0 :                 this.close(null);
     663                 :           0 :                 throw err;
     664                 :             :             }
     665                 :             :         }
     666                 :           1 :         this.close(null);
     667                 :           1 :     };
     668                 :             : 
     669                 :          21 :     Gio.FileEnumerator.prototype[Symbol.asyncIterator] = async function* AsyncFileEnumeratorIterator() {
     670                 :           1 :         const self = this;
     671                 :             : 
     672                 :          41 :         function next() {
     673                 :          32 :             return new Promise((resolve, reject) => {
     674                 :          32 :                 self.next_files_async(1, GLib.PRIORITY_DEFAULT, null, (_self, res) => {
     675                 :          32 :                     try {
     676                 :          32 :                         const files = self.next_files_finish(res);
     677         [ +  + ]:          32 :                         resolve(files.length === 0 ? null : files[0]);
     678                 :           0 :                     } catch (err) {
     679                 :           0 :                         reject(err);
     680                 :             :                     }
     681                 :             :                 });
     682                 :             :             });
     683                 :             :         }
     684                 :             : 
     685                 :           1 :         function close() {
     686                 :           1 :             return new Promise((resolve, reject) => {
     687                 :           1 :                 self.close_async(GLib.PRIORITY_DEFAULT, null, (_self, res) => {
     688                 :           1 :                     try {
     689                 :           1 :                         resolve(self.close_finish(res));
     690                 :           0 :                     } catch (err) {
     691                 :           0 :                         reject(err);
     692                 :             :                     }
     693                 :             :                 });
     694                 :             :             });
     695                 :             :         }
     696                 :             : 
     697         [ +  + ]:          33 :         while (true) {
     698                 :          32 :             try {
     699                 :             :                 // eslint-disable-next-line no-await-in-loop
     700         [ -  + ]:          32 :                 const info = await next();
     701         [ +  + ]:          32 :                 if (info === null)
     702                 :           1 :                     break;
     703         [ -  + ]:          31 :                 yield info;
     704                 :           0 :             } catch (err) {
     705                 :             :                 // eslint-disable-next-line no-await-in-loop
     706         [ #  # ]:           0 :                 await close();
     707                 :           0 :                 throw err;
     708                 :             :             }
     709                 :             :         }
     710                 :             : 
     711         [ -  + ]:           1 :         return close();
     712                 :           1 :     };
     713                 :             : 
     714                 :             :     // Override Gio.Settings and Gio.SettingsSchema - the C API asserts if
     715                 :             :     // trying to access a nonexistent schema or key, which is not handy for
     716                 :             :     // shell-extension writers
     717                 :             : 
     718                 :          20 :     Gio.SettingsSchema.prototype._realGetKey = Gio.SettingsSchema.prototype.get_key;
     719                 :          20 :     Gio.SettingsSchema.prototype.get_key = function (key) {
     720         [ +  + ]:           2 :         if (!this.has_key(key))
     721                 :           1 :             throw new Error(`GSettings key ${key} not found in schema ${this.get_id()}`);
     722                 :           1 :         return this._realGetKey(key);
     723                 :             :     };
     724                 :             : 
     725                 :          20 :     Gio.Settings.prototype._realMethods = Object.assign({}, Gio.Settings.prototype);
     726                 :             : 
     727         [ +  + ]:         600 :     function createCheckedMethod(method, checkMethod = '_checkKey') {
     728                 :         657 :         return function (id, ...args) {
     729                 :          57 :             this[checkMethod](id);
     730         [ +  + ]:          27 :             return this._realMethods[method].call(this, id, ...args);
     731                 :             :         };
     732                 :             :     }
     733                 :             : 
     734                 :          40 :     Object.assign(Gio.Settings.prototype, {
     735                 :          20 :         _realInit: Gio.Settings.prototype._init,  // add manually, not enumerable
     736         [ +  + ]:          37 :         _init(props = {}) {
     737                 :             :             // 'schema' is a deprecated alias for schema_id
     738                 :          17 :             const schemaIdProp = ['schema', 'schema-id', 'schema_id',
     739                 :          85 :                 'schemaId'].find(prop => prop in props);
     740                 :          17 :             const settingsSchemaProp = ['settings-schema', 'settings_schema',
     741                 :          68 :                 'settingsSchema'].find(prop => prop in props);
     742 [ +  + ][ +  + ]:          17 :             if (!schemaIdProp && !settingsSchemaProp) {
     743                 :           1 :                 throw new Error('One of property \'schema-id\' or ' +
     744                 :             :                     '\'settings-schema\' are required for Gio.Settings');
     745                 :             :             }
     746 [ +  + ][ +  - ]:          16 :             if (settingsSchemaProp && !(props[settingsSchemaProp] instanceof Gio.SettingsSchema))
     747                 :           0 :                 throw new Error(`Value of property '${settingsSchemaProp}' is not of type Gio.SettingsSchema`);
     748                 :             : 
     749                 :          16 :             const source = Gio.SettingsSchemaSource.get_default();
     750         [ +  + ]:          16 :             const settingsSchema = settingsSchemaProp
     751                 :           1 :                 ? props[settingsSchemaProp]
     752                 :          15 :                 : source.lookup(props[schemaIdProp], true);
     753                 :             : 
     754         [ +  + ]:          16 :             if (!settingsSchema)
     755                 :           1 :                 throw new Error(`GSettings schema ${props[schemaIdProp]} not found`);
     756                 :             : 
     757                 :          15 :             const settingsSchemaPath = settingsSchema.get_path();
     758 [ +  + ][ +  + ]:          15 :             if (props['path'] === undefined && !settingsSchemaPath) {
     759                 :           2 :                 throw new Error('Attempting to create schema ' +
     760                 :           1 :                     `'${settingsSchema.get_id()}' without a path`);
     761                 :             :             }
     762                 :             : 
     763 [ +  + ][ -  + ]:          14 :             if (props['path'] !== undefined && settingsSchemaPath &&
     764         [ +  + ]:           1 :                 props['path'] !== settingsSchemaPath) {
     765                 :           2 :                 throw new Error(`GSettings created for path '${props['path']}'` +
     766                 :           1 :                     `, but schema specifies '${settingsSchemaPath}'`);
     767                 :             :             }
     768                 :             : 
     769                 :          13 :             return this._realInit(props);
     770                 :          13 :         },
     771                 :             : 
     772                 :          20 :         _checkKey(key) {
     773                 :             :             // Avoid using has_key(); checking a JS array is faster than calling
     774                 :             :             // through G-I.
     775         [ +  + ]:          55 :             if (!this._keys)
     776                 :          11 :                 this._keys = this.settings_schema.list_keys();
     777                 :             : 
     778         [ +  + ]:          55 :             if (!this._keys.includes(key))
     779                 :          29 :                 throw new Error(`GSettings key ${key} not found in schema ${this.schema_id}`);
     780                 :             :         },
     781                 :             : 
     782                 :          20 :         _checkChild(name) {
     783         [ -  + ]:           2 :             if (!this._children)
     784                 :           2 :                 this._children = this.list_children();
     785                 :             : 
     786         [ +  + ]:           2 :             if (!this._children.includes(name))
     787                 :           1 :                 throw new Error(`Child ${name} not found in GSettings schema ${this.schema_id}`);
     788                 :             :         },
     789                 :             : 
     790                 :          20 :         get_boolean: createCheckedMethod('get_boolean'),
     791                 :          20 :         set_boolean: createCheckedMethod('set_boolean'),
     792                 :          20 :         get_double: createCheckedMethod('get_double'),
     793                 :          20 :         set_double: createCheckedMethod('set_double'),
     794                 :          20 :         get_enum: createCheckedMethod('get_enum'),
     795                 :          20 :         set_enum: createCheckedMethod('set_enum'),
     796                 :          20 :         get_flags: createCheckedMethod('get_flags'),
     797                 :          20 :         set_flags: createCheckedMethod('set_flags'),
     798                 :          20 :         get_int: createCheckedMethod('get_int'),
     799                 :          20 :         set_int: createCheckedMethod('set_int'),
     800                 :          20 :         get_int64: createCheckedMethod('get_int64'),
     801                 :          20 :         set_int64: createCheckedMethod('set_int64'),
     802                 :          20 :         get_string: createCheckedMethod('get_string'),
     803                 :          20 :         set_string: createCheckedMethod('set_string'),
     804                 :          20 :         get_strv: createCheckedMethod('get_strv'),
     805                 :          20 :         set_strv: createCheckedMethod('set_strv'),
     806                 :          20 :         get_uint: createCheckedMethod('get_uint'),
     807                 :          20 :         set_uint: createCheckedMethod('set_uint'),
     808                 :          20 :         get_uint64: createCheckedMethod('get_uint64'),
     809                 :          20 :         set_uint64: createCheckedMethod('set_uint64'),
     810                 :          20 :         get_value: createCheckedMethod('get_value'),
     811                 :          20 :         set_value: createCheckedMethod('set_value'),
     812                 :             : 
     813                 :          20 :         bind: createCheckedMethod('bind'),
     814                 :          20 :         bind_writable: createCheckedMethod('bind_writable'),
     815                 :          20 :         create_action: createCheckedMethod('create_action'),
     816                 :          20 :         get_default_value: createCheckedMethod('get_default_value'),
     817                 :          20 :         get_user_value: createCheckedMethod('get_user_value'),
     818                 :          20 :         is_writable: createCheckedMethod('is_writable'),
     819                 :          20 :         reset: createCheckedMethod('reset'),
     820                 :             : 
     821                 :          20 :         get_child: createCheckedMethod('get_child', '_checkChild'),
     822                 :             :     });
     823                 :             : 
     824                 :             :     // ActionMap
     825                 :             :     // add_action_entries is not introspectable
     826                 :             :     // https://gitlab.gnome.org/GNOME/gjs/-/issues/407
     827                 :          20 :     Gio.ActionMap.prototype.add_action_entries = function add_action_entries(entries) {
     828         [ +  + ]:           9 :         for (const {name, activate, parameter_type, state, change_state} of entries) {
     829 [ +  + ][ +  + ]:           6 :             if (typeof parameter_type === 'string' && !GLib.variant_type_string_is_valid(parameter_type))
     830                 :           1 :                 throw new Error(`parameter_type "${parameter_type}" is not a valid VariantType`);
     831                 :             : 
     832                 :           9 :             const action = new Gio.SimpleAction({
     833                 :           5 :                 name,
     834         [ +  + ]:           5 :                 parameter_type: typeof parameter_type === 'string' ? new GLib.VariantType(parameter_type) : null,
     835         [ +  + ]:           5 :                 state: typeof state === 'string' ? GLib.Variant.parse(null, state, null, null) : null,
     836                 :             :             });
     837                 :             : 
     838         [ +  + ]:           4 :             if (typeof activate === 'function')
     839                 :           1 :                 action.connect('activate', activate.bind(action));
     840                 :             : 
     841         [ +  + ]:           4 :             if (typeof change_state === 'function')
     842                 :           1 :                 action.connect('change-state', change_state.bind(action));
     843                 :             : 
     844                 :           4 :             this.add_action(action);
     845                 :             :         }
     846                 :             :     };
     847                 :             : }
        

Generated by: LCOV version 2.0-1