-
Notifications
You must be signed in to change notification settings - Fork 4k
/
MetadataDecoder.cs
580 lines (487 loc) · 22.1 KB
/
MetadataDecoder.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection.Metadata;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE
{
/// <summary>
/// Helper class to resolve metadata tokens and signatures.
/// </summary>
internal class MetadataDecoder : MetadataDecoder<PEModuleSymbol, TypeSymbol, MethodSymbol, FieldSymbol, Symbol>
{
/// <summary>
/// Type context for resolving generic type arguments.
/// </summary>
private readonly PENamedTypeSymbol _typeContextOpt;
/// <summary>
/// Method context for resolving generic method type arguments.
/// </summary>
private readonly PEMethodSymbol _methodContextOpt;
public MetadataDecoder(
PEModuleSymbol moduleSymbol,
PENamedTypeSymbol context) :
this(moduleSymbol, context, null)
{
}
public MetadataDecoder(
PEModuleSymbol moduleSymbol,
PEMethodSymbol context) :
this(moduleSymbol, (PENamedTypeSymbol)context.ContainingType, context)
{
}
public MetadataDecoder(
PEModuleSymbol moduleSymbol) :
this(moduleSymbol, null, null)
{
}
private MetadataDecoder(PEModuleSymbol moduleSymbol, PENamedTypeSymbol typeContextOpt, PEMethodSymbol methodContextOpt)
// TODO (tomat): if the containing assembly is a source assembly and we are about to decode assembly level attributes, we run into a cycle,
// so for now ignore the assembly identity.
: base(moduleSymbol.Module, (moduleSymbol.ContainingAssembly is PEAssemblySymbol) ? moduleSymbol.ContainingAssembly.Identity : null, SymbolFactory.Instance, moduleSymbol)
{
Debug.Assert((object)moduleSymbol != null);
_typeContextOpt = typeContextOpt;
_methodContextOpt = methodContextOpt;
}
internal PEModuleSymbol ModuleSymbol
{
get { return moduleSymbol; }
}
protected override TypeSymbol GetGenericMethodTypeParamSymbol(int position)
{
if ((object)_methodContextOpt == null)
{
return new UnsupportedMetadataTypeSymbol(); // type parameter not associated with a method
}
var typeParameters = _methodContextOpt.TypeParameters;
if (typeParameters.Length <= position)
{
return new UnsupportedMetadataTypeSymbol(); // type parameter position too large
}
return typeParameters[position];
}
protected override TypeSymbol GetGenericTypeParamSymbol(int position)
{
PENamedTypeSymbol type = _typeContextOpt;
while ((object)type != null && (type.MetadataArity - type.Arity) > position)
{
type = type.ContainingSymbol as PENamedTypeSymbol;
}
if ((object)type == null || type.MetadataArity <= position)
{
return new UnsupportedMetadataTypeSymbol(); // position of type parameter too large
}
position -= type.MetadataArity - type.Arity;
Debug.Assert(position >= 0 && position < type.Arity);
return type.TypeParameters[position];
}
protected override ConcurrentDictionary<TypeDefinitionHandle, TypeSymbol> GetTypeHandleToTypeMap()
{
return moduleSymbol.TypeHandleToTypeMap;
}
protected override ConcurrentDictionary<TypeReferenceHandle, TypeSymbol> GetTypeRefHandleToTypeMap()
{
return moduleSymbol.TypeRefHandleToTypeMap;
}
protected override TypeSymbol LookupNestedTypeDefSymbol(TypeSymbol container, ref MetadataTypeName emittedName)
{
var result = container.LookupMetadataType(ref emittedName);
Debug.Assert((object)result != null);
return result;
}
/// <summary>
/// Lookup a type defined in referenced assembly.
/// </summary>
/// <param name="referencedAssemblyIndex"></param>
/// <param name="emittedName"></param>
protected override TypeSymbol LookupTopLevelTypeDefSymbol(
int referencedAssemblyIndex,
ref MetadataTypeName emittedName)
{
var assembly = moduleSymbol.GetReferencedAssemblySymbol(referencedAssemblyIndex);
if ((object)assembly == null)
{
return new UnsupportedMetadataTypeSymbol();
}
try
{
return assembly.LookupTopLevelMetadataType(ref emittedName, digThroughForwardedTypes: true);
}
catch (Exception e) when (FatalError.ReportAndPropagate(e)) // Trying to get more useful Watson dumps.
{
throw ExceptionUtilities.Unreachable;
}
}
/// <summary>
/// Lookup a type defined in a module of a multi-module assembly.
/// </summary>
protected override TypeSymbol LookupTopLevelTypeDefSymbol(string moduleName, ref MetadataTypeName emittedName, out bool isNoPiaLocalType)
{
foreach (ModuleSymbol m in moduleSymbol.ContainingAssembly.Modules)
{
if (string.Equals(m.Name, moduleName, StringComparison.OrdinalIgnoreCase))
{
if ((object)m == (object)moduleSymbol)
{
return moduleSymbol.LookupTopLevelMetadataType(ref emittedName, out isNoPiaLocalType);
}
else
{
isNoPiaLocalType = false;
return m.LookupTopLevelMetadataType(ref emittedName);
}
}
}
isNoPiaLocalType = false;
return new MissingMetadataTypeSymbol.TopLevel(new MissingModuleSymbolWithName(moduleSymbol.ContainingAssembly, moduleName), ref emittedName, SpecialType.None);
}
/// <summary>
/// Lookup a type defined in this module.
/// This method will be called only if the type we are
/// looking for hasn't been loaded yet. Otherwise, MetadataDecoder
/// would have found the type in TypeDefRowIdToTypeMap based on its
/// TypeDef row id.
/// </summary>
protected override TypeSymbol LookupTopLevelTypeDefSymbol(ref MetadataTypeName emittedName, out bool isNoPiaLocalType)
{
return moduleSymbol.LookupTopLevelMetadataType(ref emittedName, out isNoPiaLocalType);
}
protected override int GetIndexOfReferencedAssembly(AssemblyIdentity identity)
{
// Go through all assemblies referenced by the current module and
// find the one which *exactly* matches the given identity.
// No unification will be performed
var assemblies = this.moduleSymbol.GetReferencedAssemblies();
for (int i = 0; i < assemblies.Length; i++)
{
if (identity.Equals(assemblies[i]))
{
return i;
}
}
return -1;
}
/// <summary>
/// Perform a check whether the type or at least one of its generic arguments
/// is defined in the specified assemblies. The check is performed recursively.
/// </summary>
public static bool IsOrClosedOverATypeFromAssemblies(TypeSymbol symbol, ImmutableArray<AssemblySymbol> assemblies)
{
switch (symbol.Kind)
{
case SymbolKind.TypeParameter:
return false;
case SymbolKind.ArrayType:
return IsOrClosedOverATypeFromAssemblies(((ArrayTypeSymbol)symbol).ElementType, assemblies);
case SymbolKind.PointerType:
return IsOrClosedOverATypeFromAssemblies(((PointerTypeSymbol)symbol).PointedAtType, assemblies);
case SymbolKind.DynamicType:
return false;
case SymbolKind.ErrorType:
goto case SymbolKind.NamedType;
case SymbolKind.NamedType:
var namedType = (NamedTypeSymbol)symbol;
AssemblySymbol containingAssembly = symbol.OriginalDefinition.ContainingAssembly;
int i;
if ((object)containingAssembly != null)
{
for (i = 0; i < assemblies.Length; i++)
{
if (ReferenceEquals(containingAssembly, assemblies[i]))
{
return true;
}
}
}
do
{
var arguments = namedType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics;
int count = arguments.Length;
for (i = 0; i < count; i++)
{
if (IsOrClosedOverATypeFromAssemblies(arguments[i].Type, assemblies))
{
return true;
}
}
namedType = namedType.ContainingType;
}
while ((object)namedType != null);
return false;
default:
throw ExceptionUtilities.UnexpectedValue(symbol.Kind);
}
}
protected override TypeSymbol SubstituteNoPiaLocalType(
TypeDefinitionHandle typeDef,
ref MetadataTypeName name,
string interfaceGuid,
string scope,
string identifier)
{
TypeSymbol result;
try
{
bool isInterface = Module.IsInterfaceOrThrow(typeDef);
TypeSymbol baseType = null;
if (!isInterface)
{
EntityHandle baseToken = Module.GetBaseTypeOfTypeOrThrow(typeDef);
if (!baseToken.IsNil)
{
baseType = GetTypeOfToken(baseToken);
}
}
result = SubstituteNoPiaLocalType(
ref name,
isInterface,
baseType,
interfaceGuid,
scope,
identifier,
moduleSymbol.ContainingAssembly);
}
catch (BadImageFormatException mrEx)
{
result = GetUnsupportedMetadataTypeSymbol(mrEx);
}
Debug.Assert((object)result != null);
ConcurrentDictionary<TypeDefinitionHandle, TypeSymbol> cache = GetTypeHandleToTypeMap();
Debug.Assert(cache != null);
TypeSymbol newresult = cache.GetOrAdd(typeDef, result);
Debug.Assert(ReferenceEquals(newresult, result) || (newresult.Kind == SymbolKind.ErrorType));
return newresult;
}
/// <summary>
/// Find canonical type for NoPia embedded type.
/// </summary>
/// <returns>
/// Symbol for the canonical type or an ErrorTypeSymbol. Never returns null.
/// </returns>
internal static NamedTypeSymbol SubstituteNoPiaLocalType(
ref MetadataTypeName name,
bool isInterface,
TypeSymbol baseType,
string interfaceGuid,
string scope,
string identifier,
AssemblySymbol referringAssembly)
{
NamedTypeSymbol result = null;
Guid interfaceGuidValue = new Guid();
bool haveInterfaceGuidValue = false;
Guid scopeGuidValue = new Guid();
bool haveScopeGuidValue = false;
if (isInterface && interfaceGuid != null)
{
haveInterfaceGuidValue = Guid.TryParse(interfaceGuid, out interfaceGuidValue);
if (haveInterfaceGuidValue)
{
// To have consistent errors.
scope = null;
identifier = null;
}
}
if (scope != null)
{
haveScopeGuidValue = Guid.TryParse(scope, out scopeGuidValue);
}
foreach (AssemblySymbol assembly in referringAssembly.GetNoPiaResolutionAssemblies())
{
Debug.Assert((object)assembly != null);
if (ReferenceEquals(assembly, referringAssembly))
{
continue;
}
NamedTypeSymbol candidate = assembly.LookupTopLevelMetadataType(ref name, digThroughForwardedTypes: false);
Debug.Assert(!candidate.IsGenericType);
// Ignore type forwarders, error symbols and non-public types
if (candidate.Kind == SymbolKind.ErrorType ||
!ReferenceEquals(candidate.ContainingAssembly, assembly) ||
candidate.DeclaredAccessibility != Accessibility.Public)
{
continue;
}
// Ignore NoPia local types.
// If candidate is coming from metadata, we don't need to do any special check,
// because we do not create symbols for local types. However, local types defined in source
// is another story. However, if compilation explicitly defines a local type, it should be
// represented by a retargeting assembly, which is supposed to hide the local type.
Debug.Assert(!(assembly is SourceAssemblySymbol) || !((SourceAssemblySymbol)assembly).SourceModule.MightContainNoPiaLocalTypes());
string candidateGuid;
bool haveCandidateGuidValue = false;
Guid candidateGuidValue = new Guid();
// The type must be of the same kind (interface, struct, delegate or enum).
switch (candidate.TypeKind)
{
case TypeKind.Interface:
if (!isInterface)
{
continue;
}
// Get candidate's Guid
if (candidate.GetGuidString(out candidateGuid) && candidateGuid != null)
{
haveCandidateGuidValue = Guid.TryParse(candidateGuid, out candidateGuidValue);
}
break;
case TypeKind.Delegate:
case TypeKind.Enum:
case TypeKind.Struct:
if (isInterface)
{
continue;
}
// Let's use a trick. To make sure the kind is the same, make sure
// base type is the same.
SpecialType baseSpecialType = (candidate.BaseTypeNoUseSiteDiagnostics?.SpecialType ?? SpecialType.None);
if (baseSpecialType == SpecialType.None || baseSpecialType != (baseType?.SpecialType ?? SpecialType.None))
{
continue;
}
break;
default:
continue;
}
if (haveInterfaceGuidValue || haveCandidateGuidValue)
{
if (!haveInterfaceGuidValue || !haveCandidateGuidValue ||
candidateGuidValue != interfaceGuidValue)
{
continue;
}
}
else
{
if (!haveScopeGuidValue || identifier == null || !identifier.Equals(name.FullName))
{
continue;
}
// Scope guid must match candidate's assembly guid.
haveCandidateGuidValue = false;
if (assembly.GetGuidString(out candidateGuid) && candidateGuid != null)
{
haveCandidateGuidValue = Guid.TryParse(candidateGuid, out candidateGuidValue);
}
if (!haveCandidateGuidValue || scopeGuidValue != candidateGuidValue)
{
continue;
}
}
// OK. It looks like we found canonical type definition.
if ((object)result != null)
{
// Ambiguity
result = new NoPiaAmbiguousCanonicalTypeSymbol(referringAssembly, result, candidate);
break;
}
result = candidate;
}
if ((object)result == null)
{
result = new NoPiaMissingCanonicalTypeSymbol(
referringAssembly,
name.FullName,
interfaceGuid,
scope,
identifier);
}
return result;
}
protected override MethodSymbol FindMethodSymbolInType(TypeSymbol typeSymbol, MethodDefinitionHandle targetMethodDef)
{
Debug.Assert(typeSymbol is PENamedTypeSymbol || typeSymbol is ErrorTypeSymbol);
foreach (Symbol member in typeSymbol.GetMembersUnordered())
{
PEMethodSymbol method = member as PEMethodSymbol;
if ((object)method != null && method.Handle == targetMethodDef)
{
return method;
}
}
return null;
}
protected override FieldSymbol FindFieldSymbolInType(TypeSymbol typeSymbol, FieldDefinitionHandle fieldDef)
{
Debug.Assert(typeSymbol is PENamedTypeSymbol || typeSymbol is ErrorTypeSymbol);
foreach (Symbol member in typeSymbol.GetMembersUnordered())
{
PEFieldSymbol field = member as PEFieldSymbol;
if ((object)field != null && field.Handle == fieldDef)
{
return field;
}
}
return null;
}
internal override Symbol GetSymbolForMemberRef(MemberReferenceHandle memberRef, TypeSymbol scope = null, bool methodsOnly = false)
{
TypeSymbol targetTypeSymbol = GetMemberRefTypeSymbol(memberRef);
if (targetTypeSymbol is null)
{
return null;
}
Debug.Assert(!targetTypeSymbol.IsTupleType);
if ((object)scope != null)
{
Debug.Assert(scope.Kind == SymbolKind.NamedType || scope.Kind == SymbolKind.ErrorType);
// We only want to consider members that are at or above "scope" in the type hierarchy.
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
if (!TypeSymbol.Equals(scope, targetTypeSymbol, TypeCompareKind.ConsiderEverything2) &&
!(targetTypeSymbol.IsInterfaceType()
? scope.AllInterfacesNoUseSiteDiagnostics.IndexOf((NamedTypeSymbol)targetTypeSymbol, 0, SymbolEqualityComparer.CLRSignature) != -1
: scope.IsDerivedFrom(targetTypeSymbol, TypeCompareKind.CLRSignatureCompareOptions, useSiteDiagnostics: ref useSiteDiagnostics)))
{
return null;
}
}
if (!targetTypeSymbol.IsTupleType)
{
targetTypeSymbol = TupleTypeDecoder.DecodeTupleTypesIfApplicable(targetTypeSymbol, elementNames: default);
}
// We're going to use a special decoder that can generate usable symbols for type parameters without full context.
// (We're not just using a different type - we're also changing the type context.)
var memberRefDecoder = new MemberRefMetadataDecoder(moduleSymbol, targetTypeSymbol);
return memberRefDecoder.FindMember(targetTypeSymbol, memberRef, methodsOnly);
}
protected override void EnqueueTypeSymbolInterfacesAndBaseTypes(Queue<TypeDefinitionHandle> typeDefsToSearch, Queue<TypeSymbol> typeSymbolsToSearch, TypeSymbol typeSymbol)
{
foreach (NamedTypeSymbol @interface in typeSymbol.InterfacesNoUseSiteDiagnostics())
{
EnqueueTypeSymbol(typeDefsToSearch, typeSymbolsToSearch, @interface);
}
EnqueueTypeSymbol(typeDefsToSearch, typeSymbolsToSearch, typeSymbol.BaseTypeNoUseSiteDiagnostics);
}
protected override void EnqueueTypeSymbol(Queue<TypeDefinitionHandle> typeDefsToSearch, Queue<TypeSymbol> typeSymbolsToSearch, TypeSymbol typeSymbol)
{
if ((object)typeSymbol != null)
{
PENamedTypeSymbol peTypeSymbol = typeSymbol as PENamedTypeSymbol;
if ((object)peTypeSymbol != null && ReferenceEquals(peTypeSymbol.ContainingPEModule, moduleSymbol))
{
typeDefsToSearch.Enqueue(peTypeSymbol.Handle);
}
else
{
typeSymbolsToSearch.Enqueue(typeSymbol);
}
}
}
protected override MethodDefinitionHandle GetMethodHandle(MethodSymbol method)
{
PEMethodSymbol peMethod = method as PEMethodSymbol;
if ((object)peMethod != null && ReferenceEquals(peMethod.ContainingModule, moduleSymbol))
{
return peMethod.Handle;
}
return default(MethodDefinitionHandle);
}
}
}