@@ -3,6 +3,73 @@ use huff_neo_utils::ast::span::AstSpan;
33use huff_neo_utils:: prelude:: * ;
44use std:: str:: FromStr ;
55
6+ /// Resolves an argument name to its actual value by searching through the macro invocation stack.
7+ ///
8+ /// This function recursively traverses the macro invocation stack (`mis`) to find where an argument
9+ /// is defined and returns its resolved value. It handles cases where arguments reference other
10+ /// arguments (ArgCall) or are macro invocations with arguments (ArgCallMacroInvocation).
11+ ///
12+ /// # Arguments
13+ /// * `arg_name` - The name of the argument to resolve
14+ /// * `mis` - The macro invocation stack to search through
15+ /// * `contract` - The contract containing macro definitions
16+ ///
17+ /// # Returns
18+ /// * `Ok(String)` - The resolved macro name
19+ /// * `Err(CodegenErrorKind)` - If the argument cannot be resolved
20+ fn resolve_argument_through_mis ( arg_name : & str , mis : & [ ( usize , MacroInvocation ) ] , contract : & Contract ) -> Result < String , CodegenErrorKind > {
21+ tracing:: debug!( target: "codegen" , "Resolving argument '{}' through mis stack of length {}" , arg_name, mis. len( ) ) ;
22+
23+ // Search through the macro invocation stack in reverse (from current to root)
24+ for ( idx, ( _, mi) ) in mis. iter ( ) . enumerate ( ) . rev ( ) {
25+ tracing:: debug!( target: "codegen" , "Checking invocation {} for macro '{}'" , idx, mi. macro_name) ;
26+
27+ // Find the macro definition
28+ if let Some ( invoked_macro) = contract. find_macro_by_name ( & mi. macro_name ) {
29+ // Check if this macro has our argument
30+ if let Some ( param_index) = invoked_macro. parameters . iter ( ) . position ( |p| p. name . as_deref ( ) == Some ( arg_name) ) {
31+ tracing:: debug!( target: "codegen" , "Found parameter '{}' at index {} in macro '{}'" , arg_name, param_index, mi. macro_name) ;
32+
33+ // Get the corresponding argument value
34+ if let Some ( arg_value) = mi. args . get ( param_index) {
35+ match arg_value {
36+ MacroArg :: Ident ( macro_name) => {
37+ // Direct macro name - we're done
38+ tracing:: info!( target: "codegen" , "Resolved '{}' to macro: {}" , arg_name, macro_name) ;
39+ return Ok ( macro_name. clone ( ) ) ;
40+ }
41+ MacroArg :: ArgCall ( arg_call) => {
42+ // The argument is itself an argument reference - need to resolve it recursively
43+ tracing:: debug!( target: "codegen" , "Argument '{}' references another argument '{}', resolving recursively" , arg_name, arg_call. name) ;
44+ return resolve_argument_through_mis ( & arg_call. name , mis, contract) ;
45+ }
46+ MacroArg :: ArgCallMacroInvocation ( inner_arg_name, _) => {
47+ // The argument is an invocation of another argument - resolve that argument
48+ tracing:: debug!( target: "codegen" , "Argument '{}' is a macro invocation of argument '{}', resolving recursively" , arg_name, inner_arg_name) ;
49+ return resolve_argument_through_mis ( inner_arg_name, mis, contract) ;
50+ }
51+ MacroArg :: Noop => {
52+ // __NOOP cannot be invoked as a macro
53+ return Err ( CodegenErrorKind :: InvalidMacroArgumentType ( format ! (
54+ "Cannot invoke __NOOP as a macro in argument '{}'" ,
55+ arg_name
56+ ) ) ) ;
57+ }
58+ _ => {
59+ // Other types of arguments not supported for macro invocation
60+ tracing:: debug!( target: "codegen" , "Argument type {:?} not supported for macro invocation, continuing search" , arg_value) ;
61+ continue ;
62+ }
63+ }
64+ }
65+ }
66+ }
67+ }
68+
69+ tracing:: error!( target: "codegen" , "Failed to resolve argument '{}' in mis stack" , arg_name) ;
70+ Err ( CodegenErrorKind :: MissingArgumentDefinition ( arg_name. to_string ( ) ) )
71+ }
72+
673/// Computes the scope context for label resolution in argument calls.
774///
875/// When a label is passed as an argument (e.g., `<label>` in a macro body), it must be
@@ -91,62 +158,67 @@ pub fn bubble_arg_call(
91158 if let MacroArg :: ArgCallMacroInvocation ( inner_arg_name, invoc_args) = arg {
92159 tracing:: info!( target: "codegen" , "GOT ArgCallMacroInvocation for argument '{}', expanding it" , inner_arg_name) ;
93160
94- // We need to resolve inner_arg_name and expand the macro
95- // Look in the parent scope for the actual macro name
96- if scope. len ( ) > 1 {
97- let parent_macro = scope[ scope. len ( ) - 2 ] ;
98- if let Some ( parent_param_idx) = parent_macro. parameters . iter ( ) . position ( |p| p. name . as_ref ( ) == Some ( inner_arg_name) )
99- && mis. len ( ) > 1
100- && let Some ( parent_invoc) = mis. get ( mis. len ( ) - 2 )
101- && let Some ( MacroArg :: Ident ( actual_macro_name) ) = parent_invoc. 1 . args . get ( parent_param_idx)
102- && let Some ( called_macro) = contract. find_macro_by_name ( actual_macro_name)
103- {
104- let inner_mi =
105- MacroInvocation { macro_name : actual_macro_name. clone ( ) , args : invoc_args. clone ( ) , span : span. clone ( ) } ;
106-
107- let mut new_scope = scope. to_vec ( ) ;
108- new_scope. push ( called_macro) ;
109- let mut new_mis = mis. to_vec ( ) ;
110- new_mis. push ( ( * offset, inner_mi) ) ;
111-
112- match Codegen :: macro_to_bytecode (
113- evm_version,
114- called_macro,
115- contract,
116- & mut new_scope,
117- * offset,
118- & mut new_mis,
119- false ,
120- None ,
121- ) {
122- Ok ( expanded) => {
123- let byte_len: usize = expanded. bytes . iter ( ) . map ( |( _, b) | b. 0 . len ( ) / 2 ) . sum ( ) ;
124- bytes. extend ( expanded. bytes ) ;
125- * offset += byte_len;
126-
127- // Handle jumps and tables
128- for jump in expanded. unmatched_jumps {
129- if let Some ( existing) = jump_table. get_mut ( & jump. bytecode_index ) {
130- existing. push ( jump) ;
131- } else {
132- jump_table. insert ( jump. bytecode_index , vec ! [ jump] ) ;
161+ // We need to resolve inner_arg_name to get the actual macro name
162+ // Use the helper function to search through the entire mis stack
163+ match resolve_argument_through_mis ( inner_arg_name, mis, contract) {
164+ Ok ( actual_macro_name) => {
165+ if let Some ( called_macro) = contract. find_macro_by_name ( & actual_macro_name) {
166+ let inner_mi =
167+ MacroInvocation { macro_name : actual_macro_name. clone ( ) , args : invoc_args. clone ( ) , span : span. clone ( ) } ;
168+
169+ let mut new_scope = scope. to_vec ( ) ;
170+ new_scope. push ( called_macro) ;
171+ let mut new_mis = mis. to_vec ( ) ;
172+ new_mis. push ( ( * offset, inner_mi) ) ;
173+
174+ match Codegen :: macro_to_bytecode (
175+ evm_version,
176+ called_macro,
177+ contract,
178+ & mut new_scope,
179+ * offset,
180+ & mut new_mis,
181+ false ,
182+ None ,
183+ ) {
184+ Ok ( expanded) => {
185+ let byte_len: usize = expanded. bytes . iter ( ) . map ( |( _, b) | b. 0 . len ( ) / 2 ) . sum ( ) ;
186+ bytes. extend ( expanded. bytes ) ;
187+ * offset += byte_len;
188+
189+ // Handle jumps and tables
190+ for jump in expanded. unmatched_jumps {
191+ if let Some ( existing) = jump_table. get_mut ( & jump. bytecode_index ) {
192+ existing. push ( jump) ;
193+ } else {
194+ jump_table. insert ( jump. bytecode_index , vec ! [ jump] ) ;
195+ }
133196 }
134- }
135- for table_instance in expanded. table_instances {
136- table_instances. push ( table_instance) ;
137- }
138- for table in expanded. utilized_tables {
139- if !utilized_tables. contains ( & table) {
140- utilized_tables. push ( table) ;
197+ for table_instance in expanded. table_instances {
198+ table_instances. push ( table_instance) ;
141199 }
200+ for table in expanded. utilized_tables {
201+ if !utilized_tables. contains ( & table) {
202+ utilized_tables. push ( table) ;
203+ }
204+ }
205+ return Ok ( ( ) ) ;
142206 }
143- return Ok ( ( ) ) ;
207+ Err ( e ) => return Err ( e ) ,
144208 }
145- Err ( e) => return Err ( e) ,
209+ } else {
210+ return Err ( CodegenError {
211+ kind : CodegenErrorKind :: MissingMacroDefinition ( actual_macro_name) ,
212+ span : span. clone_box ( ) ,
213+ token : None ,
214+ } ) ;
146215 }
147216 }
217+ Err ( e) => {
218+ // Could not resolve the argument
219+ return Err ( CodegenError { kind : e, span : span. clone_box ( ) , token : None } ) ;
220+ }
148221 }
149- // If we couldn't expand it, fall through to normal handling
150222 }
151223
152224 // Original handling for other argument types
@@ -302,14 +374,15 @@ pub fn bubble_arg_call(
302374 }
303375 MacroArg :: ArgCallMacroInvocation ( arg_name, invoc_args) => {
304376 // Resolve the argument to get the actual macro name
305- if let Some ( param_idx) = target_macro. parameters . iter ( ) . position ( |p| p. name . as_ref ( ) == Some ( arg_name) ) {
306- if let Some ( MacroArg :: Ident ( actual_macro_name) ) = target_macro_invoc. 1 . args . get ( param_idx) {
377+ // Use the helper function to search through the entire mis stack
378+ match resolve_argument_through_mis ( arg_name, mis, contract) {
379+ Ok ( actual_macro_name) => {
307380 // Now invoke the resolved macro with the provided arguments
308- if let Some ( called_macro) = contract. find_macro_by_name ( actual_macro_name) {
381+ if let Some ( called_macro) = contract. find_macro_by_name ( & actual_macro_name) {
309382 let inner_mi = MacroInvocation {
310383 macro_name : actual_macro_name. clone ( ) ,
311384 args : invoc_args. clone ( ) ,
312- span : AstSpan ( vec ! [ ] ) , // We don't have the original span here
385+ span : span . clone ( ) ,
313386 } ;
314387
315388 let mut new_scope = scope. to_vec ( ) ;
@@ -353,24 +426,15 @@ pub fn bubble_arg_call(
353426 }
354427 } else {
355428 return Err ( CodegenError {
356- kind : CodegenErrorKind :: MissingMacroDefinition ( actual_macro_name. clone ( ) ) ,
429+ kind : CodegenErrorKind :: MissingMacroDefinition ( actual_macro_name) ,
357430 span : span. clone_box ( ) ,
358431 token : None ,
359432 } ) ;
360433 }
361- } else {
362- return Err ( CodegenError {
363- kind : CodegenErrorKind :: MissingArgumentDefinition ( arg_name. clone ( ) ) ,
364- span : span. clone_box ( ) ,
365- token : None ,
366- } ) ;
367434 }
368- } else {
369- return Err ( CodegenError {
370- kind : CodegenErrorKind :: MissingArgumentDefinition ( arg_name. clone ( ) ) ,
371- span : span. clone_box ( ) ,
372- token : None ,
373- } ) ;
435+ Err ( e) => {
436+ return Err ( CodegenError { kind : e, span : span. clone_box ( ) , token : None } ) ;
437+ }
374438 }
375439 }
376440 MacroArg :: MacroCall ( inner_mi) => {
0 commit comments