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

DelegateInfo.cs « IL « TypeSystem « src « Common « src - github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 8b5fc4dfbd3d02bba250f1e29dae97b24794b731 (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
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
// 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 Internal.IL.Stubs;
using Internal.TypeSystem;

using Debug = System.Diagnostics.Debug;
using Interlocked = System.Threading.Interlocked;

namespace Internal.IL
{
    /// <summary>
    /// Represents a delegate and provides access to compiler-generated methods on the delegate type.
    /// </summary>
    public class DelegateInfo
    {
        private readonly TypeDesc _delegateType;
        private readonly DelegateFeature _supportedFeatures;

        private MethodSignature _signature;

        private MethodDesc _getThunkMethod;
        private DelegateThunkCollection _thunks;

        public static bool SupportsDynamicInvoke(TypeSystemContext context)
        {
            return DynamicInvokeMethodThunk.SupportsDynamicInvoke(context);
        }

        /// <summary>
        /// Gets the synthetic methods that support this delegate type.
        /// </summary>
        public IEnumerable<MethodDesc> Methods
        {
            get
            {
                if (_getThunkMethod == null)
                {
                    Interlocked.CompareExchange(ref _getThunkMethod, new DelegateGetThunkMethodOverride(this), null);
                }

                yield return _getThunkMethod;

                DelegateThunkCollection thunks = Thunks;
                for (DelegateThunkKind kind = 0; kind < DelegateThunkCollection.MaxThunkKind; kind++)
                {
                    MethodDesc thunk = thunks[kind];
                    if (thunk != null)
                        yield return thunk;
                }
            }
        }

        /// <summary>
        /// Gets the collection of delegate invocation thunks.
        /// </summary>
        public DelegateThunkCollection Thunks
        {
            get
            {
                if (_thunks == null)
                {
                    Interlocked.CompareExchange(ref _thunks, new DelegateThunkCollection(this), null);
                }
                return _thunks;
            }
        }

        /// <summary>
        /// Gets the signature of the delegate type.
        /// </summary>
        public MethodSignature Signature
        {
            get
            {
                if (_signature == null)
                {
                    _signature = _delegateType.GetKnownMethod("Invoke", null).Signature;
                }
                return _signature;
            }
        }

        public DelegateFeature SupportedFeatures
        {
            get
            {
                return _supportedFeatures;
            }
        }

        /// <summary>
        /// Gets the type of the delegate.
        /// </summary>
        public TypeDesc Type
        {
            get
            {
                return _delegateType;
            }
        }

        public DelegateInfo(TypeDesc delegateType, DelegateFeature features)
        {
            Debug.Assert(delegateType.IsDelegate);
            Debug.Assert(delegateType.IsTypeDefinition);

            _delegateType = delegateType;
            _supportedFeatures = features;
        }
    }

    /// <summary>
    /// Represents a collection of delegate invocation thunks.
    /// </summary>
    public class DelegateThunkCollection
    {
        public const DelegateThunkKind MaxThunkKind = DelegateThunkKind.ObjectArrayThunk + 1;

        private MethodDesc _openStaticThunk;
        private MethodDesc _multicastThunk;
        private MethodDesc _closedStaticThunk;
        private MethodDesc _invokeThunk;
        private MethodDesc _closedInstanceOverGeneric;
        private MethodDesc _reversePInvokeThunk;
        private MethodDesc _invokeObjectArrayThunk;
        private MethodDesc _openInstanceThunk;

        internal DelegateThunkCollection(DelegateInfo owningDelegate)
        {
            _openStaticThunk = new DelegateInvokeOpenStaticThunk(owningDelegate);
            _multicastThunk = new DelegateInvokeMulticastThunk(owningDelegate);
            _closedStaticThunk = new DelegateInvokeClosedStaticThunk(owningDelegate);
            _closedInstanceOverGeneric = new DelegateInvokeInstanceClosedOverGenericMethodThunk(owningDelegate);

            // Methods that have a byref-like type in the signature cannot be invoked with the object array thunk.
            // We would need to box the parameter and these can't be boxed.
            // Neither can be methods that have pointers in the signature.
            MethodSignature delegateSignature = owningDelegate.Signature;
            bool generateObjectArrayThunk = true;
            for (int i = 0; i < delegateSignature.Length; i++)
            {
                TypeDesc paramType = delegateSignature[i];
                if (paramType.IsByRef)
                    paramType = ((ByRefType)paramType).ParameterType;
                if (!paramType.IsSignatureVariable && paramType.IsByRefLike)
                {
                    generateObjectArrayThunk = false;
                    break;
                }
                if (paramType.IsPointer || paramType.IsFunctionPointer)
                {
                    generateObjectArrayThunk = false;
                    break;
                }
            }
            TypeDesc normalizedReturnType = delegateSignature.ReturnType;
            if (normalizedReturnType.IsByRef)
                normalizedReturnType = ((ByRefType)normalizedReturnType).ParameterType;
            if (!normalizedReturnType.IsSignatureVariable && normalizedReturnType.IsByRefLike)
                generateObjectArrayThunk = false;
            if (normalizedReturnType.IsPointer || normalizedReturnType.IsFunctionPointer)
                generateObjectArrayThunk = false;

            if ((owningDelegate.SupportedFeatures & DelegateFeature.ObjectArrayThunk) != 0 && generateObjectArrayThunk)
                _invokeObjectArrayThunk = new DelegateInvokeObjectArrayThunk(owningDelegate);

            //
            // Check whether we have a reverse p/invoke thunk
            //

            if (!owningDelegate.Type.HasInstantiation && IsNativeCallingConventionCompatible(delegateSignature))
                _reversePInvokeThunk = new DelegateReversePInvokeThunk(owningDelegate);
            
            //
            // Check whether we have an open instance thunk
            //

            if (delegateSignature.Length > 0)
            {
                TypeDesc firstParam = delegateSignature[0];

                bool generateOpenInstanceMethod;

                switch (firstParam.Category)
                {
                    case TypeFlags.Pointer:
                    case TypeFlags.FunctionPointer:
                        generateOpenInstanceMethod = false;
                        break;

                    case TypeFlags.ByRef:
                        firstParam = ((ByRefType)firstParam).ParameterType;
                        generateOpenInstanceMethod = firstParam.IsSignatureVariable || firstParam.IsValueType;
                        break;

                    case TypeFlags.Array:
                    case TypeFlags.SzArray:
                    case TypeFlags.SignatureTypeVariable:
                        generateOpenInstanceMethod = true;
                        break;

                    default:
                        Debug.Assert(firstParam.IsDefType);
                        generateOpenInstanceMethod = !firstParam.IsValueType;
                        break;
                }

                if (generateOpenInstanceMethod)
                {
                    _openInstanceThunk = new DelegateInvokeOpenInstanceThunk(owningDelegate);
                }
            }

            //
            // Check whether we have a dynamic invoke stub
            //

            if ((owningDelegate.SupportedFeatures & DelegateFeature.DynamicInvoke) != 0 &&
                DynamicInvokeMethodThunk.SupportsSignature(delegateSignature))
            {
                var sig = new DynamicInvokeMethodSignature(delegateSignature);
                _invokeThunk = owningDelegate.Type.Context.GetDynamicInvokeThunk(sig);
            }
        }

        #region Temporary interop logic
        // TODO: interop should provide a way to query this
        private static bool IsNativeCallingConventionCompatible(MethodSignature delegateSignature)
        {
            if (!IsNativeCallingConventionCompatible(delegateSignature.ReturnType))
                return false;
            else
            {
                for (int i = 0; i < delegateSignature.Length; i++)
                {
                    if (!IsNativeCallingConventionCompatible(delegateSignature[i]))
                    {
                        return false;
                    }
                }
            }

            return true;
        }

        private static bool IsNativeCallingConventionCompatible(TypeDesc type)
        {
            if (type.IsPointer)
                return true;

            if (type.IsByRef)
                return IsNativeCallingConventionCompatible(((ParameterizedType)type).ParameterType);

            if (!type.IsValueType)
                return false;

            if (type.IsPrimitive)
            {
                if (type.IsWellKnownType(WellKnownType.Boolean))
                    return false;

                return true;
            }

            foreach (FieldDesc field in type.GetFields())
            {
                if (!field.IsStatic && !IsNativeCallingConventionCompatible(field.FieldType))
                    return false;
            }

            return true;
        }
        #endregion

        public MethodDesc this[DelegateThunkKind kind]
        {
            get
            {
                switch (kind)
                {
                    case DelegateThunkKind.OpenStaticThunk:
                        return _openStaticThunk;
                    case DelegateThunkKind.MulticastThunk:
                        return _multicastThunk;
                    case DelegateThunkKind.ClosedStaticThunk:
                        return _closedStaticThunk;
                    case DelegateThunkKind.DelegateInvokeThunk:
                        return _invokeThunk;
                    case DelegateThunkKind.ClosedInstanceThunkOverGenericMethod:
                        return _closedInstanceOverGeneric;
                    case DelegateThunkKind.ReversePinvokeThunk:
                        return _reversePInvokeThunk;
                    case DelegateThunkKind.ObjectArrayThunk:
                        return _invokeObjectArrayThunk;
                    case DelegateThunkKind.OpenInstanceThunk:
                        return _openInstanceThunk;
                    default:
                        return null;
                }
            }
        }
    }

    // TODO: Unify with the consts used in Delegate.cs within the class library.
    public enum DelegateThunkKind
    {
        MulticastThunk = 0,
        ClosedStaticThunk = 1,
        OpenStaticThunk = 2,
        ClosedInstanceThunkOverGenericMethod = 3, // This may not exist
        DelegateInvokeThunk = 4,
        OpenInstanceThunk = 5,        // This may not exist
        ReversePinvokeThunk = 6,       // This may not exist
        ObjectArrayThunk = 7,         // This may not exist
    }

    [Flags]
    public enum DelegateFeature
    {
        DynamicInvoke = 0x1,
        ObjectArrayThunk = 0x2,
    }
}