automatic dependency tracking


git-svn-id: https://asn1c.svn.sourceforge.net/svnroot/asn1c/trunk@975 59561ff5-6e30-0410-9f3c-9617f08c8826
diff --git a/libasn1compiler/asn1c_C.c b/libasn1compiler/asn1c_C.c
index a2bdcb7..f8962be 100644
--- a/libasn1compiler/asn1c_C.c
+++ b/libasn1compiler/asn1c_C.c
@@ -42,6 +42,9 @@
 static int emit_member_table(arg_t *arg, asn1p_expr_t *expr);
 static int emit_tag2member_map(arg_t *arg, tag2el_t *tag2el, int tag2el_count, const char *opt_modifier);
 static int emit_include_dependencies(arg_t *arg);
+static asn1p_expr_t *terminal_structable(arg_t *arg, asn1p_expr_t *expr);
+static int expr_defined_recursively(arg_t *arg, asn1p_expr_t *expr);
+static int asn1c_recurse(arg_t *arg, asn1p_expr_t *expr, int (*callback)(arg_t *arg, void *key), void *key);
 
 enum tvm_compat {
 	_TVM_SAME	= 0,	/* tags and all_tags are same */
@@ -274,7 +277,7 @@
 		if(v->expr_type == A1TC_EXTENSIBLE)
 			if(comp_mode < 3) comp_mode++;
 		if(comp_mode == 1)
-			v->marker.flags |= EM_INDIRECT;
+			v->marker.flags |= EM_OMITABLE;
 		EMBED(v);
 	}
 
@@ -335,7 +338,7 @@
 				continue;
 			}
 			if(comp_mode == 1)
-				v->marker.flags |= EM_INDIRECT;
+				v->marker.flags |= EM_OMITABLE;
 			emit_member_table(arg, v);
 			elements++;
 		});
@@ -435,7 +438,7 @@
 			if(comp_mode < 3) comp_mode++;
 		}
 		if(comp_mode == 1)
-			v->marker.flags |= EM_INDIRECT;
+			v->marker.flags |= EM_OMITABLE;
 		EMBED(v);
 	}
 
@@ -509,7 +512,7 @@
 				if(comp_mode < 3) comp_mode++;
 			} else {
 				if(comp_mode == 1)
-					v->marker.flags |= EM_INDIRECT;
+					v->marker.flags |= EM_OMITABLE;
 				emit_member_table(arg, v);
 				elements++;
 			}
@@ -552,7 +555,7 @@
 				OUT(" | ");
 			}
 			OUT("(%d << %d)",
-				v->marker.flags?0:1,
+				(v->marker.flags & EM_OMITABLE) != EM_OMITABLE,
 				7 - (el % 8));
 			if(el && (el % 8) == 0)
 				delimit = 1;
@@ -943,7 +946,6 @@
 
 		tmp = *arg;
 		tmp.asn = arg->asn;
-		tmp.mod = extract->module;
 		tmp.expr = extract;
 
 		ret = arg->default_cb(&tmp);
@@ -975,10 +977,7 @@
 		 * as it may recursively include the current structure.
 		 */
 		if(expr->marker.flags & (EM_INDIRECT | EM_UNRECURSE)) {
-			asn1p_expr_t *terminal;
-			terminal = asn1f_find_terminal_type_ex(arg->asn, expr);
-			if(terminal
-			&& (terminal->expr_type & ASN_CONSTR_MASK)) {
+			if(terminal_structable(arg, expr)) {
 				tnfmt = TNF_RSAFE;
 				REDIR(OT_FWD_DECLS);
 				OUT("%s;\n",
@@ -1462,10 +1461,9 @@
 	if(arg->expr->expr_type == A1TC_REFERENCE) {
 		arg_t tmp = *arg;
 		asn1p_expr_t *expr;
-		expr = asn1f_lookup_symbol_ex(tmp.asn, tmp.mod, tmp.expr,
+		expr = asn1f_lookup_symbol_ex(tmp.asn, tmp.expr,
 			arg->expr->reference);
 		if(expr) {
-			tmp.mod = expr->module;
 			tmp.expr = expr;
 			return _add_tag2el_member(&tmp, tag2el, count, el_no, flags);
 		} else {
@@ -1613,106 +1611,6 @@
 }
 
 static int
-emit_include_dependencies(arg_t *arg) {
-	asn1p_expr_t *expr = arg->expr;
-	asn1p_expr_t *memb;
-
-	TQ_FOR(memb, &(expr->members), next) {
-
-		/* Avoid recursive definitions. */
-		expr_break_recursion(arg, memb);
-
-		if(memb->marker.flags & (EM_INDIRECT | EM_UNRECURSE)) {
-			asn1p_expr_t *terminal;
-			terminal = asn1f_find_terminal_type_ex(arg->asn, memb);
-			if(terminal
-			&& !terminal->parent_expr
-			&& (terminal->expr_type & ASN_CONSTR_MASK)) {
-				int saved_target = arg->target->target;
-				REDIR(OT_FWD_DECLS);
-				OUT("%s;\n",
-					asn1c_type_name(arg, memb, TNF_RSAFE));
-				REDIR(saved_target);
-			}
-		}
-
-		if((!(memb->expr_type & ASN_CONSTR_MASK)
-			&& memb->expr_type > ASN_CONSTR_MASK)
-		|| memb->meta_type == AMT_TYPEREF) {
-			if(memb->marker.flags & EM_UNRECURSE) {
-				GEN_POSTINCLUDE(asn1c_type_name(arg,
-					memb, TNF_INCLUDE));
-			} else {
-				GEN_INCLUDE(asn1c_type_name(arg,
-					memb, TNF_INCLUDE));
-			}
-		}
-	}
-
-	return 0;
-}
-
-/*
- * Check if it is better to make this type indirectly accessed via
- * a pointer.
- * This may be the case for the following recursive definition:
- * Type ::= CHOICE { member Type };
- */
-static int
-expr_break_recursion(arg_t *arg, asn1p_expr_t *expr) {
-	asn1p_expr_t *top_parent;
-	asn1p_expr_t *terminal;
-
-	if(expr->marker.flags & EM_UNRECURSE)
-		return 1;	/* Already broken */
-
-	terminal = asn1f_find_terminal_type_ex(arg->asn, expr);
-
-	/* -findirect-choice compiles members of CHOICE as indirect pointers */
-	if((arg->flags & A1C_INDIRECT_CHOICE)
-	 && arg->expr->expr_type == ASN_CONSTR_CHOICE
-	 && terminal
-	 && (terminal->expr_type & ASN_CONSTR_MASK)
-	) {
-		/* Break cross-reference */
-		expr->marker.flags |= EM_INDIRECT | EM_UNRECURSE;
-		return 1;
-	}
-
-	if((expr->marker.flags & EM_INDIRECT)
-	|| arg->expr->expr_type == ASN_CONSTR_SET_OF
-	|| arg->expr->expr_type == ASN_CONSTR_SEQUENCE_OF) {
-		if(terminal
-		&& !terminal->parent_expr
-		&& (terminal->expr_type & ASN_CONSTR_MASK)) {
-			expr->marker.flags |= EM_UNRECURSE;
-
-			if(arg->expr->expr_type == ASN_CONSTR_SET_OF
-			|| arg->expr->expr_type == ASN_CONSTR_SEQUENCE_OF) {
-				/* Don't put EM_INDIRECT even if recursion */
-				return 1;
-			}
-
-			/* Fall through */
-		}
-	}
-
-	/* Look for recursive back-references */
-	top_parent = expr->parent_expr;
-	if(top_parent) {
-		while(top_parent->parent_expr)
-			top_parent = top_parent->parent_expr;
-		if(top_parent == terminal) {
-			/* Explicitly break the recursion */
-			expr->marker.flags |= EM_INDIRECT | EM_UNRECURSE;
-			return 1;
-		}
-	}
-
-	return 0;
-}
-
-static int
 emit_member_table(arg_t *arg, asn1p_expr_t *expr) {
 	int save_target;
 	arg_t tmp_arg;
@@ -1734,10 +1632,11 @@
 		OUT("ATF_OPEN_TYPE | ");
 	OUT("%s, ",
 		(expr->marker.flags & EM_INDIRECT)?"ATF_POINTER":"ATF_NOFLAGS");
-	if((expr->marker.flags & EM_OPTIONAL) == EM_OPTIONAL) {
+	if((expr->marker.flags & EM_OMITABLE) == EM_OMITABLE) {
 		asn1p_expr_t *tv;
 		int opts = 0;
-		for(tv = expr; tv && tv->marker.flags;
+		for(tv = expr;
+			tv && (tv->marker.flags & EM_OMITABLE) == EM_OMITABLE;
 			tv = TQ_NEXT(tv, next), opts++) {
 			if(tv->expr_type == A1TC_EXTENSIBLE)
 				opts--;
@@ -2018,3 +1917,178 @@
 
 	return 0;
 }
+
+static int
+emit_include_dependencies(arg_t *arg) {
+	asn1p_expr_t *expr = arg->expr;
+	asn1p_expr_t *memb;
+
+	/* Avoid recursive definitions. */
+	TQ_FOR(memb, &(expr->members), next) {
+		expr_break_recursion(arg, memb);
+	}
+
+	TQ_FOR(memb, &(expr->members), next) {
+
+		if(memb->marker.flags & (EM_INDIRECT | EM_UNRECURSE)) {
+			if(terminal_structable(arg, memb)) {
+				int saved_target = arg->target->target;
+				REDIR(OT_FWD_DECLS);
+				OUT("%s;\n",
+					asn1c_type_name(arg, memb, TNF_RSAFE));
+				REDIR(saved_target);
+			}
+		}
+
+		if((!(memb->expr_type & ASN_CONSTR_MASK)
+			&& memb->expr_type > ASN_CONSTR_MASK)
+		|| memb->meta_type == AMT_TYPEREF) {
+			if(memb->marker.flags & EM_UNRECURSE) {
+				GEN_POSTINCLUDE(asn1c_type_name(arg,
+					memb, TNF_INCLUDE));
+			} else {
+				GEN_INCLUDE(asn1c_type_name(arg,
+					memb, TNF_INCLUDE));
+			}
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Check if it is better to make this type indirectly accessed via
+ * a pointer.
+ * This may be the case for the following recursive definition:
+ * Type ::= CHOICE { member Type };
+ */
+static int
+expr_break_recursion(arg_t *arg, asn1p_expr_t *expr) {
+	asn1p_expr_t *terminal;
+	int ret;
+
+	if(expr->marker.flags & EM_UNRECURSE)
+		return 1;	/* Already broken */
+
+	terminal = asn1f_find_terminal_type_ex(arg->asn, expr);
+
+	/* -findirect-choice compiles members of CHOICE as indirect pointers */
+	if((arg->flags & A1C_INDIRECT_CHOICE)
+	 && arg->expr->expr_type == ASN_CONSTR_CHOICE
+	 && terminal
+	 && (terminal->expr_type & ASN_CONSTR_MASK)
+	) {
+		/* Break cross-reference */
+		expr->marker.flags |= EM_INDIRECT | EM_UNRECURSE;
+		return 1;
+	}
+
+	if((expr->marker.flags & EM_INDIRECT)
+	|| arg->expr->expr_type == ASN_CONSTR_SET_OF
+	|| arg->expr->expr_type == ASN_CONSTR_SEQUENCE_OF) {
+		if(terminal_structable(arg, expr)) {
+			expr->marker.flags |= EM_UNRECURSE;
+
+			if(arg->expr->expr_type == ASN_CONSTR_SET_OF
+			|| arg->expr->expr_type == ASN_CONSTR_SEQUENCE_OF) {
+				/* Don't put EM_INDIRECT even if recursion */
+				return 1;
+			}
+
+			/* Fall through */
+		}
+	}
+
+	/* Look for recursive back-references */
+	ret = expr_defined_recursively(arg, expr);
+	switch(ret) {
+	case 2: /* Explicitly break the recursion */
+	case 1: /* Use safer typing */
+		expr->marker.flags |= EM_INDIRECT;
+		expr->marker.flags |= EM_UNRECURSE;
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * Check if the type can be represented using simple `struct TYPE`.
+ */
+static asn1p_expr_t *
+terminal_structable(arg_t *arg, asn1p_expr_t *expr) {
+	asn1p_expr_t *terminal = asn1f_find_terminal_type_ex(arg->asn, expr);
+	if(terminal
+	&& !terminal->parent_expr
+	&& (terminal->expr_type & ASN_CONSTR_MASK)) {
+		return terminal;
+	}
+	return 0;
+}
+
+static int
+asn1c_recurse(arg_t *arg, asn1p_expr_t *expr, int (*callback)(arg_t *arg, void *key), void *key) {
+	arg_t tmp = *arg;
+	int maxret = 0;
+	int ret;
+
+	if(expr->_mark) return 0;
+	expr->_mark |= TM_RECURSION;
+
+	/* Invoke callback for every type going into recursion */
+	tmp.expr = expr;
+	maxret = callback(&tmp, key);
+	if(maxret <= 1) {
+		/*
+		 * Recursively invoke myself and the callbacks.
+		 */
+		TQ_FOR(tmp.expr, &(expr->members), next) {
+			ret = asn1c_recurse(&tmp, tmp.expr, callback, key);
+			if(ret > maxret)
+				maxret = ret;
+			if(maxret > 1) break;
+		}
+	}
+
+	expr->_mark &= ~TM_RECURSION;
+	return maxret;
+}
+
+static int
+check_is_refer_to(arg_t *arg, void *key) {
+	asn1p_expr_t *terminal = terminal_structable(arg, arg->expr);
+	if(terminal == key) {
+		if(arg->expr->marker.flags & EM_INDIRECT)
+			return 1; /* This is almost safe indirection */
+		return 2;
+	} else if(terminal) {
+		/* This might be N-step circular loop. Dive into it. */
+		return asn1c_recurse(arg, terminal, check_is_refer_to, key);
+	}
+	return 0;
+}
+
+/*
+ * Check if the possibly inner expression defined recursively.
+ */
+static int
+expr_defined_recursively(arg_t *arg, asn1p_expr_t *expr) {
+	asn1p_expr_t *terminal;
+	asn1p_expr_t *topmost;
+
+	/* If expression is top-level, there's no way it can be recursive. */
+	if(expr->parent_expr == 0) return 0;
+	if(expr->expr_type != A1TC_REFERENCE)
+		return 0;	/* Basic types are never recursive */
+
+	terminal = terminal_structable(arg, expr);
+	if(!terminal) return 0;	/* Terminal cannot be indirected */
+
+	/* Search for the parent container for the given expression */
+	topmost = expr;
+	while(topmost->parent_expr)
+		topmost = topmost->parent_expr;
+
+	/* Look inside the terminal type if it mentions the parent expression */
+	return asn1c_recurse(arg, terminal, check_is_refer_to, topmost);
+}
diff --git a/libasn1compiler/asn1c_constraint.c b/libasn1compiler/asn1c_constraint.c
index 372730f..8af5e36 100644
--- a/libasn1compiler/asn1c_constraint.c
+++ b/libasn1compiler/asn1c_constraint.c
@@ -609,13 +609,13 @@
 	default:
 		WARNING("%s:%d: Value cannot be determined "
 			"for constraint check for %s",
-			arg->mod->source_file_name,
+			arg->expr->module->source_file_name,
 			arg->expr->_lineno,
 			arg->expr->Identifier
 		);
 		OUT_NOINDENT(
 			"#error %s:%d: Value of %s cannot be determined\n",
-			arg->mod->source_file_name,
+			arg->expr->module->source_file_name,
 			arg->expr->_lineno,
 			arg->expr->Identifier
 		);
diff --git a/libasn1compiler/asn1c_internal.h b/libasn1compiler/asn1c_internal.h
index e32187f..fdf953d 100644
--- a/libasn1compiler/asn1c_internal.h
+++ b/libasn1compiler/asn1c_internal.h
@@ -51,7 +51,7 @@
 	struct compiler_streams *target;
 
 	asn1p_t		*asn;
-	asn1p_module_t	*mod;
+	//asn1p_module_t	*mod;
 	asn1p_expr_t	*expr;
 
 	int embed;
diff --git a/libasn1compiler/asn1c_misc.c b/libasn1compiler/asn1c_misc.c
index 7483a20..779426a 100644
--- a/libasn1compiler/asn1c_misc.c
+++ b/libasn1compiler/asn1c_misc.c
@@ -142,11 +142,10 @@
 			 * This is a reference to a type defined in a class.
 			 * Resolve it and use instead.
 			 */
-			tmp.expr = asn1f_class_access_ex(arg->asn, arg->mod,
-				arg->expr, expr->reference);
+			tmp.expr = asn1f_class_access_ex(arg->asn,
+				arg->expr->module, arg->expr, expr->reference);
 			if(tmp.expr) return NULL;
 
-			tmp.mod = tmp.expr->module;
 			return asn1c_type_name(&tmp, tmp.expr, _format);
 		}
 
diff --git a/libasn1compiler/asn1c_save.c b/libasn1compiler/asn1c_save.c
index 0ab8156..0eb70c9 100644
--- a/libasn1compiler/asn1c_save.c
+++ b/libasn1compiler/asn1c_save.c
@@ -17,6 +17,7 @@
 		int argc, char **argv) {
 	asn1c_fdeps_t *deps = 0;
 	asn1c_fdeps_t *dlist;
+	asn1p_module_t *mod;
 	FILE *mkf;
 	int i;
 
@@ -26,8 +27,8 @@
 			"from %s\n", datadir);
 	}
 
-	TQ_FOR(arg->mod, &(arg->asn->modules), mod_next) {
-		TQ_FOR(arg->expr, &(arg->mod->members), next) {
+	TQ_FOR(mod, &(arg->asn->modules), mod_next) {
+		TQ_FOR(arg->expr, &(mod->members), next) {
 			if(asn1_lang_map[arg->expr->meta_type]
 				[arg->expr->expr_type].type_cb) {
 				if(asn1c_dump_streams(arg, deps))
@@ -51,8 +52,8 @@
 	}
 
 	fprintf(mkf, "ASN_MODULE_SOURCES=");
-	TQ_FOR(arg->mod, &(arg->asn->modules), mod_next) {
-		TQ_FOR(arg->expr, &(arg->mod->members), next) {
+	TQ_FOR(mod, &(arg->asn->modules), mod_next) {
+		TQ_FOR(arg->expr, &(mod->members), next) {
 			if(asn1_lang_map[arg->expr->meta_type]
 				[arg->expr->expr_type].type_cb) {
 				fprintf(mkf, "\t\\\n\t%s.c",
@@ -61,8 +62,8 @@
 		}
 	}
 	fprintf(mkf, "\n\nASN_MODULE_HEADERS=");
-	TQ_FOR(arg->mod, &(arg->asn->modules), mod_next) {
-		TQ_FOR(arg->expr, &(arg->mod->members), next) {
+	TQ_FOR(mod, &(arg->asn->modules), mod_next) {
+		TQ_FOR(arg->expr, &(mod->members), next) {
 			if(asn1_lang_map[arg->expr->meta_type]
 				[arg->expr->expr_type].type_cb) {
 				fprintf(mkf, "\t\\\n\t%s.h",
@@ -208,8 +209,8 @@
 	" * From ASN.1 module \"%s\"\n"
 	" * \tfound in \"%s\"\n"
 	" */\n\n",
-		arg->mod->ModuleName,
-		arg->mod->source_file_name
+		expr->module->ModuleName,
+		expr->module->source_file_name
 	);
 	fprintf(fp_h,
 	"/*\n"
@@ -217,8 +218,8 @@
 	" * From ASN.1 module \"%s\"\n"
 	" * \tfound in \"%s\"\n"
 	" */\n\n",
-		arg->mod->ModuleName,
-		arg->mod->source_file_name
+		expr->module->ModuleName,
+		expr->module->source_file_name
 	);
 
 	header_id = asn1c_make_identifier(0, expr->Identifier, NULL);
diff --git a/libasn1compiler/asn1compiler.c b/libasn1compiler/asn1compiler.c
index 31a4227..e8e6739 100644
--- a/libasn1compiler/asn1compiler.c
+++ b/libasn1compiler/asn1compiler.c
@@ -12,6 +12,7 @@
 		int argc, char **argv) {
 	arg_t arg_s;
 	arg_t *arg = &arg_s;
+	asn1p_module_t *mod;
 	int ret;
 
 	/*
@@ -29,8 +30,8 @@
 	/*
 	 * Compile each individual top level structure.
 	 */
-	TQ_FOR(arg->mod, &(asn->modules), mod_next) {
-		TQ_FOR(arg->expr, &(arg->mod->members), next) {
+	TQ_FOR(mod, &(asn->modules), mod_next) {
+		TQ_FOR(arg->expr, &(mod->members), next) {
 			compiler_streams_t *cs = NULL;
 
 			if(asn1c_attach_streams(arg->expr))