Coverage Report

Created: 2025-06-22 06:29

/src/libxmlb/src/xb-machine.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2018 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
0
#define G_LOG_DOMAIN "XbMachine"
8
9
#include "config.h"
10
11
#include <gio/gio.h>
12
#include <string.h>
13
14
#if !GLIB_CHECK_VERSION(2, 54, 0)
15
#include <errno.h>
16
#endif
17
18
#include "xb-machine-private.h"
19
#include "xb-opcode-private.h"
20
#include "xb-silo-private.h"
21
#include "xb-stack-private.h"
22
#include "xb-string-private.h"
23
24
typedef struct {
25
  XbMachineDebugFlags debug_flags;
26
  GPtrArray *methods;    /* of XbMachineMethodItem */
27
  GPtrArray *operators;    /* of XbMachineOperator */
28
  GPtrArray *text_handlers;  /* of XbMachineTextHandlerItem */
29
  GHashTable *opcode_fixup;  /* of str[XbMachineOpcodeFixupItem] */
30
  GHashTable *opcode_tokens; /* of utf8 */
31
  guint stack_size;
32
} XbMachinePrivate;
33
34
G_DEFINE_TYPE_WITH_PRIVATE(XbMachine, xb_machine, G_TYPE_OBJECT)
35
0
#define GET_PRIVATE(o) (xb_machine_get_instance_private(o))
36
37
typedef struct {
38
  gchar *str;
39
  gsize strsz;
40
  gchar *name;
41
} XbMachineOperator;
42
43
typedef struct {
44
  XbMachineOpcodeFixupFunc fixup_cb;
45
  gpointer user_data;
46
  GDestroyNotify user_data_free;
47
} XbMachineOpcodeFixupItem;
48
49
typedef struct {
50
  XbMachineTextHandlerFunc handler_cb;
51
  gpointer user_data;
52
  GDestroyNotify user_data_free;
53
} XbMachineTextHandlerItem;
54
55
typedef struct {
56
  guint32 idx;
57
  gchar *name;
58
  guint n_opcodes;
59
  XbMachineMethodFunc method_cb;
60
  gpointer user_data;
61
  GDestroyNotify user_data_free;
62
} XbMachineMethodItem;
63
64
0
#define XB_MACHINE_STACK_LEVELS_MAX 20
65
66
/**
67
 * xb_machine_set_debug_flags:
68
 * @self: a #XbMachine
69
 * @flags: #XbMachineDebugFlags, e.g. %XB_MACHINE_DEBUG_FLAG_SHOW_STACK
70
 *
71
 * Sets the debug level of the virtual machine.
72
 *
73
 * Since: 0.1.1
74
 **/
75
void
76
xb_machine_set_debug_flags(XbMachine *self, XbMachineDebugFlags flags)
77
0
{
78
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
79
0
  g_return_if_fail(XB_IS_MACHINE(self));
80
0
  priv->debug_flags = flags;
81
0
}
82
83
/**
84
 * xb_machine_add_operator:
85
 * @self: a #XbMachine
86
 * @str: operator string, e.g. `==`
87
 * @name: function name, e.g. `contains`
88
 *
89
 * Adds a new operator to the virtual machine. Operators can then be used
90
 * instead of explicit methods like `eq()`.
91
 *
92
 * You need to add a custom operator using xb_machine_add_operator() before
93
 * using xb_machine_parse(). Common operators like `<=` and `=` are built-in
94
 * and do not have to be added manually.
95
 *
96
 * Since: 0.1.1
97
 **/
98
void
99
xb_machine_add_operator(XbMachine *self, const gchar *str, const gchar *name)
100
0
{
101
0
  XbMachineOperator *op;
102
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
103
104
0
  g_return_if_fail(XB_IS_MACHINE(self));
105
0
  g_return_if_fail(str != NULL);
106
0
  g_return_if_fail(name != NULL);
107
108
0
  op = g_slice_new0(XbMachineOperator);
109
0
  op->str = g_strdup(str);
110
0
  op->strsz = strlen(str);
111
0
  op->name = g_strdup(name);
112
0
  g_ptr_array_add(priv->operators, op);
113
0
}
114
115
/**
116
 * xb_machine_add_method:
117
 * @self: a #XbMachine
118
 * @name: function name, e.g. `contains`
119
 * @n_opcodes: minimum number of opcodes required on the stack
120
 * @method_cb: function to call
121
 * @user_data: user pointer to pass to @method_cb, or %NULL
122
 * @user_data_free: a function which gets called to free @user_data, or %NULL
123
 *
124
 * Adds a new function to the virtual machine. Registered functions can then be
125
 * used as methods.
126
 *
127
 * The @method_cb must not modify the stack it’s passed unless it’s going to
128
 * succeed. In particular, if a method call is not optimisable, it must not
129
 * modify the stack it’s passed.
130
 *
131
 * You need to add a custom function using xb_machine_add_method() before using
132
 * methods that may reference it, for example xb_machine_add_opcode_fixup().
133
 *
134
 * Since: 0.1.1
135
 **/
136
void
137
xb_machine_add_method(XbMachine *self,
138
          const gchar *name,
139
          guint n_opcodes,
140
          XbMachineMethodFunc method_cb,
141
          gpointer user_data,
142
          GDestroyNotify user_data_free)
143
0
{
144
0
  XbMachineMethodItem *item;
145
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
146
147
0
  g_return_if_fail(XB_IS_MACHINE(self));
148
0
  g_return_if_fail(name != NULL);
149
0
  g_return_if_fail(method_cb != NULL);
150
151
0
  item = g_slice_new0(XbMachineMethodItem);
152
0
  item->idx = priv->methods->len;
153
0
  item->name = g_strdup(name);
154
0
  item->n_opcodes = n_opcodes;
155
0
  item->method_cb = method_cb;
156
0
  item->user_data = user_data;
157
0
  item->user_data_free = user_data_free;
158
0
  g_ptr_array_add(priv->methods, item);
159
0
}
160
161
/**
162
 * xb_machine_add_opcode_fixup:
163
 * @self: a #XbMachine
164
 * @opcodes_sig: signature, e.g. `INTE,TEXT`
165
 * @fixup_cb: callback
166
 * @user_data: user pointer to pass to @fixup_cb
167
 * @user_data_free: a function which gets called to free @user_data, or %NULL
168
 *
169
 * Adds an opcode fixup. Fixups can be used to optimize the stack of opcodes or
170
 * to add support for a nonstandard feature, for instance supporting missing
171
 * attributes to functions.
172
 *
173
 * Since: 0.1.1
174
 **/
175
void
176
xb_machine_add_opcode_fixup(XbMachine *self,
177
          const gchar *opcodes_sig,
178
          XbMachineOpcodeFixupFunc fixup_cb,
179
          gpointer user_data,
180
          GDestroyNotify user_data_free)
181
0
{
182
0
  XbMachineOpcodeFixupItem *item = g_slice_new0(XbMachineOpcodeFixupItem);
183
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
184
0
  item->fixup_cb = fixup_cb;
185
0
  item->user_data = user_data;
186
0
  item->user_data_free = user_data_free;
187
0
  g_hash_table_insert(priv->opcode_fixup, g_strdup(opcodes_sig), item);
188
0
}
189
190
/**
191
 * xb_machine_add_text_handler:
192
 * @self: a #XbMachine
193
 * @handler_cb: callback
194
 * @user_data: user pointer to pass to @handler_cb
195
 * @user_data_free: a function which gets called to free @user_data, or %NULL
196
 *
197
 * Adds a text handler. This allows the virtual machine to support nonstandard
198
 * encoding or shorthand mnemonics for standard functions.
199
 *
200
 * Since: 0.1.1
201
 **/
202
void
203
xb_machine_add_text_handler(XbMachine *self,
204
          XbMachineTextHandlerFunc handler_cb,
205
          gpointer user_data,
206
          GDestroyNotify user_data_free)
207
0
{
208
0
  XbMachineTextHandlerItem *item = g_slice_new0(XbMachineTextHandlerItem);
209
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
210
0
  item->handler_cb = handler_cb;
211
0
  item->user_data = user_data;
212
0
  item->user_data_free = user_data_free;
213
0
  g_ptr_array_add(priv->text_handlers, item);
214
0
}
215
216
static XbMachineMethodItem *
217
xb_machine_find_func(XbMachine *self, const gchar *func_name)
218
0
{
219
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
220
0
  for (guint i = 0; i < priv->methods->len; i++) {
221
0
    XbMachineMethodItem *item = g_ptr_array_index(priv->methods, i);
222
0
    if (g_strcmp0(item->name, func_name) == 0)
223
0
      return item;
224
0
  }
225
0
  return NULL;
226
0
}
227
228
/**
229
 * xb_machine_opcode_func_init:
230
 * @self: a #XbMachine
231
 * @opcode: (out caller-allocates): a stack allocated #XbOpcode to initialise
232
 * @func_name: function name, e.g. `eq`
233
 *
234
 * Initialises a stack allocated #XbOpcode for a registered function.
235
 * Some standard functions are registered by default, for instance `eq` or `ge`.
236
 * Other functions have to be added using xb_machine_add_method().
237
 *
238
 * Returns: %TRUE if the function was found and the opcode initialised, %FALSE
239
 *    otherwise
240
 * Since: 0.2.0
241
 **/
242
gboolean
243
xb_machine_opcode_func_init(XbMachine *self, XbOpcode *opcode, const gchar *func_name)
244
0
{
245
0
  XbMachineMethodItem *item = xb_machine_find_func(self, func_name);
246
0
  if (item == NULL)
247
0
    return FALSE;
248
0
  xb_opcode_init(opcode, XB_OPCODE_KIND_FUNCTION, g_strdup(func_name), item->idx, g_free);
249
0
  return TRUE;
250
0
}
251
252
static gboolean
253
xb_machine_parse_add_func(XbMachine *self,
254
        XbStack *opcodes,
255
        const gchar *func_name,
256
        guint8 level,
257
        GError **error)
258
0
{
259
0
  XbOpcode *opcode;
260
261
0
  if (!xb_stack_push(opcodes, &opcode, error))
262
0
    return FALSE;
263
264
  /* match opcode, which should always exist */
265
0
  if (!xb_machine_opcode_func_init(self, opcode, func_name)) {
266
0
    if (error != NULL) {
267
0
      g_set_error(error,
268
0
            G_IO_ERROR,
269
0
            G_IO_ERROR_NOT_SUPPORTED,
270
0
            "built-in function not found: %s",
271
0
            func_name);
272
0
    }
273
0
    xb_stack_pop(opcodes, NULL, NULL);
274
0
    return FALSE;
275
0
  }
276
0
  xb_opcode_set_level(opcode, level);
277
278
0
  return TRUE;
279
0
}
280
281
#if !GLIB_CHECK_VERSION(2, 54, 0)
282
static gboolean
283
str_has_sign(const gchar *str)
284
{
285
  return str[0] == '-' || str[0] == '+';
286
}
287
288
static gboolean
289
str_has_hex_prefix(const gchar *str)
290
{
291
  return str[0] == '0' && g_ascii_tolower(str[1]) == 'x';
292
}
293
294
static gboolean
295
g_ascii_string_to_unsigned(const gchar *str,
296
         guint base,
297
         guint64 min,
298
         guint64 max,
299
         guint64 *out_num,
300
         GError **error)
301
{
302
  const gchar *end_ptr = NULL;
303
  gint saved_errno = 0;
304
  guint64 number;
305
306
  g_return_val_if_fail(str != NULL, FALSE);
307
  g_return_val_if_fail(base >= 2 && base <= 36, FALSE);
308
  g_return_val_if_fail(min <= max, FALSE);
309
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
310
311
  if (str[0] == '\0') {
312
    g_set_error_literal(error,
313
            G_IO_ERROR,
314
            G_IO_ERROR_INVALID_DATA,
315
            "Empty string is not a number");
316
    return FALSE;
317
  }
318
319
  errno = 0;
320
  number = g_ascii_strtoull(str, (gchar **)&end_ptr, base);
321
  saved_errno = errno;
322
323
  if (g_ascii_isspace(str[0]) || str_has_sign(str) ||
324
      (base == 16 && str_has_hex_prefix(str)) ||
325
      (saved_errno != 0 && saved_errno != ERANGE) || end_ptr == NULL || *end_ptr != '\0') {
326
    if (error != NULL) {
327
      g_set_error(error,
328
            G_IO_ERROR,
329
            G_IO_ERROR_INVALID_DATA,
330
            "“%s” is not an unsigned number",
331
            str);
332
    }
333
    return FALSE;
334
  }
335
  if (saved_errno == ERANGE || number < min || number > max) {
336
    if (error != NULL) {
337
      g_autofree gchar *min_str = g_strdup_printf("%" G_GUINT64_FORMAT, min);
338
      g_autofree gchar *max_str = g_strdup_printf("%" G_GUINT64_FORMAT, max);
339
      g_set_error(error,
340
            G_IO_ERROR,
341
            G_IO_ERROR_INVALID_DATA,
342
            "Number “%s” is out of bounds [%s, %s]",
343
            str,
344
            min_str,
345
            max_str);
346
    }
347
    return FALSE;
348
  }
349
  if (out_num != NULL)
350
    *out_num = number;
351
  return TRUE;
352
}
353
#endif
354
355
static gboolean
356
xb_machine_parse_add_text(XbMachine *self,
357
        XbStack *opcodes,
358
        const gchar *text,
359
        gssize text_len,
360
        guint8 level,
361
        GError **error)
362
0
{
363
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
364
0
  g_autofree gchar *str = NULL;
365
0
  guint64 val;
366
367
  /* NULL is perfectly valid */
368
0
  if (text == NULL) {
369
0
    XbOpcode *opcode;
370
0
    if (!xb_stack_push(opcodes, &opcode, error))
371
0
      return FALSE;
372
0
    xb_opcode_text_init_static(opcode, str);
373
0
    return TRUE;
374
0
  }
375
376
  /* never add empty literals */
377
0
  if (text_len < 0)
378
0
    text_len = strlen(text);
379
0
  if (text_len == 0)
380
0
    return TRUE;
381
382
  /* do any additional handlers */
383
0
  str = g_strndup(text, text_len);
384
0
  for (guint i = 0; i < priv->text_handlers->len; i++) {
385
0
    XbMachineTextHandlerItem *item = g_ptr_array_index(priv->text_handlers, i);
386
0
    gboolean handled = FALSE;
387
0
    guint opcodes_sz = xb_stack_get_size(opcodes);
388
0
    if (!item->handler_cb(self, opcodes, str, &handled, item->user_data, error))
389
0
      return FALSE;
390
0
    if (handled) {
391
      /* ideally the XbMachineTextHandlerFunc would contain a `guint8 level` but
392
       * that is now public ABI. Just fixup the level for any added opcodes */
393
0
      for (guint j = xb_stack_get_size(opcodes); j > opcodes_sz; j--) {
394
0
        XbOpcode *op_tmp = xb_stack_peek(opcodes, j - 1);
395
0
        xb_opcode_set_level(op_tmp, level);
396
0
      }
397
0
      return TRUE;
398
0
    }
399
0
  }
400
401
  /* quoted text */
402
0
  if (text_len >= 2) {
403
0
    if (str[0] == '\'' && str[text_len - 1] == '\'') {
404
0
      g_autofree gchar *tmp = g_strndup(str + 1, text_len - 2);
405
0
      XbOpcode *opcode;
406
0
      if (!xb_stack_push(opcodes, &opcode, error))
407
0
        return FALSE;
408
0
      xb_opcode_text_init_steal(opcode, g_steal_pointer(&tmp));
409
0
      xb_opcode_set_level(opcode, level);
410
0
      return TRUE;
411
0
    }
412
0
  }
413
414
  /* indexed text */
415
0
  if (text_len >= 3) {
416
0
    if (str[0] == '$' && str[1] == '\'' && str[text_len - 1] == '\'') {
417
0
      g_autofree gchar *tmp = g_strndup(str + 2, text_len - 3);
418
0
      XbOpcode *opcode;
419
0
      if (!xb_stack_push(opcodes, &opcode, error))
420
0
        return FALSE;
421
0
      xb_opcode_init(opcode,
422
0
               XB_OPCODE_KIND_INDEXED_TEXT,
423
0
               g_steal_pointer(&tmp),
424
0
               XB_SILO_UNSET,
425
0
               g_free);
426
0
      xb_opcode_set_level(opcode, level);
427
0
      return TRUE;
428
0
    }
429
0
  }
430
431
  /* bind variables */
432
0
  if (g_strcmp0(str, "?") == 0) {
433
0
    XbOpcode *opcode;
434
0
    if (!xb_stack_push(opcodes, &opcode, error))
435
0
      return FALSE;
436
0
    xb_opcode_bind_init(opcode);
437
0
    xb_opcode_set_level(opcode, level);
438
0
    return TRUE;
439
0
  }
440
441
  /* check for plain integer */
442
0
  if (g_ascii_string_to_unsigned(str, 10, 0, G_MAXUINT32, &val, NULL)) {
443
0
    XbOpcode *opcode;
444
0
    if (!xb_stack_push(opcodes, &opcode, error))
445
0
      return FALSE;
446
0
    xb_opcode_integer_init(opcode, val);
447
0
    xb_opcode_set_level(opcode, level);
448
0
    return TRUE;
449
0
  }
450
451
  /* not supported */
452
0
  if (error != NULL) {
453
0
    g_set_error(error,
454
0
          G_IO_ERROR,
455
0
          G_IO_ERROR_NOT_SUPPORTED,
456
0
          "cannot parse text or number `%s`",
457
0
          str);
458
0
  }
459
0
  return FALSE;
460
0
}
461
462
static gboolean
463
xb_machine_parse_section(XbMachine *self,
464
       XbStack *opcodes,
465
       const gchar *text,
466
       gssize text_len,
467
       gboolean is_method,
468
       guint8 level,
469
       GError **error)
470
0
{
471
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
472
473
  /* fall back for simplicity */
474
0
  if (text_len < 0)
475
0
    text_len = strlen(text);
476
0
  if (text_len == 0)
477
0
    return TRUE;
478
479
0
  for (gssize i = 0; i < text_len; i++) {
480
0
    for (guint j = 0; j < priv->operators->len; j++) {
481
0
      XbMachineOperator *op = g_ptr_array_index(priv->operators, j);
482
0
      if (strncmp(text + i, op->str, op->strsz) != 0)
483
0
        continue;
484
0
      if (is_method) {
485
0
        XbOpcode *op_tail;
486
0
        const gchar *op_name = op->name;
487
488
        /* after then before */
489
0
        if (!xb_machine_parse_section(self,
490
0
                    opcodes,
491
0
                    text + i + op->strsz,
492
0
                    -1,
493
0
                    is_method,
494
0
                    level,
495
0
                    error))
496
0
          return FALSE;
497
0
        if (i > 0) {
498
0
          if (!xb_machine_parse_section(self,
499
0
                      opcodes,
500
0
                      text,
501
0
                      i,
502
0
                      FALSE,
503
0
                      level,
504
0
                      error))
505
0
            return FALSE;
506
0
        }
507
508
        /* multiple "eq" sections are converted to "in" */
509
0
        op_tail = xb_stack_peek_tail(opcodes);
510
0
        if (op_tail != NULL && _xb_opcode_get_level(op_tail) != level &&
511
0
            g_strcmp0(op_name, "eq") == 0)
512
0
          op_name = "in";
513
0
        if (!xb_machine_parse_add_func(self,
514
0
                     opcodes,
515
0
                     op_name,
516
0
                     level,
517
0
                     error))
518
0
          return FALSE;
519
0
      } else {
520
        /* before then after */
521
0
        if (i > 0) {
522
0
          if (!xb_machine_parse_section(self,
523
0
                      opcodes,
524
0
                      text,
525
0
                      i,
526
0
                      FALSE,
527
0
                      level,
528
0
                      error))
529
0
            return FALSE;
530
0
        }
531
0
        if (!xb_machine_parse_section(self,
532
0
                    opcodes,
533
0
                    text + i + op->strsz,
534
0
                    -1,
535
0
                    is_method,
536
0
                    level,
537
0
                    error))
538
0
          return FALSE;
539
0
        if (!xb_machine_parse_add_func(self,
540
0
                     opcodes,
541
0
                     op->name,
542
0
                     level,
543
0
                     error))
544
0
          return FALSE;
545
0
      }
546
0
      return TRUE;
547
0
    }
548
0
  }
549
550
  /* nothing matched */
551
0
  if (is_method) {
552
0
    g_autoptr(GError) error_local = NULL;
553
0
    if (!xb_machine_parse_add_text(self,
554
0
                 opcodes,
555
0
                 text,
556
0
                 text_len,
557
0
                 level,
558
0
                 &error_local)) {
559
0
      if (priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_PARSING)
560
0
        g_debug("Failed to add text %s, trying function", text);
561
0
      return xb_machine_parse_add_func(self, opcodes, text, level, error);
562
0
    }
563
0
    return TRUE;
564
0
  }
565
0
  return xb_machine_parse_add_text(self, opcodes, text, text_len, level, error);
566
0
}
567
568
static gboolean
569
xb_machine_parse_sections(XbMachine *self,
570
        XbStack *opcodes,
571
        const gchar *text,
572
        gsize text_len,
573
        gboolean is_method,
574
        guint8 level,
575
        GError **error)
576
0
{
577
0
  g_autofree gchar *tmp = NULL;
578
0
  if (text_len == 0)
579
0
    return TRUE;
580
581
  /* leading comma */
582
0
  if (text[0] == ',') {
583
0
    tmp = g_strndup(text + 1, text_len - 1);
584
0
  } else {
585
0
    tmp = g_strndup(text, text_len);
586
0
  }
587
0
  for (gint i = text_len - 1; i >= 0; i--) {
588
0
    if (tmp[i] == ',') {
589
0
      tmp[i] = '\0';
590
0
      if (is_method) {
591
0
        if (!xb_machine_parse_add_func(self,
592
0
                     opcodes,
593
0
                     tmp + i + 1,
594
0
                     level,
595
0
                     error))
596
0
          return FALSE;
597
0
        is_method = FALSE;
598
0
      } else {
599
0
        if (!xb_machine_parse_section(self,
600
0
                    opcodes,
601
0
                    tmp + i + 1,
602
0
                    -1,
603
0
                    TRUE,
604
0
                    level,
605
0
                    error))
606
0
          return FALSE;
607
0
      }
608
0
    }
609
0
  }
610
0
  if (tmp[0] != '\0') {
611
0
    if (!xb_machine_parse_section(self, opcodes, tmp, -1, is_method, level, error))
612
0
      return FALSE;
613
0
  }
614
0
  return TRUE;
615
0
}
616
617
static gchar *
618
xb_machine_get_opcodes_sig(XbMachine *self, XbStack *opcodes)
619
0
{
620
0
  GString *str = g_string_new(NULL);
621
0
  for (guint i = 0; i < xb_stack_get_size(opcodes); i++) {
622
0
    XbOpcode *op = xb_stack_peek(opcodes, i);
623
0
    g_autofree gchar *sig = xb_opcode_get_sig(op);
624
0
    g_string_append_printf(str, "%s,", sig);
625
0
  }
626
0
  if (str->len > 0)
627
0
    g_string_truncate(str, str->len - 1);
628
0
  return g_string_free(str, FALSE);
629
0
}
630
631
/* @results *must* have enough space
632
 * @op is transfer full into this function */
633
static gboolean
634
xb_machine_opcodes_optimize_fn(XbMachine *self,
635
             XbStack *opcodes,
636
             XbOpcode op,
637
             XbStack *results,
638
             GError **error)
639
0
{
640
0
  XbMachineMethodItem *item;
641
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
642
0
  g_autofree gchar *stack_str = NULL;
643
0
  g_autoptr(GError) error_local = NULL;
644
0
  g_auto(XbOpcode) op_result = XB_OPCODE_INIT();
645
0
  g_auto(XbOpcode) op_owned = op;
646
647
  /* not a function */
648
0
  if (_xb_opcode_get_kind(&op) != XB_OPCODE_KIND_FUNCTION) {
649
0
    XbOpcode *op_out;
650
0
    if (!xb_stack_push(results, &op_out, error))
651
0
      return FALSE;
652
0
    *op_out = xb_opcode_steal(&op_owned);
653
0
    return TRUE;
654
0
  }
655
656
  /* get function, check if we have enough arguments */
657
0
  item = g_ptr_array_index(priv->methods, _xb_opcode_get_val(&op));
658
0
  if (item->n_opcodes > xb_stack_get_size(opcodes)) {
659
0
    g_set_error_literal(error,
660
0
            G_IO_ERROR,
661
0
            G_IO_ERROR_INVALID_DATA,
662
0
            "predicate invalid -- not enough args");
663
0
    return FALSE;
664
0
  }
665
666
  /* run the method. it's only supposed to pop its arguments off the stack
667
   * if it can complete successfully */
668
0
  if (!item->method_cb(self, opcodes, NULL, item->user_data, NULL, &error_local)) {
669
0
    XbOpcode *op_out;
670
671
0
    if (priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_OPTIMIZER) {
672
0
      stack_str = xb_stack_to_string(opcodes);
673
0
      g_debug("ignoring optimized call to %s(%s): %s",
674
0
        item->name,
675
0
        stack_str,
676
0
        error_local->message);
677
0
    }
678
0
    if (!xb_stack_push(results, &op_out, error))
679
0
      return FALSE;
680
0
    *op_out = xb_opcode_steal(&op_owned);
681
0
    return TRUE;
682
0
  }
683
684
  /* the method ran, add the result. the arguments have already been popped */
685
0
  if (!xb_machine_stack_pop(self, opcodes, &op_result, error))
686
0
    return FALSE;
687
0
  if (_xb_opcode_get_kind(&op_result) != XB_OPCODE_KIND_BOOLEAN ||
688
0
      _xb_opcode_get_val(&op_result)) {
689
0
    XbOpcode *op_out;
690
691
0
    if (priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_OPTIMIZER) {
692
0
      g_autofree gchar *tmp = xb_opcode_to_string(&op_result);
693
0
      g_debug("method ran, adding result %s", tmp);
694
0
    }
695
0
    if (!xb_stack_push(results, &op_out, error))
696
0
      return FALSE;
697
0
    xb_opcode_set_level(&op_result, _xb_opcode_get_level(&op));
698
0
    *op_out = xb_opcode_steal(&op_result);
699
700
0
    return TRUE;
701
0
  }
702
703
  /* the predicate will always evalulate to FALSE */
704
0
  if (error != NULL) {
705
0
    stack_str = xb_stack_to_string(opcodes);
706
0
    g_set_error(error,
707
0
          G_IO_ERROR,
708
0
          G_IO_ERROR_INVALID_DATA,
709
0
          "the predicate will always evalulate to FALSE: %s",
710
0
          stack_str);
711
0
  }
712
0
  return FALSE;
713
0
}
714
715
static gboolean
716
xb_machine_opcodes_optimize(XbMachine *self, XbStack *opcodes, GError **error)
717
0
{
718
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
719
0
  g_autoptr(XbStack) results = xb_stack_new_inline(xb_stack_get_size(opcodes));
720
0
  g_auto(XbOpcode) op = XB_OPCODE_INIT();
721
722
  /* debug */
723
0
  if (priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_OPTIMIZER) {
724
0
    g_autofree gchar *str = xb_stack_to_string(opcodes);
725
0
    g_debug("before optimizing: %s", str);
726
0
  }
727
728
  /* process the stack in reverse order */
729
0
  while (xb_machine_stack_pop(self, opcodes, &op, NULL)) {
730
    /* this takes ownership of @op */
731
0
    if (!xb_machine_opcodes_optimize_fn(self,
732
0
                opcodes,
733
0
                xb_opcode_steal(&op),
734
0
                results,
735
0
                error))
736
0
      return FALSE;
737
0
  }
738
739
  /* copy back the result into the opcodes stack (and reverse it) */
740
0
  while (xb_stack_pop(results, &op, NULL)) {
741
0
    XbOpcode *op_out;
742
0
    if (!xb_stack_push(opcodes, &op_out, error))
743
0
      return FALSE;
744
0
    *op_out = xb_opcode_steal(&op);
745
0
  }
746
747
  /* debug */
748
0
  if (priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_OPTIMIZER) {
749
0
    g_autofree gchar *str = xb_stack_to_string(opcodes);
750
0
    g_debug("after optimizing: %s", str);
751
0
  }
752
0
  return TRUE;
753
0
}
754
755
static gsize
756
xb_machine_parse_text(XbMachine *self,
757
          XbStack *opcodes,
758
          const gchar *text,
759
          gsize text_len,
760
          guint8 level,
761
          GError **error)
762
0
{
763
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
764
0
  guint tail = 0;
765
766
  /* sanity check */
767
0
  if (level > XB_MACHINE_STACK_LEVELS_MAX) {
768
0
    if (error != NULL) {
769
0
      g_autofree gchar *tmp = g_strndup(text, text_len);
770
0
      g_set_error(error,
771
0
            G_IO_ERROR,
772
0
            G_IO_ERROR_INVALID_DATA,
773
0
            "nesting deeper than 20 levels supported: %s",
774
0
            tmp);
775
0
    }
776
0
    return G_MAXSIZE;
777
0
  }
778
0
  for (guint i = 0; i < text_len; i++) {
779
0
    if (priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_PARSING)
780
0
      g_debug("LVL %u\t%u:\t\t%c", level, i, text[i]);
781
0
    if (text[i] == '(') {
782
0
      gsize j = 0;
783
0
      j = xb_machine_parse_text(self,
784
0
              opcodes,
785
0
              text + i + 1,
786
0
              text_len - i,
787
0
              level + 1,
788
0
              error);
789
0
      if (j == G_MAXSIZE)
790
0
        return G_MAXSIZE;
791
0
      if (!xb_machine_parse_sections(self,
792
0
                   opcodes,
793
0
                   text + tail,
794
0
                   i - tail,
795
0
                   TRUE,
796
0
                   level,
797
0
                   error))
798
0
        return G_MAXSIZE;
799
0
      i += j;
800
0
      tail = i + 1;
801
0
      continue;
802
0
    }
803
0
    if (text[i] == ')') {
804
0
      if (!xb_machine_parse_sections(self,
805
0
                   opcodes,
806
0
                   text + tail,
807
0
                   i - tail,
808
0
                   FALSE,
809
0
                   level,
810
0
                   error))
811
0
        return G_MAXSIZE;
812
0
      return i + 1;
813
0
    }
814
0
  }
815
0
  if (tail != text_len && level > 0) {
816
0
    if (error != NULL) {
817
0
      g_autofree gchar *tmp = g_strndup(text, text_len);
818
0
      g_set_error(error,
819
0
            G_IO_ERROR,
820
0
            G_IO_ERROR_INVALID_DATA,
821
0
            "brackets did not match: %s",
822
0
            tmp);
823
0
    }
824
0
    return G_MAXSIZE;
825
0
  }
826
0
  if (!xb_machine_parse_sections(self,
827
0
               opcodes,
828
0
               text + tail,
829
0
               text_len - tail,
830
0
               FALSE,
831
0
               level,
832
0
               error))
833
0
    return G_MAXSIZE;
834
0
  return 0;
835
0
}
836
837
/**
838
 * xb_machine_parse_full:
839
 * @self: a #XbMachine
840
 * @text: predicate to parse, e.g. `contains(text(),'xyx')`
841
 * @text_len: length of @text, or -1 if @text is `NUL` terminated
842
 * @flags: #XbMachineParseFlags, e.g. %XB_MACHINE_PARSE_FLAG_OPTIMIZE
843
 * @error: a #GError, or %NULL
844
 *
845
 * Parses an XPath predicate. Not all of XPath 1.0 or XPath 1.0 is supported,
846
 * and new functions and mnemonics can be added using xb_machine_add_method()
847
 * and xb_machine_add_text_handler().
848
 *
849
 * Returns: (transfer full): opcodes, or %NULL on error
850
 *
851
 * Since: 0.1.4
852
 **/
853
XbStack *
854
xb_machine_parse_full(XbMachine *self,
855
          const gchar *text,
856
          gssize text_len,
857
          XbMachineParseFlags flags,
858
          GError **error)
859
0
{
860
0
  XbMachineOpcodeFixupItem *item;
861
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
862
0
  guint8 level = 0;
863
0
  g_autoptr(XbStack) opcodes = NULL;
864
0
  g_autofree gchar *opcodes_sig = NULL;
865
866
0
  g_return_val_if_fail(XB_IS_MACHINE(self), NULL);
867
0
  g_return_val_if_fail(text != NULL, NULL);
868
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
869
870
  /* assume NUL terminated */
871
0
  if (text_len < 0)
872
0
    text_len = strlen(text);
873
0
  if (text_len == 0) {
874
0
    g_set_error_literal(error,
875
0
            G_IO_ERROR,
876
0
            G_IO_ERROR_INVALID_DATA,
877
0
            "string was zero size");
878
0
    return NULL;
879
0
  }
880
881
  /* parse into opcodes */
882
0
  opcodes = xb_stack_new(priv->stack_size);
883
0
  if (xb_machine_parse_text(self, opcodes, text, text_len, level, error) == G_MAXSIZE)
884
0
    return NULL;
885
886
  /* do any fixups */
887
0
  opcodes_sig = xb_machine_get_opcodes_sig(self, opcodes);
888
0
  if (priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_OPTIMIZER)
889
0
    g_debug("opcodes_sig=%s", opcodes_sig);
890
0
  item = g_hash_table_lookup(priv->opcode_fixup, opcodes_sig);
891
0
  if (item != NULL) {
892
0
    if (!item->fixup_cb(self, opcodes, item->user_data, error))
893
0
      return NULL;
894
0
  }
895
896
  /* optimize */
897
0
  if (flags & XB_MACHINE_PARSE_FLAG_OPTIMIZE) {
898
0
    for (guint i = 0; i < 10; i++) {
899
0
      guint oldsz = xb_stack_get_size(opcodes);
900
901
      /* Is the stack optimal already? */
902
0
      if (oldsz == 1)
903
0
        break;
904
905
0
      if (!xb_machine_opcodes_optimize(self, opcodes, error))
906
0
        return NULL;
907
0
      if (oldsz == xb_stack_get_size(opcodes))
908
0
        break;
909
0
    }
910
0
  }
911
912
  /* success */
913
0
  return g_steal_pointer(&opcodes);
914
0
}
915
916
/**
917
 * xb_machine_parse:
918
 * @self: a #XbMachine
919
 * @text: predicate to parse, e.g. `contains(text(),'xyx')`
920
 * @text_len: length of @text, or -1 if @text is `NUL` terminated
921
 * @error: a #GError, or %NULL
922
 *
923
 * Parses an XPath predicate. Not all of XPath 1.0 or XPath 1.0 is supported,
924
 * and new functions and mnemonics can be added using xb_machine_add_method()
925
 * and xb_machine_add_text_handler().
926
 *
927
 * Returns: (transfer full): opcodes, or %NULL on error
928
 *
929
 * Since: 0.1.1
930
 **/
931
XbStack *
932
xb_machine_parse(XbMachine *self, const gchar *text, gssize text_len, GError **error)
933
0
{
934
0
  return xb_machine_parse_full(self, text, text_len, XB_MACHINE_PARSE_FLAG_OPTIMIZE, error);
935
0
}
936
937
static void
938
xb_machine_debug_show_stack(XbMachine *self, XbStack *stack)
939
0
{
940
0
  g_autofree gchar *str = NULL;
941
0
  if (xb_stack_get_size(stack) == 0) {
942
0
    g_debug("stack is empty");
943
0
    return;
944
0
  }
945
0
  str = xb_stack_to_string(stack);
946
0
  g_debug("stack: %s", str);
947
0
}
948
949
static gboolean
950
xb_machine_run_func(XbMachine *self,
951
        XbStack *stack,
952
        XbOpcode *opcode,
953
        gpointer exec_data,
954
        GError **error)
955
0
{
956
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
957
0
  XbMachineMethodItem *item = g_ptr_array_index(priv->methods, _xb_opcode_get_val(opcode));
958
959
  /* optional debugging */
960
0
  if (priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_STACK) {
961
0
    g_autofree gchar *str = xb_opcode_to_string(opcode);
962
0
    g_debug("running: %s", str);
963
0
    xb_machine_debug_show_stack(self, stack);
964
0
  }
965
966
  /* check we have enough stack elements */
967
0
  if (item->n_opcodes > xb_stack_get_size(stack)) {
968
0
    if (error != NULL) {
969
0
      g_set_error(error,
970
0
            G_IO_ERROR,
971
0
            G_IO_ERROR_NOT_SUPPORTED,
972
0
            "function required %u arguments, stack only has %u",
973
0
            item->n_opcodes,
974
0
            xb_stack_get_size(stack));
975
0
    }
976
0
    return FALSE;
977
0
  }
978
0
  if (!item->method_cb(self, stack, NULL, item->user_data, exec_data, error)) {
979
0
    g_prefix_error(error, "failed to call %s(): ", item->name);
980
0
    return FALSE;
981
0
  }
982
0
  return TRUE;
983
0
}
984
985
/**
986
 * xb_machine_run:
987
 * @self: a #XbMachine
988
 * @opcodes: a #XbStack of opcodes
989
 * @result: (out): return status after running @opcodes
990
 * @exec_data: per-run user data that is passed to all the #XbMachineMethodFunc functions
991
 * @error: a #GError, or %NULL
992
 *
993
 * Runs a set of opcodes on the virtual machine.
994
 *
995
 * It is safe to call this function from a different thread to the one that
996
 * created the #XbMachine.
997
 *
998
 * Returns: a new #XbOpcode, or %NULL
999
 *
1000
 * Since: 0.1.1
1001
 * Deprecated: 0.3.0: Use xb_machine_run_with_bindings() instead.
1002
 **/
1003
gboolean
1004
xb_machine_run(XbMachine *self,
1005
         XbStack *opcodes,
1006
         gboolean *result,
1007
         gpointer exec_data,
1008
         GError **error)
1009
0
{
1010
0
  return xb_machine_run_with_bindings(self, opcodes, NULL, result, exec_data, error);
1011
0
}
1012
1013
/**
1014
 * xb_machine_run_with_bindings:
1015
 * @self: a #XbMachine
1016
 * @opcodes: a #XbStack of opcodes
1017
 * @bindings: (nullable) (transfer none): values bound to opcodes of type
1018
 *     %XB_OPCODE_KIND_BOUND_INTEGER or %XB_OPCODE_KIND_BOUND_TEXT, or %NULL if
1019
 *     the query doesn’t need any bound values
1020
 * @result: (out): return status after running @opcodes
1021
 * @exec_data: per-run user data that is passed to all the #XbMachineMethodFunc functions
1022
 * @error: a #GError, or %NULL
1023
 *
1024
 * Runs a set of opcodes on the virtual machine, using the bound values given in
1025
 * @bindings to substitute for bound opcodes.
1026
 *
1027
 * It is safe to call this function from a different thread to the one that
1028
 * created the #XbMachine.
1029
 *
1030
 * Returns: a new #XbOpcode, or %NULL
1031
 *
1032
 * Since: 0.3.0
1033
 **/
1034
gboolean
1035
xb_machine_run_with_bindings(XbMachine *self,
1036
           XbStack *opcodes,
1037
           XbValueBindings *bindings,
1038
           gboolean *result,
1039
           gpointer exec_data,
1040
           GError **error)
1041
0
{
1042
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
1043
0
  g_auto(XbOpcode) opcode_success = XB_OPCODE_INIT();
1044
0
  g_autoptr(XbStack) stack = NULL;
1045
0
  guint opcodes_stack_size = xb_stack_get_size(opcodes);
1046
0
  guint bound_opcode_idx = 0;
1047
1048
0
  g_return_val_if_fail(XB_IS_MACHINE(self), FALSE);
1049
0
  g_return_val_if_fail(opcodes != NULL, FALSE);
1050
0
  g_return_val_if_fail(result != NULL, FALSE);
1051
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1052
1053
  /* process each opcode */
1054
0
  stack = xb_stack_new_inline(priv->stack_size);
1055
0
  for (guint i = 0; i < opcodes_stack_size; i++) {
1056
0
    XbOpcode *opcode = xb_stack_peek(opcodes, i);
1057
0
    XbOpcodeKind kind = _xb_opcode_get_kind(opcode);
1058
1059
    /* replace post-0.3.0-style bound opcodes with their bound values */
1060
0
    if (bindings != NULL && (kind == XB_OPCODE_KIND_BOUND_TEXT ||
1061
0
           kind == XB_OPCODE_KIND_BOUND_INDEXED_TEXT ||
1062
0
           kind == XB_OPCODE_KIND_BOUND_INTEGER)) {
1063
0
      XbOpcode *machine_opcode;
1064
0
      if (!xb_machine_stack_push(self, stack, &machine_opcode, error))
1065
0
        return FALSE;
1066
0
      if (!xb_value_bindings_lookup_opcode(bindings,
1067
0
                   bound_opcode_idx++,
1068
0
                   machine_opcode)) {
1069
0
        if (error != NULL) {
1070
0
          g_autofree gchar *tmp1 = xb_stack_to_string(stack);
1071
0
          g_autofree gchar *tmp2 = xb_stack_to_string(opcodes);
1072
0
          g_set_error(
1073
0
              error,
1074
0
              G_IO_ERROR,
1075
0
              G_IO_ERROR_INVALID_DATA,
1076
0
              "opcode was not bound at runtime, stack:%s, opcodes:%s",
1077
0
              tmp1,
1078
0
              tmp2);
1079
0
        }
1080
0
        return FALSE;
1081
0
      }
1082
0
      continue;
1083
0
    }
1084
0
    if (kind == XB_OPCODE_KIND_BOUND_UNSET) {
1085
0
      if (error != NULL) {
1086
0
        g_autofree gchar *tmp1 = xb_stack_to_string(stack);
1087
0
        g_autofree gchar *tmp2 = xb_stack_to_string(opcodes);
1088
0
        g_set_error(error,
1089
0
              G_IO_ERROR,
1090
0
              G_IO_ERROR_INVALID_DATA,
1091
0
              "opcode was not bound at runtime, stack:%s, opcodes:%s",
1092
0
              tmp1,
1093
0
              tmp2);
1094
0
      }
1095
0
      return FALSE;
1096
0
    }
1097
1098
    /* process the stack */
1099
0
    if (kind == XB_OPCODE_KIND_FUNCTION) {
1100
0
      if (!xb_machine_run_func(self, stack, opcode, exec_data, error))
1101
0
        return FALSE;
1102
0
      continue;
1103
0
    }
1104
1105
    /* add to stack; this uses a const copy of the input opcode,
1106
     * so ownership of anything allocated on the heap remains with
1107
     * the caller */
1108
0
    if (kind == XB_OPCODE_KIND_TEXT || kind == XB_OPCODE_KIND_BOOLEAN ||
1109
0
        kind == XB_OPCODE_KIND_INTEGER || kind == XB_OPCODE_KIND_INDEXED_TEXT ||
1110
0
        (bindings == NULL && (kind == XB_OPCODE_KIND_BOUND_TEXT ||
1111
0
            kind == XB_OPCODE_KIND_BOUND_INDEXED_TEXT ||
1112
0
            kind == XB_OPCODE_KIND_BOUND_INTEGER))) {
1113
0
      XbOpcode *machine_opcode;
1114
0
      if (!xb_machine_stack_push(self, stack, &machine_opcode, error))
1115
0
        return FALSE;
1116
0
      *machine_opcode = *opcode;
1117
0
      machine_opcode->destroy_func = NULL;
1118
0
      continue;
1119
0
    }
1120
1121
    /* invalid */
1122
0
    if (error != NULL) {
1123
0
      g_set_error(error,
1124
0
            G_IO_ERROR,
1125
0
            G_IO_ERROR_INVALID_DATA,
1126
0
            "opcode kind %u not recognised",
1127
0
            kind);
1128
0
    }
1129
0
    return FALSE;
1130
0
  }
1131
1132
  /* the stack should have one boolean left on the stack */
1133
0
  if (xb_stack_get_size(stack) != 1) {
1134
0
    if (error != NULL) {
1135
0
      g_autofree gchar *tmp = xb_stack_to_string(stack);
1136
0
      g_set_error(error,
1137
0
            G_IO_ERROR,
1138
0
            G_IO_ERROR_INVALID_DATA,
1139
0
            "%u opcodes remain on the stack (%s)",
1140
0
            xb_stack_get_size(stack),
1141
0
            tmp);
1142
0
    }
1143
0
    return FALSE;
1144
0
  }
1145
0
  if (!xb_stack_pop(stack, &opcode_success, error))
1146
0
    return FALSE;
1147
0
  if (_xb_opcode_get_kind(&opcode_success) != XB_OPCODE_KIND_BOOLEAN) {
1148
0
    if (error != NULL) {
1149
0
      g_autofree gchar *tmp = xb_stack_to_string(stack);
1150
0
      g_set_error(error,
1151
0
            G_IO_ERROR,
1152
0
            G_IO_ERROR_INVALID_DATA,
1153
0
            "Expected boolean, got: %s",
1154
0
            tmp);
1155
0
    }
1156
0
    return FALSE;
1157
0
  }
1158
0
  *result = _xb_opcode_get_val(&opcode_success);
1159
1160
  /* success */
1161
0
  return TRUE;
1162
0
}
1163
1164
/**
1165
 * xb_machine_stack_pop:
1166
 * @self: a #XbMachine
1167
 * @stack: a #XbStack
1168
 * @opcode_out: (out caller-allocates) (optional): return location for the popped #XbOpcode
1169
 * @error: a #GError, or %NULL
1170
 *
1171
 * Pops an opcode from the stack.
1172
 *
1173
 * Returns: %TRUE if popping succeeded, %FALSE if the stack was empty already
1174
 *
1175
 * Since: 0.2.0
1176
 **/
1177
gboolean
1178
xb_machine_stack_pop(XbMachine *self, XbStack *stack, XbOpcode *opcode_out, GError **error)
1179
0
{
1180
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
1181
0
  gboolean retval;
1182
1183
0
  if (priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_STACK) {
1184
0
    XbOpcode *opcode_peek = xb_stack_peek(stack, xb_stack_get_size(stack) - 1);
1185
0
    if (opcode_peek != NULL) {
1186
0
      g_autofree gchar *str = xb_opcode_to_string(opcode_peek);
1187
0
      g_debug("popping: %s", str);
1188
0
    } else {
1189
0
      g_debug("not popping: stack empty");
1190
0
    }
1191
0
  }
1192
1193
0
  retval = xb_stack_pop(stack, opcode_out, error);
1194
1195
0
  if (priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_STACK)
1196
0
    xb_machine_debug_show_stack(self, stack);
1197
1198
0
  return retval;
1199
0
}
1200
1201
/**
1202
 * xb_machine_stack_pop_two: (skip):
1203
 **/
1204
gboolean
1205
xb_machine_stack_pop_two(XbMachine *self,
1206
       XbStack *stack,
1207
       XbOpcode *opcode1_out,
1208
       XbOpcode *opcode2_out,
1209
       GError **error)
1210
0
{
1211
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
1212
0
  gboolean retval;
1213
1214
0
  if (priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_STACK) {
1215
0
    XbOpcode *opcode_peek1 = xb_stack_peek(stack, xb_stack_get_size(stack) - 1);
1216
0
    XbOpcode *opcode_peek2 = xb_stack_peek(stack, xb_stack_get_size(stack) - 2);
1217
0
    if (opcode_peek1 != NULL && opcode_peek2 != NULL) {
1218
0
      g_autofree gchar *str1 = xb_opcode_to_string(opcode_peek1);
1219
0
      g_autofree gchar *str2 = xb_opcode_to_string(opcode_peek2);
1220
0
      g_debug("popping1: %s", str1);
1221
0
      g_debug("popping2: %s", str2);
1222
0
    } else {
1223
0
      g_debug("not popping: stack empty");
1224
0
    }
1225
0
  }
1226
1227
0
  retval = xb_stack_pop_two(stack, opcode1_out, opcode2_out, error);
1228
1229
0
  if (priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_STACK)
1230
0
    xb_machine_debug_show_stack(self, stack);
1231
1232
0
  return retval;
1233
0
}
1234
1235
/**
1236
 * xb_machine_stack_push:
1237
 * @self: a #XbMachine
1238
 * @stack: a #XbStack
1239
 * @opcode_out: (out) (nullable): return location for the new #XbOpcode
1240
 * @error: return location for a #GError, or %NULL
1241
 *
1242
 * Pushes a new empty opcode onto the end of the stack. A pointer to the opcode
1243
 * is returned in @opcode_out so that the caller can initialise it.
1244
 *
1245
 * If the stack reaches its maximum size, %G_IO_ERROR_NO_SPACE will be returned.
1246
 *
1247
 * Returns: %TRUE if a new empty opcode was returned, or %FALSE if the stack has
1248
 *    reached its maximum size
1249
 * Since: 0.2.0
1250
 **/
1251
gboolean
1252
xb_machine_stack_push(XbMachine *self, XbStack *stack, XbOpcode **opcode_out, GError **error)
1253
0
{
1254
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
1255
1256
0
  if (G_UNLIKELY(priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_STACK)) {
1257
0
    g_debug("pushing generic opcode");
1258
0
  }
1259
1260
0
  return xb_stack_push(stack, opcode_out, error);
1261
0
}
1262
1263
/**
1264
 * xb_machine_stack_push_text:
1265
 * @self: a #XbMachine
1266
 * @stack: a #XbStack
1267
 * @str: text literal
1268
 * @error: return location for a #GError, or %NULL
1269
 *
1270
 * Adds a text literal to the stack, copying @str.
1271
 *
1272
 * Errors are as for xb_machine_stack_push().
1273
 *
1274
 * Returns: %TRUE on success, %FALSE otherwise
1275
 * Since: 0.2.0
1276
 **/
1277
gboolean
1278
xb_machine_stack_push_text(XbMachine *self, XbStack *stack, const gchar *str, GError **error)
1279
0
{
1280
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
1281
0
  XbOpcode *opcode;
1282
1283
0
  if (G_UNLIKELY(priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_STACK))
1284
0
    g_debug("pushing: %s", str);
1285
1286
0
  if (!xb_stack_push(stack, &opcode, error))
1287
0
    return FALSE;
1288
0
  xb_opcode_text_init(opcode, str);
1289
0
  if (G_UNLIKELY(priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_STACK))
1290
0
    xb_machine_debug_show_stack(self, stack);
1291
1292
0
  return TRUE;
1293
0
}
1294
1295
/**
1296
 * xb_machine_stack_push_text_static:
1297
 * @self: a #XbMachine
1298
 * @stack: a #XbStack
1299
 * @str: text literal
1300
 * @error: return location for a #GError, or %NULL
1301
 *
1302
 * Adds static text literal to the stack.
1303
 *
1304
 * Errors are as for xb_machine_stack_push().
1305
 *
1306
 * Returns: %TRUE on success, %FALSE otherwise
1307
 * Since: 0.2.0
1308
 **/
1309
gboolean
1310
xb_machine_stack_push_text_static(XbMachine *self, XbStack *stack, const gchar *str, GError **error)
1311
0
{
1312
0
  XbOpcode *opcode;
1313
1314
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
1315
0
  if (G_UNLIKELY(priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_STACK))
1316
0
    g_debug("pushing: %s", str);
1317
1318
0
  if (!xb_stack_push(stack, &opcode, error))
1319
0
    return FALSE;
1320
0
  xb_opcode_text_init_static(opcode, str);
1321
0
  if (priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_STACK)
1322
0
    xb_machine_debug_show_stack(self, stack);
1323
1324
0
  return TRUE;
1325
0
}
1326
1327
/**
1328
 * xb_machine_stack_push_text_steal:
1329
 * @self: a #XbMachine
1330
 * @stack: a #XbStack
1331
 * @str: (transfer full): text literal
1332
 * @error: return location for a #GError, or %NULL
1333
 *
1334
 * Adds a stolen text literal to the stack.
1335
 *
1336
 * Errors are as for xb_machine_stack_push().
1337
 *
1338
 * Returns: %TRUE on success, %FALSE otherwise
1339
 * Since: 0.2.0
1340
 **/
1341
gboolean
1342
xb_machine_stack_push_text_steal(XbMachine *self, XbStack *stack, gchar *str, GError **error)
1343
0
{
1344
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
1345
0
  XbOpcode *opcode;
1346
0
  g_autofree gchar *str_stolen = g_steal_pointer(&str);
1347
1348
0
  if (G_UNLIKELY(priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_STACK))
1349
0
    g_debug("pushing: %s", str_stolen);
1350
1351
0
  if (!xb_stack_push(stack, &opcode, error))
1352
0
    return FALSE;
1353
0
  xb_opcode_text_init_steal(opcode, g_steal_pointer(&str_stolen));
1354
0
  if (priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_STACK)
1355
0
    xb_machine_debug_show_stack(self, stack);
1356
1357
0
  return TRUE;
1358
0
}
1359
1360
/**
1361
 * xb_machine_stack_push_integer:
1362
 * @self: a #XbMachine
1363
 * @stack: a #XbStack
1364
 * @val: integer literal
1365
 * @error: return location for a #GError, or %NULL
1366
 *
1367
 * Adds an integer literal to the stack.
1368
 *
1369
 * Errors are as for xb_machine_stack_push().
1370
 *
1371
 * Returns: %TRUE on success, %FALSE otherwise
1372
 * Since: 0.2.0
1373
 **/
1374
gboolean
1375
xb_machine_stack_push_integer(XbMachine *self, XbStack *stack, guint32 val, GError **error)
1376
0
{
1377
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
1378
0
  XbOpcode *opcode;
1379
1380
0
  if (G_UNLIKELY(priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_STACK))
1381
0
    g_debug("pushing: %u", val);
1382
1383
0
  if (!xb_stack_push(stack, &opcode, error))
1384
0
    return FALSE;
1385
0
  xb_opcode_integer_init(opcode, val);
1386
0
  if (priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_STACK)
1387
0
    xb_machine_debug_show_stack(self, stack);
1388
1389
0
  return TRUE;
1390
0
}
1391
1392
/**
1393
 * xb_machine_set_stack_size:
1394
 * @self: a #XbMachine
1395
 * @stack_size: integer
1396
 *
1397
 * Sets the maximum stack size used for the machine.
1398
 *
1399
 * The stack size will be affective for new jobs started with xb_machine_run()
1400
 * and xb_machine_parse().
1401
 *
1402
 * Since: 0.1.3
1403
 **/
1404
void
1405
xb_machine_set_stack_size(XbMachine *self, guint stack_size)
1406
0
{
1407
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
1408
0
  g_return_if_fail(XB_IS_MACHINE(self));
1409
0
  g_return_if_fail(stack_size != 0);
1410
0
  priv->stack_size = stack_size;
1411
0
}
1412
1413
/**
1414
 * xb_machine_get_stack_size:
1415
 * @self: a #XbMachine
1416
 *
1417
 * Gets the maximum stack size used for the machine.
1418
 *
1419
 * Returns: integer
1420
 *
1421
 * Since: 0.1.3
1422
 **/
1423
guint
1424
xb_machine_get_stack_size(XbMachine *self)
1425
0
{
1426
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
1427
0
  g_return_val_if_fail(XB_IS_MACHINE(self), 0);
1428
0
  return priv->stack_size;
1429
0
}
1430
1431
static const gchar *
1432
xb_machine_intern_token(XbMachine *self, const gchar *str)
1433
0
{
1434
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
1435
0
  const gchar *tmp;
1436
0
  gchar *newstr;
1437
1438
  /* existing value */
1439
0
  tmp = g_hash_table_lookup(priv->opcode_tokens, str);
1440
0
  if (tmp != NULL)
1441
0
    return tmp;
1442
1443
  /* add as both key and value */
1444
0
  newstr = g_strdup(str);
1445
0
  g_hash_table_add(priv->opcode_tokens, newstr);
1446
0
  return newstr;
1447
0
}
1448
1449
/* private */
1450
void
1451
xb_machine_opcode_tokenize(XbMachine *self, XbOpcode *op)
1452
0
{
1453
0
  const gchar *str;
1454
0
  g_auto(GStrv) tokens = NULL;
1455
0
  g_auto(GStrv) ascii_tokens = NULL;
1456
1457
  /* use the fast token path even if there are no valid tokens */
1458
0
  xb_opcode_add_flag(op, XB_OPCODE_FLAG_TOKENIZED);
1459
1460
0
  str = _xb_opcode_get_str(op);
1461
0
  tokens = g_str_tokenize_and_fold(str, NULL, &ascii_tokens);
1462
0
  for (guint i = 0; tokens[i] != NULL; i++) {
1463
0
    if (!xb_string_token_valid(tokens[i]))
1464
0
      continue;
1465
0
    xb_opcode_append_token(op, xb_machine_intern_token(self, tokens[i]));
1466
0
  }
1467
0
  for (guint i = 0; ascii_tokens[i] != NULL; i++) {
1468
0
    if (!xb_string_token_valid(ascii_tokens[i]))
1469
0
      continue;
1470
0
    xb_opcode_append_token(op, xb_machine_intern_token(self, ascii_tokens[i]));
1471
0
  }
1472
0
}
1473
1474
typedef gboolean (*OpcodeCheckFunc)(XbOpcode *op);
1475
1476
static gboolean
1477
_xb_opcode_cmp_val_or_str(XbOpcode *op)
1478
0
{
1479
0
  return xb_opcode_cmp_str(op) || _xb_opcode_cmp_int(op) || _xb_opcode_cmp_itx(op);
1480
0
}
1481
1482
static gboolean
1483
xb_machine_check_one_arg(XbStack *stack, OpcodeCheckFunc f, GError **error)
1484
0
{
1485
0
  XbOpcode *head;
1486
1487
0
  head = xb_stack_peek_tail(stack);
1488
0
  if (head == NULL || !f(head)) {
1489
0
    if (error != NULL) {
1490
0
      g_set_error(error,
1491
0
            G_IO_ERROR,
1492
0
            G_IO_ERROR_NOT_SUPPORTED,
1493
0
            "%s type not supported",
1494
0
            (head != NULL)
1495
0
          ? xb_opcode_kind_to_string(_xb_opcode_get_kind(head))
1496
0
          : "(null)");
1497
0
    }
1498
0
    return FALSE;
1499
0
  }
1500
1501
0
  return TRUE;
1502
0
}
1503
1504
static gboolean
1505
xb_machine_check_two_args(XbStack *stack, OpcodeCheckFunc f1, OpcodeCheckFunc f2, GError **error)
1506
0
{
1507
0
  XbOpcode *head1 = NULL;
1508
0
  XbOpcode *head2 = NULL;
1509
0
  guint stack_size = xb_stack_get_size(stack);
1510
1511
0
  if (stack_size >= 2) {
1512
0
    head1 = xb_stack_peek(stack, stack_size - 1);
1513
0
    head2 = xb_stack_peek(stack, stack_size - 2);
1514
0
  }
1515
0
  if (head1 == NULL || head2 == NULL || !f1(head1) || !f2(head2)) {
1516
0
    if (error != NULL) {
1517
0
      g_set_error(
1518
0
          error,
1519
0
          G_IO_ERROR,
1520
0
          G_IO_ERROR_NOT_SUPPORTED,
1521
0
          "%s:%s types not supported",
1522
0
          (head1 != NULL) ? xb_opcode_kind_to_string(_xb_opcode_get_kind(head1))
1523
0
              : "(null)",
1524
0
          (head2 != NULL) ? xb_opcode_kind_to_string(_xb_opcode_get_kind(head2))
1525
0
              : "(null)");
1526
0
    }
1527
0
    return FALSE;
1528
0
  }
1529
1530
0
  return TRUE;
1531
0
}
1532
1533
static gboolean
1534
xb_machine_func_and_cb(XbMachine *self,
1535
           XbStack *stack,
1536
           gboolean *result,
1537
           gpointer user_data,
1538
           gpointer exec_data,
1539
           GError **error)
1540
0
{
1541
0
  g_auto(XbOpcode) op1 = XB_OPCODE_INIT();
1542
0
  g_auto(XbOpcode) op2 = XB_OPCODE_INIT();
1543
1544
0
  if (!xb_machine_check_two_args(stack, _xb_opcode_cmp_int, _xb_opcode_cmp_int, error))
1545
0
    return FALSE;
1546
0
  if (!xb_machine_stack_pop_two(self, stack, &op1, &op2, error))
1547
0
    return FALSE;
1548
1549
  /* INTE:INTE */
1550
0
  return xb_stack_push_bool(stack,
1551
0
          _xb_opcode_get_val(&op1) && _xb_opcode_get_val(&op2),
1552
0
          error);
1553
0
}
1554
1555
static gboolean
1556
xb_machine_func_or_cb(XbMachine *self,
1557
          XbStack *stack,
1558
          gboolean *result,
1559
          gpointer user_data,
1560
          gpointer exec_data,
1561
          GError **error)
1562
0
{
1563
0
  g_auto(XbOpcode) op1 = XB_OPCODE_INIT();
1564
0
  g_auto(XbOpcode) op2 = XB_OPCODE_INIT();
1565
1566
0
  if (!xb_machine_check_two_args(stack, _xb_opcode_cmp_int, _xb_opcode_cmp_int, error))
1567
0
    return FALSE;
1568
0
  if (!xb_machine_stack_pop_two(self, stack, &op1, &op2, error))
1569
0
    return FALSE;
1570
1571
  /* INTE:INTE */
1572
0
  return xb_stack_push_bool(stack,
1573
0
          _xb_opcode_get_val(&op1) || _xb_opcode_get_val(&op2),
1574
0
          error);
1575
0
}
1576
1577
static gboolean
1578
xb_machine_func_eq_cb(XbMachine *self,
1579
          XbStack *stack,
1580
          gboolean *result,
1581
          gpointer user_data,
1582
          gpointer exec_data,
1583
          GError **error)
1584
0
{
1585
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
1586
0
  g_auto(XbOpcode) op1 = XB_OPCODE_INIT();
1587
0
  g_auto(XbOpcode) op2 = XB_OPCODE_INIT();
1588
1589
0
  if (!xb_machine_check_two_args(stack,
1590
0
               _xb_opcode_cmp_val_or_str,
1591
0
               _xb_opcode_cmp_val_or_str,
1592
0
               error))
1593
0
    return FALSE;
1594
0
  if (!xb_machine_stack_pop_two(self, stack, &op1, &op2, error))
1595
0
    return FALSE;
1596
1597
  /* INTE:INTE */
1598
0
  if ((_xb_opcode_cmp_int(&op1) && _xb_opcode_cmp_int(&op2)) ||
1599
0
      (_xb_opcode_cmp_itx(&op1) && _xb_opcode_cmp_itx(&op2)))
1600
0
    return xb_stack_push_bool(stack,
1601
0
            _xb_opcode_get_val(&op1) == _xb_opcode_get_val(&op2),
1602
0
            error);
1603
1604
  /* TEXT:TEXT */
1605
0
  if (xb_opcode_cmp_str(&op1) && xb_opcode_cmp_str(&op2)) {
1606
0
    if (priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_SLOW_PATH) {
1607
0
      g_autofree gchar *str1 = xb_opcode_to_string(&op1);
1608
0
      g_autofree gchar *str2 = xb_opcode_to_string(&op2);
1609
0
      g_debug("slow strcmp fallback of %s:%s", str1, str2);
1610
0
    }
1611
0
    return xb_stack_push_bool(
1612
0
        stack,
1613
0
        g_strcmp0(_xb_opcode_get_str(&op1), _xb_opcode_get_str(&op2)) == 0,
1614
0
        error);
1615
0
  }
1616
1617
  /* INTE:TEXT */
1618
0
  if (_xb_opcode_cmp_int(&op1) && xb_opcode_cmp_str(&op2)) {
1619
0
    guint64 val = 0;
1620
0
    if (_xb_opcode_get_str(&op2) == NULL)
1621
0
      return xb_stack_push_bool(stack, FALSE, error);
1622
0
    if (priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_SLOW_PATH) {
1623
0
      g_autofree gchar *str1 = xb_opcode_to_string(&op1);
1624
0
      g_autofree gchar *str2 = xb_opcode_to_string(&op2);
1625
0
      g_debug("slow atoi fallback of %s:%s", str1, str2);
1626
0
    }
1627
0
    if (!g_ascii_string_to_unsigned(_xb_opcode_get_str(&op2),
1628
0
            10,
1629
0
            0,
1630
0
            G_MAXUINT32,
1631
0
            &val,
1632
0
            error)) {
1633
0
      return FALSE;
1634
0
    }
1635
0
    return xb_stack_push_bool(stack, val == _xb_opcode_get_val(&op1), error);
1636
0
  }
1637
1638
  /* TEXT:INTE */
1639
0
  if (xb_opcode_cmp_str(&op1) && _xb_opcode_cmp_int(&op2)) {
1640
0
    guint64 val = 0;
1641
0
    if (_xb_opcode_get_str(&op1) == NULL)
1642
0
      return xb_stack_push_bool(stack, FALSE, error);
1643
0
    if (priv->debug_flags & XB_MACHINE_DEBUG_FLAG_SHOW_SLOW_PATH) {
1644
0
      g_autofree gchar *str1 = xb_opcode_to_string(&op1);
1645
0
      g_autofree gchar *str2 = xb_opcode_to_string(&op2);
1646
0
      g_debug("slow atoi fallback of %s:%s", str1, str2);
1647
0
    }
1648
0
    if (!g_ascii_string_to_unsigned(_xb_opcode_get_str(&op1),
1649
0
            10,
1650
0
            0,
1651
0
            G_MAXUINT32,
1652
0
            &val,
1653
0
            error)) {
1654
0
      return FALSE;
1655
0
    }
1656
0
    return xb_stack_push_bool(stack, val == _xb_opcode_get_val(&op2), error);
1657
0
  }
1658
1659
  /* should have been checked above */
1660
0
  if (error != NULL) {
1661
0
    g_set_error(error,
1662
0
          G_IO_ERROR,
1663
0
          G_IO_ERROR_NOT_SUPPORTED,
1664
0
          "cannot compare %s and %s",
1665
0
          xb_opcode_kind_to_string(_xb_opcode_get_kind(&op1)),
1666
0
          xb_opcode_kind_to_string(_xb_opcode_get_kind(&op2)));
1667
0
  }
1668
0
  return FALSE;
1669
0
}
1670
1671
static gboolean
1672
xb_machine_func_ne_cb(XbMachine *self,
1673
          XbStack *stack,
1674
          gboolean *result,
1675
          gpointer user_data,
1676
          gpointer exec_data,
1677
          GError **error)
1678
0
{
1679
0
  g_auto(XbOpcode) op1 = XB_OPCODE_INIT();
1680
0
  g_auto(XbOpcode) op2 = XB_OPCODE_INIT();
1681
1682
0
  if (!xb_machine_check_two_args(stack,
1683
0
               _xb_opcode_cmp_val_or_str,
1684
0
               _xb_opcode_cmp_val_or_str,
1685
0
               error))
1686
0
    return FALSE;
1687
0
  if (!xb_machine_stack_pop_two(self, stack, &op1, &op2, error))
1688
0
    return FALSE;
1689
1690
  /* INTE:INTE */
1691
0
  if ((_xb_opcode_cmp_int(&op1) && _xb_opcode_cmp_int(&op2)) ||
1692
0
      (_xb_opcode_cmp_itx(&op1) && _xb_opcode_cmp_itx(&op2))) {
1693
0
    return xb_stack_push_bool(stack,
1694
0
            _xb_opcode_get_val(&op1) != _xb_opcode_get_val(&op2),
1695
0
            error);
1696
0
  }
1697
1698
  /* TEXT:TEXT */
1699
0
  if (xb_opcode_cmp_str(&op1) && xb_opcode_cmp_str(&op2)) {
1700
0
    return xb_stack_push_bool(
1701
0
        stack,
1702
0
        g_strcmp0(_xb_opcode_get_str(&op1), _xb_opcode_get_str(&op2)) != 0,
1703
0
        error);
1704
0
  }
1705
1706
  /* INTE:TEXT */
1707
0
  if (_xb_opcode_cmp_int(&op1) && xb_opcode_cmp_str(&op2)) {
1708
0
    guint64 val = 0;
1709
0
    if (_xb_opcode_get_str(&op2) == NULL)
1710
0
      return xb_stack_push_bool(stack, FALSE, error);
1711
0
    if (!g_ascii_string_to_unsigned(_xb_opcode_get_str(&op2),
1712
0
            10,
1713
0
            0,
1714
0
            G_MAXUINT32,
1715
0
            &val,
1716
0
            error)) {
1717
0
      return FALSE;
1718
0
    }
1719
0
    return xb_stack_push_bool(stack, val != _xb_opcode_get_val(&op1), error);
1720
0
  }
1721
1722
  /* TEXT:INTE */
1723
0
  if (xb_opcode_cmp_str(&op1) && _xb_opcode_cmp_int(&op2)) {
1724
0
    guint64 val = 0;
1725
0
    if (_xb_opcode_get_str(&op1) == NULL)
1726
0
      return xb_stack_push_bool(stack, FALSE, error);
1727
0
    if (!g_ascii_string_to_unsigned(_xb_opcode_get_str(&op1),
1728
0
            10,
1729
0
            0,
1730
0
            G_MAXUINT32,
1731
0
            &val,
1732
0
            error)) {
1733
0
      return FALSE;
1734
0
    }
1735
0
    return xb_stack_push_bool(stack, val != _xb_opcode_get_val(&op2), error);
1736
0
  }
1737
1738
  /* should have been checked above */
1739
0
  if (error != NULL) {
1740
0
    g_set_error(error,
1741
0
          G_IO_ERROR,
1742
0
          G_IO_ERROR_NOT_SUPPORTED,
1743
0
          "cannot compare %s and %s",
1744
0
          xb_opcode_kind_to_string(_xb_opcode_get_kind(&op1)),
1745
0
          xb_opcode_kind_to_string(_xb_opcode_get_kind(&op2)));
1746
0
  }
1747
0
  return FALSE;
1748
0
}
1749
1750
static gboolean
1751
xb_machine_func_lt_cb(XbMachine *self,
1752
          XbStack *stack,
1753
          gboolean *result,
1754
          gpointer user_data,
1755
          gpointer exec_data,
1756
          GError **error)
1757
0
{
1758
0
  g_auto(XbOpcode) op1 = XB_OPCODE_INIT();
1759
0
  g_auto(XbOpcode) op2 = XB_OPCODE_INIT();
1760
1761
0
  if (!xb_machine_check_two_args(stack,
1762
0
               _xb_opcode_cmp_val_or_str,
1763
0
               _xb_opcode_cmp_val_or_str,
1764
0
               error))
1765
0
    return FALSE;
1766
0
  if (!xb_machine_stack_pop_two(self, stack, &op1, &op2, error))
1767
0
    return FALSE;
1768
1769
  /* INTE:INTE */
1770
0
  if (_xb_opcode_cmp_int(&op1) && _xb_opcode_cmp_int(&op2)) {
1771
0
    return xb_stack_push_bool(stack,
1772
0
            _xb_opcode_get_val(&op2) < _xb_opcode_get_val(&op1),
1773
0
            error);
1774
0
  }
1775
1776
  /* TEXT:TEXT */
1777
0
  if (xb_opcode_cmp_str(&op1) && xb_opcode_cmp_str(&op2)) {
1778
0
    return xb_stack_push_bool(
1779
0
        stack,
1780
0
        g_strcmp0(_xb_opcode_get_str(&op2), _xb_opcode_get_str(&op1)) < 0,
1781
0
        error);
1782
0
  }
1783
1784
  /* INTE:TEXT */
1785
0
  if (_xb_opcode_cmp_int(&op1) && xb_opcode_cmp_str(&op2)) {
1786
0
    guint64 val = 0;
1787
0
    if (_xb_opcode_get_str(&op2) == NULL)
1788
0
      return xb_stack_push_bool(stack, FALSE, error);
1789
0
    if (!g_ascii_string_to_unsigned(_xb_opcode_get_str(&op2),
1790
0
            10,
1791
0
            0,
1792
0
            G_MAXUINT32,
1793
0
            &val,
1794
0
            error)) {
1795
0
      return FALSE;
1796
0
    }
1797
0
    return xb_stack_push_bool(stack, val < _xb_opcode_get_val(&op1), error);
1798
0
  }
1799
1800
  /* TEXT:INTE */
1801
0
  if (xb_opcode_cmp_str(&op1) && _xb_opcode_cmp_int(&op2)) {
1802
0
    guint64 val = 0;
1803
0
    if (_xb_opcode_get_str(&op1) == NULL)
1804
0
      return xb_stack_push_bool(stack, FALSE, error);
1805
0
    if (!g_ascii_string_to_unsigned(_xb_opcode_get_str(&op1),
1806
0
            10,
1807
0
            0,
1808
0
            G_MAXUINT32,
1809
0
            &val,
1810
0
            error)) {
1811
0
      return FALSE;
1812
0
    }
1813
0
    return xb_stack_push_bool(stack, val < _xb_opcode_get_val(&op2), error);
1814
0
  }
1815
1816
  /* should have been checked above */
1817
0
  if (error != NULL) {
1818
0
    g_set_error(error,
1819
0
          G_IO_ERROR,
1820
0
          G_IO_ERROR_NOT_SUPPORTED,
1821
0
          "cannot compare %s and %s",
1822
0
          xb_opcode_kind_to_string(_xb_opcode_get_kind(&op1)),
1823
0
          xb_opcode_kind_to_string(_xb_opcode_get_kind(&op2)));
1824
0
  }
1825
0
  return FALSE;
1826
0
}
1827
1828
static gboolean
1829
xb_machine_func_gt_cb(XbMachine *self,
1830
          XbStack *stack,
1831
          gboolean *result,
1832
          gpointer user_data,
1833
          gpointer exec_data,
1834
          GError **error)
1835
0
{
1836
0
  g_auto(XbOpcode) op1 = XB_OPCODE_INIT();
1837
0
  g_auto(XbOpcode) op2 = XB_OPCODE_INIT();
1838
1839
0
  if (!xb_machine_check_two_args(stack,
1840
0
               _xb_opcode_cmp_val_or_str,
1841
0
               _xb_opcode_cmp_val_or_str,
1842
0
               error))
1843
0
    return FALSE;
1844
0
  if (!xb_machine_stack_pop_two(self, stack, &op1, &op2, error))
1845
0
    return FALSE;
1846
1847
  /* INTE:INTE */
1848
0
  if (_xb_opcode_cmp_int(&op1) && _xb_opcode_cmp_int(&op2)) {
1849
0
    return xb_stack_push_bool(stack,
1850
0
            _xb_opcode_get_val(&op2) > _xb_opcode_get_val(&op1),
1851
0
            error);
1852
0
  }
1853
1854
  /* TEXT:TEXT */
1855
0
  if (xb_opcode_cmp_str(&op1) && xb_opcode_cmp_str(&op2)) {
1856
0
    return xb_stack_push_bool(
1857
0
        stack,
1858
0
        g_strcmp0(_xb_opcode_get_str(&op2), _xb_opcode_get_str(&op1)) > 0,
1859
0
        error);
1860
0
  }
1861
1862
  /* INTE:TEXT */
1863
0
  if (_xb_opcode_cmp_int(&op1) && xb_opcode_cmp_str(&op2)) {
1864
0
    guint64 val = 0;
1865
0
    if (_xb_opcode_get_str(&op2) == NULL)
1866
0
      return xb_stack_push_bool(stack, FALSE, error);
1867
0
    if (!g_ascii_string_to_unsigned(_xb_opcode_get_str(&op2),
1868
0
            10,
1869
0
            0,
1870
0
            G_MAXUINT32,
1871
0
            &val,
1872
0
            error)) {
1873
0
      return FALSE;
1874
0
    }
1875
0
    return xb_stack_push_bool(stack, val > _xb_opcode_get_val(&op1), error);
1876
0
  }
1877
1878
  /* TEXT:INTE */
1879
0
  if (xb_opcode_cmp_str(&op1) && _xb_opcode_cmp_int(&op2)) {
1880
0
    guint64 val = 0;
1881
0
    if (_xb_opcode_get_str(&op1) == NULL)
1882
0
      return xb_stack_push_bool(stack, FALSE, error);
1883
0
    if (!g_ascii_string_to_unsigned(_xb_opcode_get_str(&op1),
1884
0
            10,
1885
0
            0,
1886
0
            G_MAXUINT32,
1887
0
            &val,
1888
0
            error)) {
1889
0
      return FALSE;
1890
0
    }
1891
0
    return xb_stack_push_bool(stack, val > _xb_opcode_get_val(&op2), error);
1892
0
  }
1893
1894
  /* should have been checked above */
1895
0
  if (error != NULL) {
1896
0
    g_set_error(error,
1897
0
          G_IO_ERROR,
1898
0
          G_IO_ERROR_NOT_SUPPORTED,
1899
0
          "cannot compare %s and %s",
1900
0
          xb_opcode_kind_to_string(_xb_opcode_get_kind(&op1)),
1901
0
          xb_opcode_kind_to_string(_xb_opcode_get_kind(&op2)));
1902
0
  }
1903
0
  return FALSE;
1904
0
}
1905
1906
static gboolean
1907
xb_machine_func_le_cb(XbMachine *self,
1908
          XbStack *stack,
1909
          gboolean *result,
1910
          gpointer user_data,
1911
          gpointer exec_data,
1912
          GError **error)
1913
0
{
1914
0
  g_auto(XbOpcode) op1 = XB_OPCODE_INIT();
1915
0
  g_auto(XbOpcode) op2 = XB_OPCODE_INIT();
1916
1917
0
  if (!xb_machine_check_two_args(stack,
1918
0
               _xb_opcode_cmp_val_or_str,
1919
0
               _xb_opcode_cmp_val_or_str,
1920
0
               error))
1921
0
    return FALSE;
1922
0
  if (!xb_machine_stack_pop_two(self, stack, &op1, &op2, error))
1923
0
    return FALSE;
1924
1925
  /* INTE:INTE */
1926
0
  if (_xb_opcode_cmp_int(&op1) && _xb_opcode_cmp_int(&op2)) {
1927
0
    return xb_stack_push_bool(stack,
1928
0
            _xb_opcode_get_val(&op2) <= _xb_opcode_get_val(&op1),
1929
0
            error);
1930
0
  }
1931
1932
  /* TEXT:TEXT */
1933
0
  if (xb_opcode_cmp_str(&op1) && xb_opcode_cmp_str(&op2)) {
1934
0
    return xb_stack_push_bool(
1935
0
        stack,
1936
0
        g_strcmp0(_xb_opcode_get_str(&op2), _xb_opcode_get_str(&op1)) <= 0,
1937
0
        error);
1938
0
    return TRUE;
1939
0
  }
1940
1941
  /* INTE:TEXT */
1942
0
  if (_xb_opcode_cmp_int(&op1) && xb_opcode_cmp_str(&op2)) {
1943
0
    guint64 val = 0;
1944
0
    if (_xb_opcode_get_str(&op2) == NULL)
1945
0
      return xb_stack_push_bool(stack, FALSE, error);
1946
0
    if (!g_ascii_string_to_unsigned(_xb_opcode_get_str(&op2),
1947
0
            10,
1948
0
            0,
1949
0
            G_MAXUINT32,
1950
0
            &val,
1951
0
            error)) {
1952
0
      return FALSE;
1953
0
    }
1954
0
    return xb_stack_push_bool(stack, val <= _xb_opcode_get_val(&op1), error);
1955
0
  }
1956
1957
  /* TEXT:INTE */
1958
0
  if (xb_opcode_cmp_str(&op1) && _xb_opcode_cmp_int(&op2)) {
1959
0
    guint64 val = 0;
1960
0
    if (_xb_opcode_get_str(&op1) == NULL)
1961
0
      return xb_stack_push_bool(stack, FALSE, error);
1962
0
    if (!g_ascii_string_to_unsigned(_xb_opcode_get_str(&op1),
1963
0
            10,
1964
0
            0,
1965
0
            G_MAXUINT32,
1966
0
            &val,
1967
0
            error)) {
1968
0
      return FALSE;
1969
0
    }
1970
0
    return xb_stack_push_bool(stack, val <= _xb_opcode_get_val(&op2), error);
1971
0
  }
1972
1973
  /* should have been checked above */
1974
0
  if (error != NULL) {
1975
0
    g_set_error(error,
1976
0
          G_IO_ERROR,
1977
0
          G_IO_ERROR_NOT_SUPPORTED,
1978
0
          "cannot compare %s and %s",
1979
0
          xb_opcode_kind_to_string(_xb_opcode_get_kind(&op1)),
1980
0
          xb_opcode_kind_to_string(_xb_opcode_get_kind(&op2)));
1981
0
  }
1982
0
  return FALSE;
1983
0
}
1984
1985
static gboolean
1986
xb_machine_func_lower_cb(XbMachine *self,
1987
       XbStack *stack,
1988
       gboolean *result,
1989
       gpointer user_data,
1990
       gpointer exec_data,
1991
       GError **error)
1992
0
{
1993
0
  g_auto(XbOpcode) op = XB_OPCODE_INIT();
1994
1995
0
  if (!xb_machine_check_one_arg(stack, xb_opcode_cmp_str, error))
1996
0
    return FALSE;
1997
0
  if (!xb_machine_stack_pop(self, stack, &op, error))
1998
0
    return FALSE;
1999
2000
  /* TEXT */
2001
0
  return xb_machine_stack_push_text_steal(self,
2002
0
            stack,
2003
0
            g_utf8_strdown(_xb_opcode_get_str(&op), -1),
2004
0
            error);
2005
0
}
2006
2007
static gboolean
2008
xb_machine_func_upper_cb(XbMachine *self,
2009
       XbStack *stack,
2010
       gboolean *result,
2011
       gpointer user_data,
2012
       gpointer exec_data,
2013
       GError **error)
2014
0
{
2015
0
  g_auto(XbOpcode) op = XB_OPCODE_INIT();
2016
2017
0
  if (!xb_machine_check_one_arg(stack, xb_opcode_cmp_str, error))
2018
0
    return FALSE;
2019
0
  if (!xb_machine_stack_pop(self, stack, &op, error))
2020
0
    return FALSE;
2021
2022
  /* TEXT */
2023
0
  return xb_machine_stack_push_text_steal(self,
2024
0
            stack,
2025
0
            g_utf8_strup(_xb_opcode_get_str(&op), -1),
2026
0
            error);
2027
0
}
2028
2029
static gboolean
2030
xb_machine_func_not_cb(XbMachine *self,
2031
           XbStack *stack,
2032
           gboolean *result,
2033
           gpointer user_data,
2034
           gpointer exec_data,
2035
           GError **error)
2036
0
{
2037
0
  g_auto(XbOpcode) op = XB_OPCODE_INIT();
2038
2039
0
  if (!xb_machine_check_one_arg(stack, _xb_opcode_cmp_val_or_str, error))
2040
0
    return FALSE;
2041
0
  if (!xb_machine_stack_pop(self, stack, &op, error))
2042
0
    return FALSE;
2043
2044
  /* TEXT */
2045
0
  if (xb_opcode_cmp_str(&op))
2046
0
    return xb_stack_push_bool(stack, _xb_opcode_get_str(&op) == NULL, error);
2047
2048
  /* INTE */
2049
0
  if (_xb_opcode_cmp_int(&op))
2050
0
    return xb_stack_push_bool(stack, _xb_opcode_get_val(&op) == 0, error);
2051
2052
  /* should have been checked above */
2053
0
  if (error != NULL) {
2054
0
    g_set_error(error,
2055
0
          G_IO_ERROR,
2056
0
          G_IO_ERROR_NOT_SUPPORTED,
2057
0
          "cannot invert %s",
2058
0
          xb_opcode_kind_to_string(_xb_opcode_get_kind(&op)));
2059
0
  }
2060
0
  return FALSE;
2061
0
}
2062
2063
static gboolean
2064
xb_machine_func_ge_cb(XbMachine *self,
2065
          XbStack *stack,
2066
          gboolean *result,
2067
          gpointer user_data,
2068
          gpointer exec_data,
2069
          GError **error)
2070
0
{
2071
0
  g_auto(XbOpcode) op1 = XB_OPCODE_INIT();
2072
0
  g_auto(XbOpcode) op2 = XB_OPCODE_INIT();
2073
2074
0
  if (!xb_machine_check_two_args(stack,
2075
0
               _xb_opcode_cmp_val_or_str,
2076
0
               _xb_opcode_cmp_val_or_str,
2077
0
               error))
2078
0
    return FALSE;
2079
0
  if (!xb_machine_stack_pop_two(self, stack, &op1, &op2, error))
2080
0
    return FALSE;
2081
2082
  /* TEXT:TEXT */
2083
0
  if (xb_opcode_cmp_str(&op1) && xb_opcode_cmp_str(&op2)) {
2084
0
    return xb_stack_push_bool(
2085
0
        stack,
2086
0
        g_strcmp0(_xb_opcode_get_str(&op2), _xb_opcode_get_str(&op1)) >= 0,
2087
0
        error);
2088
0
  }
2089
2090
  /* INTE:INTE */
2091
0
  if (_xb_opcode_cmp_int(&op1) && _xb_opcode_cmp_int(&op2)) {
2092
0
    return xb_stack_push_bool(stack,
2093
0
            _xb_opcode_get_val(&op2) >= _xb_opcode_get_val(&op1),
2094
0
            error);
2095
0
  }
2096
2097
  /* INTE:TEXT */
2098
0
  if (_xb_opcode_cmp_int(&op1) && xb_opcode_cmp_str(&op2)) {
2099
0
    guint64 val = 0;
2100
0
    if (_xb_opcode_get_str(&op2) == NULL)
2101
0
      return xb_stack_push_bool(stack, FALSE, error);
2102
0
    if (!g_ascii_string_to_unsigned(_xb_opcode_get_str(&op2),
2103
0
            10,
2104
0
            0,
2105
0
            G_MAXUINT32,
2106
0
            &val,
2107
0
            error)) {
2108
0
      return FALSE;
2109
0
    }
2110
0
    return xb_stack_push_bool(stack, val >= _xb_opcode_get_val(&op1), error);
2111
0
  }
2112
2113
  /* TEXT:INTE */
2114
0
  if (xb_opcode_cmp_str(&op1) && _xb_opcode_cmp_int(&op2)) {
2115
0
    guint64 val = 0;
2116
0
    if (_xb_opcode_get_str(&op1) == NULL)
2117
0
      return xb_stack_push_bool(stack, FALSE, error);
2118
0
    if (!g_ascii_string_to_unsigned(_xb_opcode_get_str(&op1),
2119
0
            10,
2120
0
            0,
2121
0
            G_MAXUINT32,
2122
0
            &val,
2123
0
            error)) {
2124
0
      return FALSE;
2125
0
    }
2126
0
    return xb_stack_push_bool(stack, val >= _xb_opcode_get_val(&op2), error);
2127
0
  }
2128
2129
  /* should have been checked above */
2130
0
  if (error != NULL) {
2131
0
    g_set_error(error,
2132
0
          G_IO_ERROR,
2133
0
          G_IO_ERROR_NOT_SUPPORTED,
2134
0
          "cannot compare %s and %s",
2135
0
          xb_opcode_kind_to_string(_xb_opcode_get_kind(&op1)),
2136
0
          xb_opcode_kind_to_string(_xb_opcode_get_kind(&op2)));
2137
0
  }
2138
0
  return FALSE;
2139
0
}
2140
2141
static gboolean
2142
xb_machine_func_contains_cb(XbMachine *self,
2143
          XbStack *stack,
2144
          gboolean *result,
2145
          gpointer user_data,
2146
          gpointer exec_data,
2147
          GError **error)
2148
0
{
2149
0
  g_auto(XbOpcode) op1 = XB_OPCODE_INIT();
2150
0
  g_auto(XbOpcode) op2 = XB_OPCODE_INIT();
2151
2152
0
  if (!xb_machine_check_two_args(stack, xb_opcode_cmp_str, xb_opcode_cmp_str, error))
2153
0
    return FALSE;
2154
0
  if (!xb_machine_stack_pop_two(self, stack, &op1, &op2, error))
2155
0
    return FALSE;
2156
2157
  /* TEXT:TEXT */
2158
0
  return xb_stack_push_bool(
2159
0
      stack,
2160
0
      xb_string_contains(_xb_opcode_get_str(&op2), _xb_opcode_get_str(&op1)),
2161
0
      error);
2162
0
}
2163
2164
static gboolean
2165
xb_machine_func_starts_with_cb(XbMachine *self,
2166
             XbStack *stack,
2167
             gboolean *result,
2168
             gpointer user_data,
2169
             gpointer exec_data,
2170
             GError **error)
2171
0
{
2172
0
  g_auto(XbOpcode) op1 = XB_OPCODE_INIT();
2173
0
  g_auto(XbOpcode) op2 = XB_OPCODE_INIT();
2174
2175
0
  if (!xb_machine_check_two_args(stack, xb_opcode_cmp_str, xb_opcode_cmp_str, error))
2176
0
    return FALSE;
2177
0
  if (!xb_machine_stack_pop_two(self, stack, &op1, &op2, error))
2178
0
    return FALSE;
2179
2180
  /* TEXT:TEXT */
2181
0
  return xb_stack_push_bool(
2182
0
      stack,
2183
0
      g_str_has_prefix(_xb_opcode_get_str(&op2), _xb_opcode_get_str(&op1)),
2184
0
      error);
2185
0
}
2186
2187
static gboolean
2188
xb_machine_func_ends_with_cb(XbMachine *self,
2189
           XbStack *stack,
2190
           gboolean *result,
2191
           gpointer user_data,
2192
           gpointer exec_data,
2193
           GError **error)
2194
0
{
2195
0
  g_auto(XbOpcode) op1 = XB_OPCODE_INIT();
2196
0
  g_auto(XbOpcode) op2 = XB_OPCODE_INIT();
2197
2198
0
  if (!xb_machine_check_two_args(stack, xb_opcode_cmp_str, xb_opcode_cmp_str, error))
2199
0
    return FALSE;
2200
0
  if (!xb_machine_stack_pop_two(self, stack, &op1, &op2, error))
2201
0
    return FALSE;
2202
2203
  /* TEXT:TEXT */
2204
0
  return xb_stack_push_bool(
2205
0
      stack,
2206
0
      g_str_has_suffix(_xb_opcode_get_str(&op2), _xb_opcode_get_str(&op1)),
2207
0
      error);
2208
0
}
2209
2210
static gboolean
2211
xb_machine_func_number_cb(XbMachine *self,
2212
        XbStack *stack,
2213
        gboolean *result,
2214
        gpointer user_data,
2215
        gpointer exec_data,
2216
        GError **error)
2217
0
{
2218
0
  guint64 val = 0;
2219
0
  g_auto(XbOpcode) op = XB_OPCODE_INIT();
2220
2221
0
  if (!xb_machine_check_one_arg(stack, xb_opcode_cmp_str, error))
2222
0
    return FALSE;
2223
0
  if (!xb_machine_stack_pop(self, stack, &op, error))
2224
0
    return FALSE;
2225
2226
  /* TEXT */
2227
0
  if (_xb_opcode_get_str(&op) == NULL)
2228
0
    return xb_stack_push_bool(stack, FALSE, error);
2229
0
  if (!g_ascii_string_to_unsigned(_xb_opcode_get_str(&op), 10, 0, G_MAXUINT32, &val, error)) {
2230
0
    return FALSE;
2231
0
  }
2232
0
  return xb_machine_stack_push_integer(self, stack, val, error);
2233
0
}
2234
2235
static gboolean
2236
xb_machine_func_strlen_cb(XbMachine *self,
2237
        XbStack *stack,
2238
        gboolean *result,
2239
        gpointer user_data,
2240
        gpointer exec_data,
2241
        GError **error)
2242
0
{
2243
0
  g_auto(XbOpcode) op = XB_OPCODE_INIT();
2244
2245
0
  if (!xb_machine_check_one_arg(stack, xb_opcode_cmp_str, error))
2246
0
    return FALSE;
2247
0
  if (!xb_machine_stack_pop(self, stack, &op, error))
2248
0
    return FALSE;
2249
2250
  /* TEXT */
2251
0
  if (_xb_opcode_get_str(&op) == NULL)
2252
0
    return xb_stack_push_bool(stack, FALSE, error);
2253
0
  return xb_machine_stack_push_integer(self, stack, strlen(_xb_opcode_get_str(&op)), error);
2254
0
}
2255
2256
static gboolean
2257
xb_machine_func_string_cb(XbMachine *self,
2258
        XbStack *stack,
2259
        gboolean *result,
2260
        gpointer user_data,
2261
        gpointer exec_data,
2262
        GError **error)
2263
0
{
2264
0
  gchar *tmp;
2265
0
  g_auto(XbOpcode) op = XB_OPCODE_INIT();
2266
2267
0
  if (!xb_machine_check_one_arg(stack, _xb_opcode_cmp_int, error))
2268
0
    return FALSE;
2269
0
  if (!xb_machine_stack_pop(self, stack, &op, error))
2270
0
    return FALSE;
2271
2272
  /* INTE */
2273
0
  tmp = g_strdup_printf("%" G_GUINT32_FORMAT, _xb_opcode_get_val(&op));
2274
0
  return xb_machine_stack_push_text_steal(self, stack, tmp, error);
2275
0
}
2276
2277
static gboolean
2278
xb_machine_func_in_cb(XbMachine *self,
2279
          XbStack *stack,
2280
          gboolean *result,
2281
          gpointer user_data,
2282
          gpointer exec_data,
2283
          GError **error)
2284
0
{
2285
0
  XbOpcode *op_needle;
2286
0
  const gchar *haystack[XB_MACHINE_STACK_LEVELS_MAX + 1] = {NULL};
2287
0
  g_auto(XbOpcode) op = XB_OPCODE_INIT();
2288
0
  guint8 level = G_MAXUINT8;
2289
0
  guint nr_args = 0;
2290
2291
  /* get the size of the haystack, ensuring we only have strings */
2292
0
  for (guint i = xb_stack_get_size(stack) - 1; i > 0; i--) {
2293
0
    XbOpcode *op_tmp = xb_stack_peek(stack, i);
2294
2295
    /* this is a hack as we do not get the current @level */
2296
0
    if (level != G_MAXUINT8) {
2297
0
      if (_xb_opcode_get_level(op_tmp) != level)
2298
0
        break;
2299
0
    } else {
2300
0
      level = _xb_opcode_get_level(op_tmp);
2301
0
    }
2302
0
    if (!xb_opcode_cmp_str(op_tmp)) {
2303
0
      if (error != NULL) {
2304
0
        g_set_error(error,
2305
0
              G_IO_ERROR,
2306
0
              G_IO_ERROR_NOT_SUPPORTED,
2307
0
              "%s type not supported",
2308
0
              xb_opcode_kind_to_string(_xb_opcode_get_kind(op_tmp)));
2309
0
      }
2310
0
      return FALSE;
2311
0
    }
2312
0
    nr_args++;
2313
0
  }
2314
2315
  /* ensure the needle is also a string */
2316
0
  op_needle = xb_stack_peek(stack, xb_stack_get_size(stack) - (nr_args + 1));
2317
0
  if (!xb_opcode_cmp_str(op_needle)) {
2318
0
    if (error != NULL) {
2319
0
      g_set_error(error,
2320
0
            G_IO_ERROR,
2321
0
            G_IO_ERROR_NOT_SUPPORTED,
2322
0
            "%s type not supported",
2323
0
            xb_opcode_kind_to_string(_xb_opcode_get_kind(op_needle)));
2324
0
    }
2325
0
    return FALSE;
2326
0
  }
2327
2328
  /* build the haystack */
2329
0
  for (guint i = 0; i < nr_args; i++) {
2330
0
    g_auto(XbOpcode) op_tmp = XB_OPCODE_INIT();
2331
0
    if (!xb_machine_stack_pop(self, stack, &op_tmp, error))
2332
0
      return FALSE;
2333
0
    haystack[i] = _xb_opcode_get_str(&op_tmp);
2334
0
  }
2335
2336
  /* get the needle */
2337
0
  if (!xb_machine_stack_pop(self, stack, &op, error))
2338
0
    return FALSE;
2339
2340
  /* found */
2341
0
  return xb_stack_push_bool(stack, g_strv_contains(haystack, _xb_opcode_get_str(&op)), error);
2342
0
}
2343
2344
static void
2345
xb_machine_opcode_fixup_free(XbMachineOpcodeFixupItem *item)
2346
0
{
2347
0
  if (item->user_data_free != NULL)
2348
0
    item->user_data_free(item->user_data);
2349
0
  g_slice_free(XbMachineOpcodeFixupItem, item);
2350
0
}
2351
2352
static void
2353
xb_machine_func_free(XbMachineMethodItem *item)
2354
0
{
2355
0
  if (item->user_data_free != NULL)
2356
0
    item->user_data_free(item->user_data);
2357
0
  g_free(item->name);
2358
0
  g_slice_free(XbMachineMethodItem, item);
2359
0
}
2360
2361
static void
2362
xb_machine_text_handler_free(XbMachineTextHandlerItem *item)
2363
0
{
2364
0
  if (item->user_data_free != NULL)
2365
0
    item->user_data_free(item->user_data);
2366
0
  g_slice_free(XbMachineTextHandlerItem, item);
2367
0
}
2368
2369
static void
2370
xb_machine_operator_free(XbMachineOperator *op)
2371
0
{
2372
0
  g_free(op->str);
2373
0
  g_free(op->name);
2374
0
  g_slice_free(XbMachineOperator, op);
2375
0
}
2376
2377
static void
2378
xb_machine_init(XbMachine *self)
2379
0
{
2380
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
2381
0
  priv->stack_size = 10;
2382
0
  priv->methods = g_ptr_array_new_with_free_func((GDestroyNotify)xb_machine_func_free);
2383
0
  priv->operators = g_ptr_array_new_with_free_func((GDestroyNotify)xb_machine_operator_free);
2384
0
  priv->text_handlers =
2385
0
      g_ptr_array_new_with_free_func((GDestroyNotify)xb_machine_text_handler_free);
2386
0
  priv->opcode_fixup = g_hash_table_new_full(g_str_hash,
2387
0
               g_str_equal,
2388
0
               g_free,
2389
0
               (GDestroyNotify)xb_machine_opcode_fixup_free);
2390
0
  priv->opcode_tokens = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
2391
2392
  /* built-in functions */
2393
0
  xb_machine_add_method(self, "and", 2, xb_machine_func_and_cb, NULL, NULL);
2394
0
  xb_machine_add_method(self, "or", 2, xb_machine_func_or_cb, NULL, NULL);
2395
0
  xb_machine_add_method(self, "eq", 2, xb_machine_func_eq_cb, NULL, NULL);
2396
0
  xb_machine_add_method(self, "ne", 2, xb_machine_func_ne_cb, NULL, NULL);
2397
0
  xb_machine_add_method(self, "lt", 2, xb_machine_func_lt_cb, NULL, NULL);
2398
0
  xb_machine_add_method(self, "gt", 2, xb_machine_func_gt_cb, NULL, NULL);
2399
0
  xb_machine_add_method(self, "le", 2, xb_machine_func_le_cb, NULL, NULL);
2400
0
  xb_machine_add_method(self, "ge", 2, xb_machine_func_ge_cb, NULL, NULL);
2401
0
  xb_machine_add_method(self, "not", 1, xb_machine_func_not_cb, NULL, NULL);
2402
0
  xb_machine_add_method(self, "lower-case", 1, xb_machine_func_lower_cb, NULL, NULL);
2403
0
  xb_machine_add_method(self, "upper-case", 1, xb_machine_func_upper_cb, NULL, NULL);
2404
0
  xb_machine_add_method(self, "contains", 2, xb_machine_func_contains_cb, NULL, NULL);
2405
0
  xb_machine_add_method(self, "starts-with", 2, xb_machine_func_starts_with_cb, NULL, NULL);
2406
0
  xb_machine_add_method(self, "ends-with", 2, xb_machine_func_ends_with_cb, NULL, NULL);
2407
0
  xb_machine_add_method(self, "string", 1, xb_machine_func_string_cb, NULL, NULL);
2408
0
  xb_machine_add_method(self, "number", 1, xb_machine_func_number_cb, NULL, NULL);
2409
0
  xb_machine_add_method(self, "string-length", 1, xb_machine_func_strlen_cb, NULL, NULL);
2410
0
  xb_machine_add_method(self, "in", 0, xb_machine_func_in_cb, NULL, NULL);
2411
2412
  /* built-in operators */
2413
0
  xb_machine_add_operator(self, " and ", "and");
2414
0
  xb_machine_add_operator(self, " or ", "or");
2415
0
  xb_machine_add_operator(self, "&&", "and");
2416
0
  xb_machine_add_operator(self, "||", "or");
2417
0
  xb_machine_add_operator(self, "!=", "ne");
2418
0
  xb_machine_add_operator(self, "<=", "le");
2419
0
  xb_machine_add_operator(self, ">=", "ge");
2420
0
  xb_machine_add_operator(self, "==", "eq");
2421
0
  xb_machine_add_operator(self, "=", "eq");
2422
0
  xb_machine_add_operator(self, ">", "gt");
2423
0
  xb_machine_add_operator(self, "<", "lt");
2424
0
}
2425
2426
static void
2427
xb_machine_finalize(GObject *obj)
2428
0
{
2429
0
  XbMachine *self = XB_MACHINE(obj);
2430
0
  XbMachinePrivate *priv = GET_PRIVATE(self);
2431
0
  g_ptr_array_unref(priv->methods);
2432
0
  g_ptr_array_unref(priv->operators);
2433
0
  g_ptr_array_unref(priv->text_handlers);
2434
0
  g_hash_table_unref(priv->opcode_fixup);
2435
0
  g_hash_table_unref(priv->opcode_tokens);
2436
0
  G_OBJECT_CLASS(xb_machine_parent_class)->finalize(obj);
2437
0
}
2438
2439
static void
2440
xb_machine_class_init(XbMachineClass *klass)
2441
0
{
2442
0
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
2443
0
  object_class->finalize = xb_machine_finalize;
2444
0
}
2445
2446
/**
2447
 * xb_machine_new:
2448
 *
2449
 * Creates a new virtual machine.
2450
 *
2451
 * Returns: a new #XbMachine
2452
 *
2453
 * Since: 0.1.1
2454
 **/
2455
XbMachine *
2456
xb_machine_new(void)
2457
0
{
2458
0
  return g_object_new(XB_TYPE_MACHINE, NULL);
2459
0
}