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.Text;
6 using System.Runtime;
7 using System.Reflection;
8 using System.Runtime.Serialization;
9 using System.Runtime.InteropServices;
10 using System.Runtime.CompilerServices;
11 using System.Diagnostics;
12 using Internal.Reflection.Augments;
13 using Internal.Runtime.Augments;
14 using Internal.Runtime.CompilerServices;
15 
16 namespace System
17 {
18     // WARNING: Bartok hard-codes offsets to the delegate fields, so it's notion of where fields are may
19     // differ from the runtime's notion.  Bartok honors sequential and explicit layout directives, so I've
20     // ordered the fields in such a way as to match the runtime's layout and then just tacked on this
21     // sequential layout directive so that Bartok matches it.
22     [StructLayout(LayoutKind.Sequential)]
23     [DebuggerDisplay("Target method(s) = {GetTargetMethodsDescriptionForDebugger()}")]
24     public abstract partial class Delegate : ICloneable, ISerializable
25     {
26         // This ctor exists solely to prevent C# from generating a protected .ctor that violates the surface area. I really want this to be a
27         // "protected-and-internal" rather than "internal" but C# has no keyword for the former.
Delegate()28         internal Delegate()
29         {
30             // ! Do NOT put any code here. Delegate constructers are not guaranteed to be executed.
31         }
32 
33         // V1 API: Create closed instance delegates. Method name matching is case sensitive.
Delegate(Object target, String method)34         protected Delegate(Object target, String method)
35         {
36             // This constructor cannot be used by application code. To create a delegate by specifying the name of a method, an
37             // overload of the public static CreateDelegate method is used. This will eventually end up calling into the internal
38             // implementation of CreateDelegate below, and does not invoke this constructor.
39             // The constructor is just for API compatibility with the public contract of the Delegate class.
40             throw new PlatformNotSupportedException();
41         }
42 
43         // V1 API: Create open static delegates. Method name matching is case insensitive.
Delegate(Type target, String method)44         protected Delegate(Type target, String method)
45         {
46             // This constructor cannot be used by application code. To create a delegate by specifying the name of a method, an
47             // overload of the public static CreateDelegate method is used. This will eventually end up calling into the internal
48             // implementation of CreateDelegate below, and does not invoke this constructor.
49             // The constructor is just for API compatibility with the public contract of the Delegate class.
50             throw new PlatformNotSupportedException();
51         }
52 
53         // New Delegate Implementation
54 
55         protected internal object m_firstParameter;
56         protected internal object m_helperObject;
57         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible")]
58         protected internal IntPtr m_extraFunctionPointerOrData;
59         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible")]
60         protected internal IntPtr m_functionPointer;
61 
62         [ThreadStatic]
63         protected static string s_DefaultValueString;
64 
65         // WARNING: These constants are also declared in System.Private.TypeLoader\Internal\Runtime\TypeLoader\CallConverterThunk.cs
66         // Do not change their values without updating the values in the calling convention converter component
67         protected const int MulticastThunk = 0;
68         protected const int ClosedStaticThunk = 1;
69         protected const int OpenStaticThunk = 2;
70         protected const int ClosedInstanceThunkOverGenericMethod = 3; // This may not exist
71         protected const int DelegateInvokeThunk = 4;
72         protected const int OpenInstanceThunk = 5;        // This may not exist
73         protected const int ReversePinvokeThunk = 6;       // This may not exist
74         protected const int ObjectArrayThunk = 7;         // This may not exist
75 
76         //
77         // If the thunk does not exist, the function will return IntPtr.Zero.
GetThunk(int whichThunk)78         protected virtual IntPtr GetThunk(int whichThunk)
79         {
80 #if DEBUG
81             // The GetThunk function should be overriden on all delegate types, except for universal
82             // canonical delegates which use calling convention converter thunks to marshal arguments
83             // for the delegate call. If we execute this version of GetThunk, we can at least assert
84             // that the current delegate type is a generic type.
85             Debug.Assert(this.EETypePtr.IsGeneric);
86 #endif
87             return TypeLoaderExports.GetDelegateThunk(this, whichThunk);
88         }
89 
90         //
91         // If there is a default value string, the overridden function should set the
92         // s_DefaultValueString field and return true.
LoadDefaultValueString()93         protected virtual bool LoadDefaultValueString() { return false; }
94 
95         /// <summary>
96         /// Used by various parts of the runtime as a replacement for Delegate.Method
97         ///
98         /// The Interop layer uses this to distinguish between different methods on a
99         /// single type, and to get the function pointer for delegates to static functions
100         ///
101         /// The reflection apis use this api to figure out what MethodInfo is related
102         /// to a delegate.
103         ///
104         /// </summary>
105         /// <param name="typeOfFirstParameterIfInstanceDelegate">
106         ///   This value indicates which type an delegate's function pointer is associated with
107         ///   This value is ONLY set for delegates where the function pointer points at an instance method
108         /// </param>
109         /// <param name="isOpenResolver">
110         ///   This value indicates if the returned pointer is an open resolver structure.
111         /// </param>
112         /// <param name="isInterpreterEntrypoint">
113         ///   Delegate points to an object array thunk (the delegate wraps a Func<object[], object> delegate). This
114         ///   is typically a delegate pointing to the LINQ expression interpreter.
115         /// </param>
116         /// <returns></returns>
GetFunctionPointer(out RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate, out bool isOpenResolver, out bool isInterpreterEntrypoint)117         unsafe internal IntPtr GetFunctionPointer(out RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate, out bool isOpenResolver, out bool isInterpreterEntrypoint)
118         {
119             typeOfFirstParameterIfInstanceDelegate = default(RuntimeTypeHandle);
120             isOpenResolver = false;
121             isInterpreterEntrypoint = false;
122 
123             if (GetThunk(MulticastThunk) == m_functionPointer)
124             {
125                 return IntPtr.Zero;
126             }
127             else if (GetThunk(ObjectArrayThunk) == m_functionPointer)
128             {
129                 isInterpreterEntrypoint = true;
130                 return IntPtr.Zero;
131             }
132             else if (m_extraFunctionPointerOrData != IntPtr.Zero)
133             {
134                 if (GetThunk(OpenInstanceThunk) == m_functionPointer)
135                 {
136                     typeOfFirstParameterIfInstanceDelegate = ((OpenMethodResolver*)m_extraFunctionPointerOrData)->DeclaringType;
137                     isOpenResolver = true;
138                 }
139                 return m_extraFunctionPointerOrData;
140             }
141             else
142             {
143                 if (m_firstParameter != null)
144                     typeOfFirstParameterIfInstanceDelegate = new RuntimeTypeHandle(m_firstParameter.EETypePtr);
145 
146                 // TODO! Implementation issue for generic invokes here ... we need another IntPtr for uniqueness.
147 
148                 return m_functionPointer;
149             }
150         }
151 
152         // @todo: Not an api but some NativeThreadPool code still depends on it.
GetNativeFunctionPointer()153         internal IntPtr GetNativeFunctionPointer()
154         {
155             if (GetThunk(ReversePinvokeThunk) != m_functionPointer)
156             {
157                 throw new InvalidOperationException("GetNativeFunctionPointer may only be used on a reverse pinvoke delegate");
158             }
159 
160             return m_extraFunctionPointerOrData;
161         }
162 
163         // This function is known to the IL Transformer.
InitializeClosedInstance(object firstParameter, IntPtr functionPointer)164         protected void InitializeClosedInstance(object firstParameter, IntPtr functionPointer)
165         {
166             if (firstParameter == null)
167                 throw new ArgumentException(SR.Arg_DlgtNullInst);
168 
169             m_functionPointer = functionPointer;
170             m_firstParameter = firstParameter;
171         }
172 
173         // This function is known to the IL Transformer.
InitializeClosedInstanceSlow(object firstParameter, IntPtr functionPointer)174         protected void InitializeClosedInstanceSlow(object firstParameter, IntPtr functionPointer)
175         {
176             // This method is like InitializeClosedInstance, but it handles ALL cases. In particular, it handles generic method with fun function pointers.
177 
178             if (firstParameter == null)
179                 throw new ArgumentException(SR.Arg_DlgtNullInst);
180 
181             if (!FunctionPointerOps.IsGenericMethodPointer(functionPointer))
182             {
183                 m_functionPointer = functionPointer;
184                 m_firstParameter = firstParameter;
185             }
186             else
187             {
188                 m_firstParameter = this;
189                 m_functionPointer = GetThunk(ClosedInstanceThunkOverGenericMethod);
190                 m_extraFunctionPointerOrData = functionPointer;
191                 m_helperObject = firstParameter;
192             }
193         }
194 
195         // This function is known to the compiler.
InitializeClosedInstanceWithGVMResolution(object firstParameter, RuntimeMethodHandle tokenOfGenericVirtualMethod)196         protected void InitializeClosedInstanceWithGVMResolution(object firstParameter, RuntimeMethodHandle tokenOfGenericVirtualMethod)
197         {
198             if (firstParameter == null)
199                 throw new ArgumentException(SR.Arg_DlgtNullInst);
200 
201             IntPtr functionResolution = TypeLoaderExports.GVMLookupForSlot(firstParameter, tokenOfGenericVirtualMethod);
202 
203             if (functionResolution == IntPtr.Zero)
204             {
205                 // TODO! What to do when GVM resolution fails. Should never happen
206                 throw new InvalidOperationException();
207             }
208             if (!FunctionPointerOps.IsGenericMethodPointer(functionResolution))
209             {
210                 m_functionPointer = functionResolution;
211                 m_firstParameter = firstParameter;
212             }
213             else
214             {
215                 m_firstParameter = this;
216                 m_functionPointer = GetThunk(ClosedInstanceThunkOverGenericMethod);
217                 m_extraFunctionPointerOrData = functionResolution;
218                 m_helperObject = firstParameter;
219             }
220 
221             return;
222         }
223 
InitializeClosedInstanceToInterface(object firstParameter, IntPtr dispatchCell)224         private void InitializeClosedInstanceToInterface(object firstParameter, IntPtr dispatchCell)
225         {
226             if (firstParameter == null)
227                 throw new ArgumentException(SR.Arg_DlgtNullInst);
228 
229             m_functionPointer = RuntimeImports.RhpResolveInterfaceMethod(firstParameter, dispatchCell);
230             m_firstParameter = firstParameter;
231         }
232 
233         // This is used to implement MethodInfo.CreateDelegate() in a desktop-compatible way. Yes, the desktop really
234         // let you use that api to invoke an instance method with a null 'this'.
InitializeClosedInstanceWithoutNullCheck(object firstParameter, IntPtr functionPointer)235         private void InitializeClosedInstanceWithoutNullCheck(object firstParameter, IntPtr functionPointer)
236         {
237             if (!FunctionPointerOps.IsGenericMethodPointer(functionPointer))
238             {
239                 m_functionPointer = functionPointer;
240                 m_firstParameter = firstParameter;
241             }
242             else
243             {
244                 m_firstParameter = this;
245                 m_functionPointer = GetThunk(ClosedInstanceThunkOverGenericMethod);
246                 m_extraFunctionPointerOrData = functionPointer;
247                 m_helperObject = firstParameter;
248             }
249         }
250 
251         // This function is known to the compiler backend.
InitializeClosedStaticThunk(object firstParameter, IntPtr functionPointer, IntPtr functionPointerThunk)252         protected void InitializeClosedStaticThunk(object firstParameter, IntPtr functionPointer, IntPtr functionPointerThunk)
253         {
254             m_extraFunctionPointerOrData = functionPointer;
255             m_helperObject = firstParameter;
256             m_functionPointer = functionPointerThunk;
257             m_firstParameter = this;
258         }
259 
260         // This function is known to the compiler backend.
InitializeClosedStaticWithoutThunk(object firstParameter, IntPtr functionPointer)261         protected void InitializeClosedStaticWithoutThunk(object firstParameter, IntPtr functionPointer)
262         {
263             m_extraFunctionPointerOrData = functionPointer;
264             m_helperObject = firstParameter;
265             m_functionPointer = GetThunk(ClosedStaticThunk);
266             m_firstParameter = this;
267         }
268 
269         // This function is known to the compiler backend.
InitializeOpenStaticThunk(object firstParameter, IntPtr functionPointer, IntPtr functionPointerThunk)270         protected void InitializeOpenStaticThunk(object firstParameter, IntPtr functionPointer, IntPtr functionPointerThunk)
271         {
272             // This sort of delegate is invoked by calling the thunk function pointer with the arguments to the delegate + a reference to the delegate object itself.
273             m_firstParameter = this;
274             m_functionPointer = functionPointerThunk;
275             m_extraFunctionPointerOrData = functionPointer;
276         }
277 
278         // This function is known to the compiler backend.
InitializeOpenStaticWithoutThunk(object firstParameter, IntPtr functionPointer)279         protected void InitializeOpenStaticWithoutThunk(object firstParameter, IntPtr functionPointer)
280         {
281             // This sort of delegate is invoked by calling the thunk function pointer with the arguments to the delegate + a reference to the delegate object itself.
282             m_firstParameter = this;
283             m_functionPointer = GetThunk(OpenStaticThunk);
284             m_extraFunctionPointerOrData = functionPointer;
285         }
286 
287         // This function is known to the compiler backend.
InitializeReversePInvokeThunk(object firstParameter, IntPtr functionPointer, IntPtr functionPointerThunk)288         protected void InitializeReversePInvokeThunk(object firstParameter, IntPtr functionPointer, IntPtr functionPointerThunk)
289         {
290             // This sort of delegate is invoked by calling the thunk function pointer with the arguments to the delegate + a reference to the delegate object itself.
291             m_firstParameter = this;
292             m_functionPointer = functionPointerThunk;
293             m_extraFunctionPointerOrData = functionPointer;
294         }
295 
296         // This function is known to the compiler backend.
InitializeReversePInvokeWithoutThunk(object firstParameter, IntPtr functionPointer)297         protected void InitializeReversePInvokeWithoutThunk(object firstParameter, IntPtr functionPointer)
298         {
299             // This sort of delegate is invoked by calling the thunk function pointer with the arguments to the delegate + a reference to the delegate object itself.
300             m_firstParameter = this;
301             m_functionPointer = GetThunk(ReversePinvokeThunk);
302             m_extraFunctionPointerOrData = functionPointer;
303         }
304 
305         // This function is known to the compiler backend.
InitializeOpenInstanceThunk(object firstParameter, IntPtr functionPointer, IntPtr functionPointerThunk)306         protected void InitializeOpenInstanceThunk(object firstParameter, IntPtr functionPointer, IntPtr functionPointerThunk)
307         {
308             // This sort of delegate is invoked by calling the thunk function pointer with the arguments to the delegate + a reference to the delegate object itself.
309             m_firstParameter = this;
310             m_functionPointer = functionPointerThunk;
311             OpenMethodResolver instanceMethodResolver = new OpenMethodResolver(default(RuntimeTypeHandle), functionPointer, default(GCHandle), 0);
312             m_extraFunctionPointerOrData = instanceMethodResolver.ToIntPtr();
313         }
314 
315         // This function is known to the compiler backend.
InitializeOpenInstanceWithoutThunk(object firstParameter, IntPtr functionPointer, IntPtr functionPointerThunk)316         protected void InitializeOpenInstanceWithoutThunk(object firstParameter, IntPtr functionPointer, IntPtr functionPointerThunk)
317         {
318             // This sort of delegate is invoked by calling the thunk function pointer with the arguments to the delegate + a reference to the delegate object itself.
319             m_firstParameter = this;
320             m_functionPointer = GetThunk(OpenInstanceThunk);
321             OpenMethodResolver instanceMethodResolver = new OpenMethodResolver(default(RuntimeTypeHandle), functionPointer, default(GCHandle), 0);
322             m_extraFunctionPointerOrData = instanceMethodResolver.ToIntPtr();
323         }
324 
InitializeOpenInstanceThunkDynamic(IntPtr functionPointer, IntPtr functionPointerThunk)325         protected void InitializeOpenInstanceThunkDynamic(IntPtr functionPointer, IntPtr functionPointerThunk)
326         {
327             // This sort of delegate is invoked by calling the thunk function pointer with the arguments to the delegate + a reference to the delegate object itself.
328             m_firstParameter = this;
329             m_functionPointer = functionPointerThunk;
330             m_extraFunctionPointerOrData = functionPointer;
331         }
332 
SetClosedStaticFirstParameter(object firstParameter)333         internal void SetClosedStaticFirstParameter(object firstParameter)
334         {
335             // Closed static delegates place a value in m_helperObject that they pass to the target method.
336             Debug.Assert(m_functionPointer == GetThunk(ClosedStaticThunk));
337             m_helperObject = firstParameter;
338         }
339 
340         // This function is only ever called by the open instance method thunk, and in that case,
341         // m_extraFunctionPointerOrData always points to an OpenMethodResolver
342         [MethodImpl(MethodImplOptions.NoInlining)]
GetActualTargetFunctionPointer(object thisObject)343         protected IntPtr GetActualTargetFunctionPointer(object thisObject)
344         {
345             return OpenMethodResolver.ResolveMethod(m_extraFunctionPointerOrData, thisObject);
346         }
347 
GetHashCode()348         public override int GetHashCode()
349         {
350             return GetType().GetHashCode();
351         }
352 
IsDynamicDelegate()353         private bool IsDynamicDelegate()
354         {
355             if (this.GetThunk(MulticastThunk) == IntPtr.Zero)
356             {
357                 return true;
358             }
359 
360             return false;
361         }
362 
363         [DebuggerGuidedStepThroughAttribute]
DynamicInvokeImpl(object[] args)364         protected virtual object DynamicInvokeImpl(object[] args)
365         {
366             if (IsDynamicDelegate())
367             {
368                 // DynamicDelegate case
369                 object result = ((Func<object[], object>)m_helperObject)(args);
370                 DebugAnnotations.PreviousCallContainsDebuggerStepInCode();
371                 return result;
372             }
373             else
374             {
375                 IntPtr invokeThunk = this.GetThunk(DelegateInvokeThunk);
376                 object result = System.InvokeUtils.CallDynamicInvokeMethod(this.m_firstParameter, this.m_functionPointer, this, invokeThunk, IntPtr.Zero, this, args, binderBundle: null, wrapInTargetInvocationException: true);
377                 DebugAnnotations.PreviousCallContainsDebuggerStepInCode();
378                 return result;
379             }
380         }
381 
382         [DebuggerGuidedStepThroughAttribute]
DynamicInvoke(params object[] args)383         public object DynamicInvoke(params object[] args)
384         {
385             object result = DynamicInvokeImpl(args);
386             DebugAnnotations.PreviousCallContainsDebuggerStepInCode();
387             return result;
388         }
389 
Combine(Delegate a, Delegate b)390         public static unsafe Delegate Combine(Delegate a, Delegate b)
391         {
392             if (a == null)
393                 return b;
394             if (b == null)
395                 return a;
396 
397             return a.CombineImpl(b);
398         }
399 
Remove(Delegate source, Delegate value)400         public static Delegate Remove(Delegate source, Delegate value)
401         {
402             if (source == null)
403                 return null;
404 
405             if (value == null)
406                 return source;
407 
408             if (!InternalEqualTypes(source, value))
409                 throw new ArgumentException(SR.Arg_DlgtTypeMis);
410 
411             return source.RemoveImpl(value);
412         }
413 
RemoveAll(Delegate source, Delegate value)414         public static Delegate RemoveAll(Delegate source, Delegate value)
415         {
416             Delegate newDelegate = null;
417 
418             do
419             {
420                 newDelegate = source;
421                 source = Remove(source, value);
422             }
423             while (newDelegate != source);
424 
425             return newDelegate;
426         }
427 
428         // Used to support the C# compiler in implementing the "+" operator for delegates
Combine(params Delegate[] delegates)429         public static Delegate Combine(params Delegate[] delegates)
430         {
431             if ((delegates == null) || (delegates.Length == 0))
432                 return null;
433 
434             Delegate d = delegates[0];
435             for (int i = 1; i < delegates.Length; i++)
436             {
437                 d = Combine(d, delegates[i]);
438             }
439 
440             return d;
441         }
442 
NewMulticastDelegate(Delegate[] invocationList, int invocationCount, bool thisIsMultiCastAlready)443         private MulticastDelegate NewMulticastDelegate(Delegate[] invocationList, int invocationCount, bool thisIsMultiCastAlready)
444         {
445             // First, allocate a new multicast delegate just like this one, i.e. same type as the this object
446             MulticastDelegate result = (MulticastDelegate)RuntimeImports.RhNewObject(this.EETypePtr);
447 
448             // Performance optimization - if this already points to a true multicast delegate,
449             // copy _methodPtr and _methodPtrAux fields rather than calling into the EE to get them
450             if (thisIsMultiCastAlready)
451             {
452                 result.m_functionPointer = this.m_functionPointer;
453             }
454             else
455             {
456                 result.m_functionPointer = GetThunk(MulticastThunk);
457             }
458             result.m_firstParameter = result;
459             result.m_helperObject = invocationList;
460             result.m_extraFunctionPointerOrData = (IntPtr)invocationCount;
461 
462             return result;
463         }
464 
NewMulticastDelegate(Delegate[] invocationList, int invocationCount)465         internal MulticastDelegate NewMulticastDelegate(Delegate[] invocationList, int invocationCount)
466         {
467             return NewMulticastDelegate(invocationList, invocationCount, false);
468         }
469 
TrySetSlot(Delegate[] a, int index, Delegate o)470         private bool TrySetSlot(Delegate[] a, int index, Delegate o)
471         {
472             if (a[index] == null && System.Threading.Interlocked.CompareExchange<Delegate>(ref a[index], o, null) == null)
473                 return true;
474 
475             // The slot may be already set because we have added and removed the same method before.
476             // Optimize this case, because it's cheaper than copying the array.
477             if (a[index] != null)
478             {
479                 MulticastDelegate d = (MulticastDelegate)o;
480                 MulticastDelegate dd = (MulticastDelegate)a[index];
481 
482                 if (Object.ReferenceEquals(dd.m_firstParameter, d.m_firstParameter) &&
483                     Object.ReferenceEquals(dd.m_helperObject, d.m_helperObject) &&
484                     dd.m_extraFunctionPointerOrData == d.m_extraFunctionPointerOrData &&
485                     dd.m_functionPointer == d.m_functionPointer)
486                 {
487                     return true;
488                 }
489             }
490             return false;
491         }
492 
493 
494         // This method will combine this delegate with the passed delegate
495         //  to form a new delegate.
CombineImpl(Delegate follow)496         protected virtual Delegate CombineImpl(Delegate follow)
497         {
498             if ((Object)follow == null) // cast to object for a more efficient test
499                 return this;
500 
501             // Verify that the types are the same...
502             if (!InternalEqualTypes(this, follow))
503                 throw new ArgumentException();
504 
505             if (IsDynamicDelegate() && follow.IsDynamicDelegate())
506             {
507                 throw new InvalidOperationException();
508             }
509 
510             MulticastDelegate dFollow = (MulticastDelegate)follow;
511             Delegate[] resultList;
512             int followCount = 1;
513             Delegate[] followList = dFollow.m_helperObject as Delegate[];
514             if (followList != null)
515                 followCount = (int)dFollow.m_extraFunctionPointerOrData;
516 
517             int resultCount;
518             Delegate[] invocationList = m_helperObject as Delegate[];
519             if (invocationList == null)
520             {
521                 resultCount = 1 + followCount;
522                 resultList = new Delegate[resultCount];
523                 resultList[0] = this;
524                 if (followList == null)
525                 {
526                     resultList[1] = dFollow;
527                 }
528                 else
529                 {
530                     for (int i = 0; i < followCount; i++)
531                         resultList[1 + i] = followList[i];
532                 }
533                 return NewMulticastDelegate(resultList, resultCount);
534             }
535             else
536             {
537                 int invocationCount = (int)m_extraFunctionPointerOrData;
538                 resultCount = invocationCount + followCount;
539                 resultList = null;
540                 if (resultCount <= invocationList.Length)
541                 {
542                     resultList = invocationList;
543                     if (followList == null)
544                     {
545                         if (!TrySetSlot(resultList, invocationCount, dFollow))
546                             resultList = null;
547                     }
548                     else
549                     {
550                         for (int i = 0; i < followCount; i++)
551                         {
552                             if (!TrySetSlot(resultList, invocationCount + i, followList[i]))
553                             {
554                                 resultList = null;
555                                 break;
556                             }
557                         }
558                     }
559                 }
560 
561                 if (resultList == null)
562                 {
563                     int allocCount = invocationList.Length;
564                     while (allocCount < resultCount)
565                         allocCount *= 2;
566 
567                     resultList = new Delegate[allocCount];
568 
569                     for (int i = 0; i < invocationCount; i++)
570                         resultList[i] = invocationList[i];
571 
572                     if (followList == null)
573                     {
574                         resultList[invocationCount] = dFollow;
575                     }
576                     else
577                     {
578                         for (int i = 0; i < followCount; i++)
579                             resultList[invocationCount + i] = followList[i];
580                     }
581                 }
582                 return NewMulticastDelegate(resultList, resultCount, true);
583             }
584         }
585 
DeleteFromInvocationList(Delegate[] invocationList, int invocationCount, int deleteIndex, int deleteCount)586         private Delegate[] DeleteFromInvocationList(Delegate[] invocationList, int invocationCount, int deleteIndex, int deleteCount)
587         {
588             Delegate[] thisInvocationList = m_helperObject as Delegate[];
589             int allocCount = thisInvocationList.Length;
590             while (allocCount / 2 >= invocationCount - deleteCount)
591                 allocCount /= 2;
592 
593             Delegate[] newInvocationList = new Delegate[allocCount];
594 
595             for (int i = 0; i < deleteIndex; i++)
596                 newInvocationList[i] = invocationList[i];
597 
598             for (int i = deleteIndex + deleteCount; i < invocationCount; i++)
599                 newInvocationList[i - deleteCount] = invocationList[i];
600 
601             return newInvocationList;
602         }
603 
EqualInvocationLists(Delegate[] a, Delegate[] b, int start, int count)604         private bool EqualInvocationLists(Delegate[] a, Delegate[] b, int start, int count)
605         {
606             for (int i = 0; i < count; i++)
607             {
608                 if (!(a[start + i].Equals(b[i])))
609                     return false;
610             }
611             return true;
612         }
613 
614         // This method currently looks backward on the invocation list
615         //  for an element that has Delegate based equality with value.  (Doesn't
616         //  look at the invocation list.)  If this is found we remove it from
617         //  this list and return a new delegate.  If its not found a copy of the
618         //  current list is returned.
RemoveImpl(Delegate value)619         protected virtual Delegate RemoveImpl(Delegate value)
620         {
621             // There is a special case were we are removing using a delegate as
622             //    the value we need to check for this case
623             //
624             MulticastDelegate v = value as MulticastDelegate;
625 
626             if (v == null)
627                 return this;
628             if (v.m_helperObject as Delegate[] == null)
629             {
630                 Delegate[] invocationList = m_helperObject as Delegate[];
631                 if (invocationList == null)
632                 {
633                     // they are both not real Multicast
634                     if (this.Equals(value))
635                         return null;
636                 }
637                 else
638                 {
639                     int invocationCount = (int)m_extraFunctionPointerOrData;
640                     for (int i = invocationCount; --i >= 0;)
641                     {
642                         if (value.Equals(invocationList[i]))
643                         {
644                             if (invocationCount == 2)
645                             {
646                                 // Special case - only one value left, either at the beginning or the end
647                                 return invocationList[1 - i];
648                             }
649                             else
650                             {
651                                 Delegate[] list = DeleteFromInvocationList(invocationList, invocationCount, i, 1);
652                                 return NewMulticastDelegate(list, invocationCount - 1, true);
653                             }
654                         }
655                     }
656                 }
657             }
658             else
659             {
660                 Delegate[] invocationList = m_helperObject as Delegate[];
661                 if (invocationList != null)
662                 {
663                     int invocationCount = (int)m_extraFunctionPointerOrData;
664                     int vInvocationCount = (int)v.m_extraFunctionPointerOrData;
665                     for (int i = invocationCount - vInvocationCount; i >= 0; i--)
666                     {
667                         if (EqualInvocationLists(invocationList, v.m_helperObject as Delegate[], i, vInvocationCount))
668                         {
669                             if (invocationCount - vInvocationCount == 0)
670                             {
671                                 // Special case - no values left
672                                 return null;
673                             }
674                             else if (invocationCount - vInvocationCount == 1)
675                             {
676                                 // Special case - only one value left, either at the beginning or the end
677                                 return invocationList[i != 0 ? 0 : invocationCount - 1];
678                             }
679                             else
680                             {
681                                 Delegate[] list = DeleteFromInvocationList(invocationList, invocationCount, i, vInvocationCount);
682                                 return NewMulticastDelegate(list, invocationCount - vInvocationCount, true);
683                             }
684                         }
685                     }
686                 }
687             }
688 
689             return this;
690         }
691 
GetInvocationList()692         public virtual Delegate[] GetInvocationList()
693         {
694             Delegate[] del;
695             Delegate[] invocationList = m_helperObject as Delegate[];
696             if (invocationList == null)
697             {
698                 del = new Delegate[1];
699                 del[0] = this;
700             }
701             else
702             {
703                 // Create an array of delegate copies and each
704                 //    element into the array
705                 int invocationCount = (int)m_extraFunctionPointerOrData;
706                 del = new Delegate[invocationCount];
707 
708                 for (int i = 0; i < del.Length; i++)
709                     del[i] = invocationList[i];
710             }
711             return del;
712         }
713 
714         public MethodInfo Method
715         {
716             get
717             {
718                 return GetMethodImpl();
719             }
720         }
721 
GetMethodImpl()722         protected virtual MethodInfo GetMethodImpl()
723         {
724             return RuntimeAugments.Callbacks.GetDelegateMethod(this);
725         }
726 
Equals(Object obj)727         public override bool Equals(Object obj)
728         {
729             // It is expected that all real uses of the Equals method will hit the MulticastDelegate.Equals logic instead of this
730             // therefore, instead of duplicating the desktop behavior where direct calls to this Equals function do not behave
731             // correctly, we'll just throw here.
732             throw new PlatformNotSupportedException();
733         }
734 
operator ==(Delegate d1, Delegate d2)735         public static bool operator ==(Delegate d1, Delegate d2)
736         {
737             if ((Object)d1 == null)
738                 return (Object)d2 == null;
739 
740             return d1.Equals(d2);
741         }
742 
operator !=(Delegate d1, Delegate d2)743         public static bool operator !=(Delegate d1, Delegate d2)
744         {
745             if ((Object)d1 == null)
746                 return (Object)d2 != null;
747 
748             return !d1.Equals(d2);
749         }
750 
751         public Object Target
752         {
753             get
754             {
755                 // Multi-cast delegates return the Target of the last delegate in the list
756                 if (m_functionPointer == GetThunk(MulticastThunk))
757                 {
758                     Delegate[] invocationList = (Delegate[])m_helperObject;
759                     int invocationCount = (int)m_extraFunctionPointerOrData;
760                     return invocationList[invocationCount - 1].Target;
761                 }
762 
763                 // Closed static delegates place a value in m_helperObject that they pass to the target method.
764                 if (m_functionPointer == GetThunk(ClosedStaticThunk) ||
765                     m_functionPointer == GetThunk(ClosedInstanceThunkOverGenericMethod) ||
766                     m_functionPointer == GetThunk(ObjectArrayThunk))
767                     return m_helperObject;
768 
769                 // Other non-closed thunks can be identified as the m_firstParameter field points at this.
770                 if (Object.ReferenceEquals(m_firstParameter, this))
771                 {
772                     return null;
773                 }
774 
775                 // Closed instance delegates place a value in m_firstParameter, and we've ruled out all other types of delegates
776                 return m_firstParameter;
777             }
778         }
779 
780         // V2 api: Creates open or closed delegates to static or instance methods - relaxed signature checking allowed.
CreateDelegate(Type type, object firstArgument, MethodInfo method)781         public static Delegate CreateDelegate(Type type, object firstArgument, MethodInfo method) => CreateDelegate(type, firstArgument, method, throwOnBindFailure: true);
CreateDelegate(Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure)782         public static Delegate CreateDelegate(Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure) => ReflectionAugments.ReflectionCoreCallbacks.CreateDelegate(type, firstArgument, method, throwOnBindFailure);
783 
784         // V1 api: Creates open delegates to static or instance methods - relaxed signature checking allowed.
CreateDelegate(Type type, MethodInfo method)785         public static Delegate CreateDelegate(Type type, MethodInfo method) => CreateDelegate(type, method, throwOnBindFailure: true);
CreateDelegate(Type type, MethodInfo method, bool throwOnBindFailure)786         public static Delegate CreateDelegate(Type type, MethodInfo method, bool throwOnBindFailure) => ReflectionAugments.ReflectionCoreCallbacks.CreateDelegate(type, method, throwOnBindFailure);
787 
788         // V1 api: Creates closed delegates to instance methods only, relaxed signature checking disallowed.
CreateDelegate(Type type, object target, string method)789         public static Delegate CreateDelegate(Type type, object target, string method) => CreateDelegate(type, target, method, ignoreCase: false, throwOnBindFailure: true);
CreateDelegate(Type type, object target, string method, bool ignoreCase)790         public static Delegate CreateDelegate(Type type, object target, string method, bool ignoreCase) => CreateDelegate(type, target, method, ignoreCase, throwOnBindFailure: true);
CreateDelegate(Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure)791         public static Delegate CreateDelegate(Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure) => ReflectionAugments.ReflectionCoreCallbacks.CreateDelegate(type, target, method, ignoreCase, throwOnBindFailure);
792 
793         // V1 api: Creates open delegates to static methods only, relaxed signature checking disallowed.
CreateDelegate(Type type, Type target, string method)794         public static Delegate CreateDelegate(Type type, Type target, string method) => CreateDelegate(type, target, method, ignoreCase: false, throwOnBindFailure: true);
CreateDelegate(Type type, Type target, string method, bool ignoreCase)795         public static Delegate CreateDelegate(Type type, Type target, string method, bool ignoreCase) => CreateDelegate(type, target, method, ignoreCase, throwOnBindFailure: true);
CreateDelegate(Type type, Type target, string method, bool ignoreCase, bool throwOnBindFailure)796         public static Delegate CreateDelegate(Type type, Type target, string method, bool ignoreCase, bool throwOnBindFailure) => ReflectionAugments.ReflectionCoreCallbacks.CreateDelegate(type, target, method, ignoreCase, throwOnBindFailure);
797 
Clone()798         public virtual object Clone()
799         {
800             return MemberwiseClone();
801         }
802 
GetObjectData(SerializationInfo info, StreamingContext context)803         public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
804         {
805             throw new PlatformNotSupportedException(SR.Serialization_DelegatesNotSupported);
806         }
807 
808         internal bool IsOpenStatic
809         {
810             get
811             {
812                 return GetThunk(OpenStaticThunk) == m_functionPointer;
813             }
814         }
815 
InternalEqualTypes(object a, object b)816         internal static bool InternalEqualTypes(object a, object b)
817         {
818             return a.EETypePtr == b.EETypePtr;
819         }
820 
821         // Returns a new delegate of the specified type whose implementation is provied by the
822         // provided delegate.
CreateObjectArrayDelegate(Type t, Func<object[], object> handler)823         internal static Delegate CreateObjectArrayDelegate(Type t, Func<object[], object> handler)
824         {
825             EETypePtr delegateEEType;
826             if (!t.TryGetEEType(out delegateEEType))
827             {
828                 throw new InvalidOperationException();
829             }
830 
831             if (!delegateEEType.IsDefType || delegateEEType.IsGenericTypeDefinition)
832             {
833                 throw new InvalidOperationException();
834             }
835 
836             Delegate del = (Delegate)(RuntimeImports.RhNewObject(delegateEEType));
837 
838             IntPtr objArrayThunk = del.GetThunk(Delegate.ObjectArrayThunk);
839             if (objArrayThunk == IntPtr.Zero)
840             {
841                 throw new InvalidOperationException();
842             }
843 
844             del.m_helperObject = handler;
845             del.m_functionPointer = objArrayThunk;
846             del.m_firstParameter = del;
847             return del;
848         }
849 
850         //
851         // Internal (and quite unsafe) helper to create delegates of an arbitrary type. This is used to support Reflection invoke.
852         //
853         // Note that delegates constructed the normal way do not come through here. The IL transformer generates the equivalent of
854         // this code customized for each delegate type.
855         //
CreateDelegate(EETypePtr delegateEEType, IntPtr ldftnResult, Object thisObject, bool isStatic, bool isOpen)856         internal static Delegate CreateDelegate(EETypePtr delegateEEType, IntPtr ldftnResult, Object thisObject, bool isStatic, bool isOpen)
857         {
858             Delegate del = (Delegate)(RuntimeImports.RhNewObject(delegateEEType));
859 
860             // What? No constructor call? That's right, and it's not an oversight. All "construction" work happens in
861             // the Initialize() methods. This helper has a hard dependency on this invariant.
862 
863             if (isStatic)
864             {
865                 if (isOpen)
866                 {
867                     IntPtr thunk = del.GetThunk(Delegate.OpenStaticThunk);
868                     del.InitializeOpenStaticThunk(null, ldftnResult, thunk);
869                 }
870                 else
871                 {
872                     IntPtr thunk = del.GetThunk(Delegate.ClosedStaticThunk);
873                     del.InitializeClosedStaticThunk(thisObject, ldftnResult, thunk);
874                 }
875             }
876             else
877             {
878                 if (isOpen)
879                 {
880                     IntPtr thunk = del.GetThunk(Delegate.OpenInstanceThunk);
881                     del.InitializeOpenInstanceThunkDynamic(ldftnResult, thunk);
882                 }
883                 else
884                 {
885                     del.InitializeClosedInstanceWithoutNullCheck(thisObject, ldftnResult);
886                 }
887             }
888             return del;
889         }
890 
GetTargetMethodsDescriptionForDebugger()891         private string GetTargetMethodsDescriptionForDebugger()
892         {
893             if (m_functionPointer == GetThunk(MulticastThunk))
894             {
895                 // Multi-cast delegates return the Target of the last delegate in the list
896                 Delegate[] invocationList = (Delegate[])m_helperObject;
897                 int invocationCount = (int)m_extraFunctionPointerOrData;
898                 StringBuilder builder = new StringBuilder();
899                 for (int c = 0; c < invocationCount; c++)
900                 {
901                     if (c != 0)
902                         builder.Append(", ");
903 
904                     builder.Append(invocationList[c].GetTargetMethodsDescriptionForDebugger());
905                 }
906 
907                 return builder.ToString();
908             }
909             else
910             {
911                 RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate;
912                 IntPtr functionPointer = GetFunctionPointer(out typeOfFirstParameterIfInstanceDelegate, out bool _, out bool _);
913                 if (!FunctionPointerOps.IsGenericMethodPointer(functionPointer))
914                 {
915                     return DebuggerFunctionPointerFormattingHook(functionPointer, typeOfFirstParameterIfInstanceDelegate);
916                 }
917                 else
918                 {
919                     unsafe
920                     {
921                         GenericMethodDescriptor* pointerDef = FunctionPointerOps.ConvertToGenericDescriptor(functionPointer);
922                         return DebuggerFunctionPointerFormattingHook(pointerDef->InstantiationArgument, typeOfFirstParameterIfInstanceDelegate);
923                     }
924                 }
925             }
926         }
927 
DebuggerFunctionPointerFormattingHook(IntPtr functionPointer, RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate)928         private static string DebuggerFunctionPointerFormattingHook(IntPtr functionPointer, RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate)
929         {
930             // This method will be hooked by the debugger and the debugger will cause it to return a description for the function pointer
931             throw new NotSupportedException();
932         }
933     }
934 }
935