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