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.Diagnostics;
7 
8 using Internal.Text;
9 using Internal.TypeSystem;
10 using Internal.NativeFormat;
11 
12 using FieldTableFlags = Internal.Runtime.FieldTableFlags;
13 
14 namespace ILCompiler.DependencyAnalysis
15 {
16     /// <summary>
17     /// Represents a map between reflection metadata and native field offsets.
18     /// </summary>
19     internal sealed class ReflectionFieldMapNode : ObjectNode, ISymbolDefinitionNode
20     {
21         private ObjectAndOffsetSymbolNode _endSymbol;
22         private ExternalReferencesTableNode _externalReferences;
23 
ReflectionFieldMapNode(ExternalReferencesTableNode externalReferences)24         public ReflectionFieldMapNode(ExternalReferencesTableNode externalReferences)
25         {
26             _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__field_to_offset_map_End", true);
27             _externalReferences = externalReferences;
28         }
29 
30         public ISymbolNode EndSymbol => _endSymbol;
31 
AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)32         public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
33         {
34             sb.Append(nameMangler.CompilationUnitPrefix).Append("__field_to_offset_map");
35         }
36 
37         public int Offset => 0;
38         public override bool IsShareable => false;
39 
40         public override ObjectNodeSection Section => _externalReferences.Section;
41 
42         public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection;
43 
44         public override bool StaticDependenciesAreComputed => true;
45 
46         protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
47 
GetData(NodeFactory factory, bool relocsOnly = false)48         public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
49         {
50             // This node does not trigger generation of other nodes.
51             if (relocsOnly)
52                 return new ObjectData(Array.Empty<byte>(), Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this });
53 
54             var writer = new NativeWriter();
55             var fieldMapHashTable = new VertexHashtable();
56 
57             Section hashTableSection = writer.NewSection();
58             hashTableSection.Place(fieldMapHashTable);
59 
60             foreach (var fieldMapping in factory.MetadataManager.GetFieldMapping(factory))
61             {
62                 FieldDesc field = fieldMapping.Entity;
63 
64                 if (field.IsLiteral || field.HasRva)
65                     continue;
66 
67                 FieldTableFlags flags;
68                 if (field.IsThreadStatic)
69                 {
70                     flags = FieldTableFlags.ThreadStatic | FieldTableFlags.FieldOffsetEncodedDirectly;
71                 }
72                 else if (field.IsStatic)
73                 {
74                     flags = FieldTableFlags.Static;
75 
76                     if (field.HasGCStaticBase)
77                         flags |= FieldTableFlags.IsGcSection;
78 
79                     if (field.OwningType.HasInstantiation)
80                         flags |= FieldTableFlags.FieldOffsetEncodedDirectly;
81                 }
82                 else
83                 {
84                     flags = FieldTableFlags.Instance | FieldTableFlags.FieldOffsetEncodedDirectly;
85                 }
86 
87                 if (fieldMapping.MetadataHandle != 0)
88                     flags |= FieldTableFlags.HasMetadataHandle;
89 
90                 if (field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any))
91                     flags |= FieldTableFlags.IsAnyCanonicalEntry;
92 
93                 if (field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Universal))
94                     flags |= FieldTableFlags.IsUniversalCanonicalEntry;
95 
96                 // Grammar of a hash table entry:
97                 // Flags + DeclaringType + MdHandle or Name + Cookie or Ordinal or Offset
98 
99                 Vertex vertex = writer.GetUnsignedConstant((uint)flags);
100 
101                 uint declaringTypeId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(field.OwningType));
102                 vertex = writer.GetTuple(vertex,
103                     writer.GetUnsignedConstant(declaringTypeId));
104 
105                 if ((flags & FieldTableFlags.HasMetadataHandle) != 0)
106                 {
107                     // Only store the offset portion of the metadata handle to get better integer compression
108                     vertex = writer.GetTuple(vertex,
109                         writer.GetUnsignedConstant((uint)(fieldMapping.MetadataHandle & MetadataManager.MetadataOffsetMask)));
110                 }
111                 else
112                 {
113                     // No metadata handle means we need to store name
114                     vertex = writer.GetTuple(vertex,
115                         writer.GetStringConstant(field.Name));
116                 }
117 
118                 if ((flags & FieldTableFlags.IsUniversalCanonicalEntry) != 0)
119                 {
120                     vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant(checked((uint)field.GetFieldOrdinal())));
121                 }
122                 else
123                 {
124                     switch (flags & FieldTableFlags.StorageClass)
125                     {
126                         case FieldTableFlags.ThreadStatic:
127                             if (factory.Target.Abi == TargetAbi.ProjectN)
128                             {
129                                 if (!field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any))
130                                 {
131                                     ISymbolNode tlsOffsetForType = ((UtcNodeFactory)factory).TypeThreadStaticsOffsetSymbol((MetadataType)field.OwningType);
132                                     vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant(_externalReferences.GetIndex(tlsOffsetForType)));
133                                 }
134                                 vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant((uint)(field.Offset.AsInt)));
135                             }
136                             else
137                             {
138                                 // TODO: CoreRT
139                                 continue;
140                             }
141                             break;
142 
143                         case FieldTableFlags.Static:
144                             {
145                                 if (field.OwningType.HasInstantiation)
146                                 {
147                                     vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant((uint)(field.Offset.AsInt)));
148                                 }
149                                 else
150                                 {
151                                     MetadataType metadataType = (MetadataType)field.OwningType;
152 
153                                     ISymbolNode staticsNode = field.HasGCStaticBase ?
154                                         factory.TypeGCStaticsSymbol(metadataType) :
155                                         factory.TypeNonGCStaticsSymbol(metadataType);
156 
157                                     if (!field.HasGCStaticBase || factory.Target.Abi == TargetAbi.ProjectN)
158                                     {
159                                         uint index = _externalReferences.GetIndex(staticsNode, field.Offset.AsInt);
160                                         vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant(index));
161                                     }
162                                     else
163                                     {
164                                         Debug.Assert(field.HasGCStaticBase && factory.Target.Abi == TargetAbi.CoreRT);
165 
166                                         uint index = _externalReferences.GetIndex(staticsNode);
167                                         vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant(index));
168                                         vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant((uint)(field.Offset.AsInt)));
169                                     }
170                                 }
171                             }
172                             break;
173 
174                         case FieldTableFlags.Instance:
175                             vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant((uint)field.Offset.AsInt));
176                             break;
177                     }
178                 }
179 
180                 int hashCode = field.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific).GetHashCode();
181                 fieldMapHashTable.Append((uint)hashCode, hashTableSection.Place(vertex));
182             }
183 
184             byte[] hashTableBytes = writer.Save();
185 
186             _endSymbol.SetSymbolOffset(hashTableBytes.Length);
187 
188             return new ObjectData(hashTableBytes, Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this, _endSymbol });
189         }
190 
191         protected internal override int Phase => (int)ObjectNodePhase.Ordered;
192         protected internal override int ClassCode => (int)ObjectNodeOrder.ReflectionFieldMapNode;
193     }
194 }
195