2020-03-24 11:08:37 -07:00
// Copyright (c) Microsoft Corporation.
2018-02-13 09:23:53 -08:00
// Licensed under the MIT License.
2016-03-30 14:20:52 -07:00
using System.Collections.Concurrent ;
using System.Collections.Generic ;
using System.Collections.ObjectModel ;
using System.ComponentModel ;
2017-07-17 11:27:01 -07:00
using System.Data ;
2016-03-30 14:20:52 -07:00
using System.Diagnostics ;
using System.Diagnostics.CodeAnalysis ;
using System.Dynamic ;
using System.Globalization ;
using System.Linq ;
using System.Linq.Expressions ;
using System.Management.Automation.Language ;
using System.Management.Automation.Runspaces ;
using System.Reflection ;
using System.Reflection.Emit ;
using System.Text ;
using System.Threading ;
using System.Xml ;
using System.Management.Automation.Internal ;
using Microsoft.PowerShell ;
using Dbg = System . Management . Automation . Diagnostics ;
#pragma warning disable 1634 , 1691 // Stops compiler from warning about unknown warnings
namespace System.Management.Automation
{
/// <summary>
/// Base class for all Adapters
2017-10-27 05:12:19 +08:00
/// This is the place to look every time you create a new Adapter. Consider if you
2016-03-30 14:20:52 -07:00
/// should implement each of the virtual methods here.
/// The base class deals with errors and performs additional operations before and after
2016-08-28 05:44:55 +00:00
/// calling the derived virtual methods.
2016-03-30 14:20:52 -07:00
/// </summary>
internal abstract class Adapter
{
/// <summary>
2019-01-10 02:07:41 +05:00
/// Tracer for this and derivate classes.
2016-03-30 14:20:52 -07:00
/// </summary>
[TraceSource("ETS", "Extended Type System")]
protected static PSTraceSource tracer = PSTraceSource . GetTracer ( "ETS" , "Extended Type System" );
#region virtual
#region member
2018-03-15 15:09:38 -07:00
internal virtual bool CanSiteBinderOptimize ( MemberTypes typeToOperateOn ) { return false ; }
2016-03-30 14:20:52 -07:00
protected static IEnumerable < string > GetDotNetTypeNameHierarchy ( Type type )
{
2018-04-13 15:01:41 -07:00
for (; type != null ; type = type . BaseType )
2016-03-30 14:20:52 -07:00
{
yield return type . FullName ;
}
}
protected static IEnumerable < string > GetDotNetTypeNameHierarchy ( object obj )
{
return GetDotNetTypeNameHierarchy ( obj . GetType ());
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the TypeNameHierarchy out of an object.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to get the TypeNameHierarchy from.</param>
2016-03-30 14:20:52 -07:00
protected virtual IEnumerable < string > GetTypeNameHierarchy ( object obj )
{
return GetDotNetTypeNameHierarchy ( obj );
}
/// <summary>
/// Returns the cached typename, if it can be cached, otherwise constructs a new typename.
/// By default, we don't return interned values, adapters can override if they choose.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to get the TypeNameHierarchy from.</param>
2016-03-30 14:20:52 -07:00
protected virtual ConsolidatedString GetInternedTypeNameHierarchy ( object obj )
{
return new ConsolidatedString ( GetTypeNameHierarchy ( obj ));
}
/// <summary>
/// Returns null if memberName is not a member in the adapter or
2019-01-06 04:40:24 +05:00
/// the corresponding PSMemberInfo.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to retrieve the PSMemberInfo from.</param>
/// <param name="memberName">Name of the member to be retrieved.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The PSMemberInfo corresponding to memberName from obj.</returns>
2016-03-30 14:20:52 -07:00
protected abstract T GetMember < T >( object obj , string memberName ) where T : PSMemberInfo ;
2019-03-21 02:43:52 +01:00
/// <summary>
/// Returns the first PSMemberInfo whose name matches the specified <see cref="MemberNamePredicate"/>.
/// Otherwise, return null.
/// </summary>
/// <param name="obj">Object to retrieve the PSMemberInfo from.</param>
/// <param name="predicate">The predicate to find the matching member.</param>
/// <returns>The PSMemberInfo corresponding to the predicate match.</returns>
protected abstract T GetFirstMemberOrDefault < T >( object obj , MemberNamePredicate predicate ) where T : PSMemberInfo ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// Retrieves all the members available in the object.
/// The adapter implementation is encouraged to cache all properties/methods available
/// in the first call to GetMember and GetMembers so that subsequent
/// calls can use the cache.
/// In the case of the .NET adapter that would be a cache from the .NET type to
2017-10-27 05:12:19 +08:00
/// the public properties and fields available in that type.
2016-03-30 14:20:52 -07:00
/// In the case of the DirectoryEntry adapter, this could be a cache of the objectClass
/// to the properties available in it.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to get all the member information from.</param>
2018-12-25 21:43:03 +05:00
/// <returns>All members in obj.</returns>
2016-07-29 13:02:49 -07:00
protected abstract PSMemberInfoInternalCollection < T > GetMembers < T >( object obj ) where T : PSMemberInfo ;
2016-03-30 14:20:52 -07:00
#endregion member
#region property
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the value from a property coming from a previous call to GetMember.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="property">PSProperty coming from a previous call to GetMember.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The value of the property.</returns>
2016-03-30 14:20:52 -07:00
protected abstract object PropertyGet ( PSProperty property );
/// <summary>
2019-01-06 04:40:24 +05:00
/// Sets the value of a property coming from a previous call to GetMember.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="property">PSProperty coming from a previous call to GetMember.</param>
2018-12-29 08:25:49 +05:00
/// <param name="setValue">Value to set the property with.</param>
/// <param name="convertIfPossible">Instructs the adapter to convert before setting, if the adapter supports conversion.</param>
2016-03-30 14:20:52 -07:00
protected abstract void PropertySet ( PSProperty property , object setValue , bool convertIfPossible );
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if the property is settable.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if the property is settable.</returns>
2016-03-30 14:20:52 -07:00
protected abstract bool PropertyIsSettable ( PSProperty property );
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if the property is gettable.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if the property is gettable.</returns>
2016-03-30 14:20:52 -07:00
protected abstract bool PropertyIsGettable ( PSProperty property );
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the name of the type corresponding to the property's value.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="property">PSProperty obtained in a previous GetMember.</param>
/// <param name="forDisplay">True if the result is for display purposes only.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The name of the type corresponding to the member.</returns>
2016-03-30 14:20:52 -07:00
protected abstract string PropertyType ( PSProperty property , bool forDisplay );
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the string representation of the property in the object.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property obtained in a previous GetMember.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The string representation of the property in the object.</returns>
2016-03-30 14:20:52 -07:00
protected abstract string PropertyToString ( PSProperty property );
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns an array with the property attributes.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property we want the attributes from.</param>
2018-12-25 21:43:03 +05:00
/// <returns>An array with the property attributes.</returns>
2016-03-30 14:20:52 -07:00
protected abstract AttributeCollection PropertyAttributes ( PSProperty property );
#endregion property
#region method
/// <summary>
/// Called after a non null return from GetMember to try to call
2019-01-06 04:40:24 +05:00
/// the method with the arguments.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="method">The non empty return from GetMethods.</param>
/// <param name="invocationConstraints">Invocation constraints.</param>
/// <param name="arguments">The arguments to use.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The return value for the method.</returns>
2016-03-30 14:20:52 -07:00
protected virtual object MethodInvoke ( PSMethod method , PSMethodInvocationConstraints invocationConstraints , object [] arguments )
{
return this . MethodInvoke ( method , arguments );
}
/// <summary>
/// Called after a non null return from GetMember to try to call
2019-01-06 04:40:24 +05:00
/// the method with the arguments.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="method">The non empty return from GetMethods.</param>
/// <param name="arguments">The arguments to use.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The return value for the method.</returns>
2016-03-30 14:20:52 -07:00
protected abstract object MethodInvoke ( PSMethod method , object [] arguments );
/// <summary>
2019-01-06 04:40:24 +05:00
/// Called after a non null return from GetMember to return the overloads.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="method">The return of GetMember.</param>
2016-03-30 14:20:52 -07:00
/// <returns></returns>
2018-12-22 01:26:06 -08:00
protected abstract Collection < string > MethodDefinitions ( PSMethod method );
2016-03-30 14:20:52 -07:00
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the string representation of the method in the object.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-25 21:43:03 +05:00
/// <returns>The string representation of the method in the object.</returns>
2016-03-30 14:20:52 -07:00
protected virtual string MethodToString ( PSMethod method )
{
StringBuilder returnValue = new StringBuilder ();
Collection < string > definitions = MethodDefinitions ( method );
for ( int i = 0 ; i < definitions . Count ; i ++)
{
returnValue . Append ( definitions [ i ]);
returnValue . Append ( ", " );
}
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
returnValue . Remove ( returnValue . Length - 2 , 2 );
return returnValue . ToString ();
}
#endregion method
#region parameterized property
2019-03-21 02:43:52 +01:00
2016-03-30 14:20:52 -07:00
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the name of the type corresponding to the property's value.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property obtained in a previous GetMember.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The name of the type corresponding to the member.</returns>
2016-03-30 14:20:52 -07:00
/// <remarks>
/// It is not necessary for derived methods to override this.
/// This method is called only if ParameterizedProperties are present.
/// </remarks>
protected virtual string ParameterizedPropertyType ( PSParameterizedProperty property )
{
Diagnostics . Assert ( false , "adapter is not called for parameterized properties" );
throw PSTraceSource . NewNotSupportedException ();
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if the property is settable.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if the property is settable.</returns>
2016-03-30 14:20:52 -07:00
/// <remarks>
/// It is not necessary for derived methods to override this.
/// This method is called only if ParameterizedProperties are present.
/// </remarks>
protected virtual bool ParameterizedPropertyIsSettable ( PSParameterizedProperty property )
{
Diagnostics . Assert ( false , "adapter is not called for parameterized properties" );
throw PSTraceSource . NewNotSupportedException ();
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if the property is gettable.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if the property is gettable.</returns>
2016-03-30 14:20:52 -07:00
/// <remarks>
/// It is not necessary for derived methods to override this.
/// This method is called only if ParameterizedProperties are present.
/// </remarks>
protected virtual bool ParameterizedPropertyIsGettable ( PSParameterizedProperty property )
{
Diagnostics . Assert ( false , "adapter is not called for parameterized properties" );
throw PSTraceSource . NewNotSupportedException ();
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Called after a non null return from GetMember to return the overloads.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">The return of GetMember.</param>
2016-03-30 14:20:52 -07:00
/// <remarks>
/// It is not necessary for derived methods to override this.
/// This method is called only if ParameterizedProperties are present.
/// </remarks>
2018-12-22 01:26:06 -08:00
protected virtual Collection < string > ParameterizedPropertyDefinitions ( PSParameterizedProperty property )
2016-03-30 14:20:52 -07:00
{
Diagnostics . Assert ( false , "adapter is not called for parameterized properties" );
throw PSTraceSource . NewNotSupportedException ();
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Called after a non null return from GetMember to get the property value.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">The non empty return from GetMember.</param>
/// <param name="arguments">The arguments to use.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The return value for the property.</returns>
2016-03-30 14:20:52 -07:00
/// <remarks>
/// It is not necessary for derived methods to override this.
/// This method is called only if ParameterizedProperties are present.
/// </remarks>
protected virtual object ParameterizedPropertyGet ( PSParameterizedProperty property , object [] arguments )
{
Diagnostics . Assert ( false , "adapter is not called for parameterized properties" );
throw PSTraceSource . NewNotSupportedException ();
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Called after a non null return from GetMember to set the property value.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">The non empty return from GetMember.</param>
/// <param name="setValue">The value to set property with.</param>
/// <param name="arguments">The arguments to use.</param>
2016-03-30 14:20:52 -07:00
/// <remarks>
/// It is not necessary for derived methods to override this.
/// This method is called only if ParameterizedProperties are present.
/// </remarks>
protected virtual void ParameterizedPropertySet ( PSParameterizedProperty property , object setValue , object [] arguments )
{
Diagnostics . Assert ( false , "adapter is not called for parameterized properties" );
throw PSTraceSource . NewNotSupportedException ();
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the string representation of the property in the object.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property obtained in a previous GetMember.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The string representation of the property in the object.</returns>
2016-03-30 14:20:52 -07:00
/// <remarks>
/// It is not necessary for derived methods to override this.
/// This method is called only if ParameterizedProperties are present.
/// </remarks>
protected virtual string ParameterizedPropertyToString ( PSParameterizedProperty property )
{
Diagnostics . Assert ( false , "adapter is not called for parameterized properties" );
throw PSTraceSource . NewNotSupportedException ();
}
2017-10-27 05:12:19 +08:00
#endregion parameterized property
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
#endregion virtual
#region base
#region private
2019-03-21 02:43:52 +01:00
private static Exception NewException (
Exception e ,
string errorId ,
string targetErrorId ,
string resourceString ,
params object [] parameters )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
object [] newParameters = new object [ parameters . Length + 1 ];
2016-03-30 14:20:52 -07:00
for ( int i = 0 ; i < parameters . Length ; i ++)
{
newParameters [ i + 1 ] = parameters [ i ];
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
Exception ex = e as TargetInvocationException ;
if ( ex != null )
{
Exception inner = ex . InnerException ?? ex ;
newParameters [ 0 ] = inner . Message ;
2019-03-23 09:52:30 +05:00
return new ExtendedTypeSystemException (
targetErrorId ,
2016-03-30 14:20:52 -07:00
inner ,
resourceString ,
newParameters );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
newParameters [ 0 ] = e . Message ;
2019-03-23 09:52:30 +05:00
return new ExtendedTypeSystemException (
errorId ,
2016-03-30 14:20:52 -07:00
e ,
resourceString ,
newParameters );
}
#endregion private
#region member
2019-03-21 02:43:52 +01:00
2016-03-30 14:20:52 -07:00
internal ConsolidatedString BaseGetTypeNameHierarchy ( object obj )
{
try
{
return GetInternedTypeNameHierarchy ( obj );
}
catch ( ExtendedTypeSystemException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw NewException (
e ,
"CatchFromBaseGetTypeNameHierarchy" ,
"CatchFromBaseGetTypeNameHierarchyTI" ,
ExtendedTypeSystem . ExceptionRetrievingTypeNameHierarchy );
2016-03-30 14:20:52 -07:00
}
}
2016-07-29 13:02:49 -07:00
internal T BaseGetMember < T >( object obj , string memberName ) where T : PSMemberInfo
2016-03-30 14:20:52 -07:00
{
try
{
return this . GetMember < T >( obj , memberName );
}
catch ( ExtendedTypeSystemException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw NewException (
e ,
"CatchFromBaseGetMember" ,
"CatchFromBaseGetMemberTI" ,
ExtendedTypeSystem . ExceptionGettingMember ,
memberName );
2016-03-30 14:20:52 -07:00
}
}
2019-03-21 02:43:52 +01:00
internal T BaseGetFirstMemberOrDefault < T >( object obj , MemberNamePredicate predicate ) where T : PSMemberInfo
{
try
{
return this . GetFirstMemberOrDefault < T >( obj , predicate );
}
catch ( ExtendedTypeSystemException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw NewException (
e ,
"CatchFromBaseGetMember" ,
"CatchFromBaseGetMemberTI" ,
ExtendedTypeSystem . ExceptionGettingMember ,
nameof ( predicate ));
2019-03-21 02:43:52 +01:00
}
}
2016-03-30 14:20:52 -07:00
internal PSMemberInfoInternalCollection < T > BaseGetMembers < T >( object obj ) where T : PSMemberInfo
{
try
{
return this . GetMembers < T >( obj );
}
catch ( ExtendedTypeSystemException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw NewException (
e ,
"CatchFromBaseGetMembers" ,
"CatchFromBaseGetMembersTI" ,
2016-03-30 14:20:52 -07:00
ExtendedTypeSystem . ExceptionGettingMembers );
}
}
#endregion member
#region property
internal object BasePropertyGet ( PSProperty property )
{
try
{
return PropertyGet ( property );
}
catch ( TargetInvocationException ex )
{
Exception inner = ex . InnerException ?? ex ;
2019-03-23 09:52:30 +05:00
throw new GetValueInvocationException (
"CatchFromBaseAdapterGetValueTI" ,
2016-03-30 14:20:52 -07:00
inner ,
ExtendedTypeSystem . ExceptionWhenGetting ,
property . Name , inner . Message );
}
catch ( GetValueException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw new GetValueInvocationException (
"CatchFromBaseAdapterGetValue" ,
2016-03-30 14:20:52 -07:00
e ,
ExtendedTypeSystem . ExceptionWhenGetting ,
property . Name , e . Message );
}
}
internal void BasePropertySet ( PSProperty property , object setValue , bool convert )
{
try
{
PropertySet ( property , setValue , convert );
}
catch ( TargetInvocationException ex )
{
Exception inner = ex . InnerException ?? ex ;
2019-03-23 09:52:30 +05:00
throw new SetValueInvocationException (
"CatchFromBaseAdapterSetValueTI" ,
2016-03-30 14:20:52 -07:00
inner ,
ExtendedTypeSystem . ExceptionWhenSetting ,
property . Name , inner . Message );
}
catch ( SetValueException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw new SetValueInvocationException (
"CatchFromBaseAdapterSetValue" ,
2016-03-30 14:20:52 -07:00
e ,
ExtendedTypeSystem . ExceptionWhenSetting ,
property . Name , e . Message );
}
}
internal bool BasePropertyIsSettable ( PSProperty property )
{
try
{
return this . PropertyIsSettable ( property );
}
catch ( ExtendedTypeSystemException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw NewException (
e ,
"CatchFromBasePropertyIsSettable" ,
"CatchFromBasePropertyIsSettableTI" ,
ExtendedTypeSystem . ExceptionRetrievingPropertyWriteState ,
property . Name );
2016-03-30 14:20:52 -07:00
}
}
internal bool BasePropertyIsGettable ( PSProperty property )
{
try
{
return this . PropertyIsGettable ( property );
}
catch ( ExtendedTypeSystemException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw NewException (
e ,
"CatchFromBasePropertyIsGettable" ,
"CatchFromBasePropertyIsGettableTI" ,
ExtendedTypeSystem . ExceptionRetrievingPropertyReadState ,
property . Name );
2016-03-30 14:20:52 -07:00
}
}
internal string BasePropertyType ( PSProperty property )
{
try
{
return this . PropertyType ( property , forDisplay : false );
}
catch ( ExtendedTypeSystemException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw NewException (
e ,
"CatchFromBasePropertyType" ,
"CatchFromBasePropertyTypeTI" ,
ExtendedTypeSystem . ExceptionRetrievingPropertyType ,
property . Name );
2016-03-30 14:20:52 -07:00
}
}
internal string BasePropertyToString ( PSProperty property )
{
try
{
return this . PropertyToString ( property );
}
catch ( ExtendedTypeSystemException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw NewException (
e ,
"CatchFromBasePropertyToString" ,
"CatchFromBasePropertyToStringTI" ,
ExtendedTypeSystem . ExceptionRetrievingPropertyString ,
property . Name );
2016-03-30 14:20:52 -07:00
}
}
internal AttributeCollection BasePropertyAttributes ( PSProperty property )
{
try
{
return this . PropertyAttributes ( property );
}
catch ( ExtendedTypeSystemException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw NewException (
e ,
"CatchFromBasePropertyAttributes" ,
"CatchFromBasePropertyAttributesTI" ,
ExtendedTypeSystem . ExceptionRetrievingPropertyAttributes ,
property . Name );
2016-03-30 14:20:52 -07:00
}
}
#endregion property
#region method
internal object BaseMethodInvoke ( PSMethod method , PSMethodInvocationConstraints invocationConstraints , params object [] arguments )
{
try
{
return this . MethodInvoke ( method , invocationConstraints , arguments );
}
catch ( TargetInvocationException ex )
{
Exception inner = ex . InnerException ?? ex ;
2019-03-23 09:52:30 +05:00
throw new MethodInvocationException (
"CatchFromBaseAdapterMethodInvokeTI" ,
2016-03-30 14:20:52 -07:00
inner ,
ExtendedTypeSystem . MethodInvocationException ,
2019-03-23 09:52:30 +05:00
method . Name ,
arguments . Length ,
inner . Message );
2016-03-30 14:20:52 -07:00
}
catch ( FlowControlException ) { throw ; }
catch ( ScriptCallDepthException ) { throw ; }
catch ( PipelineStoppedException ) { throw ; }
catch ( MethodException ) { throw ; }
catch ( Exception e )
{
if ( method . baseObject is SteppablePipeline
&& ( method . Name . Equals ( "Begin" , StringComparison . OrdinalIgnoreCase ) ||
method . Name . Equals ( "Process" , StringComparison . OrdinalIgnoreCase ) ||
method . Name . Equals ( "End" , StringComparison . OrdinalIgnoreCase )))
{
throw ;
}
2019-03-23 09:52:30 +05:00
throw new MethodInvocationException (
"CatchFromBaseAdapterMethodInvoke" ,
2016-03-30 14:20:52 -07:00
e ,
ExtendedTypeSystem . MethodInvocationException ,
2019-03-23 09:52:30 +05:00
method . Name ,
arguments . Length ,
e . Message );
2016-03-30 14:20:52 -07:00
}
}
2018-12-22 01:26:06 -08:00
internal Collection < string > BaseMethodDefinitions ( PSMethod method )
2016-03-30 14:20:52 -07:00
{
try
{
return this . MethodDefinitions ( method );
}
catch ( ExtendedTypeSystemException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw NewException (
e ,
"CatchFromBaseMethodDefinitions" ,
"CatchFromBaseMethodDefinitionsTI" ,
ExtendedTypeSystem . ExceptionRetrievingMethodDefinitions ,
method . Name );
2016-03-30 14:20:52 -07:00
}
}
internal string BaseMethodToString ( PSMethod method )
{
try
{
return this . MethodToString ( method );
}
catch ( ExtendedTypeSystemException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw NewException (
e ,
"CatchFromBaseMethodToString" ,
"CatchFromBaseMethodToStringTI" ,
ExtendedTypeSystem . ExceptionRetrievingMethodString ,
method . Name );
2016-03-30 14:20:52 -07:00
}
}
#endregion method
#region parameterized property
internal string BaseParameterizedPropertyType ( PSParameterizedProperty property )
{
try
{
return this . ParameterizedPropertyType ( property );
}
catch ( ExtendedTypeSystemException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw NewException (
e ,
"CatchFromBaseParameterizedPropertyType" ,
"CatchFromBaseParameterizedPropertyTypeTI" ,
ExtendedTypeSystem . ExceptionRetrievingParameterizedPropertytype ,
property . Name );
2016-03-30 14:20:52 -07:00
}
}
internal bool BaseParameterizedPropertyIsSettable ( PSParameterizedProperty property )
{
try
{
return this . ParameterizedPropertyIsSettable ( property );
}
catch ( ExtendedTypeSystemException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw NewException (
e ,
"CatchFromBaseParameterizedPropertyIsSettable" ,
"CatchFromBaseParameterizedPropertyIsSettableTI" ,
ExtendedTypeSystem . ExceptionRetrievingParameterizedPropertyWriteState ,
property . Name );
2016-03-30 14:20:52 -07:00
}
}
internal bool BaseParameterizedPropertyIsGettable ( PSParameterizedProperty property )
{
try
{
return this . ParameterizedPropertyIsGettable ( property );
}
catch ( ExtendedTypeSystemException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw NewException (
e ,
"CatchFromBaseParameterizedPropertyIsGettable" ,
"CatchFromBaseParameterizedPropertyIsGettableTI" ,
ExtendedTypeSystem . ExceptionRetrievingParameterizedPropertyReadState ,
property . Name );
2016-03-30 14:20:52 -07:00
}
}
2018-12-22 01:26:06 -08:00
internal Collection < string > BaseParameterizedPropertyDefinitions ( PSParameterizedProperty property )
2016-03-30 14:20:52 -07:00
{
try
{
return this . ParameterizedPropertyDefinitions ( property );
}
catch ( ExtendedTypeSystemException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw NewException (
e ,
"CatchFromBaseParameterizedPropertyDefinitions" ,
"CatchFromBaseParameterizedPropertyDefinitionsTI" ,
ExtendedTypeSystem . ExceptionRetrievingParameterizedPropertyDefinitions ,
property . Name );
2016-03-30 14:20:52 -07:00
}
}
internal object BaseParameterizedPropertyGet ( PSParameterizedProperty property , params object [] arguments )
{
try
{
return this . ParameterizedPropertyGet ( property , arguments );
}
catch ( TargetInvocationException ex )
{
2016-08-02 22:25:13 -07:00
Exception inner = ex . InnerException ?? ex ;
2019-03-23 09:52:30 +05:00
throw new GetValueInvocationException (
"CatchFromBaseAdapterParameterizedPropertyGetValueTI" ,
2016-03-30 14:20:52 -07:00
inner ,
ExtendedTypeSystem . ExceptionWhenGetting ,
2019-03-23 09:52:30 +05:00
property . Name ,
inner . Message );
2016-03-30 14:20:52 -07:00
}
catch ( GetValueException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw new GetValueInvocationException (
"CatchFromBaseParameterizedPropertyAdapterGetValue" ,
2016-03-30 14:20:52 -07:00
e ,
ExtendedTypeSystem . ExceptionWhenGetting ,
2019-03-23 09:52:30 +05:00
property . Name ,
e . Message );
2016-03-30 14:20:52 -07:00
}
}
internal void BaseParameterizedPropertySet ( PSParameterizedProperty property , object setValue , params object [] arguments )
{
try
{
this . ParameterizedPropertySet ( property , setValue , arguments );
}
catch ( TargetInvocationException ex )
{
Exception inner = ex . InnerException ?? ex ;
2019-03-23 09:52:30 +05:00
throw new SetValueInvocationException (
"CatchFromBaseAdapterParameterizedPropertySetValueTI" ,
2016-03-30 14:20:52 -07:00
inner ,
ExtendedTypeSystem . ExceptionWhenSetting ,
2019-03-23 09:52:30 +05:00
property . Name ,
inner . Message );
2016-03-30 14:20:52 -07:00
}
catch ( SetValueException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw new SetValueInvocationException (
"CatchFromBaseAdapterParameterizedPropertySetValue" ,
2016-03-30 14:20:52 -07:00
e ,
ExtendedTypeSystem . ExceptionWhenSetting ,
2019-03-23 09:52:30 +05:00
property . Name ,
e . Message );
2016-03-30 14:20:52 -07:00
}
}
internal string BaseParameterizedPropertyToString ( PSParameterizedProperty property )
{
try
{
return this . ParameterizedPropertyToString ( property );
}
catch ( ExtendedTypeSystemException ) { throw ; }
catch ( Exception e )
{
2019-03-23 09:52:30 +05:00
throw NewException (
e ,
"CatchFromBaseParameterizedPropertyToString" ,
"CatchFromBaseParameterizedPropertyToStringTI" ,
ExtendedTypeSystem . ExceptionRetrievingParameterizedPropertyString ,
property . Name );
2016-03-30 14:20:52 -07:00
}
}
#endregion parameterized property
#region Internal Helper Methods
2018-09-11 16:57:35 -07:00
private static Type GetArgumentType ( object argument , bool isByRefParameter )
2016-03-30 14:20:52 -07:00
{
2020-07-30 16:06:38 -07:00
if ( argument == null )
2016-03-30 14:20:52 -07:00
{
return typeof ( LanguagePrimitives . Null );
}
2018-09-11 16:57:35 -07:00
if ( isByRefParameter && argument is PSReference psref )
2016-03-30 14:20:52 -07:00
{
2018-09-11 16:57:35 -07:00
return GetArgumentType ( PSObject . Base ( psref . Value ), isByRefParameter : false );
2016-03-30 14:20:52 -07:00
}
2018-09-11 16:57:35 -07:00
2016-03-30 14:20:52 -07:00
return argument . GetType ();
}
2018-09-11 16:57:35 -07:00
internal static ConversionRank GetArgumentConversionRank ( object argument , Type parameterType , bool isByRef , bool allowCastingToByRefLikeType )
2016-03-30 14:20:52 -07:00
{
2018-09-11 16:57:35 -07:00
Type fromType = null ;
ConversionRank rank = ConversionRank . None ;
if ( allowCastingToByRefLikeType && parameterType . IsByRefLike )
{
// When resolving best method for use in binders, we can accept implicit/explicit casting conversions to
// a ByRef-like target type, because when generating IL from a call site with the binder, the IL includes
// the casting operation. However, we don't accept such conversions when it's for invoking the method via
// reflection, because reflection just doesn't support ByRef-like type.
fromType = GetArgumentType ( PSObject . Base ( argument ), isByRefParameter : false );
if ( fromType != typeof ( LanguagePrimitives . Null ))
{
LanguagePrimitives . FigureCastConversion ( fromType , parameterType , ref rank );
}
2018-12-23 22:20:06 -08:00
2018-09-11 16:57:35 -07:00
return rank ;
}
fromType = GetArgumentType ( argument , isByRef );
rank = LanguagePrimitives . GetConversionRank ( fromType , parameterType );
2016-03-30 14:20:52 -07:00
if ( rank == ConversionRank . None )
{
2018-09-11 16:57:35 -07:00
fromType = GetArgumentType ( PSObject . Base ( argument ), isByRef );
2016-03-30 14:20:52 -07:00
rank = LanguagePrimitives . GetConversionRank ( fromType , parameterType );
}
return rank ;
}
private static ParameterInformation [] ExpandParameters ( int argCount , ParameterInformation [] parameters , Type elementType )
{
Diagnostics . Assert ( parameters [ parameters . Length - 1 ]. isParamArray , "ExpandParameters shouldn't be called on non-param method" );
ParameterInformation [] newParameters = new ParameterInformation [ argCount ];
Array . Copy ( parameters , newParameters , parameters . Length - 1 );
for ( int i = parameters . Length - 1 ; i < argCount ; ++ i )
{
newParameters [ i ] = new ParameterInformation ( elementType , false , null , false );
}
return newParameters ;
}
/// <summary>
/// Compare the 2 methods, determining which method is better.
/// </summary>
/// <returns>1 if method1 is better, -1 if method2 is better, 0 otherwise.</returns>
private static int CompareOverloadCandidates ( OverloadCandidate candidate1 , OverloadCandidate candidate2 , object [] arguments )
{
Diagnostics . Assert ( candidate1 . conversionRanks . Length == candidate2 . conversionRanks . Length ,
"should have same number of conversions regardless of the number of parameters - default arguments are not included here" );
ParameterInformation [] params1 = candidate1 . expandedParameters ?? candidate1 . parameters ;
ParameterInformation [] params2 = candidate2 . expandedParameters ?? candidate2 . parameters ;
int betterCount = 0 ;
int multiplier = candidate1 . conversionRanks . Length ;
for ( int i = 0 ; i < candidate1 . conversionRanks . Length ; ++ i , -- multiplier )
{
if ( candidate1 . conversionRanks [ i ] < candidate2 . conversionRanks [ i ])
{
betterCount -= multiplier ;
}
else if ( candidate1 . conversionRanks [ i ] > candidate2 . conversionRanks [ i ])
{
betterCount += multiplier ;
}
else if ( candidate1 . conversionRanks [ i ] == ConversionRank . UnrelatedArrays )
{
// If both are unrelated arrays, then use the element type conversions instead.
Type argElemType = EffectiveArgumentType ( arguments [ i ]). GetElementType ();
ConversionRank rank1 = LanguagePrimitives . GetConversionRank ( argElemType , params1 [ i ]. parameterType . GetElementType ());
ConversionRank rank2 = LanguagePrimitives . GetConversionRank ( argElemType , params2 [ i ]. parameterType . GetElementType ());
if ( rank1 < rank2 )
{
betterCount -= multiplier ;
}
else if ( rank1 > rank2 )
{
betterCount += multiplier ;
}
}
}
if ( betterCount == 0 )
{
multiplier = candidate1 . conversionRanks . Length ;
for ( int i = 0 ; i < candidate1 . conversionRanks . Length ; ++ i , multiplier = Math . Abs ( multiplier ) - 1 )
{
// The following rather tricky logic tries to pick the best method in 2 very different cases -
// - Pick the most specific method when conversions aren't losing information
// - Pick the most general method when conversions will lose information.
// Consider:
// f(uint32), f(decimal), call with f([byte]$i)
// in this case, we want to call f(uint32) because it is more specific
// while not losing information
// f(byte), f(int16), call with f([int]$i)
// in this case, we want to call f(int16) because it is more general,
// we know we could lose information with either call, but we will lose
// less information calling f(int16).
ConversionRank rank1 = candidate1 . conversionRanks [ i ];
ConversionRank rank2 = candidate2 . conversionRanks [ i ];
if ( rank1 < ConversionRank . NullToValue || rank2 < ConversionRank . NullToValue )
{
// The tie breaking rules here do not apply to conversions that are not
// numeric or inheritance related.
continue ;
}
if (( rank1 >= ConversionRank . NumericImplicit ) != ( rank2 >= ConversionRank . NumericImplicit ))
{
// Skip trying to break ties when argument conversions are not both implicit or both
// explicit. If we have that situation, there are multiple arguments and an
// ambiguity is probably the best choice.
continue ;
}
// We will now compare the parameter types, ignoring the actual argument type. To choose
// the right method, we need to know if we want the "most specific" or the "most general".
// If we have implicit argument conversions, we'll want the most specific, so invert the multiplier.
if ( rank1 >= ConversionRank . NumericImplicit )
{
multiplier = - multiplier ;
}
// With a positive multiplier, we'll choose the "most general" type, and a negative
// multiplier will choose the "most specific".
rank1 = LanguagePrimitives . GetConversionRank ( params1 [ i ]. parameterType , params2 [ i ]. parameterType );
rank2 = LanguagePrimitives . GetConversionRank ( params2 [ i ]. parameterType , params1 [ i ]. parameterType );
if ( rank1 < rank2 )
{
betterCount += multiplier ;
}
else if ( rank1 > rank2 )
{
betterCount -= multiplier ;
}
}
}
if ( betterCount == 0 )
{
// Check if parameters are the same. If so, we have a few tiebreakering rules down below.
for ( int i = 0 ; i < candidate1 . conversionRanks . Length ; ++ i )
{
if ( params1 [ i ]. parameterType != params2 [ i ]. parameterType )
{
return 0 ;
}
}
// Apply tie breaking rules, related to expanded parameters
if ( candidate1 . expandedParameters != null && candidate2 . expandedParameters != null )
{
// Both are using expanded parameters. The one with more parameters is better
return ( candidate1 . parameters . Length > candidate2 . parameters . Length ) ? 1 : - 1 ;
}
else if ( candidate1 . expandedParameters != null )
{
return - 1 ;
}
else if ( candidate2 . expandedParameters != null )
{
return 1 ;
}
// Apply tie breaking rules, related to specificity of parameters
betterCount = CompareTypeSpecificity ( candidate1 , candidate2 );
}
// The methods with fewer parameter wins
2019-01-06 06:51:20 +05:00
// Need to revisit this if we support named arguments
2016-03-30 14:20:52 -07:00
if ( betterCount == 0 )
{
if ( candidate1 . parameters . Length < candidate2 . parameters . Length )
{
return 1 ;
}
2016-07-29 13:02:49 -07:00
else if ( candidate1 . parameters . Length > candidate2 . parameters . Length )
2016-03-30 14:20:52 -07:00
{
return - 1 ;
}
}
return betterCount ;
}
private static OverloadCandidate FindBestCandidate ( List < OverloadCandidate > candidates , object [] arguments )
{
Dbg . Assert ( candidates != null , "Caller should verify candidates != null" );
OverloadCandidate bestCandidateSoFar = null ;
bool multipleBestCandidates = false ;
for ( int i = 0 ; i < candidates . Count ; i ++)
{
OverloadCandidate currentCandidate = candidates [ i ];
2020-07-30 16:06:38 -07:00
if ( bestCandidateSoFar == null ) // first iteration
2016-03-30 14:20:52 -07:00
{
bestCandidateSoFar = currentCandidate ;
continue ;
}
int comparisonResult = CompareOverloadCandidates ( bestCandidateSoFar , currentCandidate , arguments );
if ( comparisonResult == 0 )
{
multipleBestCandidates = true ;
}
else if ( comparisonResult < 0 )
{
bestCandidateSoFar = currentCandidate ;
multipleBestCandidates = false ;
}
}
Dbg . Assert (
! candidates . Any ( otherCandidate => otherCandidate != bestCandidateSoFar && CompareOverloadCandidates ( otherCandidate , bestCandidateSoFar , arguments ) > 0 ),
"No other candidates are better than bestCandidateSoFar" );
return multipleBestCandidates ? null : bestCandidateSoFar ;
}
private static OverloadCandidate FindBestCandidate ( List < OverloadCandidate > candidates , object [] arguments , PSMethodInvocationConstraints invocationConstraints )
{
List < OverloadCandidate > filteredCandidates = candidates . Where ( candidate => IsInvocationConstraintSatisfied ( candidate , invocationConstraints )). ToList ();
if ( filteredCandidates . Count > 0 )
{
candidates = filteredCandidates ;
}
OverloadCandidate bestCandidate = FindBestCandidate ( candidates , arguments );
return bestCandidate ;
}
private static int CompareTypeSpecificity ( Type type1 , Type type2 )
{
if ( type1 . IsGenericParameter || type2 . IsGenericParameter )
{
int result = 0 ;
if ( type1 . IsGenericParameter )
{
result -= 1 ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
if ( type2 . IsGenericParameter )
{
result += 1 ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return result ;
}
if ( type1 . IsArray )
{
Dbg . Assert ( type2 . IsArray , "Caller should verify that both overload candidates have the same parameter types" );
Dbg . Assert ( type1 . GetArrayRank () == type2 . GetArrayRank (), "Caller should verify that both overload candidates have the same parameter types" );
return CompareTypeSpecificity ( type1 . GetElementType (), type2 . GetElementType ());
}
2018-04-13 15:01:41 -07:00
if ( type1 . IsGenericType )
2016-03-30 14:20:52 -07:00
{
2018-04-13 15:01:41 -07:00
Dbg . Assert ( type2 . IsGenericType , "Caller should verify that both overload candidates have the same parameter types" );
2016-03-30 14:20:52 -07:00
Dbg . Assert ( type1 . GetGenericTypeDefinition () == type2 . GetGenericTypeDefinition (), "Caller should verify that both overload candidates have the same parameter types" );
return CompareTypeSpecificity ( type1 . GetGenericArguments (), type2 . GetGenericArguments ());
}
return 0 ;
}
private static int CompareTypeSpecificity ( Type [] params1 , Type [] params2 )
{
Dbg . Assert ( params1 . Length == params2 . Length , "Caller should verify that both overload candidates have the same number of parameters" );
bool candidate1hasAtLeastOneMoreSpecificParameter = false ;
bool candidate2hasAtLeastOneMoreSpecificParameter = false ;
for ( int i = 0 ; i < params1 . Length ; ++ i )
{
int specificityComparison = CompareTypeSpecificity ( params1 [ i ], params2 [ i ]);
if ( specificityComparison > 0 )
{
candidate1hasAtLeastOneMoreSpecificParameter = true ;
}
else if ( specificityComparison < 0 )
{
candidate2hasAtLeastOneMoreSpecificParameter = true ;
}
if ( candidate1hasAtLeastOneMoreSpecificParameter && candidate2hasAtLeastOneMoreSpecificParameter )
{
break ;
}
}
if ( candidate1hasAtLeastOneMoreSpecificParameter && ! candidate2hasAtLeastOneMoreSpecificParameter )
{
return 1 ;
}
else if ( candidate2hasAtLeastOneMoreSpecificParameter && ! candidate1hasAtLeastOneMoreSpecificParameter )
{
return - 1 ;
}
else
{
return 0 ;
}
}
/// <summary>
/// Returns -1 if <paramref name="candidate1"/> is less specific than <paramref name="candidate2"/>
/// (1 otherwise, or 0 if both are equally specific or non-comparable)
/// </summary>
private static int CompareTypeSpecificity ( OverloadCandidate candidate1 , OverloadCandidate candidate2 )
{
if (!( candidate1 . method . isGeneric || candidate2 . method . isGeneric ))
{
return 0 ;
}
2021-04-13 00:44:05 +05:00
Type [] params1 = GetGenericMethodDefinitionIfPossible ( candidate1 . method . method ). GetParameters (). Select ( static p => p . ParameterType ). ToArray ();
Type [] params2 = GetGenericMethodDefinitionIfPossible ( candidate2 . method . method ). GetParameters (). Select ( static p => p . ParameterType ). ToArray ();
2016-03-30 14:20:52 -07:00
return CompareTypeSpecificity ( params1 , params2 );
}
private static MethodBase GetGenericMethodDefinitionIfPossible ( MethodBase method )
{
if ( method . IsGenericMethod && ! method . IsGenericMethodDefinition )
{
MethodInfo methodInfo = method as MethodInfo ;
if ( methodInfo != null )
{
return methodInfo . GetGenericMethodDefinition ();
}
}
return method ;
}
[DebuggerDisplay("OverloadCandidate: {method.methodDefinition}")]
2021-07-19 10:09:12 +01:00
private sealed class OverloadCandidate
2016-03-30 14:20:52 -07:00
{
internal MethodInformation method ;
internal ParameterInformation [] parameters ;
internal ParameterInformation [] expandedParameters ;
internal ConversionRank [] conversionRanks ;
internal OverloadCandidate ( MethodInformation method , int argCount )
{
this . method = method ;
this . parameters = method . parameters ;
conversionRanks = new ConversionRank [ argCount ];
}
}
private static bool IsInvocationTargetConstraintSatisfied ( MethodInformation method , PSMethodInvocationConstraints invocationConstraints )
{
Dbg . Assert ( method != null , "Caller should verify method != null" );
2020-07-30 16:06:38 -07:00
if ( method . method == null )
2016-03-30 14:20:52 -07:00
{
return true ; // do not apply methodTargetType constraint to non-.NET types (i.e. to COM or WMI types)
}
// An invocation constraint is only specified when there is an explicit cast on the target expression, so:
//
// [IFoo]$x.Bar()
//
// will have [IFoo] as the method target type, but
//
// $hash = @{}; $hash.Add(1,2)
//
// will have no method target type.
var methodDeclaringType = method . method . DeclaringType ;
2020-07-30 16:06:38 -07:00
if ( invocationConstraints == null || invocationConstraints . MethodTargetType == null )
2016-03-30 14:20:52 -07:00
{
// If no method target type is specified, we say the constraint is matched as long as the method is not an interface.
// This behavior matches V2 - our candidate sets never included methods with declaring type as an interface in V2.
2018-04-13 15:01:41 -07:00
return ! methodDeclaringType . IsInterface ;
2016-03-30 14:20:52 -07:00
}
var targetType = invocationConstraints . MethodTargetType ;
2018-04-13 15:01:41 -07:00
if ( targetType . IsInterface )
2016-03-30 14:20:52 -07:00
{
// If targetType is an interface, types must match exactly. This is how we can call method impls.
// We also allow the method declaring type to be in a base interface.
2018-04-13 15:01:41 -07:00
return methodDeclaringType == targetType || ( methodDeclaringType . IsInterface && targetType . IsSubclassOf ( methodDeclaringType ));
2016-03-30 14:20:52 -07:00
}
2018-04-13 15:01:41 -07:00
if ( methodDeclaringType . IsInterface )
2016-03-30 14:20:52 -07:00
{
// targetType is a class. We don't try comparing with targetType because we'll end up with
// an ambiguous set because what is effectively the same method may appear in our set multiple
// times (once with the declaring type as the interface, and once as the actual class type.)
return false ;
}
// Dual-purpose of ([type]<expression>).method() syntax makes this code a little bit tricky to understand.
2017-10-27 05:12:19 +08:00
// First purpose of this syntax is cast.
2016-03-30 14:20:52 -07:00
// Second is a non-virtual super-class method call.
//
// Consider this code:
//
// ```
// class B {
// [string]foo() {return 'B.foo'}
// [string]foo($a) {return 'B.foo'}
// }
2017-10-27 05:12:19 +08:00
//
2016-03-30 14:20:52 -07:00
// class Q : B {
// [string]$Name
// Q([string]$name) {$this.name = $name}
// }
2017-10-27 05:12:19 +08:00
//
2016-03-30 14:20:52 -07:00
// ([Q]'t').foo()
// ```
//
// Here we are using [Q] just for the cast and we are expecting foo() to be resolved to a super-class method.
// So methodDeclaringType is [B] and targetType is [Q]
//
// Now consider another code
2017-10-27 05:12:19 +08:00
//
2016-03-30 14:20:52 -07:00
// ```
// ([object]"abc").ToString()
// ```
//
// Here we are using [object] to specify that we want a super-class implementation of ToString(), so it should
// return "System.String"
// Here methodDeclaringType is [string] and targetType is [object]
//
// Notice: in one case targetType is a subclass of methodDeclaringType,
// in another case it's the reverse.
// Both of them are valid.
//
// Array is a special case.
return targetType . IsAssignableFrom ( methodDeclaringType )
|| methodDeclaringType . IsAssignableFrom ( targetType )
2018-04-13 15:01:41 -07:00
|| ( targetType . IsArray && methodDeclaringType == typeof ( Array ));
2016-03-30 14:20:52 -07:00
}
private static bool IsInvocationConstraintSatisfied ( OverloadCandidate overloadCandidate , PSMethodInvocationConstraints invocationConstraints )
{
Dbg . Assert ( overloadCandidate != null , "Caller should verify overloadCandidate != null" );
2020-07-30 16:06:38 -07:00
if ( invocationConstraints == null )
2016-03-30 14:20:52 -07:00
{
return true ;
}
if ( invocationConstraints . ParameterTypes != null )
{
int parameterIndex = 0 ;
foreach ( Type parameterTypeConstraint in invocationConstraints . ParameterTypes )
{
if ( parameterTypeConstraint != null )
{
if ( parameterIndex >= overloadCandidate . parameters . Length )
{
return false ;
}
Type parameterType = overloadCandidate . parameters [ parameterIndex ]. parameterType ;
if ( parameterType != parameterTypeConstraint )
{
return false ;
}
}
parameterIndex ++;
}
}
return true ;
}
/// <summary>
2016-08-28 05:44:55 +00:00
/// Return the best method out of overloaded methods.
2016-03-30 14:20:52 -07:00
/// The best has the smallest type distance between the method's parameters and the given arguments.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="methods">Different overloads for a method.</param>
/// <param name="invocationConstraints">Invocation constraints.</param>
/// <param name="allowCastingToByRefLikeType">True if we accept implicit/explicit casting conversion to a ByRef-like parameter type for method resolution.</param>
/// <param name="arguments">Arguments to check against the overloads.</param>
/// <param name="errorId">If no best method, the error id to use in the error message.</param>
/// <param name="errorMsg">If no best method, the error message (format string) to use in the error message.</param>
/// <param name="expandParamsOnBest">True if the best method's last parameter is a params method.</param>
/// <param name="callNonVirtually">True if best method should be called as non-virtual.</param>
2016-03-30 14:20:52 -07:00
internal static MethodInformation FindBestMethod (
MethodInformation [] methods ,
PSMethodInvocationConstraints invocationConstraints ,
2018-09-11 16:57:35 -07:00
bool allowCastingToByRefLikeType ,
2016-03-30 14:20:52 -07:00
object [] arguments ,
ref string errorId ,
ref string errorMsg ,
out bool expandParamsOnBest ,
out bool callNonVirtually )
{
callNonVirtually = false ;
2018-09-11 16:57:35 -07:00
var methodInfo = FindBestMethodImpl ( methods , invocationConstraints , allowCastingToByRefLikeType , arguments , ref errorId , ref errorMsg , out expandParamsOnBest );
2020-07-30 16:06:38 -07:00
if ( methodInfo == null )
2016-03-30 14:20:52 -07:00
{
return null ;
}
// For PS classes we need to support base method call syntax:
//
// class BaseClass
// {
// [int] foo() { return 1}
// }
// class DerivedClass : BaseClass
// {
// [int] foo() { return 2 * ([BaseClass]$this).foo() }
// }
//
// If we have such information in invocationConstraints then we should call method on the baseClass.
2016-07-29 13:02:49 -07:00
if ( invocationConstraints != null &&
invocationConstraints . MethodTargetType != null &&
methodInfo . method != null &&
2016-03-30 14:20:52 -07:00
methodInfo . method . DeclaringType != null )
{
Type targetType = methodInfo . method . DeclaringType ;
if ( targetType != invocationConstraints . MethodTargetType && targetType . IsSubclassOf ( invocationConstraints . MethodTargetType ))
{
2021-04-13 00:44:05 +05:00
var parameterTypes = methodInfo . method . GetParameters (). Select ( static parameter => parameter . ParameterType ). ToArray ();
2016-03-30 14:20:52 -07:00
var targetTypeMethod = invocationConstraints . MethodTargetType . GetMethod ( methodInfo . method . Name , BindingFlags . IgnoreCase | BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Instance , null , parameterTypes , null );
2018-08-29 08:24:06 +03:00
if ( targetTypeMethod != null && ( targetTypeMethod . IsPublic || targetTypeMethod . IsFamily || targetTypeMethod . IsFamilyOrAssembly ))
2016-03-30 14:20:52 -07:00
{
methodInfo = new MethodInformation ( targetTypeMethod , 0 );
callNonVirtually = true ;
}
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return methodInfo ;
}
2016-07-29 13:02:49 -07:00
2022-02-03 19:58:02 +00:00
private static Type [] ResolveGenericTypeParameters ( object [] genericTypeParameters )
{
if ( genericTypeParameters is null || genericTypeParameters . Length == 0 )
{
return null ;
}
Type [] genericParamTypes = new Type [ genericTypeParameters . Length ];
for ( int i = 0 ; i < genericTypeParameters . Length ; i ++)
{
genericParamTypes [ i ] = genericTypeParameters [ i ] switch
{
Type paramType => paramType ,
ITypeName paramTypeName => TypeOps . ResolveTypeName ( paramTypeName , paramTypeName . Extent ),
_ => throw new ArgumentException ( "Unexpected value" ),
};
}
return genericParamTypes ;
}
2016-03-30 14:20:52 -07:00
private static MethodInformation FindBestMethodImpl (
MethodInformation [] methods ,
PSMethodInvocationConstraints invocationConstraints ,
2018-09-11 16:57:35 -07:00
bool allowCastingToByRefLikeType ,
2016-03-30 14:20:52 -07:00
object [] arguments ,
ref string errorId ,
ref string errorMsg ,
out bool expandParamsOnBest )
{
expandParamsOnBest = false ;
// Small optimization so we don't calculate type distances when there is only one method
// We skip the optimization, if the method hasVarArgs, since in the case where arguments
// and parameters are of the same size, we want to know if the last argument should
// be turned into an array.
// We also skip the optimization if the number of arguments and parameters is different
// so we let the loop deal with possible optional parameters.
2022-01-27 12:55:54 -05:00
if ( methods . Length == 1
&& ! methods [ 0 ]. hasVarArgs
2016-03-30 14:20:52 -07:00
// generic methods need to be double checked in a loop below - generic methods can be rejected if type inference fails
2022-01-27 12:55:54 -05:00
&& ! methods [ 0 ]. isGeneric
&& ( methods [ 0 ]. method is null || ! methods [ 0 ]. method . DeclaringType . IsGenericTypeDefinition )
&& methods [ 0 ]. parameters . Length == arguments . Length )
2016-03-30 14:20:52 -07:00
{
return methods [ 0 ];
}
2022-02-03 19:58:02 +00:00
Type [] genericParamTypes = ResolveGenericTypeParameters ( invocationConstraints ?. GenericTypeParameters );
var candidates = new List < OverloadCandidate >();
2016-03-30 14:20:52 -07:00
for ( int i = 0 ; i < methods . Length ; i ++)
{
2022-01-27 12:55:54 -05:00
MethodInformation methodInfo = methods [ i ];
2016-03-30 14:20:52 -07:00
2022-01-27 12:55:54 -05:00
if ( methodInfo . method ?. DeclaringType . IsGenericTypeDefinition == true
2022-02-03 19:58:02 +00:00
|| (! methodInfo . isGeneric && genericParamTypes is not null ))
2016-03-30 14:20:52 -07:00
{
2022-01-27 12:55:54 -05:00
// If method is defined by an *open* generic type, or
// if generic parameters were provided and this method isn't generic, skip it.
continue ;
2016-03-30 14:20:52 -07:00
}
2022-01-27 12:55:54 -05:00
if ( methodInfo . isGeneric )
2016-03-30 14:20:52 -07:00
{
2022-02-03 19:58:02 +00:00
if ( genericParamTypes is not null )
2022-01-27 12:55:54 -05:00
{
try
{
2022-02-03 19:58:02 +00:00
// This cast is safe, because
// 1. Only ConstructorInfo and MethodInfo derive from MethodBase
// 2. ConstructorInfo.IsGenericMethod is always false
var originalMethod = ( MethodInfo ) methodInfo . method ;
2022-01-27 12:55:54 -05:00
methodInfo = new MethodInformation (
2022-02-03 19:58:02 +00:00
originalMethod . MakeGenericMethod ( genericParamTypes ),
2022-01-27 12:55:54 -05:00
parametersToIgnore : 0 );
}
catch ( ArgumentException )
{
// Just skip this possibility if the generic type parameters can't be used to make
// a valid generic method here.
continue ;
}
}
2022-02-03 19:58:02 +00:00
else
2016-03-30 14:20:52 -07:00
{
2022-02-03 19:58:02 +00:00
// Infer the generic method when generic parameter types are not specified.
Type [] argumentTypes = arguments . Select ( EffectiveArgumentType ). ToArray ();
Type [] paramConstraintTypes = invocationConstraints ?. ParameterTypes ;
if ( paramConstraintTypes is not null )
{
for ( int k = 0 ; k < paramConstraintTypes . Length ; k ++)
{
if ( paramConstraintTypes [ k ] is not null )
{
argumentTypes [ k ] = paramConstraintTypes [ k ];
}
}
}
methodInfo = TypeInference . Infer ( methodInfo , argumentTypes );
if ( methodInfo is null )
{
// Skip generic methods for which we cannot infer type arguments
continue ;
}
2016-03-30 14:20:52 -07:00
}
}
2022-01-27 12:55:54 -05:00
if (! IsInvocationTargetConstraintSatisfied ( methodInfo , invocationConstraints ))
2016-03-30 14:20:52 -07:00
{
continue ;
}
2022-01-27 12:55:54 -05:00
ParameterInformation [] parameters = methodInfo . parameters ;
2016-03-30 14:20:52 -07:00
if ( arguments . Length != parameters . Length )
{
// Skip methods w/ an incorrect # of arguments.
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
if ( arguments . Length > parameters . Length )
{
// If too many args,it's only OK if the method is varargs.
2022-01-27 12:55:54 -05:00
if (! methodInfo . hasVarArgs )
2016-03-30 14:20:52 -07:00
{
continue ;
}
}
else
{
// Too few args, OK if there are optionals, or varargs with the param array omitted
2022-01-27 12:55:54 -05:00
if (! methodInfo . hasOptional && (! methodInfo . hasVarArgs || ( arguments . Length + 1 ) != parameters . Length ))
2016-03-30 14:20:52 -07:00
{
continue ;
}
2022-01-27 12:55:54 -05:00
if ( methodInfo . hasOptional )
2016-03-30 14:20:52 -07:00
{
// Count optionals. This code is rarely hit, mainly when calling code in the
// assembly Microsoft.VisualBasic. If it were more frequent, the optional count
// should be stored in MethodInformation.
int optionals = 0 ;
for ( int j = 0 ; j < parameters . Length ; j ++)
{
2016-07-29 13:02:49 -07:00
if ( parameters [ j ]. isOptional )
{
2016-03-30 14:20:52 -07:00
optionals += 1 ;
}
}
if ( arguments . Length + optionals < parameters . Length )
{
// Even with optionals, there are too few.
continue ;
}
}
}
}
2022-01-27 12:55:54 -05:00
OverloadCandidate candidate = new OverloadCandidate ( methodInfo , arguments . Length );
2016-03-30 14:20:52 -07:00
for ( int j = 0 ; candidate != null && j < parameters . Length ; j ++)
{
ParameterInformation parameter = parameters [ j ];
if ( parameter . isOptional && arguments . Length <= j )
{
break ; // All the other parameters are optional and it is ok not to have more arguments
}
else if ( parameter . isParamArray )
{
Type elementType = parameter . parameterType . GetElementType ();
if ( parameters . Length == arguments . Length )
{
2018-09-11 16:57:35 -07:00
ConversionRank arrayConv = GetArgumentConversionRank (
arguments [ j ],
parameter . parameterType ,
isByRef : false ,
allowCastingToByRefLikeType : false );
ConversionRank elemConv = GetArgumentConversionRank (
arguments [ j ],
elementType ,
isByRef : false ,
allowCastingToByRefLikeType : false );
2016-03-30 14:20:52 -07:00
if ( elemConv > arrayConv )
{
candidate . expandedParameters = ExpandParameters ( arguments . Length , parameters , elementType );
candidate . conversionRanks [ j ] = elemConv ;
}
else
{
candidate . conversionRanks [ j ] = arrayConv ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
if ( candidate . conversionRanks [ j ] == ConversionRank . None )
{
candidate = null ;
2016-07-29 13:02:49 -07:00
}
2016-03-30 14:20:52 -07:00
}
else
{
// All remaining arguments will be added to one array to be passed as this params
// argument.
// Note that we go through here when the param array parameter has no argument.
for ( int k = j ; k < arguments . Length ; k ++)
{
2018-09-11 16:57:35 -07:00
candidate . conversionRanks [ k ] = GetArgumentConversionRank (
arguments [ k ],
elementType ,
isByRef : false ,
allowCastingToByRefLikeType : false );
2016-03-30 14:20:52 -07:00
if ( candidate . conversionRanks [ k ] == ConversionRank . None )
{
// No longer a candidate
candidate = null ;
break ;
}
}
2018-06-26 02:37:09 +05:00
if ( candidate != null )
2016-03-30 14:20:52 -07:00
{
candidate . expandedParameters = ExpandParameters ( arguments . Length , parameters , elementType );
}
}
}
else
{
2018-09-11 16:57:35 -07:00
candidate . conversionRanks [ j ] = GetArgumentConversionRank (
arguments [ j ],
parameter . parameterType ,
parameter . isByRef ,
allowCastingToByRefLikeType );
2016-03-30 14:20:52 -07:00
if ( candidate . conversionRanks [ j ] == ConversionRank . None )
{
// No longer a candidate
candidate = null ;
}
}
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
if ( candidate != null )
{
candidates . Add ( candidate );
}
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
if ( candidates . Count == 0 )
{
2022-02-03 19:58:02 +00:00
if ( methods . Length > 0 && methods . All ( static m => m . method != null && m . method . DeclaringType . IsGenericTypeDefinition && m . method . IsStatic ))
2016-03-30 14:20:52 -07:00
{
errorId = "CannotInvokeStaticMethodOnUninstantiatedGenericType" ;
errorMsg = string . Format (
CultureInfo . InvariantCulture ,
ExtendedTypeSystem . CannotInvokeStaticMethodOnUninstantiatedGenericType ,
methods [ 0 ]. method . DeclaringType . FullName );
return null ;
}
2022-02-03 19:58:02 +00:00
else if ( genericParamTypes is not null )
2022-01-27 12:55:54 -05:00
{
errorId = "MethodCountCouldNotFindBestGeneric" ;
errorMsg = string . Format (
ExtendedTypeSystem . MethodGenericArgumentCountException ,
methods [ 0 ]. method . Name ,
2022-02-03 19:58:02 +00:00
genericParamTypes . Length ,
2022-01-27 12:55:54 -05:00
arguments . Length );
return null ;
}
2016-03-30 14:20:52 -07:00
else
{
errorId = "MethodCountCouldNotFindBest" ;
errorMsg = ExtendedTypeSystem . MethodArgumentCountException ;
return null ;
}
}
2016-07-29 13:02:49 -07:00
OverloadCandidate bestCandidate = candidates . Count == 1
2016-03-30 14:20:52 -07:00
? candidates [ 0 ]
: FindBestCandidate ( candidates , arguments , invocationConstraints );
if ( bestCandidate != null )
{
expandParamsOnBest = bestCandidate . expandedParameters != null ;
return bestCandidate . method ;
}
errorId = "MethodCountCouldNotFindBest" ;
errorMsg = ExtendedTypeSystem . MethodAmbiguousException ;
return null ;
}
internal static Type EffectiveArgumentType ( object arg )
{
if ( arg != null )
{
arg = PSObject . Base ( arg );
object [] argAsArray = arg as object [];
if ( argAsArray != null && argAsArray . Length > 0 && PSObject . Base ( argAsArray [ 0 ]) != null )
{
Type firstType = PSObject . Base ( argAsArray [ 0 ]). GetType ();
bool allSameType = true ;
for ( int j = 1 ; j < argAsArray . Length ; ++ j )
{
2020-07-30 16:06:38 -07:00
if ( argAsArray [ j ] == null || firstType != PSObject . Base ( argAsArray [ j ]). GetType ())
2016-03-30 14:20:52 -07:00
{
allSameType = false ;
break ;
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
if ( allSameType )
{
return firstType . MakeArrayType ();
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return arg . GetType ();
}
else
{
return typeof ( LanguagePrimitives . Null );
}
}
internal static void SetReferences ( object [] arguments , MethodInformation methodInformation , object [] originalArguments )
{
2019-03-21 02:43:52 +01:00
using ( PSObject . MemberResolution . TraceScope ( "Checking for possible references." ))
2016-03-30 14:20:52 -07:00
{
ParameterInformation [] parameters = methodInformation . parameters ;
for ( int i = 0 ; ( i < originalArguments . Length ) && ( i < parameters . Length ) && ( i < arguments . Length ); i ++)
{
object originalArgument = originalArguments [ i ];
PSReference originalArgumentReference = originalArgument as PSReference ;
// It still might be an PSObject wrapping an PSReference
2020-07-30 16:06:38 -07:00
if ( originalArgumentReference == null )
2016-03-30 14:20:52 -07:00
{
2020-11-05 16:58:48 +00:00
if (!( originalArgument is PSObject originalArgumentObj ))
2016-03-30 14:20:52 -07:00
{
continue ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
originalArgumentReference = originalArgumentObj . BaseObject as PSReference ;
2020-07-30 16:06:38 -07:00
if ( originalArgumentReference == null )
2016-03-30 14:20:52 -07:00
{
continue ;
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
ParameterInformation parameter = parameters [ i ];
if (! parameter . isByRef )
{
continue ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
object argument = arguments [ i ];
2019-03-21 02:43:52 +01:00
PSObject . MemberResolution . WriteLine ( "Argument '{0}' was a reference so it will be set to \"{1}\"." , i + 1 , argument );
2016-03-30 14:20:52 -07:00
originalArgumentReference . Value = argument ;
}
}
}
internal static MethodInformation GetBestMethodAndArguments (
2016-07-29 13:02:49 -07:00
string methodName ,
2016-03-30 14:20:52 -07:00
MethodInformation [] methods ,
object [] arguments ,
out object [] newArguments )
{
return GetBestMethodAndArguments ( methodName , methods , null , arguments , out newArguments );
}
internal static MethodInformation GetBestMethodAndArguments (
2016-07-29 13:02:49 -07:00
string methodName ,
2016-03-30 14:20:52 -07:00
MethodInformation [] methods ,
PSMethodInvocationConstraints invocationConstraints ,
object [] arguments ,
out object [] newArguments )
{
bool expandParamsOnBest ;
bool callNonVirtually ;
string errorId = null ;
string errorMsg = null ;
2018-09-11 16:57:35 -07:00
MethodInformation bestMethod = FindBestMethod (
methods ,
invocationConstraints ,
allowCastingToByRefLikeType : false ,
arguments ,
ref errorId ,
ref errorMsg ,
out expandParamsOnBest ,
out callNonVirtually );
2020-07-30 16:06:38 -07:00
if ( bestMethod == null )
2016-03-30 14:20:52 -07:00
{
throw new MethodException ( errorId , null , errorMsg , methodName , arguments . Length );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
newArguments = GetMethodArgumentsBase ( methodName , bestMethod . parameters , arguments , expandParamsOnBest );
return bestMethod ;
}
/// <summary>
/// Called in GetBestMethodAndArguments after a call to FindBestMethod to perform the
/// type conversion, copying(varArg) and optional value setting of the final arguments.
2017-10-27 05:12:19 +08:00
/// </summary>
2016-07-29 13:02:49 -07:00
internal static object [] GetMethodArgumentsBase ( string methodName ,
ParameterInformation [] parameters , object [] arguments ,
2016-03-30 14:20:52 -07:00
bool expandParamsOnBest )
{
int parametersLength = parameters . Length ;
if ( parametersLength == 0 )
{
2019-03-10 06:53:31 +05:00
return Array . Empty < object >();
2016-03-30 14:20:52 -07:00
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
object [] retValue = new object [ parametersLength ];
for ( int i = 0 ; i < parametersLength - 1 ; i ++)
{
ParameterInformation parameter = parameters [ i ];
SetNewArgument ( methodName , arguments , retValue , parameter , i );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
ParameterInformation lastParameter = parameters [ parametersLength - 1 ];
if (! expandParamsOnBest )
{
SetNewArgument ( methodName , arguments , retValue , lastParameter , parametersLength - 1 );
return retValue ;
}
// From this point on, we are dealing with VarArgs (Params)
// If we have no arguments left, we use an appropriate empty array for the last parameter
if ( arguments . Length < parametersLength )
{
retValue [ parametersLength - 1 ] = Array . CreateInstance ( lastParameter . parameterType . GetElementType (), new int [] { 0 });
return retValue ;
}
// We are going to put all the remaining arguments into an array
// and convert them to the propper type, if necessary to be the
// one argument for this last parameter
int remainingArgumentCount = arguments . Length - parametersLength + 1 ;
2020-07-30 16:06:38 -07:00
if ( remainingArgumentCount == 1 && arguments [ arguments . Length - 1 ] == null )
2016-03-30 14:20:52 -07:00
{
// Don't turn a single null argument into an array of 1 element, just pass null.
retValue [ parametersLength - 1 ] = null ;
}
else
{
object [] remainingArguments = new object [ remainingArgumentCount ];
Type paramsElementType = lastParameter . parameterType . GetElementType ();
for ( int j = 0 ; j < remainingArgumentCount ; j ++)
{
int argumentIndex = j + parametersLength - 1 ;
try
{
remainingArguments [ j ] = MethodArgumentConvertTo ( arguments [ argumentIndex ], false , argumentIndex ,
paramsElementType , CultureInfo . InvariantCulture );
}
catch ( InvalidCastException e )
{
// NTRAID#Windows Out Of Band Releases-924162-2005/11/17-JonN
throw new MethodException (
"MethodArgumentConversionInvalidCastArgument" ,
e ,
ExtendedTypeSystem . MethodArgumentConversionException ,
argumentIndex , arguments [ argumentIndex ], methodName , paramsElementType , e . Message );
}
}
try
{
retValue [ parametersLength - 1 ] = MethodArgumentConvertTo ( remainingArguments ,
lastParameter . isByRef , parametersLength - 1 , lastParameter . parameterType ,
CultureInfo . InvariantCulture );
}
catch ( InvalidCastException e )
{
// NTRAID#Windows Out Of Band Releases-924162-2005/11/17-JonN
throw new MethodException (
"MethodArgumentConversionParamsConversion" ,
e ,
ExtendedTypeSystem . MethodArgumentConversionException ,
parametersLength - 1 , remainingArguments , methodName , lastParameter . parameterType , e . Message );
}
}
return retValue ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Auxiliary method in MethodInvoke to set newArguments[index] with the propper value.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="methodName">Used for the MethodException that might be thrown.</param>
/// <param name="arguments">The complete array of arguments.</param>
/// <param name="newArguments">The complete array of new arguments.</param>
/// <param name="parameter">The parameter to use.</param>
/// <param name="index">The index in newArguments to set.</param>
2016-07-29 13:02:49 -07:00
internal static void SetNewArgument ( string methodName , object [] arguments ,
2016-03-30 14:20:52 -07:00
object [] newArguments , ParameterInformation parameter , int index )
{
if ( arguments . Length > index )
{
try
{
2016-07-29 13:02:49 -07:00
newArguments [ index ] = MethodArgumentConvertTo ( arguments [ index ], parameter . isByRef , index ,
2016-03-30 14:20:52 -07:00
parameter . parameterType , CultureInfo . InvariantCulture );
}
catch ( InvalidCastException e )
{
// NTRAID#Windows Out Of Band Releases-924162-2005/11/17-JonN
throw new MethodException (
"MethodArgumentConversionInvalidCastArgument" ,
e ,
ExtendedTypeSystem . MethodArgumentConversionException ,
index , arguments [ index ], methodName , parameter . parameterType , e . Message );
}
}
else
{
Diagnostics . Assert ( parameter . isOptional , "FindBestMethod would not return this method if there is no corresponding argument for a non optional parameter" );
newArguments [ index ] = parameter . defaultValue ;
}
}
2016-07-29 13:02:49 -07:00
internal static object MethodArgumentConvertTo ( object valueToConvert ,
2016-03-30 14:20:52 -07:00
bool isParameterByRef , int parameterIndex , Type resultType ,
IFormatProvider formatProvider )
{
2019-03-21 02:43:52 +01:00
using ( PSObject . MemberResolution . TraceScope ( "Method argument conversion." ))
2016-03-30 14:20:52 -07:00
{
2020-07-30 16:06:38 -07:00
if ( resultType == null )
2016-03-30 14:20:52 -07:00
{
2020-05-19 19:57:52 +01:00
throw PSTraceSource . NewArgumentNullException ( nameof ( resultType ));
2016-03-30 14:20:52 -07:00
}
bool isArgumentByRef ;
valueToConvert = UnReference ( valueToConvert , out isArgumentByRef );
if ( isParameterByRef && ! isArgumentByRef )
{
throw new MethodException ( "NonRefArgumentToRefParameterMsg" , null ,
ExtendedTypeSystem . NonRefArgumentToRefParameter , parameterIndex + 1 , typeof ( PSReference ). FullName , "[ref]" );
}
if ( isArgumentByRef && ! isParameterByRef )
{
throw new MethodException ( "RefArgumentToNonRefParameterMsg" , null ,
ExtendedTypeSystem . RefArgumentToNonRefParameter , parameterIndex + 1 , typeof ( PSReference ). FullName , "[ref]" );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return PropertySetAndMethodArgumentConvertTo ( valueToConvert , resultType , formatProvider );
}
}
internal static object UnReference ( object obj , out bool isArgumentByRef )
{
isArgumentByRef = false ;
PSReference reference = obj as PSReference ;
if ( reference != null )
{
2019-03-21 02:43:52 +01:00
PSObject . MemberResolution . WriteLine ( "Parameter was a reference." );
2016-03-30 14:20:52 -07:00
isArgumentByRef = true ;
return reference . Value ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
PSObject mshObj = obj as PSObject ;
if ( mshObj != null )
{
reference = mshObj . BaseObject as PSReference ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
if ( reference != null )
{
2019-03-21 02:43:52 +01:00
PSObject . MemberResolution . WriteLine ( "Parameter was an PSObject containing a reference." );
2016-03-30 14:20:52 -07:00
isArgumentByRef = true ;
return reference . Value ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return obj ;
}
2016-07-29 13:02:49 -07:00
internal static object PropertySetAndMethodArgumentConvertTo ( object valueToConvert ,
2016-03-30 14:20:52 -07:00
Type resultType , IFormatProvider formatProvider )
{
2019-03-21 02:43:52 +01:00
using ( PSObject . MemberResolution . TraceScope ( "Converting parameter \"{0}\" to \"{1}\"." , valueToConvert , resultType ))
2016-03-30 14:20:52 -07:00
{
2020-07-30 16:06:38 -07:00
if ( resultType == null )
2016-03-30 14:20:52 -07:00
{
2020-05-19 19:57:52 +01:00
throw PSTraceSource . NewArgumentNullException ( nameof ( resultType ));
2016-03-30 14:20:52 -07:00
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
PSObject mshObj = valueToConvert as PSObject ;
if ( mshObj != null )
{
if ( resultType == typeof ( object ))
{
2019-03-21 02:43:52 +01:00
PSObject . MemberResolution . WriteLine ( "Parameter was an PSObject and will be converted to System.Object." );
2017-10-27 05:12:19 +08:00
// we use PSObject.Base so we don't return
2016-03-30 14:20:52 -07:00
// PSCustomObject
return PSObject . Base ( mshObj );
}
}
return LanguagePrimitives . ConvertTo ( valueToConvert , resultType , formatProvider );
}
}
internal static void DoBoxingIfNecessary ( ILGenerator generator , Type type )
{
if ( type . IsByRef )
{
// We can't use a byref like we would use System.Object (the CLR will
// crash if we attempt to do so.) There isn't much anyone could do
// with a byref in PowerShell anyway, so just load the object and
// return that instead.
type = type . GetElementType ();
2018-04-13 15:01:41 -07:00
if ( type . IsPrimitive )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
if ( type == typeof ( byte )) { generator . Emit ( OpCodes . Ldind_U1 ); }
2016-03-30 14:20:52 -07:00
else if ( type == typeof ( ushort )) { generator . Emit ( OpCodes . Ldind_U2 ); }
2016-07-29 13:02:49 -07:00
else if ( type == typeof ( uint )) { generator . Emit ( OpCodes . Ldind_U4 ); }
else if ( type == typeof ( sbyte )) { generator . Emit ( OpCodes . Ldind_I8 ); }
else if ( type == typeof ( short )) { generator . Emit ( OpCodes . Ldind_I2 ); }
else if ( type == typeof ( int )) { generator . Emit ( OpCodes . Ldind_I4 ); }
else if ( type == typeof ( long )) { generator . Emit ( OpCodes . Ldind_I8 ); }
else if ( type == typeof ( float )) { generator . Emit ( OpCodes . Ldind_R4 ); }
2016-03-30 14:20:52 -07:00
else if ( type == typeof ( double )) { generator . Emit ( OpCodes . Ldind_R8 ); }
}
2018-04-13 15:01:41 -07:00
else if ( type . IsValueType )
2016-03-30 14:20:52 -07:00
{
generator . Emit ( OpCodes . Ldobj , type );
}
else
{
generator . Emit ( OpCodes . Ldind_Ref );
}
}
else if ( type . IsPointer )
{
// Pointers are similar to a byref. Here we mimic what C# would do
// when assigning a pointer to an object. This might not be useful
// to PowerShell script, but if we did nothing, the CLR would crash
// our process.
MethodInfo boxMethod = typeof ( Pointer ). GetMethod ( "Box" );
MethodInfo typeFromHandle = typeof ( Type ). GetMethod ( "GetTypeFromHandle" );
generator . Emit ( OpCodes . Ldtoken , type );
generator . Emit ( OpCodes . Call , typeFromHandle );
generator . Emit ( OpCodes . Call , boxMethod );
}
2018-04-13 15:01:41 -07:00
if ( type . IsValueType )
2016-03-30 14:20:52 -07:00
{
generator . Emit ( OpCodes . Box , type );
}
}
#endregion
#endregion base
}
2019-12-11 15:37:18 -08:00
/// <summary>
/// The abstract cache entry type.
/// All specific cache entry types should derive from it.
/// </summary>
internal abstract class CacheEntry
{
/// <summary>
/// Gets the boolean value to indicate if the member is hidden.
/// </summary>
/// <remarks>
/// Currently, we only check the 'HiddenAttribute' declared for properties and methods,
/// because it can be done for them through the 'hidden' keyword in PowerShell Class.
///
/// We can't currently write a parameterized property in a PowerShell class so it's not too important
/// to check for the 'HiddenAttribute' for parameterized properties. But if someone added the attribute
/// to their C#, it'd be good to set this property correctly.
/// </remarks>
internal virtual bool IsHidden => false ;
}
2016-03-30 14:20:52 -07:00
/// <summary>
2019-01-10 02:07:41 +05:00
/// Ordered and case insensitive hashtable.
2016-03-30 14:20:52 -07:00
/// </summary>
internal class CacheTable
{
2019-12-11 15:37:18 -08:00
/// <summary>
/// An object collection is used to help make populating method cache table more efficient
/// <see cref="DotNetAdapter.PopulateMethodReflectionTable(Type, CacheTable, BindingFlags)"/>.
/// </summary>
2016-03-30 14:20:52 -07:00
internal Collection < object > memberCollection ;
2020-11-05 03:36:23 +00:00
private readonly Dictionary < string , int > _indexes ;
2019-12-11 15:37:18 -08:00
2016-03-30 14:20:52 -07:00
internal CacheTable ()
{
memberCollection = new Collection < object >();
2016-07-29 13:02:49 -07:00
_indexes = new Dictionary < string , int >( StringComparer . OrdinalIgnoreCase );
2016-03-30 14:20:52 -07:00
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
internal void Add ( string name , object member )
{
2016-07-29 13:02:49 -07:00
_indexes [ name ] = memberCollection . Count ;
2016-03-30 14:20:52 -07:00
memberCollection . Add ( member );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
internal object this [ string name ]
{
get
{
int indexObj ;
2016-07-29 13:02:49 -07:00
if (! _indexes . TryGetValue ( name , out indexObj ))
2016-03-30 14:20:52 -07:00
{
return null ;
}
2018-12-23 22:20:06 -08:00
2019-12-11 15:37:18 -08:00
return memberCollection [ indexObj ];
2016-03-30 14:20:52 -07:00
}
}
2019-03-21 02:43:52 +01:00
2019-12-11 15:37:18 -08:00
/// <summary>
/// Get the first non-hidden member that satisfies the predicate.
/// </summary>
/// <remarks>
/// Hidden members are not returned for any fuzzy searches (searching by 'match' or enumerating a collection).
/// A hidden member is returned only if the member name is explicitly looked for.
/// </remarks>
2019-03-21 02:43:52 +01:00
internal object GetFirstOrDefault ( MemberNamePredicate predicate )
{
foreach ( var entry in _indexes )
{
if ( predicate ( entry . Key ))
{
2019-12-11 15:37:18 -08:00
object member = memberCollection [ entry . Value ];
if ( member is CacheEntry cacheEntry && cacheEntry . IsHidden )
{
continue ;
}
return member ;
2019-03-21 02:43:52 +01:00
}
}
return null ;
}
2016-03-30 14:20:52 -07:00
}
/// <summary>
/// Stores method related information.
/// This structure should be used whenever a new type is adapted.
/// For example, ManagementObjectAdapter uses this structure to store
/// WMI method information.
/// </summary>
[DebuggerDisplay("MethodInformation: {methodDefinition}")]
internal class MethodInformation
{
internal MethodBase method ;
private string _cachedMethodDefinition ;
2020-05-29 05:56:33 +01:00
2016-03-30 14:20:52 -07:00
internal string methodDefinition
{
get
{
2020-07-30 16:06:38 -07:00
if ( _cachedMethodDefinition == null )
2016-03-30 14:20:52 -07:00
{
var name = method is ConstructorInfo ? "new" : method . Name ;
var methodDefn = DotNetAdapter . GetMethodInfoOverloadDefinition ( name , method , method . GetParameters (). Length - parameters . Length );
Interlocked . CompareExchange ( ref _cachedMethodDefinition , methodDefn , null );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return _cachedMethodDefinition ;
}
}
internal ParameterInformation [] parameters ;
internal bool hasVarArgs ;
internal bool hasOptional ;
internal bool isGeneric ;
2016-07-29 13:02:49 -07:00
private bool _useReflection ;
2020-05-29 05:56:33 +01:00
2016-03-30 14:20:52 -07:00
private delegate object MethodInvoker ( object target , object [] arguments );
2020-05-29 05:56:33 +01:00
2016-07-29 13:02:49 -07:00
private MethodInvoker _methodInvoker ;
2016-03-30 14:20:52 -07:00
/// <summary>
2019-01-06 04:40:24 +05:00
/// This constructor supports .net methods.
2016-03-30 14:20:52 -07:00
/// </summary>
internal MethodInformation ( MethodBase method , int parametersToIgnore )
{
this . method = method ;
this . isGeneric = method . IsGenericMethod ;
ParameterInfo [] methodParameters = method . GetParameters ();
int parametersLength = methodParameters . Length - parametersToIgnore ;
this . parameters = new ParameterInformation [ parametersLength ];
for ( int i = 0 ; i < parametersLength ; i ++)
{
this . parameters [ i ] = new ParameterInformation ( methodParameters [ i ]);
if ( methodParameters [ i ]. IsOptional )
{
hasOptional = true ;
}
}
this . hasVarArgs = false ;
if ( parametersLength > 0 )
{
ParameterInfo lastParameter = methodParameters [ parametersLength - 1 ];
// Optional and params together are forbidden in VB and so we only check for params
// if !hasOptional
if (! hasOptional && lastParameter . ParameterType . IsArray )
{
// The extension method 'CustomAttributeExtensions.GetCustomAttributes(ParameterInfo, Type, Boolean)' has inconsistent
// behavior on its return value in both FullCLR and CoreCLR. According to MSDN, if the attribute cannot be found, it
// should return an empty collection. However, it returns null in some rare cases [when the parameter isn't backed by
// actual metadata].
// This inconsistent behavior affects OneCore powershell because we are using the extension method here when compiling
// against CoreCLR. So we need to add a null check until this is fixed in CLR.
var paramArrayAttrs = lastParameter . GetCustomAttributes ( typeof ( ParamArrayAttribute ), false );
2020-06-25 07:30:40 +01:00
if ( paramArrayAttrs != null && paramArrayAttrs . Length > 0 )
2016-03-30 14:20:52 -07:00
{
this . hasVarArgs = true ;
this . parameters [ parametersLength - 1 ]. isParamArray = true ;
}
}
}
}
internal MethodInformation ( bool hasvarargs , bool hasoptional , ParameterInformation [] arguments )
{
hasVarArgs = hasvarargs ;
hasOptional = hasoptional ;
parameters = arguments ;
}
internal object Invoke ( object target , object [] arguments )
{
2018-08-26 12:50:58 -07:00
// There may be parameters of ByRef-like types, but they will be taken care of
// when we resolve overloads to find the best methods -- proper exception will
// be thrown when converting arguments to the ByRef-like parameter types.
//
// So when reaching here, we only care about (1) if the method return type is
// BeRef-like; (2) if it's a constrcutor of a ByRef-like type.
if ( method is ConstructorInfo ctor )
{
if ( ctor . DeclaringType . IsByRefLike )
{
throw new MethodException (
nameof ( ExtendedTypeSystem . CannotInstantiateBoxedByRefLikeType ),
innerException : null ,
ExtendedTypeSystem . CannotInstantiateBoxedByRefLikeType ,
ctor . DeclaringType );
}
return ctor . Invoke ( arguments );
}
2019-04-24 19:27:22 +05:00
var methodInfo = ( MethodInfo ) method ;
2018-08-26 12:50:58 -07:00
if ( methodInfo . ReturnType . IsByRefLike )
{
throw new MethodException (
nameof ( ExtendedTypeSystem . CannotCallMethodWithByRefLikeReturnType ),
innerException : null ,
ExtendedTypeSystem . CannotCallMethodWithByRefLikeReturnType ,
methodInfo . Name ,
methodInfo . ReturnType );
}
2016-03-30 14:20:52 -07:00
if ( target is PSObject )
{
if (! method . DeclaringType . IsAssignableFrom ( target . GetType ()))
{
target = PSObject . Base ( target );
}
}
2016-07-29 13:02:49 -07:00
if (! _useReflection )
2016-03-30 14:20:52 -07:00
{
2022-07-25 21:11:29 +04:00
_methodInvoker ??= GetMethodInvoker ( methodInfo );
2018-08-26 12:50:58 -07:00
2016-07-29 13:02:49 -07:00
if ( _methodInvoker != null )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
return _methodInvoker ( target , arguments );
2016-03-30 14:20:52 -07:00
}
}
2018-08-26 12:50:58 -07:00
2016-03-30 14:20:52 -07:00
return method . Invoke ( target , arguments );
}
2020-11-05 03:36:23 +00:00
private static readonly OpCode [] s_ldc = new OpCode [] {
2016-03-30 14:20:52 -07:00
OpCodes . Ldc_I4_0 , OpCodes . Ldc_I4_1 , OpCodes . Ldc_I4_2 , OpCodes . Ldc_I4_3 , OpCodes . Ldc_I4_4 ,
OpCodes . Ldc_I4_5 , OpCodes . Ldc_I4_6 , OpCodes . Ldc_I4_7 , OpCodes . Ldc_I4_8
};
private static void EmitLdc ( ILGenerator emitter , int c )
{
2016-07-29 13:02:49 -07:00
if ( c < s_ldc . Length )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
emitter . Emit ( s_ldc [ c ]);
2016-03-30 14:20:52 -07:00
}
else
{
emitter . Emit ( OpCodes . Ldc_I4 , c );
}
}
private static bool CompareMethodParameters ( MethodBase method1 , MethodBase method2 )
{
ParameterInfo [] params1 = method1 . GetParameters ();
ParameterInfo [] params2 = method2 . GetParameters ();
if ( params1 . Length != params2 . Length )
{
return false ;
}
for ( int i = 0 ; i < params1 . Length ; ++ i )
{
if ( params1 [ i ]. ParameterType != params2 [ i ]. ParameterType )
{
return false ;
}
}
return true ;
}
private static Type FindInterfaceForMethod ( MethodInfo method , out MethodInfo methodToCall )
{
methodToCall = null ;
Type valuetype = method . DeclaringType ;
2016-07-29 13:02:49 -07:00
2018-04-13 15:01:41 -07:00
Diagnostics . Assert ( valuetype . IsValueType , "This code only works with valuetypes" );
2016-03-30 14:20:52 -07:00
Type [] interfaces = valuetype . GetInterfaces ();
for ( int i = 0 ; i < interfaces . Length ; i ++)
{
Type type = interfaces [ i ];
MethodInfo methodInfo = type . GetMethod ( method . Name , BindingFlags . Instance );
if ( methodInfo != null && CompareMethodParameters ( methodInfo , method ))
{
methodToCall = methodInfo ;
return type ;
}
}
// TODO: method impls (not especially important because I don't think they can be called in script.
return null ;
}
2016-07-29 13:02:49 -07:00
[SuppressMessage("NullPtr", "#pw26500", Justification = "This is a false positive. Original warning was on the deference of 'locals' on line 1863: emitter.Emit(OpCodes.Ldloca, locals[cLocal] ) ")]
2016-03-30 14:20:52 -07:00
private MethodInvoker GetMethodInvoker ( MethodInfo method )
{
Type type ;
bool valueTypeInstanceMethod = false ;
bool anyOutOrRefParameters = false ;
bool mustStoreRetVal = false ;
MethodInfo methodToCall = method ;
int cLocal = 0 ;
int c ;
DynamicMethod dynamicMethod = new DynamicMethod ( method . Name , typeof ( object ),
2018-04-13 15:01:41 -07:00
new Type [] { typeof ( object ), typeof ( object []) }, typeof ( Adapter ). Module , true );
2016-03-30 14:20:52 -07:00
ILGenerator emitter = dynamicMethod . GetILGenerator ();
ParameterInfo [] parameters = method . GetParameters ();
int localCount = 0 ;
2018-04-13 15:01:41 -07:00
if (! method . IsStatic && method . DeclaringType . IsValueType )
2016-03-30 14:20:52 -07:00
{
if (! method . IsVirtual )
{
// We need a local to unbox the instance argument into
valueTypeInstanceMethod = true ;
localCount += 1 ;
}
}
for ( int i = 0 ; i < parameters . Length ; i ++)
{
// We need locals for any out/ref parameters. We could get
// away with avoiding a local if the parameter was 'object',
// but that optimization is not implemented.
if ( parameters [ i ]. IsOut || parameters [ i ]. ParameterType . IsByRef )
{
anyOutOrRefParameters = true ;
localCount += 1 ;
}
}
LocalBuilder [] locals = null ;
Type returnType = method . ReturnType ;
if ( localCount > 0 )
{
if ( anyOutOrRefParameters && returnType != typeof ( void ))
{
// If there are any ref/out parameters, we set them after the
// call. We can't leave the return value on the stack (it fails
// verification), so we must create a local to hold the return value.
localCount += 1 ;
mustStoreRetVal = true ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
locals = new LocalBuilder [ localCount ];
cLocal = 0 ;
if ( valueTypeInstanceMethod )
{
// Unbox the instance parameter into a local.
type = method . DeclaringType ;
locals [ cLocal ] = emitter . DeclareLocal ( type );
emitter . Emit ( OpCodes . Ldarg_0 );
emitter . Emit ( OpCodes . Unbox_Any , type );
emitter . Emit ( OpCodes . Stloc , locals [ cLocal ]);
cLocal += 1 ;
}
// Copy all arguments that are being passed as out/ref parameters into
// locals.
for ( c = 0 ; c < parameters . Length ; ++ c )
{
type = parameters [ c ]. ParameterType ;
if ( parameters [ c ]. IsOut || type . IsByRef )
{
if ( type . IsByRef )
{
type = type . GetElementType ();
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
locals [ cLocal ] = emitter . DeclareLocal ( type );
emitter . Emit ( OpCodes . Ldarg_1 );
EmitLdc ( emitter , c );
emitter . Emit ( OpCodes . Ldelem_Ref );
2018-04-13 15:01:41 -07:00
if ( type . IsValueType )
2016-03-30 14:20:52 -07:00
{
emitter . Emit ( OpCodes . Unbox_Any , type );
}
else if ( type != typeof ( object ))
{
emitter . Emit ( OpCodes . Castclass , type );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
emitter . Emit ( OpCodes . Stloc , locals [ cLocal ]);
cLocal += 1 ;
}
}
if ( mustStoreRetVal )
{
locals [ cLocal ] = emitter . DeclareLocal ( returnType );
}
}
cLocal = 0 ;
if (! method . IsStatic )
{
// Load the "instance" argument.
2018-04-13 15:01:41 -07:00
if ( method . DeclaringType . IsValueType )
2016-03-30 14:20:52 -07:00
{
if ( method . IsVirtual )
{
type = FindInterfaceForMethod ( method , out methodToCall );
2020-07-30 16:06:38 -07:00
if ( type == null )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
_useReflection = true ;
2016-03-30 14:20:52 -07:00
return null ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
emitter . Emit ( OpCodes . Ldarg_0 );
emitter . Emit ( OpCodes . Castclass , type );
}
else
{
emitter . Emit ( OpCodes . Ldloca , locals [ cLocal ]);
cLocal += 1 ;
}
}
else
{
emitter . Emit ( OpCodes . Ldarg_0 );
}
}
for ( c = 0 ; c < parameters . Length ; c ++)
{
type = parameters [ c ]. ParameterType ;
if ( type . IsByRef )
{
emitter . Emit ( OpCodes . Ldloca , locals [ cLocal ]);
cLocal += 1 ;
}
else if ( parameters [ c ]. IsOut )
{
emitter . Emit ( OpCodes . Ldloc , locals [ cLocal ]);
cLocal += 1 ;
}
else
{
emitter . Emit ( OpCodes . Ldarg_1 );
EmitLdc ( emitter , c );
emitter . Emit ( OpCodes . Ldelem_Ref );
// Unbox value types since our args array is full of objects
2018-04-13 15:01:41 -07:00
if ( type . IsValueType )
2016-03-30 14:20:52 -07:00
{
emitter . Emit ( OpCodes . Unbox_Any , type );
}
// For reference types, cast from object
else if ( type != typeof ( object ))
{
emitter . Emit ( OpCodes . Castclass , type );
}
}
}
emitter . Emit ( method . IsStatic ? OpCodes . Call : OpCodes . Callvirt , methodToCall );
if ( mustStoreRetVal )
{
emitter . Emit ( OpCodes . Stloc , locals [ locals . Length - 1 ]);
}
// Handle the ref/out arguments by copying the locals
// back into the original args array
if ( anyOutOrRefParameters )
{
cLocal = valueTypeInstanceMethod ? 1 : 0 ;
for ( c = 0 ; c < parameters . Length ; c ++)
{
type = parameters [ c ]. ParameterType ;
if (! parameters [ c ]. IsOut && ! type . IsByRef )
{
continue ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
if ( type . IsByRef )
{
type = type . GetElementType ();
}
emitter . Emit ( OpCodes . Ldarg_1 );
EmitLdc ( emitter , c );
emitter . Emit ( OpCodes . Ldloc , locals [ cLocal ]);
// Again, box value types since the args array holds objects
2018-04-13 15:01:41 -07:00
if ( type . IsValueType )
2016-03-30 14:20:52 -07:00
{
emitter . Emit ( OpCodes . Box , type );
}
emitter . Emit ( OpCodes . Stelem_Ref );
cLocal += 1 ;
}
}
// We must return something, so return null for void methods
if ( returnType == typeof ( void ))
{
emitter . Emit ( OpCodes . Ldnull );
}
else
{
if ( mustStoreRetVal )
{
// Return value was stored in a local, load it before return
emitter . Emit ( OpCodes . Ldloc , locals [ locals . Length - 1 ]);
}
Adapter . DoBoxingIfNecessary ( emitter , returnType );
}
emitter . Emit ( OpCodes . Ret );
return ( MethodInvoker ) dynamicMethod . CreateDelegate ( typeof ( MethodInvoker ));
}
}
/// <summary>
/// Stores parameter related information.
/// This structure should be used whenever a new type is adapted.
/// For example, ManagementObjectAdapter uses this structure to store
/// method parameter information.
/// </summary>
internal class ParameterInformation
{
internal Type parameterType ;
internal object defaultValue ;
internal bool isOptional ;
internal bool isByRef ;
internal bool isParamArray ;
internal ParameterInformation ( System . Reflection . ParameterInfo parameter )
{
this . isOptional = parameter . IsOptional ;
this . defaultValue = parameter . DefaultValue ;
this . parameterType = parameter . ParameterType ;
if ( this . parameterType . IsByRef )
{
this . isByRef = true ;
this . parameterType = this . parameterType . GetElementType ();
}
else
{
this . isByRef = false ;
}
}
internal ParameterInformation ( Type parameterType , bool isOptional , object defaultValue , bool isByRef )
{
this . parameterType = parameterType ;
this . isOptional = isOptional ;
this . defaultValue = defaultValue ;
this . isByRef = isByRef ;
}
}
/// <summary>
/// This is the adapter used for all objects that don't match the appropriate types for other adapters.
/// It uses reflection to retrieve property information.
/// </summary>
internal class DotNetAdapter : Adapter
{
2016-08-28 05:44:55 +00:00
#region auxiliary methods and classes
2016-03-30 14:20:52 -07:00
private const BindingFlags instanceBindingFlags = ( BindingFlags . FlattenHierarchy | BindingFlags . Public |
BindingFlags . IgnoreCase | BindingFlags . Instance );
2020-05-29 05:56:33 +01:00
2016-03-30 14:20:52 -07:00
private const BindingFlags staticBindingFlags = ( BindingFlags . FlattenHierarchy | BindingFlags . Public |
BindingFlags . IgnoreCase | BindingFlags . Static );
2020-05-29 05:56:33 +01:00
2020-11-05 03:36:23 +00:00
private readonly bool _isStatic ;
2016-03-30 14:20:52 -07:00
internal DotNetAdapter () { }
internal DotNetAdapter ( bool isStatic )
{
2016-07-29 13:02:49 -07:00
_isStatic = isStatic ;
2016-03-30 14:20:52 -07:00
}
// This static is thread safe based on the lock in GetInstancePropertyReflectionTable
/// <summary>
2019-01-06 04:40:24 +05:00
/// CLR reflection property cache for instance properties.
2016-03-30 14:20:52 -07:00
/// </summary>
2020-11-05 03:36:23 +00:00
private static readonly Dictionary < Type , CacheTable > s_instancePropertyCacheTable = new Dictionary < Type , CacheTable >();
2016-03-30 14:20:52 -07:00
// This static is thread safe based on the lock in GetStaticPropertyReflectionTable
/// <summary>
2019-01-06 04:40:24 +05:00
/// CLR reflection property cache for static properties.
2016-03-30 14:20:52 -07:00
/// </summary>
2020-11-05 03:36:23 +00:00
private static readonly Dictionary < Type , CacheTable > s_staticPropertyCacheTable = new Dictionary < Type , CacheTable >();
2016-03-30 14:20:52 -07:00
// This static is thread safe based on the lock in GetInstanceMethodReflectionTable
/// <summary>
2019-01-06 04:40:24 +05:00
/// CLR reflection method cache for instance methods.
2016-03-30 14:20:52 -07:00
/// </summary>
2020-11-05 03:36:23 +00:00
private static readonly Dictionary < Type , CacheTable > s_instanceMethodCacheTable = new Dictionary < Type , CacheTable >();
2016-03-30 14:20:52 -07:00
// This static is thread safe based on the lock in GetStaticMethodReflectionTable
/// <summary>
2019-01-06 04:40:24 +05:00
/// CLR reflection method cache for static methods.
2016-03-30 14:20:52 -07:00
/// </summary>
2020-11-05 03:36:23 +00:00
private static readonly Dictionary < Type , CacheTable > s_staticMethodCacheTable = new Dictionary < Type , CacheTable >();
2016-03-30 14:20:52 -07:00
// This static is thread safe based on the lock in GetInstanceMethodReflectionTable
/// <summary>
2019-01-06 04:40:24 +05:00
/// CLR reflection method cache for instance events.
2016-03-30 14:20:52 -07:00
/// </summary>
2016-07-29 13:02:49 -07:00
private static readonly Dictionary < Type , Dictionary < string , EventCacheEntry >> s_instanceEventCacheTable
2016-03-30 14:20:52 -07:00
= new Dictionary < Type , Dictionary < string , EventCacheEntry >>();
// This static is thread safe based on the lock in GetStaticMethodReflectionTable
/// <summary>
2019-01-06 04:40:24 +05:00
/// CLR reflection method cache for static events.
2016-03-30 14:20:52 -07:00
/// </summary>
2016-07-29 13:02:49 -07:00
private static readonly Dictionary < Type , Dictionary < string , EventCacheEntry >> s_staticEventCacheTable
2016-03-30 14:20:52 -07:00
= new Dictionary < Type , Dictionary < string , EventCacheEntry >>();
2019-12-11 15:37:18 -08:00
internal class MethodCacheEntry : CacheEntry
2016-03-30 14:20:52 -07:00
{
2019-12-11 15:37:18 -08:00
internal readonly MethodInformation [] methodInformationStructures ;
2017-12-02 03:40:35 +01:00
/// <summary>
/// Cache delegate to the ctor of PSMethod<> with a template parameter derived from the methodInformationStructures.
/// </summary>
2018-04-06 17:01:04 -07:00
internal Func < string , DotNetAdapter , object , DotNetAdapter . MethodCacheEntry , bool , bool , PSMethod > PSMethodCtor ;
2016-03-30 14:20:52 -07:00
2021-08-14 07:56:14 -07:00
internal MethodCacheEntry ( IList < MethodBase > methods )
2016-03-30 14:20:52 -07:00
{
methodInformationStructures = DotNetAdapter . GetMethodInformationArray ( methods );
}
internal MethodInformation this [ int i ]
{
get
{
return methodInformationStructures [ i ];
}
}
2019-12-11 15:37:18 -08:00
private bool? _isHidden ;
2020-05-29 05:56:33 +01:00
2019-12-11 15:37:18 -08:00
internal override bool IsHidden
{
get
{
2020-07-30 16:06:38 -07:00
if ( _isHidden == null )
2019-12-11 15:37:18 -08:00
{
bool hasHiddenAttribute = false ;
foreach ( var method in methodInformationStructures )
{
if ( method . method . GetCustomAttributes ( typeof ( HiddenAttribute ), inherit : false ). Length != 0 )
{
hasHiddenAttribute = true ;
break ;
}
}
_isHidden = hasHiddenAttribute ;
}
return _isHidden . Value ;
}
}
2016-03-30 14:20:52 -07:00
}
2019-12-11 15:37:18 -08:00
internal class EventCacheEntry : CacheEntry
2016-03-30 14:20:52 -07:00
{
internal EventInfo [] events ;
internal EventCacheEntry ( EventInfo [] events )
{
this . events = events ;
}
}
2019-12-11 15:37:18 -08:00
internal class ParameterizedPropertyCacheEntry : CacheEntry
2016-03-30 14:20:52 -07:00
{
internal MethodInformation [] getterInformation ;
internal MethodInformation [] setterInformation ;
internal string propertyName ;
internal bool readOnly ;
internal bool writeOnly ;
internal Type propertyType ;
// propertyDefinition is used as a string representation of the property
internal string [] propertyDefinition ;
internal ParameterizedPropertyCacheEntry ( List < PropertyInfo > properties )
{
PropertyInfo firstProperty = properties [ 0 ];
this . propertyName = firstProperty . Name ;
this . propertyType = firstProperty . PropertyType ;
var getterList = new List < MethodInfo >();
var setterList = new List < MethodInfo >();
var definitionArray = new List < string >();
for ( int i = 0 ; i < properties . Count ; i ++)
{
PropertyInfo property = properties [ i ];
// Properties can have different return types. If they do
2016-08-28 05:44:55 +00:00
// we pretend it is System.Object
2016-03-30 14:20:52 -07:00
if ( property . PropertyType != this . propertyType )
{
this . propertyType = typeof ( object );
}
// Get the public getter
MethodInfo propertyGetter = property . GetGetMethod ();
StringBuilder definition = new StringBuilder ();
StringBuilder extraDefinition = new StringBuilder ();
if ( propertyGetter != null )
{
extraDefinition . Append ( "get;" );
definition . Append ( DotNetAdapter . GetMethodInfoOverloadDefinition ( this . propertyName , propertyGetter , 0 ));
getterList . Add ( propertyGetter );
}
// Get the public setter
MethodInfo propertySetter = property . GetSetMethod ();
if ( propertySetter != null )
{
extraDefinition . Append ( "set;" );
if ( definition . Length == 0 )
{
definition . Append ( DotNetAdapter . GetMethodInfoOverloadDefinition ( this . propertyName , propertySetter , 1 ));
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
setterList . Add ( propertySetter );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
definition . Append ( " {" );
definition . Append ( extraDefinition );
2020-10-30 07:07:18 +00:00
definition . Append ( '}' );
2016-03-30 14:20:52 -07:00
definitionArray . Add ( definition . ToString ());
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
propertyDefinition = definitionArray . ToArray ();
this . writeOnly = getterList . Count == 0 ;
this . readOnly = setterList . Count == 0 ;
this . getterInformation = new MethodInformation [ getterList . Count ];
for ( int i = 0 ; i < getterList . Count ; i ++)
{
this . getterInformation [ i ] = new MethodInformation ( getterList [ i ], 0 );
}
this . setterInformation = new MethodInformation [ setterList . Count ];
for ( int i = 0 ; i < setterList . Count ; i ++)
{
this . setterInformation [ i ] = new MethodInformation ( setterList [ i ], 1 );
}
}
}
2019-12-11 15:37:18 -08:00
internal class PropertyCacheEntry : CacheEntry
2016-03-30 14:20:52 -07:00
{
internal delegate object GetterDelegate ( object instance );
2020-11-20 11:11:36 +00:00
2016-03-30 14:20:52 -07:00
internal delegate void SetterDelegate ( object instance , object setValue );
internal PropertyCacheEntry ( PropertyInfo property )
{
this . member = property ;
this . propertyType = property . PropertyType ;
// Generating code for fields/properties in ValueTypes is complex and will probably
// require different delegates
// The same is true for generics, COM Types.
2018-04-13 15:01:41 -07:00
Type declaringType = property . DeclaringType ;
2016-03-30 14:20:52 -07:00
2018-04-13 15:01:41 -07:00
if ( declaringType . IsValueType ||
propertyType . IsGenericType ||
declaringType . IsGenericType ||
2018-08-26 12:50:58 -07:00
declaringType . IsCOMObject ||
propertyType . IsCOMObject )
2016-03-30 14:20:52 -07:00
{
2020-07-30 16:06:38 -07:00
this . readOnly = property . GetSetMethod () == null ;
this . writeOnly = property . GetGetMethod () == null ;
2016-03-30 14:20:52 -07:00
this . useReflection = true ;
return ;
}
// Get the public or protected getter
MethodInfo propertyGetter = property . GetGetMethod ( true );
if ( propertyGetter != null && ( propertyGetter . IsPublic || propertyGetter . IsFamily ))
{
this . isStatic = propertyGetter . IsStatic ;
// Delegate is initialized later to avoid jit if it's not called
}
else
{
this . writeOnly = true ;
}
// Get the public or protected setter
MethodInfo propertySetter = property . GetSetMethod ( true );
if ( propertySetter != null && ( propertySetter . IsPublic || propertySetter . IsFamily ))
{
this . isStatic = propertySetter . IsStatic ;
}
else
{
this . readOnly = true ;
}
}
internal PropertyCacheEntry ( FieldInfo field )
{
this . member = field ;
this . isStatic = field . IsStatic ;
this . propertyType = field . FieldType ;
// const fields have no setter and we are getting them with GetValue instead of
// using generated code. Init fields are only settable during initialization
// then cannot be set afterwards..
if ( field . IsLiteral || field . IsInitOnly )
{
this . readOnly = true ;
}
}
2016-07-29 13:02:49 -07:00
private void InitGetter ()
2016-03-30 14:20:52 -07:00
{
if ( writeOnly || useReflection )
2018-08-26 12:50:58 -07:00
{
2016-03-30 14:20:52 -07:00
return ;
2018-08-26 12:50:58 -07:00
}
2016-03-30 14:20:52 -07:00
var parameter = Expression . Parameter ( typeof ( object ));
Expression instance = null ;
var field = member as FieldInfo ;
if ( field != null )
{
var declaringType = field . DeclaringType ;
if (! field . IsStatic )
{
2018-04-13 15:01:41 -07:00
if ( declaringType . IsValueType )
2016-03-30 14:20:52 -07:00
{
// I'm not sure we can get here with a Nullable, but if so,
// we must use the Value property, see PSGetMemberBinder.GetTargetValue.
instance = Nullable . GetUnderlyingType ( declaringType ) != null
? ( Expression ) Expression . Property ( parameter , "Value" )
: Expression . Unbox ( parameter , declaringType );
}
else
{
instance = parameter . Cast ( declaringType );
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
Expression getterExpr ;
2018-04-13 15:01:41 -07:00
if ( declaringType . IsGenericTypeDefinition )
2016-03-30 14:20:52 -07:00
{
Expression innerException = Expression . New ( CachedReflectionInfo . GetValueException_ctor ,
Expression . Constant ( "PropertyGetException" ),
2016-07-29 13:02:49 -07:00
Expression . Constant ( null , typeof ( Exception )),
2016-03-30 14:20:52 -07:00
Expression . Constant ( ParserStrings . PropertyInGenericType ),
Expression . NewArrayInit ( typeof ( object ), Expression . Constant ( field . Name )));
getterExpr = Compiler . ThrowRuntimeErrorWithInnerException ( "PropertyGetException" ,
Expression . Constant ( ParserStrings . PropertyInGenericType ),
innerException , typeof ( object ), Expression . Constant ( field . Name ));
}
else
{
2016-07-29 13:02:49 -07:00
getterExpr = Expression . Field ( instance , field ). Cast ( typeof ( object ));
2016-03-30 14:20:52 -07:00
}
2016-07-29 13:02:49 -07:00
_getterDelegate = Expression . Lambda < GetterDelegate >( getterExpr , parameter ). Compile ();
2016-03-30 14:20:52 -07:00
return ;
}
var property = ( PropertyInfo ) member ;
var propertyGetter = property . GetGetMethod ( true );
instance = this . isStatic ? null : parameter . Cast ( propertyGetter . DeclaringType );
2016-07-29 13:02:49 -07:00
_getterDelegate = Expression . Lambda < GetterDelegate >(
2016-03-30 14:20:52 -07:00
Expression . Property ( instance , property ). Cast ( typeof ( object )), parameter ). Compile ();
}
2016-07-29 13:02:49 -07:00
private void InitSetter ()
2016-03-30 14:20:52 -07:00
{
if ( readOnly || useReflection )
2018-08-26 12:50:58 -07:00
{
2016-03-30 14:20:52 -07:00
return ;
2018-08-26 12:50:58 -07:00
}
2016-03-30 14:20:52 -07:00
var parameter = Expression . Parameter ( typeof ( object ));
var value = Expression . Parameter ( typeof ( object ));
Expression instance = null ;
var field = member as FieldInfo ;
if ( field != null )
{
var declaringType = field . DeclaringType ;
if (! field . IsStatic )
{
2018-04-13 15:01:41 -07:00
if ( declaringType . IsValueType )
2016-03-30 14:20:52 -07:00
{
// I'm not sure we can get here with a Nullable, but if so,
// we must use the Value property, see PSGetMemberBinder.GetTargetValue.
instance = Nullable . GetUnderlyingType ( declaringType ) != null
? ( Expression ) Expression . Property ( parameter , "Value" )
: Expression . Unbox ( parameter , declaringType );
}
else
{
instance = parameter . Cast ( declaringType );
}
}
Expression setterExpr ;
string errMessage = null ;
Type errType = field . FieldType ;
2018-04-13 15:01:41 -07:00
if ( declaringType . IsGenericTypeDefinition )
2016-03-30 14:20:52 -07:00
{
errMessage = ParserStrings . PropertyInGenericType ;
2018-04-13 15:01:41 -07:00
if ( errType . ContainsGenericParameters )
2016-03-30 14:20:52 -07:00
{
errType = typeof ( object );
}
}
else if ( readOnly )
{
errMessage = ParserStrings . PropertyIsReadOnly ;
2016-07-29 13:02:49 -07:00
}
2016-03-30 14:20:52 -07:00
if ( errMessage != null )
{
Expression innerException = Expression . New ( CachedReflectionInfo . SetValueException_ctor ,
Expression . Constant ( "PropertyAssignmentException" ),
Expression . Constant ( null , typeof ( Exception )),
Expression . Constant ( errMessage ),
Expression . NewArrayInit ( typeof ( object ), Expression . Constant ( field . Name )));
setterExpr = Compiler . ThrowRuntimeErrorWithInnerException ( "PropertyAssignmentException" ,
Expression . Constant ( errMessage ),
innerException , errType , Expression . Constant ( field . Name ));
}
else
{
setterExpr = Expression . Assign ( Expression . Field ( instance , field ), Expression . Convert ( value , field . FieldType ));
}
2018-12-23 22:20:06 -08:00
2016-07-29 13:02:49 -07:00
_setterDelegate = Expression . Lambda < SetterDelegate >( setterExpr , parameter , value ). Compile ();
2016-03-30 14:20:52 -07:00
return ;
}
2016-07-29 13:02:49 -07:00
var property = ( PropertyInfo ) member ;
2016-03-30 14:20:52 -07:00
MethodInfo propertySetter = property . GetSetMethod ( true );
instance = this . isStatic ? null : parameter . Cast ( propertySetter . DeclaringType );
2016-07-29 13:02:49 -07:00
_setterDelegate =
2016-03-30 14:20:52 -07:00
Expression . Lambda < SetterDelegate >(
Expression . Assign ( Expression . Property ( instance , property ),
Expression . Convert ( value , property . PropertyType )), parameter , value ). Compile ();
}
internal MemberInfo member ;
internal GetterDelegate getterDelegate
{
get
{
2020-07-30 16:06:38 -07:00
if ( _getterDelegate == null )
2016-03-30 14:20:52 -07:00
{
InitGetter ();
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return _getterDelegate ;
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
private GetterDelegate _getterDelegate ;
internal SetterDelegate setterDelegate
{
get
{
2020-07-30 16:06:38 -07:00
if ( _setterDelegate == null )
2016-03-30 14:20:52 -07:00
{
InitSetter ();
}
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
return _setterDelegate ;
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
private SetterDelegate _setterDelegate ;
internal bool useReflection ;
internal bool readOnly ;
internal bool writeOnly ;
internal bool isStatic ;
internal Type propertyType ;
2019-12-11 15:37:18 -08:00
private bool? _isHidden ;
2020-05-29 05:56:33 +01:00
2019-12-11 15:37:18 -08:00
internal override bool IsHidden
{
get
{
2022-07-25 21:11:29 +04:00
_isHidden ??= member . GetCustomAttributes ( typeof ( HiddenAttribute ), inherit : false ). Length != 0 ;
2019-12-11 15:37:18 -08:00
return _isHidden . Value ;
}
}
2016-07-29 13:02:49 -07:00
private AttributeCollection _attributes ;
2020-05-29 05:56:33 +01:00
2016-03-30 14:20:52 -07:00
internal AttributeCollection Attributes
{
get
{
2020-07-30 16:06:38 -07:00
if ( _attributes == null )
2016-03-30 14:20:52 -07:00
{
// Since AttributeCollection can only be constructed with an Attribute[], one is built.
var objAttributes = this . member . GetCustomAttributes ( true );
2016-07-29 13:02:49 -07:00
_attributes = new AttributeCollection ( objAttributes . Cast < Attribute >(). ToArray ());
2016-03-30 14:20:52 -07:00
}
2018-12-23 22:20:06 -08:00
2016-07-29 13:02:49 -07:00
return _attributes ;
2016-03-30 14:20:52 -07:00
}
}
}
/// <summary>
/// Compare the signatures of the methods, returning true if the methods have
/// the same signature.
/// </summary>
private static bool SameSignature ( MethodBase method1 , MethodBase method2 )
{
if ( method1 . GetGenericArguments (). Length != method2 . GetGenericArguments (). Length )
{
return false ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
ParameterInfo [] parameters1 = method1 . GetParameters ();
ParameterInfo [] parameters2 = method2 . GetParameters ();
if ( parameters1 . Length != parameters2 . Length )
{
return false ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
for ( int i = 0 ; i < parameters1 . Length ; ++ i )
{
if ( parameters1 [ i ]. ParameterType != parameters2 [ i ]. ParameterType
|| parameters1 [ i ]. IsOut != parameters2 [ i ]. IsOut
|| parameters1 [ i ]. IsOptional != parameters2 [ i ]. IsOptional )
{
return false ;
}
}
return true ;
}
/// <summary>
/// Adds an overload to a list of MethodInfo. Before adding to the list, the
/// list is searched to make sure we don't end up with 2 functions with the
/// same signature. This can happen when there is a newslot method.
/// </summary>
private static void AddOverload ( List < MethodBase > previousMethodEntry , MethodInfo method )
{
bool add = true ;
for ( int i = 0 ; i < previousMethodEntry . Count ; i ++)
{
if ( SameSignature ( previousMethodEntry [ i ], method ))
{
add = false ;
break ;
}
}
if ( add )
{
previousMethodEntry . Add ( method );
}
}
private static void PopulateMethodReflectionTable ( Type type , MethodInfo [] methods , CacheTable typeMethods )
{
2021-08-14 07:56:14 -07:00
foreach ( MethodInfo method in methods )
2016-03-30 14:20:52 -07:00
{
if ( method . DeclaringType == type )
{
string methodName = method . Name ;
var previousMethodEntry = ( List < MethodBase >) typeMethods [ methodName ];
2020-07-30 16:06:38 -07:00
if ( previousMethodEntry == null )
2016-03-30 14:20:52 -07:00
{
var methodEntry = new List < MethodBase > { method };
typeMethods . Add ( methodName , methodEntry );
}
else
{
AddOverload ( previousMethodEntry , method );
}
}
}
2018-04-13 15:01:41 -07:00
if ( type . BaseType != null )
2016-03-30 14:20:52 -07:00
{
2018-04-13 15:01:41 -07:00
PopulateMethodReflectionTable ( type . BaseType , methods , typeMethods );
2016-03-30 14:20:52 -07:00
}
}
private static void PopulateMethodReflectionTable ( ConstructorInfo [] ctors , CacheTable typeMethods )
{
2021-08-14 07:56:14 -07:00
foreach ( ConstructorInfo ctor in ctors )
2016-03-30 14:20:52 -07:00
{
var previousMethodEntry = ( List < MethodBase >) typeMethods [ "new" ];
2020-07-30 16:06:38 -07:00
if ( previousMethodEntry == null )
2016-03-30 14:20:52 -07:00
{
var methodEntry = new List < MethodBase >();
methodEntry . Add ( ctor );
typeMethods . Add ( "new" , methodEntry );
}
else
{
previousMethodEntry . Add ( ctor );
}
}
}
/// <summary>
/// Called from GetMethodReflectionTable within a lock to fill the
2019-01-06 04:40:24 +05:00
/// method cache table.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="type">Type to get methods from.</param>
/// <param name="typeMethods">Table to be filled.</param>
/// <param name="bindingFlags">BindingFlags to use.</param>
2016-03-30 14:20:52 -07:00
private static void PopulateMethodReflectionTable ( Type type , CacheTable typeMethods , BindingFlags bindingFlags )
{
2021-08-14 07:56:14 -07:00
bool isStatic = bindingFlags . HasFlag ( BindingFlags . Static );
2018-04-13 15:01:41 -07:00
2021-08-14 07:56:14 -07:00
MethodInfo [] methods = type . GetMethods ( bindingFlags );
PopulateMethodReflectionTable ( type , methods , typeMethods );
2016-03-30 14:20:52 -07:00
Type [] interfaces = type . GetInterfaces ();
2021-08-14 07:56:14 -07:00
foreach ( Type interfaceType in interfaces )
2016-03-30 14:20:52 -07:00
{
2018-04-13 15:01:41 -07:00
if (! TypeResolver . IsPublic ( interfaceType ))
2016-03-30 14:20:52 -07:00
{
continue ;
}
2018-04-13 15:01:41 -07:00
if ( interfaceType . IsGenericType && type . IsArray )
2016-03-30 14:20:52 -07:00
{
2021-08-14 07:56:14 -07:00
// A bit of background: Array doesn't directly support any generic interface at all. Instead, a stub class
// named 'SZArrayHelper' provides these generic interfaces at runtime for zero-based one-dimension arrays.
// This is why '[object[]].GetInterfaceMap([ICollection[object]])' throws 'ArgumentException'.
// (see https://stackoverflow.com/a/31883327)
//
// We had always been skipping generic interfaces for array types because 'GetInterfaceMap' doesn't work
// for it. Today, even though we don't use 'GetInterfaceMap' anymore, the same code is kept here because
// methods from generic interfaces of an array type could cause ambiguity in method overloads resolution.
// For example, "$objs = @(1,2,3,4); $objs.Contains(1)" would fail because there would be 2 overloads of
// the 'Contains' methods which are equally good matches for the call.
// bool IList.Contains(System.Object value)
// bool ICollection[Object].Contains(System.Object item)
continue ;
2016-03-30 14:20:52 -07:00
}
2021-08-14 07:56:14 -07:00
methods = interfaceType . GetMethods ( bindingFlags );
2018-12-23 22:20:06 -08:00
2021-08-14 07:56:14 -07:00
foreach ( MethodInfo interfaceMethod in methods )
2016-03-30 14:20:52 -07:00
{
2021-08-14 07:56:14 -07:00
if ( isStatic && interfaceMethod . IsVirtual )
2016-03-30 14:20:52 -07:00
{
2021-08-14 07:56:14 -07:00
// Ignore static virtual/abstract methods on an interface because:
// 1. if it's implicitly implemented, which will be mostly the case, then the corresponding
// methods were already retrieved from the 'type.GetMethods' step above;
// 2. if it's explicitly implemented, we cannot call 'Invoke(null, args)' on the static method,
// but have to use 'type.GetInterfaceMap(interfaceType)' to get the corresponding target
// methods, and call 'Invoke(null, args)' on them. The target methods will be non-public
// in this case, which we always ignore.
// 3. The recommendation from .NET team is to ignore the static virtuals on interfaces,
// especially given that the APIs may change in .NET 7.
2016-03-30 14:20:52 -07:00
continue ;
}
2021-08-14 07:56:14 -07:00
var previousMethodEntry = ( List < MethodBase >) typeMethods [ interfaceMethod . Name ];
2020-07-30 16:06:38 -07:00
if ( previousMethodEntry == null )
2016-03-30 14:20:52 -07:00
{
2021-08-14 07:56:14 -07:00
var methodEntry = new List < MethodBase > { interfaceMethod };
typeMethods . Add ( interfaceMethod . Name , methodEntry );
2016-03-30 14:20:52 -07:00
}
else
{
2021-08-14 07:56:14 -07:00
if (! previousMethodEntry . Contains ( interfaceMethod ))
2016-03-30 14:20:52 -07:00
{
2021-08-14 07:56:14 -07:00
previousMethodEntry . Add ( interfaceMethod );
2016-03-30 14:20:52 -07:00
}
}
}
}
2018-04-13 15:01:41 -07:00
if (( bindingFlags & BindingFlags . Static ) != 0 && TypeResolver . IsPublic ( type ))
2016-03-30 14:20:52 -07:00
{
// We don't add constructors if there was a static method named new.
// We don't add constructors if the target type is not public, because it's useless to an internal type.
var previousMethodEntry = ( List < MethodBase >) typeMethods [ "new" ];
2020-07-30 16:06:38 -07:00
if ( previousMethodEntry == null )
2016-03-30 14:20:52 -07:00
{
var ctorBindingFlags = bindingFlags & ~( BindingFlags . FlattenHierarchy | BindingFlags . Static );
ctorBindingFlags |= BindingFlags . Instance ;
var ctorInfos = type . GetConstructors ( ctorBindingFlags );
PopulateMethodReflectionTable ( ctorInfos , typeMethods );
}
}
for ( int i = 0 ; i < typeMethods . memberCollection . Count ; i ++)
{
typeMethods . memberCollection [ i ] =
2021-08-14 07:56:14 -07:00
new MethodCacheEntry (( List < MethodBase >) typeMethods . memberCollection [ i ]);
2016-03-30 14:20:52 -07:00
}
}
/// <summary>
/// Called from GetEventReflectionTable within a lock to fill the
2019-01-06 04:40:24 +05:00
/// event cache table.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="type">Type to get events from.</param>
/// <param name="typeEvents">Table to be filled.</param>
/// <param name="bindingFlags">BindingFlags to use.</param>
2016-03-30 14:20:52 -07:00
private static void PopulateEventReflectionTable ( Type type , Dictionary < string , EventCacheEntry > typeEvents , BindingFlags bindingFlags )
{
2021-08-14 07:56:14 -07:00
EventInfo [] events = type . GetEvents ( bindingFlags );
var tempTable = new Dictionary < string , List < EventInfo >>( StringComparer . OrdinalIgnoreCase );
foreach ( EventInfo typeEvent in events )
2016-03-30 14:20:52 -07:00
{
2021-08-14 07:56:14 -07:00
string eventName = typeEvent . Name ;
if (! tempTable . TryGetValue ( eventName , out List < EventInfo > entryList ))
{
entryList = new List < EventInfo >();
tempTable . Add ( eventName , entryList );
}
entryList . Add ( typeEvent );
2016-03-30 14:20:52 -07:00
}
2018-04-13 15:01:41 -07:00
2021-08-14 07:56:14 -07:00
foreach ( KeyValuePair < string , List < EventInfo >> entry in tempTable )
2016-03-30 14:20:52 -07:00
{
2021-08-14 07:56:14 -07:00
typeEvents . Add ( entry . Key , new EventCacheEntry ( entry . Value . ToArray ()));
2016-03-30 14:20:52 -07:00
}
}
/// <summary>
2016-08-29 18:06:24 +00:00
/// This method is necessary because an overridden property in a specific class derived from a generic one will
2016-03-30 14:20:52 -07:00
/// appear twice. The second time, it should be ignored.
/// </summary>
private static bool PropertyAlreadyPresent ( List < PropertyInfo > previousProperties , PropertyInfo property )
{
2017-10-27 05:12:19 +08:00
// The loop below
2016-03-30 14:20:52 -07:00
bool returnValue = false ;
ParameterInfo [] propertyParameters = property . GetIndexParameters ();
int propertyIndexLength = propertyParameters . Length ;
2021-08-14 07:56:14 -07:00
foreach ( PropertyInfo previousProperty in previousProperties )
2016-03-30 14:20:52 -07:00
{
ParameterInfo [] previousParameters = previousProperty . GetIndexParameters ();
if ( previousParameters . Length == propertyIndexLength )
{
bool parametersAreSame = true ;
for ( int parameterIndex = 0 ; parameterIndex < previousParameters . Length ; parameterIndex ++)
{
ParameterInfo previousParameter = previousParameters [ parameterIndex ];
ParameterInfo propertyParameter = propertyParameters [ parameterIndex ];
if ( previousParameter . ParameterType != propertyParameter . ParameterType )
{
parametersAreSame = false ;
break ;
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
if ( parametersAreSame )
{
returnValue = true ;
break ;
}
}
}
return returnValue ;
}
/// <summary>
/// Called from GetPropertyReflectionTable within a lock to fill the
2019-01-06 04:40:24 +05:00
/// property cache table.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="type">Type to get properties from.</param>
/// <param name="typeProperties">Table to be filled.</param>
/// <param name="bindingFlags">BindingFlags to use.</param>
2016-03-30 14:20:52 -07:00
private static void PopulatePropertyReflectionTable ( Type type , CacheTable typeProperties , BindingFlags bindingFlags )
{
2021-08-14 07:56:14 -07:00
bool isStatic = bindingFlags . HasFlag ( BindingFlags . Static );
2016-03-30 14:20:52 -07:00
var tempTable = new Dictionary < string , List < PropertyInfo >>( StringComparer . OrdinalIgnoreCase );
2018-04-13 15:01:41 -07:00
2021-08-14 07:56:14 -07:00
PropertyInfo [] properties = type . GetProperties ( bindingFlags );
foreach ( PropertyInfo property in properties )
2016-03-30 14:20:52 -07:00
{
2021-08-14 07:56:14 -07:00
PopulateSingleProperty ( type , property , tempTable , property . Name );
2016-03-30 14:20:52 -07:00
}
Type [] interfaces = type . GetInterfaces ();
2021-08-14 07:56:14 -07:00
foreach ( Type interfaceType in interfaces )
2016-03-30 14:20:52 -07:00
{
if (! TypeResolver . IsPublic ( interfaceType ))
{
continue ;
}
properties = interfaceType . GetProperties ( bindingFlags );
2021-08-14 07:56:14 -07:00
foreach ( PropertyInfo property in properties )
2016-03-30 14:20:52 -07:00
{
2021-08-14 07:56:14 -07:00
if ( isStatic &&
( property . GetMethod ?. IsVirtual == true || property . SetMethod ?. IsVirtual == true ))
{
// Ignore static virtual/abstract properties on an interface because:
// 1. if it's implicitly implemented, which will be mostly the case, then the corresponding
// properties were already retrieved from the 'type.GetProperties' step above;
// 2. if it's explicitly implemented, we cannot call 'GetValue(null)' on the static property,
// but have to use 'type.GetInterfaceMap(interfaceType)' to get the corresponding target
// get/set accessor methods, and call 'Invoke(null, args)' on them. The target methods will
// be non-public in this case, which we always ignore.
// 3. The recommendation from .NET team is to ignore the static virtuals on interfaces,
// especially given that the APIs may change in .NET 7.
continue ;
}
PopulateSingleProperty ( type , property , tempTable , property . Name );
2016-03-30 14:20:52 -07:00
}
}
2021-08-14 07:56:14 -07:00
foreach ( KeyValuePair < string , List < PropertyInfo >> entry in tempTable )
2016-03-30 14:20:52 -07:00
{
2021-08-14 07:56:14 -07:00
List < PropertyInfo > propertiesList = entry . Value ;
2016-03-30 14:20:52 -07:00
PropertyInfo firstProperty = propertiesList [ 0 ];
if (( propertiesList . Count > 1 ) || ( firstProperty . GetIndexParameters (). Length != 0 ))
{
2021-08-14 07:56:14 -07:00
typeProperties . Add ( entry . Key , new ParameterizedPropertyCacheEntry ( propertiesList ));
2016-03-30 14:20:52 -07:00
}
else
{
2021-08-14 07:56:14 -07:00
typeProperties . Add ( entry . Key , new PropertyCacheEntry ( firstProperty ));
2016-03-30 14:20:52 -07:00
}
}
2021-08-14 07:56:14 -07:00
FieldInfo [] fields = type . GetFields ( bindingFlags );
foreach ( FieldInfo field in fields )
2016-03-30 14:20:52 -07:00
{
2021-08-14 07:56:14 -07:00
string fieldName = field . Name ;
var previousMember = ( PropertyCacheEntry ) typeProperties [ fieldName ];
if ( previousMember == null )
2016-03-30 14:20:52 -07:00
{
2021-08-14 07:56:14 -07:00
typeProperties . Add ( fieldName , new PropertyCacheEntry ( field ));
}
else if (! string . Equals ( previousMember . member . Name , fieldName ))
{
// A property/field declared with 'new' in a derived class might appear twice, and it's OK to ignore
// the second property/field in that case.
// However, if the names of two properties/fields are different only in letter casing, then it's not
// CLS complaint and we throw an exception.
throw new ExtendedTypeSystemException (
"NotACLSComplaintField" ,
innerException : null ,
ExtendedTypeSystem . NotAClsCompliantFieldProperty ,
fieldName ,
type . FullName ,
previousMember . member . Name );
2016-03-30 14:20:52 -07:00
}
}
}
private static void PopulateSingleProperty ( Type type , PropertyInfo property , Dictionary < string , List < PropertyInfo >> tempTable , string propertyName )
{
List < PropertyInfo > previousPropertyEntry ;
if (! tempTable . TryGetValue ( propertyName , out previousPropertyEntry ))
{
previousPropertyEntry = new List < PropertyInfo > { property };
tempTable . Add ( propertyName , previousPropertyEntry );
}
else
{
var firstProperty = previousPropertyEntry [ 0 ];
2018-12-31 20:44:16 +05:00
if (! string . Equals ( property . Name , firstProperty . Name , StringComparison . Ordinal ))
2016-03-30 14:20:52 -07:00
{
throw new ExtendedTypeSystemException ( "NotACLSComplaintProperty" , null ,
ExtendedTypeSystem . NotAClsCompliantFieldProperty , property . Name , type . FullName , firstProperty . Name );
}
if ( PropertyAlreadyPresent ( previousPropertyEntry , property ))
{
return ;
}
previousPropertyEntry . Add ( property );
}
}
/// <summary>
/// Called from GetProperty and GetProperties to populate the
/// typeTable with all public properties and fields
/// of type.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="type">Type to load properties for.</param>
2016-03-30 14:20:52 -07:00
private static CacheTable GetStaticPropertyReflectionTable ( Type type )
{
2016-07-29 13:02:49 -07:00
lock ( s_staticPropertyCacheTable )
2016-03-30 14:20:52 -07:00
{
CacheTable typeTable = null ;
2016-07-29 13:02:49 -07:00
if ( s_staticPropertyCacheTable . TryGetValue ( type , out typeTable ))
2016-03-30 14:20:52 -07:00
{
return typeTable ;
}
typeTable = new CacheTable ();
PopulatePropertyReflectionTable ( type , typeTable , staticBindingFlags );
2016-07-29 13:02:49 -07:00
s_staticPropertyCacheTable [ type ] = typeTable ;
2016-03-30 14:20:52 -07:00
return typeTable ;
}
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Retrieves the table for static methods.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="type">Type to load methods for.</param>
2016-03-30 14:20:52 -07:00
private static CacheTable GetStaticMethodReflectionTable ( Type type )
{
2016-07-29 13:02:49 -07:00
lock ( s_staticMethodCacheTable )
2016-03-30 14:20:52 -07:00
{
CacheTable typeTable = null ;
2016-07-29 13:02:49 -07:00
if ( s_staticMethodCacheTable . TryGetValue ( type , out typeTable ))
2016-03-30 14:20:52 -07:00
{
return typeTable ;
}
typeTable = new CacheTable ();
PopulateMethodReflectionTable ( type , typeTable , staticBindingFlags );
2016-07-29 13:02:49 -07:00
s_staticMethodCacheTable [ type ] = typeTable ;
2016-03-30 14:20:52 -07:00
return typeTable ;
}
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Retrieves the table for static events.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="type">Type containing properties to load in typeTable.</param>
2016-03-30 14:20:52 -07:00
private static Dictionary < string , EventCacheEntry > GetStaticEventReflectionTable ( Type type )
{
2016-07-29 13:02:49 -07:00
lock ( s_staticEventCacheTable )
2016-03-30 14:20:52 -07:00
{
Dictionary < string , EventCacheEntry > typeTable ;
2016-07-29 13:02:49 -07:00
if ( s_staticEventCacheTable . TryGetValue ( type , out typeTable ))
2016-03-30 14:20:52 -07:00
{
return typeTable ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
typeTable = new Dictionary < string , EventCacheEntry >();
PopulateEventReflectionTable ( type , typeTable , staticBindingFlags );
2016-07-29 13:02:49 -07:00
s_staticEventCacheTable [ type ] = typeTable ;
2016-03-30 14:20:52 -07:00
return typeTable ;
}
}
/// <summary>
/// Called from GetProperty and GetProperties to populate the
/// typeTable with all public properties and fields
/// of type.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="type">Type with properties to load in typeTable.</param>
2016-03-30 14:20:52 -07:00
private static CacheTable GetInstancePropertyReflectionTable ( Type type )
{
2016-07-29 13:02:49 -07:00
lock ( s_instancePropertyCacheTable )
2016-03-30 14:20:52 -07:00
{
CacheTable typeTable = null ;
2016-07-29 13:02:49 -07:00
if ( s_instancePropertyCacheTable . TryGetValue ( type , out typeTable ))
2016-03-30 14:20:52 -07:00
{
return typeTable ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
typeTable = new CacheTable ();
PopulatePropertyReflectionTable ( type , typeTable , instanceBindingFlags );
2016-07-29 13:02:49 -07:00
s_instancePropertyCacheTable [ type ] = typeTable ;
2016-03-30 14:20:52 -07:00
return typeTable ;
}
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Retrieves the table for instance methods.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="type">Type with methods to load in typeTable.</param>
2016-03-30 14:20:52 -07:00
private static CacheTable GetInstanceMethodReflectionTable ( Type type )
{
2016-07-29 13:02:49 -07:00
lock ( s_instanceMethodCacheTable )
2016-03-30 14:20:52 -07:00
{
CacheTable typeTable = null ;
2016-07-29 13:02:49 -07:00
if ( s_instanceMethodCacheTable . TryGetValue ( type , out typeTable ))
2016-03-30 14:20:52 -07:00
{
return typeTable ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
typeTable = new CacheTable ();
PopulateMethodReflectionTable ( type , typeTable , instanceBindingFlags );
2016-07-29 13:02:49 -07:00
s_instanceMethodCacheTable [ type ] = typeTable ;
2016-03-30 14:20:52 -07:00
return typeTable ;
}
}
internal IEnumerable < object > GetPropertiesAndMethods ( Type type , bool @static )
{
CacheTable propertyTable = @static
? GetStaticPropertyReflectionTable ( type )
: GetInstancePropertyReflectionTable ( type );
for ( int i = 0 ; i < propertyTable . memberCollection . Count ; i ++)
{
var propertyCacheEntry = propertyTable . memberCollection [ i ] as PropertyCacheEntry ;
if ( propertyCacheEntry != null )
yield return propertyCacheEntry . member ;
}
CacheTable methodTable = @static
? GetStaticMethodReflectionTable ( type )
: GetInstanceMethodReflectionTable ( type );
for ( int i = 0 ; i < methodTable . memberCollection . Count ; i ++)
{
var method = methodTable . memberCollection [ i ] as MethodCacheEntry ;
if ( method != null && ! method [ 0 ]. method . IsSpecialName )
{
yield return method ;
}
}
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Retrieves the table for instance events.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="type">Type containing methods to load in typeTable.</param>
2016-03-30 14:20:52 -07:00
private static Dictionary < string , EventCacheEntry > GetInstanceEventReflectionTable ( Type type )
{
2016-07-29 13:02:49 -07:00
lock ( s_instanceEventCacheTable )
2016-03-30 14:20:52 -07:00
{
Dictionary < string , EventCacheEntry > typeTable ;
2016-07-29 13:02:49 -07:00
if ( s_instanceEventCacheTable . TryGetValue ( type , out typeTable ))
2016-03-30 14:20:52 -07:00
{
return typeTable ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
typeTable = new Dictionary < string , EventCacheEntry >( StringComparer . OrdinalIgnoreCase );
PopulateEventReflectionTable ( type , typeTable , instanceBindingFlags );
2016-07-29 13:02:49 -07:00
s_instanceEventCacheTable [ type ] = typeTable ;
2016-03-30 14:20:52 -07:00
return typeTable ;
}
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if a parameterized property should be in a PSMemberInfoCollection of type t.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="t">Type of a PSMemberInfoCollection like the type of T in PSMemberInfoCollection of T.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if a parameterized property should be in a collection.</returns>
2016-03-30 14:20:52 -07:00
/// <remarks>
/// Usually typeof(T).IsAssignableFrom(typeof(PSParameterizedProperty)) would work like it does
/// for PSMethod and PSProperty, but since PSParameterizedProperty derives from PSMethodInfo and
/// since we don't want to have ParameterizedProperties in PSMemberInfoCollection of PSMethodInfo
/// we need this method.
/// </remarks>
internal static bool IsTypeParameterizedProperty ( Type t )
{
return t == typeof ( PSMemberInfo ) || t == typeof ( PSParameterizedProperty );
}
2019-03-21 02:43:52 +01:00
private T GetDotNetPropertyImpl < T >( object obj , string propertyName , MemberNamePredicate predicate ) where T : PSMemberInfo
2016-03-30 14:20:52 -07:00
{
bool lookingForProperties = typeof ( T ). IsAssignableFrom ( typeof ( PSProperty ));
bool lookingForParameterizedProperties = IsTypeParameterizedProperty ( typeof ( T ));
if (! lookingForProperties && ! lookingForParameterizedProperties )
{
return null ;
}
2016-07-29 13:02:49 -07:00
CacheTable typeTable = _isStatic
2019-03-21 02:43:52 +01:00
? GetStaticPropertyReflectionTable (( Type ) obj )
: GetInstancePropertyReflectionTable ( obj . GetType ());
2016-03-30 14:20:52 -07:00
2019-03-21 02:43:52 +01:00
object entry = predicate != null
? typeTable . GetFirstOrDefault ( predicate )
: typeTable [ propertyName ];
switch ( entry )
2016-03-30 14:20:52 -07:00
{
2019-04-24 19:27:22 +05:00
case null :
return null ;
2019-03-21 02:43:52 +01:00
case PropertyCacheEntry cacheEntry when lookingForProperties :
2019-12-11 15:37:18 -08:00
return new PSProperty ( cacheEntry . member . Name , this , obj , cacheEntry ) { IsHidden = cacheEntry . IsHidden } as T ;
2019-03-21 02:43:52 +01:00
case ParameterizedPropertyCacheEntry paramCacheEntry when lookingForParameterizedProperties :
2016-03-30 14:20:52 -07:00
2019-03-21 02:43:52 +01:00
// TODO: check for HiddenAttribute
// We can't currently write a parameterized property in a PowerShell class so this isn't too important,
// but if someone added the attribute to their C#, it'd be good to set isHidden correctly here.
return new PSParameterizedProperty ( paramCacheEntry . propertyName , this , obj , paramCacheEntry ) as T ;
default : return null ;
2016-03-30 14:20:52 -07:00
}
}
2019-03-21 02:43:52 +01:00
private T GetDotNetMethodImpl < T >( object obj , string methodName , MemberNamePredicate predicate ) where T : PSMemberInfo
2016-03-30 14:20:52 -07:00
{
if (! typeof ( T ). IsAssignableFrom ( typeof ( PSMethod )))
{
return null ;
}
2016-07-29 13:02:49 -07:00
CacheTable typeTable = _isStatic
2019-03-21 02:43:52 +01:00
? GetStaticMethodReflectionTable (( Type ) obj )
: GetInstanceMethodReflectionTable ( obj . GetType ());
var methods = predicate != null
? ( MethodCacheEntry ) typeTable . GetFirstOrDefault ( predicate )
: ( MethodCacheEntry ) typeTable [ methodName ];
2016-03-30 14:20:52 -07:00
2020-07-30 16:06:38 -07:00
if ( methods == null )
2016-03-30 14:20:52 -07:00
{
return null ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
var isCtor = methods [ 0 ]. method is ConstructorInfo ;
bool isSpecial = ! isCtor && methods [ 0 ]. method . IsSpecialName ;
2018-12-23 22:20:06 -08:00
2019-12-11 15:37:18 -08:00
return PSMethod . Create ( methods [ 0 ]. method . Name , this , obj , methods , isSpecial , methods . IsHidden ) as T ;
2016-03-30 14:20:52 -07:00
}
2019-03-21 02:43:52 +01:00
internal T GetDotNetProperty < T >( object obj , string propertyName ) where T : PSMemberInfo
{
return GetDotNetPropertyImpl < T >( obj , propertyName , predicate : null );
}
internal T GetDotNetMethod < T >( object obj , string methodName ) where T : PSMemberInfo
{
return GetDotNetMethodImpl < T >( obj , methodName , predicate : null );
}
protected T GetFirstDotNetPropertyOrDefault < T >( object obj , MemberNamePredicate predicate ) where T : PSMemberInfo
{
return GetDotNetPropertyImpl < T >( obj , propertyName : null , predicate );
}
protected T GetFirstDotNetMethodOrDefault < T >( object obj , MemberNamePredicate predicate ) where T : PSMemberInfo
{
return GetDotNetMethodImpl < T >( obj , methodName : null , predicate );
}
protected T GetFirstDotNetEventOrDefault < T >( object obj , MemberNamePredicate predicate ) where T : PSMemberInfo
{
if (! typeof ( T ). IsAssignableFrom ( typeof ( PSEvent )))
{
return null ;
}
var table = _isStatic
? GetStaticEventReflectionTable (( Type ) obj )
: GetInstanceEventReflectionTable ( obj . GetType ());
foreach ( var psEvent in table . Values )
{
if ( predicate ( psEvent . events [ 0 ]. Name ))
{
return new PSEvent ( psEvent . events [ 0 ]) as T ;
}
}
return null ;
}
protected T GetFirstDynamicMemberOrDefault < T >( object obj , MemberNamePredicate predicate ) where T : PSMemberInfo
{
var idmop = obj as IDynamicMetaObjectProvider ;
2020-07-30 16:06:38 -07:00
if ( idmop == null || obj is PSObject )
2019-03-21 02:43:52 +01:00
{
return null ;
}
if (! typeof ( T ). IsAssignableFrom ( typeof ( PSDynamicMember )))
{
return null ;
}
foreach ( var name in idmop . GetMetaObject ( Expression . Variable ( idmop . GetType ())). GetDynamicMemberNames ())
{
if ( predicate ( name ))
{
return new PSDynamicMember ( name ) as T ;
}
}
return null ;
}
2016-07-29 13:02:49 -07:00
internal void AddAllProperties < T >( object obj , PSMemberInfoInternalCollection < T > members , bool ignoreDuplicates ) where T : PSMemberInfo
2016-03-30 14:20:52 -07:00
{
bool lookingForProperties = typeof ( T ). IsAssignableFrom ( typeof ( PSProperty ));
bool lookingForParameterizedProperties = IsTypeParameterizedProperty ( typeof ( T ));
if (! lookingForProperties && ! lookingForParameterizedProperties )
{
return ;
}
2016-07-29 13:02:49 -07:00
CacheTable table = _isStatic
2016-03-30 14:20:52 -07:00
? GetStaticPropertyReflectionTable (( Type ) obj )
2016-04-28 13:41:25 -07:00
: GetInstancePropertyReflectionTable ( obj . GetType ());
2016-03-30 14:20:52 -07:00
for ( int i = 0 ; i < table . memberCollection . Count ; i ++)
{
2019-03-21 02:43:52 +01:00
if ( table . memberCollection [ i ] is PropertyCacheEntry propertyEntry )
2016-03-30 14:20:52 -07:00
{
if ( lookingForProperties )
{
2020-07-30 16:06:38 -07:00
if (! ignoreDuplicates || ( members [ propertyEntry . member . Name ] == null ))
2016-03-30 14:20:52 -07:00
{
2019-12-11 15:37:18 -08:00
members . Add (
new PSProperty (
name : propertyEntry . member . Name ,
adapter : this ,
baseObject : obj ,
2020-01-31 22:56:22 +00:00
adapterData : propertyEntry )
{ IsHidden = propertyEntry . IsHidden } as T );
2016-03-30 14:20:52 -07:00
}
}
}
else if ( lookingForParameterizedProperties )
{
var parameterizedPropertyEntry = ( ParameterizedPropertyCacheEntry ) table . memberCollection [ i ];
2020-07-30 16:06:38 -07:00
if (! ignoreDuplicates || ( members [ parameterizedPropertyEntry . propertyName ] == null ))
2016-03-30 14:20:52 -07:00
{
// TODO: check for HiddenAttribute
// We can't currently write a parameterized property in a PowerShell class so this isn't too important,
// but if someone added the attribute to their C#, it'd be good to set isHidden correctly here.
members . Add ( new PSParameterizedProperty ( parameterizedPropertyEntry . propertyName ,
this , obj , parameterizedPropertyEntry ) as T );
}
}
}
}
2016-07-29 13:02:49 -07:00
internal void AddAllMethods < T >( object obj , PSMemberInfoInternalCollection < T > members , bool ignoreDuplicates ) where T : PSMemberInfo
2016-03-30 14:20:52 -07:00
{
if (! typeof ( T ). IsAssignableFrom ( typeof ( PSMethod )))
{
return ;
}
2016-07-29 13:02:49 -07:00
CacheTable table = _isStatic
2016-03-30 14:20:52 -07:00
? GetStaticMethodReflectionTable (( Type ) obj )
2016-04-28 13:41:25 -07:00
: GetInstanceMethodReflectionTable ( obj . GetType ());
2016-03-30 14:20:52 -07:00
for ( int i = 0 ; i < table . memberCollection . Count ; i ++)
{
var method = ( MethodCacheEntry ) table . memberCollection [ i ];
var isCtor = method [ 0 ]. method is ConstructorInfo ;
var name = isCtor ? "new" : method [ 0 ]. method . Name ;
2020-07-30 16:06:38 -07:00
if (! ignoreDuplicates || ( members [ name ] == null ))
2016-03-30 14:20:52 -07:00
{
bool isSpecial = ! isCtor && method [ 0 ]. method . IsSpecialName ;
2019-12-11 15:37:18 -08:00
members . Add ( PSMethod . Create ( name , this , obj , method , isSpecial , method . IsHidden ) as T );
2016-03-30 14:20:52 -07:00
}
}
}
internal void AddAllEvents < T >( object obj , PSMemberInfoInternalCollection < T > members , bool ignoreDuplicates ) where T : PSMemberInfo
{
if (! typeof ( T ). IsAssignableFrom ( typeof ( PSEvent )))
{
return ;
}
2016-07-29 13:02:49 -07:00
var table = _isStatic
2016-03-30 14:20:52 -07:00
? GetStaticEventReflectionTable (( Type ) obj )
2016-04-28 13:41:25 -07:00
: GetInstanceEventReflectionTable ( obj . GetType ());
2016-03-30 14:20:52 -07:00
foreach ( var psEvent in table . Values )
{
2020-07-30 16:06:38 -07:00
if (! ignoreDuplicates || ( members [ psEvent . events [ 0 ]. Name ] == null ))
2016-03-30 14:20:52 -07:00
{
members . Add ( new PSEvent ( psEvent . events [ 0 ]) as T );
}
}
}
internal void AddAllDynamicMembers < T >( object obj , PSMemberInfoInternalCollection < T > members , bool ignoreDuplicates ) where T : PSMemberInfo
{
var idmop = obj as IDynamicMetaObjectProvider ;
2020-07-30 16:06:38 -07:00
if ( idmop == null || obj is PSObject )
2016-03-30 14:20:52 -07:00
{
return ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
if (! typeof ( T ). IsAssignableFrom ( typeof ( PSDynamicMember )))
{
return ;
}
foreach ( var name in idmop . GetMetaObject ( Expression . Variable ( idmop . GetType ())). GetDynamicMemberNames ())
{
members . Add ( new PSDynamicMember ( name ) as T );
}
}
private static bool PropertyIsStatic ( PSProperty property )
{
2020-11-05 16:58:48 +00:00
if (!( property . adapterData is PropertyCacheEntry entry ))
2016-03-30 14:20:52 -07:00
{
return false ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return entry . isStatic ;
}
2022-06-27 22:21:25 +03:00
/// <summary>
/// Get the string representation of the default value of passed-in parameter.
/// </summary>
/// <param name="parameterInfo">ParameterInfo containing the parameter's default value.</param>
/// <returns>String representation of the parameter's default value.</returns>
private static string GetDefaultValueStringRepresentation ( ParameterInfo parameterInfo )
{
var parameterType = parameterInfo . ParameterType ;
var parameterDefaultValue = parameterInfo . DefaultValue ;
if ( parameterDefaultValue == null )
{
return ( parameterType . IsValueType || parameterType . IsGenericMethodParameter )
? "default"
: "null" ;
}
if ( parameterType . IsEnum )
{
return string . Format ( CultureInfo . InvariantCulture , "{0}.{1}" , parameterType . ToString (), parameterDefaultValue . ToString ());
}
return ( parameterDefaultValue is string )
? string . Format ( CultureInfo . InvariantCulture , "\"{0}\"" , parameterDefaultValue . ToString ())
: parameterDefaultValue . ToString ();
}
2016-08-28 05:44:55 +00:00
#endregion auxiliary methods and classes
2016-03-30 14:20:52 -07:00
#region virtual
#region member
2018-03-15 15:09:38 -07:00
internal override bool CanSiteBinderOptimize ( MemberTypes typeToOperateOn ) { return true ; }
2016-03-30 14:20:52 -07:00
2020-11-05 03:36:23 +00:00
private static readonly ConcurrentDictionary < Type , ConsolidatedString > s_typeToTypeNameDictionary =
2016-03-30 14:20:52 -07:00
new ConcurrentDictionary < Type , ConsolidatedString >();
internal static ConsolidatedString GetInternedTypeNameHierarchy ( Type type )
{
2016-07-29 13:02:49 -07:00
return s_typeToTypeNameDictionary . GetOrAdd ( type ,
2016-03-30 14:20:52 -07:00
t => new ConsolidatedString ( GetDotNetTypeNameHierarchy ( t ), interned : true ));
}
protected override ConsolidatedString GetInternedTypeNameHierarchy ( object obj )
{
return GetInternedTypeNameHierarchy ( obj . GetType ());
}
/// <summary>
2018-06-20 00:23:55 -07:00
/// Get the .NET member based on the given member name.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-06-20 00:23:55 -07:00
/// <remark>
/// Dynamic members of an object that implements IDynamicMetaObjectProvider are not included because
/// 1. Dynamic members cannot be invoked via reflection;
/// 2. Access to dynamic members is handled by the DLR for free.
/// </remark>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to retrieve the PSMemberInfo from.</param>
/// <param name="memberName">Name of the member to be retrieved.</param>
2018-06-20 00:23:55 -07:00
/// <returns>
/// The PSMemberInfo corresponding to memberName from obj,
/// or null if the given member name is not a member in the adapter.
/// </returns>
2016-03-30 14:20:52 -07:00
protected override T GetMember < T >( object obj , string memberName )
{
T returnValue = GetDotNetProperty < T >( obj , memberName );
if ( returnValue != null ) return returnValue ;
return GetDotNetMethod < T >( obj , memberName );
}
2019-03-21 02:43:52 +01:00
/// <summary>
/// Get the first .NET member whose name matches the specified <see cref="MemberNamePredicate"/>.
/// </summary>
protected override T GetFirstMemberOrDefault < T >( object obj , MemberNamePredicate predicate )
{
return GetFirstDotNetPropertyOrDefault < T >( obj , predicate ) ?? GetFirstDotNetMethodOrDefault < T >( obj , predicate ) ??
GetFirstDotNetEventOrDefault < T >( obj , predicate ) ?? GetFirstDynamicMemberOrDefault < T >( obj , predicate );
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Retrieves all the members available in the object.
/// The adapter implementation is encouraged to cache all properties/methods available
/// in the first call to GetMember and GetMembers so that subsequent
/// calls can use the cache.
/// In the case of the .NET adapter that would be a cache from the .NET type to
2017-10-27 05:12:19 +08:00
/// the public properties and fields available in that type.
2016-03-30 14:20:52 -07:00
/// In the case of the DirectoryEntry adapter, this could be a cache of the objectClass
/// to the properties available in it.
/// </summary>
2018-06-20 00:23:55 -07:00
/// <remark>
/// Dynamic members of an object that implements IDynamicMetaObjectProvider are included because
/// we want to view the dynamic members via 'Get-Member' and be able to auto-complete those members.
/// </remark>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to get all the member information from.</param>
2018-12-25 21:43:03 +05:00
/// <returns>All members in obj.</returns>
2016-03-30 14:20:52 -07:00
protected override PSMemberInfoInternalCollection < T > GetMembers < T >( object obj )
{
PSMemberInfoInternalCollection < T > returnValue = new PSMemberInfoInternalCollection < T >();
AddAllProperties < T >( obj , returnValue , false );
AddAllMethods < T >( obj , returnValue , false );
AddAllEvents < T >( obj , returnValue , false );
AddAllDynamicMembers ( obj , returnValue , false );
return returnValue ;
}
#endregion member
#region property
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns an array with the property attributes.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property we want the attributes from.</param>
2018-12-25 21:43:03 +05:00
/// <returns>An array with the property attributes.</returns>
2016-03-30 14:20:52 -07:00
protected override AttributeCollection PropertyAttributes ( PSProperty property )
{
PropertyCacheEntry adapterData = ( PropertyCacheEntry ) property . adapterData ;
return adapterData . Attributes ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the string representation of the property in the object.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property obtained in a previous GetMember.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The string representation of the property in the object.</returns>
2016-03-30 14:20:52 -07:00
protected override string PropertyToString ( PSProperty property )
{
StringBuilder returnValue = new StringBuilder ();
if ( PropertyIsStatic ( property ))
{
returnValue . Append ( "static " );
}
returnValue . Append ( PropertyType ( property , forDisplay : true ));
2020-10-30 07:07:18 +00:00
returnValue . Append ( ' ' );
2016-03-30 14:20:52 -07:00
returnValue . Append ( property . Name );
returnValue . Append ( " {" );
if ( PropertyIsGettable ( property ))
{
returnValue . Append ( "get;" );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
if ( PropertyIsSettable ( property ))
{
returnValue . Append ( "set;" );
}
2018-12-23 22:20:06 -08:00
2020-10-30 07:07:18 +00:00
returnValue . Append ( '}' );
2016-03-30 14:20:52 -07:00
return returnValue . ToString ();
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the value from a property coming from a previous call to GetMember.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="property">PSProperty coming from a previous call to GetMember.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The value of the property.</returns>
2016-03-30 14:20:52 -07:00
protected override object PropertyGet ( PSProperty property )
{
PropertyCacheEntry adapterData = ( PropertyCacheEntry ) property . adapterData ;
2018-08-26 12:50:58 -07:00
if ( adapterData . propertyType . IsByRefLike )
{
throw new GetValueException (
nameof ( ExtendedTypeSystem . CannotAccessByRefLikePropertyOrField ),
innerException : null ,
ExtendedTypeSystem . CannotAccessByRefLikePropertyOrField ,
adapterData . member . Name ,
adapterData . propertyType );
}
2016-03-30 14:20:52 -07:00
PropertyInfo propertyInfo = adapterData . member as PropertyInfo ;
if ( propertyInfo != null )
{
if ( adapterData . writeOnly )
{
2018-08-26 12:50:58 -07:00
throw new GetValueException (
nameof ( ExtendedTypeSystem . WriteOnlyProperty ),
innerException : null ,
2016-03-30 14:20:52 -07:00
ExtendedTypeSystem . WriteOnlyProperty ,
propertyInfo . Name );
}
2018-08-26 12:50:58 -07:00
2016-03-30 14:20:52 -07:00
if ( adapterData . useReflection )
{
return propertyInfo . GetValue ( property . baseObject , null );
}
else
{
return adapterData . getterDelegate ( property . baseObject );
}
}
FieldInfo field = adapterData . member as FieldInfo ;
if ( adapterData . useReflection )
{
2019-02-13 16:51:05 +05:00
return field ?. GetValue ( property . baseObject );
2016-03-30 14:20:52 -07:00
}
else
{
return adapterData . getterDelegate ( property . baseObject );
}
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Sets the value of a property coming from a previous call to GetMember.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="property">PSProperty coming from a previous call to GetMember.</param>
2018-12-29 08:25:49 +05:00
/// <param name="setValue">Value to set the property with.</param>
/// <param name="convertIfPossible">Instructs the adapter to convert before setting, if the adapter supports conversion.</param>
2016-03-30 14:20:52 -07:00
protected override void PropertySet ( PSProperty property , object setValue , bool convertIfPossible )
{
PropertyCacheEntry adapterData = ( PropertyCacheEntry ) property . adapterData ;
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
if ( adapterData . readOnly )
{
2018-08-26 12:50:58 -07:00
throw new SetValueException (
nameof ( ExtendedTypeSystem . ReadOnlyProperty ),
innerException : null ,
2016-03-30 14:20:52 -07:00
ExtendedTypeSystem . ReadOnlyProperty ,
adapterData . member . Name );
}
2018-08-26 12:50:58 -07:00
if ( adapterData . propertyType . IsByRefLike )
{
throw new SetValueException (
nameof ( ExtendedTypeSystem . CannotAccessByRefLikePropertyOrField ),
innerException : null ,
ExtendedTypeSystem . CannotAccessByRefLikePropertyOrField ,
adapterData . member . Name ,
adapterData . propertyType );
}
2016-03-30 14:20:52 -07:00
PropertyInfo propertyInfo = adapterData . member as PropertyInfo ;
if ( propertyInfo != null )
2016-07-29 13:02:49 -07:00
{
2016-03-30 14:20:52 -07:00
if ( convertIfPossible )
{
setValue = PropertySetAndMethodArgumentConvertTo ( setValue , propertyInfo . PropertyType , CultureInfo . InvariantCulture );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
if ( adapterData . useReflection )
{
propertyInfo . SetValue ( property . baseObject , setValue , null );
}
else
{
adapterData . setterDelegate ( property . baseObject , setValue );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return ;
}
FieldInfo field = adapterData . member as FieldInfo ;
if ( convertIfPossible )
{
setValue = PropertySetAndMethodArgumentConvertTo ( setValue , field . FieldType , CultureInfo . InvariantCulture );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
if ( adapterData . useReflection )
{
field . SetValue ( property . baseObject , setValue );
}
else
{
adapterData . setterDelegate ( property . baseObject , setValue );
}
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if the property is settable.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if the property is settable.</returns>
2016-03-30 14:20:52 -07:00
protected override bool PropertyIsSettable ( PSProperty property )
{
return !(( PropertyCacheEntry ) property . adapterData ). readOnly ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if the property is gettable.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if the property is gettable.</returns>
2016-03-30 14:20:52 -07:00
protected override bool PropertyIsGettable ( PSProperty property )
{
return !(( PropertyCacheEntry ) property . adapterData ). writeOnly ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the name of the type corresponding to the property's value.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="property">PSProperty obtained in a previous GetMember.</param>
/// <param name="forDisplay">True if the result is for display purposes only.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The name of the type corresponding to the member.</returns>
2016-03-30 14:20:52 -07:00
protected override string PropertyType ( PSProperty property , bool forDisplay )
{
var propertyType = (( PropertyCacheEntry ) property . adapterData ). propertyType ;
return forDisplay ? ToStringCodeMethods . Type ( propertyType ) : propertyType . FullName ;
}
#endregion property
#region method
2016-08-28 05:44:55 +00:00
#region auxiliary to method calling
2016-03-30 14:20:52 -07:00
/// <summary>
2019-01-06 04:40:24 +05:00
/// Calls constructor using the arguments and catching the appropriate exception.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="arguments">Final arguments to the constructor.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The return of the constructor.</returns>
2016-03-30 14:20:52 -07:00
/// <param name="methodInformation">Information about the method to call. Used for setting references.</param>
/// <param name="originalArguments">Original arguments in the method call. Used for setting references.</param>
2018-12-29 08:25:49 +05:00
/// <exception cref="MethodInvocationException">If the constructor throws an exception.</exception>
2016-03-30 14:20:52 -07:00
internal static object AuxiliaryConstructorInvoke ( MethodInformation methodInformation , object [] arguments , object [] originalArguments )
{
object returnValue ;
#pragma warning disable 56500
try
{
2018-08-26 12:50:58 -07:00
returnValue = methodInformation . Invoke ( target : null , arguments );
2016-03-30 14:20:52 -07:00
}
catch ( TargetInvocationException ex )
{
2016-08-02 22:25:13 -07:00
Exception inner = ex . InnerException ?? ex ;
2016-03-30 14:20:52 -07:00
throw new MethodInvocationException (
"DotNetconstructorTargetInvocation" ,
inner ,
ExtendedTypeSystem . MethodInvocationException ,
".ctor" , arguments . Length , inner . Message );
}
catch ( Exception e )
{
throw new MethodInvocationException (
"DotNetconstructorException" ,
e ,
ExtendedTypeSystem . MethodInvocationException ,
".ctor" , arguments . Length , e . Message );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
SetReferences ( arguments , methodInformation , originalArguments );
return returnValue ;
#pragma warning restore 56500
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Calls method on target using the arguments and catching the appropriate exception.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="target">Object we want to call the method on.</param>
/// <param name="arguments">Final arguments to the method.</param>
2016-03-30 14:20:52 -07:00
/// <param name="methodInformation">Information about the method to call. Used for setting references.</param>
/// <param name="originalArguments">Original arguments in the method call. Used for setting references.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The return of the method.</returns>
2018-12-29 08:25:49 +05:00
/// <exception cref="MethodInvocationException">If the method throws an exception.</exception>
2016-03-30 14:20:52 -07:00
internal static object AuxiliaryMethodInvoke ( object target , object [] arguments , MethodInformation methodInformation , object [] originalArguments )
{
object result ;
#pragma warning disable 56500
try
{
// call the method and return the result unless the return type is void in which
// case we'll return AutomationNull.Value
result = methodInformation . Invoke ( target , arguments );
}
catch ( TargetInvocationException ex )
{
// Special handling to allow methods to throw flowcontrol exceptions
// Needed for ExitNestedPrompt exception.
if ( ex . InnerException is FlowControlException || ex . InnerException is ScriptCallDepthException )
throw ex . InnerException ;
// Win7:138054 - When wrapping cmdlets, we want the original exception to be raised,
// not the wrapped exception that occurs from invoking a steppable pipeline method.
if ( ex . InnerException is ParameterBindingException )
throw ex . InnerException ;
2016-08-02 22:25:13 -07:00
Exception inner = ex . InnerException ?? ex ;
2016-03-30 14:20:52 -07:00
throw new MethodInvocationException (
"DotNetMethodTargetInvocation" ,
inner ,
ExtendedTypeSystem . MethodInvocationException ,
methodInformation . method . Name , arguments . Length , inner . Message );
}
//
2017-10-27 05:12:19 +08:00
// Note that FlowControlException, ScriptCallDepthException and ParameterBindingException will be wrapped in
2016-03-30 14:20:52 -07:00
// a TargetInvocationException only when the invocation uses reflection so we need to bubble them up here as well.
//
catch ( ParameterBindingException ) { throw ; }
2016-07-29 13:02:49 -07:00
catch ( FlowControlException ) { throw ; }
2016-03-30 14:20:52 -07:00
catch ( ScriptCallDepthException ) { throw ; }
catch ( PipelineStoppedException ) { throw ; }
catch ( Exception e )
{
if ( methodInformation . method . DeclaringType == typeof ( SteppablePipeline ) &&
( methodInformation . method . Name . Equals ( "Begin" ) ||
methodInformation . method . Name . Equals ( "Process" ) ||
methodInformation . method . Name . Equals ( "End" )))
{
// Don't wrap exceptions that happen when calling methods on SteppablePipeline
// that are only used for proxy commands.
throw ;
}
throw new MethodInvocationException (
"DotNetMethodException" ,
e ,
ExtendedTypeSystem . MethodInvocationException ,
methodInformation . method . Name , arguments . Length , e . Message );
}
#pragma warning restore 56500
SetReferences ( arguments , methodInformation , originalArguments );
MethodInfo methodInfo = methodInformation . method as MethodInfo ;
if ( methodInfo != null && methodInfo . ReturnType != typeof ( void ))
return result ;
return AutomationNull . Value ;
}
/// <summary>
/// Converts a MethodBase[] into a MethodInformation[]
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="methods">The methods to be converted.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The MethodInformation[] corresponding to methods.</returns>
2021-08-14 07:56:14 -07:00
internal static MethodInformation [] GetMethodInformationArray ( IList < MethodBase > methods )
2016-03-30 14:20:52 -07:00
{
2021-08-14 07:56:14 -07:00
var returnValue = new MethodInformation [ methods . Count ];
for ( int i = 0 ; i < methods . Count ; i ++)
2016-03-30 14:20:52 -07:00
{
returnValue [ i ] = new MethodInformation ( methods [ i ], 0 );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return returnValue ;
}
/// <summary>
/// Calls the method best suited to the arguments on target.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="methodName">Used for error messages.</param>
/// <param name="target">Object to call the method on.</param>
/// <param name="methodInformation">Method information corresponding to methods.</param>
/// <param name="invocationConstraints">Invocation constraints.</param>
/// <param name="arguments">Arguments of the call.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The return of the method.</returns>
2018-12-29 08:25:49 +05:00
/// <exception cref="MethodInvocationException">If the method throws an exception.</exception>
/// <exception cref="MethodException">If we could not find a method for the given arguments.</exception>
2016-03-30 14:20:52 -07:00
internal static object MethodInvokeDotNet (
2016-07-29 13:02:49 -07:00
string methodName ,
object target ,
MethodInformation [] methodInformation ,
2016-03-30 14:20:52 -07:00
PSMethodInvocationConstraints invocationConstraints ,
object [] arguments )
{
object [] newArguments ;
MethodInformation bestMethod = GetBestMethodAndArguments ( methodName , methodInformation , invocationConstraints , arguments , out newArguments );
if ( bestMethod . method is ConstructorInfo )
{
return InvokeResolvedConstructor ( bestMethod , newArguments , arguments );
}
string methodDefinition = bestMethod . methodDefinition ;
ScriptTrace . Trace ( 1 , "TraceMethodCall" , ParserStrings . TraceMethodCall , methodDefinition );
2019-03-21 02:43:52 +01:00
PSObject . MemberResolution . WriteLine ( "Calling Method: {0}" , methodDefinition );
2016-03-30 14:20:52 -07:00
return AuxiliaryMethodInvoke ( target , newArguments , bestMethod , arguments );
}
/// <summary>
/// Calls the method best suited to the arguments on target.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="type">The type being constructed, used for diagnostics and caching.</param>
/// <param name="constructors">All overloads for the constructors.</param>
/// <param name="arguments">Arguments of the call.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The return of the method.</returns>
2018-12-29 08:25:49 +05:00
/// <exception cref="MethodInvocationException">If the method throws an exception.</exception>
/// <exception cref="MethodException">If we could not find a method for the given arguments.</exception>
2016-03-30 14:20:52 -07:00
internal static object ConstructorInvokeDotNet ( Type type , ConstructorInfo [] constructors , object [] arguments )
{
var newConstructors = GetMethodInformationArray ( constructors );
object [] newArguments ;
MethodInformation bestMethod = GetBestMethodAndArguments ( type . Name , newConstructors , arguments , out newArguments );
return InvokeResolvedConstructor ( bestMethod , newArguments , arguments );
}
private static object InvokeResolvedConstructor ( MethodInformation bestMethod , object [] newArguments , object [] arguments )
{
2019-03-21 02:43:52 +01:00
if (( PSObject . MemberResolution . Options & PSTraceSourceOptions . WriteLine ) != 0 )
2016-03-30 14:20:52 -07:00
{
2019-03-21 02:43:52 +01:00
PSObject . MemberResolution . WriteLine ( "Calling Constructor: {0}" , DotNetAdapter . GetMethodInfoOverloadDefinition ( null ,
2016-03-30 14:20:52 -07:00
bestMethod . method , 0 ));
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return AuxiliaryConstructorInvoke ( bestMethod , newArguments , arguments );
}
/// <summary>
2019-01-10 02:07:41 +05:00
/// This is a flavor of MethodInvokeDotNet to deal with a peculiarity of property setters:
2017-10-27 05:12:19 +08:00
/// Tthe setValue is always the last parameter. This enables a parameter after a varargs or optional
2016-03-30 14:20:52 -07:00
/// parameters and GetBestMethodAndArguments is not prepared for that.
/// This method disregards the last parameter in its call to GetBestMethodAndArguments used in this case
/// more for its "Arguments" side than for its "BestMethod" side, since there is only one method.
/// </summary>
internal static void ParameterizedPropertyInvokeSet ( string propertyName , object target , object valuetoSet , MethodInformation [] methodInformation , object [] arguments )
{
// bestMethodIndex is ignored since we know we have only 1 method. GetBestMethodAndArguments
// is still useful to deal with optional and varargs parameters and to perform the type conversions
// of all parameters but the last one
object [] newArguments ;
MethodInformation bestMethod = GetBestMethodAndArguments ( propertyName , methodInformation , arguments , out newArguments );
2019-03-21 02:43:52 +01:00
PSObject . MemberResolution . WriteLine ( "Calling Set Method: {0}" , bestMethod . methodDefinition );
2016-03-30 14:20:52 -07:00
ParameterInfo [] bestMethodParameters = bestMethod . method . GetParameters ();
Type propertyType = bestMethodParameters [ bestMethodParameters . Length - 1 ]. ParameterType ;
// we have to convert the last parameter (valuetoSet) manually since it has been
// disregarded in GetBestMethodAndArguments.
object lastArgument ;
try
{
lastArgument = PropertySetAndMethodArgumentConvertTo ( valuetoSet , propertyType , CultureInfo . InvariantCulture );
}
catch ( InvalidCastException e )
{
// NTRAID#Windows Out Of Band Releases-924162-2005/11/17-JonN
throw new MethodException (
"PropertySetterConversionInvalidCastArgument" ,
e ,
ExtendedTypeSystem . MethodArgumentConversionException ,
arguments . Length - 1 , valuetoSet , propertyName , propertyType , e . Message );
}
// and we also have to rebuild the argument array to include the last parameter
object [] finalArguments = new object [ newArguments . Length + 1 ];
for ( int i = 0 ; i < newArguments . Length ; i ++)
{
finalArguments [ i ] = newArguments [ i ];
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
finalArguments [ newArguments . Length ] = lastArgument ;
AuxiliaryMethodInvoke ( target , finalArguments , bestMethod , arguments );
}
internal static string GetMethodInfoOverloadDefinition ( string memberName , MethodBase methodEntry , int parametersToIgnore )
{
StringBuilder builder = new StringBuilder ();
if ( methodEntry . IsStatic )
{
builder . Append ( "static " );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
MethodInfo method = methodEntry as MethodInfo ;
if ( method != null )
{
builder . Append ( ToStringCodeMethods . Type ( method . ReturnType ));
2020-10-30 07:07:18 +00:00
builder . Append ( ' ' );
2016-03-30 14:20:52 -07:00
}
else
{
ConstructorInfo ctorInfo = methodEntry as ConstructorInfo ;
if ( ctorInfo != null )
{
builder . Append ( ToStringCodeMethods . Type ( ctorInfo . DeclaringType ));
2020-10-30 07:07:18 +00:00
builder . Append ( ' ' );
2016-03-30 14:20:52 -07:00
}
}
2018-12-23 22:20:06 -08:00
2018-04-13 15:01:41 -07:00
if ( methodEntry . DeclaringType . IsInterface )
2016-03-30 14:20:52 -07:00
{
builder . Append ( ToStringCodeMethods . Type ( methodEntry . DeclaringType , dropNamespaces : true ));
2020-10-30 07:07:18 +00:00
builder . Append ( '.' );
2016-03-30 14:20:52 -07:00
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
builder . Append ( memberName ?? methodEntry . Name );
if ( methodEntry . IsGenericMethodDefinition )
{
2020-10-30 07:07:18 +00:00
builder . Append ( '[' );
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
Type [] genericArgs = methodEntry . GetGenericArguments ();
for ( int i = 0 ; i < genericArgs . Length ; i ++)
{
if ( i > 0 ) { builder . Append ( ", " ); }
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
builder . Append ( ToStringCodeMethods . Type ( genericArgs [ i ]));
}
2020-10-30 07:07:18 +00:00
builder . Append ( ']' );
2016-03-30 14:20:52 -07:00
}
2018-12-23 22:20:06 -08:00
2020-10-30 07:07:18 +00:00
builder . Append ( '(' );
2016-03-30 14:20:52 -07:00
System . Reflection . ParameterInfo [] parameters = methodEntry . GetParameters ();
int parametersLength = parameters . Length - parametersToIgnore ;
if ( parametersLength > 0 )
{
for ( int i = 0 ; i < parametersLength ; i ++)
{
System . Reflection . ParameterInfo parameter = parameters [ i ];
var parameterType = parameter . ParameterType ;
if ( parameterType . IsByRef )
{
builder . Append ( "[ref] " );
parameterType = parameterType . GetElementType ();
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
if ( parameterType . IsArray && ( i == parametersLength - 1 ))
{
// The extension method 'CustomAttributeExtensions.GetCustomAttributes(ParameterInfo, Type, Boolean)' has inconsistent
// behavior on its return value in both FullCLR and CoreCLR. According to MSDN, if the attribute cannot be found, it
// should return an empty collection. However, it returns null in some rare cases [when the parameter isn't backed by
// actual metadata].
// This inconsistent behavior affects OneCore powershell because we are using the extension method here when compiling
// against CoreCLR. So we need to add a null check until this is fixed in CLR.
var paramArrayAttrs = parameter . GetCustomAttributes ( typeof ( ParamArrayAttribute ), false );
2020-06-25 07:30:40 +01:00
if ( paramArrayAttrs != null && paramArrayAttrs . Length > 0 )
2016-03-30 14:20:52 -07:00
builder . Append ( "Params " );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
builder . Append ( ToStringCodeMethods . Type ( parameterType ));
2020-10-30 07:07:18 +00:00
builder . Append ( ' ' );
2016-03-30 14:20:52 -07:00
builder . Append ( parameter . Name );
2022-06-27 22:21:25 +03:00
if ( parameter . HasDefaultValue )
{
builder . Append ( " = " );
builder . Append ( GetDefaultValueStringRepresentation ( parameter ));
}
2016-03-30 14:20:52 -07:00
builder . Append ( ", " );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
builder . Remove ( builder . Length - 2 , 2 );
}
2018-12-23 22:20:06 -08:00
2020-10-30 07:07:18 +00:00
builder . Append ( ')' );
2016-03-30 14:20:52 -07:00
return builder . ToString ();
}
2016-08-28 05:44:55 +00:00
#endregion auxiliary to method calling
2016-03-30 14:20:52 -07:00
/// <summary>
/// Called after a non null return from GetMember to try to call
2019-01-06 04:40:24 +05:00
/// the method with the arguments.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="method">The non empty return from GetMethods.</param>
/// <param name="arguments">The arguments to use.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The return value for the method.</returns>
2016-03-30 14:20:52 -07:00
protected override object MethodInvoke ( PSMethod method , object [] arguments )
{
return this . MethodInvoke ( method , null , arguments );
}
/// <summary>
/// Called after a non null return from GetMember to try to call
2019-01-06 04:40:24 +05:00
/// the method with the arguments.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="method">The non empty return from GetMethods.</param>
/// <param name="invocationConstraints">Invocation constraints.</param>
/// <param name="arguments">The arguments to use.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The return value for the method.</returns>
2016-03-30 14:20:52 -07:00
protected override object MethodInvoke ( PSMethod method , PSMethodInvocationConstraints invocationConstraints , object [] arguments )
{
MethodCacheEntry methodEntry = ( MethodCacheEntry ) method . adapterData ;
return DotNetAdapter . MethodInvokeDotNet (
2016-07-29 13:02:49 -07:00
method . Name ,
2016-03-30 14:20:52 -07:00
method . baseObject ,
2016-07-29 13:02:49 -07:00
methodEntry . methodInformationStructures ,
2016-03-30 14:20:52 -07:00
invocationConstraints ,
arguments );
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Called after a non null return from GetMember to return the overloads.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="method">The return of GetMember.</param>
2016-03-30 14:20:52 -07:00
/// <returns></returns>
2018-12-22 01:26:06 -08:00
protected override Collection < string > MethodDefinitions ( PSMethod method )
2016-03-30 14:20:52 -07:00
{
MethodCacheEntry methodEntry = ( MethodCacheEntry ) method . adapterData ;
IList < string > uniqueValues = methodEntry
. methodInformationStructures
2021-04-13 00:44:05 +05:00
. Select ( static m => m . methodDefinition )
2016-03-30 14:20:52 -07:00
. Distinct ( StringComparer . Ordinal )
. ToList ();
return new Collection < string >( uniqueValues );
}
#endregion method
#region parameterized property
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the name of the type corresponding to the property's value.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property obtained in a previous GetMember.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The name of the type corresponding to the member.</returns>
2016-03-30 14:20:52 -07:00
protected override string ParameterizedPropertyType ( PSParameterizedProperty property )
{
var adapterData = ( ParameterizedPropertyCacheEntry ) property . adapterData ;
return adapterData . propertyType . FullName ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if the property is settable.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if the property is settable.</returns>
2016-03-30 14:20:52 -07:00
protected override bool ParameterizedPropertyIsSettable ( PSParameterizedProperty property )
{
return !(( ParameterizedPropertyCacheEntry ) property . adapterData ). readOnly ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if the property is gettable.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if the property is gettable.</returns>
2016-03-30 14:20:52 -07:00
protected override bool ParameterizedPropertyIsGettable ( PSParameterizedProperty property )
{
return !(( ParameterizedPropertyCacheEntry ) property . adapterData ). writeOnly ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Called after a non null return from GetMember to get the property value.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">The non empty return from GetMember.</param>
/// <param name="arguments">The arguments to use.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The return value for the property.</returns>
2016-03-30 14:20:52 -07:00
protected override object ParameterizedPropertyGet ( PSParameterizedProperty property , object [] arguments )
{
var adapterData = ( ParameterizedPropertyCacheEntry ) property . adapterData ;
return DotNetAdapter . MethodInvokeDotNet ( property . Name , property . baseObject ,
adapterData . getterInformation , null , arguments );
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Called after a non null return from GetMember to set the property value.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">The non empty return from GetMember.</param>
/// <param name="setValue">The value to set property with.</param>
/// <param name="arguments">The arguments to use.</param>
2016-03-30 14:20:52 -07:00
protected override void ParameterizedPropertySet ( PSParameterizedProperty property , object setValue , object [] arguments )
{
var adapterData = ( ParameterizedPropertyCacheEntry ) property . adapterData ;
ParameterizedPropertyInvokeSet ( adapterData . propertyName , property . baseObject , setValue ,
adapterData . setterInformation , arguments );
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Called after a non null return from GetMember to return the overloads.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-22 01:26:06 -08:00
protected override Collection < string > ParameterizedPropertyDefinitions ( PSParameterizedProperty property )
2016-03-30 14:20:52 -07:00
{
var adapterData = ( ParameterizedPropertyCacheEntry ) property . adapterData ;
var returnValue = new Collection < string >();
for ( int i = 0 ; i < adapterData . propertyDefinition . Length ; i ++)
{
returnValue . Add ( adapterData . propertyDefinition [ i ]);
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return returnValue ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the string representation of the property in the object.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property obtained in a previous GetMember.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The string representation of the property in the object.</returns>
2016-03-30 14:20:52 -07:00
protected override string ParameterizedPropertyToString ( PSParameterizedProperty property )
{
StringBuilder returnValue = new StringBuilder ();
Collection < string > definitions = ParameterizedPropertyDefinitions ( property );
for ( int i = 0 ; i < definitions . Count ; i ++)
{
returnValue . Append ( definitions [ i ]);
returnValue . Append ( ", " );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
returnValue . Remove ( returnValue . Length - 2 , 2 );
return returnValue . ToString ();
}
#endregion parameterized property
#endregion virtual
}
#region DotNetAdapterWithOnlyPropertyLookup
2019-03-21 02:43:52 +01:00
2016-03-30 14:20:52 -07:00
/// <summary>
/// This is used by PSObject to support dotnet member lookup for the adapted
/// objects.
/// </summary>
/// <remarks>
/// This class is created to avoid cluttering DotNetAdapter with if () { } blocks .
/// </remarks>
internal class BaseDotNetAdapterForAdaptedObjects : DotNetAdapter
{
/// <summary>
2017-10-27 05:12:19 +08:00
/// Return a collection representing the <paramref name="obj"/> object's
2016-03-30 14:20:52 -07:00
/// members as returned by CLR reflection.
/// </summary>
protected override PSMemberInfoInternalCollection < T > GetMembers < T >( object obj )
{
PSMemberInfoInternalCollection < T > returnValue = new PSMemberInfoInternalCollection < T >();
AddAllProperties < T >( obj , returnValue , true );
AddAllMethods < T >( obj , returnValue , true );
AddAllEvents < T >( obj , returnValue , true );
return returnValue ;
}
/// <summary>
/// Returns a member representing the <paramref name="obj"/> as given by CLR reflection.
/// </summary>
protected override T GetMember < T >( object obj , string memberName )
{
PSProperty property = base . GetDotNetProperty < PSProperty >( obj , memberName );
2018-06-26 02:37:09 +05:00
if ( typeof ( T ). IsAssignableFrom ( typeof ( PSProperty )) && ( property != null ))
2016-03-30 14:20:52 -07:00
{
return property as T ;
}
2016-08-28 05:44:55 +00:00
// In order to not break v1..base dotnet adapter should not return methods
2016-03-30 14:20:52 -07:00
// when accessed with T as PSMethod.. accessing method with PSMemberInfo
// is ok as property always gets precedence over methods and duplicates
// are ignored.
if ( typeof ( T ) == typeof ( PSMemberInfo ))
{
2018-04-04 14:47:44 -07:00
T returnValue = base . GetDotNetMethod < T >( obj , memberName );
2016-03-30 14:20:52 -07:00
// We only return a method if there is no property by the same name
// to match the behavior we have in GetMembers
2020-07-30 16:06:38 -07:00
if ( returnValue != null && property == null )
2016-03-30 14:20:52 -07:00
{
return returnValue ;
}
}
if ( IsTypeParameterizedProperty ( typeof ( T )))
{
2018-04-04 14:47:44 -07:00
PSParameterizedProperty parameterizedProperty = base . GetDotNetProperty < PSParameterizedProperty >( obj , memberName );
2016-03-30 14:20:52 -07:00
// We only return a parameterized property if there is no property by the same name
// to match the behavior we have in GetMembers
2020-07-30 16:06:38 -07:00
if ( parameterizedProperty != null && property == null )
2016-03-30 14:20:52 -07:00
{
return parameterizedProperty as T ;
}
}
2018-12-23 22:20:06 -08:00
2016-07-29 13:02:49 -07:00
return null ;
2016-03-30 14:20:52 -07:00
}
2019-03-21 02:43:52 +01:00
/// <summary>
/// Returns the first reflection member whose name matches the specified <see cref="MemberNamePredicate"/>.
/// Otherwise, return null.
/// </summary>
protected override T GetFirstMemberOrDefault < T >( object obj , MemberNamePredicate predicate )
{
PSProperty property = base . GetFirstDotNetPropertyOrDefault < PSProperty >( obj , predicate );
if ( typeof ( T ). IsAssignableFrom ( typeof ( PSProperty )) && property != null )
{
return property as T ;
}
// In order to not break v1..base dotnet adapter should not return methods
// when accessed with T as PSMethod.. accessing method with PSMemberInfo
// is ok as property always gets precedence over methods and duplicates
// are ignored.
if ( typeof ( T ) == typeof ( PSMemberInfo ))
{
T returnValue = base . GetFirstDotNetMethodOrDefault < T >( obj , predicate );
// We only return a method if there is no property by the same name
// to match the behavior we have in GetMembers
2020-07-30 16:06:38 -07:00
if ( returnValue != null && property == null )
2019-03-21 02:43:52 +01:00
{
return returnValue ;
}
}
if ( IsTypeParameterizedProperty ( typeof ( T )))
{
var parameterizedProperty = base . GetFirstDotNetPropertyOrDefault < PSParameterizedProperty >( obj , predicate );
// We only return a parameterized property if there is no property by the same name
// to match the behavior we have in GetMembers
2020-07-30 16:06:38 -07:00
if ( parameterizedProperty != null && property == null )
2019-03-21 02:43:52 +01:00
{
return parameterizedProperty as T ;
}
}
return null ;
}
2016-03-30 14:20:52 -07:00
}
#endregion
/// <summary>
2019-01-06 04:40:24 +05:00
/// Used only to add a COM style type name to a COM interop .NET type.
2016-03-30 14:20:52 -07:00
/// </summary>
internal class DotNetAdapterWithComTypeName : DotNetAdapter
{
2020-11-05 03:36:23 +00:00
private readonly ComTypeInfo _comTypeInfo ;
2020-05-29 05:56:33 +01:00
2016-03-30 14:20:52 -07:00
internal DotNetAdapterWithComTypeName ( ComTypeInfo comTypeInfo )
{
2016-07-29 13:02:49 -07:00
_comTypeInfo = comTypeInfo ;
2016-03-30 14:20:52 -07:00
}
protected override IEnumerable < string > GetTypeNameHierarchy ( object obj )
{
2018-04-13 15:01:41 -07:00
for ( Type type = obj . GetType (); type != null ; type = type . BaseType )
2016-03-30 14:20:52 -07:00
{
if ( type . FullName . Equals ( "System.__ComObject" ))
{
2016-07-29 13:02:49 -07:00
yield return ComAdapter . GetComTypeName ( _comTypeInfo . Clsid );
2016-03-30 14:20:52 -07:00
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
yield return type . FullName ;
}
}
protected override ConsolidatedString GetInternedTypeNameHierarchy ( object obj )
{
return new ConsolidatedString ( GetTypeNameHierarchy ( obj ), interned : true );
}
}
/// <summary>
/// Adapter used for GetMember and GetMembers only.
/// All other methods will not be called.
/// </summary>
internal abstract class MemberRedirectionAdapter : Adapter
{
#region virtual
#region property specific
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns an array with the property attributes.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property we want the attributes from.</param>
2018-12-25 21:43:03 +05:00
/// <returns>An array with the property attributes.</returns>
2016-03-30 14:20:52 -07:00
protected override AttributeCollection PropertyAttributes ( PSProperty property )
{
return new AttributeCollection ();
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the value from a property coming from a previous call to GetMember.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="property">PSProperty coming from a previous call to GetMember.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The value of the property.</returns>
2016-03-30 14:20:52 -07:00
protected override object PropertyGet ( PSProperty property )
{
Diagnostics . Assert ( false , "redirection adapter is not called for properties" );
throw PSTraceSource . NewNotSupportedException ();
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Sets the value of a property coming from a previous call to GetMember.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="property">PSProperty coming from a previous call to GetMember.</param>
2018-12-29 08:25:49 +05:00
/// <param name="setValue">Value to set the property with.</param>
/// <param name="convertIfPossible">Instructs the adapter to convert before setting, if the adapter supports conversion.</param>
2016-03-30 14:20:52 -07:00
protected override void PropertySet ( PSProperty property , object setValue , bool convertIfPossible )
{
Diagnostics . Assert ( false , "redirection adapter is not called for properties" );
throw PSTraceSource . NewNotSupportedException ();
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if the property is settable.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if the property is settable.</returns>
2016-03-30 14:20:52 -07:00
protected override bool PropertyIsSettable ( PSProperty property )
{
Diagnostics . Assert ( false , "redirection adapter is not called for properties" );
throw PSTraceSource . NewNotSupportedException ();
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if the property is gettable.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if the property is gettable.</returns>
2016-03-30 14:20:52 -07:00
protected override bool PropertyIsGettable ( PSProperty property )
{
Diagnostics . Assert ( false , "redirection adapter is not called for properties" );
throw PSTraceSource . NewNotSupportedException ();
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the name of the type corresponding to the property's value.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="property">PSProperty obtained in a previous GetMember.</param>
/// <param name="forDisplay">True if the result is for display purposes only.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The name of the type corresponding to the member.</returns>
2016-03-30 14:20:52 -07:00
protected override string PropertyType ( PSProperty property , bool forDisplay )
{
Diagnostics . Assert ( false , "redirection adapter is not called for properties" );
throw PSTraceSource . NewNotSupportedException ();
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the string representation of the property in the object.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property obtained in a previous GetMember.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The string representation of the property in the object.</returns>
2016-03-30 14:20:52 -07:00
protected override string PropertyToString ( PSProperty property )
{
Diagnostics . Assert ( false , "redirection adapter is not called for properties" );
throw PSTraceSource . NewNotSupportedException ();
}
#endregion property specific
#region method specific
/// <summary>
/// Called after a non null return from GetMember to try to call
2019-01-06 04:40:24 +05:00
/// the method with the arguments.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="method">The non empty return from GetMethods.</param>
/// <param name="arguments">The arguments to use.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The return value for the method.</returns>
2016-03-30 14:20:52 -07:00
protected override object MethodInvoke ( PSMethod method , object [] arguments )
{
Diagnostics . Assert ( false , "redirection adapter is not called for methods" );
throw PSTraceSource . NewNotSupportedException ();
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Called after a non null return from GetMember to return the overloads.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="method">The return of GetMember.</param>
2016-03-30 14:20:52 -07:00
/// <returns></returns>
2018-12-22 01:26:06 -08:00
protected override Collection < string > MethodDefinitions ( PSMethod method )
2016-03-30 14:20:52 -07:00
{
Diagnostics . Assert ( false , "redirection adapter is not called for methods" );
throw PSTraceSource . NewNotSupportedException ();
}
#endregion method specific
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
#endregion virtual
}
/// <summary>
2019-01-10 02:07:41 +05:00
/// Adapter for properties in the inside PSObject if it has a null BaseObject.
2016-03-30 14:20:52 -07:00
/// </summary>
internal class PSObjectAdapter : MemberRedirectionAdapter
{
#region virtual
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the TypeNameHierarchy out of an object.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to get the TypeNameHierarchy from.</param>
2016-03-30 14:20:52 -07:00
protected override IEnumerable < string > GetTypeNameHierarchy ( object obj )
{
return (( PSObject ) obj ). InternalTypeNames ;
}
/// <summary>
/// Returns null if memberName is not a member in the adapter or
2019-01-06 04:40:24 +05:00
/// the corresponding PSMemberInfo.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to retrieve the PSMemberInfo from.</param>
/// <param name="memberName">Name of the member to be retrieved.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The PSMemberInfo corresponding to memberName from obj.</returns>
2016-03-30 14:20:52 -07:00
protected override T GetMember < T >( object obj , string memberName )
{
return (( PSObject ) obj ). Members [ memberName ] as T ;
}
2019-03-21 02:43:52 +01:00
/// <summary>
/// Returns the first PSMemberInfo whose name matches the specified <see cref="MemberNamePredicate"/>.
/// Otherwise, return null.
/// </summary>
protected override T GetFirstMemberOrDefault < T >( object obj , MemberNamePredicate predicate )
{
return (( PSObject ) obj ). GetFirstPropertyOrDefault ( predicate ) as T ;
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Retrieves all the members available in the object.
/// The adapter implementation is encouraged to cache all properties/methods available
/// in the first call to GetMember and GetMembers so that subsequent
/// calls can use the cache.
/// In the case of the .NET adapter that would be a cache from the .NET type to
2017-10-27 05:12:19 +08:00
/// the public properties and fields available in that type.
2016-03-30 14:20:52 -07:00
/// In the case of the DirectoryEntry adapter, this could be a cache of the objectClass
/// to the properties available in it.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to get all the member information from.</param>
2018-12-25 21:43:03 +05:00
/// <returns>All members in obj.</returns>
2016-03-30 14:20:52 -07:00
protected override PSMemberInfoInternalCollection < T > GetMembers < T >( object obj )
{
var returnValue = new PSMemberInfoInternalCollection < T >();
PSObject mshObj = ( PSObject ) obj ;
foreach ( PSMemberInfo member in mshObj . Members )
{
T memberAsT = member as T ;
if ( memberAsT != null )
{
returnValue . Add ( memberAsT );
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return returnValue ;
}
#endregion virtual
}
/// <summary>
2019-01-10 02:07:41 +05:00
/// Adapter for properties inside a member set.
2016-03-30 14:20:52 -07:00
/// </summary>
internal class PSMemberSetAdapter : MemberRedirectionAdapter
{
#region virtual
protected override IEnumerable < string > GetTypeNameHierarchy ( object obj )
{
// Make sure PSMemberSet adapter shows PSMemberSet as the typename.
// This is because PSInternalMemberSet internal class derives from
// PSMemberSet to support delay loading PSBase, PSObject etc. We
// should not show internal type members to the users. Also this
// might break type files shipped in v1.
yield return typeof ( PSMemberSet ). FullName ;
}
/// <summary>
/// Returns null if memberName is not a member in the adapter or
2019-01-06 04:40:24 +05:00
/// the corresponding PSMemberInfo.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to retrieve the PSMemberInfo from.</param>
/// <param name="memberName">Name of the member to be retrieved.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The PSMemberInfo corresponding to memberName from obj.</returns>
2016-03-30 14:20:52 -07:00
protected override T GetMember < T >( object obj , string memberName )
{
return (( PSMemberSet ) obj ). Members [ memberName ] as T ;
}
2019-03-21 02:43:52 +01:00
/// <summary>
/// Returns the first PSMemberInfo whose name matches the specified <see cref="MemberNamePredicate"/>.
/// Otherwise, return null.
/// </summary>
protected override T GetFirstMemberOrDefault < T >( object obj , MemberNamePredicate predicate )
{
foreach ( var member in (( PSMemberSet ) obj ). Members )
{
if ( predicate ( member . Name ))
{
return member as T ;
}
}
return null ;
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Retrieves all the members available in the object.
/// The adapter implementation is encouraged to cache all properties/methods available
/// in the first call to GetMember and GetMembers so that subsequent
/// calls can use the cache.
/// In the case of the .NET adapter that would be a cache from the .NET type to
2017-10-27 05:12:19 +08:00
/// the public properties and fields available in that type.
2016-03-30 14:20:52 -07:00
/// In the case of the DirectoryEntry adapter, this could be a cache of the objectClass
/// to the properties available in it.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to get all the member information from.</param>
2018-12-25 21:43:03 +05:00
/// <returns>All members in obj.</returns>
2016-03-30 14:20:52 -07:00
protected override PSMemberInfoInternalCollection < T > GetMembers < T >( object obj )
{
var returnValue = new PSMemberInfoInternalCollection < T >();
foreach ( PSMemberInfo member in (( PSMemberSet ) obj ). Members )
{
T memberAsT = member as T ;
if ( memberAsT != null )
{
returnValue . Add ( memberAsT );
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return returnValue ;
}
2017-09-04 19:45:51 -07:00
#endregion virtual
2016-03-30 14:20:52 -07:00
}
/// <summary>
2017-10-27 05:12:19 +08:00
/// Base class for all adapters that adapt only properties and retain
2019-01-06 04:40:24 +05:00
/// .NET methods.
2016-03-30 14:20:52 -07:00
/// </summary>
internal abstract class PropertyOnlyAdapter : DotNetAdapter
{
2018-03-15 15:09:38 -07:00
/// <summary>
/// For a PropertyOnlyAdapter, the property may come from various sources,
/// but methods, including parameterized properties, still come from DotNetAdapter.
/// So, the binder can optimize on method calls for objects that map to a
/// custom PropertyOnlyAdapter.
/// </summary>
internal override bool CanSiteBinderOptimize ( MemberTypes typeToOperateOn )
{
switch ( typeToOperateOn )
{
case MemberTypes . Property :
return false ;
case MemberTypes . Method :
return true ;
default :
throw new InvalidOperationException ( "Should be unreachable. Update code if other member types need to be handled here." );
}
}
2016-03-30 14:20:52 -07:00
protected override ConsolidatedString GetInternedTypeNameHierarchy ( object obj )
{
return new ConsolidatedString ( GetTypeNameHierarchy ( obj ), interned : true );
}
/// <summary>
/// Returns null if propertyName is not a property in the adapter or
/// the corresponding PSProperty with its adapterData set to information
/// to be used when retrieving the property.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to retrieve the PSProperty from.</param>
/// <param name="propertyName">Name of the property to be retrieved.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The PSProperty corresponding to propertyName from obj.</returns>
2016-03-30 14:20:52 -07:00
protected abstract PSProperty DoGetProperty ( object obj , string propertyName );
2019-03-21 02:43:52 +01:00
/// <summary>
/// Returns the first PSProperty whose name matches the specified <see cref="MemberNamePredicate"/>.
/// Otherwise, return null.
/// </summary>
/// <param name="obj">Object to retrieve the PSProperty from.</param>
/// <param name="predicate">The predicate to find the matching member.</param>
/// <returns>The first PSProperty whose name matches the <paramref name="predicate"/>.</returns>
protected abstract PSProperty DoGetFirstPropertyOrDefault ( object obj , MemberNamePredicate predicate );
2016-03-30 14:20:52 -07:00
/// <summary>
/// Retrieves all the properties available in the object.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to get all the property information from.</param>
/// <param name="members">Collection where the properties will be added.</param>
2016-03-30 14:20:52 -07:00
protected abstract void DoAddAllProperties < T >( object obj , PSMemberInfoInternalCollection < T > members ) where T : PSMemberInfo ;
/// <summary>
/// Returns null if memberName is not a member in the adapter or
2019-01-06 04:40:24 +05:00
/// the corresponding PSMemberInfo.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to retrieve the PSMemberInfo from.</param>
/// <param name="memberName">Name of the member to be retrieved.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The PSMemberInfo corresponding to memberName from obj.</returns>
2016-03-30 14:20:52 -07:00
protected override T GetMember < T >( object obj , string memberName )
{
PSProperty property = DoGetProperty ( obj , memberName );
2016-07-29 13:02:49 -07:00
if ( typeof ( T ). IsAssignableFrom ( typeof ( PSProperty )) && property != null )
2016-03-30 14:20:52 -07:00
{
return property as T ;
}
if ( typeof ( T ). IsAssignableFrom ( typeof ( PSMethod )))
{
2018-04-04 14:47:44 -07:00
T returnValue = base . GetDotNetMethod < T >( obj , memberName );
2016-03-30 14:20:52 -07:00
// We only return a method if there is no property by the same name
// to match the behavior we have in GetMembers
2020-07-30 16:06:38 -07:00
if ( returnValue != null && property == null )
2016-03-30 14:20:52 -07:00
{
return returnValue ;
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
if ( IsTypeParameterizedProperty ( typeof ( T )))
{
2018-04-04 14:47:44 -07:00
PSParameterizedProperty parameterizedProperty = base . GetDotNetProperty < PSParameterizedProperty >( obj , memberName );
2016-03-30 14:20:52 -07:00
// We only return a parameterized property if there is no property by the same name
// to match the behavior we have in GetMembers
2020-07-30 16:06:38 -07:00
if ( parameterizedProperty != null && property == null )
2016-03-30 14:20:52 -07:00
{
return parameterizedProperty as T ;
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return null ;
}
2019-03-21 02:43:52 +01:00
/// <summary>
/// Returns the first PSMemberInfo whose name matches the specified <see cref="MemberNamePredicate"/>.
/// Otherwise, return null.
/// </summary>
/// <typeparam name="T">A subtype of <see cref="PSMemberInfo"/>.</typeparam>
/// <param name="obj">Object to retrieve the PSMemberInfo from.</param>
/// <param name="predicate">A name matching predicate.</param>
/// <returns>The PSMemberInfo corresponding to the predicate match.</returns>
protected override T GetFirstMemberOrDefault < T >( object obj , MemberNamePredicate predicate )
{
PSProperty property = DoGetFirstPropertyOrDefault ( obj , predicate );
if ( typeof ( T ). IsAssignableFrom ( typeof ( PSProperty )))
{
return property as T ;
}
if ( typeof ( T ). IsAssignableFrom ( typeof ( PSMethod )))
{
T returnValue = base . GetFirstDotNetMethodOrDefault < T >( obj , predicate );
// We only return a method if there is no property by the same name
// to match the behavior we have in GetMembers
2020-07-30 16:06:38 -07:00
if ( returnValue != null && property == null )
2019-03-21 02:43:52 +01:00
{
return returnValue ;
}
}
if ( IsTypeParameterizedProperty ( typeof ( T )))
{
var parameterizedProperty = base . GetFirstDotNetPropertyOrDefault < PSParameterizedProperty >( obj , predicate );
// We only return a parameterized property if there is no property by the same name
// to match the behavior we have in GetMembers
2020-07-30 16:06:38 -07:00
if ( parameterizedProperty != null && property == null )
2019-03-21 02:43:52 +01:00
{
return parameterizedProperty as T ;
}
}
return null ;
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Retrieves all the members available in the object.
/// The adapter implementation is encouraged to cache all properties/methods available
/// in the first call to GetMember and GetMembers so that subsequent
/// calls can use the cache.
/// In the case of the .NET adapter that would be a cache from the .NET type to
2017-10-27 05:12:19 +08:00
/// the public properties and fields available in that type.
2016-03-30 14:20:52 -07:00
/// In the case of the DirectoryEntry adapter, this could be a cache of the objectClass
/// to the properties available in it.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to get all the member information from.</param>
2018-12-25 21:43:03 +05:00
/// <returns>All members in obj.</returns>
2016-03-30 14:20:52 -07:00
protected override PSMemberInfoInternalCollection < T > GetMembers < T >( object obj )
{
var returnValue = new PSMemberInfoInternalCollection < T >();
if ( typeof ( T ). IsAssignableFrom ( typeof ( PSProperty )))
{
DoAddAllProperties < T >( obj , returnValue );
}
2018-12-23 22:20:06 -08:00
2018-04-04 14:47:44 -07:00
base . AddAllMethods ( obj , returnValue , true );
2016-03-30 14:20:52 -07:00
if ( IsTypeParameterizedProperty ( typeof ( T )))
{
var parameterizedProperties = new PSMemberInfoInternalCollection < PSParameterizedProperty >();
2018-04-04 14:47:44 -07:00
base . AddAllProperties ( obj , parameterizedProperties , true );
2016-03-30 14:20:52 -07:00
foreach ( PSParameterizedProperty parameterizedProperty in parameterizedProperties )
{
try
{
returnValue . Add ( parameterizedProperty as T );
}
catch ( ExtendedTypeSystemException )
{
// ignore duplicates: the adapted properties will take precedence
}
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return returnValue ;
}
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Deals with XmlNode objects.
2016-03-30 14:20:52 -07:00
/// </summary>
internal class XmlNodeAdapter : PropertyOnlyAdapter
{
#region virtual
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the TypeNameHierarchy out of an object.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to get the TypeNameHierarchy from.</param>
2016-03-30 14:20:52 -07:00
protected override IEnumerable < string > GetTypeNameHierarchy ( object obj )
{
XmlNode node = ( XmlNode ) obj ;
string nodeNamespace = node . NamespaceURI ;
IEnumerable < string > baseTypeNames = GetDotNetTypeNameHierarchy ( obj );
2018-12-31 15:10:15 +05:00
if ( string . IsNullOrEmpty ( nodeNamespace ))
2016-03-30 14:20:52 -07:00
{
foreach ( string baseType in baseTypeNames )
{
yield return baseType ;
}
}
else
{
StringBuilder firstType = null ;
foreach ( string baseType in baseTypeNames )
{
2020-07-30 16:06:38 -07:00
if ( firstType == null )
2016-03-30 14:20:52 -07:00
{
firstType = new StringBuilder ( baseType );
2020-10-30 07:07:18 +00:00
firstType . Append ( '#' );
2016-03-30 14:20:52 -07:00
firstType . Append ( node . NamespaceURI );
2020-10-30 07:07:18 +00:00
firstType . Append ( '#' );
2016-03-30 14:20:52 -07:00
firstType . Append ( node . LocalName );
yield return firstType . ToString ();
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
yield return baseType ;
}
}
}
/// <summary>
/// Retrieves all the properties available in the object.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to get all the property information from.</param>
/// <param name="members">Collection where the members will be added.</param>
2016-03-30 14:20:52 -07:00
protected override void DoAddAllProperties < T >( object obj , PSMemberInfoInternalCollection < T > members )
{
XmlNode node = ( XmlNode ) obj ;
Dictionary < string , List < XmlNode >> nodeArrays = new Dictionary < string , List < XmlNode >>( StringComparer . OrdinalIgnoreCase );
if ( node . Attributes != null )
{
foreach ( XmlNode attribute in node . Attributes )
{
List < XmlNode > nodeList ;
if (! nodeArrays . TryGetValue ( attribute . LocalName , out nodeList ))
{
nodeList = new List < XmlNode >();
nodeArrays [ attribute . LocalName ] = nodeList ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
nodeList . Add ( attribute );
}
}
if ( node . ChildNodes != null )
{
foreach ( XmlNode childNode in node . ChildNodes )
{
// Win8: 437544 ignore whitespace
if ( childNode is XmlWhitespace )
{
continue ;
}
List < XmlNode > nodeList ;
if (! nodeArrays . TryGetValue ( childNode . LocalName , out nodeList ))
{
nodeList = new List < XmlNode >();
nodeArrays [ childNode . LocalName ] = nodeList ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
nodeList . Add ( childNode );
}
}
foreach ( KeyValuePair < string , List < XmlNode >> nodeArrayEntry in nodeArrays )
{
members . Add ( new PSProperty ( nodeArrayEntry . Key , this , obj , nodeArrayEntry . Value . ToArray ()) as T );
}
}
/// <summary>
/// Returns null if propertyName is not a property in the adapter or
/// the corresponding PSProperty with its adapterData set to information
/// to be used when retrieving the property.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to retrieve the PSProperty from.</param>
/// <param name="propertyName">Name of the property to be retrieved.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The PSProperty corresponding to propertyName from obj.</returns>
2016-03-30 14:20:52 -07:00
protected override PSProperty DoGetProperty ( object obj , string propertyName )
{
XmlNode [] nodes = FindNodes ( obj , propertyName , StringComparison . OrdinalIgnoreCase );
if ( nodes . Length == 0 )
{
return null ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return new PSProperty ( nodes [ 0 ]. LocalName , this , obj , nodes );
}
2019-03-21 02:43:52 +01:00
protected override PSProperty DoGetFirstPropertyOrDefault ( object obj , MemberNamePredicate predicate )
{
XmlNode node = FindNode ( obj , predicate );
2020-07-30 16:06:38 -07:00
return node == null ? null : new PSProperty ( node . LocalName , this , obj , node );
2019-03-21 02:43:52 +01:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if the property is settable.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if the property is settable.</returns>
2016-03-30 14:20:52 -07:00
protected override bool PropertyIsSettable ( PSProperty property )
{
XmlNode [] nodes = ( XmlNode []) property . adapterData ;
Diagnostics . Assert ( nodes . Length != 0 , "DoGetProperty would not return an empty array, it would return null instead" );
if ( nodes . Length != 1 )
{
return false ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
XmlNode node = nodes [ 0 ];
if ( node is XmlText )
{
return true ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
if ( node is XmlAttribute )
{
return true ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
XmlAttributeCollection nodeAttributes = node . Attributes ;
if (( nodeAttributes != null ) && ( nodeAttributes . Count != 0 ))
{
return false ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
XmlNodeList nodeChildren = node . ChildNodes ;
2020-07-30 16:06:38 -07:00
if (( nodeChildren == null ) || ( nodeChildren . Count == 0 ))
2016-03-30 14:20:52 -07:00
{
return true ;
}
if (( nodeChildren . Count == 1 ) && ( nodeChildren [ 0 ]. NodeType == XmlNodeType . Text ))
{
return true ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return false ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if the property is gettable.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if the property is gettable.</returns>
2016-03-30 14:20:52 -07:00
protected override bool PropertyIsGettable ( PSProperty property )
{
return true ;
}
private static object GetNodeObject ( XmlNode node )
{
XmlText text = node as XmlText ;
if ( text != null )
{
return text . InnerText ;
}
XmlAttributeCollection nodeAttributes = node . Attributes ;
// A node with attributes should not be simplified
if (( nodeAttributes != null ) && ( nodeAttributes . Count != 0 ))
{
return node ;
}
// If node does not have any children, return the innertext of the node
if (! node . HasChildNodes )
{
return node . InnerText ;
}
XmlNodeList nodeChildren = node . ChildNodes ;
// nodeChildren will not be null as we already verified iff the node has children.
if (( nodeChildren . Count == 1 ) && ( nodeChildren [ 0 ]. NodeType == XmlNodeType . Text ))
{
return node . InnerText ;
}
XmlAttribute attribute = node as XmlAttribute ;
if ( attribute != null )
{
return attribute . Value ;
}
return node ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the value from a property coming from a previous call to DoGetProperty.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="property">PSProperty coming from a previous call to DoGetProperty.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The value of the property.</returns>
2016-03-30 14:20:52 -07:00
protected override object PropertyGet ( PSProperty property )
{
XmlNode [] nodes = ( XmlNode []) property . adapterData ;
if ( nodes . Length == 1 )
{
return GetNodeObject ( nodes [ 0 ]);
}
object [] returnValue = new object [ nodes . Length ];
for ( int i = 0 ; i < nodes . Length ; i ++)
{
returnValue [ i ] = GetNodeObject ( nodes [ i ]);
}
return returnValue ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Sets the value of a property coming from a previous call to DoGetProperty.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="property">PSProperty coming from a previous call to DoGetProperty.</param>
2018-12-29 08:25:49 +05:00
/// <param name="setValue">Value to set the property with.</param>
/// <param name="convertIfPossible">Instructs the adapter to convert before setting, if the adapter supports conversion.</param>
2016-03-30 14:20:52 -07:00
protected override void PropertySet ( PSProperty property , object setValue , bool convertIfPossible )
{
2016-12-03 05:43:10 +06:00
// XML is always a string so implicitly convert to string
string valueString = LanguagePrimitives . ConvertTo < string >( setValue );
2016-03-30 14:20:52 -07:00
XmlNode [] nodes = ( XmlNode []) property . adapterData ;
Diagnostics . Assert ( nodes . Length != 0 , "DoGetProperty would not return an empty array, it would return null instead" );
if ( nodes . Length > 1 )
{
throw new SetValueException ( "XmlNodeSetRestrictionsMoreThanOneNode" ,
null ,
ExtendedTypeSystem . XmlNodeSetShouldBeAString ,
property . Name );
}
XmlNode node = nodes [ 0 ];
XmlText text = node as XmlText ;
if ( text != null )
{
text . InnerText = valueString ;
return ;
}
XmlAttributeCollection nodeAttributes = node . Attributes ;
// A node with attributes cannot be set
if (( nodeAttributes != null ) && ( nodeAttributes . Count != 0 ))
{
throw new SetValueException ( "XmlNodeSetRestrictionsNodeWithAttributes" ,
null ,
ExtendedTypeSystem . XmlNodeSetShouldBeAString ,
property . Name );
}
XmlNodeList nodeChildren = node . ChildNodes ;
2020-07-30 16:06:38 -07:00
if ( nodeChildren == null || nodeChildren . Count == 0 )
2016-03-30 14:20:52 -07:00
{
node . InnerText = valueString ;
return ;
}
if (( nodeChildren . Count == 1 ) && ( nodeChildren [ 0 ]. NodeType == XmlNodeType . Text ))
{
node . InnerText = valueString ;
return ;
}
XmlAttribute attribute = node as XmlAttribute ;
if ( attribute != null )
{
attribute . Value = valueString ;
return ;
}
throw new SetValueException ( "XmlNodeSetRestrictionsUnknownNodeType" ,
null ,
ExtendedTypeSystem . XmlNodeSetShouldBeAString ,
property . Name );
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the name of the type corresponding to the property.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="property">PSProperty obtained in a previous DoGetProperty.</param>
/// <param name="forDisplay">True if the result is for display purposes only.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The name of the type corresponding to the property.</returns>
2016-03-30 14:20:52 -07:00
protected override string PropertyType ( PSProperty property , bool forDisplay )
{
object value = null ;
try
{
value = BasePropertyGet ( property );
}
catch ( GetValueException )
{
}
2018-12-23 22:20:06 -08:00
2020-07-30 16:06:38 -07:00
var type = value == null ? typeof ( object ) : value . GetType ();
2016-03-30 14:20:52 -07:00
return forDisplay ? ToStringCodeMethods . Type ( type ) : type . FullName ;
}
#endregion virtual
/// <summary>
2016-08-28 05:44:55 +00:00
/// Auxiliary in GetProperty to perform case sensitive and case insensitive searches
2019-01-06 04:40:24 +05:00
/// in the child nodes.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="obj">XmlNode to extract property from.</param>
2018-12-29 08:25:49 +05:00
/// <param name="propertyName">Property to look for.</param>
/// <param name="comparisonType">Type pf comparison to perform.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The corresponding XmlNode or null if not present.</returns>
2016-03-30 14:20:52 -07:00
private static XmlNode [] FindNodes ( object obj , string propertyName , StringComparison comparisonType )
{
List < XmlNode > retValue = new List < XmlNode >();
XmlNode node = ( XmlNode ) obj ;
if ( node . Attributes != null )
{
foreach ( XmlNode attribute in node . Attributes )
{
if ( attribute . LocalName . Equals ( propertyName , comparisonType ))
{
retValue . Add ( attribute );
}
}
}
foreach ( XmlNode childNode in node . ChildNodes )
{
if ( childNode is XmlWhitespace )
{
// Win8: 437544 ignore whitespace
continue ;
}
if ( childNode . LocalName . Equals ( propertyName , comparisonType ))
{
retValue . Add ( childNode );
}
}
return retValue . ToArray ();
}
2019-03-21 02:43:52 +01:00
private static XmlNode FindNode ( object obj , MemberNamePredicate predicate )
{
var node = ( XmlNode ) obj ;
if ( node . Attributes != null )
{
foreach ( XmlNode attribute in node . Attributes )
{
if ( predicate ( attribute . LocalName ))
{
return attribute ;
}
}
}
foreach ( XmlNode childNode in node . ChildNodes )
{
if ( childNode is XmlWhitespace )
{
// Win8: 437544 ignore whitespace
continue ;
}
if ( predicate ( childNode . LocalName ))
{
return childNode ;
}
}
return null ;
}
2016-03-30 14:20:52 -07:00
}
2017-07-17 11:27:01 -07:00
/// <summary>
2019-01-06 04:40:24 +05:00
/// Deals with DataRow objects.
2017-07-17 11:27:01 -07:00
/// </summary>
internal class DataRowAdapter : PropertyOnlyAdapter
{
#region virtual
2019-03-21 02:43:52 +01:00
2017-07-17 11:27:01 -07:00
/// <summary>
/// Retrieves all the properties available in the object.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to get all the property information from.</param>
/// <param name="members">Collection where the members will be added.</param>
2017-07-17 11:27:01 -07:00
protected override void DoAddAllProperties < T >( object obj , PSMemberInfoInternalCollection < T > members )
{
DataRow dataRow = ( DataRow ) obj ;
2020-07-30 16:06:38 -07:00
if ( dataRow . Table == null || dataRow . Table . Columns == null )
2017-07-17 11:27:01 -07:00
{
return ;
}
foreach ( DataColumn property in dataRow . Table . Columns )
{
members . Add ( new PSProperty ( property . ColumnName , this , obj , property . ColumnName ) as T );
}
return ;
}
/// <summary>
/// Returns null if propertyName is not a property in the adapter or
/// the corresponding PSProperty with its adapterData set to information
/// to be used when retrieving the property.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to retrieve the PSProperty from.</param>
/// <param name="propertyName">Name of the property to be retrieved.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The PSProperty corresponding to propertyName from obj.</returns>
2017-07-17 11:27:01 -07:00
protected override PSProperty DoGetProperty ( object obj , string propertyName )
{
DataRow dataRow = ( DataRow ) obj ;
if (! dataRow . Table . Columns . Contains ( propertyName ))
{
return null ;
}
string columnName = dataRow . Table . Columns [ propertyName ]. ColumnName ;
return new PSProperty ( columnName , this , obj , columnName );
}
2019-03-21 02:43:52 +01:00
protected override PSProperty DoGetFirstPropertyOrDefault ( object obj , MemberNamePredicate predicate )
{
DataRow dataRow = ( DataRow ) obj ;
foreach ( DataColumn property in dataRow . Table . Columns )
{
if ( predicate ( property . ColumnName ))
{
return new PSProperty ( property . ColumnName , this , obj , property . ColumnName );
}
}
return null ;
}
2017-07-17 11:27:01 -07:00
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the name of the type corresponding to the property.
2017-07-17 11:27:01 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="property">PSProperty obtained in a previous DoGetProperty.</param>
/// <param name="forDisplay">True if the result is for display purposes only.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The name of the type corresponding to the property.</returns>
2017-07-17 11:27:01 -07:00
protected override string PropertyType ( PSProperty property , bool forDisplay )
{
string columnName = ( string ) property . adapterData ;
DataRow dataRow = ( DataRow ) property . baseObject ;
var dataType = dataRow . Table . Columns [ columnName ]. DataType ;
return forDisplay ? ToStringCodeMethods . Type ( dataType ) : dataType . FullName ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if the property is settable.
2017-07-17 11:27:01 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if the property is settable.</returns>
2017-07-17 11:27:01 -07:00
protected override bool PropertyIsSettable ( PSProperty property )
{
string columnName = ( string ) property . adapterData ;
DataRow dataRow = ( DataRow ) property . baseObject ;
return ! dataRow . Table . Columns [ columnName ]. ReadOnly ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if the property is gettable.
2017-07-17 11:27:01 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if the property is gettable.</returns>
2017-07-17 11:27:01 -07:00
protected override bool PropertyIsGettable ( PSProperty property )
{
return true ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the value from a property coming from a previous call to DoGetProperty.
2017-07-17 11:27:01 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="property">PSProperty coming from a previous call to DoGetProperty.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The value of the property.</returns>
2017-07-17 11:27:01 -07:00
protected override object PropertyGet ( PSProperty property )
{
DataRow dataRow = ( DataRow ) property . baseObject ;
return dataRow [( string ) property . adapterData ];
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Sets the value of a property coming from a previous call to DoGetProperty.
2017-07-17 11:27:01 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="property">PSProperty coming from a previous call to DoGetProperty.</param>
2018-12-29 08:25:49 +05:00
/// <param name="setValue">Value to set the property with.</param>
/// <param name="convertIfPossible">Instructs the adapter to convert before setting, if the adapter supports conversion.</param>
2017-07-17 11:27:01 -07:00
protected override void PropertySet ( PSProperty property , object setValue , bool convertIfPossible )
{
DataRow dataRow = ( DataRow ) property . baseObject ;
dataRow [( string ) property . adapterData ] = setValue ;
return ;
}
#endregion virtual
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Deals with DataRowView objects.
2017-07-17 11:27:01 -07:00
/// </summary>
internal class DataRowViewAdapter : PropertyOnlyAdapter
{
#region virtual
/// <summary>
/// Retrieves all the properties available in the object.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to get all the property information from.</param>
/// <param name="members">Collection where the members will be added.</param>
2017-07-17 11:27:01 -07:00
protected override void DoAddAllProperties < T >( object obj , PSMemberInfoInternalCollection < T > members )
{
DataRowView dataRowView = ( DataRowView ) obj ;
2020-07-30 16:06:38 -07:00
if ( dataRowView . Row == null || dataRowView . Row . Table == null || dataRowView . Row . Table . Columns == null )
2017-07-17 11:27:01 -07:00
{
return ;
}
foreach ( DataColumn property in dataRowView . Row . Table . Columns )
{
members . Add ( new PSProperty ( property . ColumnName , this , obj , property . ColumnName ) as T );
}
return ;
}
/// <summary>
/// Returns null if propertyName is not a property in the adapter or
/// the corresponding PSProperty with its adapterData set to information
/// to be used when retrieving the property.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Object to retrieve the PSProperty from.</param>
/// <param name="propertyName">Name of the property to be retrieved.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The PSProperty corresponding to propertyName from obj.</returns>
2017-07-17 11:27:01 -07:00
protected override PSProperty DoGetProperty ( object obj , string propertyName )
{
DataRowView dataRowView = ( DataRowView ) obj ;
if (! dataRowView . Row . Table . Columns . Contains ( propertyName ))
{
return null ;
}
2018-12-23 22:20:06 -08:00
2017-07-17 11:27:01 -07:00
string columnName = dataRowView . Row . Table . Columns [ propertyName ]. ColumnName ;
return new PSProperty ( columnName , this , obj , columnName );
}
2019-03-21 02:43:52 +01:00
protected override PSProperty DoGetFirstPropertyOrDefault ( object obj , MemberNamePredicate predicate )
{
DataRowView dataRowView = ( DataRowView ) obj ;
foreach ( DataColumn column in dataRowView . Row . Table . Columns )
{
string columnName = column . ColumnName ;
if ( predicate ( columnName ))
{
return new PSProperty ( columnName , this , obj , columnName );
}
}
return null ;
}
2017-07-17 11:27:01 -07:00
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the name of the type corresponding to the property.
2017-07-17 11:27:01 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="property">PSProperty obtained in a previous DoGetProperty.</param>
/// <param name="forDisplay">True if the result is for display purposes only.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The name of the type corresponding to the property.</returns>
2017-07-17 11:27:01 -07:00
protected override string PropertyType ( PSProperty property , bool forDisplay )
{
string columnName = ( string ) property . adapterData ;
DataRowView dataRowView = ( DataRowView ) property . baseObject ;
var dataType = dataRowView . Row . Table . Columns [ columnName ]. DataType ;
return forDisplay ? ToStringCodeMethods . Type ( dataType ) : dataType . FullName ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if the property is settable.
2017-07-17 11:27:01 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if the property is settable.</returns>
2017-07-17 11:27:01 -07:00
protected override bool PropertyIsSettable ( PSProperty property )
{
string columnName = ( string ) property . adapterData ;
DataRowView dataRowView = ( DataRowView ) property . baseObject ;
return ! dataRowView . Row . Table . Columns [ columnName ]. ReadOnly ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if the property is gettable.
2017-07-17 11:27:01 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="property">Property to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if the property is gettable.</returns>
2017-07-17 11:27:01 -07:00
protected override bool PropertyIsGettable ( PSProperty property )
{
return true ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns the value from a property coming from a previous call to DoGetProperty.
2017-07-17 11:27:01 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="property">PSProperty coming from a previous call to DoGetProperty.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The value of the property.</returns>
2017-07-17 11:27:01 -07:00
protected override object PropertyGet ( PSProperty property )
{
DataRowView dataRowView = ( DataRowView ) property . baseObject ;
return dataRowView [( string ) property . adapterData ];
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Sets the value of a property coming from a previous call to DoGetProperty.
2017-07-17 11:27:01 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="property">PSProperty coming from a previous call to DoGetProperty.</param>
2018-12-29 08:25:49 +05:00
/// <param name="setValue">Value to set the property with.</param>
/// <param name="convertIfPossible">Instructs the adapter to convert before setting, if the adapter supports conversion.</param>
2017-07-17 11:27:01 -07:00
protected override void PropertySet ( PSProperty property , object setValue , bool convertIfPossible )
{
DataRowView dataRowView = ( DataRowView ) property . baseObject ;
dataRowView [( string ) property . adapterData ] = setValue ;
return ;
}
#endregion virtual
}
2016-03-30 14:20:52 -07:00
internal class TypeInference
{
[TraceSource("ETS", "Extended Type System")]
2016-07-29 13:02:49 -07:00
private static readonly PSTraceSource s_tracer = PSTraceSource . GetTracer ( "ETS" , "Extended Type System" );
2016-03-30 14:20:52 -07:00
internal static MethodInformation Infer ( MethodInformation genericMethod , Type [] argumentTypes )
{
Dbg . Assert ( genericMethod != null , "Caller should verify that genericMethod != null" );
Dbg . Assert ( argumentTypes != null , "Caller should verify that arguments != null" );
// the cast is safe, because
// 1) only ConstructorInfo and MethodInfo derive from MethodBase
// 2) ConstructorInfo.IsGenericMethod is always false
MethodInfo originalMethod = ( MethodInfo ) genericMethod . method ;
MethodInfo inferredMethod = TypeInference . Infer ( originalMethod , argumentTypes , genericMethod . hasVarArgs );
if ( inferredMethod != null )
{
return new MethodInformation ( inferredMethod , 0 );
}
else
{
return null ;
}
}
private static MethodInfo Infer ( MethodInfo genericMethod , Type [] typesOfMethodArguments , bool hasVarArgs )
{
Dbg . Assert ( genericMethod != null , "Caller should verify that genericMethod != null" );
Dbg . Assert ( typesOfMethodArguments != null , "Caller should verify that arguments != null" );
if (! genericMethod . ContainsGenericParameters )
{
return genericMethod ;
}
Type [] typeParameters = genericMethod . GetGenericArguments ();
2021-04-13 00:44:05 +05:00
Type [] typesOfMethodParameters = genericMethod . GetParameters (). Select ( static p => p . ParameterType ). ToArray ();
2016-03-30 14:20:52 -07:00
MethodInfo inferredMethod = Infer ( genericMethod , typeParameters , typesOfMethodParameters , typesOfMethodArguments );
2017-10-27 05:12:19 +08:00
// normal inference failed, perhaps instead of inferring for
2016-03-30 14:20:52 -07:00
// M<T1,T2,T3>(T1, T2, ..., params T3 [])
// we can try to infer for this signature instead
// M<T1,T2,T3>)(T1, T2, ..., T3, T3, T3, T3)
// where T3 is repeated appropriate number of times depending on the number of actual method arguments.
2020-07-30 16:06:38 -07:00
if ( inferredMethod == null &&
2016-03-30 14:20:52 -07:00
hasVarArgs &&
typesOfMethodArguments . Length >= ( typesOfMethodParameters . Length - 1 ))
{
IEnumerable < Type > typeOfRegularParameters = typesOfMethodParameters . Take ( typesOfMethodParameters . Length - 1 );
IEnumerable < Type > multipliedVarArgsElementType = Enumerable . Repeat (
typesOfMethodParameters [ typesOfMethodParameters . Length - 1 ]. GetElementType (),
typesOfMethodArguments . Length - typesOfMethodParameters . Length + 1 );
inferredMethod = Infer (
genericMethod ,
typeParameters ,
typeOfRegularParameters . Concat ( multipliedVarArgsElementType ),
typesOfMethodArguments );
}
return inferredMethod ;
}
private static MethodInfo Infer ( MethodInfo genericMethod , ICollection < Type > typeParameters , IEnumerable < Type > typesOfMethodParameters , IEnumerable < Type > typesOfMethodArguments )
{
Dbg . Assert ( genericMethod != null , "Caller should verify that genericMethod != null" );
Dbg . Assert ( typeParameters != null , "Caller should verify that typeParameters != null" );
Dbg . Assert ( typesOfMethodParameters != null , "Caller should verify that typesOfMethodParameters != null" );
Dbg . Assert ( typesOfMethodArguments != null , "Caller should verify that typesOfMethodArguments != null" );
2016-07-29 13:02:49 -07:00
using ( s_tracer . TraceScope ( "Inferring type parameters for the following method: {0}" , genericMethod ))
2016-03-30 14:20:52 -07:00
{
2020-11-20 12:26:46 +00:00
if (( s_tracer . Options & PSTraceSourceOptions . WriteLine ) == PSTraceSourceOptions . WriteLine )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
s_tracer . WriteLine (
2016-03-30 14:20:52 -07:00
"Types of method arguments: {0}" ,
2021-04-13 00:44:05 +05:00
string . Join ( ", " , typesOfMethodArguments . Select ( static t => t . ToString ()). ToArray ()));
2016-03-30 14:20:52 -07:00
}
var typeInference = new TypeInference ( typeParameters );
if (! typeInference . UnifyMultipleTerms ( typesOfMethodParameters , typesOfMethodArguments ))
{
return null ;
}
IEnumerable < Type > inferredTypeParameters = typeParameters . Select ( typeInference . GetInferredType );
2021-04-13 00:44:05 +05:00
if ( inferredTypeParameters . Any ( static inferredType => inferredType == null ))
2016-03-30 14:20:52 -07:00
{
return null ;
}
try
{
MethodInfo instantiatedMethod = genericMethod . MakeGenericMethod ( inferredTypeParameters . ToArray ());
2022-06-07 01:14:53 +08:00
s_tracer . WriteLine ( "Inference successful: {0}" , instantiatedMethod );
2016-03-30 14:20:52 -07:00
return instantiatedMethod ;
}
catch ( ArgumentException e )
{
// Inference failure - most likely due to generic constraints being violated (i.e. where T: IEnumerable)
2016-07-29 13:02:49 -07:00
s_tracer . WriteLine ( "Inference failure: {0}" , e . Message );
2016-03-30 14:20:52 -07:00
return null ;
}
}
}
2016-07-29 13:02:49 -07:00
private readonly HashSet < Type >[] _typeParameterIndexToSetOfInferenceCandidates ;
2016-03-30 14:20:52 -07:00
#if DEBUG
2016-07-29 13:02:49 -07:00
private readonly HashSet < Type > _typeParametersOfTheMethod ;
2016-03-30 14:20:52 -07:00
#endif
internal TypeInference ( ICollection < Type > typeParameters )
{
#if DEBUG
Dbg . Assert ( typeParameters != null , "Caller should verify that typeParameters != null" );
Dbg . Assert (
typeParameters . All ( t => t . IsGenericParameter ),
"Caller should verify that typeParameters are really generic type parameters" );
#endif
2016-07-29 13:02:49 -07:00
_typeParameterIndexToSetOfInferenceCandidates = new HashSet < Type >[ typeParameters . Count ];
2016-03-30 14:20:52 -07:00
#if DEBUG
2021-04-13 00:44:05 +05:00
List < int > listOfTypeParameterPositions = typeParameters . Select ( static t => t . GenericParameterPosition ). ToList ();
2016-03-30 14:20:52 -07:00
listOfTypeParameterPositions . Sort ();
Dbg . Assert (
2016-07-29 13:02:49 -07:00
listOfTypeParameterPositions . Count == listOfTypeParameterPositions . Distinct (). Count (),
2016-03-30 14:20:52 -07:00
"No type parameters should occupy the same position" );
Dbg . Assert (
2016-07-29 13:02:49 -07:00
listOfTypeParameterPositions . All ( p => p >= 0 ),
2016-03-30 14:20:52 -07:00
"Type parameter positions should be between 0 and #ofParams" );
Dbg . Assert (
2016-07-29 13:02:49 -07:00
listOfTypeParameterPositions . All ( p => p < _typeParameterIndexToSetOfInferenceCandidates . Length ),
2016-03-30 14:20:52 -07:00
"Type parameter positions should be between 0 and #ofParams" );
2016-07-29 13:02:49 -07:00
_typeParametersOfTheMethod = new HashSet < Type >();
2016-03-30 14:20:52 -07:00
foreach ( Type t in typeParameters )
{
2016-07-29 13:02:49 -07:00
_typeParametersOfTheMethod . Add ( t );
2016-03-30 14:20:52 -07:00
}
#endif
}
internal Type GetInferredType ( Type typeParameter )
{
#if DEBUG
Dbg . Assert ( typeParameter != null , "Caller should verify typeParameter != null" );
Dbg . Assert (
2016-07-29 13:02:49 -07:00
_typeParametersOfTheMethod . Contains ( typeParameter ),
2016-08-29 18:06:24 +00:00
"Caller should verify that typeParameter is actually a generic type parameter of the method" );
2016-03-30 14:20:52 -07:00
#endif
ICollection < Type > inferenceCandidates =
2016-07-29 13:02:49 -07:00
_typeParameterIndexToSetOfInferenceCandidates [ typeParameter . GenericParameterPosition ];
2016-03-30 14:20:52 -07:00
2021-04-13 00:44:05 +05:00
if (( inferenceCandidates != null ) && ( inferenceCandidates . Any ( static t => t == typeof ( LanguagePrimitives . Null ))))
2016-03-30 14:20:52 -07:00
{
2021-04-13 00:44:05 +05:00
Type firstValueType = inferenceCandidates . FirstOrDefault ( static t => t . IsValueType );
2016-03-30 14:20:52 -07:00
if ( firstValueType != null )
{
2016-07-29 13:02:49 -07:00
s_tracer . WriteLine ( "Cannot reconcile null and {0} (a value type)" , firstValueType );
2016-03-30 14:20:52 -07:00
inferenceCandidates = null ;
2016-07-29 13:02:49 -07:00
_typeParameterIndexToSetOfInferenceCandidates [ typeParameter . GenericParameterPosition ] = null ;
2016-03-30 14:20:52 -07:00
}
else
{
2021-04-13 00:44:05 +05:00
inferenceCandidates = inferenceCandidates . Where ( static t => t != typeof ( LanguagePrimitives . Null )). ToList ();
2016-03-30 14:20:52 -07:00
if ( inferenceCandidates . Count == 0 )
{
inferenceCandidates = null ;
2016-07-29 13:02:49 -07:00
_typeParameterIndexToSetOfInferenceCandidates [ typeParameter . GenericParameterPosition ] = null ;
2016-03-30 14:20:52 -07:00
}
}
}
if (( inferenceCandidates != null ) && ( inferenceCandidates . Count > 1 ))
{
// "base class" assignability-wise (to account for interfaces)
Type commonBaseClass = inferenceCandidates . FirstOrDefault (
2016-07-29 13:02:49 -07:00
potentiallyCommonBaseClass =>
2016-03-30 14:20:52 -07:00
inferenceCandidates . All (
otherCandidate =>
otherCandidate == potentiallyCommonBaseClass ||
potentiallyCommonBaseClass . IsAssignableFrom ( otherCandidate )));
if ( commonBaseClass != null )
{
inferenceCandidates . Clear ();
inferenceCandidates . Add ( commonBaseClass );
}
else
{
2016-07-29 13:02:49 -07:00
s_tracer . WriteLine ( "Multiple unreconcilable inferences for type parameter {0}" , typeParameter );
2016-03-30 14:20:52 -07:00
inferenceCandidates = null ;
2016-07-29 13:02:49 -07:00
_typeParameterIndexToSetOfInferenceCandidates [ typeParameter . GenericParameterPosition ] = null ;
2016-03-30 14:20:52 -07:00
}
}
2020-07-30 16:06:38 -07:00
if ( inferenceCandidates == null )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
s_tracer . WriteLine ( "Couldn't infer type parameter {0}" , typeParameter );
2016-03-30 14:20:52 -07:00
return null ;
}
else
{
Dbg . Assert ( inferenceCandidates . Count == 1 , "inferenceCandidates should contain exactly 1 element at this point" );
return inferenceCandidates . Single ();
}
}
internal bool UnifyMultipleTerms ( IEnumerable < Type > parameterTypes , IEnumerable < Type > argumentTypes )
{
List < Type > leftList = parameterTypes . ToList ();
List < Type > rightList = argumentTypes . ToList ();
if ( leftList . Count != rightList . Count )
{
2016-07-29 13:02:49 -07:00
s_tracer . WriteLine ( "Mismatch in number of parameters and arguments" );
2016-03-30 14:20:52 -07:00
return false ;
}
for ( int i = 0 ; i < leftList . Count ; i ++)
{
if (! this . Unify ( leftList [ i ], rightList [ i ]))
{
2016-07-29 13:02:49 -07:00
s_tracer . WriteLine ( "Couldn't unify {0} with {1}" , leftList [ i ], rightList [ i ]);
2016-03-30 14:20:52 -07:00
return false ;
}
}
return true ;
}
private bool Unify ( Type parameterType , Type argumentType )
{
2018-04-13 15:01:41 -07:00
if (! parameterType . ContainsGenericParameters )
2016-03-30 14:20:52 -07:00
{
return true ;
}
if ( parameterType . IsGenericParameter )
{
#if DEBUG
Dbg . Assert (
2016-07-29 13:02:49 -07:00
_typeParametersOfTheMethod . Contains ( parameterType ),
2016-08-29 18:06:24 +00:00
"Only uninstantiated generic type parameters encountered in real life, should be the ones coming from the method" );
2016-03-30 14:20:52 -07:00
#endif
2016-07-29 13:02:49 -07:00
HashSet < Type > inferenceCandidates = _typeParameterIndexToSetOfInferenceCandidates [ parameterType . GenericParameterPosition ];
2020-07-30 16:06:38 -07:00
if ( inferenceCandidates == null )
2016-03-30 14:20:52 -07:00
{
inferenceCandidates = new HashSet < Type >();
2016-07-29 13:02:49 -07:00
_typeParameterIndexToSetOfInferenceCandidates [ parameterType . GenericParameterPosition ] = inferenceCandidates ;
2016-03-30 14:20:52 -07:00
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
inferenceCandidates . Add ( argumentType );
2016-07-29 13:02:49 -07:00
s_tracer . WriteLine ( "Inferred {0} => {1}" , parameterType , argumentType );
2016-03-30 14:20:52 -07:00
return true ;
}
if ( parameterType . IsArray )
{
if ( argumentType == typeof ( LanguagePrimitives . Null ))
{
return true ;
}
if ( argumentType . IsArray && parameterType . GetArrayRank () == argumentType . GetArrayRank ())
{
return this . Unify ( parameterType . GetElementType (), argumentType . GetElementType ());
}
2016-07-29 13:02:49 -07:00
s_tracer . WriteLine ( "Couldn't unify array {0} with {1}" , parameterType , argumentType );
2016-03-30 14:20:52 -07:00
return false ;
}
if ( parameterType . IsByRef )
{
2018-04-13 15:01:41 -07:00
if ( argumentType . IsGenericType && argumentType . GetGenericTypeDefinition () == typeof ( PSReference <>))
2016-03-30 14:20:52 -07:00
{
Type referencedType = argumentType . GetGenericArguments ()[ 0 ];
if ( referencedType == typeof ( LanguagePrimitives . Null ))
{
return true ;
}
else
{
return this . Unify (
parameterType . GetElementType (),
referencedType );
}
}
else
{
2016-07-29 13:02:49 -07:00
s_tracer . WriteLine ( "Couldn't unify reference type {0} with {1}" , parameterType , argumentType );
2016-03-30 14:20:52 -07:00
return false ;
}
}
2018-04-13 15:01:41 -07:00
if ( parameterType . IsGenericType && parameterType . GetGenericTypeDefinition () == typeof ( Nullable <>))
2016-03-30 14:20:52 -07:00
{
if ( argumentType == typeof ( LanguagePrimitives . Null ))
{
return true ;
}
return this . Unify ( parameterType . GetGenericArguments ()[ 0 ], argumentType );
}
2018-04-13 15:01:41 -07:00
if ( parameterType . IsGenericType )
2016-03-30 14:20:52 -07:00
{
if ( argumentType == typeof ( LanguagePrimitives . Null ))
{
return true ;
}
return this . UnifyConstructedType ( parameterType , argumentType );
}
Dbg . Assert ( false , "Unrecognized kind of type" );
2016-07-29 13:02:49 -07:00
s_tracer . WriteLine ( "Unrecognized kind of type: {0}" , parameterType );
2016-03-30 14:20:52 -07:00
return false ;
}
private bool UnifyConstructedType ( Type parameterType , Type argumentType )
{
2018-04-13 15:01:41 -07:00
Dbg . Assert ( parameterType . IsGenericType , "Caller should verify parameterType.IsGenericType before calling this method" );
2016-03-30 14:20:52 -07:00
if ( IsEqualGenericTypeDefinition ( parameterType , argumentType ))
{
IEnumerable < Type > typeParametersOfParameterType = parameterType . GetGenericArguments ();
IEnumerable < Type > typeArgumentsOfArgumentType = argumentType . GetGenericArguments ();
return this . UnifyMultipleTerms ( typeParametersOfParameterType , typeArgumentsOfArgumentType );
}
Type [] interfaces = argumentType . GetInterfaces ();
for ( int i = 0 ; i < interfaces . Length ; i ++)
{
if ( IsEqualGenericTypeDefinition ( parameterType , interfaces [ i ]))
{
return UnifyConstructedType ( parameterType , interfaces [ i ]);
}
}
2018-04-13 15:01:41 -07:00
Type baseType = argumentType . BaseType ;
2016-03-30 14:20:52 -07:00
while ( baseType != null )
{
if ( IsEqualGenericTypeDefinition ( parameterType , baseType ))
{
return UnifyConstructedType ( parameterType , baseType );
}
2018-12-23 22:20:06 -08:00
2018-04-13 15:01:41 -07:00
baseType = baseType . BaseType ;
2016-03-30 14:20:52 -07:00
}
2016-07-29 13:02:49 -07:00
s_tracer . WriteLine ( "Attempt to unify different constructed types: {0} and {1}" , parameterType , argumentType );
2016-03-30 14:20:52 -07:00
return false ;
}
2016-08-02 19:00:50 -07:00
private static bool IsEqualGenericTypeDefinition ( Type parameterType , Type argumentType )
2016-03-30 14:20:52 -07:00
{
2018-04-13 15:01:41 -07:00
Dbg . Assert ( parameterType . IsGenericType , "Caller should verify parameterType.IsGenericType before calling this method" );
2016-03-30 14:20:52 -07:00
2018-04-13 15:01:41 -07:00
if (! argumentType . IsGenericType )
2016-03-30 14:20:52 -07:00
{
return false ;
}
return parameterType . GetGenericTypeDefinition () == argumentType . GetGenericTypeDefinition ();
}
}
}