From 7050cd8d5ccc12cf7230d4025b929825d87f396d Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Sun, 29 Nov 2015 11:50:44 +0100
Subject: [PATCH] lib-sieve: Implemented running code loops in the interpreter.

---
 src/lib-sieve/sieve-interpreter.c | 192 +++++++++++++++++++++++++++++-
 src/lib-sieve/sieve-interpreter.h |  32 +++++
 2 files changed, 221 insertions(+), 3 deletions(-)

diff --git a/src/lib-sieve/sieve-interpreter.c b/src/lib-sieve/sieve-interpreter.c
index b68659a12..62bf3b340 100644
--- a/src/lib-sieve/sieve-interpreter.c
+++ b/src/lib-sieve/sieve-interpreter.c
@@ -37,6 +37,18 @@ struct sieve_interpreter_extension_reg {
 	void *context;
 };
 
+/*
+ * Code loop
+ */
+
+struct sieve_interpreter_loop {
+	unsigned int level;
+	sieve_size_t begin, end;
+	const struct sieve_extension_def *ext_def;
+	pool_t pool;
+	void *context;
+};
+
 /*
  * Interpreter
  */
@@ -55,6 +67,10 @@ struct sieve_interpreter {
 	bool interrupted;         /* Interpreter interrupt requested */
 	bool test_result;         /* Result of previous test command */
 
+	/* Loop stack */
+	ARRAY(struct sieve_interpreter_loop) loop_stack;
+	sieve_size_t loop_limit;
+
 	/* Runtime environment */
 	struct sieve_runtime_env runenv;
 	struct sieve_runtime_trace trace;
@@ -225,14 +241,21 @@ void sieve_interpreter_free(struct sieve_interpreter **_interp)
 	struct sieve_interpreter *interp = *_interp;
 	struct sieve_runtime_env *renv = &interp->runenv;
 	const struct sieve_interpreter_extension_reg *eregs;
-	unsigned int ext_count, i;
+	struct sieve_interpreter_loop *loops;
+	unsigned int count, i;
+
+	if ( array_is_created(&interp->loop_stack) ) {
+		loops = array_get_modifiable(&interp->loop_stack, &count);
+		for ( i = 0; i < count; i++ )
+			pool_unref(&loops[i].pool);
+	}
 
 	interp->trace.indent = 0;
 	sieve_runtime_trace_end(renv);
 
 	/* Signal registered extensions that the interpreter is being destroyed */
-	eregs = array_get(&interp->extensions, &ext_count);
-	for ( i = 0; i < ext_count; i++ ) {
+	eregs = array_get(&interp->extensions, &count);
+	for ( i = 0; i < count; i++ ) {
 		if ( eregs[i].intext != NULL && eregs[i].intext->free != NULL )
 			eregs[i].intext->free(eregs[i].ext, interp, eregs[i].context);
 	}
@@ -455,6 +478,169 @@ void *sieve_interpreter_extension_get_context
 	return reg->context;
 }
 
+/*
+ * Loop handling
+ */
+
+struct sieve_interpreter_loop *sieve_interpreter_loop_start
+(struct sieve_interpreter *interp, sieve_size_t loop_end,
+	const struct sieve_extension_def *ext_def)
+{
+	const struct sieve_runtime_env *renv = &interp->runenv;
+	struct sieve_interpreter_loop *loop;
+
+	i_assert( loop_end > interp->runenv.pc );
+
+	if ( loop_end > sieve_binary_block_get_size(renv->sblock) ) {
+		sieve_runtime_trace_error(renv,
+			"loop end offset out of range");
+		return NULL;
+	}
+
+	if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS) ) {
+		unsigned int line =
+			sieve_runtime_get_source_location(renv, loop_end);
+
+		if ( sieve_runtime_trace_hasflag(renv, SIEVE_TRFLG_ADDRESSES) ) {
+			sieve_runtime_trace(renv, 0, "loop ends at line %d [%08llx]",
+				line, (long long unsigned int) loop_end);
+		} else {
+			sieve_runtime_trace(renv, 0, "loop ends at line %d", line);
+		}
+	}
+
+	if ( !array_is_created(&interp->loop_stack) )
+		p_array_init(&interp->loop_stack, interp->pool, 8);
+
+	loop = array_append_space(&interp->loop_stack);
+	loop->level = array_count(&interp->loop_stack)-1;
+	loop->ext_def = ext_def;
+	loop->begin = interp->runenv.pc;
+	loop->end = loop_end;
+	loop->pool =  pool_alloconly_create("sieve_interpreter", 128);
+	
+	return loop;
+}
+
+struct sieve_interpreter_loop *sieve_interpreter_loop_get
+(struct sieve_interpreter *interp, sieve_size_t loop_end,
+	const struct sieve_extension_def *ext_def)
+{
+	struct sieve_interpreter_loop *loops;
+	unsigned int count, i;
+
+	if ( !array_is_created(&interp->loop_stack) )
+		return NULL;
+
+	loops = array_get_modifiable(&interp->loop_stack, &count);
+	for ( i = count; i > 0; i-- ) {
+		/* We're really making sure our loop matches */
+		if ( loops[i-1].end == loop_end &&
+			loops[i-1].ext_def == ext_def )
+			return &loops[i-1];
+	}
+	return NULL;
+}
+
+void sieve_interpreter_loop_next(struct sieve_interpreter *interp,
+	struct sieve_interpreter_loop *loop,
+	sieve_size_t loop_begin)
+{
+	const struct sieve_runtime_env *renv = &interp->runenv;
+	struct sieve_interpreter_loop *loops;
+	unsigned int count;
+
+	if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS) ) {
+		unsigned int line =
+			sieve_runtime_get_source_location(renv, loop_begin);
+
+		if ( sieve_runtime_trace_hasflag(renv, SIEVE_TRFLG_ADDRESSES) ) {
+			sieve_runtime_trace(renv, 0, "looping back to line %d [%08llx]",
+				line, (long long unsigned int) loop_begin);
+		} else {
+			sieve_runtime_trace(renv, 0, "looping back to line %d", line);
+		}
+	}
+
+	i_assert( loop->begin == loop_begin );
+
+	i_assert( array_is_created(&interp->loop_stack) );
+	loops = array_get_modifiable(&interp->loop_stack, &count);
+	i_assert( &loops[count-1] == loop );
+
+	interp->runenv.pc = loop_begin;
+}
+
+void sieve_interpreter_loop_break(struct sieve_interpreter *interp,
+	struct sieve_interpreter_loop *loop)
+{
+	const struct sieve_runtime_env *renv = &interp->runenv;
+	struct sieve_interpreter_loop *loops;
+	sieve_size_t loop_end = loop->end;
+	unsigned int count, i;
+
+	i_assert( array_is_created(&interp->loop_stack) );
+	loops = array_get_modifiable(&interp->loop_stack, &count);
+	for ( i = count; i > 0 && &loops[i-1] != loop; i-- )
+		pool_unref(&loops[i-1].pool);
+	i_assert( i > 0 && &loops[i-1] == loop );
+
+	array_delete(&interp->loop_stack, i-1, count - (i-1));
+
+	if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS) ) {
+		unsigned int jmp_line =
+			sieve_runtime_get_source_location(renv, loop_end);
+
+		if ( sieve_runtime_trace_hasflag(renv, SIEVE_TRFLG_ADDRESSES) ) {
+			sieve_runtime_trace(renv, 0, "ending loop at line %d [%08llx]",
+				jmp_line, (long long unsigned int) loop_end);
+		} else {
+			sieve_runtime_trace(renv, 0, "ending loop at line %d", jmp_line);
+		}
+	}
+
+	interp->runenv.pc = loop->end;
+}
+
+struct sieve_interpreter_loop *sieve_interpreter_loop_get_surrounding
+(struct sieve_interpreter *interp,
+	struct sieve_interpreter_loop *loop,
+	const struct sieve_extension_def *ext_def)
+{
+	struct sieve_interpreter_loop *loops;
+	unsigned int count, i;
+
+	if ( !array_is_created(&interp->loop_stack) )
+		return NULL;
+
+	loops = array_get_modifiable(&interp->loop_stack, &count);
+	i_assert(loop == NULL || loop->level < count);
+
+	for ( i = (loop == NULL ? count : loop->level); i > 0; i-- ) {
+		if ( ext_def == NULL || loops[i-1].ext_def == ext_def )
+			return &loops[i-1];
+	}
+	return NULL;
+}
+
+pool_t sieve_interpreter_loop_get_pool
+(struct sieve_interpreter_loop *loop)
+{
+	return loop->pool;
+}
+
+void *sieve_interpreter_loop_get_context
+(struct sieve_interpreter_loop *loop)
+{
+	return loop->context;
+}
+
+void sieve_interpreter_loop_set_context
+(struct sieve_interpreter_loop *loop, void *context)
+{
+	loop->context = context;
+}
+
 /*
  * Program flow
  */
diff --git a/src/lib-sieve/sieve-interpreter.h b/src/lib-sieve/sieve-interpreter.h
index 501efb353..0073d097e 100644
--- a/src/lib-sieve/sieve-interpreter.h
+++ b/src/lib-sieve/sieve-interpreter.h
@@ -45,6 +45,38 @@ struct sieve_instance *sieve_interpreter_svinst
 void sieve_interpreter_set_result
 	(struct sieve_interpreter *interp, struct sieve_result *result);
 
+/*
+ * Loop handling
+ */
+
+struct sieve_interpreter_loop;
+
+struct sieve_interpreter_loop *sieve_interpreter_loop_start
+	(struct sieve_interpreter *interp, sieve_size_t loop_end,
+		const struct sieve_extension_def *ext_def);
+struct sieve_interpreter_loop *sieve_interpreter_loop_get
+	(struct sieve_interpreter *interp, sieve_size_t loop_end,
+		const struct sieve_extension_def *ext_def);
+void sieve_interpreter_loop_next
+	(struct sieve_interpreter *interp,
+		struct sieve_interpreter_loop *loop,
+		sieve_size_t loop_begin);
+void sieve_interpreter_loop_break
+	(struct sieve_interpreter *interp,
+		struct sieve_interpreter_loop *loop);
+
+struct sieve_interpreter_loop *sieve_interpreter_loop_get_surrounding
+(struct sieve_interpreter *interp,
+	struct sieve_interpreter_loop *loop,
+	const struct sieve_extension_def *ext_def) ATTR_NULL(2, 3);
+
+pool_t sieve_interpreter_loop_get_pool
+	(struct sieve_interpreter_loop *loop) ATTR_PURE;
+void *sieve_interpreter_loop_get_context
+	(struct sieve_interpreter_loop *loop) ATTR_PURE;
+void sieve_interpreter_loop_set_context
+	(struct sieve_interpreter_loop *loop, void *context);
+
 /*
  * Program flow
  */
-- 
GitLab