Coverage Report

Created: 2026-10-17 06:32

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