diff --git a/crates/steel-core/src/parser/expand_visitor.rs b/crates/steel-core/src/parser/expand_visitor.rs index 57d2421ef..eb532c969 100644 --- a/crates/steel-core/src/parser/expand_visitor.rs +++ b/crates/steel-core/src/parser/expand_visitor.rs @@ -7,6 +7,7 @@ use crate::compiler::passes::Folder; use crate::compiler::program::REQUIRE_DYLIB; use crate::parser::ast::ExprKind; use crate::parser::parser::SyntaxObject; +use crate::parser::span_visitor::get_span; use crate::steel_vm::builtin::BuiltInModule; use crate::steel_vm::engine::ModuleContainer; use crate::{compiler::program::REQUIRE_BUILTIN, rvals::Result}; @@ -219,6 +220,7 @@ pub fn expand_kernel_in_env( changed: false, builtin_modules, environment: Some(Cow::from(env)), + depth: 0, } .visit(expr) } @@ -233,6 +235,7 @@ pub fn expand_kernel( changed: false, builtin_modules, environment: None, + depth: 0, } .visit(expr) } @@ -242,6 +245,7 @@ pub struct KernelExpander<'a> { pub(crate) changed: bool, builtin_modules: ModuleContainer, environment: Option>, + depth: usize, } impl<'a> KernelExpander<'a> { @@ -251,6 +255,7 @@ impl<'a> KernelExpander<'a> { changed: false, builtin_modules, environment: None, + depth: 0, } } @@ -719,7 +724,14 @@ impl<'a> ConsumingVisitor for KernelExpander<'a> { .unwrap_or("default"), )?; self.changed = true; - return self.visit(expanded); + + self.depth += 1; + + let result = self.visit(expanded); + + self.depth -= 1; + + return result; } } @@ -918,6 +930,32 @@ impl<'a> ConsumingVisitor for KernelExpander<'a> { Ok(ExprKind::Let(l)) } + + fn visit(&mut self, expr: ExprKind) -> Self::Output { + if self.depth > 96 { + stop!(BadSyntax => "Current expansion depth of defmacro style macros exceeded: depth capped at 96"; get_span(&expr)); + } + + let res = match expr { + ExprKind::If(f) => self.visit_if(f), + ExprKind::Define(d) => self.visit_define(d), + ExprKind::LambdaFunction(l) => self.visit_lambda_function(l), + ExprKind::Begin(b) => self.visit_begin(b), + ExprKind::Return(r) => self.visit_return(r), + ExprKind::Let(l) => self.visit_let(l), + ExprKind::Quote(q) => self.visit_quote(q), + ExprKind::Macro(m) => self.visit_macro(m), + ExprKind::Atom(a) => self.visit_atom(a), + ExprKind::List(l) => self.visit_list(l), + ExprKind::SyntaxRules(s) => self.visit_syntax_rules(s), + ExprKind::Set(s) => self.visit_set(s), + ExprKind::Require(r) => self.visit_require(r), + }; + + // self.depth -= 1; + + res + } } fn _define_quoted_ast_node(ast_name: ExprKind, expanded_expr: &ExprKind) -> ExprKind { diff --git a/crates/steel-core/src/tests/failure/capped_depth_defmacro.scm b/crates/steel-core/src/tests/failure/capped_depth_defmacro.scm new file mode 100644 index 000000000..74c38b0fa --- /dev/null +++ b/crates/steel-core/src/tests/failure/capped_depth_defmacro.scm @@ -0,0 +1,4 @@ +;; Should stop before we get a stack overflow +(defmacro (foo x) x) + +(foo (define bar 10)) diff --git a/crates/steel-core/src/tests/mod.rs b/crates/steel-core/src/tests/mod.rs index 6f1f54820..4a30fedb8 100644 --- a/crates/steel-core/src/tests/mod.rs +++ b/crates/steel-core/src/tests/mod.rs @@ -123,6 +123,7 @@ test_harness_success! { } test_harness_failure! { + capped_depth_defmacro, function_used_before_definition, identifier_used_before_definition, local_struct_inaccessible,