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); 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); wcstring script_source = L"source " + escape_string(path, ESCAPE_ALL);
exec_subshell(script_source, parser_t::principal_parser(), exec_subshell(script_source, parser, false /* do not apply exit status */);
false /* do not apply exit status */);
} }

View file

@ -15,6 +15,7 @@
class autoload_file_cache_t; class autoload_file_cache_t;
class environment_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 /// 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 /// 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. /// Helper to actually perform an autoload.
/// This is a static function because it executes fish script, and so must be called without /// This is a static function because it executes fish script, and so must be called without
/// holding any particular locks. /// 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. /// Mark that a command previously returned from path_to_autoload is finished autoloading.
void mark_autoload_finished(const wcstring &cmd) { 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()); const wcstring ft = tok_first(job->command());
//For compatibility with fish 2.0's $_, now replaced with `status current-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); 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->promote();
job->set_flag(job_flag_t::FOREGROUND, true); 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, 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 *path = L"n/a";
const wchar_t *autoloaded = L"n/a"; const wchar_t *autoloaded = L"n/a";
const wchar_t *shadows_scope = L"n/a"; const wchar_t *shadows_scope = L"n/a";
wcstring description = L"n/a"; wcstring description = L"n/a";
int line_number = 0; int line_number = 0;
if (function_exists(funcname)) { if (function_exists(funcname, parser)) {
auto props = function_get_properties(funcname); auto props = function_get_properties(funcname);
path = function_get_definition_file(funcname); path = function_get_definition_file(funcname);
if (path) { if (path) {
@ -301,13 +301,13 @@ int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
} }
func = argv[optind]; 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); streams.err.append_format(_(L"%ls: Function '%ls' does not exist\n"), cmd, func);
builtin_print_error_trailer(parser, streams.err, cmd); builtin_print_error_trailer(parser, streams.err, cmd);
return STATUS_CMD_ERROR; return STATUS_CMD_ERROR;
} }
function_set_desc(func, opts.description); function_set_desc(func, opts.description, parser);
return STATUS_CMD_OK; 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]; 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) { if (opts.handlers) {
@ -376,7 +376,7 @@ int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
current_func = argv[optind]; current_func = argv[optind];
new_func = argv[optind + 1]; 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, streams.err.append_format(_(L"%ls: Function '%ls' does not exist\n"), cmd,
current_func.c_str()); current_func.c_str());
builtin_print_error_trailer(parser, streams.err, cmd); 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. // 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( streams.err.append_format(
_(L"%ls: Function '%ls' already exists. Cannot create copy '%ls'\n"), cmd, _(L"%ls: Function '%ls' already exists. Cannot create copy '%ls'\n"), cmd,
new_func.c_str(), current_func.c_str()); 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; int res = STATUS_CMD_OK;
for (int i = optind; i < argc; i++) { for (int i = optind; i < argc; i++) {
if (!function_exists(argv[i])) { if (!function_exists(argv[i], parser)) {
res++; res++;
} else { } else {
if (!opts.query) { if (!opts.query) {
if (i != optind) streams.out.append(L"\n"); if (i != optind) streams.out.append(L"\n");
const wchar_t *funcname = argv[optind]; 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)); 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) { 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. // We have to load this as a function, since it may define a --wraps or signature.
// See issue #2466. // 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. // 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 // 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(); const environment_t &vars = parser_t::principal_parser().vars();
maybe_t<wcstring> path_to_load = completion_autoloader.acquire()->resolve_command(name, vars); maybe_t<wcstring> path_to_load = completion_autoloader.acquire()->resolve_command(name, vars);
if (path_to_load) { 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); 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 /// Make sure that if the specified function is a dynamically loaded function, it has been fully
/// loaded. /// loaded.
/// Note this executes fish script code. /// 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(); ASSERT_IS_MAIN_THREAD();
maybe_t<wcstring> path_to_autoload; maybe_t<wcstring> path_to_autoload;
// Note we can't autoload while holding the funcset lock. // 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(); auto funcset = function_set.acquire();
if (funcset->allow_autoload(name)) { if (funcset->allow_autoload(name)) {
const environment_t &vars = parser_t::principal_parser().vars(); path_to_autoload = funcset->autoloader.resolve_command(name, parser.vars());
path_to_autoload = funcset->autoloader.resolve_command(name, vars);
} }
} }
// Release the lock and perform any autoload, then reacquire the lock and clean up. // Release the lock and perform any autoload, then reacquire the lock and clean up.
if (path_to_autoload) { if (path_to_autoload) {
// Crucially, the lock is acquired *after* do_autoload_file_at_path(). // 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); 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; return nullptr;
} }
int function_exists(const wcstring &cmd) { int function_exists(const wcstring &cmd, parser_t &parser) {
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
if (parser_keywords_is_reserved(cmd)) return 0; if (parser_keywords_is_reserved(cmd)) return 0;
try_autoload(cmd); try_autoload(cmd, parser);
auto funcset = function_set.acquire(); auto funcset = function_set.acquire();
return funcset->funcs.find(cmd) != funcset->funcs.end(); 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(); ASSERT_IS_MAIN_THREAD();
if (!parser_keywords_is_reserved(cmd)) { 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; 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(); ASSERT_IS_MAIN_THREAD();
try_autoload(name); try_autoload(name, parser);
auto funcset = function_set.acquire(); auto funcset = function_set.acquire();
auto iter = funcset->funcs.find(name); auto iter = funcset->funcs.find(name);
if (iter != funcset->funcs.end()) { 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); bool function_get_desc(const wcstring &name, wcstring &out_desc);
/// Sets the description of the function with the name \c name. /// 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. /// Returns true if the function with the name name exists.
/// This may autoload. /// 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. /// 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. /// 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); 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; process_type = process_type_t::builtin;
break; break;
case parse_statement_decoration_none: case parse_statement_decoration_none:
if (function_exists(cmd)) { if (function_exists(cmd, *parser)) {
process_type = process_type_t::function; process_type = process_type_t::function;
} else if (builtin_exists(cmd)) { } else if (builtin_exists(cmd)) {
process_type = process_type_t::builtin; 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(); path_to_external_command.clear();
// If we have defined a wrapper around cd, use it, otherwise use the cd builtin. // 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 { } else {
// Not implicit cd. // Not implicit cd.
const globspec_t glob_behavior = (cmd == L"set" || cmd == L"count") ? nullglob : failglob; 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; 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; if (!term_supports_setting_title()) return;
wcstring fish_title_command = DEFAULT_TITLE; 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"; fish_title_command = L"fish_title";
if (!cmd.empty()) { if (!cmd.empty()) {
fish_title_command.append(L" "); fish_title_command.append(L" ");
@ -889,8 +889,7 @@ void reader_write_title(const wcstring &cmd, bool reset_cursor_position) {
wcstring_list_t lst; wcstring_list_t lst;
proc_push_interactive(0); proc_push_interactive(0);
if (exec_subshell(fish_title_command, parser_t::principal_parser(), lst, if (exec_subshell(fish_title_command, parser, lst, false /* ignore exit status */) != -1 &&
false /* ignore exit status */) != -1 &&
!lst.empty()) { !lst.empty()) {
std::fputws(L"\x1B]0;", stdout); std::fputws(L"\x1B]0;", stdout);
for (size_t i = 0; i < lst.size(); i++) { 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() { void reader_data_t::exec_mode_prompt() {
mode_prompt_buff.clear(); 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; wcstring_list_t mode_indicator_list;
exec_subshell(MODE_PROMPT_FUNCTION_NAME, parser(), mode_indicator_list, exec_subshell(MODE_PROMPT_FUNCTION_NAME, parser(), mode_indicator_list,
false); 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 // 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 // may still be output on the line from the previous command (#2499) and we need our PROMPT_SP
// hack to work. // hack to work.
reader_write_title(L"", false); reader_write_title(L"", parser(), false);
} }
void reader_init() { 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); if (!ft.empty()) parser.vars().set_one(L"_", ENV_GLOBAL, ft);
outputter_t &outp = outputter_t::stdoutput(); outputter_t &outp = outputter_t::stdoutput();
reader_write_title(cmd); reader_write_title(cmd, parser);
term_donate(outp); term_donate(outp);
gettimeofday(&time_before, NULL); gettimeofday(&time_before, NULL);
@ -2278,15 +2277,15 @@ static int read_i() {
event_fire_generic(L"fish_prompt"); event_fire_generic(L"fish_prompt");
run_count++; 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); 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); reader_set_left_prompt(LEFT_PROMPT_FUNCTION_NAME);
} else { } else {
reader_set_left_prompt(DEFAULT_PROMPT); 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); reader_set_right_prompt(RIGHT_PROMPT_FUNCTION_NAME);
} else { } else {
reader_set_right_prompt(L""); 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. // Repaint the mode-prompt only if it exists.
// This is an optimization basically exclusively for vi-mode, since the prompt // 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. // 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(); exec_mode_prompt();
s_reset(&screen, screen_reset_current_line_and_prompt); s_reset(&screen, screen_reset_current_line_and_prompt);
screen_reset_needed = false; screen_reset_needed = false;

View file

@ -82,8 +82,9 @@ void reader_pop_current_filename();
/// executing and just after it finishes. /// executing and just after it finishes.
/// ///
/// \param cmd Command line string passed to \c fish_title if is defined. /// \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 /// \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 /// Call this function to tell the reader that a repaint is needed, and should be performed when
/// possible. /// possible.