1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Collections.Generic;
6 
7 using Internal.TypeSystem;
8 
9 using ILCompiler.Metadata;
10 using ILCompiler.DependencyAnalysis;
11 
12 using Debug = System.Diagnostics.Debug;
13 
14 namespace ILCompiler
15 {
16     /// <summary>
17     /// A metadata manager that knows the full set of metadata ahead of time.
18     /// </summary>
19     public sealed class AnalysisBasedMetadataManager : GeneratingMetadataManager, ICompilationRootProvider
20     {
21         private readonly List<ModuleDesc> _modulesWithMetadata;
22 
23         private readonly Dictionary<TypeDesc, MetadataCategory> _reflectableTypes = new Dictionary<TypeDesc, MetadataCategory>();
24         private readonly Dictionary<MethodDesc, MetadataCategory> _reflectableMethods = new Dictionary<MethodDesc, MetadataCategory>();
25         private readonly Dictionary<FieldDesc, MetadataCategory> _reflectableFields = new Dictionary<FieldDesc, MetadataCategory>();
26 
AnalysisBasedMetadataManager( ModuleDesc generatedAssembly, CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy, string logFile, StackTraceEmissionPolicy stackTracePolicy, IEnumerable<ModuleDesc> modulesWithMetadata, IEnumerable<ReflectableEntity<TypeDesc>> reflectableTypes, IEnumerable<ReflectableEntity<MethodDesc>> reflectableMethods, IEnumerable<ReflectableEntity<FieldDesc>> reflectableFields)27         public AnalysisBasedMetadataManager(
28             ModuleDesc generatedAssembly,
29             CompilerTypeSystemContext typeSystemContext,
30             MetadataBlockingPolicy blockingPolicy,
31             string logFile,
32             StackTraceEmissionPolicy stackTracePolicy,
33             IEnumerable<ModuleDesc> modulesWithMetadata,
34             IEnumerable<ReflectableEntity<TypeDesc>> reflectableTypes,
35             IEnumerable<ReflectableEntity<MethodDesc>> reflectableMethods,
36             IEnumerable<ReflectableEntity<FieldDesc>> reflectableFields)
37             : base(generatedAssembly, typeSystemContext, blockingPolicy, logFile, stackTracePolicy)
38         {
39             _modulesWithMetadata = new List<ModuleDesc>(modulesWithMetadata);
40 
41             foreach (var refType in reflectableTypes)
42             {
43                 _reflectableTypes.Add(refType.Entity, refType.Category);
44             }
45 
46             foreach (var refMethod in reflectableMethods)
47             {
48                 // Asking for description or runtime mapping for a member without asking
49                 // for the owning type would mean we can't actually satisfy the request.
50                 Debug.Assert((refMethod.Category & MetadataCategory.Description) == 0
51                     || (_reflectableTypes[refMethod.Entity.OwningType] & MetadataCategory.Description) != 0);
52                 Debug.Assert((refMethod.Category & MetadataCategory.RuntimeMapping) == 0
53                     || (_reflectableTypes[refMethod.Entity.OwningType] & MetadataCategory.RuntimeMapping) != 0);
54                 _reflectableMethods.Add(refMethod.Entity, refMethod.Category);
55 
56                 MethodDesc canonMethod = refMethod.Entity.GetCanonMethodTarget(CanonicalFormKind.Specific);
57                 if (refMethod.Entity != canonMethod)
58                 {
59                     if (!_reflectableMethods.TryGetValue(canonMethod, out MetadataCategory category))
60                         category = 0;
61                     _reflectableMethods[canonMethod] = category | refMethod.Category;
62                 }
63             }
64 
65             foreach (var refField in reflectableFields)
66             {
67                 // Asking for description or runtime mapping for a member without asking
68                 // for the owning type would mean we can't actually satisfy the request.
69                 Debug.Assert((refField.Category & MetadataCategory.Description) == 0
70                     || (_reflectableTypes[refField.Entity.OwningType] & MetadataCategory.Description) != 0);
71                 Debug.Assert((refField.Category & MetadataCategory.RuntimeMapping) == 0
72                     || (_reflectableTypes[refField.Entity.OwningType] & MetadataCategory.RuntimeMapping) != 0);
73                 _reflectableFields.Add(refField.Entity, refField.Category);
74             }
75 
76 #if DEBUG
77             HashSet<ModuleDesc> moduleHash = new HashSet<ModuleDesc>(_modulesWithMetadata);
78             foreach (var refType in reflectableTypes)
79             {
80                 // The instantiated types need to agree on the Description bit with the definition.
81                 // GetMetadataCategory relies on that.
82                 Debug.Assert((GetMetadataCategory(refType.Entity.GetTypeDefinition()) & MetadataCategory.Description)
83                     == (GetMetadataCategory(refType.Entity) & MetadataCategory.Description));
84 
85                 Debug.Assert(!(refType.Entity is MetadataType) || moduleHash.Contains(((MetadataType)refType.Entity).Module));
86             }
87 
88             foreach (var refMethod in reflectableMethods)
89             {
90                 // The instantiated methods need to agree on the Description bit with the definition.
91                 // GetMetadataCategory relies on that.
92                 Debug.Assert((GetMetadataCategory(refMethod.Entity.GetTypicalMethodDefinition()) & MetadataCategory.Description)
93                     == (GetMetadataCategory(refMethod.Entity) & MetadataCategory.Description));
94             }
95 
96             foreach (var refField in reflectableFields)
97             {
98                 // The instantiated fields need to agree on the Description bit with the definition.
99                 // GetMetadataCategory relies on that.
100                 Debug.Assert((GetMetadataCategory(refField.Entity.GetTypicalFieldDefinition()) & MetadataCategory.Description)
101                     == (GetMetadataCategory(refField.Entity) & MetadataCategory.Description));
102             }
103 #endif
104         }
105 
GetCompilationModulesWithMetadata()106         public override IEnumerable<ModuleDesc> GetCompilationModulesWithMetadata()
107         {
108             return _modulesWithMetadata;
109         }
110 
ComputeMetadata(NodeFactory factory, out byte[] metadataBlob, out List<MetadataMapping<MetadataType>> typeMappings, out List<MetadataMapping<MethodDesc>> methodMappings, out List<MetadataMapping<FieldDesc>> fieldMappings, out List<MetadataMapping<MethodDesc>> stackTraceMapping)111         protected override void ComputeMetadata(NodeFactory factory,
112             out byte[] metadataBlob,
113             out List<MetadataMapping<MetadataType>> typeMappings,
114             out List<MetadataMapping<MethodDesc>> methodMappings,
115             out List<MetadataMapping<FieldDesc>> fieldMappings,
116             out List<MetadataMapping<MethodDesc>> stackTraceMapping)
117         {
118             ComputeMetadata(new Policy(_blockingPolicy, this), factory,
119                 out metadataBlob,
120                 out typeMappings,
121                 out methodMappings,
122                 out fieldMappings,
123                 out stackTraceMapping);
124         }
125 
GetMetadataCategory(MethodDesc method)126         protected sealed override MetadataCategory GetMetadataCategory(MethodDesc method)
127         {
128             if (_reflectableMethods.TryGetValue(method, out MetadataCategory value))
129                 return value;
130             return 0;
131         }
132 
GetMetadataCategory(TypeDesc type)133         protected sealed override MetadataCategory GetMetadataCategory(TypeDesc type)
134         {
135             if (_reflectableTypes.TryGetValue(type, out MetadataCategory value))
136                 return value;
137             return 0;
138         }
139 
GetMetadataCategory(FieldDesc field)140         protected sealed override MetadataCategory GetMetadataCategory(FieldDesc field)
141         {
142             if (_reflectableFields.TryGetValue(field, out MetadataCategory value))
143                 return value;
144             return 0;
145         }
146 
GetFieldsWithRuntimeMapping()147         protected override IEnumerable<FieldDesc> GetFieldsWithRuntimeMapping()
148         {
149             foreach (var pair in _reflectableFields)
150             {
151                 if ((pair.Value & MetadataCategory.RuntimeMapping) != 0)
152                     yield return pair.Key;
153             }
154         }
155 
ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootProvider)156         void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootProvider)
157         {
158             // We go over all the types and members that need a runtime artiface present in the
159             // compiled executable and root it.
160 
161             const string reason = "Reflection";
162 
163             foreach (var pair in _reflectableTypes)
164             {
165                 if ((pair.Value & MetadataCategory.RuntimeMapping) != 0)
166                     rootProvider.AddCompilationRoot(pair.Key, reason);
167             }
168 
169             foreach (var pair in _reflectableMethods)
170             {
171                 if ((pair.Value & MetadataCategory.RuntimeMapping) != 0)
172                 {
173                     MethodDesc method = pair.Key;
174 
175                     // We need to root virtual methods as if they were called virtually.
176                     // This will possibly trigger the generation of other overrides too.
177                     if (method.IsVirtual)
178                         rootProvider.RootVirtualMethodForReflection(method, reason);
179 
180                     if (!method.IsAbstract)
181                         rootProvider.AddCompilationRoot(pair.Key, reason);
182                 }
183             }
184 
185             foreach (var pair in _reflectableFields)
186             {
187                 if ((pair.Value & MetadataCategory.RuntimeMapping) != 0)
188                 {
189                     FieldDesc field = pair.Key;
190 
191                     // We only care about static fields at this point. Instance fields don't need
192                     // runtime artifacts generated in the image.
193                     if (field.IsStatic && !field.IsLiteral)
194                     {
195                         if (field.IsThreadStatic)
196                             rootProvider.RootThreadStaticBaseForType(field.OwningType, reason);
197                         else if (field.HasGCStaticBase)
198                             rootProvider.RootGCStaticBaseForType(field.OwningType, reason);
199                         else
200                             rootProvider.RootNonGCStaticBaseForType(field.OwningType, reason);
201                     }
202                 }
203             }
204         }
205 
206         private struct Policy : IMetadataPolicy
207         {
208             private readonly MetadataBlockingPolicy _blockingPolicy;
209             private readonly ExplicitScopeAssemblyPolicyMixin _explicitScopeMixin;
210             private readonly AnalysisBasedMetadataManager _parent;
211 
PolicyILCompiler.AnalysisBasedMetadataManager.Policy212             public Policy(MetadataBlockingPolicy blockingPolicy,
213                 AnalysisBasedMetadataManager parent)
214             {
215                 _blockingPolicy = blockingPolicy;
216                 _parent = parent;
217                 _explicitScopeMixin = new ExplicitScopeAssemblyPolicyMixin();
218             }
219 
GeneratesMetadataILCompiler.AnalysisBasedMetadataManager.Policy220             public bool GeneratesMetadata(FieldDesc fieldDef)
221             {
222                 return (_parent.GetMetadataCategory(fieldDef) & MetadataCategory.Description) != 0;
223             }
224 
GeneratesMetadataILCompiler.AnalysisBasedMetadataManager.Policy225             public bool GeneratesMetadata(MethodDesc methodDef)
226             {
227                 return (_parent.GetMetadataCategory(methodDef) & MetadataCategory.Description) != 0;
228             }
229 
GeneratesMetadataILCompiler.AnalysisBasedMetadataManager.Policy230             public bool GeneratesMetadata(MetadataType typeDef)
231             {
232                 return (_parent.GetMetadataCategory(typeDef) & MetadataCategory.Description) != 0;
233             }
234 
IsBlockedILCompiler.AnalysisBasedMetadataManager.Policy235             public bool IsBlocked(MetadataType typeDef)
236             {
237                 return _blockingPolicy.IsBlocked(typeDef);
238             }
239 
IsBlockedILCompiler.AnalysisBasedMetadataManager.Policy240             public bool IsBlocked(MethodDesc methodDef)
241             {
242                 return _blockingPolicy.IsBlocked(methodDef);
243             }
244 
GetModuleOfTypeILCompiler.AnalysisBasedMetadataManager.Policy245             public ModuleDesc GetModuleOfType(MetadataType typeDef)
246             {
247                 return _explicitScopeMixin.GetModuleOfType(typeDef);
248             }
249         }
250     }
251 
252     public struct ReflectableEntity<TEntity>
253     {
254         public readonly TEntity Entity;
255         public readonly MetadataCategory Category;
256 
ReflectableEntityILCompiler.ReflectableEntity257         public ReflectableEntity(TEntity entity, MetadataCategory category)
258         {
259             Entity = entity;
260             Category = category;
261         }
262     }
263 }
264