Make eval a decorator

`eval` has always been implemented as a function, which was always a bit
of a hack that caused some issues such as triggering the creation of a
new scope. This turns `eval` into a decorator.

The scoping issues with eval prevented it from being usable to actually
implement other shell components in fish script, such as the problems
described in #4442, which should now no longer be the case.

Closes #4443.
This commit is contained in:
Mahmoud Al-Qudsi
2019-04-10 22:38:19 -05:00
parent 0bda853dc7
commit 2fe2169065
6 changed files with 64 additions and 90 deletions

View File

@@ -6,6 +6,7 @@
#include <errno.h>
#include <fcntl.h>
#include <numeric>
#ifdef HAVE_SIGINFO_H
#include <siginfo.h>
#endif
@@ -903,6 +904,7 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, std::shared_ptr<
// The IO chain for this process.
io_chain_t process_net_io_chain = j->block_io_chain();
auto cached_status = proc_get_last_status();
if (pipes.write.valid()) {
process_net_io_chain.push_back(std::make_shared<io_pipe_t>(
@@ -944,7 +946,6 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, std::shared_ptr<
// Execute the process.
p->check_generations_before_launch();
switch (p->type) {
case process_type_t::eval: /* so long as `eval` is a function */
case process_type_t::function:
case process_type_t::block_node: {
// Allow buffering unless this is a deferred run. If deferred, then processes after us
@@ -982,6 +983,29 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, std::shared_ptr<
"Aborting.");
break;
}
case process_type_t::eval: {
// int eval(const wcstring &cmd, const io_chain_t &io, enum block_type_t block_type);
bool has_args = false;
wcstring new_cmd;
for (const wchar_t * const* arg = p->get_argv() + 1; *arg != nullptr; ++arg) {
has_args = true;
new_cmd += L' ';
new_cmd += *arg;
}
// `eval` is not supposed to error or do anything at all if no arguments are provided,
// or if it is used to execute a function that wouldn't have changed the status code
// (e.g. an empty function) if it were executed normally.
j->processes[0]->completed = true;
p->status = proc_status_t::from_exit_code(cached_status);
if (has_args) {
parser.eval(new_cmd.c_str(), process_net_io_chain, block_type_t::TOP);
p->status = proc_status_t::from_exit_code(proc_get_last_status());
}
break;
}
}
return true;
}