mirror of
https://github.com/asterisk/asterisk.git
synced 2025-11-15 22:38:08 +00:00
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:
234
pbx/pbx_ael.c
234
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");
|
||||
|
||||
Reference in New Issue
Block a user