2022-01-04 22:30:34 +00:00
|
|
|
use std::collections::{HashMap, HashSet};
|
2021-08-15 22:33:34 +00:00
|
|
|
|
2022-05-20 21:49:42 +00:00
|
|
|
use crate::engine::EngineState;
|
|
|
|
use crate::engine::DEFAULT_OVERLAY_NAME;
|
2022-04-18 22:28:01 +00:00
|
|
|
use crate::{ShellError, Span, Value, VarId};
|
2021-08-15 22:33:34 +00:00
|
|
|
|
2022-05-07 19:39:22 +00:00
|
|
|
/// Environment variables per overlay
|
|
|
|
pub type EnvVars = HashMap<String, HashMap<String, Value>>;
|
|
|
|
|
2023-02-11 21:35:48 +00:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct ProfilingConfig {
|
|
|
|
pub max_depth: i64,
|
|
|
|
pub depth: i64,
|
|
|
|
pub collect_source: bool,
|
|
|
|
pub collect_values: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ProfilingConfig {
|
|
|
|
pub fn new(max_depth: i64, collect_source: bool, collect_values: bool) -> Self {
|
|
|
|
ProfilingConfig {
|
|
|
|
max_depth,
|
|
|
|
depth: 0,
|
|
|
|
collect_source,
|
|
|
|
collect_values,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn enter_block(&mut self) {
|
|
|
|
self.depth += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn leave_block(&mut self) {
|
|
|
|
self.depth -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn should_debug(&self) -> bool {
|
|
|
|
self.depth <= self.max_depth
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn reset(&mut self) {
|
|
|
|
self.max_depth = 0;
|
|
|
|
self.depth = 0;
|
|
|
|
self.collect_source = false;
|
|
|
|
self.collect_values = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-02 19:53:48 +00:00
|
|
|
/// A runtime value stack used during evaluation
|
|
|
|
///
|
|
|
|
/// A note on implementation:
|
|
|
|
///
|
|
|
|
/// We previously set up the stack in a traditional way, where stack frames had parents which would
|
|
|
|
/// represent other frames that you might return to when exiting a function.
|
|
|
|
///
|
|
|
|
/// While experimenting with blocks, we found that we needed to have closure captures of variables
|
|
|
|
/// seen outside of the blocks, so that they blocks could be run in a way that was both thread-safe
|
|
|
|
/// and followed the restrictions for closures applied to iterators. The end result left us with
|
|
|
|
/// closure-captured single stack frames that blocks could see.
|
|
|
|
///
|
|
|
|
/// Blocks make up the only scope and stack definition abstraction in Nushell. As a result, we were
|
|
|
|
/// creating closure captures at any point we wanted to have a Block value we could safely evaluate
|
|
|
|
/// in any context. This meant that the parents were going largely unused, with captured variables
|
|
|
|
/// taking their place. The end result is this, where we no longer have separate frames, but instead
|
|
|
|
/// use the Stack as a way of representing the local and closure-captured state.
|
2021-10-25 04:01:02 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2021-10-25 20:04:23 +00:00
|
|
|
pub struct Stack {
|
2021-11-30 06:14:05 +00:00
|
|
|
/// Variables
|
2021-08-15 22:33:34 +00:00
|
|
|
pub vars: HashMap<VarId, Value>,
|
2021-11-30 06:14:05 +00:00
|
|
|
/// Environment variables arranged as a stack to be able to recover values from parent scopes
|
2022-05-07 19:39:22 +00:00
|
|
|
pub env_vars: Vec<EnvVars>,
|
|
|
|
/// Tells which environment variables from engine state are hidden, per overlay.
|
|
|
|
pub env_hidden: HashMap<String, HashSet<String>>,
|
|
|
|
/// List of active overlays
|
|
|
|
pub active_overlays: Vec<String>,
|
2023-01-05 02:38:50 +00:00
|
|
|
pub recursion_count: Box<u64>,
|
2023-02-11 21:35:48 +00:00
|
|
|
pub profiling_config: ProfilingConfig,
|
2021-08-15 22:33:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Stack {
|
|
|
|
pub fn new() -> Stack {
|
2021-10-25 20:04:23 +00:00
|
|
|
Stack {
|
2021-08-15 22:33:34 +00:00
|
|
|
vars: HashMap::new(),
|
2021-11-30 06:14:05 +00:00
|
|
|
env_vars: vec![],
|
2022-05-07 19:39:22 +00:00
|
|
|
env_hidden: HashMap::new(),
|
|
|
|
active_overlays: vec![DEFAULT_OVERLAY_NAME.to_string()],
|
2023-01-05 02:38:50 +00:00
|
|
|
recursion_count: Box::new(0),
|
2023-02-11 21:35:48 +00:00
|
|
|
profiling_config: ProfilingConfig::new(0, false, false),
|
2022-01-04 22:30:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-07 19:39:22 +00:00
|
|
|
pub fn with_env(
|
|
|
|
&mut self,
|
|
|
|
env_vars: &[EnvVars],
|
|
|
|
env_hidden: &HashMap<String, HashSet<String>>,
|
|
|
|
) {
|
2022-01-05 22:21:26 +00:00
|
|
|
// Do not clone the environment if it hasn't changed
|
|
|
|
if self.env_vars.iter().any(|scope| !scope.is_empty()) {
|
2022-01-04 22:30:34 +00:00
|
|
|
self.env_vars = env_vars.to_owned();
|
|
|
|
}
|
|
|
|
|
2022-01-05 22:21:26 +00:00
|
|
|
if !self.env_hidden.is_empty() {
|
2022-05-07 19:39:22 +00:00
|
|
|
self.env_hidden = env_hidden.to_owned();
|
2021-10-25 20:04:23 +00:00
|
|
|
}
|
2021-08-15 22:33:34 +00:00
|
|
|
}
|
2021-11-15 23:16:06 +00:00
|
|
|
|
2022-01-29 13:00:48 +00:00
|
|
|
pub fn get_var(&self, var_id: VarId, span: Span) -> Result<Value, ShellError> {
|
2021-10-25 20:04:23 +00:00
|
|
|
if let Some(v) = self.vars.get(&var_id) {
|
2022-01-29 13:00:48 +00:00
|
|
|
return Ok(v.clone().with_span(span));
|
2021-08-15 22:33:34 +00:00
|
|
|
}
|
2021-11-14 19:25:57 +00:00
|
|
|
|
2022-01-29 13:00:48 +00:00
|
|
|
Err(ShellError::VariableNotFoundAtRuntime(span))
|
2021-08-15 22:33:34 +00:00
|
|
|
}
|
|
|
|
|
2022-02-05 14:39:51 +00:00
|
|
|
pub fn get_var_with_origin(&self, var_id: VarId, span: Span) -> Result<Value, ShellError> {
|
|
|
|
if let Some(v) = self.vars.get(&var_id) {
|
|
|
|
return Ok(v.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
Err(ShellError::VariableNotFoundAtRuntime(span))
|
|
|
|
}
|
|
|
|
|
2021-10-25 04:01:02 +00:00
|
|
|
pub fn add_var(&mut self, var_id: VarId, value: Value) {
|
2021-10-25 20:04:23 +00:00
|
|
|
self.vars.insert(var_id, value);
|
2021-08-15 22:33:34 +00:00
|
|
|
}
|
|
|
|
|
2021-12-17 01:04:54 +00:00
|
|
|
pub fn add_env_var(&mut self, var: String, value: Value) {
|
2022-05-07 19:39:22 +00:00
|
|
|
if let Some(last_overlay) = self.active_overlays.last() {
|
|
|
|
if let Some(env_hidden) = self.env_hidden.get_mut(last_overlay) {
|
|
|
|
// if the env var was hidden, let's activate it again
|
|
|
|
env_hidden.remove(&var);
|
|
|
|
}
|
2022-01-04 22:30:34 +00:00
|
|
|
|
2022-05-07 19:39:22 +00:00
|
|
|
if let Some(scope) = self.env_vars.last_mut() {
|
|
|
|
if let Some(env_vars) = scope.get_mut(last_overlay) {
|
|
|
|
env_vars.insert(var, value);
|
|
|
|
} else {
|
|
|
|
scope.insert(last_overlay.into(), HashMap::from([(var, value)]));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self.env_vars.push(HashMap::from([(
|
|
|
|
last_overlay.into(),
|
|
|
|
HashMap::from([(var, value)]),
|
|
|
|
)]));
|
|
|
|
}
|
2021-11-30 06:14:05 +00:00
|
|
|
} else {
|
2022-05-07 19:39:22 +00:00
|
|
|
// TODO: Remove panic
|
|
|
|
panic!("internal error: no active overlay");
|
2021-11-30 06:14:05 +00:00
|
|
|
}
|
2021-08-15 22:33:34 +00:00
|
|
|
}
|
|
|
|
|
2022-05-07 19:39:22 +00:00
|
|
|
pub fn last_overlay_name(&self) -> Result<String, ShellError> {
|
|
|
|
self.active_overlays
|
|
|
|
.last()
|
|
|
|
.cloned()
|
|
|
|
.ok_or_else(|| ShellError::NushellFailed("No active overlay".into()))
|
|
|
|
}
|
2022-01-12 04:06:56 +00:00
|
|
|
|
2022-05-07 19:39:22 +00:00
|
|
|
pub fn captures_to_stack(&self, captures: &HashMap<VarId, Value>) -> Stack {
|
2022-01-12 04:06:56 +00:00
|
|
|
// FIXME: this is probably slow
|
2022-05-07 19:39:22 +00:00
|
|
|
let mut env_vars = self.env_vars.clone();
|
|
|
|
env_vars.push(HashMap::new());
|
2022-01-12 04:06:56 +00:00
|
|
|
|
2022-05-07 19:39:22 +00:00
|
|
|
Stack {
|
|
|
|
vars: captures.clone(),
|
|
|
|
env_vars,
|
|
|
|
env_hidden: HashMap::new(),
|
|
|
|
active_overlays: self.active_overlays.clone(),
|
2023-01-05 02:38:50 +00:00
|
|
|
recursion_count: self.recursion_count.to_owned(),
|
2023-02-11 21:35:48 +00:00
|
|
|
profiling_config: self.profiling_config.clone(),
|
2022-05-07 19:39:22 +00:00
|
|
|
}
|
2022-01-12 04:06:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gather_captures(&self, captures: &[VarId]) -> Stack {
|
2022-05-07 19:39:22 +00:00
|
|
|
let mut vars = HashMap::new();
|
2021-10-25 20:04:23 +00:00
|
|
|
|
2022-01-29 13:00:48 +00:00
|
|
|
let fake_span = Span::new(0, 0);
|
|
|
|
|
2021-10-25 20:04:23 +00:00
|
|
|
for capture in captures {
|
2021-10-25 21:14:21 +00:00
|
|
|
// Note: this assumes we have calculated captures correctly and that commands
|
|
|
|
// that take in a var decl will manually set this into scope when running the blocks
|
2022-01-29 13:00:48 +00:00
|
|
|
if let Ok(value) = self.get_var(*capture, fake_span) {
|
2022-05-07 19:39:22 +00:00
|
|
|
vars.insert(*capture, value);
|
2021-10-25 21:14:21 +00:00
|
|
|
}
|
2021-10-25 20:04:23 +00:00
|
|
|
}
|
2021-10-25 04:01:02 +00:00
|
|
|
|
2022-05-07 19:39:22 +00:00
|
|
|
let mut env_vars = self.env_vars.clone();
|
|
|
|
env_vars.push(HashMap::new());
|
2021-11-04 02:32:35 +00:00
|
|
|
|
2022-05-07 19:39:22 +00:00
|
|
|
Stack {
|
|
|
|
vars,
|
|
|
|
env_vars,
|
|
|
|
env_hidden: HashMap::new(),
|
|
|
|
active_overlays: self.active_overlays.clone(),
|
2023-01-05 02:38:50 +00:00
|
|
|
recursion_count: self.recursion_count.to_owned(),
|
2023-02-11 21:35:48 +00:00
|
|
|
profiling_config: self.profiling_config.clone(),
|
2022-05-07 19:39:22 +00:00
|
|
|
}
|
2021-08-15 22:33:34 +00:00
|
|
|
}
|
|
|
|
|
2021-11-30 06:14:05 +00:00
|
|
|
/// Flatten the env var scope frames into one frame
|
2022-01-04 22:30:34 +00:00
|
|
|
pub fn get_env_vars(&self, engine_state: &EngineState) -> HashMap<String, Value> {
|
2022-05-07 19:39:22 +00:00
|
|
|
let mut result = HashMap::new();
|
|
|
|
|
|
|
|
for active_overlay in self.active_overlays.iter() {
|
|
|
|
if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
|
|
|
|
result.extend(
|
|
|
|
env_vars
|
|
|
|
.iter()
|
|
|
|
.filter(|(k, _)| {
|
|
|
|
if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
|
|
|
|
!env_hidden.contains(*k)
|
|
|
|
} else {
|
|
|
|
// nothing has been hidden in this overlay
|
|
|
|
true
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.map(|(k, v)| (k.clone(), v.clone()))
|
|
|
|
.collect::<HashMap<String, Value>>(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result.extend(self.get_stack_env_vars());
|
|
|
|
|
|
|
|
result
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get flattened environment variables only from the stack
|
|
|
|
pub fn get_stack_env_vars(&self) -> HashMap<String, Value> {
|
|
|
|
let mut result = HashMap::new();
|
2021-11-30 06:14:05 +00:00
|
|
|
|
|
|
|
for scope in &self.env_vars {
|
2022-05-07 19:39:22 +00:00
|
|
|
for active_overlay in self.active_overlays.iter() {
|
|
|
|
if let Some(env_vars) = scope.get(active_overlay) {
|
|
|
|
result.extend(env_vars.clone());
|
|
|
|
}
|
|
|
|
}
|
2021-11-30 06:14:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
result
|
2021-09-19 19:29:58 +00:00
|
|
|
}
|
|
|
|
|
2022-05-24 21:22:17 +00:00
|
|
|
/// Get flattened environment variables only from the stack and one overlay
|
|
|
|
pub fn get_stack_overlay_env_vars(&self, overlay_name: &str) -> HashMap<String, Value> {
|
|
|
|
let mut result = HashMap::new();
|
|
|
|
|
|
|
|
for scope in &self.env_vars {
|
|
|
|
if let Some(active_overlay) = self.active_overlays.iter().find(|n| n == &overlay_name) {
|
|
|
|
if let Some(env_vars) = scope.get(active_overlay) {
|
|
|
|
result.extend(env_vars.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result
|
|
|
|
}
|
|
|
|
|
2022-02-04 18:02:03 +00:00
|
|
|
/// Same as get_env_vars, but returns only the names as a HashSet
|
|
|
|
pub fn get_env_var_names(&self, engine_state: &EngineState) -> HashSet<String> {
|
2022-05-07 19:39:22 +00:00
|
|
|
let mut result = HashSet::new();
|
|
|
|
|
|
|
|
for active_overlay in self.active_overlays.iter() {
|
|
|
|
if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
|
|
|
|
result.extend(
|
|
|
|
env_vars
|
|
|
|
.keys()
|
|
|
|
.filter(|k| {
|
|
|
|
if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
|
|
|
|
!env_hidden.contains(*k)
|
|
|
|
} else {
|
|
|
|
// nothing has been hidden in this overlay
|
|
|
|
true
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.cloned()
|
|
|
|
.collect::<HashSet<String>>(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2022-02-04 18:02:03 +00:00
|
|
|
|
|
|
|
for scope in &self.env_vars {
|
2022-05-07 19:39:22 +00:00
|
|
|
for active_overlay in self.active_overlays.iter() {
|
|
|
|
if let Some(env_vars) = scope.get(active_overlay) {
|
|
|
|
result.extend(env_vars.keys().cloned().collect::<HashSet<String>>());
|
|
|
|
}
|
|
|
|
}
|
2022-02-04 18:02:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
result
|
|
|
|
}
|
|
|
|
|
2022-01-04 22:30:34 +00:00
|
|
|
pub fn get_env_var(&self, engine_state: &EngineState, name: &str) -> Option<Value> {
|
2021-11-30 06:14:05 +00:00
|
|
|
for scope in self.env_vars.iter().rev() {
|
2022-05-07 19:39:22 +00:00
|
|
|
for active_overlay in self.active_overlays.iter().rev() {
|
|
|
|
if let Some(env_vars) = scope.get(active_overlay) {
|
|
|
|
if let Some(v) = env_vars.get(name) {
|
|
|
|
return Some(v.clone());
|
|
|
|
}
|
|
|
|
}
|
2021-11-30 06:14:05 +00:00
|
|
|
}
|
2021-10-25 04:01:02 +00:00
|
|
|
}
|
2021-11-30 06:14:05 +00:00
|
|
|
|
2022-05-07 19:39:22 +00:00
|
|
|
for active_overlay in self.active_overlays.iter().rev() {
|
|
|
|
let is_hidden = if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
|
|
|
|
env_hidden.contains(name)
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
};
|
|
|
|
|
|
|
|
if !is_hidden {
|
|
|
|
if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
|
|
|
|
if let Some(v) = env_vars.get(name) {
|
|
|
|
return Some(v.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-01-04 22:30:34 +00:00
|
|
|
}
|
2022-05-07 19:39:22 +00:00
|
|
|
|
|
|
|
None
|
2021-10-02 13:10:28 +00:00
|
|
|
}
|
|
|
|
|
2022-02-04 18:02:03 +00:00
|
|
|
pub fn has_env_var(&self, engine_state: &EngineState, name: &str) -> bool {
|
|
|
|
for scope in self.env_vars.iter().rev() {
|
2022-05-07 19:39:22 +00:00
|
|
|
for active_overlay in self.active_overlays.iter().rev() {
|
|
|
|
if let Some(env_vars) = scope.get(active_overlay) {
|
|
|
|
if env_vars.contains_key(name) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2022-02-04 18:02:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-07 19:39:22 +00:00
|
|
|
for active_overlay in self.active_overlays.iter().rev() {
|
|
|
|
let is_hidden = if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
|
|
|
|
env_hidden.contains(name)
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
};
|
|
|
|
|
|
|
|
if !is_hidden {
|
|
|
|
if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
|
|
|
|
if env_vars.contains_key(name) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-02-04 18:02:03 +00:00
|
|
|
}
|
2022-05-07 19:39:22 +00:00
|
|
|
|
|
|
|
false
|
2022-02-04 18:02:03 +00:00
|
|
|
}
|
|
|
|
|
2022-01-04 22:30:34 +00:00
|
|
|
pub fn remove_env_var(&mut self, engine_state: &EngineState, name: &str) -> Option<Value> {
|
2021-11-30 06:14:05 +00:00
|
|
|
for scope in self.env_vars.iter_mut().rev() {
|
2022-05-07 19:39:22 +00:00
|
|
|
for active_overlay in self.active_overlays.iter().rev() {
|
|
|
|
if let Some(env_vars) = scope.get_mut(active_overlay) {
|
|
|
|
if let Some(v) = env_vars.remove(name) {
|
|
|
|
return Some(v);
|
|
|
|
}
|
|
|
|
}
|
2021-11-30 06:14:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-07 19:39:22 +00:00
|
|
|
for active_overlay in self.active_overlays.iter().rev() {
|
|
|
|
if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
|
|
|
|
if let Some(val) = env_vars.get(name) {
|
|
|
|
if let Some(env_hidden) = self.env_hidden.get_mut(active_overlay) {
|
|
|
|
env_hidden.insert(name.into());
|
|
|
|
} else {
|
|
|
|
self.env_hidden
|
|
|
|
.insert(active_overlay.into(), HashSet::from([name.into()]));
|
|
|
|
}
|
|
|
|
|
|
|
|
return Some(val.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn has_env_overlay(&self, name: &str, engine_state: &EngineState) -> bool {
|
|
|
|
for scope in self.env_vars.iter().rev() {
|
|
|
|
if scope.contains_key(name) {
|
|
|
|
return true;
|
2021-11-30 06:14:05 +00:00
|
|
|
}
|
2021-08-15 22:33:34 +00:00
|
|
|
}
|
2022-05-07 19:39:22 +00:00
|
|
|
|
|
|
|
engine_state.env_vars.contains_key(name)
|
|
|
|
}
|
|
|
|
|
2022-05-24 21:22:17 +00:00
|
|
|
pub fn is_overlay_active(&self, name: &String) -> bool {
|
|
|
|
self.active_overlays.contains(name)
|
|
|
|
}
|
|
|
|
|
2022-05-07 19:39:22 +00:00
|
|
|
pub fn add_overlay(&mut self, name: String) {
|
|
|
|
self.active_overlays.retain(|o| o != &name);
|
|
|
|
self.active_overlays.push(name);
|
|
|
|
}
|
|
|
|
|
2022-05-24 21:22:17 +00:00
|
|
|
pub fn remove_overlay(&mut self, name: &String) {
|
2022-05-07 19:39:22 +00:00
|
|
|
self.active_overlays.retain(|o| o != name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Stack {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::new()
|
2021-08-15 22:33:34 +00:00
|
|
|
}
|
|
|
|
}
|