Limit recursion to avoid stack overflow (#7657)

Add recursion limit to `def` and `block`.
Summary of this PR , it will detect if `def` call itself or not .
Then execute by using `stack` which I think best choice to use with this
design and core as it is available in all crates and mutable and
calculate the recursion limit on calling `def`.
Set 50 as recursion limit on `Config`.
Add some tests too .

Fixes #5899

Co-authored-by: Reilly Wood <reilly.wood@icloud.com>
This commit is contained in:
Amirhossein Akhlaghpour
2023-01-04 21:38:50 -05:00
committed by GitHub
parent 9bc4e6794d
commit 00469de93e
6 changed files with 85 additions and 0 deletions

View File

@ -11,6 +11,7 @@ pub struct Block {
pub captures: Vec<VarId>,
pub redirect_env: bool,
pub span: Option<Span>, // None option encodes no span to avoid using test_span()
pub recursive: Option<bool>, // does the block call itself?
}
impl Block {
@ -51,6 +52,7 @@ impl Block {
captures: vec![],
redirect_env: false,
span: None,
recursive: None,
}
}
}
@ -66,6 +68,7 @@ where
captures: vec![],
redirect_env: false,
span: None,
recursive: None,
}
}
}

View File

@ -34,6 +34,7 @@ pub struct Stack {
pub env_hidden: HashMap<String, HashSet<String>>,
/// List of active overlays
pub active_overlays: Vec<String>,
pub recursion_count: Box<u64>,
}
impl Stack {
@ -43,6 +44,7 @@ impl Stack {
env_vars: vec![],
env_hidden: HashMap::new(),
active_overlays: vec![DEFAULT_OVERLAY_NAME.to_string()],
recursion_count: Box::new(0),
}
}
@ -123,6 +125,7 @@ impl Stack {
env_vars,
env_hidden: HashMap::new(),
active_overlays: self.active_overlays.clone(),
recursion_count: self.recursion_count.to_owned(),
}
}
@ -147,6 +150,7 @@ impl Stack {
env_vars,
env_hidden: HashMap::new(),
active_overlays: self.active_overlays.clone(),
recursion_count: self.recursion_count.to_owned(),
}
}

View File

@ -903,6 +903,19 @@ Either make sure {0} is a string, or add a 'to_string' entry for it in ENV_CONVE
/// Return event, which may become an error if used outside of a function
#[error("Return used outside of function")]
Return(#[label = "used outside of function"] Span, Box<Value>),
/// The code being executed called itself too many times.
///
/// ## Resolution
///
/// Adjust your Nu code to
#[error("Recursion limit ({recursion_limit}) reached")]
#[diagnostic(code(nu::shell::recursion_limit_reached), url(docsrs))]
RecursionLimitReached {
recursion_limit: u64,
#[label("This called itself too many times")]
span: Option<Span>,
},
}
impl From<std::io::Error> for ShellError {