2014-08-13 10:39:27 +01:00
//
// System.Delegate.cs
//
// Authors:
// Miguel de Icaza (miguel@ximian.com)
// Daniel Stodden (stodden@in.tum.de)
// Dietmar Maurer (dietmar@ximian.com)
2014-09-04 09:07:35 +01:00
// Marek Safar (marek.safar@gmail.com)
2014-08-13 10:39:27 +01:00
//
// (C) Ximian, Inc. http://www.ximian.com
// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
2014-09-04 09:07:35 +01:00
// Copyright 2014 Xamarin, Inc (http://www.xamarin.com)
2014-08-13 10:39:27 +01:00
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Reflection ;
using System.Runtime.Remoting ;
using System.Runtime.Serialization ;
using System.Runtime.CompilerServices ;
using System.Runtime.InteropServices ;
namespace System
{
/* Contains the rarely used fields of Delegate */
2014-09-04 09:07:35 +01:00
sealed class DelegateData
{
2014-08-13 10:39:27 +01:00
public Type target_type ;
public string method_name ;
2014-09-04 09:07:35 +01:00
public bool curried_first_arg ;
2014-08-13 10:39:27 +01:00
}
[Serializable]
[StructLayout (LayoutKind.Sequential)]
public abstract class Delegate : ICloneable , ISerializable
{
#region Sync with object - internals . h
#pragma warning disable 169 , 414 , 649
private IntPtr method_ptr ;
private IntPtr invoke_impl ;
private object m_target ;
private IntPtr method ;
private IntPtr delegate_trampoline ;
2016-02-22 11:00:01 -05:00
private IntPtr extra_arg ;
2014-08-13 10:39:27 +01:00
private IntPtr method_code ;
2018-08-07 15:19:03 +00:00
private IntPtr interp_method ;
private IntPtr interp_invoke_impl ;
2014-08-13 10:39:27 +01:00
private MethodInfo method_info ;
// Keep a ref of the MethodInfo passed to CreateDelegate.
// Used to keep DynamicMethods alive.
private MethodInfo original_method_info ;
private DelegateData data ;
2015-10-06 08:40:39 -04:00
private bool method_is_virtual ;
2014-08-13 10:39:27 +01:00
#pragma warning restore 169 , 414 , 649
#endregion
protected Delegate ( object target , string method )
{
if ( target = = null )
throw new ArgumentNullException ( "target" ) ;
if ( method = = null )
throw new ArgumentNullException ( "method" ) ;
this . m_target = target ;
this . data = new DelegateData ( ) ;
this . data . method_name = method ;
}
protected Delegate ( Type target , string method )
{
if ( target = = null )
throw new ArgumentNullException ( "target" ) ;
if ( method = = null )
throw new ArgumentNullException ( "method" ) ;
this . data = new DelegateData ( ) ;
this . data . method_name = method ;
this . data . target_type = target ;
}
public MethodInfo Method {
get {
2015-12-18 19:40:30 -05:00
return GetMethodImpl ( ) ;
2014-08-13 10:39:27 +01:00
}
}
2015-10-06 08:40:39 -04:00
[MethodImplAttribute (MethodImplOptions.InternalCall)]
extern MethodInfo GetVirtualMethod_internal ( ) ;
2014-08-13 10:39:27 +01:00
public object Target {
get {
return m_target ;
}
}
2018-05-10 08:37:03 +00:00
internal IntPtr GetNativeFunctionPointer ( ) = > method_ptr ;
2014-08-13 10:39:27 +01:00
//
// Methods
//
[MethodImplAttribute (MethodImplOptions.InternalCall)]
internal static extern Delegate CreateDelegate_internal ( Type type , object target , MethodInfo info , bool throwOnBindFailure ) ;
private static bool arg_type_match ( Type delArgType , Type argType ) {
bool match = delArgType = = argType ;
// Delegate contravariance
if ( ! match ) {
if ( ! argType . IsValueType & & argType . IsAssignableFrom ( delArgType ) )
match = true ;
}
// enum basetypes
if ( ! match ) {
if ( delArgType . IsEnum & & Enum . GetUnderlyingType ( delArgType ) = = argType )
match = true ;
2018-04-24 09:31:23 +00:00
else if ( argType . IsEnum & & Enum . GetUnderlyingType ( argType ) = = delArgType )
match = true ;
2014-08-13 10:39:27 +01:00
}
return match ;
}
private static bool arg_type_match_this ( Type delArgType , Type argType , bool boxedThis ) {
bool match ;
if ( argType . IsValueType )
match = delArgType . IsByRef & & delArgType . GetElementType ( ) = = argType | |
( boxedThis & & delArgType = = argType ) ;
else
match = delArgType = = argType | | argType . IsAssignableFrom ( delArgType ) ;
return match ;
}
private static bool return_type_match ( Type delReturnType , Type returnType ) {
bool returnMatch = returnType = = delReturnType ;
if ( ! returnMatch ) {
// Delegate covariance
if ( ! returnType . IsValueType & & delReturnType . IsAssignableFrom ( returnType ) )
returnMatch = true ;
}
return returnMatch ;
}
public static Delegate CreateDelegate ( Type type , object firstArgument , MethodInfo method , bool throwOnBindFailure )
{
return CreateDelegate ( type , firstArgument , method , throwOnBindFailure , true ) ;
}
static Delegate CreateDelegate ( Type type , object firstArgument , MethodInfo method , bool throwOnBindFailure , bool allowClosed )
{
// The name of the parameter changed in 2.0
object target = firstArgument ;
if ( type = = null )
throw new ArgumentNullException ( "type" ) ;
if ( method = = null )
throw new ArgumentNullException ( "method" ) ;
if ( ! type . IsSubclassOf ( typeof ( MulticastDelegate ) ) )
throw new ArgumentException ( "type is not a subclass of Multicastdelegate" ) ;
MethodInfo invoke = type . GetMethod ( "Invoke" ) ;
2015-08-26 07:17:56 -04:00
if ( ! return_type_match ( invoke . ReturnType , method . ReturnType ) ) {
2014-08-13 10:39:27 +01:00
if ( throwOnBindFailure )
throw new ArgumentException ( "method return type is incompatible" ) ;
else
return null ;
2015-08-26 07:17:56 -04:00
}
2014-08-13 10:39:27 +01:00
ParameterInfo [ ] delargs = invoke . GetParametersInternal ( ) ;
ParameterInfo [ ] args = method . GetParametersInternal ( ) ;
bool argLengthMatch ;
if ( target ! = null ) {
// delegate closed over target
if ( ! method . IsStatic )
// target is passed as this
argLengthMatch = ( args . Length = = delargs . Length ) ;
else
// target is passed as the first argument to the static method
argLengthMatch = ( args . Length = = delargs . Length + 1 ) ;
} else {
if ( ! method . IsStatic ) {
//
// Net 2.0 feature. The first argument of the delegate is passed
// as the 'this' argument to the method.
//
argLengthMatch = ( args . Length + 1 = = delargs . Length ) ;
if ( ! argLengthMatch )
// closed over a null reference
argLengthMatch = ( args . Length = = delargs . Length ) ;
} else {
argLengthMatch = ( args . Length = = delargs . Length ) ;
if ( ! argLengthMatch )
// closed over a null reference
argLengthMatch = args . Length = = delargs . Length + 1 ;
}
}
2015-08-26 07:17:56 -04:00
if ( ! argLengthMatch ) {
2014-08-13 10:39:27 +01:00
if ( throwOnBindFailure )
2019-02-04 20:11:37 +00:00
throw new TargetParameterCountException ( "Parameter count mismatch." ) ;
2014-08-13 10:39:27 +01:00
else
return null ;
2015-08-26 07:17:56 -04:00
}
2014-08-13 10:39:27 +01:00
bool argsMatch ;
2014-09-04 09:07:35 +01:00
DelegateData delegate_data = new DelegateData ( ) ;
2014-08-13 10:39:27 +01:00
if ( target ! = null ) {
if ( ! method . IsStatic ) {
argsMatch = arg_type_match_this ( target . GetType ( ) , method . DeclaringType , true ) ;
for ( int i = 0 ; i < args . Length ; i + + )
argsMatch & = arg_type_match ( delargs [ i ] . ParameterType , args [ i ] . ParameterType ) ;
} else {
argsMatch = arg_type_match ( target . GetType ( ) , args [ 0 ] . ParameterType ) ;
for ( int i = 1 ; i < args . Length ; i + + )
2014-09-04 09:07:35 +01:00
argsMatch & = arg_type_match ( delargs [ i - 1 ] . ParameterType , args [ i ] . ParameterType ) ;
delegate_data . curried_first_arg = true ;
2014-08-13 10:39:27 +01:00
}
} else {
if ( ! method . IsStatic ) {
if ( args . Length + 1 = = delargs . Length ) {
// The first argument should match this
argsMatch = arg_type_match_this ( delargs [ 0 ] . ParameterType , method . DeclaringType , false ) ;
for ( int i = 0 ; i < args . Length ; i + + )
argsMatch & = arg_type_match ( delargs [ i + 1 ] . ParameterType , args [ i ] . ParameterType ) ;
} else {
// closed over a null reference
argsMatch = allowClosed ;
for ( int i = 0 ; i < args . Length ; i + + )
argsMatch & = arg_type_match ( delargs [ i ] . ParameterType , args [ i ] . ParameterType ) ;
}
} else {
if ( delargs . Length + 1 = = args . Length ) {
// closed over a null reference
argsMatch = ! ( args [ 0 ] . ParameterType . IsValueType | | args [ 0 ] . ParameterType . IsByRef ) & & allowClosed ;
for ( int i = 0 ; i < delargs . Length ; i + + )
argsMatch & = arg_type_match ( delargs [ i ] . ParameterType , args [ i + 1 ] . ParameterType ) ;
2014-09-04 09:07:35 +01:00
delegate_data . curried_first_arg = true ;
2014-08-13 10:39:27 +01:00
} else {
argsMatch = true ;
for ( int i = 0 ; i < args . Length ; i + + )
argsMatch & = arg_type_match ( delargs [ i ] . ParameterType , args [ i ] . ParameterType ) ;
}
}
}
2015-08-26 07:17:56 -04:00
if ( ! argsMatch ) {
2014-08-13 10:39:27 +01:00
if ( throwOnBindFailure )
throw new ArgumentException ( "method arguments are incompatible" ) ;
else
return null ;
2015-08-26 07:17:56 -04:00
}
2014-08-13 10:39:27 +01:00
Delegate d = CreateDelegate_internal ( type , target , method , throwOnBindFailure ) ;
if ( d ! = null )
d . original_method_info = method ;
2014-09-04 09:07:35 +01:00
if ( delegate_data ! = null )
d . data = delegate_data ;
2014-08-13 10:39:27 +01:00
return d ;
}
public static Delegate CreateDelegate ( Type type , object firstArgument , MethodInfo method )
{
return CreateDelegate ( type , firstArgument , method , true , true ) ;
}
public static Delegate CreateDelegate ( Type type , MethodInfo method , bool throwOnBindFailure )
{
return CreateDelegate ( type , null , method , throwOnBindFailure , false ) ;
}
public static Delegate CreateDelegate ( Type type , MethodInfo method )
{
return CreateDelegate ( type , method , true ) ;
}
public static Delegate CreateDelegate ( Type type , object target , string method )
{
return CreateDelegate ( type , target , method , false ) ;
}
static MethodInfo GetCandidateMethod ( Type type , Type target , string method , BindingFlags bflags , bool ignoreCase , bool throwOnBindFailure )
{
if ( type = = null )
throw new ArgumentNullException ( "type" ) ;
if ( method = = null )
throw new ArgumentNullException ( "method" ) ;
if ( ! type . IsSubclassOf ( typeof ( MulticastDelegate ) ) )
throw new ArgumentException ( "type is not subclass of MulticastDelegate." ) ;
MethodInfo invoke = type . GetMethod ( "Invoke" ) ;
ParameterInfo [ ] delargs = invoke . GetParametersInternal ( ) ;
Type [ ] delargtypes = new Type [ delargs . Length ] ;
for ( int i = 0 ; i < delargs . Length ; i + + )
delargtypes [ i ] = delargs [ i ] . ParameterType ;
/ *
* FIXME : we should check the caller has reflection permission
* or if it lives in the same assembly . . .
* /
/ *
* since we need to walk the inheritance chain anyway to
* find private methods , adjust the bindingflags to ignore
* inherited methods
* /
BindingFlags flags = BindingFlags . ExactBinding |
BindingFlags . Public | BindingFlags . NonPublic |
BindingFlags . DeclaredOnly | bflags ;
if ( ignoreCase )
flags | = BindingFlags . IgnoreCase ;
MethodInfo info = null ;
for ( Type targetType = target ; targetType ! = null ; targetType = targetType . BaseType ) {
MethodInfo mi = targetType . GetMethod ( method , flags ,
2019-04-12 14:10:50 +00:00
null , delargtypes , Array . Empty < ParameterModifier > ( ) ) ;
2014-08-13 10:39:27 +01:00
if ( mi ! = null & & return_type_match ( invoke . ReturnType , mi . ReturnType ) ) {
info = mi ;
break ;
}
}
if ( info = = null ) {
if ( throwOnBindFailure )
throw new ArgumentException ( "Couldn't bind to method '" + method + "'." ) ;
else
return null ;
}
return info ;
}
public static Delegate CreateDelegate ( Type type , Type target , string method , bool ignoreCase , bool throwOnBindFailure )
{
if ( target = = null )
throw new ArgumentNullException ( "target" ) ;
MethodInfo info = GetCandidateMethod ( type , target , method ,
BindingFlags . Static , ignoreCase , throwOnBindFailure ) ;
if ( info = = null )
return null ;
return CreateDelegate_internal ( type , null , info , throwOnBindFailure ) ;
}
public static Delegate CreateDelegate ( Type type , Type target , string method ) {
return CreateDelegate ( type , target , method , false , true ) ;
}
public static Delegate CreateDelegate ( Type type , Type target , string method , bool ignoreCase ) {
return CreateDelegate ( type , target , method , ignoreCase , true ) ;
}
public static Delegate CreateDelegate ( Type type , object target , string method , bool ignoreCase , bool throwOnBindFailure )
{
if ( target = = null )
throw new ArgumentNullException ( "target" ) ;
MethodInfo info = GetCandidateMethod ( type , target . GetType ( ) , method ,
BindingFlags . Instance , ignoreCase , throwOnBindFailure ) ;
if ( info = = null )
return null ;
return CreateDelegate_internal ( type , target , info , throwOnBindFailure ) ;
}
public static Delegate CreateDelegate ( Type type , object target , string method , bool ignoreCase ) {
return CreateDelegate ( type , target , method , ignoreCase , true ) ;
}
public object DynamicInvoke ( params object [ ] args )
{
return DynamicInvokeImpl ( args ) ;
}
2014-09-04 09:07:35 +01:00
void InitializeDelegateData ( )
{
DelegateData delegate_data = new DelegateData ( ) ;
if ( method_info . IsStatic ) {
if ( m_target ! = null ) {
delegate_data . curried_first_arg = true ;
} else {
MethodInfo invoke = GetType ( ) . GetMethod ( "Invoke" ) ;
if ( invoke . GetParametersCount ( ) + 1 = = method_info . GetParametersCount ( ) )
delegate_data . curried_first_arg = true ;
}
}
this . data = delegate_data ;
}
2014-08-13 10:39:27 +01:00
protected virtual object DynamicInvokeImpl ( object [ ] args )
{
if ( Method = = null ) {
Type [ ] mtypes = new Type [ args . Length ] ;
for ( int i = 0 ; i < args . Length ; + + i ) {
mtypes [ i ] = args [ i ] . GetType ( ) ;
}
method_info = m_target . GetType ( ) . GetMethod ( data . method_name , mtypes ) ;
}
2014-09-04 09:07:35 +01:00
var target = m_target ;
if ( this . data = = null )
InitializeDelegateData ( ) ;
if ( Method . IsStatic ) {
//
2014-08-13 10:39:27 +01:00
// The delegate is bound to m_target
2014-09-04 09:07:35 +01:00
//
if ( data . curried_first_arg ) {
if ( args = = null ) {
args = new [ ] { target } ;
} else {
Array . Resize ( ref args , args . Length + 1 ) ;
Array . Copy ( args , 0 , args , 1 , args . Length - 1 ) ;
args [ 0 ] = target ;
}
target = null ;
}
} else {
if ( m_target = = null & & args ! = null & & args . Length > 0 ) {
target = args [ 0 ] ;
Array . Copy ( args , 1 , args , 0 , args . Length - 1 ) ;
Array . Resize ( ref args , args . Length - 1 ) ;
2014-08-13 10:39:27 +01:00
}
}
2014-09-04 09:07:35 +01:00
return Method . Invoke ( target , args ) ;
2014-08-13 10:39:27 +01:00
}
public virtual object Clone ( )
{
return MemberwiseClone ( ) ;
}
2015-10-06 08:40:39 -04:00
public override bool Equals ( object obj )
2014-08-13 10:39:27 +01:00
{
2015-10-06 08:40:39 -04:00
Delegate d = obj as Delegate ;
2014-08-13 10:39:27 +01:00
if ( d = = null )
return false ;
2015-10-06 08:40:39 -04:00
2014-08-13 10:39:27 +01:00
// Do not compare method_ptr, since it can point to a trampoline
2015-10-06 08:40:39 -04:00
if ( d . m_target = = m_target & & d . Method = = Method ) {
2014-08-13 10:39:27 +01:00
if ( d . data ! = null | | data ! = null ) {
/* Uncommon case */
if ( d . data ! = null & & data ! = null )
return ( d . data . target_type = = data . target_type & & d . data . method_name = = data . method_name ) ;
2014-09-04 09:07:35 +01:00
else {
if ( d . data ! = null )
return d . data . target_type = = null ;
if ( data ! = null )
return data . target_type = = null ;
2014-08-13 10:39:27 +01:00
return false ;
2014-09-04 09:07:35 +01:00
}
2014-08-13 10:39:27 +01:00
}
return true ;
}
return false ;
}
public override int GetHashCode ( )
{
2017-08-21 15:34:15 +00:00
MethodInfo m ;
m = Method ;
2017-09-25 16:57:44 +00:00
return ( m ! = null ? m . GetHashCode ( ) : GetType ( ) . GetHashCode ( ) ) ^ RuntimeHelpers . GetHashCode ( m_target ) ;
2014-08-13 10:39:27 +01:00
}
protected virtual MethodInfo GetMethodImpl ( )
{
2015-12-18 19:40:30 -05:00
if ( method_info ! = null ) {
return method_info ;
} else {
if ( method ! = IntPtr . Zero ) {
if ( ! method_is_virtual )
2019-04-12 14:10:50 +00:00
method_info = ( MethodInfo ) RuntimeMethodInfo . GetMethodFromHandleNoGenericCheck ( new RuntimeMethodHandle ( method ) ) ;
2015-12-18 19:40:30 -05:00
else
method_info = GetVirtualMethod_internal ( ) ;
}
return method_info ;
}
2014-08-13 10:39:27 +01:00
}
// This is from ISerializable
public virtual void GetObjectData ( SerializationInfo info , StreamingContext context )
{
DelegateSerializationHolder . GetDelegateData ( this , info , context ) ;
}
public virtual Delegate [ ] GetInvocationList ( )
{
return new Delegate [ ] { this } ;
}
/// <symmary>
/// Returns a new MulticastDelegate holding the
/// concatenated invocation lists of MulticastDelegates a and b
/// </symmary>
public static Delegate Combine ( Delegate a , Delegate b )
{
2015-04-07 09:35:12 +01:00
if ( a = = null )
2014-08-13 10:39:27 +01:00
return b ;
2016-08-03 10:59:49 +00:00
if ( b = = null )
return a ;
if ( a . GetType ( ) ! = b . GetType ( ) )
2019-04-12 14:10:50 +00:00
throw new ArgumentException ( string . Format ( "Incompatible Delegate Types. First is {0} second is {1}." , a . GetType ( ) . FullName , b . GetType ( ) . FullName ) ) ;
2016-08-03 10:59:49 +00:00
2014-08-13 10:39:27 +01:00
return a . CombineImpl ( b ) ;
}
/// <symmary>
/// Returns a new MulticastDelegate holding the
/// concatenated invocation lists of an Array of MulticastDelegates
/// </symmary>
[System.Runtime.InteropServices.ComVisible (true)]
public static Delegate Combine ( params Delegate [ ] delegates )
{
if ( delegates = = null )
return null ;
Delegate retval = null ;
foreach ( Delegate next in delegates )
retval = Combine ( retval , next ) ;
return retval ;
}
protected virtual Delegate CombineImpl ( Delegate d )
{
throw new MulticastNotSupportedException ( String . Empty ) ;
}
public static Delegate Remove ( Delegate source , Delegate value )
{
if ( source = = null )
return null ;
if ( value = = null )
return source ;
if ( source . GetType ( ) ! = value . GetType ( ) )
2019-04-12 14:10:50 +00:00
throw new ArgumentException ( string . Format ( "Incompatible Delegate Types. First is {0} second is {1}." , source . GetType ( ) . FullName , value . GetType ( ) . FullName ) ) ;
2014-08-13 10:39:27 +01:00
return source . RemoveImpl ( value ) ;
}
protected virtual Delegate RemoveImpl ( Delegate d )
{
if ( this . Equals ( d ) )
return null ;
return this ;
}
public static Delegate RemoveAll ( Delegate source , Delegate value )
{
Delegate tmp = source ;
while ( ( source = Delegate . Remove ( source , value ) ) ! = tmp )
tmp = source ;
return tmp ;
}
public static bool operator = = ( Delegate d1 , Delegate d2 )
{
if ( ( object ) d1 = = null ) {
if ( ( object ) d2 = = null )
return true ;
return false ;
} else if ( ( object ) d2 = = null )
return false ;
return d1 . Equals ( d2 ) ;
}
public static bool operator ! = ( Delegate d1 , Delegate d2 )
{
return ! ( d1 = = d2 ) ;
}
internal bool IsTransparentProxy ( )
{
#if DISABLE_REMOTING
return false ;
#else
return RemotingServices . IsTransparentProxy ( m_target ) ;
#endif
}
2015-08-26 07:17:56 -04:00
internal static Delegate CreateDelegateNoSecurityCheck ( RuntimeType type , Object firstArgument , MethodInfo method )
{
return CreateDelegate_internal ( type , firstArgument , method , true ) ;
}
/* Internal call largely inspired from MS Delegate.InternalAllocLike */
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal extern static MulticastDelegate AllocDelegateLike_internal ( Delegate d ) ;
2014-08-13 10:39:27 +01:00
}
}