Skip to content

Commit 8e19c73

Browse files
authored
fix: handle nested macro invocation for different levels (#142)
1 parent 9f6a077 commit 8e19c73

File tree

12 files changed

+1460
-840
lines changed

12 files changed

+1460
-840
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
- Fix macro argument scoping bug where parameters leaked across macro boundaries (fixes #139).
1010
- Macros can no longer access parameters from parent scopes unless explicitly passed.
1111
- Example: If `M1(arg)` calls `M2()` without passing `arg`, then `M2` cannot use `<arg>`.
12+
- Fix nested macro invocation resulted in stack overflow (fixes #140).
13+
- Example: `M1(M1(M1(<a>)))`
1214

1315
## [1.5.3] - 2025-11-06
1416
- Introducing `--relax-jumps` CLI flag to optimize jump instructions from PUSH2 to PUSH1.

bin/hnc/src/main.rs

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -170,18 +170,10 @@ fn main() {
170170
});
171171

172172
// Recurse through the macro and generate bytecode
173-
let bytecode_res: BytecodeRes = Codegen::macro_to_bytecode(
174-
&evm_version,
175-
macro_def,
176-
contract,
177-
&mut vec![macro_def],
178-
0,
179-
&mut Vec::default(),
180-
false,
181-
None,
182-
false,
183-
)
184-
.unwrap();
173+
let mut scope_mgr = huff_neo_utils::scope::ScopeManager::new();
174+
scope_mgr.push_macro(macro_def, 0);
175+
let bytecode_res: BytecodeRes =
176+
Codegen::macro_to_bytecode(&evm_version, macro_def, contract, &mut scope_mgr, 0, false, None, false).unwrap();
185177

186178
if bytecode_res.label_indices.is_empty() {
187179
eprintln!(
@@ -204,7 +196,7 @@ fn main() {
204196
Cell::new(format!(
205197
"{}{}",
206198
label,
207-
if sl.scope_depth > 0 { format!(" (scope {})", sl.scope_depth) } else { String::new() }
199+
if sl.scope_id.depth > 0 { format!(" (scope {:?})", sl.scope_id.path) } else { String::new() }
208200
)),
209201
Cell::new(format!("{:#04x}", sl.offset)),
210202
])

crates/codegen/src/irgen/arg_calls.rs

Lines changed: 615 additions & 556 deletions
Large diffs are not rendered by default.

crates/codegen/src/irgen/builtin_function.rs

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ use huff_neo_utils::bytes_util::{bytes32_to_hex_string, format_even_bytes, pad_n
1010
use huff_neo_utils::error::{CodegenError, CodegenErrorKind};
1111
use huff_neo_utils::evm_version::EVMVersion;
1212
use huff_neo_utils::prelude::{
13-
Argument, AstSpan, BuiltinFunctionArg, BuiltinFunctionCall, BuiltinFunctionKind, Contract, MacroDefinition, MacroInvocation, PushValue,
14-
TableDefinition,
13+
Argument, AstSpan, BuiltinFunctionArg, BuiltinFunctionCall, BuiltinFunctionKind, Contract, MacroDefinition, PushValue, TableDefinition,
1514
};
15+
use huff_neo_utils::scope::ScopeManager;
1616

1717
/// Creates a CodegenError for invalid arguments
1818
fn invalid_arguments_error(msg: impl Into<String>, span: &AstSpan) -> CodegenError {
@@ -72,9 +72,8 @@ pub fn builtin_function_gen<'a>(
7272
evm_version: &EVMVersion,
7373
contract: &'a Contract,
7474
macro_def: &MacroDefinition,
75-
scope: &mut Vec<&'a MacroDefinition>,
75+
scope_mgr: &mut ScopeManager<'a>,
7676
offset: &mut usize,
77-
mis: &mut Vec<(usize, MacroInvocation)>,
7877
table_instances: &mut Jumps,
7978
utilized_tables: &mut Vec<TableDefinition>,
8079
circular_codesize_invocations: &mut CircularCodeSizeIndices,
@@ -87,7 +86,7 @@ pub fn builtin_function_gen<'a>(
8786
match bf.kind {
8887
BuiltinFunctionKind::Codesize => {
8988
let (codesize_offset, bytes_variant) =
90-
codesize(evm_version, contract, macro_def, scope, offset, mis, circular_codesize_invocations, bf, relax_jumps)?;
89+
codesize(evm_version, contract, macro_def, scope_mgr, offset, circular_codesize_invocations, bf, relax_jumps)?;
9190

9291
*offset += codesize_offset;
9392
bytes.push_with_offset(starting_offset, bytes_variant);
@@ -107,20 +106,12 @@ pub fn builtin_function_gen<'a>(
107106
// Make sure the table exists
108107
if let Some(t) = contract.find_table_by_name(first_arg.name.as_ref().unwrap()) {
109108
tracing::debug!(target: "codegen", "Creating table instance for {} at offset {}", first_arg.name.as_ref().unwrap(), *offset);
110-
let scope_path: Vec<String> = if scope.len() > 1 {
111-
let mut path: Vec<String> = scope[..scope.len() - 1].iter().map(|m| m.name.clone()).collect();
112-
path.push(format!("{}_{}", scope.last().unwrap().name, *offset));
113-
path
114-
} else {
115-
scope.iter().map(|m| m.name.clone()).collect()
116-
};
117-
let scope_depth = scope.len().saturating_sub(1);
109+
let scope_id = scope_mgr.current_scope();
118110
table_instances.push(Jump {
119111
label: first_arg.name.as_ref().unwrap().to_owned(),
120112
bytecode_index: *offset,
121113
span: bf.span.clone(),
122-
scope_depth,
123-
scope_path,
114+
scope_id,
124115
});
125116
if !utilized_tables.contains(&t) {
126117
utilized_tables.push(t);
@@ -313,9 +304,8 @@ fn codesize<'a>(
313304
evm_version: &EVMVersion,
314305
contract: &'a Contract,
315306
macro_def: &MacroDefinition,
316-
scope: &mut Vec<&'a MacroDefinition>,
307+
scope_mgr: &mut ScopeManager<'a>,
317308
offset: &mut usize,
318-
mis: &mut Vec<(usize, MacroInvocation)>,
319309
circular_codesize_invocations: &mut CircularCodeSizeIndices,
320310
bf: &BuiltinFunctionCall,
321311
relax_jumps: bool,
@@ -338,7 +328,7 @@ fn codesize<'a>(
338328

339329
// Get the name of the macro being passed to __codesize
340330
let codesize_arg = first_arg.name.as_ref().unwrap();
341-
let is_previous_parent = scope.iter().any(|def| def.name == *codesize_arg);
331+
let is_previous_parent = scope_mgr.macro_stack().iter().any(|def| def.name == *codesize_arg);
342332

343333
// Special case:
344334
// If the macro provided to __codesize is the current macro, we need to avoid a
@@ -362,9 +352,8 @@ fn codesize<'a>(
362352
evm_version,
363353
ir_macro,
364354
contract,
365-
scope,
355+
scope_mgr,
366356
*offset,
367-
mis,
368357
ir_macro.name.eq("CONSTRUCTOR"),
369358
Some(circular_codesize_invocations),
370359
relax_jumps,

0 commit comments

Comments
 (0)