int parser_t::eval_block_node(node_offset_t node_idx, const io_chain_t &io, enum block_type_t block_type) { // Paranoia. It's a little frightening that we're given only a node_idx and we interpret this in // the topmost execution context's tree. What happens if two trees were to be interleaved? // Fortunately that cannot happen (yet); in the future we probably want some sort of reference // counted trees. parse_execution_context_t *ctx = execution_contexts.back(); assert(ctx != NULL); CHECK_BLOCK(1); // Handle cancellation requests. If our block stack is currently empty, then we already did // successfully cancel (or there was nothing to cancel); clear the flag. If our block stack is // not empty, we are still in the process of cancelling; refuse to evaluate anything. if (this->cancellation_requested) { if (!block_stack.empty()) { return 1; } this->cancellation_requested = false; } // Only certain blocks are allowed. if ((block_type != TOP) && (block_type != SUBST)) { debug(1, INVALID_SCOPE_ERR_MSG, parser_t::get_block_desc(block_type)); bugreport(); return 1; } job_reap(0); // not sure why we reap jobs here /* Start it up */ const block_t *const start_current_block = current_block(); block_t *scope_block = new scope_block_t(block_type); this->push_block(scope_block); int result = ctx->eval_node_at_offset(node_idx, scope_block, io); // Clean up the block stack. this->pop_block(); while (start_current_block != current_block()) { if (current_block() == NULL) { debug(0, _(L"End of block mismatch. Program terminating.")); bugreport(); FATAL_EXIT(); break; } this->pop_block(); } job_reap(0); // reap again return result; }
void parser_t::pop_block() { if (block_stack.empty()) { debug(1, L"function %s called on empty block stack.", __func__); bugreport(); return; } block_t *old = block_stack.back(); block_stack.pop_back(); if (old->wants_pop_env) env_pop(); delete old; // Figure out if `status -b` should consider us to be in a block now int new_is_block=0; for (std::vector<block_t*>::const_iterator it = block_stack.begin(), end = block_stack.end(); it != end; ++it) { const enum block_type_t type = (*it)->type(); if (type != TOP && type != SUBST) { new_is_block = 1; break; } } is_block = new_is_block; }
int parser_t::eval(const wcstring &cmd, const io_chain_t &io, enum block_type_t block_type) { CHECK_BLOCK(1); if (block_type != TOP && block_type != SUBST) { debug(1, INVALID_SCOPE_ERR_MSG, parser_t::get_block_desc(block_type)); bugreport(); return 1; } /* Parse the source into a tree, if we can */ parse_node_tree_t tree; parse_error_list_t error_list; if (! parse_tree_from_string(cmd, parse_flag_none, &tree, this->show_errors ? &error_list : NULL)) { if (this->show_errors) { /* Get a backtrace */ wcstring backtrace_and_desc; this->get_backtrace(cmd, error_list, &backtrace_and_desc); /* Print it */ fprintf(stderr, "%ls", backtrace_and_desc.c_str()); } return 1; } //print_stderr(block_stack_description()); /* Determine the initial eval level. If this is the first context, it's -1; otherwise it's the eval level of the top context. This is sort of wonky because we're stitching together a global notion of eval level from these separate objects. A better approach would be some profile object that all contexts share, and that tracks the eval levels on its own. */ int exec_eval_level = (execution_contexts.empty() ? -1 : execution_contexts.back()->current_eval_level()); /* Append to the execution context stack */ parse_execution_context_t *ctx = new parse_execution_context_t(tree, cmd, this, exec_eval_level); execution_contexts.push_back(ctx); /* Execute the first node */ if (! tree.empty()) { this->eval_block_node(0, io, block_type); } /* Clean up the execution context stack */ assert(! execution_contexts.empty() && execution_contexts.back() == ctx); execution_contexts.pop_back(); delete ctx; return 0; }
void signal_unblock() { ASSERT_IS_MAIN_THREAD(); sigset_t chldset; block_count--; if (block_count < 0) { debug(0, _(L"Signal block mismatch")); bugreport(); FATAL_EXIT(); } if (!block_count) { sigfillset(&chldset); VOMIT_ON_FAILURE(pthread_sigmask(SIG_UNBLOCK, &chldset, 0)); } // debug( 0, L"signal block level decreased to %d", block_count ); }
void parser_t::pop_block() { if (block_stack.empty()) { debug(1, L"function %s called on empty block stack.", __func__); bugreport(); return; } block_t *old = block_stack.back(); block_stack.pop_back(); if (old->wants_pop_env) env_pop(); delete old; }
int parser_t::eval_node(parsed_source_ref_t ps, tnode_t<T> node, const io_chain_t &io, enum block_type_t block_type) { static_assert( std::is_same<T, grammar::statement>::value || std::is_same<T, grammar::job_list>::value, "Unexpected node type"); CHECK_BLOCK(1); // Handle cancellation requests. If our block stack is currently empty, then we already did // successfully cancel (or there was nothing to cancel); clear the flag. If our block stack is // not empty, we are still in the process of cancelling; refuse to evaluate anything. if (this->cancellation_requested) { if (!block_stack.empty()) { return 1; } this->cancellation_requested = false; } // Only certain blocks are allowed. if ((block_type != TOP) && (block_type != SUBST)) { debug(1, INVALID_SCOPE_ERR_MSG, parser_t::get_block_desc(block_type)); bugreport(); return 1; } job_reap(0); // not sure why we reap jobs here // Start it up scope_block_t *scope_block = this->push_block<scope_block_t>(block_type); // Create and set a new execution context. using exc_ctx_ref_t = std::unique_ptr<parse_execution_context_t>; scoped_push<exc_ctx_ref_t> exc(&execution_context, make_unique<parse_execution_context_t>(ps, this)); int result = execution_context->eval_node(node, scope_block, io); exc.restore(); this->pop_block(scope_block); job_reap(0); // reap again return result; }
void parser_t::pop_block(const block_t *expected) { assert(expected == this->current_block()); if (block_stack.empty()) { debug(1, L"function %s called on empty block stack.", __func__); bugreport(); return; } // acquire ownership out of the block stack // this will trigger deletion when it goes out of scope std::unique_ptr<block_t> old = std::move(block_stack.back()); block_stack.pop_back(); if (old->wants_pop_env) env_pop(); // Figure out if `status is-block` should consider us to be in a block now. bool new_is_block = false; for (const auto &b : block_stack) { const enum block_type_t type = b->type(); if (type != TOP && type != SUBST) { new_is_block = true; break; } } is_block = new_is_block; // Are we still in a breakpoint? bool new_is_breakpoint = false; for (const auto &b : block_stack) { const enum block_type_t type = b->type(); if (type == BREAKPOINT) { new_is_breakpoint = true; break; } } is_breakpoint = new_is_breakpoint; }