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: https://origsvn.digium.com/svn/asterisk/trunk@43747 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Steve Murphy
2006-09-27 03:45:22 +00:00
parent 2b7debf368
commit 35b951d2ac
14 changed files with 785 additions and 451 deletions

View File

@@ -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");