| File: Compiler\DependencyAnalysis\ManagedDataDescriptorNode.cs | Web Access |
| Project: src\runtime\src\coreclr\tools\aot\ILCompiler.Compiler\ILCompiler.Compiler.csproj (ILCompiler.Compiler) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Diagnostics; using System.IO; using System.Text.Json; using Internal.Text; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; namespace ILCompiler.DependencyAnalysis { /// <summary> /// Emits a ContractDescriptor for managed type layouts that the cDAC reader /// can consume as a sub-descriptor. ILC knows managed type layouts at compile time, /// so it can emit field offsets that would otherwise require runtime metadata resolution. /// /// Types are discovered by scanning MetadataManager.GetTypesWithEETypes() for types /// annotated with [DataContract], ensuring only types that actually have a MethodTable /// in the binary are included. /// </summary> public class ManagedDataDescriptorNode : ObjectNode, ISymbolDefinitionNode { private const string DataContractAttributeNamespace = "System.Diagnostics"; private const string DataContractAttributeName = "DataContractAttribute"; public const string SymbolName = "DotNetManagedContractDescriptor"; public override ObjectNodeSection GetSection(NodeFactory factory) => factory.Target.IsWindows ? ObjectNodeSection.ReadOnlyDataSection : ObjectNodeSection.DataSection; public override bool StaticDependenciesAreComputed => true; public override bool IsShareable => false; public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.NodeMangler.ExternVariable(new Utf8String(SymbolName))); } public int Offset => 0; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { if (relocsOnly) return new ObjectData(Array.Empty<byte>(), Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this }); byte[] jsonBytes = BuildJsonDescriptor(factory); // Header layout: magic(8) + flags(4) + desc_size(4) + desc_ptr(ptr) + pointer_data_count(4) + pad(4) + pointer_data(ptr) int headerSize = 8 + 4 + 4 + factory.Target.PointerSize + 4 + 4 + factory.Target.PointerSize; ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); builder.RequireInitialPointerAlignment(); builder.AddSymbol(this); // uint64_t magic builder.EmitLong(0x0043414443434e44L); // "DNCCDAC\0" // uint32_t flags (bit 0 must be set; bit 1 indicates 32-bit pointers) uint flags = (uint)(0x01 | (factory.Target.PointerSize == 4 ? 0x02 : 0x00)); builder.EmitUInt(flags); // uint32_t descriptor_size builder.EmitUInt((uint)jsonBytes.Length); // char* descriptor — points to inline JSON after the header builder.EmitPointerReloc(this, headerSize); // uint32_t pointer_data_count = 0 builder.EmitUInt(0); // uint32_t pad0 builder.EmitUInt(0); // void** pointer_data = null builder.EmitZeroPointer(); // Emit JSON bytes inline, null-terminated Debug.Assert(builder.CountBytes == headerSize); builder.EmitBytes(jsonBytes); builder.EmitByte(0); return builder.ToObjectData(); } /// <summary> /// Build the JSON descriptor using the compact format expected by the cDAC reader's /// ContractDescriptorParser. Types are objects with an optional "!" size sigil and /// field-name properties mapped to their offsets. /// </summary> private static byte[] BuildJsonDescriptor(NodeFactory factory) { using var stream = new MemoryStream(); using (var writer = new Utf8JsonWriter(stream)) { writer.WriteStartObject(); writer.WriteNumber("version", 0); writer.WriteString("baseline", "empty"); writer.WriteStartObject("types"); foreach (TypeDesc type in factory.MetadataManager.GetTypesWithEETypes()) { if (type is not EcmaType ecmaType) continue; if (!ecmaType.HasCustomAttribute(DataContractAttributeNamespace, DataContractAttributeName)) continue; WriteType(writer, ecmaType); } writer.WriteEndObject(); writer.WriteStartObject("globals"); writer.WriteEndObject(); writer.WriteStartObject("contracts"); writer.WriteEndObject(); writer.WriteEndObject(); } return stream.ToArray(); } private static void WriteType(Utf8JsonWriter writer, EcmaType type) { writer.WriteStartObject(GetFullTypeName(type)); if (type.IsValueType) { writer.WriteNumber("!", type.InstanceFieldSize.AsInt); } foreach (FieldDesc field in type.GetFields()) { if (field.IsStatic || field is not EcmaField ecmaField) continue; if (!ecmaField.HasCustomAttribute(DataContractAttributeNamespace, DataContractAttributeName)) continue; writer.WriteNumber(field.GetName(), field.Offset.AsInt); } writer.WriteEndObject(); } /// <summary> /// Returns a fully-qualified type name for cDAC descriptors /// (e.g., "System.Threading.Thread" or "System.Foo.Outer+Inner"). /// </summary> private static string GetFullTypeName(MetadataType type) { if (type.ContainingType is not null) return $"{GetFullTypeName(type.ContainingType)}+{type.GetName()}"; string ns = type.GetNamespace(); string name = type.GetName(); if (string.IsNullOrEmpty(ns)) return name; return $"{ns}.{name}"; } public override int ClassCode => 0x4d444e01; } }