protected void ComputeMetadata <TPolicy>( TPolicy policy, 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) where TPolicy : struct, IMetadataPolicy { var transformed = MetadataTransform.Run(policy, GetCompilationModulesWithMetadata()); MetadataTransform transform = transformed.Transform; // TODO: DeveloperExperienceMode: Use transformed.Transform.HandleType() to generate // TypeReference records for _typeDefinitionsGenerated that don't have metadata. // (To be used in MissingMetadataException messages) // Generate metadata blob var writer = new MetadataWriter(); writer.ScopeDefinitions.AddRange(transformed.Scopes); // Generate entries in the blob for methods that will be necessary for stack trace purposes. var stackTraceRecords = new List <KeyValuePair <MethodDesc, MetadataRecord> >(); foreach (var methodBody in GetCompiledMethodBodies()) { MethodDesc method = methodBody.Method; MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); // Methods that will end up in the reflection invoke table should not have an entry in stack trace table // We'll try looking them up in reflection data at runtime. if (transformed.GetTransformedMethodDefinition(typicalMethod) != null && ShouldMethodBeInInvokeMap(method) && (GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) != 0) { continue; } if (!_stackTraceEmissionPolicy.ShouldIncludeMethod(method)) { continue; } MetadataRecord record = CreateStackTraceRecord(transform, method); stackTraceRecords.Add(new KeyValuePair <MethodDesc, MetadataRecord>( method, record)); writer.AdditionalRootRecords.Add(record); } var ms = new MemoryStream(); // .NET metadata is UTF-16 and UTF-16 contains code points that don't translate to UTF-8. var noThrowUtf8Encoding = new UTF8Encoding(false, false); using (var logWriter = _metadataLogFile != null ? new StreamWriter(File.Open(_metadataLogFile, FileMode.Create, FileAccess.Write, FileShare.Read), noThrowUtf8Encoding) : null) { writer.LogWriter = logWriter; writer.Write(ms); } metadataBlob = ms.ToArray(); const int MaxAllowedMetadataOffset = 0xFFFFFF; if (metadataBlob.Length > MaxAllowedMetadataOffset) { // Offset portion of metadata handles is limited to 16 MB. throw new InvalidOperationException($"Metadata blob exceeded the addressing range (allowed: {MaxAllowedMetadataOffset}, actual: {metadataBlob.Length})"); } typeMappings = new List <MetadataMapping <MetadataType> >(); methodMappings = new List <MetadataMapping <MethodDesc> >(); fieldMappings = new List <MetadataMapping <FieldDesc> >(); stackTraceMapping = new List <MetadataMapping <MethodDesc> >(); // Generate type definition mappings foreach (var type in factory.MetadataManager.GetTypesWithEETypes()) { MetadataType definition = type.IsTypeDefinition ? type as MetadataType : null; if (definition == null) { continue; } MetadataRecord record = transformed.GetTransformedTypeDefinition(definition); // Reflection requires that we maintain type identity. Even if we only generated a TypeReference record, // if there is an MethodTable for it, we also need a mapping table entry for it. if (record == null) { record = transformed.GetTransformedTypeReference(definition); } if (record != null) { typeMappings.Add(new MetadataMapping <MetadataType>(definition, writer.GetRecordHandle(record))); } } HashSet <MethodDesc> canonicalGenericMethods = new HashSet <MethodDesc>(); foreach (var method in GetReflectableMethods()) { if (method.IsGenericMethodDefinition || method.OwningType.IsGenericDefinition) { // Generic definitions don't have runtime artifacts we would need to map to. continue; } if ((method.HasInstantiation && method.IsCanonicalMethod(CanonicalFormKind.Specific)) || (!method.HasInstantiation && method.GetCanonMethodTarget(CanonicalFormKind.Specific) != method)) { // Methods that are not in their canonical form are not interesting with the exception // of generic methods: their dictionaries convey their identity. continue; } if (IsReflectionBlocked(method.Instantiation) || IsReflectionBlocked(method.OwningType.Instantiation)) { continue; } if ((GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) == 0) { continue; } // If we already added a canonically equivalent generic method, skip this one. if (method.HasInstantiation && !canonicalGenericMethods.Add(method.GetCanonMethodTarget(CanonicalFormKind.Specific))) { continue; } MetadataRecord record = transformed.GetTransformedMethodDefinition(method.GetTypicalMethodDefinition()); if (record != null) { methodMappings.Add(new MetadataMapping <MethodDesc>(method, writer.GetRecordHandle(record))); } } HashSet <FieldDesc> canonicalFields = new HashSet <FieldDesc>(); foreach (var field in GetFieldsWithRuntimeMapping()) { FieldDesc fieldToAdd = field; if (!field.IsStatic) { TypeDesc canonOwningType = field.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); if (canonOwningType != field.OwningType) { FieldDesc canonField = _typeSystemContext.GetFieldForInstantiatedType(field.GetTypicalFieldDefinition(), (InstantiatedType)canonOwningType); // If we already added a canonically equivalent field, skip this one. if (!canonicalFields.Add(canonField)) { continue; } fieldToAdd = canonField; } } Field record = transformed.GetTransformedFieldDefinition(fieldToAdd.GetTypicalFieldDefinition()); if (record != null) { fieldMappings.Add(new MetadataMapping <FieldDesc>(fieldToAdd, writer.GetRecordHandle(record))); } } // Generate stack trace metadata mapping foreach (var stackTraceRecord in stackTraceRecords) { stackTraceMapping.Add(new MetadataMapping <MethodDesc>(stackTraceRecord.Key, writer.GetRecordHandle(stackTraceRecord.Value))); } }
protected void ComputeMetadata <TPolicy>( TPolicy policy, 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) where TPolicy : struct, IMetadataPolicy { var transformed = MetadataTransform.Run(policy, GetCompilationModulesWithMetadata()); MetadataTransform transform = transformed.Transform; // TODO: DeveloperExperienceMode: Use transformed.Transform.HandleType() to generate // TypeReference records for _typeDefinitionsGenerated that don't have metadata. // (To be used in MissingMetadataException messages) // Generate metadata blob var writer = new MetadataWriter(); writer.ScopeDefinitions.AddRange(transformed.Scopes); // Generate entries in the blob for methods that will be necessary for stack trace purposes. var stackTraceRecords = new List <KeyValuePair <MethodDesc, MetadataRecord> >(); foreach (var methodBody in GetCompiledMethodBodies()) { MethodDesc method = methodBody.Method; MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); // Methods that will end up in the reflection invoke table should not have an entry in stack trace table // We'll try looking them up in reflection data at runtime. if (transformed.GetTransformedMethodDefinition(typicalMethod) != null && ShouldMethodBeInInvokeMap(method) && (GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) != 0) { continue; } if (!_stackTraceEmissionPolicy.ShouldIncludeMethod(method)) { continue; } MetadataRecord record = CreateStackTraceRecord(transform, method); stackTraceRecords.Add(new KeyValuePair <MethodDesc, MetadataRecord>( method, record)); writer.AdditionalRootRecords.Add(record); } var ms = new MemoryStream(); // .NET metadata is UTF-16 and UTF-16 contains code points that don't translate to UTF-8. var noThrowUtf8Encoding = new UTF8Encoding(false, false); using (var logWriter = _metadataLogFile != null ? new StreamWriter(File.Open(_metadataLogFile, FileMode.Create, FileAccess.Write, FileShare.Read), noThrowUtf8Encoding) : null) { writer.LogWriter = logWriter; writer.Write(ms); } metadataBlob = ms.ToArray(); typeMappings = new List <MetadataMapping <MetadataType> >(); methodMappings = new List <MetadataMapping <MethodDesc> >(); fieldMappings = new List <MetadataMapping <FieldDesc> >(); stackTraceMapping = new List <MetadataMapping <MethodDesc> >(); // Generate type definition mappings foreach (var type in factory.MetadataManager.GetTypesWithEETypes()) { MetadataType definition = type.IsTypeDefinition ? type as MetadataType : null; if (definition == null) { continue; } MetadataRecord record = transformed.GetTransformedTypeDefinition(definition); // Reflection requires that we maintain type identity. Even if we only generated a TypeReference record, // if there is an EEType for it, we also need a mapping table entry for it. if (record == null) { record = transformed.GetTransformedTypeReference(definition); } if (record != null) { typeMappings.Add(new MetadataMapping <MetadataType>(definition, writer.GetRecordHandle(record))); } } foreach (var method in GetCompiledMethods()) { if (method.IsCanonicalMethod(CanonicalFormKind.Specific)) { // Canonical methods are not interesting. continue; } if (IsReflectionBlocked(method.Instantiation) || IsReflectionBlocked(method.OwningType.Instantiation)) { continue; } if ((GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) == 0) { continue; } MetadataRecord record = transformed.GetTransformedMethodDefinition(method.GetTypicalMethodDefinition()); if (record != null) { methodMappings.Add(new MetadataMapping <MethodDesc>(method, writer.GetRecordHandle(record))); } } foreach (var field in GetFieldsWithRuntimeMapping()) { Field record = transformed.GetTransformedFieldDefinition(field.GetTypicalFieldDefinition()); if (record != null) { fieldMappings.Add(new MetadataMapping <FieldDesc>(field, writer.GetRecordHandle(record))); } } // Generate stack trace metadata mapping foreach (var stackTraceRecord in stackTraceRecords) { stackTraceMapping.Add(new MetadataMapping <MethodDesc>(stackTraceRecord.Key, writer.GetRecordHandle(stackTraceRecord.Value))); } }