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

VirtualMethodCallHelper.cs « Compiler « src « ILCompiler.Compiler « src - github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 19da1f6aedc0a152b33934c715b2edbb9d6dbf9b (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
// 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.Collections.Generic;
using System.Diagnostics;
using Internal.TypeSystem;
using ILCompiler.DependencyAnalysis;

namespace ILCompiler
{
    public static class VirtualMethodSlotHelper
    {
        /// <summary>
        /// Given a virtual method decl, return its VTable slot if the method is used on its containing type.
        /// Return -1 if the virtual method is not used.
        /// </summary>
        public static int GetVirtualMethodSlot(NodeFactory factory, MethodDesc method, TypeDesc implType, bool countDictionarySlots = true)
        {
            if (method.CanMethodBeInSealedVTable())
            {
                // If the method is a sealed newslot method, it will be put in the sealed vtable instead of the type's vtable. In this
                // case, the slot index return should be the index in the sealed vtable, plus the total number of vtable slots.

                // First, make sure we are not attempting to resolve the slot of a sealed vtable method on a special array type, which
                // does not get any sealed vtable entries
                Debug.Assert(!implType.IsArrayTypeWithoutGenericInterfaces());

                // Ensure the sealed vtable is built before computing the slot
                factory.SealedVTable(implType).BuildSealedVTableSlots(factory, relocsOnly: false /* GetVirtualMethodSlot is called in the final emission phase */);

                int sealedVTableSlot = factory.SealedVTable(implType).ComputeSealedVTableSlot(method);
                if (sealedVTableSlot == -1)
                    return -1;

                // Now compute the total number of vtable slots that would exist on the type
                int baseSlots = GetNumberOfBaseSlots(factory, implType, countDictionarySlots);

                // For types that have a generic dictionary, the introduced virtual method slots are
                // prefixed with a pointer to the generic dictionary.
                if (implType.HasGenericDictionarySlot() && countDictionarySlots)
                    baseSlots++;

                int numVTableSlots = baseSlots;
                IReadOnlyList<MethodDesc> virtualSlots = factory.VTable(implType).Slots;
                for (int slot = 0; slot < virtualSlots.Count; slot++)
                {
                    if (virtualSlots[slot].CanMethodBeInSealedVTable())
                        continue;
                    numVTableSlots++;
                }

                return numVTableSlots + sealedVTableSlot;
            }
            else
            {
                // TODO: More efficient lookup of the slot
                TypeDesc owningType = method.OwningType;
                int baseSlots = GetNumberOfBaseSlots(factory, owningType, countDictionarySlots);

                // For types that have a generic dictionary, the introduced virtual method slots are
                // prefixed with a pointer to the generic dictionary.
                if (owningType.HasGenericDictionarySlot() && countDictionarySlots)
                    baseSlots++;

                IReadOnlyList<MethodDesc> virtualSlots = factory.VTable(owningType).Slots;
                int methodSlot = -1;
                int numSealedVTableEntries = 0;
                for (int slot = 0; slot < virtualSlots.Count; slot++)
                {
                    if (virtualSlots[slot].CanMethodBeInSealedVTable())
                    {
                        numSealedVTableEntries++;
                        continue;
                    }

                    if (virtualSlots[slot] == method)
                    {
                        methodSlot = slot;
                        break;
                    }
                }

                return methodSlot == -1 ? -1 : baseSlots + methodSlot - numSealedVTableEntries;
            }
        }

        private static int GetNumberOfBaseSlots(NodeFactory factory, TypeDesc owningType, bool countDictionarySlots)
        {
            int baseSlots = 0;

            TypeDesc baseType = owningType.BaseType;
            TypeDesc templateBaseType = owningType.ConvertToCanonForm(CanonicalFormKind.Specific).BaseType;

            while (baseType != null)
            {
                // Normalize the base type. Necessary to make this work with the lazy vtable slot
                // concept - if we start with a canonical type, the base type could end up being
                // something like Base<__Canon, string>. We would get "0 slots used" for weird
                // base types like this.
                baseType = baseType.ConvertToCanonForm(CanonicalFormKind.Specific);
                templateBaseType = templateBaseType.ConvertToCanonForm(CanonicalFormKind.Specific);

                //
                // In the universal canonical types case, we could have base types in the hierarchy that are partial universal canonical types.
                // The presence of these types could cause incorrect vtable layouts, so we need to fully canonicalize them and walk the
                // hierarchy of the template type of the original input type to detect these cases.
                //
                // Exmaple: we begin with Derived<__UniversalCanon> and walk the template hierarchy:
                //
                //    class Derived<T> : Middle<T, MyStruct> { }    // -> Template is Derived<__UniversalCanon> and needs a dictionary slot
                //                                                  // -> Basetype tempalte is Middle<__UniversalCanon, MyStruct>. It's a partial
                //                                                        Universal canonical type, so we need to fully canonicalize it.
                //                                                  
                //    class Middle<T, U> : Base<U> { }              // -> Template is Middle<__UniversalCanon, __UniversalCanon> and needs a dictionary slot
                //                                                  // -> Basetype template is Base<__UniversalCanon>
                //
                //    class Base<T> { }                             // -> Template is Base<__UniversalCanon> and needs a dictionary slot.
                //
                // If we had not fully canonicalized the Middle class template, we would have ended up with Base<MyStruct>, which does not need
                // a dictionary slot, meaning we would have created a vtable layout that the runtime does not expect.
                //

                // For types that have a generic dictionary, the introduced virtual method slots are
                // prefixed with a pointer to the generic dictionary.
                if ((baseType.HasGenericDictionarySlot() || templateBaseType.HasGenericDictionarySlot()) && countDictionarySlots)
                    baseSlots++;

                IReadOnlyList<MethodDesc> baseVirtualSlots = factory.VTable(baseType).Slots;
                foreach (var vtableMethod in baseVirtualSlots)
                {
                    // Methods in the sealed vtable should be excluded from the count
                    if (vtableMethod.CanMethodBeInSealedVTable())
                        continue;
                    baseSlots++;
                }

                baseType = baseType.BaseType;
                templateBaseType = templateBaseType.BaseType;
            }

            return baseSlots;
        }

        /// <summary>
        /// Gets the vtable slot that holds the generic dictionary of this type.
        /// </summary>
        public static int GetGenericDictionarySlot(NodeFactory factory, TypeDesc type)
        {
            Debug.Assert(type.HasGenericDictionarySlot());
            return GetNumberOfBaseSlots(factory, type, countDictionarySlots: true);
        }

        /// <summary>
        /// Gets a value indicating whether the virtual method slots introduced by this type are prefixed
        /// by a pointer to the generic dictionary of the type.
        /// </summary>
        public static bool HasGenericDictionarySlot(this TypeDesc type)
        {
            // Dictionary slots on generic interfaces are necessary to support static methods on interfaces
            // The reason behind making this unconditional is simplicity, and keeping method slot indices for methods on IFoo<int> 
            // and IFoo<string> identical. That won't change.
            if (type.IsInterface)
                return type.HasInstantiation;

            return type.HasInstantiation &&
                (type.ConvertToCanonForm(CanonicalFormKind.Specific) != type || type.IsCanonicalSubtype(CanonicalFormKind.Any));
        }
    }
}