Branch data Line data Source code
1 : 49 : // 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 : 49 : const {setMainLoopHook} = imports._promiseNative;
6 : :
7 : 49 : let GLib;
8 : :
9 : 49 : const SIMPLE_TYPES = ['b', 'y', 'n', 'q', 'i', 'u', 'x', 't', 'h', 'd', 's', 'o', 'g'];
10 : :
11 : 530 : function _readSingleType(signature, forceSimple) {
12 : 530 : let char = signature.shift();
13 : 530 : let isSimple = false;
14 : :
15 [ + + ]: 530 : if (!SIMPLE_TYPES.includes(char)) {
16 [ + - ]: 332 : if (forceSimple)
17 : 0 : throw new TypeError('Invalid GVariant signature (a simple type was expected)');
18 : : } else {
19 : 198 : isSimple = true;
20 : : }
21 : :
22 [ - + ][ + + ]: 530 : if (char === 'm' || char === 'a')
23 : 2 : return [char].concat(_readSingleType(signature, false));
24 [ + + ]: 528 : if (char === '{') {
25 : 164 : let key = _readSingleType(signature, true);
26 : 164 : let val = _readSingleType(signature, false);
27 : 164 : let close = signature.shift();
28 [ + - ]: 164 : if (close !== '}')
29 : 0 : throw new TypeError('Invalid GVariant signature for type DICT_ENTRY (expected "}"');
30 : 164 : return [char].concat(key, val, close);
31 : : }
32 [ + + ]: 364 : 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 [ + + ][ + - ]: 362 : if (!isSimple && char !== 'v')
49 : 0 : throw new TypeError(`Invalid GVariant signature (${char} is not a valid type)`);
50 : :
51 : 362 : return [char];
52 : 530 : }
53 : :
54 : 1565 : function _packVariant(signature, value) {
55 [ + - ]: 1565 : if (signature.length === 0)
56 : 0 : throw new TypeError('GVariant signature cannot be empty');
57 : :
58 : 1565 : let char = signature.shift();
59 [ + + ][ + + ]: 1565 : 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 : 39 : 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 : 588 : 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 : 284 : 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 : 195 : case 'a': {
96 : 195 : let arrayType = _readSingleType(signature, false);
97 [ + + ]: 195 : if (arrayType[0] === 's') {
98 : : // special case for array of strings
99 : 13 : return GLib.Variant.new_strv(value);
100 : : }
101 [ + + ]: 182 : 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 : 169 : let arrayValue = [];
111 [ + + ]: 169 : if (arrayType[0] === '{') {
112 : : // special case for dictionaries
113 [ + + ]: 441 : for (let key in value) {
114 : 277 : let copy = [].concat(arrayType);
115 : 277 : let child = _packVariant(copy, [key, value[key]]);
116 : 277 : 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 : 169 : return GLib.Variant.new_array(new GLib.VariantType(arrayType.join('')), arrayValue);
126 : : }
127 : :
128 : 127 : case '(': {
129 : 127 : let children = [];
130 [ + + ]: 259 : for (let i = 0; i < value.length; i++) {
131 : 134 : let next = signature[0];
132 [ + - ]: 134 : if (next === ')')
133 : 0 : break;
134 : 134 : children.push(_packVariant(signature, value[i]));
135 : : }
136 : :
137 [ + - ]: 125 : if (signature[0] !== ')')
138 : 0 : throw new TypeError('Invalid GVariant signature for type TUPLE (expected ")")');
139 : 125 : signature.shift();
140 : 125 : return GLib.Variant.new_tuple(children);
141 : : }
142 : 277 : case '{': {
143 : 277 : let key = _packVariant(signature, value[0]);
144 : 277 : let child = _packVariant(signature, value[1]);
145 : :
146 [ + - ]: 277 : if (signature[0] !== '}')
147 : 0 : throw new TypeError('Invalid GVariant signature for type DICT_ENTRY (expected "}")');
148 : 277 : signature.shift();
149 : :
150 : 277 : return GLib.Variant.new_dict_entry(key, child);
151 : : }
152 : : default:
153 : 0 : throw new TypeError(`Invalid GVariant signature (unexpected character ${char})`);
154 : : }
155 : 1561 : }
156 : :
157 [ + + ]: 1417 : function _unpackVariant(variant, deep, recursive = false) {
158 [ + + ][ + + ]: 1417 : 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 : 41 : 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 : 299 : 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 [ + + ]: 298 : if (variant.is_of_type(new GLib.VariantType('a{?*}'))) {
199 : : // special case containers
200 : 82 : let ret = { };
201 : 82 : let nElements = variant.n_children();
202 [ + + ]: 299 : 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 : 82 : 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 : 350 : case '{': {
224 : 350 : let ret = [];
225 : 350 : let nElements = variant.n_children();
226 [ + + ]: 942 : for (let i = 0; i < nElements; i++) {
227 : 591 : let val = variant.get_child_value(i);
228 [ - + ]: 591 : if (deep)
229 : 591 : ret.push(_unpackVariant(val, deep, recursive));
230 : : else
231 : 0 : ret.push(val);
232 : : }
233 : 351 : return ret;
234 : : }
235 : : }
236 : :
237 : 0 : throw new Error('Assertion failure: this code should not be reached');
238 : 847 : }
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 : 49 : function _init() {
255 : : // this is imports.gi.GLib
256 : :
257 : 49 : GLib = this;
258 : :
259 : 100 : GLib.MainLoop.prototype.runAsync = function (...args) {
260 : 51 : return new Promise((resolve, reject) => {
261 : 51 : setMainLoopHook(() => {
262 : 51 : try {
263 [ + - ][ # # ]: 51 : 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 : 49 : this.MAXINT64_BIGINT = 0x7fff_ffff_ffff_ffffn;
274 : 49 : this.MININT64_BIGINT = -this.MAXINT64_BIGINT - 1n;
275 : 49 : 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 : 49 : 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 : 49 : const quarkToString = this.quark_to_string;
287 : 49 : const realNewLiteral = this.Error.new_literal;
288 : 49 : this.Error.new_literal = function (domain, code, message) {
289 [ + + ]: 10 : if (quarkToString(domain) === null)
290 : 1 : throw new TypeError(`Error.new_literal: ${domain} is not a valid domain`);
291 : 9 : return realNewLiteral(domain, code, message);
292 : : };
293 : :
294 : 638 : this.Variant._new_internal = function (sig, value) {
295 : 589 : let signature = Array.prototype.slice.call(sig);
296 : :
297 : 589 : let variant = _packVariant(signature, value);
298 [ + - ]: 587 : if (signature.length !== 0)
299 : 0 : throw new TypeError('Invalid GVariant signature (more than one single complete type)');
300 : :
301 : 587 : return variant;
302 : 587 : };
303 : :
304 : : // Deprecate version of new GLib.Variant()
305 : 49 : this.Variant.new = function (sig, value) {
306 : 4 : return new GLib.Variant(sig, value);
307 : : };
308 : 49 : this.Variant.prototype.unpack = function () {
309 : 4 : return _unpackVariant(this, false);
310 : : };
311 : 49 : this.Variant.prototype.deepUnpack = function () {
312 : 167 : return _unpackVariant(this, true);
313 : : };
314 : : // backwards compatibility alias
315 : 49 : this.Variant.prototype.deep_unpack = this.Variant.prototype.deepUnpack;
316 : :
317 : : // Note: discards type information, if the variant contains any 'v' types
318 : 49 : this.Variant.prototype.recursiveUnpack = function () {
319 : 78 : return _unpackVariant(this, true, true);
320 : : };
321 : :
322 : 49 : this.Variant.prototype.toString = function () {
323 : 0 : return `[object variant of type "${this.get_type_string()}"]`;
324 : : };
325 : :
326 : 49 : this.Bytes.prototype.toArray = function () {
327 : 322 : return imports._byteArrayNative.fromGBytes(this);
328 : : };
329 : :
330 : 49 : 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 : 116 : function log_structured(logDomain, logLevel, fields) {
338 : : /** @type {Record<string, GLib.Variant>} */
339 : 116 : let variantFields = {};
340 : :
341 [ + + ]: 306 : for (let key in fields) {
342 : 190 : const field = fields[key];
343 : :
344 [ + + ]: 190 : if (field instanceof Uint8Array) {
345 : 1 : variantFields[key] = new GLib.Variant('ay', field);
346 [ - + ]: 189 : } else if (typeof field === 'string') {
347 : 189 : 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 : 116 : 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 : 49 : 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 : 53 : 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 : 53 : 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 [ + + ][ - + ]: 57 : 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 : : // Provide overrides for one-shot idle/timeout functions in GLib.
402 : : // The original functions are not introspectable, as they don't have
403 : : // "full" variants with a GDestroy parameter for the callback.
404 : :
405 : 50 : this.idle_add_once = function (priority, callback) {
406 : 1 : const id = this.idle_add(priority, () => {
407 : 1 : callback();
408 : 1 : return this.SOURCE_REMOVE;
409 : : });
410 : 1 : return id;
411 : 1 : };
412 : :
413 : 50 : this.timeout_add_once = function (priority, interval, callback) {
414 : 1 : const id = this.timeout_add(priority, interval, () => {
415 : 1 : callback();
416 : 1 : return this.SOURCE_REMOVE;
417 : : });
418 : 1 : return id;
419 : 1 : };
420 : :
421 : 50 : this.timeout_add_seconds_once = function (priority, interval, callback) {
422 : 1 : const id = this.timeout_add_seconds(priority, interval, () => {
423 : 1 : callback();
424 : 1 : return this.SOURCE_REMOVE;
425 : : });
426 : 1 : return id;
427 : 1 : };
428 : :
429 : : // Prevent user code from calling GLib string manipulation functions that
430 : : // return the same string that was passed in. These can't be annotated
431 : : // properly, and will mostly crash.
432 : : // Here we provide approximate implementations of the functions so that if
433 : : // they had happened to work in the past, they will continue working, but
434 : : // log a stack trace and a suggestion of what to use instead.
435 : : // Exceptions are thrown instead for GLib.stpcpy() of which the return value
436 : : // is useless anyway and GLib.ascii_formatd() which is too complicated to
437 : : // implement here.
438 : :
439 : 49 : this.stpcpy = function () {
440 : 1 : throw _notIntrospectableError('GLib.stpcpy()', 'the + operator');
441 : : };
442 : :
443 : 53 : this.strstr_len = function (haystack, len, needle) {
444 : 4 : _warnNotIntrospectable('GLib.strstr_len()', 'String.indexOf()');
445 : 4 : let searchString = haystack;
446 [ + + ]: 4 : if (len !== -1)
447 : 2 : searchString = searchString.slice(0, len);
448 : 4 : const index = searchString.indexOf(needle);
449 [ + + ]: 4 : if (index === -1)
450 : 2 : return null;
451 : 2 : return haystack.slice(index);
452 : 4 : };
453 : :
454 : 51 : this.strrstr = function (haystack, needle) {
455 : 2 : _warnNotIntrospectable('GLib.strrstr()', 'String.lastIndexOf()');
456 : 2 : const index = haystack.lastIndexOf(needle);
457 [ + + ]: 2 : if (index === -1)
458 : 1 : return null;
459 : 1 : return haystack.slice(index);
460 : 2 : };
461 : :
462 : 52 : this.strrstr_len = function (haystack, len, needle) {
463 : 3 : _warnNotIntrospectable('GLib.strrstr_len()', 'String.lastIndexOf()');
464 : 3 : let searchString = haystack;
465 [ + + ]: 3 : if (len !== -1)
466 : 1 : searchString = searchString.slice(0, len);
467 : 3 : const index = searchString.lastIndexOf(needle);
468 [ + + ]: 3 : if (index === -1)
469 : 1 : return null;
470 : 2 : return haystack.slice(index);
471 : 3 : };
472 : :
473 : 49 : this.strup = function (string) {
474 : 2 : _warnNotIntrospectable('GLib.strup()',
475 : 1 : 'String.toUpperCase() or GLib.ascii_strup()');
476 : 1 : return string.toUpperCase();
477 : : };
478 : :
479 : 49 : this.strdown = function (string) {
480 : 2 : _warnNotIntrospectable('GLib.strdown()',
481 : 1 : 'String.toLowerCase() or GLib.ascii_strdown()');
482 : 1 : return string.toLowerCase();
483 : : };
484 : :
485 : 49 : this.strreverse = function (string) {
486 : 2 : _warnNotIntrospectable('GLib.strreverse()',
487 : 1 : 'Array.reverse() and String.join()');
488 [ + + ]: 1 : return [...string].reverse().join('');
489 : : };
490 : :
491 : 49 : this.ascii_dtostr = function (unused, len, number) {
492 : 2 : _warnNotIntrospectable('GLib.ascii_dtostr()', 'JS string conversion');
493 : 2 : return `${number}`.slice(0, len);
494 : : };
495 : :
496 : 49 : this.ascii_formatd = function () {
497 : 2 : throw _notIntrospectableError('GLib.ascii_formatd()',
498 : 1 : 'Number.toExponential() and string interpolation');
499 : : };
500 : :
501 : 49 : this.strchug = function (string) {
502 : 2 : _warnNotIntrospectable('GLib.strchug()', 'String.trimStart()');
503 : 2 : return string.trimStart();
504 : : };
505 : :
506 : 49 : this.strchomp = function (string) {
507 : 2 : _warnNotIntrospectable('GLib.strchomp()', 'String.trimEnd()');
508 : 2 : return string.trimEnd();
509 : : };
510 : :
511 : : // g_strstrip() is a macro and therefore doesn't even appear in the GIR
512 : : // file, but we may as well include it here since it's trivial
513 : 49 : this.strstrip = function (string) {
514 : 4 : _warnNotIntrospectable('GLib.strstrip()', 'String.trim()');
515 : 4 : return string.trim();
516 : : };
517 : :
518 : 53 : this.strdelimit = function (string, delimiters, newDelimiter) {
519 : 4 : _warnNotIntrospectable('GLib.strdelimit()', 'String.replace()');
520 : :
521 [ + + ]: 4 : if (delimiters === null)
522 : 2 : delimiters = GLib.STR_DELIMITERS;
523 [ + + ]: 4 : if (typeof newDelimiter === 'number')
524 : 2 : newDelimiter = String.fromCharCode(newDelimiter);
525 : :
526 : 4 : const delimiterChars = delimiters.split('');
527 : 4 : const escapedDelimiterChars = delimiterChars.map(_escapeCharacterSetChars);
528 : 4 : const delimiterRegex = new RegExp(`[${escapedDelimiterChars.join('')}]`, 'g');
529 : 4 : return string.replace(delimiterRegex, newDelimiter);
530 : 4 : };
531 : :
532 : 51 : this.strcanon = function (string, validChars, substitutor) {
533 : 2 : _warnNotIntrospectable('GLib.strcanon()', 'String.replace()');
534 : :
535 [ + + ]: 2 : if (typeof substitutor === 'number')
536 : 1 : substitutor = String.fromCharCode(substitutor);
537 : :
538 : 2 : const validArray = validChars.split('');
539 : 2 : const escapedValidArray = validArray.map(_escapeCharacterSetChars);
540 : 2 : const invalidRegex = new RegExp(`[^${escapedValidArray.join('')}]`, 'g');
541 : 2 : return string.replace(invalidRegex, substitutor);
542 : 2 : };
543 : :
544 : : // Prevent user code from calling GThread functions which always crash
545 : 49 : this.Thread.new = function () {
546 : 0 : throw _notIntrospectableError('GLib.Thread.new()',
547 : 0 : 'GIO asynchronous methods or Promise()');
548 : : };
549 : :
550 : 49 : this.Thread.try_new = function () {
551 : 0 : throw _notIntrospectableError('GLib.Thread.try_new()',
552 : 0 : 'GIO asynchronous methods or Promise()');
553 : : };
554 : :
555 : 49 : this.Thread.exit = function () {
556 : 0 : throw new Error('\'GLib.Thread.exit()\' may not be called in GJS');
557 : : };
558 : :
559 : 49 : this.Thread.prototype.ref = function () {
560 : 0 : throw new Error('\'GLib.Thread.ref()\' may not be called in GJS');
561 : : };
562 : :
563 : 49 : this.Thread.prototype.unref = function () {
564 : 0 : throw new Error('\'GLib.Thread.unref()\' may not be called in GJS');
565 : : };
566 : :
567 : : // Override GLib.MatchInfo with a type that keeps the UTF-8 encoded search
568 : : // string alive.
569 : 49 : const oldMatchInfo = this.MatchInfo;
570 : 49 : let matchInfoPatched = false;
571 : 18 : function patchMatchInfo(GLibModule) {
572 [ + + ]: 18 : if (matchInfoPatched)
573 : 17 : return;
574 : :
575 : 1 : const {MatchInfo} = imports.gi.GjsPrivate;
576 : :
577 : 1 : const originalMatchInfoMethods = new Set(Object.keys(oldMatchInfo.prototype));
578 : 1 : const overriddenMatchInfoMethods = new Set(Object.keys(MatchInfo.prototype));
579 : 1 : const symmetricDifference = originalMatchInfoMethods.symmetricDifference(overriddenMatchInfoMethods);
580 [ + - ]: 1 : if (symmetricDifference.size !== 0)
581 [ # # ]: 0 : throw new Error(`Methods of GMatchInfo and GjsMatchInfo don't match: ${[...symmetricDifference]}`);
582 : :
583 : 1 : GLibModule.MatchInfo = MatchInfo;
584 : 1 : matchInfoPatched = true;
585 : 17 : }
586 : :
587 : : // We can't monkeypatch GLib.MatchInfo directly at override time, because
588 : : // importing GjsPrivate requires GLib. So this monkeypatches GLib.MatchInfo
589 : : // with a Proxy that overwrites itself with the real GjsPrivate.MatchInfo
590 : : // as soon as you try to do anything with it.
591 : 49 : const allProxyOperations = ['apply', 'construct', 'defineProperty',
592 : 49 : 'deleteProperty', 'get', 'getOwnPropertyDescriptor', 'getPrototypeOf',
593 : 49 : 'has', 'isExtensible', 'ownKeys', 'preventExtensions', 'set',
594 : 49 : 'setPrototypeOf'];
595 : 637 : function delegateToMatchInfo(op) {
596 : 638 : return function (target, ...params) {
597 : 1 : patchMatchInfo(GLib);
598 [ + + ]: 1 : return Reflect[op](GLib.MatchInfo, ...params);
599 : : };
600 : : }
601 : 98 : this.MatchInfo = new Proxy(function () {},
602 : 686 : Object.fromEntries(allProxyOperations.map(op => [op, delegateToMatchInfo(op)])));
603 : :
604 : 62 : this.Regex.prototype.match = function (...args) {
605 : 13 : patchMatchInfo(GLib);
606 [ + + ]: 13 : return imports.gi.GjsPrivate.regex_match(this, ...args);
607 : : };
608 : :
609 : 51 : this.Regex.prototype.match_full = function (...args) {
610 : 2 : patchMatchInfo(GLib);
611 [ + + ]: 2 : return imports.gi.GjsPrivate.regex_match_full(this, ...args);
612 : : };
613 : :
614 : 50 : this.Regex.prototype.match_all = function (...args) {
615 : 1 : patchMatchInfo(GLib);
616 [ + + ]: 1 : return imports.gi.GjsPrivate.regex_match_all(this, ...args);
617 : : };
618 : :
619 : 50 : this.Regex.prototype.match_all_full = function (...args) {
620 : 1 : patchMatchInfo(GLib);
621 [ + + ]: 1 : return imports.gi.GjsPrivate.regex_match_all_full(this, ...args);
622 : : };
623 : : }
|