Branch data Line data Source code
1 : 48 : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
2 : : // SPDX-FileCopyrightText: 2011 Giovanni Campagna
3 : : // SPDX-FileCopyrightText: 2023 Philip Chimento <philip.chimento@gmail.com>
4 : :
5 : 48 : const {setMainLoopHook} = imports._promiseNative;
6 : :
7 : 48 : let GLib;
8 : :
9 : 48 : const SIMPLE_TYPES = ['b', 'y', 'n', 'q', 'i', 'u', 'x', 't', 'h', 'd', 's', 'o', 'g'];
10 : :
11 : 386 : function _readSingleType(signature, forceSimple) {
12 : 386 : let char = signature.shift();
13 : 386 : let isSimple = false;
14 : :
15 [ + + ]: 386 : if (!SIMPLE_TYPES.includes(char)) {
16 [ + - ]: 236 : if (forceSimple)
17 : 0 : throw new TypeError('Invalid GVariant signature (a simple type was expected)');
18 : : } else {
19 : 150 : isSimple = true;
20 : : }
21 : :
22 [ - + ][ + + ]: 386 : if (char === 'm' || char === 'a')
23 : 2 : return [char].concat(_readSingleType(signature, false));
24 [ + + ]: 384 : if (char === '{') {
25 : 116 : let key = _readSingleType(signature, true);
26 : 116 : let val = _readSingleType(signature, false);
27 : 116 : let close = signature.shift();
28 [ + - ]: 116 : if (close !== '}')
29 : 0 : throw new TypeError('Invalid GVariant signature for type DICT_ENTRY (expected "}"');
30 : 116 : return [char].concat(key, val, close);
31 : : }
32 [ + + ]: 268 : if (char === '(') {
33 : 2 : let res = [char];
34 [ - + ]: 6 : while (true) {
35 [ + - ]: 6 : if (signature.length === 0)
36 : 0 : throw new TypeError('Invalid GVariant signature for type TUPLE (expected ")")');
37 : 6 : let next = signature[0];
38 [ + + ]: 6 : if (next === ')') {
39 : 2 : signature.shift();
40 : 2 : return res.concat(next);
41 : : }
42 : 4 : let el = _readSingleType(signature);
43 : 4 : res = res.concat(el);
44 : : }
45 : : }
46 : :
47 : : // Valid types are simple types, arrays, maybes, tuples, dictionary entries and variants
48 [ + + ][ + - ]: 266 : if (!isSimple && char !== 'v')
49 : 0 : throw new TypeError(`Invalid GVariant signature (${char} is not a valid type)`);
50 : :
51 : 266 : return [char];
52 : 386 : }
53 : :
54 : 1315 : function _packVariant(signature, value) {
55 [ + - ]: 1315 : if (signature.length === 0)
56 : 0 : throw new TypeError('GVariant signature cannot be empty');
57 : :
58 : 1315 : let char = signature.shift();
59 [ + + ][ + + ]: 1315 : switch (char) {
[ - + ][ - + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + - ]
60 : : case 'b':
61 : 4 : return GLib.Variant.new_boolean(value);
62 : : case 'y':
63 : 12 : return GLib.Variant.new_byte(value);
64 : : case 'n':
65 : 0 : return GLib.Variant.new_int16(value);
66 : : case 'q':
67 : 0 : return GLib.Variant.new_uint16(value);
68 : : case 'i':
69 : 35 : return GLib.Variant.new_int32(value);
70 : : case 'u':
71 : 3 : return GLib.Variant.new_uint32(value);
72 : : case 'x':
73 : 7 : return GLib.Variant.new_int64(value);
74 : : case 't':
75 : 6 : return GLib.Variant.new_uint64(value);
76 : : case 'h':
77 : 12 : return GLib.Variant.new_handle(value);
78 : : case 'd':
79 : 7 : return GLib.Variant.new_double(value);
80 : : case 's':
81 : 494 : return GLib.Variant.new_string(value);
82 : : case 'o':
83 : 1 : return GLib.Variant.new_object_path(value);
84 : : case 'g':
85 : 1 : return GLib.Variant.new_signature(value);
86 : : case 'v':
87 : 242 : return GLib.Variant.new_variant(value);
88 : : case 'm':
89 [ + + ]: 2 : if (value !== null) {
90 : 1 : return GLib.Variant.new_maybe(null, _packVariant(signature, value));
91 : : } else {
92 : 3 : return GLib.Variant.new_maybe(new GLib.VariantType(
93 : 2 : _readSingleType(signature, false).join('')), null);
94 : : }
95 : 147 : case 'a': {
96 : 147 : let arrayType = _readSingleType(signature, false);
97 [ + + ]: 147 : if (arrayType[0] === 's') {
98 : : // special case for array of strings
99 : 13 : return GLib.Variant.new_strv(value);
100 : : }
101 [ + + ]: 134 : if (arrayType[0] === 'y') {
102 : : // special case for array of bytes
103 [ + + ]: 13 : if (typeof value === 'string')
104 [ + + ]: 2 : value = Uint8Array.of(...new TextEncoder().encode(value), 0);
105 : 13 : const bytes = new GLib.Bytes(value);
106 : 26 : return GLib.Variant.new_from_bytes(new GLib.VariantType('ay'),
107 : 13 : bytes, true);
108 : : }
109 : :
110 : 121 : let arrayValue = [];
111 [ + + ]: 121 : if (arrayType[0] === '{') {
112 : : // special case for dictionaries
113 [ + + ]: 351 : for (let key in value) {
114 : 235 : let copy = [].concat(arrayType);
115 : 235 : let child = _packVariant(copy, [key, value[key]]);
116 : 235 : arrayValue.push(child);
117 : : }
118 : : } else {
119 [ + + ]: 15 : for (let i = 0; i < value.length; i++) {
120 : 10 : let copy = [].concat(arrayType);
121 : 10 : let child = _packVariant(copy, value[i]);
122 : 10 : arrayValue.push(child);
123 : : }
124 : : }
125 : 121 : return GLib.Variant.new_array(new GLib.VariantType(arrayType.join('')), arrayValue);
126 : : }
127 : :
128 : 107 : case '(': {
129 : 107 : let children = [];
130 [ + + ]: 219 : for (let i = 0; i < value.length; i++) {
131 : 114 : let next = signature[0];
132 [ + - ]: 114 : if (next === ')')
133 : 0 : break;
134 : 114 : children.push(_packVariant(signature, value[i]));
135 : : }
136 : :
137 [ + - ]: 105 : if (signature[0] !== ')')
138 : 0 : throw new TypeError('Invalid GVariant signature for type TUPLE (expected ")")');
139 : 105 : signature.shift();
140 : 105 : return GLib.Variant.new_tuple(children);
141 : : }
142 : 235 : case '{': {
143 : 235 : let key = _packVariant(signature, value[0]);
144 : 235 : let child = _packVariant(signature, value[1]);
145 : :
146 [ + - ]: 235 : if (signature[0] !== '}')
147 : 0 : throw new TypeError('Invalid GVariant signature for type DICT_ENTRY (expected "}")');
148 : 235 : signature.shift();
149 : :
150 : 235 : return GLib.Variant.new_dict_entry(key, child);
151 : : }
152 : : default:
153 : 0 : throw new TypeError(`Invalid GVariant signature (unexpected character ${char})`);
154 : : }
155 : 1311 : }
156 : :
157 [ + + ]: 1381 : function _unpackVariant(variant, deep, recursive = false) {
158 [ + + ][ + + ]: 1381 : switch (String.fromCharCode(variant.classify())) {
[ - + ][ - + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + - ]
159 : : case 'b':
160 : 3 : return variant.get_boolean();
161 : : case 'y':
162 : 12 : return variant.get_byte();
163 : : case 'n':
164 : 0 : return variant.get_int16();
165 : : case 'q':
166 : 0 : return variant.get_uint16();
167 : : case 'i':
168 : 37 : return variant.get_int32();
169 : : case 'u':
170 : 2 : return variant.get_uint32();
171 : : case 'x':
172 : 3 : return variant.get_int64();
173 : : case 't':
174 : 2 : return variant.get_uint64();
175 : : case 'h':
176 : 8 : return variant.get_handle();
177 : : case 'd':
178 : 6 : return variant.get_double();
179 : : case 'o':
180 : : case 'g':
181 : : case 's':
182 : : // g_variant_get_string has length as out argument
183 : 291 : return variant.get_string()[0];
184 : 219 : case 'v': {
185 : 219 : const ret = variant.get_variant();
186 [ + + ][ + + ]: 219 : if (deep && recursive && ret instanceof GLib.Variant)
[ + + ]
187 : 198 : return _unpackVariant(ret, deep, recursive);
188 : 25 : return ret;
189 : : }
190 : 191 : case 'm': {
191 : 191 : let val = variant.get_maybe();
192 [ - + ][ + + ]: 191 : if (deep && val)
193 : 190 : return _unpackVariant(val, deep, recursive);
194 : : else
195 : 1 : return val;
196 : : }
197 : : case 'a':
198 [ + + ]: 292 : if (variant.is_of_type(new GLib.VariantType('a{?*}'))) {
199 : : // special case containers
200 : 76 : let ret = { };
201 : 76 : let nElements = variant.n_children();
202 [ + + ]: 293 : for (let i = 0; i < nElements; i++) {
203 : : // always unpack the dictionary entry, and always unpack
204 : : // the key (or it cannot be added as a key)
205 : 434 : let val = _unpackVariant(variant.get_child_value(i), deep,
206 : 217 : recursive);
207 : 217 : let key;
208 [ + - ]: 217 : if (!deep)
209 : 0 : key = _unpackVariant(val[0], true);
210 : : else
211 : 217 : key = val[0];
212 : 217 : ret[key] = val[1];
213 : : }
214 : 76 : return ret;
215 : : }
216 [ + + ]: 222 : if (variant.is_of_type(new GLib.VariantType('ay'))) {
217 : : // special case byte arrays
218 : 205 : return variant.get_data_as_bytes().toArray();
219 : : }
220 : :
221 : : // fall through
222 : : case '(':
223 : 332 : case '{': {
224 : 332 : let ret = [];
225 : 332 : let nElements = variant.n_children();
226 [ + + ]: 906 : for (let i = 0; i < nElements; i++) {
227 : 573 : let val = variant.get_child_value(i);
228 [ - + ]: 573 : if (deep)
229 : 573 : ret.push(_unpackVariant(val, deep, recursive));
230 : : else
231 : 0 : ret.push(val);
232 : : }
233 : 333 : return ret;
234 : : }
235 : : }
236 : :
237 : 0 : throw new Error('Assertion failure: this code should not be reached');
238 : 823 : }
239 : :
240 : 30 : function _notIntrospectableError(funcName, replacement) {
241 : 30 : return new Error(`${funcName} is not introspectable. Use ${replacement} instead.`);
242 : : }
243 : :
244 : 28 : function _warnNotIntrospectable(funcName, replacement) {
245 : 28 : logError(_notIntrospectableError(funcName, replacement));
246 : : }
247 : :
248 : 26 : function _escapeCharacterSetChars(char) {
249 [ + + ]: 26 : if ('-^]\\'.includes(char))
250 : 2 : return `\\${char}`;
251 : 24 : return char;
252 : : }
253 : :
254 : 48 : function _init() {
255 : : // this is imports.gi.GLib
256 : :
257 : 48 : GLib = this;
258 : :
259 : 98 : GLib.MainLoop.prototype.runAsync = function (...args) {
260 : 50 : return new Promise((resolve, reject) => {
261 : 50 : setMainLoopHook(() => {
262 : 50 : try {
263 [ + - ][ # # ]: 50 : resolve(this.run(...args));
264 : 0 : } catch (error) {
265 : 0 : reject(error);
266 : : }
267 : : });
268 : : });
269 : : };
270 : :
271 : : // For convenience in property min or max values, since GLib.MAXINT64 and
272 : : // friends will log a warning when used
273 : 48 : this.MAXINT64_BIGINT = 0x7fff_ffff_ffff_ffffn;
274 : 48 : this.MININT64_BIGINT = -this.MAXINT64_BIGINT - 1n;
275 : 48 : this.MAXUINT64_BIGINT = 0xffff_ffff_ffff_ffffn;
276 : :
277 : : // small HACK: we add a matches() method to standard Errors so that
278 : : // you can do "if (e.matches(Ns.FooError, Ns.FooError.SOME_CODE))"
279 : : // without checking instanceof
280 : 48 : Error.prototype.matches = function () {
281 : 0 : return false;
282 : : };
283 : :
284 : : // Guard against domains that aren't valid quarks and would lead
285 : : // to a crash
286 : 48 : const quarkToString = this.quark_to_string;
287 : 48 : const realNewLiteral = this.Error.new_literal;
288 : 48 : this.Error.new_literal = function (domain, code, message) {
289 [ + + ]: 8 : if (quarkToString(domain) === null)
290 : 1 : throw new TypeError(`Error.new_literal: ${domain} is not a valid domain`);
291 : 7 : return realNewLiteral(domain, code, message);
292 : : };
293 : :
294 : 533 : this.Variant._new_internal = function (sig, value) {
295 : 485 : let signature = Array.prototype.slice.call(sig);
296 : :
297 : 485 : let variant = _packVariant(signature, value);
298 [ + - ]: 483 : if (signature.length !== 0)
299 : 0 : throw new TypeError('Invalid GVariant signature (more than one single complete type)');
300 : :
301 : 483 : return variant;
302 : 483 : };
303 : :
304 : : // Deprecate version of new GLib.Variant()
305 : 48 : this.Variant.new = function (sig, value) {
306 : 4 : return new GLib.Variant(sig, value);
307 : : };
308 : 48 : this.Variant.prototype.unpack = function () {
309 : 4 : return _unpackVariant(this, false);
310 : : };
311 : 48 : this.Variant.prototype.deepUnpack = function () {
312 : 149 : return _unpackVariant(this, true);
313 : : };
314 : : // backwards compatibility alias
315 : 48 : this.Variant.prototype.deep_unpack = this.Variant.prototype.deepUnpack;
316 : :
317 : : // Note: discards type information, if the variant contains any 'v' types
318 : 48 : this.Variant.prototype.recursiveUnpack = function () {
319 : 78 : return _unpackVariant(this, true, true);
320 : : };
321 : :
322 : 48 : this.Variant.prototype.toString = function () {
323 : 0 : return `[object variant of type "${this.get_type_string()}"]`;
324 : : };
325 : :
326 : 48 : this.Bytes.prototype.toArray = function () {
327 : 320 : return imports._byteArrayNative.fromGBytes(this);
328 : : };
329 : :
330 : 48 : this.log_structured =
331 : : /**
332 : : * @param {string} logDomain Log domain.
333 : : * @param {GLib.LogLevelFlags} logLevel Log level, either from GLib.LogLevelFlags, or a user-defined level.
334 : : * @param {Record<string, unknown>} fields Key-value pairs of structured data to add to the log entry.
335 : : * @returns {void}
336 : : */
337 : 74 : function log_structured(logDomain, logLevel, fields) {
338 : : /** @type {Record<string, GLib.Variant>} */
339 : 74 : let variantFields = {};
340 : :
341 [ + + ]: 222 : for (let key in fields) {
342 : 148 : const field = fields[key];
343 : :
344 [ + + ]: 148 : if (field instanceof Uint8Array) {
345 : 1 : variantFields[key] = new GLib.Variant('ay', field);
346 [ - + ]: 147 : } else if (typeof field === 'string') {
347 : 147 : variantFields[key] = new GLib.Variant('s', field);
348 [ # # ]: 0 : } else if (field instanceof GLib.Variant) {
349 : : // GLib.log_variant converts all Variants that are
350 : : // not 'ay' or 's' type to strings by printing
351 : : // them.
352 : : //
353 : : // https://gitlab.gnome.org/GNOME/glib/-/blob/a380bfdf93cb3bfd3cd4caedc0127c4e5717545b/glib/gmessages.c#L1894
354 : 0 : variantFields[key] = field;
355 : : } else {
356 : 0 : throw new TypeError(`Unsupported value ${field}, log_structured supports GLib.Variant, Uint8Array, and string values.`);
357 : : }
358 : : }
359 : :
360 : 74 : GLib.log_variant(logDomain, logLevel, new GLib.Variant('a{sv}', variantFields));
361 : : };
362 : :
363 : : // GjsPrivate depends on GLib so we cannot import it
364 : : // before GLib is fully resolved.
365 : :
366 : 48 : this.log_set_writer_func_variant = function (...args) {
367 : 0 : const {log_set_writer_func} = imports.gi.GjsPrivate;
368 : :
369 [ # # ][ # # ]: 0 : log_set_writer_func(...args);
370 : : };
371 : :
372 : 52 : this.log_set_writer_default = function (...args) {
373 : 4 : const {log_set_writer_default} = imports.gi.GjsPrivate;
374 : :
375 [ + - ][ # # ]: 4 : log_set_writer_default(...args);
376 : : };
377 : :
378 : 52 : this.log_set_writer_func = function (writer_func) {
379 : 4 : const {log_set_writer_func} = imports.gi.GjsPrivate;
380 : :
381 [ + - ]: 4 : if (typeof writer_func !== 'function') {
382 : 0 : log_set_writer_func(writer_func);
383 : : } else {
384 : 78 : log_set_writer_func(function (logLevel, stringFields) {
385 : 74 : const stringFieldsObj = {...stringFields.recursiveUnpack()};
386 : 74 : return writer_func(logLevel, stringFieldsObj);
387 : 74 : });
388 : : }
389 : : };
390 : :
391 [ + + ][ - + ]: 56 : this.VariantDict.prototype.lookup = function (key, variantType = null, deep = false) {
392 [ + + ]: 8 : if (typeof variantType === 'string')
393 : 2 : variantType = new GLib.VariantType(variantType);
394 : :
395 : 8 : const variant = this.lookup_value(key, variantType);
396 [ + + ]: 8 : if (variant === null)
397 : 4 : return null;
398 : 4 : return _unpackVariant(variant, deep);
399 : 8 : };
400 : :
401 : : // Prevent user code from calling GLib string manipulation functions that
402 : : // return the same string that was passed in. These can't be annotated
403 : : // properly, and will mostly crash.
404 : : // Here we provide approximate implementations of the functions so that if
405 : : // they had happened to work in the past, they will continue working, but
406 : : // log a stack trace and a suggestion of what to use instead.
407 : : // Exceptions are thrown instead for GLib.stpcpy() of which the return value
408 : : // is useless anyway and GLib.ascii_formatd() which is too complicated to
409 : : // implement here.
410 : :
411 : 48 : this.stpcpy = function () {
412 : 1 : throw _notIntrospectableError('GLib.stpcpy()', 'the + operator');
413 : : };
414 : :
415 : 52 : this.strstr_len = function (haystack, len, needle) {
416 : 4 : _warnNotIntrospectable('GLib.strstr_len()', 'String.indexOf()');
417 : 4 : let searchString = haystack;
418 [ + + ]: 4 : if (len !== -1)
419 : 2 : searchString = searchString.slice(0, len);
420 : 4 : const index = searchString.indexOf(needle);
421 [ + + ]: 4 : if (index === -1)
422 : 2 : return null;
423 : 2 : return haystack.slice(index);
424 : 4 : };
425 : :
426 : 50 : this.strrstr = function (haystack, needle) {
427 : 2 : _warnNotIntrospectable('GLib.strrstr()', 'String.lastIndexOf()');
428 : 2 : const index = haystack.lastIndexOf(needle);
429 [ + + ]: 2 : if (index === -1)
430 : 1 : return null;
431 : 1 : return haystack.slice(index);
432 : 2 : };
433 : :
434 : 51 : this.strrstr_len = function (haystack, len, needle) {
435 : 3 : _warnNotIntrospectable('GLib.strrstr_len()', 'String.lastIndexOf()');
436 : 3 : let searchString = haystack;
437 [ + + ]: 3 : if (len !== -1)
438 : 1 : searchString = searchString.slice(0, len);
439 : 3 : const index = searchString.lastIndexOf(needle);
440 [ + + ]: 3 : if (index === -1)
441 : 1 : return null;
442 : 2 : return haystack.slice(index);
443 : 3 : };
444 : :
445 : 48 : this.strup = function (string) {
446 : 2 : _warnNotIntrospectable('GLib.strup()',
447 : 1 : 'String.toUpperCase() or GLib.ascii_strup()');
448 : 1 : return string.toUpperCase();
449 : : };
450 : :
451 : 48 : this.strdown = function (string) {
452 : 2 : _warnNotIntrospectable('GLib.strdown()',
453 : 1 : 'String.toLowerCase() or GLib.ascii_strdown()');
454 : 1 : return string.toLowerCase();
455 : : };
456 : :
457 : 48 : this.strreverse = function (string) {
458 : 2 : _warnNotIntrospectable('GLib.strreverse()',
459 : 1 : 'Array.reverse() and String.join()');
460 [ + + ]: 1 : return [...string].reverse().join('');
461 : : };
462 : :
463 : 48 : this.ascii_dtostr = function (unused, len, number) {
464 : 2 : _warnNotIntrospectable('GLib.ascii_dtostr()', 'JS string conversion');
465 : 2 : return `${number}`.slice(0, len);
466 : : };
467 : :
468 : 48 : this.ascii_formatd = function () {
469 : 2 : throw _notIntrospectableError('GLib.ascii_formatd()',
470 : 1 : 'Number.toExponential() and string interpolation');
471 : : };
472 : :
473 : 48 : this.strchug = function (string) {
474 : 2 : _warnNotIntrospectable('GLib.strchug()', 'String.trimStart()');
475 : 2 : return string.trimStart();
476 : : };
477 : :
478 : 48 : this.strchomp = function (string) {
479 : 2 : _warnNotIntrospectable('GLib.strchomp()', 'String.trimEnd()');
480 : 2 : return string.trimEnd();
481 : : };
482 : :
483 : : // g_strstrip() is a macro and therefore doesn't even appear in the GIR
484 : : // file, but we may as well include it here since it's trivial
485 : 48 : this.strstrip = function (string) {
486 : 4 : _warnNotIntrospectable('GLib.strstrip()', 'String.trim()');
487 : 4 : return string.trim();
488 : : };
489 : :
490 : 52 : this.strdelimit = function (string, delimiters, newDelimiter) {
491 : 4 : _warnNotIntrospectable('GLib.strdelimit()', 'String.replace()');
492 : :
493 [ + + ]: 4 : if (delimiters === null)
494 : 2 : delimiters = GLib.STR_DELIMITERS;
495 [ + + ]: 4 : if (typeof newDelimiter === 'number')
496 : 2 : newDelimiter = String.fromCharCode(newDelimiter);
497 : :
498 : 4 : const delimiterChars = delimiters.split('');
499 : 4 : const escapedDelimiterChars = delimiterChars.map(_escapeCharacterSetChars);
500 : 4 : const delimiterRegex = new RegExp(`[${escapedDelimiterChars.join('')}]`, 'g');
501 : 4 : return string.replace(delimiterRegex, newDelimiter);
502 : 4 : };
503 : :
504 : 50 : this.strcanon = function (string, validChars, substitutor) {
505 : 2 : _warnNotIntrospectable('GLib.strcanon()', 'String.replace()');
506 : :
507 [ + + ]: 2 : if (typeof substitutor === 'number')
508 : 1 : substitutor = String.fromCharCode(substitutor);
509 : :
510 : 2 : const validArray = validChars.split('');
511 : 2 : const escapedValidArray = validArray.map(_escapeCharacterSetChars);
512 : 2 : const invalidRegex = new RegExp(`[^${escapedValidArray.join('')}]`, 'g');
513 : 2 : return string.replace(invalidRegex, substitutor);
514 : 2 : };
515 : :
516 : : // Prevent user code from calling GThread functions which always crash
517 : 48 : this.Thread.new = function () {
518 : 0 : throw _notIntrospectableError('GLib.Thread.new()',
519 : 0 : 'GIO asynchronous methods or Promise()');
520 : : };
521 : :
522 : 48 : this.Thread.try_new = function () {
523 : 0 : throw _notIntrospectableError('GLib.Thread.try_new()',
524 : 0 : 'GIO asynchronous methods or Promise()');
525 : : };
526 : :
527 : 48 : this.Thread.exit = function () {
528 : 0 : throw new Error('\'GLib.Thread.exit()\' may not be called in GJS');
529 : : };
530 : :
531 : 48 : this.Thread.prototype.ref = function () {
532 : 0 : throw new Error('\'GLib.Thread.ref()\' may not be called in GJS');
533 : : };
534 : :
535 : 48 : this.Thread.prototype.unref = function () {
536 : 0 : throw new Error('\'GLib.Thread.unref()\' may not be called in GJS');
537 : : };
538 : :
539 : : // Override GLib.MatchInfo with a type that keeps the UTF-8 encoded search
540 : : // string alive.
541 : 48 : const oldMatchInfo = this.MatchInfo;
542 : 48 : let matchInfoPatched = false;
543 : 18 : function patchMatchInfo(GLibModule) {
544 [ + + ]: 18 : if (matchInfoPatched)
545 : 17 : return;
546 : :
547 : 1 : const {MatchInfo} = imports.gi.GjsPrivate;
548 : :
549 : 1 : const originalMatchInfoMethods = new Set(Object.keys(oldMatchInfo.prototype));
550 : 1 : const overriddenMatchInfoMethods = new Set(Object.keys(MatchInfo.prototype));
551 : 1 : const symmetricDifference = new Set(originalMatchInfoMethods);
552 [ + + ]: 17 : for (const method of overriddenMatchInfoMethods) {
553 [ - + ]: 16 : if (symmetricDifference.has(method))
554 : 16 : symmetricDifference.delete(method);
555 : : else
556 : 0 : symmetricDifference.add(method);
557 : : }
558 [ + - ]: 1 : if (symmetricDifference.size !== 0)
559 [ # # ]: 0 : throw new Error(`Methods of GMatchInfo and GjsMatchInfo don't match: ${[...symmetricDifference]}`);
560 : :
561 : 1 : GLibModule.MatchInfo = MatchInfo;
562 : 1 : matchInfoPatched = true;
563 : 17 : }
564 : :
565 : : // We can't monkeypatch GLib.MatchInfo directly at override time, because
566 : : // importing GjsPrivate requires GLib. So this monkeypatches GLib.MatchInfo
567 : : // with a Proxy that overwrites itself with the real GjsPrivate.MatchInfo
568 : : // as soon as you try to do anything with it.
569 : 48 : const allProxyOperations = ['apply', 'construct', 'defineProperty',
570 : 48 : 'deleteProperty', 'get', 'getOwnPropertyDescriptor', 'getPrototypeOf',
571 : 48 : 'has', 'isExtensible', 'ownKeys', 'preventExtensions', 'set',
572 : 48 : 'setPrototypeOf'];
573 : 624 : function delegateToMatchInfo(op) {
574 : 625 : return function (target, ...params) {
575 : 1 : patchMatchInfo(GLib);
576 [ + + ]: 1 : return Reflect[op](GLib.MatchInfo, ...params);
577 : : };
578 : : }
579 : 96 : this.MatchInfo = new Proxy(function () {},
580 : 672 : Object.fromEntries(allProxyOperations.map(op => [op, delegateToMatchInfo(op)])));
581 : :
582 : 61 : this.Regex.prototype.match = function (...args) {
583 : 13 : patchMatchInfo(GLib);
584 [ + + ]: 13 : return imports.gi.GjsPrivate.regex_match(this, ...args);
585 : : };
586 : :
587 : 50 : this.Regex.prototype.match_full = function (...args) {
588 : 2 : patchMatchInfo(GLib);
589 [ + + ]: 2 : return imports.gi.GjsPrivate.regex_match_full(this, ...args);
590 : : };
591 : :
592 : 49 : this.Regex.prototype.match_all = function (...args) {
593 : 1 : patchMatchInfo(GLib);
594 [ + + ]: 1 : return imports.gi.GjsPrivate.regex_match_all(this, ...args);
595 : : };
596 : :
597 : 49 : this.Regex.prototype.match_all_full = function (...args) {
598 : 1 : patchMatchInfo(GLib);
599 [ + + ]: 1 : return imports.gi.GjsPrivate.regex_match_all_full(this, ...args);
600 : : };
601 : : }
|