mirror of
https://github.com/fish-shell/fish-shell.git
synced 2025-02-03 01:57:31 +08:00
Use shared_ptr instead of unique_ptr in environments
This prepares for multiple environment stacks sharing the same parent.
This commit is contained in:
parent
8d7cae63ff
commit
bba66a3ecc
119
src/env.cpp
119
src/env.cpp
|
@ -118,9 +118,6 @@ static void init_curses();
|
||||||
// Struct representing one level in the function variable stack.
|
// Struct representing one level in the function variable stack.
|
||||||
// Only our variable stack should create and destroy these
|
// Only our variable stack should create and destroy these
|
||||||
class env_node_t {
|
class env_node_t {
|
||||||
friend struct var_stack_t;
|
|
||||||
env_node_t(bool is_new_scope) : new_scope(is_new_scope) {}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Variable table.
|
/// Variable table.
|
||||||
var_table_t env;
|
var_table_t env;
|
||||||
|
@ -132,13 +129,17 @@ class env_node_t {
|
||||||
/// or does it redefine any variables to not be exported?
|
/// or does it redefine any variables to not be exported?
|
||||||
bool exportv = false;
|
bool exportv = false;
|
||||||
/// Pointer to next level.
|
/// Pointer to next level.
|
||||||
std::unique_ptr<env_node_t> next;
|
std::shared_ptr<env_node_t> next;
|
||||||
|
|
||||||
|
env_node_t(bool is_new_scope) : new_scope(is_new_scope) {}
|
||||||
|
|
||||||
maybe_t<env_var_t> find_entry(const wcstring &key);
|
maybe_t<env_var_t> find_entry(const wcstring &key);
|
||||||
|
|
||||||
bool contains_any_of(const wcstring_list_t &vars) const;
|
bool contains_any_of(const wcstring_list_t &vars) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using env_node_ref_t = std::shared_ptr<env_node_t>;
|
||||||
|
|
||||||
static std::mutex env_lock;
|
static std::mutex env_lock;
|
||||||
|
|
||||||
// A class wrapping up a variable stack
|
// A class wrapping up a variable stack
|
||||||
|
@ -147,11 +148,10 @@ static std::mutex env_lock;
|
||||||
// if we introduce multiple threads of execution
|
// if we introduce multiple threads of execution
|
||||||
struct var_stack_t {
|
struct var_stack_t {
|
||||||
// Top node on the function stack.
|
// Top node on the function stack.
|
||||||
std::unique_ptr<env_node_t> top = NULL;
|
env_node_ref_t top;
|
||||||
|
|
||||||
// Bottom node on the function stack
|
// Bottom node on the function stack.
|
||||||
// This is an observer pointer
|
env_node_ref_t global_env;
|
||||||
env_node_t *global_env = NULL;
|
|
||||||
|
|
||||||
// Exported variable array used by execv.
|
// Exported variable array used by execv.
|
||||||
null_terminated_array_t<char> export_array;
|
null_terminated_array_t<char> export_array;
|
||||||
|
@ -161,7 +161,7 @@ struct var_stack_t {
|
||||||
void mark_changed_exported() { has_changed_exported = true; }
|
void mark_changed_exported() { has_changed_exported = true; }
|
||||||
void update_export_array_if_necessary();
|
void update_export_array_if_necessary();
|
||||||
|
|
||||||
var_stack_t() : top(new env_node_t(false)), global_env(top.get()) {}
|
var_stack_t() : top(globals()), global_env(globals()) {}
|
||||||
|
|
||||||
// Pushes a new node onto our stack
|
// Pushes a new node onto our stack
|
||||||
// Optionally creates a new scope for the node
|
// Optionally creates a new scope for the node
|
||||||
|
@ -170,25 +170,32 @@ struct var_stack_t {
|
||||||
// Pops the top node if it's not global
|
// Pops the top node if it's not global
|
||||||
void pop();
|
void pop();
|
||||||
|
|
||||||
// Returns the next scope to search for a given node, respecting the new_scope lag
|
// Returns the next scope to search for a given node, respecting the new_scope flag.
|
||||||
// Returns NULL if we're done
|
// Returns an empty pointer if we're done.
|
||||||
env_node_t *next_scope_to_search(env_node_t *node);
|
env_node_ref_t next_scope_to_search(const env_node_ref_t &node) const;
|
||||||
const env_node_t *next_scope_to_search(const env_node_t *node) const;
|
|
||||||
|
|
||||||
// Returns the scope used for unspecified scopes. An unspecified scope is either the topmost
|
// Returns the scope used for unspecified scopes. An unspecified scope is either the topmost
|
||||||
// shadowing scope, or the global scope if none. This implements the default behavior of `set`.
|
// shadowing scope, or the global scope if none. This implements the default behavior of `set`.
|
||||||
env_node_t *resolve_unspecified_scope();
|
env_node_ref_t resolve_unspecified_scope();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool local_scope_exports(const env_node_t *n) const;
|
bool local_scope_exports(const env_node_ref_t &n) const;
|
||||||
void get_exported(const env_node_t *n, var_table_t &h) const;
|
void get_exported(const env_node_t *n, var_table_t &h) const;
|
||||||
|
|
||||||
|
/// Returns the global variable set.
|
||||||
|
static env_node_ref_t globals();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
env_node_ref_t var_stack_t::globals() {
|
||||||
|
static env_node_ref_t s_globals{std::make_shared<env_node_t>(false)};
|
||||||
|
return s_globals;
|
||||||
|
}
|
||||||
|
|
||||||
void var_stack_t::push(bool new_scope) {
|
void var_stack_t::push(bool new_scope) {
|
||||||
std::unique_ptr<env_node_t> node(new env_node_t(new_scope));
|
auto node = std::make_shared<env_node_t>(new_scope);
|
||||||
|
|
||||||
// Copy local-exported variables.
|
// Copy local-exported variables.
|
||||||
auto top_node = top.get();
|
auto top_node = top;
|
||||||
// Only if we introduce a new shadowing scope; i.e. not if it's just `begin; end` or
|
// Only if we introduce a new shadowing scope; i.e. not if it's just `begin; end` or
|
||||||
// "--no-scope-shadowing".
|
// "--no-scope-shadowing".
|
||||||
if (new_scope && top_node != this->global_env) {
|
if (new_scope && top_node != this->global_env) {
|
||||||
|
@ -197,9 +204,9 @@ void var_stack_t::push(bool new_scope) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node->next = std::move(this->top);
|
node->next = this->top;
|
||||||
this->top = std::move(node);
|
this->top = node;
|
||||||
if (new_scope && local_scope_exports(this->top.get())) {
|
if (new_scope && local_scope_exports(this->top)) {
|
||||||
this->mark_changed_exported();
|
this->mark_changed_exported();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,7 +221,7 @@ bool env_node_t::contains_any_of(const wcstring_list_t &vars) const {
|
||||||
|
|
||||||
void var_stack_t::pop() {
|
void var_stack_t::pop() {
|
||||||
// Don't pop the top-most, global, level.
|
// Don't pop the top-most, global, level.
|
||||||
if (top.get() == this->global_env) {
|
if (top == this->global_env) {
|
||||||
debug(0, _(L"Tried to pop empty environment stack."));
|
debug(0, _(L"Tried to pop empty environment stack."));
|
||||||
sanity_lose();
|
sanity_lose();
|
||||||
return;
|
return;
|
||||||
|
@ -224,20 +231,14 @@ void var_stack_t::pop() {
|
||||||
bool curses_changed = top->contains_any_of(curses_variables);
|
bool curses_changed = top->contains_any_of(curses_variables);
|
||||||
|
|
||||||
if (top->new_scope) { //!OCLINT(collapsible if statements)
|
if (top->new_scope) { //!OCLINT(collapsible if statements)
|
||||||
if (top->exportv || local_scope_exports(top->next.get())) {
|
if (top->exportv || local_scope_exports(top->next)) {
|
||||||
this->mark_changed_exported();
|
this->mark_changed_exported();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually do the pop! Move the top pointer into a local variable, then replace the top pointer
|
// Actually do the pop!
|
||||||
// with the next pointer afterwards we should have a node with no next pointer, and our top
|
env_node_ref_t old_top = this->top;
|
||||||
// should be non-null.
|
this->top = old_top->next;
|
||||||
std::unique_ptr<env_node_t> old_top = std::move(this->top);
|
|
||||||
this->top = std::move(old_top->next);
|
|
||||||
old_top->next.reset();
|
|
||||||
assert(this->top && old_top && !old_top->next);
|
|
||||||
assert(this->top != NULL);
|
|
||||||
|
|
||||||
for (const auto &entry_pair : old_top->env) {
|
for (const auto &entry_pair : old_top->env) {
|
||||||
const env_var_t &var = entry_pair.second;
|
const env_var_t &var = entry_pair.second;
|
||||||
if (var.exports()) {
|
if (var.exports()) {
|
||||||
|
@ -250,26 +251,18 @@ void var_stack_t::pop() {
|
||||||
if (curses_changed) init_curses();
|
if (curses_changed) init_curses();
|
||||||
}
|
}
|
||||||
|
|
||||||
const env_node_t *var_stack_t::next_scope_to_search(const env_node_t *node) const {
|
env_node_ref_t var_stack_t::next_scope_to_search(const env_node_ref_t &node) const {
|
||||||
assert(node != NULL);
|
assert(node != NULL);
|
||||||
if (node == this->global_env) {
|
if (node == this->global_env) {
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return node->new_scope ? this->global_env : node->next.get();
|
return node->new_scope ? this->global_env : node->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
env_node_t *var_stack_t::next_scope_to_search(env_node_t *node) {
|
env_node_ref_t var_stack_t::resolve_unspecified_scope() {
|
||||||
assert(node != NULL);
|
env_node_ref_t node = this->top;
|
||||||
if (node == this->global_env) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return node->new_scope ? this->global_env : node->next.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
env_node_t *var_stack_t::resolve_unspecified_scope() {
|
|
||||||
env_node_t *node = this->top.get();
|
|
||||||
while (node && !node->new_scope) {
|
while (node && !node->new_scope) {
|
||||||
node = node->next.get();
|
node = node->next;
|
||||||
}
|
}
|
||||||
return node ? node : this->global_env;
|
return node ? node : this->global_env;
|
||||||
}
|
}
|
||||||
|
@ -1015,9 +1008,9 @@ void env_init(const struct config_paths_t *paths /* or NULL */) {
|
||||||
|
|
||||||
/// Search all visible scopes in order for the specified key. Return the first scope in which it was
|
/// Search all visible scopes in order for the specified key. Return the first scope in which it was
|
||||||
/// found.
|
/// found.
|
||||||
env_node_t *env_stack_t::get_node(const wcstring &key) {
|
env_node_ref_t env_stack_t::get_node(const wcstring &key) {
|
||||||
env_node_t *env = vars_stack().top.get();
|
env_node_ref_t env = vars_stack().top;
|
||||||
while (env != NULL) {
|
while (env) {
|
||||||
if (env->find_entry(key)) break;
|
if (env->find_entry(key)) break;
|
||||||
env = vars_stack().next_scope_to_search(env);
|
env = vars_stack().next_scope_to_search(env);
|
||||||
}
|
}
|
||||||
|
@ -1130,7 +1123,7 @@ int env_stack_t::set_internal(const wcstring &key, env_mode_flags_t input_var_mo
|
||||||
} else {
|
} else {
|
||||||
// Determine the node.
|
// Determine the node.
|
||||||
bool has_changed_new = false;
|
bool has_changed_new = false;
|
||||||
env_node_t *preexisting_node = get_node(key);
|
env_node_ref_t preexisting_node = get_node(key);
|
||||||
maybe_t<env_var_t::env_var_flags_t> preexisting_flags{};
|
maybe_t<env_var_t::env_var_flags_t> preexisting_flags{};
|
||||||
if (preexisting_node != NULL) {
|
if (preexisting_node != NULL) {
|
||||||
var_table_t::const_iterator result = preexisting_node->env.find(key);
|
var_table_t::const_iterator result = preexisting_node->env.find(key);
|
||||||
|
@ -1141,11 +1134,11 @@ int env_stack_t::set_internal(const wcstring &key, env_mode_flags_t input_var_mo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
env_node_t *node = NULL;
|
env_node_ref_t node = nullptr;
|
||||||
if (var_mode & ENV_GLOBAL) {
|
if (var_mode & ENV_GLOBAL) {
|
||||||
node = vars_stack().global_env;
|
node = vars_stack().global_env;
|
||||||
} else if (var_mode & ENV_LOCAL) {
|
} else if (var_mode & ENV_LOCAL) {
|
||||||
node = vars_stack().top.get();
|
node = vars_stack().top;
|
||||||
} else if (preexisting_node != NULL) {
|
} else if (preexisting_node != NULL) {
|
||||||
node = preexisting_node;
|
node = preexisting_node;
|
||||||
if ((var_mode & (ENV_EXPORT | ENV_UNEXPORT)) == 0) {
|
if ((var_mode & (ENV_EXPORT | ENV_UNEXPORT)) == 0) {
|
||||||
|
@ -1252,8 +1245,8 @@ int env_stack_t::set_empty(const wcstring &key, env_mode_flags_t mode) {
|
||||||
/// Attempt to remove/free the specified key/value pair from the specified map.
|
/// Attempt to remove/free the specified key/value pair from the specified map.
|
||||||
///
|
///
|
||||||
/// \return zero if the variable was not found, non-zero otherwise
|
/// \return zero if the variable was not found, non-zero otherwise
|
||||||
bool env_stack_t::try_remove(env_node_t *n, const wchar_t *key, int var_mode) {
|
bool env_stack_t::try_remove(env_node_ref_t n, const wchar_t *key, int var_mode) {
|
||||||
if (n == NULL) {
|
if (n == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1273,19 +1266,19 @@ bool env_stack_t::try_remove(env_node_t *n, const wchar_t *key, int var_mode) {
|
||||||
if (n->new_scope) {
|
if (n->new_scope) {
|
||||||
return try_remove(vars_stack().global_env, key, var_mode);
|
return try_remove(vars_stack().global_env, key, var_mode);
|
||||||
}
|
}
|
||||||
return try_remove(n->next.get(), key, var_mode);
|
return try_remove(n->next, key, var_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
int env_stack_t::remove(const wcstring &key, int var_mode) {
|
int env_stack_t::remove(const wcstring &key, int var_mode) {
|
||||||
ASSERT_IS_MAIN_THREAD();
|
ASSERT_IS_MAIN_THREAD();
|
||||||
env_node_t *first_node;
|
env_node_ref_t first_node{};
|
||||||
int erased = 0;
|
int erased = 0;
|
||||||
|
|
||||||
if ((var_mode & ENV_USER) && is_read_only(key)) {
|
if ((var_mode & ENV_USER) && is_read_only(key)) {
|
||||||
return ENV_SCOPE;
|
return ENV_SCOPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
first_node = vars_stack().top.get();
|
first_node = vars_stack().top;
|
||||||
|
|
||||||
if (!(var_mode & ENV_UNIVERSAL)) {
|
if (!(var_mode & ENV_UNIVERSAL)) {
|
||||||
if (var_mode & ENV_GLOBAL) {
|
if (var_mode & ENV_GLOBAL) {
|
||||||
|
@ -1387,7 +1380,7 @@ maybe_t<env_var_t> env_stack_t::get(const wcstring &key, env_mode_flags_t mode)
|
||||||
|
|
||||||
if (search_local || search_global) {
|
if (search_local || search_global) {
|
||||||
scoped_lock locker(env_lock); // lock around a local region
|
scoped_lock locker(env_lock); // lock around a local region
|
||||||
const env_node_t *env = search_local ? vars_stack().top.get() : vars_stack().global_env;
|
env_node_ref_t env = search_local ? vars_stack().top : vars_stack().global_env;
|
||||||
|
|
||||||
while (env != NULL) {
|
while (env != NULL) {
|
||||||
if (env == vars_stack().global_env && !search_global) {
|
if (env == vars_stack().global_env && !search_global) {
|
||||||
|
@ -1463,15 +1456,15 @@ void env_set_read_limit() { return env_stack_t::principal().set_read_limit(); }
|
||||||
|
|
||||||
/// Returns true if the specified scope or any non-shadowed non-global subscopes contain an exported
|
/// Returns true if the specified scope or any non-shadowed non-global subscopes contain an exported
|
||||||
/// variable.
|
/// variable.
|
||||||
bool var_stack_t::local_scope_exports(const env_node_t *n) const {
|
bool var_stack_t::local_scope_exports(const env_node_ref_t &n) const {
|
||||||
assert(n != NULL);
|
assert(n != nullptr);
|
||||||
if (n == global_env) return false;
|
if (n == global_env) return false;
|
||||||
|
|
||||||
if (n->exportv) return true;
|
if (n->exportv) return true;
|
||||||
|
|
||||||
if (n->new_scope) return false;
|
if (n->new_scope) return false;
|
||||||
|
|
||||||
return local_scope_exports(n->next.get());
|
return local_scope_exports(n->next);
|
||||||
}
|
}
|
||||||
|
|
||||||
void env_stack_t::push(bool new_scope) { vars_stack().push(new_scope); }
|
void env_stack_t::push(bool new_scope) { vars_stack().push(new_scope); }
|
||||||
|
@ -1501,7 +1494,7 @@ wcstring_list_t env_stack_t::get_names(int flags) {
|
||||||
int show_global = flags & ENV_GLOBAL;
|
int show_global = flags & ENV_GLOBAL;
|
||||||
int show_universal = flags & ENV_UNIVERSAL;
|
int show_universal = flags & ENV_UNIVERSAL;
|
||||||
|
|
||||||
const env_node_t *n = vars_stack().top.get();
|
env_node_ref_t n = vars_stack().top;
|
||||||
const bool show_exported = (flags & ENV_EXPORT) || !(flags & ENV_UNEXPORT);
|
const bool show_exported = (flags & ENV_EXPORT) || !(flags & ENV_UNEXPORT);
|
||||||
const bool show_unexported = (flags & ENV_UNEXPORT) || !(flags & ENV_EXPORT);
|
const bool show_unexported = (flags & ENV_UNEXPORT) || !(flags & ENV_EXPORT);
|
||||||
|
|
||||||
|
@ -1517,7 +1510,7 @@ wcstring_list_t env_stack_t::get_names(int flags) {
|
||||||
if (n->new_scope)
|
if (n->new_scope)
|
||||||
break;
|
break;
|
||||||
else
|
else
|
||||||
n = n->next.get();
|
n = n->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1542,7 +1535,7 @@ void var_stack_t::get_exported(const env_node_t *n, var_table_t &h) const {
|
||||||
if (!n) return;
|
if (!n) return;
|
||||||
|
|
||||||
if (n->new_scope) {
|
if (n->new_scope) {
|
||||||
get_exported(global_env, h);
|
get_exported(global_env.get(), h);
|
||||||
} else {
|
} else {
|
||||||
get_exported(n->next.get(), h);
|
get_exported(n->next.get(), h);
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,12 +192,13 @@ void env_set_read_limit();
|
||||||
struct var_stack_t;
|
struct var_stack_t;
|
||||||
class env_node_t;
|
class env_node_t;
|
||||||
class env_stack_t : public environment_t {
|
class env_stack_t : public environment_t {
|
||||||
|
friend class parser_t;
|
||||||
std::unique_ptr<var_stack_t> vars_;
|
std::unique_ptr<var_stack_t> vars_;
|
||||||
|
|
||||||
int set_internal(const wcstring &key, env_mode_flags_t var_mode, wcstring_list_t val);
|
int set_internal(const wcstring &key, env_mode_flags_t var_mode, wcstring_list_t val);
|
||||||
|
|
||||||
bool try_remove(env_node_t *n, const wchar_t *key, int var_mode);
|
bool try_remove(std::shared_ptr<env_node_t> n, const wchar_t *key, int var_mode);
|
||||||
env_node_t *get_node(const wcstring &key);
|
std::shared_ptr<env_node_t> get_node(const wcstring &key);
|
||||||
|
|
||||||
var_stack_t &vars_stack();
|
var_stack_t &vars_stack();
|
||||||
const var_stack_t &vars_stack() const;
|
const var_stack_t &vars_stack() const;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user