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; 6 using System.IO; 7 using System.Collections.Generic; 8 using System.Text; 9 10 using Internal.IL.Stubs; 11 using Internal.TypeSystem; 12 using Internal.TypeSystem.Ecma; 13 using Internal.Metadata.NativeFormat.Writer; 14 15 using ILCompiler.Metadata; 16 using ILCompiler.DependencyAnalysis; 17 18 using Debug = System.Diagnostics.Debug; 19 20 namespace ILCompiler 21 { 22 /// <summary> 23 /// Base class for metadata managers that generate metadata blobs. 24 /// </summary> 25 public abstract class GeneratingMetadataManager : MetadataManager 26 { 27 protected readonly string _metadataLogFile; 28 protected readonly StackTraceEmissionPolicy _stackTraceEmissionPolicy; 29 private readonly Dictionary<DynamicInvokeMethodSignature, MethodDesc> _dynamicInvokeThunks; 30 private readonly ModuleDesc _generatedAssembly; 31 GeneratingMetadataManager(ModuleDesc generatedAssembly, CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy, string logFile, StackTraceEmissionPolicy stackTracePolicy)32 public GeneratingMetadataManager(ModuleDesc generatedAssembly, CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy, string logFile, StackTraceEmissionPolicy stackTracePolicy) 33 : base(typeSystemContext, blockingPolicy) 34 { 35 _metadataLogFile = logFile; 36 _stackTraceEmissionPolicy = stackTracePolicy; 37 _generatedAssembly = generatedAssembly; 38 39 if (DynamicInvokeMethodThunk.SupportsThunks(typeSystemContext)) 40 { 41 _dynamicInvokeThunks = new Dictionary<DynamicInvokeMethodSignature, MethodDesc>(); 42 } 43 } 44 WillUseMetadataTokenToReferenceMethod(MethodDesc method)45 public sealed override bool WillUseMetadataTokenToReferenceMethod(MethodDesc method) 46 { 47 return (GetMetadataCategory(method) & MetadataCategory.Description) != 0; 48 } 49 WillUseMetadataTokenToReferenceField(FieldDesc field)50 public sealed override bool WillUseMetadataTokenToReferenceField(FieldDesc field) 51 { 52 return (GetMetadataCategory(field) & MetadataCategory.Description) != 0; 53 } 54 55 protected void ComputeMetadata<TPolicy>( 56 TPolicy policy, 57 NodeFactory factory, 58 out byte[] metadataBlob, 59 out List<MetadataMapping<MetadataType>> typeMappings, 60 out List<MetadataMapping<MethodDesc>> methodMappings, 61 out List<MetadataMapping<FieldDesc>> fieldMappings, 62 out List<MetadataMapping<MethodDesc>> stackTraceMapping) where TPolicy : struct, IMetadataPolicy 63 { 64 var transformed = MetadataTransform.Run(policy, GetCompilationModulesWithMetadata()); 65 MetadataTransform transform = transformed.Transform; 66 67 // TODO: DeveloperExperienceMode: Use transformed.Transform.HandleType() to generate 68 // TypeReference records for _typeDefinitionsGenerated that don't have metadata. 69 // (To be used in MissingMetadataException messages) 70 71 // Generate metadata blob 72 var writer = new MetadataWriter(); writer.ScopeDefinitions.AddRangeILCompiler.GeneratingMetadataManager.IMetadataPolicy73 writer.ScopeDefinitions.AddRange(transformed.Scopes); 74 75 // Generate entries in the blob for methods that will be necessary for stack trace purposes. 76 var stackTraceRecords = new List<KeyValuePair<MethodDesc, MetadataRecord>>(); 77 foreach (var methodBody in GetCompiledMethodBodies()) 78 { 79 MethodDesc method = methodBody.Method; 80 81 MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); 82 83 // Methods that will end up in the reflection invoke table should not have an entry in stack trace table 84 // We'll try looking them up in reflection data at runtime. 85 if (transformed.GetTransformedMethodDefinition(typicalMethod) != null && 86 ShouldMethodBeInInvokeMap(method) && 87 (GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) != 0) 88 continue; 89 90 if (!_stackTraceEmissionPolicy.ShouldIncludeMethod(method)) 91 continue; 92 93 MetadataRecord record = transform.HandleQualifiedMethod(typicalMethod); 94 95 // As a twist, instantiated generic methods appear as if instantiated over their formals. 96 if (typicalMethod.HasInstantiation) 97 { 98 var methodInst = new MethodInstantiation 99 { 100 Method = record, 101 }; 102 methodInst.GenericTypeArguments.Capacity = typicalMethod.Instantiation.Length; 103 foreach (EcmaGenericParameter typeArgument in typicalMethod.Instantiation) 104 { 105 var genericParam = new TypeReference 106 { 107 TypeName = (ConstantStringValue)typeArgument.Name, 108 }; 109 methodInst.GenericTypeArguments.Add(genericParam); 110 } 111 record = methodInst; 112 } 113 114 stackTraceRecords.Add(new KeyValuePair<MethodDesc, MetadataRecord>( 115 method, 116 record)); 117 118 writer.AdditionalRootRecords.Add(record); 119 } 120 121 var ms = new MemoryStream(); 122 123 // .NET metadata is UTF-16 and UTF-16 contains code points that don't translate to UTF-8. 124 var noThrowUtf8Encoding = new UTF8Encoding(false, false); 125 126 using (var logWriter = _metadataLogFile != null ? new StreamWriter(File.Open(_metadataLogFile, FileMode.Create, FileAccess.Write, FileShare.Read), noThrowUtf8Encoding) : null) 127 { 128 writer.LogWriter = logWriter; 129 writer.Write(ms); 130 } 131 132 metadataBlob = ms.ToArray(); 133 134 typeMappings = new List<MetadataMapping<MetadataType>>(); 135 methodMappings = new List<MetadataMapping<MethodDesc>>(); 136 fieldMappings = new List<MetadataMapping<FieldDesc>>(); 137 stackTraceMapping = new List<MetadataMapping<MethodDesc>>(); 138 139 // Generate type definition mappings 140 foreach (var type in factory.MetadataManager.GetTypesWithEETypes()) 141 { 142 MetadataType definition = type.IsTypeDefinition ? type as MetadataType : null; 143 if (definition == null) 144 continue; 145 146 MetadataRecord record = transformed.GetTransformedTypeDefinition(definition); 147 148 // Reflection requires that we maintain type identity. Even if we only generated a TypeReference record, 149 // if there is an EEType for it, we also need a mapping table entry for it. 150 if (record == null) 151 record = transformed.GetTransformedTypeReference(definition); 152 153 if (record != null) 154 typeMappings.Add(new MetadataMapping<MetadataType>(definition, writer.GetRecordHandle(record))); 155 } 156 157 foreach (var method in GetCompiledMethods()) 158 { 159 if (method.IsCanonicalMethod(CanonicalFormKind.Specific)) 160 { 161 // Canonical methods are not interesting. 162 continue; 163 } 164 165 if (IsReflectionBlocked(method.Instantiation) || IsReflectionBlocked(method.OwningType.Instantiation)) 166 continue; 167 168 if ((GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) == 0) 169 continue; 170 171 MetadataRecord record = transformed.GetTransformedMethodDefinition(method.GetTypicalMethodDefinition()); 172 173 if (record != null) 174 methodMappings.Add(new MetadataMapping<MethodDesc>(method, writer.GetRecordHandle(record))); 175 } 176 177 foreach (var field in GetFieldsWithRuntimeMapping()) 178 { 179 Field record = transformed.GetTransformedFieldDefinition(field.GetTypicalFieldDefinition()); 180 if (record != null) 181 fieldMappings.Add(new MetadataMapping<FieldDesc>(field, writer.GetRecordHandle(record))); 182 } 183 184 // Generate stack trace metadata mapping 185 foreach (var stackTraceRecord in stackTraceRecords) 186 { 187 stackTraceMapping.Add(new MetadataMapping<MethodDesc>(stackTraceRecord.Key, writer.GetRecordHandle(stackTraceRecord.Value))); 188 } 189 } 190 191 /// <summary> 192 /// Gets a list of fields that got "compiled" and are eligible for a runtime mapping. 193 /// </summary> 194 /// <returns></returns> GetFieldsWithRuntimeMapping()195 protected abstract IEnumerable<FieldDesc> GetFieldsWithRuntimeMapping(); 196 197 /// <summary> 198 /// Is there a reflection invoke stub for a method that is invokable? 199 /// </summary> HasReflectionInvokeStubForInvokableMethod(MethodDesc method)200 public sealed override bool HasReflectionInvokeStubForInvokableMethod(MethodDesc method) 201 { 202 Debug.Assert(IsReflectionInvokable(method)); 203 204 if (_dynamicInvokeThunks == null) 205 return false; 206 207 // Place an upper limit on how many parameters a method can have to still get a static stub. 208 // From the past experience, methods taking 8000+ parameters get a stub that can hit various limitations 209 // in the codegen. On Project N, we were limited to 256 parameters because of MDIL limitations. 210 // We don't have such limitations here, but it's a nice round number. 211 // Reflection invoke will still work, but will go through the calling convention converter. 212 213 return method.Signature.Length <= 256; 214 } 215 216 /// <summary> 217 /// Gets a stub that can be used to reflection-invoke a method with a given signature. 218 /// </summary> GetCanonicalReflectionInvokeStub(MethodDesc method)219 public sealed override MethodDesc GetCanonicalReflectionInvokeStub(MethodDesc method) 220 { 221 TypeSystemContext context = method.Context; 222 var sig = method.Signature; 223 224 // Get a generic method that can be used to invoke method with this shape. 225 MethodDesc thunk; 226 var lookupSig = new DynamicInvokeMethodSignature(sig); 227 if (!_dynamicInvokeThunks.TryGetValue(lookupSig, out thunk)) 228 { 229 thunk = new DynamicInvokeMethodThunk(_generatedAssembly.GetGlobalModuleType(), lookupSig); 230 _dynamicInvokeThunks.Add(lookupSig, thunk); 231 } 232 233 return InstantiateCanonicalDynamicInvokeMethodForMethod(thunk, method); 234 } 235 } 236 } 237