11'use strict'
22import { AstWalker } from '@remix-project/remix-astwalker'
3+ import { nodesAtPositionForSourceLocation } from '../source/sourceMappingDecoder'
34import { util } from '@remix-project/remix-lib'
45import { SourceLocationTracker } from '../source/sourceLocationTracker'
56import { EventManager } from '../eventManager'
@@ -50,6 +51,10 @@ export class InternalCallTree {
5051 [ Key : number ] : any
5152 }
5253
54+ codeManager : any
55+ scopesMapping : any
56+ processedFunctions : any
57+
5358 /**
5459 * constructor
5560 *
@@ -66,6 +71,7 @@ export class InternalCallTree {
6671 this . solidityProxy = solidityProxy
6772 this . traceManager = traceManager
6873 this . offsetToLineColumnConverter = offsetToLineColumnConverter
74+ this . codeManager = codeManager
6975 this . sourceLocationTracker = new SourceLocationTracker ( codeManager , { debugWithGeneratedSources : opts . debugWithGeneratedSources } )
7076 debuggerEvent . register ( 'newTraceLoaded' , async ( trace ) => {
7177 const time = Date . now ( )
@@ -131,6 +137,82 @@ export class InternalCallTree {
131137 this . constructorsStartExecution = { }
132138 this . pendingConstructor = null
133139 this . variables = { }
140+
141+ this . scopesMapping = { }
142+ this . processedFunctions = [ ]
143+ }
144+
145+ /*
146+ async printSolArtefacts (vmtraceIndex) {
147+ const address = this.traceManager.getCurrentCalledAddressAt(vmtraceIndex)
148+ const contractObj = await this.solidityProxy.contractObjectAtAddress(address)
149+ const pc = this.traceManager.getCurrentPC(vmtraceIndex)
150+ const scope = this.findEntryByRange(pc, contractObj.contract.evm.deployedBytecode.functionDebugData)
151+ console.log(pc, scope, contractObj.contract.evm.deployedBytecode.functionDebugData)
152+ }*/
153+
154+ async getFunctionDebugData ( vmtraceIndex , isCreation ) {
155+ const address = this . traceManager . getCurrentCalledAddressAt ( vmtraceIndex )
156+ const contractObj = await this . solidityProxy . contractObjectAtAddress ( address )
157+ const debugData = isCreation ? contractObj . contract . evm . bytecode . functionDebugData : contractObj . contract . evm . deployedBytecode . functionDebugData
158+ const pc = this . traceManager . getCurrentPC ( vmtraceIndex )
159+ const key = this . findEntryByRange ( pc , debugData )
160+ return { key, value : debugData [ key ] }
161+ }
162+
163+ async printResolveLocals ( vmtraceIndex ) {
164+ const address = this . traceManager . getCurrentCalledAddressAt ( vmtraceIndex )
165+ const sourceLocation = await this . extractValidSourceLocation ( vmtraceIndex , address )
166+ // this doesn't yet handle generated sources
167+ const ast = await this . solidityProxy . ast ( sourceLocation , null , address )
168+ const nodes = nodesAtPositionForSourceLocation ( '' , sourceLocation , { ast } )
169+ const node = nodes . reverse ( ) . find ( ( node ) => {
170+ return ! ! node . scope
171+ } )
172+ if ( ! node ) {
173+ console . log ( 'node not found' )
174+ return
175+ }
176+ const nodesbyScope = getAllItemByScope ( this , ast , this . astWalker )
177+ const nodesForScope = nodesbyScope [ node . scope ]
178+ const locals = getVariableDeclarationForScope ( nodesForScope )
179+ return {
180+ firstStep : 0 ,
181+ isCreation : false ,
182+ gasCost : 0 ,
183+ lastStep : 0 ,
184+ locals
185+ }
186+ }
187+
188+ /**
189+ * Find the entry in mapping whose range contains the given number.
190+ * The range is defined between consecutive entryPoint values.
191+ *
192+ * @param {number } value - The number to search for
193+ * @param {Object } mapping - Mapping of entries with entryPoint values
194+ * @returns {string|null } The key of the matching entry, or null if not found
195+ */
196+ findEntryByRange ( value : number , mapping : { [ key : string ] : { entryPoint : number , id : number } } ) : string | null {
197+ // Convert mapping to sorted array of [key, entryPoint] pairs
198+ const entries = Object . entries ( mapping )
199+ . map ( ( [ key , obj ] ) => ( { key, entryPoint : obj . entryPoint } ) )
200+ . sort ( ( a , b ) => a . entryPoint - b . entryPoint )
201+
202+ // Find the entry whose range contains the value
203+ for ( let i = 0 ; i < entries . length ; i ++ ) {
204+ const currentEntry = entries [ i ]
205+ const nextEntry = entries [ i + 1 ]
206+
207+ // Check if value is in range [currentEntry.entryPoint, nextEntry.entryPoint)
208+ if ( value >= currentEntry . entryPoint ) {
209+ if ( ! nextEntry || value < nextEntry . entryPoint ) {
210+ return currentEntry . key
211+ }
212+ }
213+ }
214+
215+ return null
134216 }
135217
136218 /**
@@ -139,6 +221,9 @@ export class InternalCallTree {
139221 * @param {Int } vmtraceIndex - index on the vm trace
140222 */
141223 findScope ( vmtraceIndex ) {
224+
225+ this . printResolveLocals ( vmtraceIndex )
226+
142227 let scopeId = this . findScopeId ( vmtraceIndex )
143228 if ( scopeId !== '' && ! scopeId ) return null
144229 let scope = this . scopes [ scopeId ]
@@ -248,18 +333,33 @@ async function buildTree (tree, step, scopeId, isCreation, functionDefinition?,
248333 let previousValidSourceLocation = validSourceLocation || currentSourceLocation
249334 let compilationResult
250335 let currentAddress = ''
336+ let currentFunctionDebugData
251337 while ( step < tree . traceManager . trace . length ) {
252338 let sourceLocation
253339 let validSourceLocation
254340 let address
255-
341+ let isFunctionEntryPoint = false
342+ let functionDebugData
256343 try {
257344 address = tree . traceManager . getCurrentCalledAddressAt ( step )
258345 sourceLocation = await tree . extractSourceLocation ( step , address )
346+ functionDebugData = await tree . getFunctionDebugData ( step , isCreation , currentFunctionDebugData )
347+
348+ if ( functionDebugData && functionDebugData . value && functionDebugData . value . id && ! tree . processedFunctions . includes ( functionDebugData . value . id ) ) {
349+ currentFunctionDebugData = functionDebugData
350+ tree . processedFunctions . push ( functionDebugData . value . id )
351+ console . log ( 'found' , functionDebugData . value . id )
352+ isFunctionEntryPoint = true
353+ }
354+
355+ if ( step === 92 || step === 93 ) {
356+ console . log ( 'start' , sourceLocation )
357+ }
259358
260359 if ( ! includedSource ( sourceLocation , currentSourceLocation ) ) {
261- tree . reducedTrace . push ( step )
360+ tree . reducedTrace . push ( step )
262361 currentSourceLocation = sourceLocation
362+ // if (step === 92 || step === 93) console.log('not include', currentSourceLocation)
263363 }
264364 if ( currentAddress !== address ) {
265365 compilationResult = await tree . solidityProxy . compilationResult ( address )
@@ -268,15 +368,21 @@ async function buildTree (tree, step, scopeId, isCreation, functionDefinition?,
268368 const amountOfSources = tree . sourceLocationTracker . getTotalAmountOfSources ( address , compilationResult . data . contracts )
269369 if ( tree . sourceLocationTracker . isInvalidSourceLocation ( currentSourceLocation , amountOfSources ) ) { // file is -1 or greater than amount of sources
270370 validSourceLocation = previousValidSourceLocation
271- } else
371+ } else {
272372 validSourceLocation = currentSourceLocation
373+ if ( step === 92 || step === 93 ) console . log ( 'valid' , validSourceLocation , currentSourceLocation )
374+ }
273375
274376 } catch ( e ) {
275377 return { outStep : step , error : 'InternalCallTree - Error resolving source location. ' + step + ' ' + e }
276378 }
277379 if ( ! sourceLocation ) {
278380 return { outStep : step , error : 'InternalCallTree - No source Location. ' + step }
279381 }
382+
383+ if ( step === 92 || step === 93 ) {
384+ console . log ( 'validSourceLocation' , validSourceLocation , currentSourceLocation )
385+ }
280386 const stepDetail : StepDetail = tree . traceManager . trace [ step ]
281387 const nextStepDetail : StepDetail = tree . traceManager . trace [ step + 1 ]
282388 if ( stepDetail && nextStepDetail ) {
@@ -300,7 +406,10 @@ async function buildTree (tree, step, scopeId, isCreation, functionDefinition?,
300406 }
301407 }
302408
409+ if ( step === 92 || step === 93 ) console . log ( 'bef offsetToLineColumn' , validSourceLocation )
410+
303411 lineColumnPos = await tree . offsetToLineColumnConverter . offsetToLineColumn ( validSourceLocation , validSourceLocation . file , sources , astSources )
412+ if ( step === 92 || step === 93 ) console . log ( 'lineColumnPos' , lineColumnPos )
304413 if ( ! tree . gasCostPerLine [ validSourceLocation . file ] ) tree . gasCostPerLine [ validSourceLocation . file ] = { }
305414 if ( ! tree . gasCostPerLine [ validSourceLocation . file ] [ lineColumnPos . start . line ] ) {
306415 tree . gasCostPerLine [ validSourceLocation . file ] [ lineColumnPos . start . line ] = {
@@ -315,26 +424,42 @@ async function buildTree (tree, step, scopeId, isCreation, functionDefinition?,
315424 }
316425 }
317426
318- tree . locationAndOpcodePerVMTraceIndex [ step ] = { sourceLocation, stepDetail, lineColumnPos, contractAddress : address }
427+ if ( step === 92 || step === 93 ) console . log ( 'set locationAndOpcodePerVMTraceIndex' , validSourceLocation , lineColumnPos , stepDetail )
428+ tree . locationAndOpcodePerVMTraceIndex [ step ] = { sourceLocation : validSourceLocation , stepDetail, lineColumnPos, contractAddress : address }
319429 tree . scopes [ scopeId ] . gasCost += stepDetail . gasCost
320430
321431 const contractObj = await tree . solidityProxy . contractObjectAtAddress ( address )
322432 const generatedSources = getGeneratedSources ( tree , scopeId , contractObj )
323- const functionDefinition = await resolveFunctionDefinition ( tree , sourceLocation , generatedSources , address )
433+ // const functionDefinition = await resolveFunctionDefinition(tree, sourceLocation, generatedSources, address)
324434
325435 const isInternalTxInstrn = isCallInstruction ( stepDetail )
326436 const isCreateInstrn = isCreateInstruction ( stepDetail )
327437 // we are checking if we are jumping in a new CALL or in an internal function
328438
329- const constructorExecutionStarts = tree . pendingConstructorExecutionAt > - 1 && tree . pendingConstructorExecutionAt < validSourceLocation . start
439+ const ast = Object . entries ( compilationResult . data . sources ) . find ( ( entry ) => {
440+ return ( entry [ 1 ] as any ) . id === validSourceLocation . file
441+ } )
442+
443+ let functionDefinition
444+ tree . astWalker . walkFull ( ( ast [ 1 ] as any ) . ast , ( node ) => {
445+ if ( functionDebugData . value && node . id === functionDebugData . value . id ) {
446+ functionDefinition = node
447+ }
448+ } )
449+ // const nodesbyScope = getAllItemByScope(this, ast, this.astWalker)
450+
451+ // const nodesForScope = nodesbyScope[functionDebugData.value.id]
452+ //const locals = getVariableDeclarationForScope(nodesForScope)
453+
454+ const constructorExecutionStarts = functionDebugData . key === 'constructor_' + contractObj . name // tree.pendingConstructorExecutionAt > -1 && tree.pendingConstructorExecutionAt < validSourceLocation.start
330455 if ( functionDefinition && functionDefinition . kind === 'constructor' && tree . pendingConstructorExecutionAt === - 1 && ! tree . constructorsStartExecution [ functionDefinition . id ] ) {
331456 tree . pendingConstructorExecutionAt = validSourceLocation . start
332457 tree . pendingConstructorId = functionDefinition . id
333458 tree . pendingConstructor = functionDefinition
334459 // from now on we'll be waiting for a change in the source location which will mark the beginning of the constructor execution.
335460 // constructorsStartExecution allows to keep track on which constructor has already been executed.
336461 }
337- const internalfunctionCall = functionDefinition && previousSourceLocation . jump === 'i'
462+ const internalfunctionCall = isFunctionEntryPoint
338463 if ( constructorExecutionStarts || isInternalTxInstrn || internalfunctionCall ) {
339464 try {
340465 const newScopeId = scopeId === '' ? subScope . toString ( ) : scopeId + '.' + subScope
@@ -525,3 +650,27 @@ function addParams (parameterList, tree, scopeId, states, contractObj, sourceLoc
525650 }
526651 return params
527652}
653+
654+ function getAllItemByScope ( tree , ast , astWalker ) {
655+ if ( Object . keys ( tree . scopesMapping ) . length > 0 ) return tree . scopesMapping
656+ astWalker . walkFull ( ast , ( node ) => {
657+ if ( node . scope ) {
658+ if ( ! tree . scopesMapping [ node . scope ] ) tree . scopesMapping [ node . scope ] = [ ]
659+ tree . scopesMapping [ node . scope ] . push ( node )
660+ }
661+ } )
662+ return tree . scopesMapping
663+ }
664+
665+ function getVariableDeclarationForScope ( nodes ) {
666+ const ret = [ ]
667+ nodes . filter ( ( node ) => {
668+ if ( node . nodeType === 'VariableDeclaration' || node . nodeType === 'YulVariableDeclaration' ) {
669+ ret . push ( node )
670+ }
671+ const hasChild = node . initialValue && ( node . nodeType === 'VariableDeclarationStatement' || node . nodeType === 'YulVariableDeclarationStatement' )
672+ if ( hasChild ) ret . push ( node . declarations )
673+ } )
674+ return ret
675+ }
676+
0 commit comments