Coverage Report

Created: 2026-06-15 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/sieve-generator.c
Line
Count
Source
1
/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2
 */
3
4
#include "lib.h"
5
#include "mempool.h"
6
7
#include "sieve-common.h"
8
#include "sieve-script.h"
9
#include "sieve-extensions.h"
10
#include "sieve-commands.h"
11
#include "sieve-code.h"
12
#include "sieve-binary.h"
13
14
#include "sieve-generator.h"
15
16
/*
17
 * Jump list
18
 */
19
20
struct sieve_jumplist *
21
sieve_jumplist_create(pool_t pool, struct sieve_binary_block *sblock)
22
0
{
23
0
  struct sieve_jumplist *jlist;
24
25
0
  jlist = p_new(pool, struct sieve_jumplist, 1);
26
0
  jlist->block = sblock;
27
0
  p_array_init(&jlist->jumps, pool, 4);
28
29
0
  return jlist;
30
0
}
31
32
void sieve_jumplist_init_temp(struct sieve_jumplist *jlist,
33
            struct sieve_binary_block *sblock)
34
0
{
35
0
  jlist->block = sblock;
36
0
  t_array_init(&jlist->jumps, 4);
37
0
}
38
39
void sieve_jumplist_reset(struct sieve_jumplist *jlist)
40
0
{
41
0
  array_clear(&jlist->jumps);
42
0
}
43
44
void sieve_jumplist_add(struct sieve_jumplist *jlist, sieve_size_t jump)
45
0
{
46
0
  array_append(&jlist->jumps, &jump, 1);
47
0
}
48
49
void sieve_jumplist_resolve(struct sieve_jumplist *jlist)
50
0
{
51
0
  unsigned int i;
52
53
0
  for (i = 0; i < array_count(&jlist->jumps); i++) {
54
0
    const sieve_size_t *jump = array_idx(&jlist->jumps, i);
55
56
0
    sieve_binary_resolve_offset(jlist->block, *jump);
57
0
  }
58
0
}
59
60
/*
61
 * Code Generator
62
 */
63
64
struct sieve_generator {
65
  pool_t pool;
66
67
  struct sieve_instance *instance;
68
69
  struct sieve_error_handler *ehandler;
70
71
  struct sieve_codegen_env genenv;
72
  struct sieve_binary_debug_writer *dwriter;
73
74
  ARRAY(void *) ext_contexts;
75
};
76
77
struct sieve_generator *
78
sieve_generator_create(struct sieve_ast *ast,
79
           struct sieve_error_handler *ehandler,
80
           enum sieve_compile_flags flags)
81
0
{
82
0
  pool_t pool;
83
0
  struct sieve_generator *gentr;
84
0
  struct sieve_script *script;
85
0
  struct sieve_instance *svinst;
86
87
0
  pool = pool_alloconly_create("sieve_generator", 4096);
88
0
  gentr = p_new(pool, struct sieve_generator, 1);
89
0
  gentr->pool = pool;
90
91
0
  gentr->ehandler = ehandler;
92
0
  sieve_error_handler_ref(ehandler);
93
94
0
  gentr->genenv.gentr = gentr;
95
0
  gentr->genenv.flags = flags;
96
0
  gentr->genenv.ast = ast;
97
0
  sieve_ast_ref(ast);
98
99
0
  script = sieve_ast_script(ast);
100
0
  svinst = sieve_script_svinst(script);
101
102
0
  gentr->genenv.script = script;
103
0
  gentr->genenv.svinst = svinst;
104
105
  /* Setup storage for extension contexts */
106
0
  p_array_init(&gentr->ext_contexts, pool,
107
0
         sieve_extensions_get_count(svinst));
108
109
0
  return gentr;
110
0
}
111
112
void sieve_generator_free(struct sieve_generator **gentr)
113
0
{
114
0
  sieve_ast_unref(&(*gentr)->genenv.ast);
115
116
0
  sieve_error_handler_unref(&(*gentr)->ehandler);
117
0
  sieve_binary_debug_writer_deinit(&(*gentr)->dwriter);
118
119
0
  sieve_binary_unref(&(*gentr)->genenv.sbin);
120
121
0
  pool_unref(&((*gentr)->pool));
122
123
0
  *gentr = NULL;
124
0
}
125
126
/*
127
 * Accessors
128
 */
129
130
struct sieve_error_handler *
131
sieve_generator_error_handler(struct sieve_generator *gentr)
132
0
{
133
0
  return gentr->ehandler;
134
0
}
135
136
pool_t sieve_generator_pool(struct sieve_generator *gentr)
137
0
{
138
0
  return gentr->pool;
139
0
}
140
141
struct sieve_script *sieve_generator_script(struct sieve_generator *gentr)
142
0
{
143
0
  return gentr->genenv.script;
144
0
}
145
146
struct sieve_binary *sieve_generator_get_binary(struct sieve_generator *gentr)
147
0
{
148
0
  return gentr->genenv.sbin;
149
0
}
150
151
struct sieve_binary_block *
152
sieve_generator_get_block(struct sieve_generator *gentr)
153
0
{
154
0
  return gentr->genenv.sblock;
155
0
}
156
157
/*
158
 * Extension support
159
 */
160
161
void sieve_generator_extension_set_context(struct sieve_generator *gentr,
162
             const struct sieve_extension *ext,
163
             void *context)
164
0
{
165
0
  if (ext->id < 0)
166
0
    return;
167
168
0
  array_idx_set(&gentr->ext_contexts, (unsigned int) ext->id, &context);
169
0
}
170
171
const void *
172
sieve_generator_extension_get_context(struct sieve_generator *gentr,
173
              const struct sieve_extension *ext)
174
0
{
175
0
  void *const *ctx;
176
177
0
  if (ext->id < 0 || ext->id >= (int) array_count(&gentr->ext_contexts))
178
0
    return NULL;
179
180
0
  ctx = array_idx(&gentr->ext_contexts, (unsigned int) ext->id);
181
182
0
  return *ctx;
183
0
}
184
185
/*
186
 * Code generation API
187
 */
188
189
static void
190
sieve_generate_debug_from_ast_node(const struct sieve_codegen_env *cgenv,
191
           struct sieve_ast_node *ast_node)
192
0
{
193
0
  sieve_size_t address = sieve_binary_block_get_size(cgenv->sblock);
194
0
  unsigned int line = sieve_ast_node_line(ast_node);
195
196
0
  sieve_binary_debug_emit(cgenv->gentr->dwriter, address, line, 0);
197
0
}
198
199
static void
200
sieve_generate_debug_from_ast_argument(const struct sieve_codegen_env *cgenv,
201
               struct sieve_ast_argument *ast_arg)
202
0
{
203
0
  sieve_size_t address = sieve_binary_block_get_size(cgenv->sblock);
204
0
  unsigned int line = sieve_ast_argument_line(ast_arg);
205
206
0
  sieve_binary_debug_emit(cgenv->gentr->dwriter, address, line, 0);
207
0
}
208
209
bool sieve_generate_argument(const struct sieve_codegen_env *cgenv,
210
           struct sieve_ast_argument *arg,
211
           struct sieve_command *cmd)
212
0
{
213
0
  const struct sieve_argument_def *arg_def;
214
215
0
  if (arg->argument == NULL || arg->argument->def == NULL)
216
0
    return FALSE;
217
218
0
  arg_def = arg->argument->def;
219
220
0
  if (arg_def->generate == NULL)
221
0
    return TRUE;
222
223
0
  sieve_generate_debug_from_ast_argument(cgenv, arg);
224
225
0
  return arg_def->generate(cgenv, arg, cmd);
226
0
}
227
228
bool sieve_generate_arguments(const struct sieve_codegen_env *cgenv,
229
            struct sieve_command *cmd,
230
            struct sieve_ast_argument **last_arg_r)
231
0
{
232
0
  enum { ARG_START, ARG_OPTIONAL, ARG_POSITIONAL } state = ARG_START;
233
0
  struct sieve_ast_argument *arg =
234
0
    sieve_ast_argument_first(cmd->ast_node);
235
236
  /* Generate all arguments with assigned generator function */
237
238
0
  while (arg != NULL) {
239
0
    const struct sieve_argument *argument;
240
0
    const struct sieve_argument_def *arg_def;
241
242
0
    if (arg->argument == NULL || arg->argument->def == NULL)
243
0
      return FALSE;
244
245
0
    argument = arg->argument;
246
0
    arg_def = argument->def;
247
248
0
    switch (state) {
249
0
    case ARG_START:
250
0
      if (argument->id_code == 0)
251
0
        state = ARG_POSITIONAL;
252
0
      else {
253
        /* Mark start of optional operands with 0
254
           operand identifier */
255
0
        sieve_binary_emit_byte(cgenv->sblock,
256
0
                   SIEVE_OPERAND_OPTIONAL);
257
258
        /* Emit argument id for optional operand */
259
0
        sieve_binary_emit_byte(
260
0
          cgenv->sblock,
261
0
          (unsigned char)argument->id_code);
262
263
0
        state = ARG_OPTIONAL;
264
0
      }
265
0
      break;
266
0
    case ARG_OPTIONAL:
267
0
      if (argument->id_code == 0)
268
0
        state = ARG_POSITIONAL;
269
270
      /* Emit argument id for optional operand (0 marks the
271
         end of the optionals) */
272
0
      sieve_binary_emit_byte(
273
0
        cgenv->sblock,
274
0
        (unsigned char)argument->id_code);
275
0
      break;
276
0
    case ARG_POSITIONAL:
277
0
      if (argument->id_code != 0)
278
0
        return FALSE;
279
0
      break;
280
0
    }
281
282
    /* Call the generation function for the argument */
283
0
    if (arg_def->generate != NULL) {
284
0
      sieve_generate_debug_from_ast_argument(cgenv, arg);
285
286
0
      if (!arg_def->generate(cgenv, arg, cmd))
287
0
        return FALSE;
288
0
    } else if (state == ARG_POSITIONAL) {
289
0
      break;
290
0
    }
291
292
0
    arg = sieve_ast_argument_next(arg);
293
0
  }
294
295
  /* Mark end of optional list if it is still open */
296
0
  if (state == ARG_OPTIONAL)
297
0
    sieve_binary_emit_byte(cgenv->sblock, 0);
298
299
0
  if (last_arg_r != NULL)
300
0
    *last_arg_r = arg;
301
302
0
  return TRUE;
303
0
}
304
305
bool sieve_generate_argument_parameters(const struct sieve_codegen_env *cgenv,
306
          struct sieve_command *cmd,
307
          struct sieve_ast_argument *arg)
308
0
{
309
0
  struct sieve_ast_argument *param = arg->parameters;
310
311
  /* Generate all parameters with assigned generator function */
312
313
0
  while (param != NULL) {
314
0
    if (param->argument != NULL && param->argument->def != NULL) {
315
0
      const struct sieve_argument_def *parameter =
316
0
        param->argument->def;
317
318
      /* Call the generation function for the parameter */
319
0
      if (parameter->generate != NULL) {
320
0
        sieve_generate_debug_from_ast_argument(
321
0
          cgenv, param);
322
323
0
        if (!parameter->generate(cgenv, param, cmd))
324
0
          return FALSE;
325
0
      }
326
0
    }
327
328
0
    param = sieve_ast_argument_next(param);
329
0
  }
330
331
0
  return TRUE;
332
0
}
333
334
bool sieve_generate_test(const struct sieve_codegen_env *cgenv,
335
       struct sieve_ast_node *tst_node,
336
       struct sieve_jumplist *jlist, bool jump_true)
337
0
{
338
0
  struct sieve_command *test;
339
0
  const struct sieve_command_def *tst_def;
340
341
0
  i_assert(tst_node->command != NULL && tst_node->command->def != NULL);
342
343
0
  test = tst_node->command;
344
0
  tst_def = test->def;
345
346
0
  if (tst_def->control_generate != NULL) {
347
0
    sieve_generate_debug_from_ast_node(cgenv, tst_node);
348
349
0
    if (tst_def->control_generate(cgenv, test, jlist, jump_true))
350
0
      return TRUE;
351
352
0
    return FALSE;
353
0
  }
354
355
0
  if (tst_def->generate != NULL) {
356
0
    sieve_generate_debug_from_ast_node(cgenv, tst_node);
357
358
0
    if (tst_def->generate(cgenv, test)) {
359
360
0
      if (jump_true) {
361
0
        sieve_operation_emit(cgenv->sblock, NULL,
362
0
                 &sieve_jmptrue_operation);
363
0
      } else {
364
0
        sieve_operation_emit(cgenv->sblock, NULL,
365
0
                 &sieve_jmpfalse_operation);
366
0
      }
367
0
      sieve_jumplist_add(
368
0
        jlist,
369
0
        sieve_binary_emit_offset(cgenv->sblock, 0));
370
0
      return TRUE;
371
0
    }
372
373
0
    return FALSE;
374
0
  }
375
376
0
  return TRUE;
377
0
}
378
379
static bool
380
sieve_generate_command(const struct sieve_codegen_env *cgenv,
381
           struct sieve_ast_node *cmd_node)
382
0
{
383
0
  struct sieve_command *command;
384
0
  const struct sieve_command_def *cmd_def;
385
386
0
  i_assert(cmd_node->command != NULL && cmd_node->command->def != NULL);
387
388
0
  command = cmd_node->command;
389
0
  cmd_def = command->def;
390
391
0
  if (cmd_def->generate != NULL) {
392
0
    sieve_generate_debug_from_ast_node(cgenv, cmd_node);
393
394
0
    return cmd_def->generate(cgenv, command);
395
0
  }
396
397
0
  return TRUE;
398
0
}
399
400
bool sieve_generate_block(const struct sieve_codegen_env *cgenv,
401
        struct sieve_ast_node *block)
402
0
{
403
0
  bool result = TRUE;
404
0
  struct sieve_ast_node *cmd_node;
405
406
0
  T_BEGIN {
407
0
    cmd_node = sieve_ast_command_first(block);
408
0
    while (result && cmd_node != NULL) {
409
0
      result = sieve_generate_command(cgenv, cmd_node);
410
0
      cmd_node = sieve_ast_command_next(cmd_node);
411
0
    }
412
0
  } T_END;
413
414
0
  return result;
415
0
}
416
417
struct sieve_binary *
418
sieve_generator_run(struct sieve_generator *gentr,
419
        struct sieve_binary_block **sblock_r)
420
0
{
421
0
  bool topmost = (sblock_r == NULL || *sblock_r == NULL);
422
0
  struct sieve_binary *sbin;
423
0
  struct sieve_binary_block *sblock, *debug_block;
424
0
  const struct sieve_extension *const *extensions;
425
0
  unsigned int i, ext_count;
426
0
  bool result = TRUE;
427
428
  /* Initialize */
429
430
0
  if (topmost) {
431
0
    sbin = sieve_binary_create_new(
432
0
      sieve_ast_script(gentr->genenv.ast));
433
0
    sblock = sieve_binary_block_get(
434
0
      sbin, SBIN_SYSBLOCK_MAIN_PROGRAM);
435
0
  } else {
436
0
    sblock = *sblock_r;
437
0
    sbin = sieve_binary_block_get_binary(sblock);
438
0
  }
439
440
0
  i_assert(sbin != NULL);
441
442
0
  gentr->genenv.sbin = sbin;
443
0
  gentr->genenv.sblock = sblock;
444
0
  sieve_binary_ref(gentr->genenv.sbin);
445
446
  /* Create debug block */
447
0
  debug_block = sieve_binary_block_create(sbin);
448
0
  gentr->dwriter = sieve_binary_debug_writer_init(debug_block);
449
0
  (void)sieve_binary_emit_unsigned(
450
0
    sblock, sieve_binary_block_get_id(debug_block));
451
452
  /* Load extensions linked to the AST and emit a list in code */
453
0
  extensions = sieve_ast_extensions_get(gentr->genenv.ast, &ext_count);
454
0
  (void) sieve_binary_emit_unsigned(sblock, ext_count);
455
0
  for (i = 0; i < ext_count; i++) {
456
0
    const struct sieve_extension *ext = extensions[i];
457
0
    bool deferred;
458
459
    /* Link to binary */
460
0
    (void)sieve_binary_extension_link(sbin, ext);
461
462
    /* Emit */
463
0
    sieve_binary_emit_extension(sblock, ext, 0);
464
465
    /* Emit deferred flag */
466
0
    deferred = !sieve_ast_extension_is_required(
467
0
      gentr->genenv.ast, ext);
468
0
    sieve_binary_emit_byte(sblock, (deferred ? 1 : 0));
469
470
    /* Load */
471
0
    if (ext->def != NULL && ext->def->generator_load != NULL &&
472
0
        !ext->def->generator_load(ext, &gentr->genenv))
473
0
      result = FALSE;
474
0
  }
475
476
  /* Generate code */
477
478
0
  if (result) {
479
0
    if (!sieve_generate_block(&gentr->genenv,
480
0
            sieve_ast_root(gentr->genenv.ast))) {
481
0
      result = FALSE;
482
0
    } else if (topmost) {
483
0
      sieve_binary_activate(sbin);
484
0
    }
485
0
  }
486
487
  /* Cleanup */
488
489
0
  sieve_binary_unref(&gentr->genenv.sbin);
490
0
  gentr->genenv.sblock = NULL;
491
492
0
  if (!result) {
493
0
    if (topmost) {
494
0
      sieve_binary_unref(&sbin);
495
0
      if (sblock_r != NULL)
496
0
        *sblock_r = NULL;
497
0
    }
498
0
    sbin = NULL;
499
0
  } else {
500
0
    if (sblock_r != NULL)
501
0
      *sblock_r = sblock;
502
0
  }
503
504
0
  return sbin;
505
0
}
506
507
/*
508
 * Error handling
509
 */
510
511
#undef sieve_generator_error
512
void sieve_generator_error(struct sieve_generator *gentr,
513
         const char *csrc_filename, unsigned int csrc_linenum,
514
         unsigned int source_line, const char *fmt, ...)
515
0
{
516
0
  struct sieve_error_params params = {
517
0
    .log_type = LOG_TYPE_ERROR,
518
0
    .csrc = {
519
0
      .filename = csrc_filename,
520
0
      .linenum = csrc_linenum,
521
0
    },
522
0
  };
523
0
  va_list args;
524
525
0
  params.location =
526
0
    sieve_error_script_location(gentr->genenv.script, source_line);
527
528
0
  va_start(args, fmt);
529
0
  sieve_logv(gentr->ehandler, &params, fmt, args);
530
0
  va_end(args);
531
0
}
532
533
#undef sieve_generator_warning
534
void sieve_generator_warning(struct sieve_generator *gentr,
535
           const char *csrc_filename,
536
           unsigned int csrc_linenum,
537
           unsigned int source_line, const char *fmt, ...)
538
0
{
539
0
  struct sieve_error_params params = {
540
0
    .log_type = LOG_TYPE_WARNING,
541
0
    .csrc = {
542
0
      .filename = csrc_filename,
543
0
      .linenum = csrc_linenum,
544
0
    },
545
0
  };
546
0
  va_list args;
547
548
0
  params.location =
549
0
    sieve_error_script_location(gentr->genenv.script, source_line);
550
551
0
  va_start(args, fmt);
552
0
  sieve_logv(gentr->ehandler, &params, fmt, args);
553
0
  va_end(args);
554
0
}
555
556
#undef sieve_generator_critical
557
void sieve_generator_critical(struct sieve_generator *gentr,
558
            const char *csrc_filename,
559
            unsigned int csrc_linenum,
560
            unsigned int source_line, const char *fmt, ...)
561
0
{
562
0
  struct sieve_error_params params = {
563
0
    .log_type = LOG_TYPE_ERROR,
564
0
    .csrc = {
565
0
      .filename = csrc_filename,
566
0
      .linenum = csrc_linenum,
567
0
    },
568
0
  };
569
0
  va_list args;
570
571
0
  params.location =
572
0
    sieve_error_script_location(gentr->genenv.script, source_line);
573
574
0
  va_start(args, fmt);
575
0
  sieve_criticalv(gentr->genenv.svinst, gentr->ehandler, &params,
576
0
      "Code generation failed", fmt, args);
577
  va_end(args);
578
0
}