1+ /**
2+ * Scope Processing Helper - Provides utilities for processing debugging scope data
3+ */
4+
5+ import { NestedScope , traceHelper , StepDetail } from '@remix-project/remix-debug' ;
6+
7+ export interface ProcessedScope {
8+ scopeId : string ;
9+ functionName ?: string ;
10+ variableCount : number ;
11+ variableNames : string [ ] ;
12+ stepRange : { first : number ; last : number } ;
13+ gasCost ?: number ;
14+ isCreation ?: boolean ;
15+ isExternalCall ?: boolean ;
16+ reverted ?: {
17+ step : StepDetail
18+ line ?: number
19+ }
20+ opcode ?: string ;
21+ children : ProcessedScope [ ] | string | null ;
22+ childCount : number ;
23+ totalDescendants : number ;
24+ message ?: string ;
25+ }
26+
27+ /**
28+ * Process a scope with depth limiting to prevent context overflow
29+ * @param scope - The nested scope to process
30+ * @param depth - Current depth (default: 0)
31+ * @param maxDepth - Maximum depth to process (default: 3)
32+ * @returns Processed scope with depth-limited children
33+ */
34+ export function processScope (
35+ scope : NestedScope ,
36+ depth : number = 0 ,
37+ maxDepth : number = 3
38+ ) : ProcessedScope {
39+ const processed : ProcessedScope = {
40+ scopeId : scope . scopeId ,
41+ functionName : scope . functionDefinition ? scope . functionDefinition . name : undefined ,
42+ variableCount : scope . locals ? Object . keys ( scope . locals ) . length : 0 ,
43+ variableNames : scope . locals ? Object . keys ( scope . locals ) : [ ] ,
44+ stepRange : { first : scope . firstStep , last : scope . lastStep } ,
45+ gasCost : scope . gasCost ,
46+ isCreation : scope . isCreation ,
47+ isExternalCall : scope . isCreation || traceHelper . isCallInstruction ( scope . opcodeInfo ) ,
48+ reverted : scope . reverted ,
49+ opcode : scope . opcodeInfo ?. op ,
50+ children : null ,
51+ childCount : 0 ,
52+ totalDescendants : 0
53+ } ;
54+
55+ // Process children with depth limit
56+ if ( scope . children && scope . children . length > 0 ) {
57+ processed . childCount = scope . children . length ;
58+
59+ if ( depth < maxDepth ) {
60+ // Recursively process children if under depth limit
61+ processed . children = scope . children . map ( child => processScope ( child , depth + 1 , maxDepth ) ) ;
62+ processed . totalDescendants = scope . children . reduce ( ( total , child ) => {
63+ const childProcessed = processScope ( child , depth + 1 , maxDepth ) ;
64+ return total + 1 + ( childProcessed . totalDescendants || 0 ) ;
65+ } , 0 ) ;
66+ } else {
67+ // At depth limit, provide guidance to use get_scopes_with_root tool
68+ processed . children = null ;
69+ processed . message = `descending the tree can be done using the tool get_scopes_with_root. scope ids: (${ scope . children . map ( el => el . scopeId ) . join ( ' , ' ) } )` ;
70+ processed . totalDescendants = scope . children . length ; // Just count direct children
71+ }
72+ }
73+
74+ return processed ;
75+ }
76+
77+ /**
78+ * Process an array of scopes with depth limiting
79+ * @param scopes - Array of nested scopes to process
80+ * @param maxDepth - Maximum depth to process (default: 3)
81+ * @returns Array of processed scopes
82+ */
83+ export function processScopes (
84+ scopes : NestedScope [ ] ,
85+ maxDepth : number = 3
86+ ) : ProcessedScope [ ] {
87+ return scopes . map ( scope => processScope ( scope , 0 , maxDepth ) ) ;
88+ }
89+
90+ /**
91+ * Count all scopes including nested ones
92+ * @param scopes - Array of processed scopes
93+ * @returns Total count of all scopes
94+ */
95+ export function countAllScopes ( scopes : ProcessedScope [ ] ) : number {
96+ return scopes . reduce ( ( total , scope ) => {
97+ const childCount = Array . isArray ( scope . children ) ? countAllScopes ( scope . children ) : 0 ;
98+ return total + 1 + childCount ;
99+ } , 0 ) ;
100+ }
101+
102+ /**
103+ * Count all variables across all scopes
104+ * @param scopes - Array of processed scopes
105+ * @returns Total count of all variables
106+ */
107+ export function countAllVariables ( scopes : ProcessedScope [ ] ) : number {
108+ return scopes . reduce ( ( total , scope ) => {
109+ const scopeVars = scope . variableCount || 0 ;
110+ const childVars = Array . isArray ( scope . children ) ? countAllVariables ( scope . children ) : 0 ;
111+ return total + scopeVars + childVars ;
112+ } , 0 ) ;
113+ }
114+
115+ /**
116+ * Get function summary across all scopes
117+ * @param scopes - Array of processed scopes
118+ * @returns Array of function summaries
119+ */
120+ export function getFunctionSummary ( scopes : ProcessedScope [ ] ) : Array < {
121+ name ?: string ;
122+ scopeId : string ;
123+ variableCount : number ;
124+ variableNames : string [ ] ;
125+ childCount : number ;
126+ stepRange : { first : number ; last : number } ;
127+ } > {
128+ const functions : Array < {
129+ name ?: string ;
130+ scopeId : string ;
131+ variableCount : number ;
132+ variableNames : string [ ] ;
133+ childCount : number ;
134+ stepRange : { first : number ; last : number } ;
135+ } > = [ ] ;
136+
137+ const collectFunctions = ( scopeList : ProcessedScope [ ] ) => {
138+ for ( const scope of scopeList ) {
139+ if ( scope . functionName ) {
140+ functions . push ( {
141+ name : scope . functionName ,
142+ scopeId : scope . scopeId ,
143+ variableCount : scope . variableCount ,
144+ variableNames : scope . variableNames ,
145+ childCount : scope . childCount ,
146+ stepRange : scope . stepRange
147+ } ) ;
148+ }
149+ if ( Array . isArray ( scope . children ) ) {
150+ collectFunctions ( scope . children ) ;
151+ }
152+ }
153+ } ;
154+
155+ collectFunctions ( scopes ) ;
156+ return functions ;
157+ }
0 commit comments