Fixed stack overflow error caused by improper cleanup of block scoped variables
This commit is contained in:
@@ -1252,7 +1252,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
fn expression_block<'v>(
|
fn expression_block<'v>(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut expr: BlockExpression,
|
mut expr: BlockExpression,
|
||||||
scope: &mut VariableScope<'v>,
|
parent_scope: &mut VariableScope<'v>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// First, sort the expressions to ensure functions are hoisted
|
// First, sort the expressions to ensure functions are hoisted
|
||||||
expr.0.sort_by(|a, b| {
|
expr.0.sort_by(|a, b| {
|
||||||
@@ -1267,10 +1267,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let mut scope = VariableScope::scoped(parent_scope);
|
||||||
|
|
||||||
for expr in expr.0 {
|
for expr in expr.0 {
|
||||||
if !self.declared_main
|
if !self.declared_main
|
||||||
&& !matches!(expr.node, Expression::Function(_))
|
&& !matches!(expr.node, Expression::Function(_))
|
||||||
&& !scope.has_parent()
|
&& !parent_scope.has_parent()
|
||||||
{
|
{
|
||||||
self.write_output("main:")?;
|
self.write_output("main:")?;
|
||||||
self.declared_main = true;
|
self.declared_main = true;
|
||||||
@@ -1278,11 +1280,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
|
|
||||||
match expr.node {
|
match expr.node {
|
||||||
Expression::Return(ret_expr) => {
|
Expression::Return(ret_expr) => {
|
||||||
self.expression_return(*ret_expr, scope)?;
|
self.expression_return(*ret_expr, &mut scope)?;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Swallow errors within expressions so block can continue
|
// Swallow errors within expressions so block can continue
|
||||||
if let Err(e) = self.expression(expr, scope).and_then(|result| {
|
if let Err(e) = self.expression(expr, &mut scope).and_then(|result| {
|
||||||
// If the expression was a statement that returned a temp result (e.g. `1 + 2;` line),
|
// If the expression was a statement that returned a temp result (e.g. `1 + 2;` line),
|
||||||
// we must free it to avoid leaking registers.
|
// we must free it to avoid leaking registers.
|
||||||
if let Some(comp_res) = result
|
if let Some(comp_res) = result
|
||||||
@@ -1298,6 +1300,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if scope.stack_offset() > 0 {
|
||||||
|
self.write_output(format!("sub sp sp {}", scope.stack_offset()))?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1700,4 +1706,3 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ impl<'a> VariableScope<'a> {
|
|||||||
pub fn scoped(parent: &'a VariableScope<'a>) -> Self {
|
pub fn scoped(parent: &'a VariableScope<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
parent: Option::Some(parent),
|
parent: Option::Some(parent),
|
||||||
|
temporary_vars: parent.temporary_vars.clone(),
|
||||||
|
persistant_vars: parent.persistant_vars.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,26 +142,34 @@ impl<'a> VariableScope<'a> {
|
|||||||
Ok(var_location)
|
Ok(var_location)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_location_of(
|
pub fn get_location_of(&self, var_name: impl Into<String>) -> Result<VariableLocation, Error> {
|
||||||
&mut self,
|
|
||||||
var_name: impl Into<String>,
|
|
||||||
) -> Result<VariableLocation, Error> {
|
|
||||||
let var_name = var_name.into();
|
let var_name = var_name.into();
|
||||||
let var = self
|
|
||||||
.var_lookup_table
|
|
||||||
.get(var_name.as_str())
|
|
||||||
.cloned()
|
|
||||||
.ok_or(Error::UnknownVariable(var_name))?;
|
|
||||||
|
|
||||||
|
// 1. Check this scope
|
||||||
|
if let Some(var) = self.var_lookup_table.get(var_name.as_str()) {
|
||||||
if let VariableLocation::Stack(inserted_at_offset) = var {
|
if let VariableLocation::Stack(inserted_at_offset) = var {
|
||||||
Ok(VariableLocation::Stack(
|
// Return offset relative to CURRENT sp
|
||||||
|
return Ok(VariableLocation::Stack(
|
||||||
self.stack_offset - inserted_at_offset,
|
self.stack_offset - inserted_at_offset,
|
||||||
))
|
));
|
||||||
} else {
|
} else {
|
||||||
Ok(var)
|
return Ok(var.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. Recursively check parent
|
||||||
|
if let Some(parent) = self.parent {
|
||||||
|
let loc = parent.get_location_of(var_name)?;
|
||||||
|
|
||||||
|
if let VariableLocation::Stack(parent_offset) = loc {
|
||||||
|
return Ok(VariableLocation::Stack(parent_offset + self.stack_offset));
|
||||||
|
}
|
||||||
|
return Ok(loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(Error::UnknownVariable(var_name))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn has_parent(&self) -> bool {
|
pub fn has_parent(&self) -> bool {
|
||||||
self.parent.is_some()
|
self.parent.is_some()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user