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