7
7
using System . Diagnostics . CodeAnalysis ;
8
8
using System . Linq ;
9
9
using System . Reflection . Metadata ;
10
- using System . Reflection . PortableExecutable ;
11
10
using ILCompiler . Logging ;
12
11
using ILLink . Shared ;
13
12
using Internal . IL ;
@@ -111,58 +110,86 @@ void ProcessMethod(MethodDesc method)
111
110
while ( reader . HasNext )
112
111
{
113
112
ILOpcode opcode = reader . ReadILOpcode ( ) ;
114
- MethodDesc ? lambdaOrLocalFunction ;
115
113
switch ( opcode )
116
114
{
117
115
case ILOpcode . ldftn :
118
116
case ILOpcode . ldtoken :
119
117
case ILOpcode . call :
120
118
case ILOpcode . callvirt :
121
119
case ILOpcode . newobj :
122
- lambdaOrLocalFunction = methodBody . GetObject ( reader . ReadILToken ( ) , NotFoundBehavior . ReturnNull ) as MethodDesc ;
120
+ {
121
+ MethodDesc ? referencedMethod = methodBody . GetObject ( reader . ReadILToken ( ) , NotFoundBehavior . ReturnNull ) as MethodDesc ;
122
+ if ( referencedMethod == null )
123
+ continue ;
124
+
125
+ referencedMethod = referencedMethod . GetTypicalMethodDefinition ( ) ;
126
+
127
+ if ( referencedMethod . IsConstructor &&
128
+ referencedMethod . OwningType is MetadataType generatedType &&
129
+ // Don't consider calls in the same type, like inside a static constructor
130
+ method . OwningType != generatedType &&
131
+ CompilerGeneratedNames . IsLambdaDisplayClass ( generatedType . Name ) )
132
+ {
133
+ Debug . Assert ( generatedType . IsTypeDefinition ) ;
134
+
135
+ // fill in null for now, attribute providers will be filled in later
136
+ _generatedTypeToTypeArgumentInfo ??= new Dictionary < MetadataType , TypeArgumentInfo > ( ) ;
137
+
138
+ if ( ! _generatedTypeToTypeArgumentInfo . TryAdd ( generatedType , new TypeArgumentInfo ( method , null ) ) )
139
+ {
140
+ var alreadyAssociatedMethod = _generatedTypeToTypeArgumentInfo [ generatedType ] . CreatingMethod ;
141
+ logger ? . LogWarning ( new MessageOrigin ( method ) , DiagnosticId . MethodsAreAssociatedWithUserMethod , method . GetDisplayName ( ) , alreadyAssociatedMethod . GetDisplayName ( ) , generatedType . GetDisplayName ( ) ) ;
142
+ }
143
+ continue ;
144
+ }
145
+
146
+ if ( ! CompilerGeneratedNames . IsLambdaOrLocalFunction ( referencedMethod . Name ) )
147
+ continue ;
148
+
149
+ if ( isStateMachineMember )
150
+ {
151
+ callGraph . TrackCall ( ( MetadataType ) method . OwningType , referencedMethod ) ;
152
+ }
153
+ else
154
+ {
155
+ callGraph . TrackCall ( method , referencedMethod ) ;
156
+ }
157
+ }
158
+ break ;
159
+
160
+ case ILOpcode . stsfld :
161
+ {
162
+ // Same as above, but stsfld instead of a call to the constructor
163
+ FieldDesc ? field = methodBody . GetObject ( reader . ReadILToken ( ) ) as FieldDesc ;
164
+ if ( field == null )
165
+ continue ;
166
+
167
+ field = field . GetTypicalFieldDefinition ( ) ;
168
+
169
+ if ( field . OwningType is MetadataType generatedType &&
170
+ // Don't consider field accesses in the same type, like inside a static constructor
171
+ method . OwningType != generatedType &&
172
+ CompilerGeneratedNames . IsLambdaDisplayClass ( generatedType . Name ) )
173
+ {
174
+ Debug . Assert ( generatedType . IsTypeDefinition ) ;
175
+
176
+ _generatedTypeToTypeArgumentInfo ??= new Dictionary < MetadataType , TypeArgumentInfo > ( ) ;
177
+
178
+ if ( ! _generatedTypeToTypeArgumentInfo . TryAdd ( generatedType , new TypeArgumentInfo ( method , null ) ) )
179
+ {
180
+ // It's expected that there may be multiple methods associated with the same static closure environment.
181
+ // All of these methods will substitute the same type arguments into the closure environment
182
+ // (if it is generic). Don't warn.
183
+ }
184
+ continue ;
185
+ }
186
+ }
123
187
break ;
124
188
125
189
default :
126
- lambdaOrLocalFunction = null ;
127
190
reader . Skip ( opcode ) ;
128
191
break ;
129
192
}
130
-
131
- if ( lambdaOrLocalFunction == null )
132
- continue ;
133
-
134
- lambdaOrLocalFunction = lambdaOrLocalFunction . GetTypicalMethodDefinition ( ) ;
135
-
136
- if ( lambdaOrLocalFunction . IsConstructor &&
137
- lambdaOrLocalFunction . OwningType is MetadataType generatedType &&
138
- // Don't consider calls in the same type, like inside a static constructor
139
- method . OwningType != generatedType &&
140
- CompilerGeneratedNames . IsLambdaDisplayClass ( generatedType . Name ) )
141
- {
142
- Debug . Assert ( generatedType . IsTypeDefinition ) ;
143
-
144
- // fill in null for now, attribute providers will be filled in later
145
- _generatedTypeToTypeArgumentInfo ??= new Dictionary < MetadataType , TypeArgumentInfo > ( ) ;
146
-
147
- if ( ! _generatedTypeToTypeArgumentInfo . TryAdd ( generatedType , new TypeArgumentInfo ( method , null ) ) )
148
- {
149
- var alreadyAssociatedMethod = _generatedTypeToTypeArgumentInfo [ generatedType ] . CreatingMethod ;
150
- logger ? . LogWarning ( new MessageOrigin ( method ) , DiagnosticId . MethodsAreAssociatedWithUserMethod , method . GetDisplayName ( ) , alreadyAssociatedMethod . GetDisplayName ( ) , generatedType . GetDisplayName ( ) ) ;
151
- }
152
- continue ;
153
- }
154
-
155
- if ( ! CompilerGeneratedNames . IsLambdaOrLocalFunction ( lambdaOrLocalFunction . Name ) )
156
- continue ;
157
-
158
- if ( isStateMachineMember )
159
- {
160
- callGraph . TrackCall ( ( MetadataType ) method . OwningType , lambdaOrLocalFunction ) ;
161
- }
162
- else
163
- {
164
- callGraph . TrackCall ( method , lambdaOrLocalFunction ) ;
165
- }
166
193
}
167
194
}
168
195
@@ -284,15 +311,7 @@ void MapGeneratedTypeTypeParameters(MetadataType generatedType)
284
311
Debug . Assert ( CompilerGeneratedNames . IsGeneratedType ( generatedType . Name ) ) ;
285
312
Debug . Assert ( generatedType == generatedType . GetTypeDefinition ( ) ) ;
286
313
287
- if ( _generatedTypeToTypeArgumentInfo ? . TryGetValue ( generatedType , out var typeInfo ) != true )
288
- {
289
- // This can happen for static (non-capturing) closure environments, where more than
290
- // nested function can map to the same closure environment. Since the current functionality
291
- // is based on a one-to-one relationship between environments (types) and methods, this is
292
- // not supported.
293
- return ;
294
- }
295
-
314
+ var typeInfo = _generatedTypeToTypeArgumentInfo [ generatedType ] ;
296
315
if ( typeInfo . OriginalAttributes is not null )
297
316
{
298
317
return ;
@@ -337,11 +356,14 @@ void MapGeneratedTypeTypeParameters(MetadataType generatedType)
337
356
{
338
357
owningType = ( MetadataType ) owningType . GetTypeDefinition ( ) ;
339
358
MapGeneratedTypeTypeParameters ( owningType ) ;
340
- if ( _generatedTypeToTypeArgumentInfo . TryGetValue ( owningType , out var owningInfo ) &&
341
- owningInfo . OriginalAttributes is { } owningAttrs )
359
+ if ( _generatedTypeToTypeArgumentInfo [ owningType ] . OriginalAttributes is { } owningAttrs )
342
360
{
343
361
userAttrs = owningAttrs [ param . Index ] ;
344
362
}
363
+ else
364
+ {
365
+ Debug . Assert ( false , "This should be impossible in valid code" ) ;
366
+ }
345
367
}
346
368
}
347
369
}
@@ -352,27 +374,67 @@ void MapGeneratedTypeTypeParameters(MetadataType generatedType)
352
374
_generatedTypeToTypeArgumentInfo [ generatedType ] = typeInfo with { OriginalAttributes = typeArgs } ;
353
375
}
354
376
355
- MetadataType ? ScanForInit ( MetadataType stateMachineType , MethodIL body )
377
+ MetadataType ? ScanForInit ( MetadataType compilerGeneratedType , MethodIL body )
356
378
{
357
379
ILReader reader = new ILReader ( body . GetILBytes ( ) ) ;
358
380
while ( reader . HasNext )
359
381
{
360
382
ILOpcode opcode = reader . ReadILOpcode ( ) ;
383
+ bool handled = false ;
384
+ MethodDesc ? methodOperand = null ;
361
385
switch ( opcode )
362
386
{
363
- case ILOpcode . initobj :
364
387
case ILOpcode . newobj :
365
- if ( body . GetObject ( reader . ReadILToken ( ) ) is MethodDesc { OwningType : MetadataType owningType }
366
- && stateMachineType == owningType . GetTypeDefinition ( ) )
367
388
{
368
- return owningType ;
389
+ methodOperand = body . GetObject ( reader . ReadILToken ( ) ) as MethodDesc ;
390
+ if ( methodOperand is MethodDesc { OwningType : MetadataType owningType }
391
+ && compilerGeneratedType == owningType . GetTypeDefinition ( ) )
392
+ {
393
+ return owningType ;
394
+ }
395
+ handled = true ;
396
+ }
397
+ break ;
398
+
399
+ case ILOpcode . ldftn :
400
+ case ILOpcode . ldtoken :
401
+ case ILOpcode . call :
402
+ case ILOpcode . callvirt :
403
+ methodOperand = body . GetObject ( reader . ReadILToken ( ) ) as MethodDesc ;
404
+ break ;
405
+
406
+ case ILOpcode . stsfld :
407
+ {
408
+ if ( body . GetObject ( reader . ReadILToken ( ) ) is FieldDesc { OwningType : MetadataType owningType }
409
+ && compilerGeneratedType == owningType . GetTypeDefinition ( ) )
410
+ {
411
+ return owningType ;
412
+ }
413
+ handled = true ;
369
414
}
370
415
break ;
371
416
372
417
default :
373
418
reader . Skip ( opcode ) ;
374
419
break ;
375
420
}
421
+
422
+ // Also look for type substitutions into generic methods
423
+ // (such as AsyncTaskMethodBuilder::Start<TStateMachine>).
424
+ if ( ! handled && methodOperand is not null )
425
+ {
426
+ if ( methodOperand != methodOperand . GetMethodDefinition ( ) )
427
+ {
428
+ foreach ( var tr in methodOperand . Instantiation )
429
+ {
430
+ if ( tr is MetadataType && tr != tr . GetTypeDefinition ( )
431
+ && compilerGeneratedType == tr . GetTypeDefinition ( ) )
432
+ {
433
+ return tr as MetadataType ;
434
+ }
435
+ }
436
+ }
437
+ }
376
438
}
377
439
return null ;
378
440
}
0 commit comments