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