summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/lib-sieve/sieve-generator.c
diff options
context:
space:
mode:
Diffstat (limited to 'pigeonhole/src/lib-sieve/sieve-generator.c')
-rw-r--r--pigeonhole/src/lib-sieve/sieve-generator.c578
1 files changed, 578 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/sieve-generator.c b/pigeonhole/src/lib-sieve/sieve-generator.c
new file mode 100644
index 0000000..ebc06ed
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/sieve-generator.c
@@ -0,0 +1,578 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "mempool.h"
+
+#include "sieve-common.h"
+#include "sieve-script.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-code.h"
+#include "sieve-binary.h"
+
+#include "sieve-generator.h"
+
+/*
+ * Jump list
+ */
+
+struct sieve_jumplist *
+sieve_jumplist_create(pool_t pool, struct sieve_binary_block *sblock)
+{
+ struct sieve_jumplist *jlist;
+
+ jlist = p_new(pool, struct sieve_jumplist, 1);
+ jlist->block = sblock;
+ p_array_init(&jlist->jumps, pool, 4);
+
+ return jlist;
+}
+
+void sieve_jumplist_init_temp(struct sieve_jumplist *jlist,
+ struct sieve_binary_block *sblock)
+{
+ jlist->block = sblock;
+ t_array_init(&jlist->jumps, 4);
+}
+
+void sieve_jumplist_reset(struct sieve_jumplist *jlist)
+{
+ array_clear(&jlist->jumps);
+}
+
+void sieve_jumplist_add(struct sieve_jumplist *jlist, sieve_size_t jump)
+{
+ array_append(&jlist->jumps, &jump, 1);
+}
+
+void sieve_jumplist_resolve(struct sieve_jumplist *jlist)
+{
+ unsigned int i;
+
+ for (i = 0; i < array_count(&jlist->jumps); i++) {
+ const sieve_size_t *jump = array_idx(&jlist->jumps, i);
+
+ sieve_binary_resolve_offset(jlist->block, *jump);
+ }
+}
+
+/*
+ * Code Generator
+ */
+
+struct sieve_generator {
+ pool_t pool;
+
+ struct sieve_instance *instance;
+
+ struct sieve_error_handler *ehandler;
+
+ struct sieve_codegen_env genenv;
+ struct sieve_binary_debug_writer *dwriter;
+
+ ARRAY(void *) ext_contexts;
+};
+
+struct sieve_generator *
+sieve_generator_create(struct sieve_ast *ast,
+ struct sieve_error_handler *ehandler,
+ enum sieve_compile_flags flags)
+{
+ pool_t pool;
+ struct sieve_generator *gentr;
+ struct sieve_script *script;
+ struct sieve_instance *svinst;
+
+ pool = pool_alloconly_create("sieve_generator", 4096);
+ gentr = p_new(pool, struct sieve_generator, 1);
+ gentr->pool = pool;
+
+ gentr->ehandler = ehandler;
+ sieve_error_handler_ref(ehandler);
+
+ gentr->genenv.gentr = gentr;
+ gentr->genenv.flags = flags;
+ gentr->genenv.ast = ast;
+ sieve_ast_ref(ast);
+
+ script = sieve_ast_script(ast);
+ svinst = sieve_script_svinst(script);
+
+ gentr->genenv.script = script;
+ gentr->genenv.svinst = svinst;
+
+ /* Setup storage for extension contexts */
+ p_array_init(&gentr->ext_contexts, pool,
+ sieve_extensions_get_count(svinst));
+
+ return gentr;
+}
+
+void sieve_generator_free(struct sieve_generator **gentr)
+{
+ sieve_ast_unref(&(*gentr)->genenv.ast);
+
+ sieve_error_handler_unref(&(*gentr)->ehandler);
+ sieve_binary_debug_writer_deinit(&(*gentr)->dwriter);
+
+ sieve_binary_unref(&(*gentr)->genenv.sbin);
+
+ pool_unref(&((*gentr)->pool));
+
+ *gentr = NULL;
+}
+
+/*
+ * Accessors
+ */
+
+struct sieve_error_handler *
+sieve_generator_error_handler(struct sieve_generator *gentr)
+{
+ return gentr->ehandler;
+}
+
+pool_t sieve_generator_pool(struct sieve_generator *gentr)
+{
+ return gentr->pool;
+}
+
+struct sieve_script *sieve_generator_script(struct sieve_generator *gentr)
+{
+ return gentr->genenv.script;
+}
+
+struct sieve_binary *sieve_generator_get_binary(struct sieve_generator *gentr)
+{
+ return gentr->genenv.sbin;
+}
+
+struct sieve_binary_block *
+sieve_generator_get_block(struct sieve_generator *gentr)
+{
+ return gentr->genenv.sblock;
+}
+
+/*
+ * Extension support
+ */
+
+void sieve_generator_extension_set_context(struct sieve_generator *gentr,
+ const struct sieve_extension *ext,
+ void *context)
+{
+ if (ext->id < 0)
+ return;
+
+ array_idx_set(&gentr->ext_contexts, (unsigned int) ext->id, &context);
+}
+
+const void *
+sieve_generator_extension_get_context(struct sieve_generator *gentr,
+ const struct sieve_extension *ext)
+{
+ void * const *ctx;
+
+ if (ext->id < 0 || ext->id >= (int) array_count(&gentr->ext_contexts))
+ return NULL;
+
+ ctx = array_idx(&gentr->ext_contexts, (unsigned int) ext->id);
+
+ return *ctx;
+}
+
+/*
+ * Code generation API
+ */
+
+static void
+sieve_generate_debug_from_ast_node(const struct sieve_codegen_env *cgenv,
+ struct sieve_ast_node *ast_node)
+{
+ sieve_size_t address = sieve_binary_block_get_size(cgenv->sblock);
+ unsigned int line = sieve_ast_node_line(ast_node);
+
+ sieve_binary_debug_emit(cgenv->gentr->dwriter, address, line, 0);
+}
+
+static void
+sieve_generate_debug_from_ast_argument(const struct sieve_codegen_env *cgenv,
+ struct sieve_ast_argument *ast_arg)
+{
+ sieve_size_t address = sieve_binary_block_get_size(cgenv->sblock);
+ unsigned int line = sieve_ast_argument_line(ast_arg);
+
+ sieve_binary_debug_emit(cgenv->gentr->dwriter, address, line, 0);
+}
+
+bool sieve_generate_argument(const struct sieve_codegen_env *cgenv,
+ struct sieve_ast_argument *arg,
+ struct sieve_command *cmd)
+{
+ const struct sieve_argument_def *arg_def;
+
+ if (arg->argument == NULL || arg->argument->def == NULL)
+ return FALSE;
+
+ arg_def = arg->argument->def;
+
+ if (arg_def->generate == NULL)
+ return TRUE;
+
+ sieve_generate_debug_from_ast_argument(cgenv, arg);
+
+ return arg_def->generate(cgenv, arg, cmd);
+}
+
+bool sieve_generate_arguments(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *cmd,
+ struct sieve_ast_argument **last_arg_r)
+{
+ enum { ARG_START, ARG_OPTIONAL, ARG_POSITIONAL } state = ARG_START;
+ struct sieve_ast_argument *arg =
+ sieve_ast_argument_first(cmd->ast_node);
+
+ /* Generate all arguments with assigned generator function */
+
+ while (arg != NULL) {
+ const struct sieve_argument *argument;
+ const struct sieve_argument_def *arg_def;
+
+ if (arg->argument == NULL || arg->argument->def == NULL)
+ return FALSE;
+
+ argument = arg->argument;
+ arg_def = argument->def;
+
+ switch (state) {
+ case ARG_START:
+ if (argument->id_code == 0)
+ state = ARG_POSITIONAL;
+ else {
+ /* Mark start of optional operands with 0
+ operand identifier */
+ sieve_binary_emit_byte(cgenv->sblock,
+ SIEVE_OPERAND_OPTIONAL);
+
+ /* Emit argument id for optional operand */
+ sieve_binary_emit_byte(
+ cgenv->sblock,
+ (unsigned char)argument->id_code);
+
+ state = ARG_OPTIONAL;
+ }
+ break;
+ case ARG_OPTIONAL:
+ if (argument->id_code == 0)
+ state = ARG_POSITIONAL;
+
+ /* Emit argument id for optional operand (0 marks the
+ end of the optionals) */
+ sieve_binary_emit_byte(
+ cgenv->sblock,
+ (unsigned char)argument->id_code);
+ break;
+ case ARG_POSITIONAL:
+ if (argument->id_code != 0)
+ return FALSE;
+ break;
+ }
+
+ /* Call the generation function for the argument */
+ if (arg_def->generate != NULL) {
+ sieve_generate_debug_from_ast_argument(cgenv, arg);
+
+ if (!arg_def->generate(cgenv, arg, cmd))
+ return FALSE;
+ } else if (state == ARG_POSITIONAL) {
+ break;
+ }
+
+ arg = sieve_ast_argument_next(arg);
+ }
+
+ /* Mark end of optional list if it is still open */
+ if (state == ARG_OPTIONAL)
+ sieve_binary_emit_byte(cgenv->sblock, 0);
+
+ if (last_arg_r != NULL)
+ *last_arg_r = arg;
+
+ return TRUE;
+}
+
+bool sieve_generate_argument_parameters(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *cmd,
+ struct sieve_ast_argument *arg)
+{
+ struct sieve_ast_argument *param = arg->parameters;
+
+ /* Generate all parameters with assigned generator function */
+
+ while (param != NULL) {
+ if (param->argument != NULL && param->argument->def != NULL) {
+ const struct sieve_argument_def *parameter =
+ param->argument->def;
+
+ /* Call the generation function for the parameter */
+ if (parameter->generate != NULL) {
+ sieve_generate_debug_from_ast_argument(
+ cgenv, param);
+
+ if (!parameter->generate(cgenv, param, cmd))
+ return FALSE;
+ }
+ }
+
+ param = sieve_ast_argument_next(param);
+ }
+
+ return TRUE;
+}
+
+bool sieve_generate_test(const struct sieve_codegen_env *cgenv,
+ struct sieve_ast_node *tst_node,
+ struct sieve_jumplist *jlist, bool jump_true)
+{
+ struct sieve_command *test;
+ const struct sieve_command_def *tst_def;
+
+ i_assert(tst_node->command != NULL && tst_node->command->def != NULL);
+
+ test = tst_node->command;
+ tst_def = test->def;
+
+ if (tst_def->control_generate != NULL) {
+ sieve_generate_debug_from_ast_node(cgenv, tst_node);
+
+ if (tst_def->control_generate(cgenv, test, jlist, jump_true))
+ return TRUE;
+
+ return FALSE;
+ }
+
+ if (tst_def->generate != NULL) {
+ sieve_generate_debug_from_ast_node(cgenv, tst_node);
+
+ if (tst_def->generate(cgenv, test)) {
+
+ if (jump_true) {
+ sieve_operation_emit(cgenv->sblock, NULL,
+ &sieve_jmptrue_operation);
+ } else {
+ sieve_operation_emit(cgenv->sblock, NULL,
+ &sieve_jmpfalse_operation);
+ }
+ sieve_jumplist_add(
+ jlist,
+ sieve_binary_emit_offset(cgenv->sblock, 0));
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static bool
+sieve_generate_command(const struct sieve_codegen_env *cgenv,
+ struct sieve_ast_node *cmd_node)
+{
+ struct sieve_command *command;
+ const struct sieve_command_def *cmd_def;
+
+ i_assert(cmd_node->command != NULL && cmd_node->command->def != NULL);
+
+ command = cmd_node->command;
+ cmd_def = command->def;
+
+ if (cmd_def->generate != NULL) {
+ sieve_generate_debug_from_ast_node(cgenv, cmd_node);
+
+ return cmd_def->generate(cgenv, command);
+ }
+
+ return TRUE;
+}
+
+bool sieve_generate_block(const struct sieve_codegen_env *cgenv,
+ struct sieve_ast_node *block)
+{
+ bool result = TRUE;
+ struct sieve_ast_node *cmd_node;
+
+ T_BEGIN {
+ cmd_node = sieve_ast_command_first(block);
+ while (result && cmd_node != NULL) {
+ result = sieve_generate_command(cgenv, cmd_node);
+ cmd_node = sieve_ast_command_next(cmd_node);
+ }
+ } T_END;
+
+ return result;
+}
+
+struct sieve_binary *
+sieve_generator_run(struct sieve_generator *gentr,
+ struct sieve_binary_block **sblock_r)
+{
+ bool topmost = (sblock_r == NULL || *sblock_r == NULL);
+ struct sieve_binary *sbin;
+ struct sieve_binary_block *sblock, *debug_block;
+ const struct sieve_extension *const *extensions;
+ unsigned int i, ext_count;
+ bool result = TRUE;
+
+ /* Initialize */
+
+ if (topmost) {
+ sbin = sieve_binary_create_new(
+ sieve_ast_script(gentr->genenv.ast));
+ sblock = sieve_binary_block_get(
+ sbin, SBIN_SYSBLOCK_MAIN_PROGRAM);
+ } else {
+ sblock = *sblock_r;
+ sbin = sieve_binary_block_get_binary(sblock);
+ }
+
+ i_assert(sbin != NULL);
+
+ gentr->genenv.sbin = sbin;
+ gentr->genenv.sblock = sblock;
+ sieve_binary_ref(gentr->genenv.sbin);
+
+ /* Create debug block */
+ debug_block = sieve_binary_block_create(sbin);
+ gentr->dwriter = sieve_binary_debug_writer_init(debug_block);
+ (void)sieve_binary_emit_unsigned(
+ sblock, sieve_binary_block_get_id(debug_block));
+
+ /* Load extensions linked to the AST and emit a list in code */
+ extensions = sieve_ast_extensions_get(gentr->genenv.ast, &ext_count);
+ (void) sieve_binary_emit_unsigned(sblock, ext_count);
+ for (i = 0; i < ext_count; i++) {
+ const struct sieve_extension *ext = extensions[i];
+ bool deferred;
+
+ /* Link to binary */
+ (void)sieve_binary_extension_link(sbin, ext);
+
+ /* Emit */
+ sieve_binary_emit_extension(sblock, ext, 0);
+
+ /* Emit deferred flag */
+ deferred = !sieve_ast_extension_is_required(
+ gentr->genenv.ast, ext);
+ sieve_binary_emit_byte(sblock, (deferred ? 1 : 0));
+
+ /* Load */
+ if (ext->def != NULL && ext->def->generator_load != NULL &&
+ !ext->def->generator_load(ext, &gentr->genenv))
+ result = FALSE;
+ }
+
+ /* Generate code */
+
+ if (result) {
+ if (!sieve_generate_block(&gentr->genenv,
+ sieve_ast_root(gentr->genenv.ast))) {
+ result = FALSE;
+ } else if (topmost) {
+ sieve_binary_activate(sbin);
+ }
+ }
+
+ /* Cleanup */
+
+ sieve_binary_unref(&gentr->genenv.sbin);
+ gentr->genenv.sblock = NULL;
+
+ if (!result) {
+ if (topmost) {
+ sieve_binary_unref(&sbin);
+ if (sblock_r != NULL)
+ *sblock_r = NULL;
+ }
+ sbin = NULL;
+ } else {
+ if (sblock_r != NULL)
+ *sblock_r = sblock;
+ }
+
+ return sbin;
+}
+
+/*
+ * Error handling
+ */
+
+#undef sieve_generator_error
+void sieve_generator_error(struct sieve_generator *gentr,
+ const char *csrc_filename, unsigned int csrc_linenum,
+ unsigned int source_line, const char *fmt, ...)
+{
+ struct sieve_error_params params = {
+ .log_type = LOG_TYPE_ERROR,
+ .csrc = {
+ .filename = csrc_filename,
+ .linenum = csrc_linenum,
+ },
+ };
+ va_list args;
+
+ params.location =
+ sieve_error_script_location(gentr->genenv.script, source_line);
+
+ va_start(args, fmt);
+ sieve_logv(gentr->ehandler, &params, fmt, args);
+ va_end(args);
+}
+
+#undef sieve_generator_warning
+void sieve_generator_warning(struct sieve_generator *gentr,
+ const char *csrc_filename,
+ unsigned int csrc_linenum,
+ unsigned int source_line, const char *fmt, ...)
+{
+ struct sieve_error_params params = {
+ .log_type = LOG_TYPE_WARNING,
+ .csrc = {
+ .filename = csrc_filename,
+ .linenum = csrc_linenum,
+ },
+ };
+ va_list args;
+
+ params.location =
+ sieve_error_script_location(gentr->genenv.script, source_line);
+
+ va_start(args, fmt);
+ sieve_logv(gentr->ehandler, &params, fmt, args);
+ va_end(args);
+}
+
+#undef sieve_generator_critical
+void sieve_generator_critical(struct sieve_generator *gentr,
+ const char *csrc_filename,
+ unsigned int csrc_linenum,
+ unsigned int source_line, const char *fmt, ...)
+{
+ struct sieve_error_params params = {
+ .log_type = LOG_TYPE_ERROR,
+ .csrc = {
+ .filename = csrc_filename,
+ .linenum = csrc_linenum,
+ },
+ };
+ va_list args;
+
+ params.location =
+ sieve_error_script_location(gentr->genenv.script, source_line);
+
+ va_start(args, fmt);
+ sieve_criticalv(gentr->genenv.svinst, gentr->ehandler, &params,
+ "Code generation failed", fmt, args);
+ va_end(args);
+}