@@ -115,13 +115,14 @@ export function getGeneratedSources (tree, scopeId, contractObj) {
115115 *
116116 * @param {InternalCallTree } tree - The call tree instance
117117 * @param {Object } functionDefinition - AST function definition node
118+ * @param {Object } contractDefinition - AST function definition node
118119 * @param {number } step - VM trace step index at function entry
119120 * @param {string } scopeId - Scope identifier for this function
120121 * @param {Object } contractObj - Contract object with ABI
121122 * @param {Object } sourceLocation - Source location of the function
122123 * @param {string } address - Contract address
123124 */
124- export async function registerFunctionParameters ( tree , functionDefinition , step , scopeId , contractObj , sourceLocation , address ) {
125+ export async function registerFunctionParameters ( tree , functionDefinition , contractDefinition , step , scopeId , contractObj , sourceLocation , address ) {
125126 if ( ! sourceLocation ) return
126127 if ( sourceLocation . jump !== 'i' ) return
127128 tree . functionCallStack . push ( step )
@@ -147,6 +148,11 @@ export async function registerFunctionParameters (tree, functionDefinition, step
147148 functionDefinitionAndInputs . inputs = addInputParams ( step , functionDefinition , inputs , tree , scopeId , states , contractObj , sourceLocation , stack . length )
148149 }
149150
151+ // For constructors, also register inherited constructor parameters
152+ if ( functionDefinition . kind === 'constructor' ) {
153+ await registerInheritedConstructorParams ( step , functionDefinition , contractDefinition , tree , scopeId , states , contractObj , sourceLocation , address , stack . length )
154+ }
155+
150156 // return params - register them but they're not yet on the stack
151157 if ( outputs && outputs . parameters && outputs . parameters . length > 0 ) {
152158 addReturnParams ( step + 1 , functionDefinition , outputs , tree , scopeId , states , contractObj , sourceLocation )
@@ -164,6 +170,120 @@ export async function registerFunctionParameters (tree, functionDefinition, step
164170 tree . functionDefinitionsByScope [ scopeId ] = functionDefinitionAndInputs
165171}
166172
173+ /**
174+ * Registers inherited constructor parameters for a constructor function.
175+ * The layout follows: main constructor params start at stack index 0, then inherited constructors
176+ * in reverse order (last inherited constructor first, then the one at length-2, etc.)
177+ *
178+ * @param {number } step - current step
179+ * @param {Object } functionDefinition - Constructor function definition
180+ * @param {InternalCallTree } tree - The call tree instance
181+ * @param {string } scopeId - Current scope identifier
182+ * @param {Object } states - State variable definitions
183+ * @param {Object } contractObj - Contract object with name and ABI
184+ * @param {Object } sourceLocation - Source location of the constructor
185+ * @param {string } address - Contract address
186+ * @param {number } stackLength - Current stack depth at constructor entry
187+ */
188+ export async function registerInheritedConstructorParams ( step , functionDefinition , contractDefinition , tree : InternalCallTree , scopeId , states , contractObj , sourceLocation , address , stackLength ) {
189+ try {
190+ if ( ! contractDefinition ) {
191+ if ( tree . debug ) console . log ( `[registerInheritedConstructorParams] No contract definition or linearized base contracts found` )
192+ return
193+ }
194+
195+ // Get linearized base contracts (excluding the current contract which is first)
196+ const baseContracts = await tree . solidityProxy . getLinearizedBaseContracts ( address , contractDefinition . id )
197+
198+ // Find constructors in inherited contracts
199+ const inheritedConstructors = [ ]
200+ for ( const baseContract of baseContracts ) {
201+ if ( baseContract . nodes ) {
202+ const constructor = baseContract . nodes . find ( node =>
203+ node . nodeType === 'FunctionDefinition' && node . kind === 'constructor'
204+ )
205+ if ( constructor && constructor . parameters && constructor . parameters . parameters && constructor . parameters . parameters . length > 0 ) {
206+ inheritedConstructors . push ( constructor )
207+ }
208+ }
209+ }
210+
211+ if ( inheritedConstructors . length === 0 ) {
212+ if ( tree . debug ) console . log ( `[registerInheritedConstructorParams] No inherited constructors with parameters found` )
213+ return
214+ }
215+
216+ if ( tree . debug ) {
217+ console . log ( `[registerInheritedConstructorParams] Found ${ inheritedConstructors . length } inherited constructors with parameters` )
218+ }
219+
220+ // Calculate starting stack index for inherited constructor parameters
221+ // Main constructor params start at index 0, inherited constructor params follow
222+ const mainConstructorParamCount = functionDefinition . parameters ?. parameters ?. length || 0
223+ let currentStackIndex = mainConstructorParamCount
224+
225+ // Process inherited constructors in reverse order (last inherited first)
226+ for ( let i = 0 ; i < inheritedConstructors . length ; i ++ ) {
227+ const inheritedConstructor = inheritedConstructors [ i ]
228+ const parameterList = inheritedConstructor . parameters
229+ const paramCount = parameterList . parameters . length
230+
231+ if ( tree . debug ) {
232+ console . log ( `[registerInheritedConstructorParams] Processing inherited constructor from contract ${ contractsById [ inheritedConstructor . parent ] ?. name || 'unknown' } ` )
233+ console . log ( ` - Parameter count: ${ paramCount } ` )
234+ console . log ( ` - Starting stack index: ${ currentStackIndex } ` )
235+ }
236+
237+ // Register parameters for this inherited constructor
238+ for ( let j = 0 ; j < paramCount ; j ++ ) {
239+ const param = parameterList . parameters [ j ]
240+ const stackIndex = currentStackIndex + j
241+
242+ // Ensure stack index is valid
243+ if ( stackIndex < 0 || stackIndex >= stackLength ) {
244+ if ( tree . debug ) console . warn ( `[registerInheritedConstructorParams] Invalid stack index ${ stackIndex } for inherited parameter ${ param . name } ` )
245+ continue
246+ }
247+
248+ let location = extractLocationFromAstVariable ( param )
249+ location = location === 'default' ? 'memory' : location
250+ const attributesName = param . name === '' ? `$inherited_${ i } _${ j } ` : `inherited_${ param . name } `
251+
252+ const newParam = {
253+ name : attributesName ,
254+ type : parseType ( param . typeDescriptions . typeString , states , contractObj . name , location ) ,
255+ stackIndex : stackIndex ,
256+ sourceLocation : sourceLocation ,
257+ abi : contractObj . contract . abi ,
258+ isParameter : true ,
259+ isReturnParameter : false ,
260+ isInheritedConstructorParam : true ,
261+ declarationStep : step ,
262+ safeToDecodeAtStep : step ,
263+ scope : inheritedConstructor . body ?. id ,
264+ id : param . id ,
265+ inheritedFrom : contractsById [ inheritedConstructor . parent ] ?. name || 'unknown'
266+ }
267+
268+ tree . scopes [ scopeId ] . locals [ attributesName ] = newParam
269+ if ( ! tree . variables [ param . id ] ) tree . variables [ param . id ] = newParam
270+
271+ // Bind parameter to symbolic stack with lifecycle tracking
272+ tree . symbolicStackManager . bindVariableWithLifecycle ( step + 1 , newParam , stackIndex , 'assigned' , scopeId )
273+
274+ if ( tree . debug ) {
275+ console . log ( `[registerInheritedConstructorParams] Bound inherited parameter: ${ attributesName } at stack index ${ stackIndex } from ${ newParam . inheritedFrom } ` )
276+ }
277+ }
278+
279+ currentStackIndex += paramCount
280+ }
281+
282+ } catch ( error ) {
283+ console . error ( 'Error in registerInheritedConstructorParams:' , error )
284+ }
285+ }
286+
167287/**
168288 * Includes variable declarations in the current scope if a new local variable is encountered at this step.
169289 * Checks the AST for variable declarations at the current source location and adds them to scope locals.
@@ -675,6 +795,7 @@ export function debugVariableTracking(tree: InternalCallTree, step: number, scop
675795export async function resolveNodesAtSourceLocation ( tree , sourceLocation , generatedSources , address ) {
676796 const ast = await tree . solidityProxy . ast ( sourceLocation , generatedSources , address )
677797 let funcDef
798+ let contractDef
678799 const blocksDef = [ ]
679800 if ( ast ) {
680801 const nodes = nodesAtPosition ( null , sourceLocation . start , { ast } )
@@ -690,12 +811,15 @@ export async function resolveNodesAtSourceLocation (tree, sourceLocation, genera
690811 funcDef = node
691812 blocksDef . push ( node )
692813 }
814+ if ( node && node . nodeType === 'ContractDefinition' ) {
815+ contractDef = node
816+ }
693817 }
694818 }
695819
696- return { nodes, functionDefinition : funcDef , blocksDefinition : blocksDef }
820+ return { nodes, functionDefinition : funcDef , contractDefinition : contractDef , blocksDefinition : blocksDef }
697821 } else {
698- return { nodes : [ ] , functionDefinition : null , blocksDefinition : [ ] }
822+ return { nodes : [ ] , functionDefinition : null , contractDefinition : null , blocksDefinition : [ ] }
699823 }
700824}
701825
0 commit comments