6
6
using System . Threading ;
7
7
using System . Threading . Tasks ;
8
8
using Microsoft . Extensions . Logging ;
9
- using Newtonsoft . Json ;
10
9
using Newtonsoft . Json . Linq ;
11
10
using System . IO ;
12
11
using Microsoft . CodeAnalysis . CSharp . Syntax ;
@@ -82,68 +81,86 @@ public async Task<JObject> GetValueFromObject(JToken objRet, CancellationToken t
82
81
return null ;
83
82
}
84
83
85
- public async Task < JObject > TryToRunOnLoadedClasses ( string varName , CancellationToken token )
84
+ public async Task < ( JObject containerObject , string remaining ) > ResolveStaticMembersInStaticTypes ( string varName , CancellationToken token )
86
85
{
87
86
string classNameToFind = "" ;
88
87
string [ ] parts = varName . Split ( "." ) ;
89
- var typeId = - 1 ;
90
- foreach ( string part in parts )
88
+ var store = await proxy . LoadStore ( sessionId , token ) ;
89
+ var methodInfo = ctx . CallStack . FirstOrDefault ( s => s . Id == scopeId ) . Method . Info ;
90
+
91
+ int typeId = - 1 ;
92
+ for ( int i = 0 ; i < parts . Length ; i ++ )
91
93
{
94
+ string part = parts [ i ] . Trim ( ) ;
95
+
92
96
if ( classNameToFind . Length > 0 )
93
97
classNameToFind += "." ;
94
- classNameToFind += part . Trim ( ) ;
98
+ classNameToFind += part ;
99
+
95
100
if ( typeId != - 1 )
96
101
{
97
- var fields = await sdbHelper . GetTypeFields ( sessionId , typeId , token ) ;
98
- foreach ( var field in fields )
99
- {
100
- if ( field . Name == part . Trim ( ) )
101
- {
102
- var isInitialized = await sdbHelper . TypeIsInitialized ( sessionId , typeId , token ) ;
103
- if ( isInitialized == 0 )
104
- {
105
- isInitialized = await sdbHelper . TypeInitialize ( sessionId , typeId , token ) ;
106
- }
107
- var valueRet = await sdbHelper . GetFieldValue ( sessionId , typeId , field . Id , token ) ;
108
- return await GetValueFromObject ( valueRet , token ) ;
109
- }
110
- }
111
- var methodId = await sdbHelper . GetPropertyMethodIdByName ( sessionId , typeId , part . Trim ( ) , token ) ;
112
- if ( methodId != - 1 )
102
+ string remaining = null ;
103
+ JObject memberObject = await FindStaticMemberInType ( part , typeId ) ;
104
+ if ( memberObject != null && i < parts . Length - 1 )
105
+ remaining = string . Join ( '.' , parts [ ( i + 1 ) ..] ) ;
106
+
107
+ return ( memberObject , remaining ) ;
108
+ }
109
+
110
+ if ( ! string . IsNullOrEmpty ( methodInfo . TypeInfo . Namespace ) )
111
+ typeId = await FindStaticTypeId ( methodInfo . TypeInfo . Namespace + "." + classNameToFind ) ;
112
+ if ( typeId == - 1 )
113
+ typeId = await FindStaticTypeId ( classNameToFind ) ;
114
+ }
115
+
116
+ return ( null , null ) ;
117
+
118
+ async Task < JObject > FindStaticMemberInType ( string name , int typeId )
119
+ {
120
+ var fields = await sdbHelper . GetTypeFields ( sessionId , typeId , token ) ;
121
+ foreach ( var field in fields )
122
+ {
123
+ if ( field . Name != name )
124
+ continue ;
125
+
126
+ var isInitialized = await sdbHelper . TypeIsInitialized ( sessionId , typeId , token ) ;
127
+ if ( isInitialized == 0 )
113
128
{
114
- var commandParamsObj = new MemoryStream ( ) ;
115
- var commandParamsObjWriter = new MonoBinaryWriter ( commandParamsObj ) ;
116
- commandParamsObjWriter . Write ( 0 ) ; //param count
117
- var retMethod = await sdbHelper . InvokeMethod ( sessionId , commandParamsObj . ToArray ( ) , methodId , "methodRet" , token ) ;
118
- return await GetValueFromObject ( retMethod , token ) ;
129
+ isInitialized = await sdbHelper . TypeInitialize ( sessionId , typeId , token ) ;
119
130
}
131
+ var valueRet = await sdbHelper . GetFieldValue ( sessionId , typeId , field . Id , token ) ;
132
+
133
+ return await GetValueFromObject ( valueRet , token ) ;
120
134
}
121
- var store = await proxy . LoadStore ( sessionId , token ) ;
122
- var info = ctx . CallStack . FirstOrDefault ( s => s . Id == scopeId ) . Method . Info ;
123
- var classNameToFindWithNamespace =
124
- string . IsNullOrEmpty ( info . TypeInfo . Namespace ) ?
125
- classNameToFind :
126
- info . TypeInfo . Namespace + "." + classNameToFind ;
127
135
128
- foreach ( var asm in store . assemblies )
136
+ var methodId = await sdbHelper . GetPropertyMethodIdByName ( sessionId , typeId , name , token ) ;
137
+ if ( methodId != - 1 )
129
138
{
130
- if ( await TryGetTypeIdFromName ( classNameToFindWithNamespace , asm ) )
131
- break ;
132
- if ( await TryGetTypeIdFromName ( classNameToFind , asm ) )
133
- break ;
139
+ var commandParamsObj = new MemoryStream ( ) ;
140
+ var commandParamsObjWriter = new MonoBinaryWriter ( commandParamsObj ) ;
141
+ commandParamsObjWriter . Write ( 0 ) ; //param count
142
+ var retMethod = await sdbHelper . InvokeMethod ( sessionId , commandParamsObj . ToArray ( ) , methodId , "methodRet" , token ) ;
143
+ return await GetValueFromObject ( retMethod , token ) ;
134
144
}
135
145
136
- async Task < bool > TryGetTypeIdFromName ( string typeName , AssemblyInfo assembly )
146
+ return null ;
147
+ }
148
+
149
+ async Task < int > FindStaticTypeId ( string typeName )
150
+ {
151
+ foreach ( var asm in store . assemblies )
137
152
{
138
- var type = assembly . GetTypeByName ( typeName ) ;
153
+ var type = asm . GetTypeByName ( typeName ) ;
139
154
if ( type == null )
140
- return false ;
155
+ continue ;
141
156
142
- typeId = await sdbHelper . GetTypeIdFromToken ( sessionId , assembly . DebugId , type . Token , token ) ;
143
- return true ;
157
+ int id = await sdbHelper . GetTypeIdFromToken ( sessionId , asm . DebugId , type . Token , token ) ;
158
+ if ( id != - 1 )
159
+ return id ;
144
160
}
161
+
162
+ return - 1 ;
145
163
}
146
- return null ;
147
164
}
148
165
149
166
// Checks Locals, followed by `this`
@@ -153,9 +170,6 @@ public async Task<JObject> Resolve(string varName, CancellationToken token)
153
170
if ( varName . Contains ( '(' ) )
154
171
return null ;
155
172
156
- string [ ] parts = varName . Split ( "." ) ;
157
- JObject rootObject = null ;
158
-
159
173
if ( scopeCache . MemberReferences . TryGetValue ( varName , out JObject ret ) ) {
160
174
return ret ;
161
175
}
@@ -164,66 +178,98 @@ public async Task<JObject> Resolve(string varName, CancellationToken token)
164
178
return await GetValueFromObject ( valueRet , token ) ;
165
179
}
166
180
167
- foreach ( string part in parts )
181
+ string [ ] parts = varName . Split ( "." ) ;
182
+ if ( parts . Length == 0 )
183
+ return null ;
184
+
185
+ JObject retObject = await ResolveAsLocalOrThisMember ( parts [ 0 ] ) ;
186
+ if ( retObject != null && parts . Length > 1 )
187
+ retObject = await ResolveAsInstanceMember ( string . Join ( '.' , parts [ 1 ..] ) , retObject ) ;
188
+
189
+ if ( retObject == null )
168
190
{
169
- string partTrimmed = part . Trim ( ) ;
170
- if ( partTrimmed == "" )
171
- return null ;
172
- if ( rootObject != null )
191
+ ( retObject , string remaining ) = await ResolveStaticMembersInStaticTypes ( varName , token ) ;
192
+ if ( ! string . IsNullOrEmpty ( remaining ) )
173
193
{
174
- if ( rootObject ? [ "subtype" ] ? . Value < string > ( ) == "null" )
175
- return null ;
176
- if ( DotnetObjectId . TryParse ( rootObject ? [ "objectId" ] ? . Value < string > ( ) , out DotnetObjectId objectId ) )
194
+ if ( retObject ? [ "subtype" ] ? . Value < string > ( ) == "null" )
177
195
{
178
- var rootResObj = await proxy . RuntimeGetPropertiesInternal ( sessionId , objectId , null , token ) ;
179
- var objRet = rootResObj . FirstOrDefault ( objPropAttr => objPropAttr [ "name" ] . Value < string > ( ) == partTrimmed ) ;
180
- if ( objRet == null )
181
- return null ;
182
-
183
- rootObject = await GetValueFromObject ( objRet , token ) ;
196
+ // NRE on null.$remaining
197
+ retObject = null ;
198
+ }
199
+ else
200
+ {
201
+ retObject = await ResolveAsInstanceMember ( remaining , retObject ) ;
184
202
}
185
- continue ;
186
203
}
204
+ }
205
+
206
+ scopeCache . MemberReferences [ varName ] = retObject ;
207
+ return retObject ;
208
+
209
+ async Task < JObject > ResolveAsLocalOrThisMember ( string name )
210
+ {
187
211
if ( scopeCache . Locals . Count == 0 && ! localsFetched )
188
212
{
189
213
Result scope_res = await proxy . GetScopeProperties ( sessionId , scopeId , token ) ;
190
214
if ( scope_res . IsErr )
191
215
throw new Exception ( $ "BUG: Unable to get properties for scope: { scopeId } . { scope_res } ") ;
192
216
localsFetched = true ;
193
217
}
194
- if ( scopeCache . Locals . TryGetValue ( partTrimmed , out JObject obj ) )
195
- {
196
- rootObject = obj [ "value" ] ? . Value < JObject > ( ) ;
197
- }
198
- else if ( scopeCache . Locals . TryGetValue ( "this" , out JObject objThis ) )
218
+
219
+ if ( scopeCache . Locals . TryGetValue ( name , out JObject obj ) )
220
+ return obj [ "value" ] ? . Value < JObject > ( ) ;
221
+
222
+ if ( ! scopeCache . Locals . TryGetValue ( "this" , out JObject objThis ) )
223
+ return null ;
224
+
225
+ if ( ! DotnetObjectId . TryParse ( objThis ? [ "value" ] ? [ "objectId" ] ? . Value < string > ( ) , out DotnetObjectId objectId ) )
226
+ return null ;
227
+
228
+ var rootResObj = await proxy . RuntimeGetPropertiesInternal ( sessionId , objectId , null , token ) ;
229
+ var objRet = rootResObj . FirstOrDefault ( objPropAttr => objPropAttr [ "name" ] . Value < string > ( ) == name ) ;
230
+ if ( objRet != null )
231
+ return await GetValueFromObject ( objRet , token ) ;
232
+
233
+ return null ;
234
+ }
235
+
236
+ async Task < JObject > ResolveAsInstanceMember ( string expr , JObject baseObject )
237
+ {
238
+ JObject resolvedObject = baseObject ;
239
+ string [ ] parts = expr . Split ( '.' ) ;
240
+ for ( int i = 0 ; i < parts . Length ; i ++ )
199
241
{
200
- if ( partTrimmed == "this" )
201
- {
202
- rootObject = objThis ? [ "value" ] . Value < JObject > ( ) ;
203
- }
204
- else if ( DotnetObjectId . TryParse ( objThis ? [ "value" ] ? [ "objectId" ] ? . Value < string > ( ) , out DotnetObjectId objectId ) )
242
+ string partTrimmed = parts [ i ] . Trim ( ) ;
243
+ if ( partTrimmed . Length == 0 )
244
+ return null ;
245
+
246
+ if ( ! DotnetObjectId . TryParse ( resolvedObject ? [ "objectId" ] ? . Value < string > ( ) , out DotnetObjectId objectId ) )
247
+ return null ;
248
+
249
+ var resolvedResObj = await proxy . RuntimeGetPropertiesInternal ( sessionId , objectId , null , token ) ;
250
+ var objRet = resolvedResObj . FirstOrDefault ( objPropAttr => objPropAttr [ "name" ] . Value < string > ( ) == partTrimmed ) ;
251
+ if ( objRet == null )
252
+ return null ;
253
+
254
+ resolvedObject = await GetValueFromObject ( objRet , token ) ;
255
+ if ( resolvedObject == null )
256
+ return null ;
257
+
258
+ if ( resolvedObject [ "subtype" ] ? . Value < string > ( ) == "null" )
205
259
{
206
- var rootResObj = await proxy . RuntimeGetPropertiesInternal ( sessionId , objectId , null , token ) ;
207
- var objRet = rootResObj . FirstOrDefault ( objPropAttr => objPropAttr [ "name" ] . Value < string > ( ) == partTrimmed ) ;
208
- if ( objRet != null )
260
+ if ( i < parts . Length - 1 )
209
261
{
210
- rootObject = await GetValueFromObject ( objRet , token ) ;
211
- }
212
- else
213
- {
214
- rootObject = await TryToRunOnLoadedClasses ( varName , token ) ;
215
- return rootObject ;
262
+ // there is some parts remaining, and can't
263
+ // do null.$remaining
264
+ return null ;
216
265
}
266
+
267
+ return resolvedObject ;
217
268
}
218
269
}
270
+
271
+ return resolvedObject ;
219
272
}
220
- if ( rootObject == null )
221
- {
222
- rootObject = await TryToRunOnLoadedClasses ( varName , token ) ;
223
- return rootObject ;
224
- }
225
- scopeCache . MemberReferences [ varName ] = rootObject ;
226
- return rootObject ;
227
273
}
228
274
229
275
public async Task < JObject > Resolve ( ElementAccessExpressionSyntax elementAccess , Dictionary < string , JObject > memberAccessValues , JObject indexObject , CancellationToken token )
0 commit comments