1
1
// Licensed to the .NET Foundation under one or more agreements.
2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
+ using System ;
4
5
using System . Collections . Generic ;
6
+ using System . Linq ;
5
7
using Microsoft . Diagnostics . DataContractReader . Contracts ;
6
8
using Microsoft . Diagnostics . DataContractReader . RuntimeTypeSystemHelpers ;
7
9
using Moq ;
@@ -11,20 +13,23 @@ namespace Microsoft.Diagnostics.DataContractReader.Tests;
11
13
12
14
public class MethodDescTests
13
15
{
14
- private static Target CreateTarget ( MockDescriptors . MethodDescriptors methodDescBuilder )
16
+ private static Target CreateTarget ( MockDescriptors . MethodDescriptors methodDescBuilder , Mock < IExecutionManager > mockExecutionManager = null )
15
17
{
16
18
MockMemorySpace . Builder builder = methodDescBuilder . Builder ;
17
19
var target = new TestPlaceholderTarget ( builder . TargetTestHelpers . Arch , builder . GetReadContext ( ) . ReadFromTarget , methodDescBuilder . Types , methodDescBuilder . Globals ) ;
20
+
21
+ mockExecutionManager ??= new Mock < IExecutionManager > ( ) ;
18
22
target . SetContracts ( Mock . Of < ContractRegistry > (
19
23
c => c . RuntimeTypeSystem == ( ( IContractFactory < IRuntimeTypeSystem > ) new RuntimeTypeSystemFactory ( ) ) . CreateContract ( target , 1 )
20
24
&& c . Loader == ( ( IContractFactory < ILoader > ) new LoaderFactory ( ) ) . CreateContract ( target , 1 )
21
- && c . PlatformMetadata == new Mock < Contracts . IPlatformMetadata > ( ) . Object ) ) ;
25
+ && c . PlatformMetadata == new Mock < IPlatformMetadata > ( ) . Object
26
+ && c . ExecutionManager == mockExecutionManager . Object ) ) ;
22
27
return target ;
23
28
}
24
29
25
30
[ Theory ]
26
31
[ ClassData ( typeof ( MockTarget . StdArch ) ) ]
27
- public void MethodDescGetMethodDescTokenOk ( MockTarget . Architecture arch )
32
+ public void GetMethodDescHandle_ILMethod_GetBasicData ( MockTarget . Architecture arch )
28
33
{
29
34
TargetTestHelpers helpers = new ( arch ) ;
30
35
MockMemorySpace . Builder builder = new ( helpers ) ;
@@ -69,6 +74,163 @@ public void MethodDescGetMethodDescTokenOk(MockTarget.Architecture arch)
69
74
Assert . False ( isCollectible ) ;
70
75
TargetPointer versioning = rts . GetMethodDescVersioningState ( handle ) ;
71
76
Assert . Equal ( TargetPointer . Null , versioning ) ;
77
+
78
+ // Method classification - IL method
79
+ Assert . False ( rts . IsStoredSigMethodDesc ( handle , out _ ) ) ;
80
+ Assert . False ( rts . IsNoMetadataMethod ( handle , out _ ) ) ;
81
+ Assert . False ( rts . IsDynamicMethod ( handle ) ) ;
82
+ Assert . False ( rts . IsILStub ( handle ) ) ;
83
+ Assert . False ( rts . IsArrayMethod ( handle , out _ ) ) ;
84
+ }
85
+
86
+ [ Theory ]
87
+ [ ClassData ( typeof ( MockTarget . StdArch ) ) ]
88
+ public void IsArrayMethod ( MockTarget . Architecture arch )
89
+ {
90
+ TargetTestHelpers helpers = new ( arch ) ;
91
+ MockMemorySpace . Builder builder = new ( helpers ) ;
92
+ MockDescriptors . RuntimeTypeSystem rtsBuilder = new ( builder ) ;
93
+ MockDescriptors . Loader loaderBuilder = new ( builder ) ;
94
+ MockDescriptors . MethodDescriptors methodDescBuilder = new ( rtsBuilder , loaderBuilder ) ;
95
+
96
+ ushort numVirtuals = 1 ;
97
+ TargetPointer methodTable = AddMethodTable ( rtsBuilder , numVirtuals ) ;
98
+
99
+ byte count = 5 ;
100
+ uint methodDescSize = methodDescBuilder . Types [ DataType . ArrayMethodDesc ] . Size . Value ;
101
+ uint methodDescSizeByAlignment = methodDescSize / methodDescBuilder . MethodDescAlignment ;
102
+ byte chunkSize = ( byte ) ( count * methodDescSizeByAlignment ) ;
103
+ TargetPointer chunk = methodDescBuilder . AddMethodDescChunk ( methodTable , string . Empty , count , chunkSize , tokenRange : 0 ) ;
104
+
105
+ TargetPointer [ ] arrayMethods = new TargetPointer [ count ] ;
106
+ for ( byte i = 0 ; i < count ; i ++ )
107
+ {
108
+ // Add the array methods by setting the appropriate slot number
109
+ // Array vtable is:
110
+ // <base class vtables>
111
+ // Get
112
+ // Set
113
+ // Address
114
+ // .ctor
115
+ // [optionally other constructors]
116
+ byte index = ( byte ) ( i * methodDescSizeByAlignment ) ;
117
+ ushort slotNum = ( ushort ) ( numVirtuals + i ) ;
118
+ ushort flags = ( ushort ) MethodClassification . Array | ( ushort ) MethodDescFlags_1 . MethodDescFlags . HasNonVtableSlot ;
119
+ arrayMethods [ i ] = methodDescBuilder . SetMethodDesc ( chunk , index , slotNum , flags , tokenRemainder : 0 ) ;
120
+ }
121
+
122
+ Target target = CreateTarget ( methodDescBuilder ) ;
123
+ IRuntimeTypeSystem rts = target . Contracts . RuntimeTypeSystem ;
124
+
125
+ for ( byte i = 0 ; i < count ; i ++ )
126
+ {
127
+ MethodDescHandle handle = rts . GetMethodDescHandle ( arrayMethods [ i ] ) ;
128
+ Assert . NotEqual ( TargetPointer . Null , handle . Address ) ;
129
+ Assert . True ( rts . IsStoredSigMethodDesc ( handle , out _ ) ) ;
130
+ Assert . True ( rts . IsArrayMethod ( handle , out ArrayFunctionType functionType ) ) ;
131
+
132
+ ArrayFunctionType expectedFunctionType = i <= ( byte ) ArrayFunctionType . Constructor
133
+ ? ( ArrayFunctionType ) i
134
+ : ArrayFunctionType . Constructor ;
135
+ Assert . Equal ( expectedFunctionType , functionType ) ;
136
+ }
137
+ }
138
+
139
+ [ Theory ]
140
+ [ ClassData ( typeof ( MockTarget . StdArch ) ) ]
141
+ public void IsDynamicMethod ( MockTarget . Architecture arch )
142
+ {
143
+ TargetTestHelpers helpers = new ( arch ) ;
144
+ MockMemorySpace . Builder builder = new ( helpers ) ;
145
+ MockDescriptors . RuntimeTypeSystem rtsBuilder = new ( builder ) ;
146
+ MockDescriptors . Loader loaderBuilder = new ( builder ) ;
147
+ MockDescriptors . MethodDescriptors methodDescBuilder = new ( rtsBuilder , loaderBuilder ) ;
148
+
149
+ TargetPointer methodTable = AddMethodTable ( rtsBuilder ) ;
150
+
151
+ byte count = 2 ;
152
+ uint methodDescSize = methodDescBuilder . Types [ DataType . DynamicMethodDesc ] . Size . Value ;
153
+ uint methodDescSizeByAlignment = methodDescSize / methodDescBuilder . MethodDescAlignment ;
154
+ byte chunkSize = ( byte ) ( count * methodDescSizeByAlignment ) ;
155
+ TargetPointer chunk = methodDescBuilder . AddMethodDescChunk ( methodTable , string . Empty , count , chunkSize , tokenRange : 0 ) ;
156
+
157
+ ushort flags = ( ushort ) MethodClassification . Dynamic ;
158
+ TargetPointer dynamicMethod = methodDescBuilder . SetMethodDesc ( chunk , index : 0 , slotNum : 0 , flags , tokenRemainder : 0 ) ;
159
+ methodDescBuilder . SetDynamicMethodDesc ( dynamicMethod , ( uint ) RuntimeTypeSystem_1 . DynamicMethodDescExtendedFlags . IsLCGMethod ) ;
160
+ TargetPointer ilStubMethod = methodDescBuilder . SetMethodDesc ( chunk , index : ( byte ) methodDescSizeByAlignment , slotNum : 1 , flags , tokenRemainder : 0 ) ;
161
+ methodDescBuilder . SetDynamicMethodDesc ( ilStubMethod , ( uint ) RuntimeTypeSystem_1 . DynamicMethodDescExtendedFlags . IsILStub ) ;
162
+
163
+ Target target = CreateTarget ( methodDescBuilder ) ;
164
+ IRuntimeTypeSystem rts = target . Contracts . RuntimeTypeSystem ;
165
+
166
+ {
167
+ MethodDescHandle handle = rts . GetMethodDescHandle ( dynamicMethod ) ;
168
+ Assert . NotEqual ( TargetPointer . Null , handle . Address ) ;
169
+ Assert . True ( rts . IsStoredSigMethodDesc ( handle , out _ ) ) ;
170
+ Assert . True ( rts . IsNoMetadataMethod ( handle , out _ ) ) ;
171
+ Assert . True ( rts . IsDynamicMethod ( handle ) ) ;
172
+ Assert . False ( rts . IsILStub ( handle ) ) ;
173
+ }
174
+ {
175
+ MethodDescHandle handle = rts . GetMethodDescHandle ( ilStubMethod ) ;
176
+ Assert . NotEqual ( TargetPointer . Null , handle . Address ) ;
177
+ Assert . True ( rts . IsStoredSigMethodDesc ( handle , out _ ) ) ;
178
+ Assert . True ( rts . IsNoMetadataMethod ( handle , out _ ) ) ;
179
+ Assert . False ( rts . IsDynamicMethod ( handle ) ) ;
180
+ Assert . True ( rts . IsILStub ( handle ) ) ;
181
+ }
182
+ }
183
+
184
+ [ Theory ]
185
+ [ ClassData ( typeof ( MockTarget . StdArch ) ) ]
186
+ public void IsGenericMethodDefinition ( MockTarget . Architecture arch )
187
+ {
188
+ TargetTestHelpers helpers = new ( arch ) ;
189
+ MockMemorySpace . Builder builder = new ( helpers ) ;
190
+ MockDescriptors . RuntimeTypeSystem rtsBuilder = new ( builder ) ;
191
+ MockDescriptors . Loader loaderBuilder = new ( builder ) ;
192
+ MockDescriptors . MethodDescriptors methodDescBuilder = new ( rtsBuilder , loaderBuilder ) ;
193
+
194
+ TargetPointer methodTable = AddMethodTable ( rtsBuilder ) ;
195
+
196
+ byte count = 2 ;
197
+ uint methodDescSize = methodDescBuilder . Types [ DataType . InstantiatedMethodDesc ] . Size . Value ;
198
+ uint methodDescSizeByAlignment = methodDescSize / methodDescBuilder . MethodDescAlignment ;
199
+ byte chunkSize = ( byte ) ( count * methodDescSizeByAlignment ) ;
200
+ TargetPointer chunk = methodDescBuilder . AddMethodDescChunk ( methodTable , string . Empty , count , chunkSize , tokenRange : 0 ) ;
201
+
202
+ ushort flags = ( ushort ) MethodClassification . Instantiated ;
203
+ TargetPointer genericMethodDef = methodDescBuilder . SetMethodDesc ( chunk , index : 0 , slotNum : 0 , flags , tokenRemainder : 0 ) ;
204
+ methodDescBuilder . SetInstantiatedMethodDesc ( genericMethodDef , ( ushort ) RuntimeTypeSystem_1 . InstantiatedMethodDescFlags2 . GenericMethodDefinition , [ ] ) ;
205
+ TargetPointer [ ] typeArgsRawAddrs = [ 0x1000 , 0x2000 , 0x3000 ] ;
206
+ TargetPointer [ ] typeArgsHandles = typeArgsRawAddrs . Select ( a => GetTypeDescHandlePointer ( a ) ) . ToArray ( ) ;
207
+
208
+ TargetPointer genericWithInst = methodDescBuilder . SetMethodDesc ( chunk , index : ( byte ) methodDescSizeByAlignment , slotNum : 1 , flags , tokenRemainder : 0 ) ;
209
+ methodDescBuilder . SetInstantiatedMethodDesc ( genericWithInst , ( ushort ) RuntimeTypeSystem_1 . InstantiatedMethodDescFlags2 . GenericMethodDefinition , typeArgsHandles ) ;
210
+
211
+ Target target = CreateTarget ( methodDescBuilder ) ;
212
+ IRuntimeTypeSystem rts = target . Contracts . RuntimeTypeSystem ;
213
+
214
+ {
215
+ MethodDescHandle handle = rts . GetMethodDescHandle ( genericMethodDef ) ;
216
+ Assert . NotEqual ( TargetPointer . Null , handle . Address ) ;
217
+ Assert . True ( rts . IsGenericMethodDefinition ( handle ) ) ;
218
+ ReadOnlySpan < TypeHandle > instantiation = rts . GetGenericMethodInstantiation ( handle ) ;
219
+ Assert . Equal ( 0 , instantiation . Length ) ;
220
+ }
221
+
222
+ {
223
+ MethodDescHandle handle = rts . GetMethodDescHandle ( genericWithInst ) ;
224
+ Assert . NotEqual ( TargetPointer . Null , handle . Address ) ;
225
+ Assert . True ( rts . IsGenericMethodDefinition ( handle ) ) ;
226
+ ReadOnlySpan < TypeHandle > instantiation = rts . GetGenericMethodInstantiation ( handle ) ;
227
+ Assert . Equal ( typeArgsRawAddrs . Length , instantiation . Length ) ;
228
+ for ( int i = 0 ; i < typeArgsRawAddrs . Length ; i ++ )
229
+ {
230
+ Assert . Equal ( typeArgsHandles [ i ] , instantiation [ i ] . Address ) ;
231
+ Assert . Equal ( typeArgsRawAddrs [ i ] , instantiation [ i ] . TypeDescAddress ( ) ) ;
232
+ }
233
+ }
72
234
}
73
235
74
236
public static IEnumerable < object [ ] > StdArchOptionalSlotsData ( )
@@ -98,12 +260,7 @@ public void GetAddressOfNativeCodeSlot_OptionalSlots(MockTarget.Architecture arc
98
260
MockDescriptors . MethodDescriptors methodDescBuilder = new ( rtsBuilder , loaderBuilder ) ;
99
261
100
262
MethodDescFlags_1 . MethodDescFlags flags = ( MethodDescFlags_1 . MethodDescFlags ) flagsValue ;
101
- ushort numVirtuals = 1 ;
102
- TargetPointer eeClass = rtsBuilder . AddEEClass ( string . Empty , 0 , 2 , 1 ) ;
103
- TargetPointer methodTable = rtsBuilder . AddMethodTable ( string . Empty ,
104
- mtflags : default , mtflags2 : default , baseSize : helpers . ObjectBaseSize ,
105
- module : TargetPointer . Null , parentMethodTable : TargetPointer . Null , numInterfaces : 0 , numVirtuals : numVirtuals ) ;
106
- rtsBuilder . SetEEClassAndCanonMTRefs ( eeClass , methodTable ) ;
263
+ TargetPointer methodTable = AddMethodTable ( rtsBuilder ) ;
107
264
108
265
uint methodDescSize = methodDescBuilder . Types [ DataType . MethodDesc ] . Size . Value ;
109
266
if ( flags . HasFlag ( MethodDescFlags_1 . MethodDescFlags . HasNonVtableSlot ) )
@@ -135,4 +292,84 @@ public void GetAddressOfNativeCodeSlot_OptionalSlots(MockTarget.Architecture arc
135
292
Assert . Equal ( expectedCodeSlotAddr , actualNativeCodeSlotAddr ) ;
136
293
}
137
294
}
295
+
296
+ public static IEnumerable < object [ ] > StdArchMethodDescTypeData ( )
297
+ {
298
+ foreach ( object [ ] arr in new MockTarget . StdArch ( ) )
299
+ {
300
+ MockTarget . Architecture arch = ( MockTarget . Architecture ) arr [ 0 ] ;
301
+ yield return new object [ ] { arch , DataType . MethodDesc } ;
302
+ yield return new object [ ] { arch , DataType . FCallMethodDesc } ;
303
+ yield return new object [ ] { arch , DataType . PInvokeMethodDesc } ;
304
+ yield return new object [ ] { arch , DataType . EEImplMethodDesc } ;
305
+ yield return new object [ ] { arch , DataType . ArrayMethodDesc } ;
306
+ yield return new object [ ] { arch , DataType . InstantiatedMethodDesc } ;
307
+ yield return new object [ ] { arch , DataType . CLRToCOMCallMethodDesc } ;
308
+ yield return new object [ ] { arch , DataType . DynamicMethodDesc } ;
309
+ }
310
+ }
311
+
312
+ [ Theory ]
313
+ [ MemberData ( nameof ( StdArchMethodDescTypeData ) ) ]
314
+ public void GetNativeCode_StableEntryPoint_NonVtableSlot ( MockTarget . Architecture arch , DataType methodDescType )
315
+ {
316
+ TargetTestHelpers helpers = new ( arch ) ;
317
+ MockMemorySpace . Builder builder = new ( helpers ) ;
318
+ MockDescriptors . RuntimeTypeSystem rtsBuilder = new ( builder ) ;
319
+ MockDescriptors . Loader loaderBuilder = new ( builder ) ;
320
+ MockDescriptors . MethodDescriptors methodDescBuilder = new ( rtsBuilder , loaderBuilder ) ;
321
+
322
+ TargetPointer methodTable = AddMethodTable ( rtsBuilder ) ;
323
+ MethodClassification classification = methodDescType switch
324
+ {
325
+ DataType . MethodDesc => MethodClassification . IL ,
326
+ DataType . FCallMethodDesc => MethodClassification . FCall ,
327
+ DataType . PInvokeMethodDesc => MethodClassification . PInvoke ,
328
+ DataType . EEImplMethodDesc => MethodClassification . EEImpl ,
329
+ DataType . ArrayMethodDesc => MethodClassification . Array ,
330
+ DataType . InstantiatedMethodDesc => MethodClassification . Instantiated ,
331
+ DataType . CLRToCOMCallMethodDesc => MethodClassification . ComInterop ,
332
+ DataType . DynamicMethodDesc => MethodClassification . Dynamic ,
333
+ _ => throw new ArgumentOutOfRangeException ( nameof ( methodDescType ) )
334
+ } ;
335
+ uint methodDescBaseSize = methodDescBuilder . Types [ methodDescType ] . Size . Value ;
336
+ uint methodDescSize = methodDescBaseSize + methodDescBuilder . Types [ DataType . NonVtableSlot ] . Size ! . Value ;
337
+ byte chunkSize = ( byte ) ( methodDescSize / methodDescBuilder . MethodDescAlignment ) ;
338
+ TargetPointer chunk = methodDescBuilder . AddMethodDescChunk ( methodTable , string . Empty , count : 1 , chunkSize , tokenRange : 0 ) ;
339
+
340
+ ushort flags = ( ushort ) ( ( ushort ) classification | ( ushort ) MethodDescFlags_1 . MethodDescFlags . HasNonVtableSlot ) ;
341
+ TargetPointer methodDescAddress = methodDescBuilder . SetMethodDesc ( chunk , index : 0 , slotNum : 0 , flags , tokenRemainder : 0 , flags3 : ( ushort ) MethodDescFlags_1 . MethodDescFlags3 . HasStableEntryPoint ) ;
342
+ TargetCodePointer nativeCode = new TargetCodePointer ( 0x0789_abc0 ) ;
343
+ helpers . WritePointer (
344
+ methodDescBuilder . Builder . BorrowAddressRange ( methodDescAddress + methodDescBaseSize , helpers . PointerSize ) ,
345
+ nativeCode ) ;
346
+
347
+ Mock < IExecutionManager > mockExecutionManager = new ( ) ;
348
+ CodeBlockHandle codeBlock = new CodeBlockHandle ( methodDescAddress ) ;
349
+ mockExecutionManager . Setup ( em => em . GetCodeBlockHandle ( nativeCode ) )
350
+ . Returns ( codeBlock ) ;
351
+ mockExecutionManager . Setup ( em => em . GetMethodDesc ( codeBlock ) )
352
+ . Returns ( methodDescAddress ) ;
353
+ Target target = CreateTarget ( methodDescBuilder , mockExecutionManager ) ;
354
+ IRuntimeTypeSystem rts = target . Contracts . RuntimeTypeSystem ;
355
+
356
+ var handle = rts . GetMethodDescHandle ( methodDescAddress ) ;
357
+ Assert . NotEqual ( TargetPointer . Null , handle . Address ) ;
358
+
359
+ TargetCodePointer actualNativeCode = rts . GetNativeCode ( handle ) ;
360
+ Assert . Equal ( nativeCode , actualNativeCode ) ;
361
+ }
362
+
363
+ private TargetPointer AddMethodTable ( MockDescriptors . RuntimeTypeSystem rtsBuilder , ushort numVirtuals = 5 )
364
+ {
365
+ TargetPointer eeClass = rtsBuilder . AddEEClass ( string . Empty , attr : 0 , numMethods : 2 , numNonVirtualSlots : 1 ) ;
366
+ TargetPointer methodTable = rtsBuilder . AddMethodTable ( string . Empty ,
367
+ mtflags : default , mtflags2 : default , baseSize : rtsBuilder . Builder . TargetTestHelpers . ObjectBaseSize ,
368
+ module : TargetPointer . Null , parentMethodTable : TargetPointer . Null , numInterfaces : 0 , numVirtuals ) ;
369
+ rtsBuilder . SetEEClassAndCanonMTRefs ( eeClass , methodTable ) ;
370
+ return methodTable ;
371
+ }
372
+
373
+ private static TargetPointer GetTypeDescHandlePointer ( TargetPointer addr )
374
+ => addr | ( ulong ) RuntimeTypeSystem_1 . TypeHandleBits . TypeDesc ;
138
375
}
0 commit comments