aboutsummaryrefslogtreecommitdiffstats
path: root/pbx/pbx_ael.c
diff options
context:
space:
mode:
authormurf <murf@f38db490-d61c-443f-a65b-d21fe96a405b>2006-09-27 03:45:22 +0000
committermurf <murf@f38db490-d61c-443f-a65b-d21fe96a405b>2006-09-27 03:45:22 +0000
commit52da543985c8469e7a2e00413686a6a478dfd63a (patch)
treeaf3705edc5f573446d149d9cdcb3d4b8f0825f44 /pbx/pbx_ael.c
parent5394044aeb7ac9c86c4546fab88c301e338041c8 (diff)
This commits the changes to AEL to use the gosub-with-args from Tilghman to perform macro calls. This results in substantially smaller stack footprint, which allows macro call depths in excess of 100,000 levels, rather than the limit of 7 calls deep, which the Macro app is subject to.
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@43747 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'pbx/pbx_ael.c')
-rw-r--r--pbx/pbx_ael.c234
1 files changed, 171 insertions, 63 deletions
diff --git a/pbx/pbx_ael.c b/pbx/pbx_ael.c
index 1594c82b3..36f65945f 100644
--- a/pbx/pbx_ael.c
+++ b/pbx/pbx_ael.c
@@ -131,6 +131,10 @@ static int extension_matches(pval *here, const char *exten, const char *pattern)
static void check_goto(pval *item);
static void find_pval_goto_item(pval *item, int lev);
static void find_pval_gotos(pval *item, int lev);
+static int check_break(pval *item);
+static int check_continue(pval *item);
+static void check_label(pval *item);
+static void check_macro_returns(pval *macro);
static struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont);
static struct pval *find_first_label_in_current_context(char *label, pval *curr_cont);
@@ -681,6 +685,49 @@ void traverse_pval_template(pval *item, int depth) /* depth comes in handy for a
/* (not all that is syntactically legal is good! */
+static void check_macro_returns(pval *macro)
+{
+ pval *i;
+ if (!macro->u3.macro_statements)
+ {
+ pval *z = calloc(1, sizeof(struct pval));
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The macro %s is empty! I will insert a return.\n",
+ macro->filename, macro->startline, macro->endline, macro->u1.str);
+
+ z->type = PV_RETURN;
+ z->startline = macro->startline;
+ z->endline = macro->endline;
+ z->startcol = macro->startcol;
+ z->endcol = macro->endcol;
+ z->filename = strdup(macro->filename);
+
+ macro->u3.macro_statements = z;
+ return;
+ }
+ for (i=macro->u3.macro_statements; i; i=i->next) {
+ /* if the last statement in the list is not return, then insert a return there */
+ if (i->next == NULL) {
+ if (i->type != PV_RETURN) {
+ pval *z = calloc(1, sizeof(struct pval));
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The macro %s does not end with a return; I will insert one.\n",
+ macro->filename, macro->startline, macro->endline, macro->u1.str);
+
+ z->type = PV_RETURN;
+ z->startline = macro->startline;
+ z->endline = macro->endline;
+ z->startcol = macro->startcol;
+ z->endcol = macro->endcol;
+ z->filename = strdup(macro->filename);
+
+ i->next = z;
+ return;
+ }
+ }
+ }
+ return;
+}
+
+
static int extension_matches(pval *here, const char *exten, const char *pattern)
{
@@ -688,10 +735,10 @@ static int extension_matches(pval *here, const char *exten, const char *pattern)
regex_t preg;
/* simple case, they match exactly, the pattern and exten name */
- if( !strcmp(pattern,exten) == 0 )
+ if (!strcmp(pattern,exten) == 0)
return 1;
- if ( pattern[0] == '_' ) {
+ if (pattern[0] == '_') {
char reg1[2000];
const char *p;
char *r = reg1;
@@ -1048,16 +1095,64 @@ static int check_continue(pval *item)
return 0;
}
+static struct pval *in_macro(pval *item)
+{
+ struct pval *curr;
+ curr = item;
+ while( curr ) {
+ if( curr->type == PV_MACRO ) {
+ return curr;
+ }
+ curr = curr->dad;
+ }
+ return 0;
+}
+
+static struct pval *in_context(pval *item)
+{
+ struct pval *curr;
+ curr = item;
+ while( curr ) {
+ if( curr->type == PV_MACRO || curr->type == PV_CONTEXT ) {
+ return curr;
+ }
+ curr = curr->dad;
+ }
+ return 0;
+}
+
/* general purpose goto finder */
static void check_label(pval *item)
{
+ struct pval *curr;
+ struct pval *x;
+ int alright = 0;
+
+ /* A label outside an extension just plain does not make sense! */
+
+ curr = item;
+
+ while( curr ) {
+ if( curr->type == PV_MACRO || curr->type == PV_EXTENSION ) {
+ alright = 1;
+ break;
+ }
+ curr = curr->dad;
+ }
+ if( !alright )
+ {
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: Label %s is not within an extension or macro!\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ errs++;
+ }
+
+
/* basically, ensure that a label is not repeated in a context. Period.
The method: well, for each label, find the first label in the context
with the same name. If it's not the current label, then throw an error. */
- struct pval *curr;
- struct pval *x;
+
/* printf("==== check_label: ====\n"); */
if( !current_extension )
@@ -1231,12 +1326,34 @@ static void check_goto(pval *item)
ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s|%s exists in the context %s or its inclusions!\n",
item->filename, item->startline, item->endline, item->u1.list->next->u1.str, item->u1.list->next->next->u1.str, item->u1.list->u1.str );
errs++;
+ } else {
+ struct pval *mac = in_macro(item); /* is this goto inside a macro? */
+ if( mac ) { /* yes! */
+ struct pval *targ = in_context(found);
+ if( mac != targ )
+ {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: It's bad form to have a goto in a macro to a target outside the macro!\n",
+ item->filename, item->startline, item->endline);
+ warns++;
+ }
+ }
}
} else {
ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no context %s could be found that matches the goto target!\n",
item->filename, item->startline, item->endline, item->u1.list->u1.str);
errs++;
}
+ } else {
+ struct pval *mac = in_macro(item); /* is this goto inside a macro? */
+ if( mac ) { /* yes! */
+ struct pval *targ = in_context(x);
+ if( mac != targ )
+ {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: It's bad form to have a goto in a macro to a target outside the macro!\n",
+ item->filename, item->startline, item->endline);
+ warns++;
+ }
+ }
}
}
}
@@ -2188,7 +2305,7 @@ void check_pval_item(pval *item, struct argapp *apps, int in_globals)
#endif
struct pval *macro_def;
struct pval *app_def;
-
+
char errmsg[4096];
char *strp;
@@ -2209,6 +2326,9 @@ void check_pval_item(pval *item, struct argapp *apps, int in_globals)
in_abstract_context = 0;
current_context = item;
current_extension = 0;
+
+ check_macro_returns(item);
+
for (lp=item->u2.arglist; lp; lp=lp->next) {
}
@@ -2282,11 +2402,32 @@ void check_pval_item(pval *item, struct argapp *apps, int in_globals)
|| strcasecmp(item->u1.str,"while") == 0
|| strcasecmp(item->u1.str,"endwhile") == 0
|| strcasecmp(item->u1.str,"random") == 0
+ || strcasecmp(item->u1.str,"gosub") == 0
+ || strcasecmp(item->u1.str,"return") == 0
+ || strcasecmp(item->u1.str,"gosubif") == 0
+ || strcasecmp(item->u1.str,"continuewhile") == 0
+ || strcasecmp(item->u1.str,"endwhile") == 0
+ || strcasecmp(item->u1.str,"execif") == 0
+ || strcasecmp(item->u1.str,"execiftime") == 0
+ || strcasecmp(item->u1.str,"exitwhile") == 0
+ || strcasecmp(item->u1.str,"goto") == 0
+ || strcasecmp(item->u1.str,"macro") == 0
+ || strcasecmp(item->u1.str,"macroexclusive") == 0
+ || strcasecmp(item->u1.str,"macroif") == 0
+ || strcasecmp(item->u1.str,"stackpop") == 0
|| strcasecmp(item->u1.str,"execIf") == 0 ) {
- ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s needs to be re-written using AEL if, while, goto, etc. keywords instead!\n",
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!\n",
item->filename, item->startline, item->endline, item->u1.str);
warns++;
}
+ if (strcasecmp(item->u1.str,"macroexit") == 0) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: I am converting the MacroExit call here to a return statement.\n",
+ item->filename, item->startline, item->endline);
+ item->type = PV_RETURN;
+ free(item->u1.str);
+ item->u1.str = 0;
+ }
+
#ifdef AAL_ARGCHECK
found = 0;
for (app=apps; app; app=app->next) {
@@ -2775,6 +2916,7 @@ static void gen_prios(struct ael_extension *exten, char *label, pval *statement,
char new_label[2000];
int default_exists;
int local_control_statement_count;
+ int first;
struct ael_priority *loop_break_save;
struct ael_priority *loop_continue_save;
struct ael_extension *switch_case;
@@ -3022,7 +3164,7 @@ static void gen_prios(struct ael_extension *exten, char *label, pval *statement,
linkprio(switch_case, fall_thru);
}
}
- if (switch_case->return_needed) {
+ if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
char buf[2000];
struct ael_priority *np2 = new_prio();
np2->type = AEL_APPCALL;
@@ -3083,7 +3225,7 @@ static void gen_prios(struct ael_extension *exten, char *label, pval *statement,
linkprio(switch_case, fall_thru);
}
}
- if (switch_case->return_needed) {
+ if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
char buf[2000];
struct ael_priority *np2 = new_prio();
np2->type = AEL_APPCALL;
@@ -3146,7 +3288,7 @@ static void gen_prios(struct ael_extension *exten, char *label, pval *statement,
linkprio(switch_case, fall_thru);
}
}
- if (switch_case->return_needed) {
+ if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
char buf[2000];
struct ael_priority *np2 = new_prio();
np2->type = AEL_APPCALL;
@@ -3170,12 +3312,19 @@ static void gen_prios(struct ael_extension *exten, char *label, pval *statement,
case PV_MACRO_CALL:
pr = new_prio();
pr->type = AEL_APPCALL;
- snprintf(buf1,sizeof(buf1),"%s", p->u1.str);
+ snprintf(buf1,sizeof(buf1),"%s|s|1", p->u1.str);
+ first = 1;
for (p2 = p->u2.arglist; p2; p2 = p2->next) {
- strcat(buf1,"|");
+ if( first )
+ {
+ strcat(buf1,":");
+ first = 0;
+ }
+ else
+ strcat(buf1,"|");
strcat(buf1,p2->u1.str);
}
- pr->app = strdup("Macro");
+ pr->app = strdup("Gosub");
pr->appargs = strdup(buf1);
pr->origin = p;
linkprio(exten, pr);
@@ -3206,9 +3355,9 @@ static void gen_prios(struct ael_extension *exten, char *label, pval *statement,
case PV_RETURN: /* hmmmm */
pr = new_prio();
- pr->type = AEL_RETURN; /* simple goto */
- exten->return_needed++;
- pr->app = strdup("Goto");
+ pr->type = AEL_RETURN; /* simple Return */
+ /* exten->return_needed++; */
+ pr->app = strdup("Return");
pr->appargs = strdup("");
pr->origin = p;
linkprio(exten, pr);
@@ -3222,46 +3371,6 @@ static void gen_prios(struct ael_extension *exten, char *label, pval *statement,
linkprio(exten, pr);
break;
-#ifdef OLD_RAND_ACTION
- case PV_RANDOM:
- control_statement_count++;
- snprintf(new_label,sizeof(new_label),"rand-%s-%d", label, control_statement_count);
- rand_test = new_prio();
- rand_test->type = AEL_RAND_CONTROL;
- snprintf(buf1,sizeof(buf1),"$[%s]",
- p->u1.str );
- rand_test->app = 0;
- rand_test->appargs = strdup(buf1);
- rand_test->origin = p;
-
- rand_end = new_prio();
- rand_end->type = AEL_APPCALL;
- snprintf(buf1,sizeof(buf1),"Finish rand-%s-%d", label, control_statement_count);
- rand_end->app = strdup("NoOp");
- rand_end->appargs = strdup(buf1);
-
- rand_skip = new_prio();
- rand_skip->type = AEL_CONTROL1; /* simple goto */
- rand_skip->goto_true = rand_end;
- rand_skip->origin = p;
-
- rand_test->goto_true = rand_skip; /* +1, really */
-
- linkprio(exten, rand_test);
-
- if (p->u3.else_statements) {
- gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the else statements here */
- }
-
- linkprio(exten, rand_skip);
-
- gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the "true" statements here */
-
- linkprio(exten, rand_end);
-
- break;
-#endif
-
case PV_IFTIME:
control_statement_count++;
snprintf(new_label,sizeof(new_label),"iftime-%s-%d", label, control_statement_count);
@@ -3383,7 +3492,7 @@ static void gen_prios(struct ael_extension *exten, char *label, pval *statement,
snprintf(new_label,sizeof(new_label),"catch-%s-%d",p->u1.str, control_statement_count);
gen_prios(switch_case, new_label, p->u2.statements,mother_exten,this_context); /* this will link in all the catch body statements here */
- if (switch_case->return_needed) {
+ if (switch_case->return_needed) { /* returns now generate a Return() app call, no longer a goto to the end of the exten */
char buf[2000];
struct ael_priority *np2 = new_prio();
np2->type = AEL_APPCALL;
@@ -3500,8 +3609,8 @@ void add_extensions(struct ael_extension *exten)
break;
case AEL_RETURN:
- strcpy(app,"Goto");
- snprintf(appargs,sizeof(appargs), "%d", exten->return_target->priority_num);
+ strcpy(app,"Return");
+ appargs[0] = 0;
break;
default:
@@ -3619,9 +3728,8 @@ void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root)
switch (p->type) {
case PV_MACRO:
- strcpy(buf,"macro-");
- strcat(buf,p->u1.str);
- context = ast_context_create(local_contexts, buf, registrar);
+
+ context = ast_context_create(local_contexts, p->u1.str, registrar);
exten = new_exten();
exten->context = context;
@@ -3640,7 +3748,7 @@ void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root)
/* CONTAINS APPCALLS, CATCH, just like extensions... */
gen_prios(exten, p->u1.str, p->u3.macro_statements, 0, context );
- if (exten->return_needed) {
+ if (exten->return_needed) { /* most likely, this will go away */
struct ael_priority *np2 = new_prio();
np2->type = AEL_APPCALL;
np2->app = strdup("NoOp");
@@ -3687,7 +3795,7 @@ void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root)
exten->hints = strdup(p2->u3.hints);
exten->regexten = p2->u4.regexten;
gen_prios(exten, p->u1.str, p2->u2.statements, 0, context );
- if (exten->return_needed) {
+ if (exten->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
struct ael_priority *np2 = new_prio();
np2->type = AEL_APPCALL;
np2->app = strdup("NoOp");