/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, ¶ms, 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, ¶ms, 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, ¶ms, |
575 | 0 | "Code generation failed", fmt, args); |
576 | | va_end(args); |
577 | 0 | } |