Thread a parser into function_exists

Since this may autoload, it needs a parser with which to autoload.
This commit is contained in:
ridiculousfish 2019-05-04 20:20:52 -07:00
parent bffacd2fbf
commit 1e57424011
10 changed files with 44 additions and 43 deletions

View file

@ -190,8 +190,7 @@ maybe_t<wcstring> autoload_t::resolve_command(const wcstring &cmd, const environ
return std::move(mfile->path);
}
void autoload_t::perform_autoload(const wcstring &path) {
void autoload_t::perform_autoload(const wcstring &path, parser_t &parser) {
wcstring script_source = L"source " + escape_string(path, ESCAPE_ALL);
exec_subshell(script_source, parser_t::principal_parser(),
false /* do not apply exit status */);
exec_subshell(script_source, parser, false /* do not apply exit status */);
}

View file

@ -15,6 +15,7 @@
class autoload_file_cache_t;
class environment_t;
class parser_t;
/// autoload_t is a class that knows how to autoload .fish files from a list of directories. This
/// is used by autoloading functions and completions. It maintains a file cache, which is
@ -58,7 +59,7 @@ class autoload_t {
/// Helper to actually perform an autoload.
/// This is a static function because it executes fish script, and so must be called without
/// holding any particular locks.
static void perform_autoload(const wcstring &path);
static void perform_autoload(const wcstring &path, parser_t &parser);
/// Mark that a command previously returned from path_to_autoload is finished autoloading.
void mark_autoload_finished(const wcstring &cmd) {

View file

@ -101,7 +101,7 @@ int builtin_fg(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
const wcstring ft = tok_first(job->command());
//For compatibility with fish 2.0's $_, now replaced with `status current-command`
if (!ft.empty()) parser.vars().set_one(L"_", ENV_EXPORT, ft);
reader_write_title(job->command());
reader_write_title(job->command(), parser);
job->promote();
job->set_flag(job_flag_t::FOREGROUND, true);

View file

@ -225,14 +225,14 @@ static wcstring functions_def(const wcstring &name) {
}
static int report_function_metadata(const wchar_t *funcname, bool verbose, io_streams_t &streams,
bool metadata_as_comments) {
parser_t &parser, bool metadata_as_comments) {
const wchar_t *path = L"n/a";
const wchar_t *autoloaded = L"n/a";
const wchar_t *shadows_scope = L"n/a";
wcstring description = L"n/a";
int line_number = 0;
if (function_exists(funcname)) {
if (function_exists(funcname, parser)) {
auto props = function_get_properties(funcname);
path = function_get_definition_file(funcname);
if (path) {
@ -301,13 +301,13 @@ int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
}
func = argv[optind];
if (!function_exists(func)) {
if (!function_exists(func, parser)) {
streams.err.append_format(_(L"%ls: Function '%ls' does not exist\n"), cmd, func);
builtin_print_error_trailer(parser, streams.err, cmd);
return STATUS_CMD_ERROR;
}
function_set_desc(func, opts.description);
function_set_desc(func, opts.description, parser);
return STATUS_CMD_OK;
}
@ -319,7 +319,7 @@ int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
}
const wchar_t *funcname = argv[optind];
return report_function_metadata(funcname, opts.verbose, streams, false);
return report_function_metadata(funcname, opts.verbose, streams, parser, false);
}
if (opts.handlers) {
@ -376,7 +376,7 @@ int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
current_func = argv[optind];
new_func = argv[optind + 1];
if (!function_exists(current_func)) {
if (!function_exists(current_func, parser)) {
streams.err.append_format(_(L"%ls: Function '%ls' does not exist\n"), cmd,
current_func.c_str());
builtin_print_error_trailer(parser, streams.err, cmd);
@ -391,7 +391,7 @@ int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
}
// Keep things simple: don't allow existing names to be copy targets.
if (function_exists(new_func)) {
if (function_exists(new_func, parser)) {
streams.err.append_format(
_(L"%ls: Function '%ls' already exists. Cannot create copy '%ls'\n"), cmd,
new_func.c_str(), current_func.c_str());
@ -405,13 +405,13 @@ int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
int res = STATUS_CMD_OK;
for (int i = optind; i < argc; i++) {
if (!function_exists(argv[i])) {
if (!function_exists(argv[i], parser)) {
res++;
} else {
if (!opts.query) {
if (i != optind) streams.out.append(L"\n");
const wchar_t *funcname = argv[optind];
report_function_metadata(funcname, opts.verbose, streams, true);
report_function_metadata(funcname, opts.verbose, streams, parser, true);
streams.out.append(functions_def(funcname));
}
}

View file

@ -859,7 +859,8 @@ static bool short_ok(const wcstring &arg, const complete_entry_opt_t *entry,
static void complete_load(const wcstring &name, bool reload) {
// We have to load this as a function, since it may define a --wraps or signature.
// See issue #2466.
function_load(name);
auto &parser = parser_t::principal_parser();
function_load(name, parser);
// It's important to NOT hold the lock around completion loading.
// We need to take the lock to decide what to load, drop it to perform the load, then reacquire
@ -867,7 +868,7 @@ static void complete_load(const wcstring &name, bool reload) {
const environment_t &vars = parser_t::principal_parser().vars();
maybe_t<wcstring> path_to_load = completion_autoloader.acquire()->resolve_command(name, vars);
if (path_to_load) {
autoload_t::perform_autoload(*path_to_load);
autoload_t::perform_autoload(*path_to_load, parser);
completion_autoloader.acquire()->mark_autoload_finished(name);
}
}

View file

@ -96,7 +96,7 @@ bool function_set_t::allow_autoload(const wcstring &name) const {
/// Make sure that if the specified function is a dynamically loaded function, it has been fully
/// loaded.
/// Note this executes fish script code.
static void try_autoload(const wcstring &name) {
static void try_autoload(const wcstring &name, parser_t &parser) {
ASSERT_IS_MAIN_THREAD();
maybe_t<wcstring> path_to_autoload;
// Note we can't autoload while holding the funcset lock.
@ -104,15 +104,14 @@ static void try_autoload(const wcstring &name) {
{
auto funcset = function_set.acquire();
if (funcset->allow_autoload(name)) {
const environment_t &vars = parser_t::principal_parser().vars();
path_to_autoload = funcset->autoloader.resolve_command(name, vars);
path_to_autoload = funcset->autoloader.resolve_command(name, parser.vars());
}
}
// Release the lock and perform any autoload, then reacquire the lock and clean up.
if (path_to_autoload) {
// Crucially, the lock is acquired *after* do_autoload_file_at_path().
autoload_t::perform_autoload(*path_to_autoload);
autoload_t::perform_autoload(*path_to_autoload, parser);
function_set.acquire()->autoloader.mark_autoload_finished(name);
}
}
@ -212,18 +211,18 @@ std::shared_ptr<const function_properties_t> function_get_properties(const wcstr
return nullptr;
}
int function_exists(const wcstring &cmd) {
int function_exists(const wcstring &cmd, parser_t &parser) {
ASSERT_IS_MAIN_THREAD();
if (parser_keywords_is_reserved(cmd)) return 0;
try_autoload(cmd);
try_autoload(cmd, parser);
auto funcset = function_set.acquire();
return funcset->funcs.find(cmd) != funcset->funcs.end();
}
void function_load(const wcstring &cmd) {
void function_load(const wcstring &cmd, parser_t &parser) {
ASSERT_IS_MAIN_THREAD();
if (!parser_keywords_is_reserved(cmd)) {
try_autoload(cmd);
try_autoload(cmd, parser);
}
}
@ -279,9 +278,9 @@ bool function_get_desc(const wcstring &name, wcstring &out_desc) {
return false;
}
void function_set_desc(const wcstring &name, const wcstring &desc) {
void function_set_desc(const wcstring &name, const wcstring &desc, parser_t &parser) {
ASSERT_IS_MAIN_THREAD();
try_autoload(name);
try_autoload(name, parser);
auto funcset = function_set.acquire();
auto iter = funcset->funcs.find(name);
if (iter != funcset->funcs.end()) {

View file

@ -67,14 +67,14 @@ bool function_get_definition(const wcstring &name, wcstring &out_definition);
bool function_get_desc(const wcstring &name, wcstring &out_desc);
/// Sets the description of the function with the name \c name.
void function_set_desc(const wcstring &name, const wcstring &desc);
void function_set_desc(const wcstring &name, const wcstring &desc, parser_t &parser);
/// Returns true if the function with the name name exists.
/// This may autoload.
int function_exists(const wcstring &name);
int function_exists(const wcstring &name, parser_t &parser);
/// Attempts to load a function if not yet loaded. This is used by the completion machinery.
void function_load(const wcstring &name);
void function_load(const wcstring &name, parser_t &parser);
/// Returns true if the function with the name name exists, without triggering autoload.
int function_exists_no_autoload(const wcstring &name, const environment_t &vars);

View file

@ -164,7 +164,7 @@ process_type_t parse_execution_context_t::process_type_for_command(
process_type = process_type_t::builtin;
break;
case parse_statement_decoration_none:
if (function_exists(cmd)) {
if (function_exists(cmd, *parser)) {
process_type = process_type_t::function;
} else if (builtin_exists(cmd)) {
process_type = process_type_t::builtin;
@ -858,7 +858,8 @@ parse_execution_result_t parse_execution_context_t::populate_plain_process(
path_to_external_command.clear();
// If we have defined a wrapper around cd, use it, otherwise use the cd builtin.
process_type = function_exists(L"cd") ? process_type_t::function : process_type_t::builtin;
process_type =
function_exists(L"cd", *parser) ? process_type_t::function : process_type_t::builtin;
} else {
// Not implicit cd.
const globspec_t glob_behavior = (cmd == L"set" || cmd == L"count") ? nullglob : failglob;

View file

@ -874,11 +874,11 @@ bool reader_thread_job_is_stale() {
return read_generation_count() != s_thread_generation;
}
void reader_write_title(const wcstring &cmd, bool reset_cursor_position) {
void reader_write_title(const wcstring &cmd, parser_t &parser, bool reset_cursor_position) {
if (!term_supports_setting_title()) return;
wcstring fish_title_command = DEFAULT_TITLE;
if (function_exists(L"fish_title")) {
if (function_exists(L"fish_title", parser)) {
fish_title_command = L"fish_title";
if (!cmd.empty()) {
fish_title_command.append(L" ");
@ -889,8 +889,7 @@ void reader_write_title(const wcstring &cmd, bool reset_cursor_position) {
wcstring_list_t lst;
proc_push_interactive(0);
if (exec_subshell(fish_title_command, parser_t::principal_parser(), lst,
false /* ignore exit status */) != -1 &&
if (exec_subshell(fish_title_command, parser, lst, false /* ignore exit status */) != -1 &&
!lst.empty()) {
std::fputws(L"\x1B]0;", stdout);
for (size_t i = 0; i < lst.size(); i++) {
@ -909,7 +908,7 @@ void reader_write_title(const wcstring &cmd, bool reset_cursor_position) {
void reader_data_t::exec_mode_prompt() {
mode_prompt_buff.clear();
if (function_exists(MODE_PROMPT_FUNCTION_NAME)) {
if (function_exists(MODE_PROMPT_FUNCTION_NAME, parser())) {
wcstring_list_t mode_indicator_list;
exec_subshell(MODE_PROMPT_FUNCTION_NAME, parser(), mode_indicator_list,
false);
@ -966,7 +965,7 @@ void reader_data_t::exec_prompt() {
// Write the screen title. Do not reset the cursor position: exec_prompt is called when there
// may still be output on the line from the previous command (#2499) and we need our PROMPT_SP
// hack to work.
reader_write_title(L"", false);
reader_write_title(L"", parser(), false);
}
void reader_init() {
@ -1957,7 +1956,7 @@ void reader_run_command(parser_t &parser, const wcstring &cmd) {
if (!ft.empty()) parser.vars().set_one(L"_", ENV_GLOBAL, ft);
outputter_t &outp = outputter_t::stdoutput();
reader_write_title(cmd);
reader_write_title(cmd, parser);
term_donate(outp);
gettimeofday(&time_before, NULL);
@ -2278,15 +2277,15 @@ static int read_i() {
event_fire_generic(L"fish_prompt");
run_count++;
if (is_breakpoint && function_exists(DEBUG_PROMPT_FUNCTION_NAME)) {
if (is_breakpoint && function_exists(DEBUG_PROMPT_FUNCTION_NAME, parser)) {
reader_set_left_prompt(DEBUG_PROMPT_FUNCTION_NAME);
} else if (function_exists(LEFT_PROMPT_FUNCTION_NAME)) {
} else if (function_exists(LEFT_PROMPT_FUNCTION_NAME, parser)) {
reader_set_left_prompt(LEFT_PROMPT_FUNCTION_NAME);
} else {
reader_set_left_prompt(DEFAULT_PROMPT);
}
if (function_exists(RIGHT_PROMPT_FUNCTION_NAME)) {
if (function_exists(RIGHT_PROMPT_FUNCTION_NAME, parser)) {
reader_set_right_prompt(RIGHT_PROMPT_FUNCTION_NAME);
} else {
reader_set_right_prompt(L"");
@ -2500,7 +2499,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
// Repaint the mode-prompt only if it exists.
// This is an optimization basically exclusively for vi-mode, since the prompt
// may sometimes take a while but when switching the mode all we care about is the mode-prompt.
if (function_exists(MODE_PROMPT_FUNCTION_NAME)) {
if (function_exists(MODE_PROMPT_FUNCTION_NAME, parser())) {
exec_mode_prompt();
s_reset(&screen, screen_reset_current_line_and_prompt);
screen_reset_needed = false;

View file

@ -82,8 +82,9 @@ void reader_pop_current_filename();
/// executing and just after it finishes.
///
/// \param cmd Command line string passed to \c fish_title if is defined.
/// \param parser The parser to use for autoloading fish_title.
/// \param reset_cursor_position If set, issue a \r so the line driver knows where we are
void reader_write_title(const wcstring &cmd, bool reset_cursor_position = true);
void reader_write_title(const wcstring &cmd, parser_t &parser, bool reset_cursor_position = true);
/// Call this function to tell the reader that a repaint is needed, and should be performed when
/// possible.