Refactor block_t storage in parser_t from a linked list to a vector

This commit is contained in:
ridiculousfish 2013-12-20 17:41:21 -08:00
parent 12be83562d
commit e38217683c
7 changed files with 218 additions and 187 deletions

View file

@ -831,7 +831,8 @@ static int builtin_block(parser_t &parser, wchar_t **argv)
} }
else else
{ {
block_t *block=parser.current_block; size_t block_idx = 0;
block_t *block = parser.block_at_index(block_idx);
event_blockage_t eb = {}; event_blockage_t eb = {};
eb.typemask = type; eb.typemask = type;
@ -840,20 +841,24 @@ static int builtin_block(parser_t &parser, wchar_t **argv)
{ {
case LOCAL: case LOCAL:
{ {
if (!block->outer) // If this is the outermost block, then we're global
block=0; if (block_idx + 1 >= parser.block_count())
{
block = NULL;
}
break; break;
} }
case GLOBAL: case GLOBAL:
{ {
block=0; block=NULL;
} }
case UNSET: case UNSET:
{ {
while (block && while (block != NULL && block->type() != FUNCTION_CALL && block->type() != FUNCTION_CALL_NO_SHADOW)
block->type() != FUNCTION_CALL && {
block->type() != FUNCTION_CALL_NO_SHADOW) // Set it in function scope
block = block->outer; block = parser.block_at_index(++block_idx);
}
} }
} }
if (block) if (block)
@ -1863,18 +1868,21 @@ static int builtin_function(parser_t &parser, wchar_t **argv)
if (is_subshell) if (is_subshell)
{ {
block_t *b = parser.current_block; size_t block_idx = 0;
while (b && (b->type() != SUBST)) /* Find the outermost substitution block */
b = b->outer; for (block_idx = 0; ; block_idx++)
if (b)
{ {
b=b->outer; const block_t *b = parser.block_at_index(block_idx);
if (b == NULL || b->type() == SUBST)
break;
} }
if (b->job)
/* Go one step beyond that, to get to the caller */
const block_t *caller_block = parser.block_at_index(block_idx + 1);
if (caller_block != NULL && caller_block->job != NULL)
{ {
job_id = b->job->job_id; job_id = caller_block->job->job_id;
} }
} }
@ -2058,8 +2066,8 @@ static int builtin_function(parser_t &parser, wchar_t **argv)
} }
} }
parser.current_block->tok_pos = parser.get_pos(); parser.current_block()->tok_pos = parser.get_pos();
parser.current_block->skip = 1; parser.current_block()->skip = 1;
return STATUS_BUILTIN_OK; return STATUS_BUILTIN_OK;
} }
@ -2712,7 +2720,7 @@ static int builtin_status(parser_t &parser, wchar_t **argv)
case STACK_TRACE: case STACK_TRACE:
{ {
parser.stack_trace(parser.current_block, stdout_buffer); parser.stack_trace(0, stdout_buffer);
break; break;
} }
@ -2727,7 +2735,7 @@ static int builtin_status(parser_t &parser, wchar_t **argv)
job_control_mode==JOB_CONTROL_INTERACTIVE?_(L"Only on interactive jobs"): job_control_mode==JOB_CONTROL_INTERACTIVE?_(L"Only on interactive jobs"):
(job_control_mode==JOB_CONTROL_NONE ? _(L"Never") : _(L"Always"))); (job_control_mode==JOB_CONTROL_NONE ? _(L"Never") : _(L"Always")));
parser.stack_trace(parser.current_block, stdout_buffer); parser.stack_trace(0, stdout_buffer);
break; break;
} }
} }
@ -3413,19 +3421,19 @@ static int builtin_for(parser_t &parser, wchar_t **argv)
} }
else else
{ {
parser.current_block->skip=1; parser.current_block()->skip=1;
} }
} }
return res; return res;
} }
/** /**
The begin builtin. Creates a nex block. The begin builtin. Creates a new block.
*/ */
static int builtin_begin(parser_t &parser, wchar_t **argv) static int builtin_begin(parser_t &parser, wchar_t **argv)
{ {
parser.push_block(new scope_block_t(BEGIN)); parser.push_block(new scope_block_t(BEGIN));
parser.current_block->tok_pos = parser.get_pos(); parser.current_block()->tok_pos = parser.get_pos();
return proc_get_last_status(); return proc_get_last_status();
} }
@ -3437,7 +3445,7 @@ static int builtin_begin(parser_t &parser, wchar_t **argv)
*/ */
static int builtin_end(parser_t &parser, wchar_t **argv) static int builtin_end(parser_t &parser, wchar_t **argv)
{ {
if (!parser.current_block->outer) if (! parser.block_at_index(1))
{ {
append_format(stderr_buffer, append_format(stderr_buffer,
_(L"%ls: Not inside of block\n"), _(L"%ls: Not inside of block\n"),
@ -3455,7 +3463,8 @@ static int builtin_end(parser_t &parser, wchar_t **argv)
*/ */
bool kill_block = true; bool kill_block = true;
switch (parser.current_block->type()) block_t * const current_block = parser.current_block();
switch (current_block->type())
{ {
case WHILE: case WHILE:
{ {
@ -3463,13 +3472,13 @@ static int builtin_end(parser_t &parser, wchar_t **argv)
If this is a while loop, we rewind the loop unless If this is a while loop, we rewind the loop unless
it's the last lap, in which case we continue. it's the last lap, in which case we continue.
*/ */
if (!(parser.current_block->skip && (parser.current_block->loop_status != LOOP_CONTINUE))) if (!(current_block->skip && (current_block->loop_status != LOOP_CONTINUE)))
{ {
parser.current_block->loop_status = LOOP_NORMAL; current_block->loop_status = LOOP_NORMAL;
parser.current_block->skip = 0; current_block->skip = 0;
kill_block = false; kill_block = false;
parser.set_pos(parser.current_block->tok_pos); parser.set_pos(current_block->tok_pos);
while_block_t *blk = static_cast<while_block_t *>(parser.current_block); while_block_t *blk = static_cast<while_block_t *>(current_block);
blk->status = WHILE_TEST_AGAIN; blk->status = WHILE_TEST_AGAIN;
} }
@ -3492,9 +3501,9 @@ static int builtin_end(parser_t &parser, wchar_t **argv)
/* /*
set loop variable to next element, and rewind to the beginning of the block. set loop variable to next element, and rewind to the beginning of the block.
*/ */
for_block_t *fb = static_cast<for_block_t *>(parser.current_block); for_block_t *fb = static_cast<for_block_t *>(current_block);
wcstring_list_t &for_vars = fb->sequence; wcstring_list_t &for_vars = fb->sequence;
if (parser.current_block->loop_status == LOOP_BREAK) if (current_block->loop_status == LOOP_BREAK)
{ {
for_vars.clear(); for_vars.clear();
} }
@ -3505,18 +3514,18 @@ static int builtin_end(parser_t &parser, wchar_t **argv)
for_vars.pop_back(); for_vars.pop_back();
const wcstring &for_variable = fb->variable; const wcstring &for_variable = fb->variable;
env_set(for_variable, val.c_str(), ENV_LOCAL); env_set(for_variable, val.c_str(), ENV_LOCAL);
parser.current_block->loop_status = LOOP_NORMAL; current_block->loop_status = LOOP_NORMAL;
parser.current_block->skip = 0; current_block->skip = 0;
kill_block = false; kill_block = false;
parser.set_pos(parser.current_block->tok_pos); parser.set_pos(current_block->tok_pos);
} }
break; break;
} }
case FUNCTION_DEF: case FUNCTION_DEF:
{ {
function_def_block_t *fdb = static_cast<function_def_block_t *>(parser.current_block); function_def_block_t *fdb = static_cast<function_def_block_t *>(current_block);
function_data_t &d = fdb->function_data; function_data_t &d = fdb->function_data;
if (d.name.empty()) if (d.name.empty())
@ -3535,8 +3544,8 @@ static int builtin_end(parser_t &parser, wchar_t **argv)
for the specified function for the specified function
*/ */
wchar_t *def = wcsndup(parser.get_buffer()+parser.current_block->tok_pos, wchar_t *def = wcsndup(parser.get_buffer()+current_block->tok_pos,
parser.get_job_pos()-parser.current_block->tok_pos); parser.get_job_pos()-current_block->tok_pos);
d.definition = def; d.definition = def;
function_add(d, parser); function_add(d, parser);
@ -3569,9 +3578,9 @@ static int builtin_else(parser_t &parser, wchar_t **argv)
{ {
bool block_ok = false; bool block_ok = false;
if_block_t *if_block = NULL; if_block_t *if_block = NULL;
if (parser.current_block != NULL && parser.current_block->type() == IF) if (parser.current_block() != NULL && parser.current_block()->type() == IF)
{ {
if_block = static_cast<if_block_t *>(parser.current_block); if_block = static_cast<if_block_t *>(parser.current_block());
/* Ensure that we're past IF but not up to an ELSE */ /* Ensure that we're past IF but not up to an ELSE */
if (if_block->if_expr_evaluated && ! if_block->else_evaluated) if (if_block->if_expr_evaluated && ! if_block->else_evaluated)
{ {
@ -3612,7 +3621,6 @@ static int builtin_break_continue(parser_t &parser, wchar_t **argv)
int is_break = (wcscmp(argv[0],L"break")==0); int is_break = (wcscmp(argv[0],L"break")==0);
int argc = builtin_count_args(argv); int argc = builtin_count_args(argv);
block_t *b = parser.current_block;
if (argc != 1) if (argc != 1)
{ {
@ -3625,15 +3633,16 @@ static int builtin_break_continue(parser_t &parser, wchar_t **argv)
return STATUS_BUILTIN_ERROR; return STATUS_BUILTIN_ERROR;
} }
/* Find the index of the enclosing for or while loop. Recall that incrementing loop_idx goes 'up' to outer blocks */
while ((b != 0) && size_t loop_idx;
(b->type() != WHILE) && for (loop_idx = 0; loop_idx < parser.block_count(); loop_idx++)
(b->type() != FOR))
{ {
b = b->outer; const block_t *b = parser.block_at_index(loop_idx);
if (b->type() == WHILE || b->type() == FOR)
break;
} }
if (b == 0) if (loop_idx >= parser.block_count())
{ {
append_format(stderr_buffer, append_format(stderr_buffer,
_(L"%ls: Not inside of loop\n"), _(L"%ls: Not inside of loop\n"),
@ -3642,15 +3651,17 @@ static int builtin_break_continue(parser_t &parser, wchar_t **argv)
return STATUS_BUILTIN_ERROR; return STATUS_BUILTIN_ERROR;
} }
b = parser.current_block; /* Skip blocks interior to the loop */
while ((b->type() != WHILE) && size_t block_idx = loop_idx;
(b->type() != FOR)) while (block_idx--)
{ {
b->skip=1; parser.block_at_index(block_idx)->skip = true;
b = b->outer;
} }
b->skip=1;
b->loop_status = is_break?LOOP_BREAK:LOOP_CONTINUE; /* Skip the loop itself */
block_t *loop_block = parser.block_at_index(loop_idx);
loop_block->skip = true;
loop_block->loop_status = is_break ? LOOP_BREAK : LOOP_CONTINUE;
return STATUS_BUILTIN_OK; return STATUS_BUILTIN_OK;
} }
@ -3679,8 +3690,6 @@ static int builtin_return(parser_t &parser, wchar_t **argv)
int argc = builtin_count_args(argv); int argc = builtin_count_args(argv);
int status = proc_get_last_status(); int status = proc_get_last_status();
block_t *b = parser.current_block;
switch (argc) switch (argc)
{ {
case 1: case 1:
@ -3709,15 +3718,16 @@ static int builtin_return(parser_t &parser, wchar_t **argv)
return STATUS_BUILTIN_ERROR; return STATUS_BUILTIN_ERROR;
} }
/* Find the function block */
while ((b != 0) && size_t function_block_idx;
(b->type() != FUNCTION_CALL && for (function_block_idx = 0; function_block_idx < parser.block_count(); function_block_idx++)
b->type() != FUNCTION_CALL_NO_SHADOW))
{ {
b = b->outer; const block_t *b = parser.block_at_index(function_block_idx);
if (b->type() == FUNCTION_CALL || b->type() == FUNCTION_CALL_NO_SHADOW)
break;
} }
if (b == 0) if (function_block_idx >= parser.block_count())
{ {
append_format(stderr_buffer, append_format(stderr_buffer,
_(L"%ls: Not inside of function\n"), _(L"%ls: Not inside of function\n"),
@ -3726,16 +3736,14 @@ static int builtin_return(parser_t &parser, wchar_t **argv)
return STATUS_BUILTIN_ERROR; return STATUS_BUILTIN_ERROR;
} }
b = parser.current_block; /* Skip everything up to (and then including) the function block */
while ((b->type() != FUNCTION_CALL && for (size_t i=0; i < function_block_idx; i++)
b->type() != FUNCTION_CALL_NO_SHADOW))
{ {
block_t *b = parser.block_at_index(i);
b->mark_as_fake(); b->mark_as_fake();
b->skip=1; b->skip = true;
b = b->outer;
} }
b->skip=1; parser.block_at_index(function_block_idx)->skip = true;
return status; return status;
} }
@ -3762,7 +3770,7 @@ static int builtin_switch(parser_t &parser, wchar_t **argv)
else else
{ {
parser.push_block(new switch_block_t(argv[1])); parser.push_block(new switch_block_t(argv[1]));
parser.current_block->skip=1; parser.current_block()->skip=1;
res = proc_get_last_status(); res = proc_get_last_status();
} }
@ -3779,7 +3787,7 @@ static int builtin_case(parser_t &parser, wchar_t **argv)
int i; int i;
wchar_t *unescaped=0; wchar_t *unescaped=0;
if (parser.current_block->type() != SWITCH) if (parser.current_block()->type() != SWITCH)
{ {
append_format(stderr_buffer, append_format(stderr_buffer,
_(L"%ls: 'case' command while not in switch block\n"), _(L"%ls: 'case' command while not in switch block\n"),
@ -3788,8 +3796,8 @@ static int builtin_case(parser_t &parser, wchar_t **argv)
return STATUS_BUILTIN_ERROR; return STATUS_BUILTIN_ERROR;
} }
parser.current_block->skip = 1; parser.current_block()->skip = 1;
switch_block_t *sb = static_cast<switch_block_t *>(parser.current_block); switch_block_t *sb = static_cast<switch_block_t *>(parser.current_block());
if (sb->switch_taken) if (sb->switch_taken)
{ {
return proc_get_last_status(); return proc_get_last_status();
@ -3806,7 +3814,7 @@ static int builtin_case(parser_t &parser, wchar_t **argv)
if (match) if (match)
{ {
parser.current_block->skip = 0; parser.current_block()->skip = 0;
sb->switch_taken = true; sb->switch_taken = true;
break; break;
} }

View file

@ -144,12 +144,15 @@ static int event_match(const event_t &classv, const event_t &instance)
*/ */
static int event_is_blocked(const event_t &e) static int event_is_blocked(const event_t &e)
{ {
block_t *block; const block_t *block;
parser_t &parser = parser_t::principal_parser(); parser_t &parser = parser_t::principal_parser();
for (block = parser.current_block; block; block = block->outer)
size_t idx = 0;
while ((block = parser.block_at_index(idx++)))
{ {
if (event_block_list_blocks_type(block->event_blocks, e.type)) if (event_block_list_blocks_type(block->event_blocks, e.type))
return true; return true;
} }
return event_block_list_blocks_type(parser.global_event_blocks, e.type); return event_block_list_blocks_type(parser.global_event_blocks, e.type);
} }

View file

@ -580,7 +580,8 @@ static void exec_no_exec(parser_t &parser, const job_t *job)
} }
else if (builtin_name == L"end") else if (builtin_name == L"end")
{ {
if (parser.current_block == NULL || parser.current_block->type() == TOP) const block_t *block = parser.current_block();
if (block == NULL || block->type() == TOP)
{ {
fprintf(stderr, "Warning: not popping the root block\n"); fprintf(stderr, "Warning: not popping the root block\n");
} }

View file

@ -189,7 +189,7 @@ void function_add(const function_data_t &data, const parser_t &parser)
/* Create and store a new function */ /* Create and store a new function */
const wchar_t *filename = reader_current_filename(); const wchar_t *filename = reader_current_filename();
int def_offset = parser.line_number_of_character_at_offset(parser.current_block->tok_pos) - 1; int def_offset = parser.line_number_of_character_at_offset(parser.current_block()->tok_pos) - 1;
const function_map_t::value_type new_pair(data.name, function_info_t(data, filename, def_offset, is_autoload)); const function_map_t::value_type new_pair(data.name, function_info_t(data, filename, def_offset, is_autoload));
loaded_functions.insert(new_pair); loaded_functions.insert(new_pair);

View file

@ -325,7 +325,6 @@ parser_t::parser_t(enum parser_type_t type, bool errors) :
current_tokenizer_pos(0), current_tokenizer_pos(0),
job_start_pos(0), job_start_pos(0),
eval_level(-1), eval_level(-1),
current_block(NULL),
block_io(shared_ptr<io_data_t>()) block_io(shared_ptr<io_data_t>())
{ {
@ -352,39 +351,36 @@ void parser_t::skip_all_blocks(void)
if (s_principal_parser) if (s_principal_parser)
{ {
//write(2, "Cancelling blocks\n", strlen("Cancelling blocks\n")); //write(2, "Cancelling blocks\n", strlen("Cancelling blocks\n"));
block_t *c = s_principal_parser->current_block; for (size_t i=0; i < s_principal_parser->block_count(); i++)
while (c)
{ {
c->skip = true; s_principal_parser->block_at_index(i)->skip = true;
//fprintf(stderr, " Cancelled %p\n", c);
c = c->outer;
} }
} }
} }
void parser_t::push_block(block_t *newv) void parser_t::push_block(block_t *new_current)
{ {
const enum block_type_t type = newv->type(); const enum block_type_t type = new_current->type();
newv->src_lineno = parser_t::get_lineno(); new_current->src_lineno = parser_t::get_lineno();
newv->src_filename = parser_t::current_filename()?intern(parser_t::current_filename()):0; new_current->src_filename = parser_t::current_filename()?intern(parser_t::current_filename()):0;
newv->outer = current_block; const block_t *old_current = this->current_block();
if (current_block && current_block->skip) if (old_current && old_current->skip)
newv->mark_as_fake(); new_current->mark_as_fake();
/* /*
New blocks should be skipped if the outer block is skipped, New blocks should be skipped if the outer block is skipped,
except TOP ans SUBST block, which open up new environments. Fake except TOP ans SUBST block, which open up new environments. Fake
blocks should always be skipped. Rather complicated... :-( blocks should always be skipped. Rather complicated... :-(
*/ */
newv->skip=current_block?current_block->skip:0; new_current->skip = old_current ? old_current->skip : 0;
/* /*
Type TOP and SUBST are never skipped Type TOP and SUBST are never skipped
*/ */
if (type == TOP || type == SUBST) if (type == TOP || type == SUBST)
{ {
newv->skip = 0; new_current->skip = 0;
} }
/* /*
@ -392,27 +388,26 @@ void parser_t::push_block(block_t *newv)
*/ */
if (type == FAKE || type == FUNCTION_DEF) if (type == FAKE || type == FUNCTION_DEF)
{ {
newv->skip = 1; new_current->skip = 1;
} }
newv->job = 0; new_current->job = 0;
newv->loop_status=LOOP_NORMAL; new_current->loop_status=LOOP_NORMAL;
current_block = newv; this->block_stack.push_back(new_current);
if ((newv->type() != FUNCTION_DEF) && if ((new_current->type() != FUNCTION_DEF) &&
(newv->type() != FAKE) && (new_current->type() != FAKE) &&
(newv->type() != TOP)) (new_current->type() != TOP))
{ {
env_push(type == FUNCTION_CALL); env_push(type == FUNCTION_CALL);
newv->wants_pop_env = true; new_current->wants_pop_env = true;
} }
} }
void parser_t::pop_block() void parser_t::pop_block()
{ {
block_t *old = current_block; if (block_stack.empty())
if (!current_block)
{ {
debug(1, debug(1,
L"function %s called on empty block stack.", L"function %s called on empty block stack.",
@ -421,7 +416,8 @@ void parser_t::pop_block()
return; return;
} }
current_block = current_block->outer; block_t *old = block_stack.back();
block_stack.pop_back();
if (old->wants_pop_env) if (old->wants_pop_env)
env_pop(); env_pop();
@ -441,6 +437,30 @@ const wchar_t *parser_t::get_block_desc(int block) const
return _(UNKNOWN_BLOCK); return _(UNKNOWN_BLOCK);
} }
const block_t *parser_t::block_at_index(size_t idx) const
{
/* 0 corresponds to the last element in our vector */
size_t count = block_stack.size();
return idx < count ? block_stack.at(count - idx - 1) : NULL;
}
block_t *parser_t::block_at_index(size_t idx)
{
size_t count = block_stack.size();
return idx < count ? block_stack.at(count - idx - 1) : NULL;
}
const block_t *parser_t::current_block() const
{
return block_stack.empty() ? NULL : block_stack.back();
}
block_t *parser_t::current_block()
{
return block_stack.empty() ? NULL : block_stack.back();
}
/** /**
Returns 1 if the specified command is a builtin that may not be used in a pipeline Returns 1 if the specified command is a builtin that may not be used in a pipeline
*/ */
@ -807,14 +827,16 @@ int parser_t::eval_args(const wchar_t *line, std::vector<completion_t> &args)
return 1; return 1;
} }
void parser_t::stack_trace(block_t *b, wcstring &buff) void parser_t::stack_trace(size_t block_idx, wcstring &buff)
{ {
/* /*
Check if we should end the recursion Check if we should end the recursion
*/ */
if (!b) if (block_idx >= this->block_count())
return; return;
const block_t *b = this->block_at_index(block_idx);
if (b->type()==EVENT) if (b->type()==EVENT)
{ {
/* /*
@ -908,7 +930,7 @@ void parser_t::stack_trace(block_t *b, wcstring &buff)
/* /*
Recursively print the next block Recursively print the next block
*/ */
parser_t::stack_trace(b->outer, buff); parser_t::stack_trace(block_idx + 1, buff);
} }
/** /**
@ -921,22 +943,19 @@ const wchar_t *parser_t::is_function() const
{ {
// PCA: Have to make this a string somehow // PCA: Have to make this a string somehow
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
wcstring result;
block_t *b = current_block; const wchar_t *result = NULL;
while (1) for (size_t block_idx = 0; block_idx < this->block_count(); block_idx++)
{ {
if (!b) const block_t *b = this->block_at_index(block_idx);
{
return NULL;
}
if (b->type() == FUNCTION_CALL) if (b->type() == FUNCTION_CALL)
{ {
const function_block_t *fb = static_cast<const function_block_t *>(b); const function_block_t *fb = static_cast<const function_block_t *>(b);
return fb->name.c_str(); result = fb->name.c_str();
break;
} }
b=b->outer;
} }
return result;
} }
@ -974,21 +993,17 @@ const wchar_t *parser_t::current_filename() const
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
assert(this == &principal_parser()); assert(this == &principal_parser());
block_t *b = current_block;
while (1) for (size_t i=0; i < this->block_count(); i++)
{ {
if (!b) const block_t *b = this->block_at_index(i);
{
return reader_current_filename();
}
if (b->type() == FUNCTION_CALL) if (b->type() == FUNCTION_CALL)
{ {
const function_block_t *fb = static_cast<const function_block_t *>(b); const function_block_t *fb = static_cast<const function_block_t *>(b);
return function_get_definition_file(fb->name); return function_get_definition_file(fb->name);
} }
b=b->outer;
} }
return reader_current_filename();
} }
/** /**
@ -1129,7 +1144,7 @@ const wchar_t *parser_t::current_line()
} }
free((void *)line); free((void *)line);
parser_t::stack_trace(current_block, lineinfo); parser_t::stack_trace(0, lineinfo);
return lineinfo.c_str(); return lineinfo.c_str();
} }
@ -1239,7 +1254,7 @@ job_t *parser_t::job_get_from_pid(int pid)
\param j the job to which the process belongs to \param j the job to which the process belongs to
\param tok the tokenizer to read options from \param tok the tokenizer to read options from
\param args the argument list to insert options into \param args the argument list to insert options into
\param args unskip whether we should ignore current_block->skip. Big hack because of our dumb handling of if statements. \param args unskip whether we should ignore current_block()->skip. Big hack because of our dumb handling of if statements.
*/ */
void parser_t::parse_job_argument_list(process_t *p, void parser_t::parse_job_argument_list(process_t *p,
job_t *j, job_t *j,
@ -1336,7 +1351,7 @@ void parser_t::parse_job_argument_list(process_t *p,
{ {
skip = 1; skip = 1;
} }
else if (current_block->skip && ! unskip) else if (current_block()->skip && ! unskip)
{ {
/* /*
If this command should be skipped, we do not expand the arguments If this command should be skipped, we do not expand the arguments
@ -1344,12 +1359,12 @@ void parser_t::parse_job_argument_list(process_t *p,
skip=1; skip=1;
/* But if this is in fact a case statement or an elseif statement, then it should be evaluated */ /* But if this is in fact a case statement or an elseif statement, then it should be evaluated */
block_type_t type = current_block->type(); block_type_t type = current_block()->type();
if (type == SWITCH && args.at(0).completion == L"case" && p->type == INTERNAL_BUILTIN) if (type == SWITCH && args.at(0).completion == L"case" && p->type == INTERNAL_BUILTIN)
{ {
skip=0; skip=0;
} }
else if (job_get_flag(j, JOB_ELSEIF) && ! job_should_skip_elseif(j, current_block)) else if (job_get_flag(j, JOB_ELSEIF) && ! job_should_skip_elseif(j, current_block()))
{ {
skip=0; skip=0;
} }
@ -1357,7 +1372,7 @@ void parser_t::parse_job_argument_list(process_t *p,
else else
{ {
/* If this is an else if, and we should skip it, then don't expand any arguments */ /* If this is an else if, and we should skip it, then don't expand any arguments */
if (job_get_flag(j, JOB_ELSEIF) && job_should_skip_elseif(j, current_block)) if (job_get_flag(j, JOB_ELSEIF) && job_should_skip_elseif(j, current_block()))
{ {
skip = 1; skip = 1;
} }
@ -1440,7 +1455,7 @@ void parser_t::parse_job_argument_list(process_t *p,
Otherwise, bogus errors may be the result. (Do check Otherwise, bogus errors may be the result. (Do check
that token is string, though) that token is string, though)
*/ */
if (current_block->skip && ! unskip) if (current_block()->skip && ! unskip)
{ {
tok_next(tok); tok_next(tok);
if (tok_last_type(tok) != TOK_STRING) if (tok_last_type(tok) != TOK_STRING)
@ -1653,7 +1668,7 @@ int parser_t::parse_job(process_t *p,
bool unskip = false; // Maybe we are an elseif inside an if block; if so we may want to evaluate this even if the if block is currently set to skip bool unskip = false; // Maybe we are an elseif inside an if block; if so we may want to evaluate this even if the if block is currently set to skip
bool allow_bogus_command = false; // If we are an elseif that will not be executed, or an AND or OR that will have been short circuited, don't complain about non-existent commands bool allow_bogus_command = false; // If we are an elseif that will not be executed, or an AND or OR that will have been short circuited, don't complain about non-existent commands
block_t *prev_block = current_block; const block_t *prev_block = current_block();
scoped_push<int> tokenizer_pos_push(&current_tokenizer_pos, tok_get_pos(tok)); scoped_push<int> tokenizer_pos_push(&current_tokenizer_pos, tok_get_pos(tok));
while (args.empty()) while (args.empty())
@ -1806,11 +1821,11 @@ int parser_t::parse_job(process_t *p,
tok_next(tok); tok_next(tok);
while_block_t *wb = NULL; while_block_t *wb = NULL;
if ((current_block->type() != WHILE)) if ((current_block()->type() != WHILE))
{ {
new_block = true; new_block = true;
} }
else if ((wb = static_cast<while_block_t*>(current_block))->status == WHILE_TEST_AGAIN) else if ((wb = static_cast<while_block_t*>(current_block()))->status == WHILE_TEST_AGAIN)
{ {
wb->status = WHILE_TEST_FIRST; wb->status = WHILE_TEST_FIRST;
} }
@ -1848,9 +1863,9 @@ int parser_t::parse_job(process_t *p,
const int else_pos = tok_get_pos(tok); const int else_pos = tok_get_pos(tok);
/* See if we have any more arguments, that is, whether we're ELSE IF ... or just ELSE. */ /* See if we have any more arguments, that is, whether we're ELSE IF ... or just ELSE. */
tok_next(tok); tok_next(tok);
if (tok_last_type(tok) == TOK_STRING && current_block->type() == IF) if (tok_last_type(tok) == TOK_STRING && current_block()->type() == IF)
{ {
const if_block_t *ib = static_cast<const if_block_t *>(current_block); const if_block_t *ib = static_cast<const if_block_t *>(current_block());
/* If we've already encountered an else, complain */ /* If we've already encountered an else, complain */
if (ib->else_evaluated) if (ib->else_evaluated)
@ -1891,7 +1906,7 @@ int parser_t::parse_job(process_t *p,
continue; continue;
} }
if (use_function && (unskip || ! current_block->skip)) if (use_function && (unskip || ! current_block()->skip))
{ {
bool nxt_forbidden=false; bool nxt_forbidden=false;
wcstring forbid; wcstring forbid;
@ -1905,9 +1920,8 @@ int parser_t::parse_job(process_t *p,
block scopes are pushed on function invocation changes, block scopes are pushed on function invocation changes,
then this check will break. then this check will break.
*/ */
if ((current_block->type() == TOP) && const block_t *current = this->block_at_index(0), *parent = this->block_at_index(1);
(current_block->outer) && if (current && parent && current->type() == TOP && parent->type() == FUNCTION_CALL)
(current_block->outer->type() == FUNCTION_CALL))
is_function_call = 1; is_function_call = 1;
/* /*
@ -1916,7 +1930,7 @@ int parser_t::parse_job(process_t *p,
may not be called, since that would mean an infinite may not be called, since that would mean an infinite
recursion. recursion.
*/ */
if (is_function_call && !current_block->had_command) if (is_function_call && !current->had_command)
{ {
forbid = forbidden_function.empty() ? wcstring(L"") : forbidden_function.back(); forbid = forbidden_function.empty() ? wcstring(L"") : forbidden_function.back();
if (forbid == nxt) if (forbid == nxt)
@ -1963,7 +1977,7 @@ int parser_t::parse_job(process_t *p,
If we are not executing the current block, allow If we are not executing the current block, allow
non-existent commands. non-existent commands.
*/ */
if (current_block->skip && ! unskip) if (current_block()->skip && ! unskip)
allow_bogus_command = true; //note this may already be true for other reasons allow_bogus_command = true; //note this may already be true for other reasons
if (allow_bogus_command) if (allow_bogus_command)
@ -2208,7 +2222,7 @@ int parser_t::parse_job(process_t *p,
tok_set_pos(tok, (int)end_pos); tok_set_pos(tok, (int)end_pos);
while (prev_block != current_block) while (prev_block != this->current_block())
{ {
parser_t::pop_block(); parser_t::pop_block();
} }
@ -2237,7 +2251,7 @@ int parser_t::parse_job(process_t *p,
{ {
if (!is_new_block) if (!is_new_block)
{ {
current_block->had_command = true; current_block()->had_command = true;
} }
} }
@ -2246,7 +2260,7 @@ int parser_t::parse_job(process_t *p,
/* /*
Make sure the block stack is consistent Make sure the block stack is consistent
*/ */
while (prev_block != current_block) while (prev_block != current_block())
{ {
parser_t::pop_block(); parser_t::pop_block();
} }
@ -2280,7 +2294,8 @@ void parser_t::skipped_exec(job_t * j)
} }
else if (wcscmp(p->argv0(), L"end")==0) else if (wcscmp(p->argv0(), L"end")==0)
{ {
if (!current_block->outer->skip) const block_t *parent = this->block_at_index(1);
if (parent && ! parent->skip)
{ {
exec_job(*this, j); exec_job(*this, j);
return; return;
@ -2289,10 +2304,10 @@ void parser_t::skipped_exec(job_t * j)
} }
else if (wcscmp(p->argv0(), L"else")==0) else if (wcscmp(p->argv0(), L"else")==0)
{ {
if (current_block->type() == IF) if (current_block()->type() == IF)
{ {
/* Evaluate this ELSE if the IF expression failed, and so has every ELSEIF (if any) expression thus far */ /* Evaluate this ELSE if the IF expression failed, and so has every ELSEIF (if any) expression thus far */
const if_block_t *ib = static_cast<const if_block_t*>(current_block); const if_block_t *ib = static_cast<const if_block_t*>(current_block());
if (ib->if_expr_evaluated && ! ib->any_branch_taken) if (ib->if_expr_evaluated && ! ib->any_branch_taken)
{ {
exec_job(*this, j); exec_job(*this, j);
@ -2302,7 +2317,7 @@ void parser_t::skipped_exec(job_t * j)
} }
else if (wcscmp(p->argv0(), L"case")==0) else if (wcscmp(p->argv0(), L"case")==0)
{ {
if (current_block->type() == SWITCH) if (current_block()->type() == SWITCH)
{ {
exec_job(*this, j); exec_job(*this, j);
return; return;
@ -2376,7 +2391,7 @@ void parser_t::eval_job(tokenizer_t *tok)
|| is_event \ || is_event \
|| (!get_is_interactive())); || (!get_is_interactive()));
current_block->job = j; current_block()->job = j;
if (get_is_interactive()) if (get_is_interactive())
{ {
@ -2411,27 +2426,27 @@ void parser_t::eval_job(tokenizer_t *tok)
{ {
t2 = get_time(); t2 = get_time();
profile_item->cmd = j->command(); profile_item->cmd = j->command();
profile_item->skipped=current_block->skip; profile_item->skipped=current_block()->skip;
} }
/* If we're an ELSEIF, then we may want to unskip, if we're skipping because of an IF */ /* If we're an ELSEIF, then we may want to unskip, if we're skipping because of an IF */
if (job_get_flag(j, JOB_ELSEIF)) if (job_get_flag(j, JOB_ELSEIF))
{ {
bool skip_elseif = job_should_skip_elseif(j, current_block); bool skip_elseif = job_should_skip_elseif(j, current_block());
/* Record that we're entering an elseif */ /* Record that we're entering an elseif */
if (! skip_elseif) if (! skip_elseif)
{ {
/* We must be an IF block here */ /* We must be an IF block here */
assert(current_block->type() == IF); assert(current_block()->type() == IF);
static_cast<if_block_t *>(current_block)->is_elseif_entry = true; static_cast<if_block_t *>(current_block())->is_elseif_entry = true;
} }
/* Record that in the block too. This is similar to what builtin_else does. */ /* Record that in the block too. This is similar to what builtin_else does. */
current_block->skip = skip_elseif; current_block()->skip = skip_elseif;
} }
skip = skip || current_block->skip; skip = skip || current_block()->skip;
skip = skip || job_get_flag(j, JOB_WILDCARD_ERROR); skip = skip || job_get_flag(j, JOB_WILDCARD_ERROR);
skip = skip || job_get_flag(j, JOB_SKIP); skip = skip || job_get_flag(j, JOB_SKIP);
@ -2460,9 +2475,9 @@ void parser_t::eval_job(tokenizer_t *tok)
profile_item->exec=(int)(t3-t2); profile_item->exec=(int)(t3-t2);
} }
if (current_block->type() == WHILE) if (current_block()->type() == WHILE)
{ {
while_block_t *wb = static_cast<while_block_t *>(current_block); while_block_t *wb = static_cast<while_block_t *>(current_block());
switch (wb->status) switch (wb->status)
{ {
case WHILE_TEST_FIRST: case WHILE_TEST_FIRST:
@ -2476,9 +2491,9 @@ void parser_t::eval_job(tokenizer_t *tok)
} }
} }
if (current_block->type() == IF) if (current_block()->type() == IF)
{ {
if_block_t *ib = static_cast<if_block_t *>(current_block); if_block_t *ib = static_cast<if_block_t *>(current_block());
if (ib->skip) if (ib->skip)
{ {
@ -2491,7 +2506,7 @@ void parser_t::eval_job(tokenizer_t *tok)
ib->any_branch_taken = if_result; ib->any_branch_taken = if_result;
/* Don't execute if the expression failed */ /* Don't execute if the expression failed */
current_block->skip = ! if_result; current_block()->skip = ! if_result;
ib->if_expr_evaluated = true; ib->if_expr_evaluated = true;
} }
else if (ib->is_elseif_entry && ! ib->any_branch_taken) else if (ib->is_elseif_entry && ! ib->any_branch_taken)
@ -2499,7 +2514,7 @@ void parser_t::eval_job(tokenizer_t *tok)
/* Maybe mark an ELSEIF branch as taken */ /* Maybe mark an ELSEIF branch as taken */
bool elseif_taken = (proc_get_last_status() == 0); bool elseif_taken = (proc_get_last_status() == 0);
ib->any_branch_taken = elseif_taken; ib->any_branch_taken = elseif_taken;
current_block->skip = ! elseif_taken; current_block()->skip = ! elseif_taken;
ib->is_elseif_entry = false; ib->is_elseif_entry = false;
} }
} }
@ -2517,7 +2532,7 @@ void parser_t::eval_job(tokenizer_t *tok)
proc_set_last_status(1); proc_set_last_status(1);
} }
current_block->job = 0; current_block()->job = 0;
break; break;
} }
@ -2579,7 +2594,7 @@ int parser_t::eval(const wcstring &cmdStr, const io_chain_t &io, enum block_type
const wchar_t * const cmd = cmdStr.c_str(); const wchar_t * const cmd = cmdStr.c_str();
size_t forbid_count; size_t forbid_count;
int code; int code;
block_t *start_current_block = current_block; const block_t *start_current_block = current_block();
/* Record the current chain so we can put it back later */ /* Record the current chain so we can put it back later */
scoped_push<io_chain_t> block_io_push(&block_io, io); scoped_push<io_chain_t> block_io_push(&block_io, io);
@ -2639,9 +2654,9 @@ int parser_t::eval(const wcstring &cmdStr, const io_chain_t &io, enum block_type
parser_t::pop_block(); parser_t::pop_block();
while (start_current_block != current_block) while (start_current_block != current_block())
{ {
if (current_block == 0) if (current_block() == NULL)
{ {
debug(0, debug(0,
_(L"End of block mismatch. Program terminating.")); _(L"End of block mismatch. Program terminating."));
@ -2656,7 +2671,7 @@ int parser_t::eval(const wcstring &cmdStr, const io_chain_t &io, enum block_type
//debug( 2, L"Status %d\n", proc_get_last_status() ); //debug( 2, L"Status %d\n", proc_get_last_status() );
debug(1, debug(1,
L"%ls", parser_t::get_block_desc(current_block->type())); L"%ls", parser_t::get_block_desc(current_block()->type()));
debug(1, debug(1,
BLOCK_END_ERR_MSG); BLOCK_END_ERR_MSG);
fwprintf(stderr, L"%ls", parser_t::current_line()); fwprintf(stderr, L"%ls", parser_t::current_line());
@ -3734,8 +3749,7 @@ block_t::block_t(block_type_t t) :
src_filename(), src_filename(),
src_lineno(), src_lineno(),
wants_pop_env(false), wants_pop_env(false),
event_blocks(), event_blocks()
outer(NULL)
{ {
} }

View file

@ -137,11 +137,6 @@ public:
/** List of event blocks. */ /** List of event blocks. */
event_blockage_list_t event_blocks; event_blockage_list_t event_blocks;
/**
Next outer block
*/
block_t *outer;
/** Destructor */ /** Destructor */
virtual ~block_t(); virtual ~block_t();
}; };
@ -301,7 +296,6 @@ class parser_t
{ {
private: private:
enum parser_type_t parser_type; enum parser_type_t parser_type;
std::vector<block_t> blocks;
/** Whether or not we output errors */ /** Whether or not we output errors */
const bool show_errors; const bool show_errors;
@ -333,6 +327,9 @@ private:
/** The jobs associated with this parser */ /** The jobs associated with this parser */
job_list_t my_job_list; job_list_t my_job_list;
/** The list of blocks, allocated with new. It's our responsibility to delete these */
std::vector<block_t *> block_stack;
/** /**
Keeps track of how many recursive eval calls have been made. Eval Keeps track of how many recursive eval calls have been made. Eval
doesn't call itself directly, recursion happens on blocks and on doesn't call itself directly, recursion happens on blocks and on
@ -377,9 +374,6 @@ public:
/** Create a parser of the given type */ /** Create a parser of the given type */
parser_t(enum parser_type_t type, bool show_errors); parser_t(enum parser_type_t type, bool show_errors);
/** The current innermost block, allocated with new */
block_t *current_block;
/** Global event blocks */ /** Global event blocks */
event_blockage_list_t global_event_blocks; event_blockage_list_t global_event_blocks;
@ -442,6 +436,20 @@ public:
/** Set the current position in the latest string of the tokenizer. */ /** Set the current position in the latest string of the tokenizer. */
void set_pos(int p); void set_pos(int p);
/** Returns the block at the given index. 0 corresponds to the innermost block. Returns NULL when idx is at or equal to the number of blocks. */
const block_t *block_at_index(size_t idx) const;
block_t *block_at_index(size_t idx);
/** Returns the current (innermost) block */
const block_t *current_block() const;
block_t *current_block();
/** Count of blocks */
size_t block_count() const
{
return block_stack.size();
}
/** Get the string currently parsed */ /** Get the string currently parsed */
const wchar_t *get_buffer() const; const wchar_t *get_buffer() const;
@ -533,7 +541,7 @@ public:
/** /**
Write a stack trace starting at the specified block to the specified wcstring Write a stack trace starting at the specified block to the specified wcstring
*/ */
void stack_trace(block_t *b, wcstring &buff); void stack_trace(size_t block_idx, wcstring &buff);
int get_block_type(const wchar_t *cmd) const; int get_block_type(const wchar_t *cmd) const;
const wchar_t *get_block_command(int type) const; const wchar_t *get_block_command(int type) const;

View file

@ -2823,14 +2823,11 @@ static void handle_end_loop()
job_t *j; job_t *j;
int stopped_jobs_count=0; int stopped_jobs_count=0;
int is_breakpoint=0; int is_breakpoint=0;
block_t *b; const parser_t &parser = parser_t::principal_parser();
parser_t &parser = parser_t::principal_parser();
for (b = parser.current_block; for (size_t i = 0; i < parser.block_count(); i++)
b;
b = b->outer)
{ {
if (b->type() == BREAKPOINT) if (parser.block_at_index(i)->type() == BREAKPOINT)
{ {
is_breakpoint = 1; is_breakpoint = 1;
break; break;