Welcome to mirror list, hosted at ThFree Co, Russian Federation.

ReflectionVirtualInvokeMapNode.cs « DependencyAnalysis « Compiler « src « ILCompiler.Compiler « src - github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 0b256dbf1efbc6129d8b44cb78a5f77a16ecff5b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Diagnostics;
using System.Collections.Generic;

using Internal.Text;
using Internal.TypeSystem;
using Internal.NativeFormat;

using VirtualInvokeTableEntry = Internal.Runtime.VirtualInvokeTableEntry;

namespace ILCompiler.DependencyAnalysis
{
    /// <summary>
    /// Represents a map containing the necessary information needed to resolve 
    /// a virtual method target called through reflection.
    /// </summary>
    internal sealed class ReflectionVirtualInvokeMapNode : ObjectNode, ISymbolDefinitionNode
    {
        private ObjectAndOffsetSymbolNode _endSymbol;
        private ExternalReferencesTableNode _externalReferences;

        public ReflectionVirtualInvokeMapNode(ExternalReferencesTableNode externalReferences)
        {
            _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__VirtualInvokeMap_End", true);
            _externalReferences = externalReferences;
        }

        public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
        {
            sb.Append(nameMangler.CompilationUnitPrefix).Append("__VirtualInvokeMap");
        }

        public ISymbolNode EndSymbol => _endSymbol;
        public int Offset => 0;
        public override bool IsShareable => false;
        public override ObjectNodeSection Section => _externalReferences.Section;
        public override bool StaticDependenciesAreComputed => true;
        protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);

        public static bool NeedsVirtualInvokeInfo(MethodDesc method)
        {
            if (!method.IsVirtual)
                return false;

            if (method.OwningType.IsInterface)
                return true;

            if (method.IsFinal)
                return false;

            if (method.OwningType.IsSealed())
                return false;

            return true;
        }

        public static MethodDesc GetDeclaringVirtualMethodAndHierarchyDistance(MethodDesc method, out int parentHierarchyDistance)
        {
            parentHierarchyDistance = 0;

            MethodDesc declaringMethodForSlot = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method.GetTypicalMethodDefinition());
            TypeDesc typeOfDeclaringMethodForSlot = declaringMethodForSlot.OwningType.GetTypeDefinition();
            TypeDesc currentType = method.OwningType.GetTypeDefinition();
            TypeDesc containingTypeOfDeclaringMethodForSlot = method.OwningType;

            while (typeOfDeclaringMethodForSlot != currentType)
            {
                parentHierarchyDistance++;
                currentType = currentType.BaseType.GetTypeDefinition();
                containingTypeOfDeclaringMethodForSlot = containingTypeOfDeclaringMethodForSlot.BaseType;
            }

            if (containingTypeOfDeclaringMethodForSlot.HasInstantiation)
            {
                declaringMethodForSlot = method.Context.GetMethodForInstantiatedType(
                    declaringMethodForSlot.GetTypicalMethodDefinition(), 
                    (InstantiatedType)containingTypeOfDeclaringMethodForSlot);
            }

            Debug.Assert(declaringMethodForSlot != null);

            return declaringMethodForSlot;
        }

        public static void GetVirtualInvokeMapDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
        {
            if (!factory.MetadataManager.SupportsReflection)
                return;

            if (NeedsVirtualInvokeInfo(method))
            {
                dependencies = dependencies ?? new DependencyList();

                if (factory.Target.Abi == TargetAbi.ProjectN)
                {
                    dependencies.Add(
                        factory.NecessaryTypeSymbol(method.OwningType.GetTypeDefinition()),
                        "Reflection virtual invoke owning type");
                }
                else
                {
                    dependencies.Add(
                        factory.NecessaryTypeSymbol(method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific)),
                        "Reflection virtual invoke owning type");
                }

                NativeLayoutMethodNameAndSignatureVertexNode nameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(method.GetTypicalMethodDefinition());
                NativeLayoutPlacedSignatureVertexNode placedNameAndSig = factory.NativeLayout.PlacedSignatureVertex(nameAndSig);
                dependencies.Add(placedNameAndSig, "Reflection virtual invoke method signature");

                if (!method.HasInstantiation)
                {
                    MethodDesc slotDefiningMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method);
                    if (!factory.VTable(slotDefiningMethod.OwningType).HasFixedSlots)
                    {
                        dependencies.Add(factory.VirtualMethodUse(slotDefiningMethod), "Reflection virtual invoke method");
                    }
                }
            }
        }

        public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
        {
            // This node does not trigger generation of other nodes.
            if (relocsOnly)
                return new ObjectData(Array.Empty<byte>(), Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this });

            // Ensure the native layout blob has been saved
            factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory);

            var writer = new NativeWriter();
            var typeMapHashTable = new VertexHashtable();

            Section hashTableSection = writer.NewSection();
            hashTableSection.Place(typeMapHashTable);

            Dictionary<int, HashSet<TypeDesc>> methodsEmitted = new Dictionary<int, HashSet<TypeDesc>>();

            // Get a list of all methods that have a method body and metadata from the metadata manager.
            foreach (var mappingEntry in factory.MetadataManager.GetMethodMapping(factory))
            {
                MethodDesc method = mappingEntry.Entity;

                // The current format requires us to have an EEType for the owning type. We might want to lift this.
                if (!factory.MetadataManager.TypeGeneratesEEType(method.OwningType))
                    continue;

                // We have a method body, we have a metadata token, but we can't get an invoke stub. Bail.
                if (!factory.MetadataManager.IsReflectionInvokable(method))
                    continue;

                // Only virtual methods are interesting
                if (!NeedsVirtualInvokeInfo(method))
                    continue;

                //
                // When working with the ProjectN ABI, the entries in the map are based on the definition types. All
                // instantiations of these definition types will have the same vtable method entries.
                // On CoreRT, the vtable entries for each instantiated type might not necessarily exist.
                // Example 1: 
                //      If there's a call to Foo<string>.Method1 and a call to Foo<int>.Method2, Foo<string> will
                //      not have Method2 in its vtable and Foo<int> will not have Method1.
                // Example 2:
                //      If there's a call to Foo<string>.Method1 and a call to Foo<object>.Method2, given that both
                //      of these instantiations share the same canonical form, Foo<__Canon> will have both method 
                //      entries, and therefore Foo<string> and Foo<object> will have both entries too.
                // For this reason, the entries that we write to the map in CoreRT will be based on the canonical form
                // of the method's containing type instead of the open type definition.
                //
                // Similarly, given that we use the open type definition for ProjectN, the slot numbers ignore dictionary
                // entries in the vtable, and computing the correct slot number in the presence of dictionary entries is 
                // done at runtime. When working with the CoreRT ABI, the correct slot numbers will be written to the map,
                // and no adjustments will be performed at runtime.
                //

                TypeDesc containingTypeKey;

                if (factory.Target.Abi == TargetAbi.ProjectN)
                    containingTypeKey = method.OwningType.GetTypeDefinition();
                else
                    containingTypeKey = method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific);

                HashSet<TypeDesc> cache;
                if (!methodsEmitted.TryGetValue(mappingEntry.MetadataHandle, out cache))
                    methodsEmitted[mappingEntry.MetadataHandle] = cache = new HashSet<TypeDesc>();

                // Only one record is needed for any instantiation.
                if (!cache.Add(containingTypeKey))
                    continue;

                // Grammar of an entry in the hash table:
                // Virtual Method uses a normal slot 
                // TypeKey + NameAndSig metadata offset into the native layout metadata + (NumberOfStepsUpParentHierarchyToType << 1) + slot
                // OR
                // Generic Virtual Method 
                // TypeKey + NameAndSig metadata offset into the native layout metadata + (NumberOfStepsUpParentHierarchyToType << 1 + 1)

                int parentHierarchyDistance;
                MethodDesc declaringMethodForSlot = GetDeclaringVirtualMethodAndHierarchyDistance(method, out parentHierarchyDistance);

                Vertex vertex = null;

                ISymbolNode containingTypeKeyNode = factory.NecessaryTypeSymbol(containingTypeKey);
                NativeLayoutMethodNameAndSignatureVertexNode nameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(method.GetTypicalMethodDefinition());
                NativeLayoutPlacedSignatureVertexNode placedNameAndSig = factory.NativeLayout.PlacedSignatureVertex(nameAndSig);

                if (method.HasInstantiation)
                {
                    vertex = writer.GetTuple(
                        writer.GetUnsignedConstant(_externalReferences.GetIndex(containingTypeKeyNode)),
                        writer.GetUnsignedConstant((uint)placedNameAndSig.SavedVertex.VertexOffset),
                        writer.GetUnsignedConstant(((uint)parentHierarchyDistance << 1) + VirtualInvokeTableEntry.GenericVirtualMethod));
                }
                else
                {
                    // Get the declaring method for slot on the instantiated declaring type
                    int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, declaringMethodForSlot, declaringMethodForSlot.OwningType, factory.Target.Abi != TargetAbi.ProjectN);
                    Debug.Assert(slot != -1);

                    vertex = writer.GetTuple(
                        writer.GetUnsignedConstant(_externalReferences.GetIndex(containingTypeKeyNode)),
                        writer.GetUnsignedConstant((uint)placedNameAndSig.SavedVertex.VertexOffset));

                    vertex = writer.GetTuple(vertex,
                        writer.GetUnsignedConstant((uint)parentHierarchyDistance << 1),
                        writer.GetUnsignedConstant((uint)slot));
                }

                int hashCode = containingTypeKey.GetHashCode();
                typeMapHashTable.Append((uint)hashCode, hashTableSection.Place(vertex));
            }

            byte[] hashTableBytes = writer.Save();

            _endSymbol.SetSymbolOffset(hashTableBytes.Length);

            return new ObjectData(hashTableBytes, Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this, _endSymbol });
        }

        protected internal override int Phase => (int)ObjectNodePhase.Ordered;
        public override int ClassCode => (int)ObjectNodeOrder.ReflectionVirtualInvokeMapNode;
    }
}