diff --git a/Shell/Shell.cpp b/Shell/Shell.cpp index 8a18f08222..962e85fc81 100644 --- a/Shell/Shell.cpp +++ b/Shell/Shell.cpp @@ -413,18 +413,27 @@ String Shell::local_variable_or(const String& name, const String& replacement) return replacement; } -void Shell::set_local_variable(const String& name, RefPtr value) +void Shell::set_local_variable(const String& name, RefPtr value, bool only_in_current_frame) { - if (auto* frame = find_frame_containing_local_variable(name)) - frame->local_variables.set(name, move(value)); - else - m_local_frames.last().local_variables.set(name, move(value)); + if (!only_in_current_frame) { + if (auto* frame = find_frame_containing_local_variable(name)) { + frame->local_variables.set(name, move(value)); + return; + } + } + + m_local_frames.last().local_variables.set(name, move(value)); } -void Shell::unset_local_variable(const String& name) +void Shell::unset_local_variable(const String& name, bool only_in_current_frame) { - if (auto* frame = find_frame_containing_local_variable(name)) - frame->local_variables.remove(name); + if (!only_in_current_frame) { + if (auto* frame = find_frame_containing_local_variable(name)) + frame->local_variables.remove(name); + return; + } + + m_local_frames.last().local_variables.remove(name); } void Shell::define_function(String name, Vector argnames, RefPtr body) @@ -473,7 +482,7 @@ bool Shell::invoke_function(const AST::Command& command, int& retval) auto argv = command.argv; argv.take_first(); - set_local_variable("ARGV", adopt(*new AST::ListValue(move(argv)))); + set_local_variable("ARGV", adopt(*new AST::ListValue(move(argv))), true); function.body->run(*this); diff --git a/Shell/Shell.h b/Shell/Shell.h index 0ead032635..b8993a93c2 100644 --- a/Shell/Shell.h +++ b/Shell/Shell.h @@ -103,8 +103,8 @@ public: RefPtr get_argument(size_t); RefPtr lookup_local_variable(const String&); String local_variable_or(const String&, const String&); - void set_local_variable(const String&, RefPtr); - void unset_local_variable(const String&); + void set_local_variable(const String&, RefPtr, bool only_in_current_frame = false); + void unset_local_variable(const String&, bool only_in_current_frame = false); void define_function(String name, Vector argnames, RefPtr body); bool has_function(const String&); diff --git a/Shell/Tests/function.sh b/Shell/Tests/function.sh index 0dbfad5122..f3ef5aaeed 100644 --- a/Shell/Tests/function.sh +++ b/Shell/Tests/function.sh @@ -24,3 +24,13 @@ if fn 2>/dev/null { fn() { echo $0 } test "$(fn)" = fn || echo '$0' in function not equal to its name && exit 1 + +# Ensure ARGV does not leak from inner frames. +fn() { + fn2 1 2 3 + echo $* +} + +fn2() { } + +test "$(fn foobar)" = "foobar" || echo 'Frames are somehow messed up in nested functions' && exit 1