2018-02-13 09:23:53 -08:00
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
2016-03-30 14:20:52 -07:00
using System.Collections ;
using System.Collections.Generic ;
using System.Collections.Specialized ;
using System.ComponentModel ;
using System.Data ;
2019-04-30 10:25:11 +05:00
using System.Diagnostics.CodeAnalysis ;
2016-03-30 14:20:52 -07:00
using System.Globalization ;
2019-04-30 10:25:11 +05:00
using System.IO ;
2016-03-30 14:20:52 -07:00
using System.Linq ;
using System.Linq.Expressions ;
2019-04-30 10:25:11 +05:00
using System.Management.Automation.Internal ;
2016-03-30 14:20:52 -07:00
using System.Management.Automation.Language ;
2019-04-30 10:25:11 +05:00
using System.Management.Automation.Runspaces ;
2016-03-30 14:20:52 -07:00
using System.Reflection ;
2018-04-06 17:01:04 -07:00
using System.Reflection.Emit ;
2016-03-30 14:20:52 -07:00
using System.Runtime.CompilerServices ;
2019-04-30 10:25:11 +05:00
using System.Text ;
2016-03-30 14:20:52 -07:00
using System.Text.RegularExpressions ;
using System.Xml ;
2019-04-30 10:25:11 +05:00
2016-03-30 14:20:52 -07:00
using Dbg = System . Management . Automation . Diagnostics ;
2018-04-06 17:01:04 -07:00
using MethodCacheEntry = System . Management . Automation . DotNetAdapter . MethodCacheEntry ;
2018-06-21 09:57:14 -07:00
#if ! UNIX
2016-03-30 14:20:52 -07:00
using System.DirectoryServices ;
2018-06-21 09:57:14 -07:00
using System.Management ;
2016-03-30 14:20:52 -07:00
#endif
#pragma warning disable 1634 , 1691 // Stops compiler from warning about unknown warnings
#pragma warning disable 56500
namespace System.Management.Automation
{
#region public type converters
/// <summary>
/// Defines a base class implemented when you need to customize the type conversion for a target class.
/// </summary>
2017-01-16 13:31:14 -08:00
/// <remarks>
2016-03-30 14:20:52 -07:00
/// There are two ways of associating the PSTypeConverter with its target class:
/// - Through the type configuration file.
/// - By applying a TypeConverterAttribute to the target class.
2017-01-16 13:31:14 -08:00
///
/// Unlike System.ComponentModel.TypeConverter, PSTypeConverter can be applied to a family of types (like all types derived from System.Enum).
2016-03-30 14:20:52 -07:00
/// PSTypeConverter has two main differences from TypeConverter:
/// - It can be applied to a family of types and not only the one type as in TypeConverter. In order to do that
/// ConvertFrom and CanConvertFrom receive destinationType to know to which type specifically we are converting sourceValue.
/// - ConvertTo and ConvertFrom receive formatProvider and ignoreCase.
/// Other differences to System.ComponentModel.TypeConverter:
/// - There is no ITypeDescriptorContext.
/// - This class is abstract
/// </remarks>
public abstract class PSTypeConverter
{
private static object GetSourceValueAsObject ( PSObject sourceValue )
{
if ( sourceValue == null )
{
return null ;
}
if ( sourceValue . BaseObject is PSCustomObject )
{
return sourceValue ;
}
else
{
return PSObject . Base ( sourceValue );
}
}
/// <summary>
/// Determines if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="sourceValue">Value supposedly *not* of the types supported by this converted to be converted to the <paramref name="destinationType"/> parameter.</param>
/// <param name="destinationType">One of the types supported by this converter to which the <paramref name="sourceValue"/> parameter should be converted.</param>
2016-03-30 14:20:52 -07:00
/// <returns>True if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter, otherwise false.</returns>
public abstract bool CanConvertFrom ( object sourceValue , Type destinationType );
/// <summary>
/// Determines if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="sourceValue">Value supposedly *not* of the types supported by this converted to be converted to the <paramref name="destinationType"/> parameter.</param>
/// <param name="destinationType">One of the types supported by this converter to which the <paramref name="sourceValue"/> parameter should be converted.</param>
2016-03-30 14:20:52 -07:00
/// <returns>True if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter, otherwise false.</returns>
public virtual bool CanConvertFrom ( PSObject sourceValue , Type destinationType )
{
return this . CanConvertFrom ( GetSourceValueAsObject ( sourceValue ), destinationType );
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Converts the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="sourceValue">Value supposedly *not* of the types supported by this converted to be converted to the <paramref name="destinationType"/> parameter.</param>
/// <param name="destinationType">One of the types supported by this converter to which the <paramref name="sourceValue"/> parameter should be converted to.</param>
2018-12-28 16:39:06 +05:00
/// <param name="formatProvider">The format provider to use like in IFormattable's ToString.</param>
2018-12-29 08:25:49 +05:00
/// <param name="ignoreCase">True if case should be ignored.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The <paramref name="sourceValue"/> parameter converted to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.</returns>
2018-12-29 08:25:49 +05:00
/// <exception cref="InvalidCastException">If no conversion was possible.</exception>
2016-03-30 14:20:52 -07:00
public abstract object ConvertFrom ( object sourceValue , Type destinationType , IFormatProvider formatProvider , bool ignoreCase );
/// <summary>
2019-01-06 04:40:24 +05:00
/// Converts the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="sourceValue">Value supposedly *not* of the types supported by this converted to be converted to the <paramref name="destinationType"/> parameter.</param>
/// <param name="destinationType">One of the types supported by this converter to which the <paramref name="sourceValue"/> parameter should be converted to.</param>
2018-12-28 16:39:06 +05:00
/// <param name="formatProvider">The format provider to use like in IFormattable's ToString.</param>
2018-12-29 08:25:49 +05:00
/// <param name="ignoreCase">True if case should be ignored.</param>
2018-12-25 21:43:03 +05:00
/// <returns>The <paramref name="sourceValue"/> parameter converted to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.</returns>
2018-12-29 08:25:49 +05:00
/// <exception cref="InvalidCastException">If no conversion was possible.</exception>
2016-03-30 14:20:52 -07:00
public virtual object ConvertFrom ( PSObject sourceValue , Type destinationType , IFormatProvider formatProvider , bool ignoreCase )
{
return this . ConvertFrom ( GetSourceValueAsObject ( sourceValue ), destinationType , formatProvider , ignoreCase );
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="sourceValue">Value supposedly from one of the types supported by this converter to be converted to the <paramref name="destinationType"/> parameter.</param>
/// <param name="destinationType">Type to convert the <paramref name="sourceValue"/> parameter, supposedly not one of the types supported by the converter.</param>
2016-03-30 14:20:52 -07:00
/// <returns>True if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter, otherwise false.</returns>
public abstract bool CanConvertTo ( object sourceValue , Type destinationType );
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="sourceValue">Value supposedly from one of the types supported by this converter to be converted to the <paramref name="destinationType"/> parameter.</param>
/// <param name="destinationType">Type to convert the <paramref name="sourceValue"/> parameter, supposedly not one of the types supported by the converter.</param>
2016-03-30 14:20:52 -07:00
/// <returns>True if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter, otherwise false.</returns>
public virtual bool CanConvertTo ( PSObject sourceValue , Type destinationType )
{
return this . CanConvertTo ( GetSourceValueAsObject ( sourceValue ), destinationType );
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Converts the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="sourceValue">Value supposedly from one of the types supported by this converter to be converted to the <paramref name="destinationType"/> parameter.</param>
/// <param name="destinationType">Type to convert the <paramref name="sourceValue"/> parameter, supposedly not one of the types supported by the converter.</param>
2018-12-28 16:39:06 +05:00
/// <param name="formatProvider">The format provider to use like in IFormattable's ToString.</param>
2018-12-29 08:25:49 +05:00
/// <param name="ignoreCase">True if case should be ignored.</param>
2018-12-25 21:43:03 +05:00
/// <returns>SourceValue converted to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.</returns>
2018-12-29 08:25:49 +05:00
/// <exception cref="InvalidCastException">If no conversion was possible.</exception>
2016-03-30 14:20:52 -07:00
public abstract object ConvertTo ( object sourceValue , Type destinationType , IFormatProvider formatProvider , bool ignoreCase );
/// <summary>
2019-01-06 04:40:24 +05:00
/// Converts the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="sourceValue">Value supposedly from one of the types supported by this converter to be converted to the <paramref name="destinationType"/> parameter.</param>
/// <param name="destinationType">Type to convert the <paramref name="sourceValue"/> parameter, supposedly not one of the types supported by the converter.</param>
2018-12-28 16:39:06 +05:00
/// <param name="formatProvider">The format provider to use like in IFormattable's ToString.</param>
2018-12-29 08:25:49 +05:00
/// <param name="ignoreCase">True if case should be ignored.</param>
2018-12-25 21:43:03 +05:00
/// <returns>SourceValue converted to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.</returns>
2018-12-29 08:25:49 +05:00
/// <exception cref="InvalidCastException">If no conversion was possible.</exception>
2016-03-30 14:20:52 -07:00
public virtual object ConvertTo ( PSObject sourceValue , Type destinationType , IFormatProvider formatProvider , bool ignoreCase )
{
return this . ConvertTo ( GetSourceValueAsObject ( sourceValue ), destinationType , formatProvider , ignoreCase );
}
}
/// <summary>
/// Enables a type that only has conversion from string to be converted from all other
2019-01-06 04:40:24 +05:00
/// types through string.
2016-03-30 14:20:52 -07:00
/// </summary>
/// <remarks>
/// It is permitted to subclass <see cref="ConvertThroughString"/>
/// but there is no established scenario for doing this, nor has it been tested.
/// </remarks>
public class ConvertThroughString : PSTypeConverter
{
/// <summary>
2019-01-06 04:40:24 +05:00
/// This will return false only if sourceValue is string.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="sourceValue">Value to convert from.</param>
/// <param name="destinationType">Ignored.</param>
2018-12-25 21:43:03 +05:00
/// <returns>False only if sourceValue is string.</returns>
2016-03-30 14:20:52 -07:00
public override bool CanConvertFrom ( object sourceValue , Type destinationType )
{
// This if avoids infinite recursion.
if ( sourceValue is string )
{
return false ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return true ;
}
/// <summary>
/// Converts to destinationType by first converting sourceValue to string
/// and then converting the result to destinationType.
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="sourceValue">The value to convert from.</param>
/// <param name="destinationType">The type this converter is associated with.</param>
/// <param name="formatProvider">The IFormatProvider to use.</param>
2018-12-29 08:25:49 +05:00
/// <param name="ignoreCase">True if case should be ignored.</param>
2018-12-25 21:43:03 +05:00
/// <returns>SourceValue converted to destinationType.</returns>
2018-12-28 16:39:06 +05:00
/// <exception cref="PSInvalidCastException">When no conversion was possible.</exception>
2016-03-30 14:20:52 -07:00
public override object ConvertFrom ( object sourceValue , Type destinationType , IFormatProvider formatProvider , bool ignoreCase )
{
string sourceAsString = ( string ) LanguagePrimitives . ConvertTo ( sourceValue , typeof ( string ), formatProvider );
return LanguagePrimitives . ConvertTo ( sourceAsString , destinationType , formatProvider );
}
/// <summary>
/// Returns false, since this converter is not designed to be used to
/// convert from the type associated with the converted to other types.
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="sourceValue">The value to convert from.</param>
/// <param name="destinationType">The value to convert from.</param>
2018-12-25 21:43:03 +05:00
/// <returns>False.</returns>
2016-03-30 14:20:52 -07:00
public override bool CanConvertTo ( object sourceValue , Type destinationType )
{
return false ;
}
/// <summary>
/// Throws NotSupportedException, since this converter is not designed to be used to
/// convert from the type associated with the converted to other types.
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="sourceValue">The value to convert from.</param>
/// <param name="destinationType">The value to convert from.</param>
/// <param name="formatProvider">The IFormatProvider to use.</param>
2018-12-29 08:25:49 +05:00
/// <param name="ignoreCase">True if case should be ignored.</param>
2016-03-30 14:20:52 -07:00
/// <returns>This method does not return a value.</returns>
2018-12-28 16:39:06 +05:00
/// <exception cref="NotSupportedException">NotSupportedException is always thrown.</exception>
2016-03-30 14:20:52 -07:00
public override object ConvertTo ( object sourceValue , Type destinationType , IFormatProvider formatProvider , bool ignoreCase )
{
throw PSTraceSource . NewNotSupportedException ();
}
}
#endregion public type converters
/// <summary>
/// The ranking of versions for comparison purposes (used in overload resolution.)
/// A larger value means the conversion is better.
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// Note that the lower nibble is all ones for named conversion ranks. This allows for
/// conversions with rankings in between the named values. For example, int=>string[]
/// is value dependent, if the conversion from int=>string succeeds, then an array is
/// created, otherwise we try some other conversion. The int=>string[] conversion should
/// be worse than int=>string, but it is probably better than many other conversions, so
/// we want it to be only slightly worse than int=>string.
/// </summary>
/// <remarks>
/// ValueDependent is a flag, but we don't mark the enum as flags because it really isn't
/// a flags enum.
/// To make debugging easier, the "in between" conversions are all named, though there are
/// no references in the code. They all use the suffix S2A which means "scalar to array".
/// </remarks>
internal enum ConversionRank
{
2016-07-29 13:02:49 -07:00
None = 0x0000 ,
2016-03-30 14:20:52 -07:00
UnrelatedArraysS2A = 0x0007 ,
2016-07-29 13:02:49 -07:00
UnrelatedArrays = 0x000F ,
ToStringS2A = 0x0017 ,
ToString = 0x001F ,
CustomS2A = 0x0027 ,
Custom = 0x002F ,
IConvertibleS2A = 0x0037 ,
IConvertible = 0x003F ,
ImplicitCastS2A = 0x0047 ,
ImplicitCast = 0x004F ,
ExplicitCastS2A = 0x0057 ,
ExplicitCast = 0x005F ,
ConstructorS2A = 0x0067 ,
Constructor = 0x006F ,
Create = 0x0073 ,
ParseS2A = 0x0077 ,
Parse = 0x007F ,
PSObjectS2A = 0x0087 ,
PSObject = 0x008F ,
LanguageS2A = 0x0097 ,
Language = 0x009F ,
NullToValue = 0x00AF ,
NullToRef = 0x00BF ,
NumericExplicitS2A = 0x00C7 ,
NumericExplicit = 0x00CF ,
2016-03-30 14:20:52 -07:00
NumericExplicit1S2A = 0x00D7 ,
2016-07-29 13:02:49 -07:00
NumericExplicit1 = 0x00DF ,
NumericStringS2A = 0x00E7 ,
NumericString = 0x00EF ,
NumericImplicitS2A = 0x00F7 ,
NumericImplicit = 0x00FF ,
AssignableS2A = 0x0107 ,
Assignable = 0x010F ,
IdentityS2A = 0x0117 ,
2016-03-30 14:20:52 -07:00
StringToCharArray = 0x011A ,
2016-07-29 13:02:49 -07:00
Identity = 0x011F ,
2016-03-30 14:20:52 -07:00
2016-07-29 13:02:49 -07:00
ValueDependent = 0xFFF7 ,
2016-03-30 14:20:52 -07:00
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Defines language support methods.
2016-03-30 14:20:52 -07:00
/// </summary>
[SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Refactoring LanguagePrimitives takes lot of dev/test effort. Since V1 code is already shipped, we tend to exclude this message.")]
public static class LanguagePrimitives
{
[TraceSource("ETS", "Extended Type System")]
2016-08-02 19:00:50 -07:00
private static PSTraceSource s_tracer = PSTraceSource . GetTracer ( "ETS" , "Extended Type System" );
2016-03-30 14:20:52 -07:00
internal delegate void MemberNotFoundError ( PSObject pso , DictionaryEntry property , Type resultType );
internal delegate void MemberSetValueError ( SetValueException e );
2018-12-22 01:26:06 -08:00
internal const string OrderedAttribute = "ordered" ;
2019-06-18 20:56:43 -07:00
internal const string DoublePrecision = "G15" ;
internal const string SinglePrecision = "G7" ;
2016-03-30 14:20:52 -07:00
internal static void CreateMemberNotFoundError ( PSObject pso , DictionaryEntry property , Type resultType )
{
2018-12-22 01:26:06 -08:00
string availableProperties = GetAvailableProperties ( pso );
2016-03-30 14:20:52 -07:00
string message = StringUtil . Format ( ExtendedTypeSystem . PropertyNotFound , property . Key . ToString (), resultType . FullName , availableProperties );
typeConversion . WriteLine ( "Issuing an error message about not being able to create an object from hashtable." );
throw new InvalidOperationException ( message );
}
internal static void CreateMemberSetValueError ( SetValueException e )
{
typeConversion . WriteLine ( "Issuing an error message about not being able to set the properties for an object." );
throw e ;
}
static LanguagePrimitives ()
{
RebuildConversionCache ();
InitializeGetEnumerableCache ();
#if ! CORECLR // AppDomain Not In CoreCLR
AppDomain . CurrentDomain . AssemblyResolve += AssemblyResolveHelper ;
#endif
}
internal static void UpdateTypeConvertFromTypeTable ( string typeName )
{
2016-07-29 13:02:49 -07:00
lock ( s_converterCache )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
var toRemove = s_converterCache . Keys . Where (
2016-03-30 14:20:52 -07:00
conv => string . Equals ( conv . to . FullName , typeName , StringComparison . OrdinalIgnoreCase ) ||
string . Equals ( conv . from . FullName , typeName , StringComparison . OrdinalIgnoreCase )). ToArray ();
foreach ( var k in toRemove )
{
2016-07-29 13:02:49 -07:00
s_converterCache . Remove ( k );
2016-03-30 14:20:52 -07:00
}
// Note we do not clear possibleTypeConverter even when removing.
//
// The conversion cache and the possibleTypeConverter cache are process wide, but
// the type table used for any specific conversion is runspace specific.
2016-07-29 13:02:49 -07:00
s_possibleTypeConverter [ typeName ] = true ;
2016-03-30 14:20:52 -07:00
}
}
#region GetEnumerable / GetEnumerator
/// <summary>
/// This is a wrapper class that allows us to use the generic IEnumerable
/// implementation of an object when we can't use it's non-generic
/// implementation.
/// </summary>
private class EnumerableTWrapper : IEnumerable
{
2016-07-29 13:02:49 -07:00
private object _enumerable ;
private Type _enumerableType ;
private DynamicMethod _getEnumerator ;
2016-03-30 14:20:52 -07:00
internal EnumerableTWrapper ( object enumerable , Type enumerableType )
{
_enumerable = enumerable ;
_enumerableType = enumerableType ;
CreateGetEnumerator ();
}
private void CreateGetEnumerator ()
{
_getEnumerator = new DynamicMethod ( "GetEnumerator" , typeof ( object ),
2018-04-13 15:01:41 -07:00
new Type [] { typeof ( object ) }, typeof ( LanguagePrimitives ). Module , true );
2016-03-30 14:20:52 -07:00
ILGenerator emitter = _getEnumerator . GetILGenerator ();
emitter . Emit ( OpCodes . Ldarg_0 );
emitter . Emit ( OpCodes . Castclass , _enumerableType );
MethodInfo methodInfo = _enumerableType . GetMethod ( "GetEnumerator" , new Type [] { });
emitter . Emit ( OpCodes . Callvirt , methodInfo );
emitter . Emit ( OpCodes . Ret );
}
#region IEnumerable Members
public IEnumerator GetEnumerator ()
{
return ( IEnumerator ) _getEnumerator . Invoke ( null , new object [] { _enumerable });
}
#endregion
}
private static IEnumerable GetEnumerableFromIEnumerableT ( object obj )
{
foreach ( Type i in obj . GetType (). GetInterfaces ())
{
2018-04-13 15:01:41 -07:00
if ( i . IsGenericType && i . GetGenericTypeDefinition () == typeof ( IEnumerable <>))
2016-03-30 14:20:52 -07:00
{
return new EnumerableTWrapper ( obj , i );
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return null ;
}
2016-07-29 13:02:49 -07:00
private delegate IEnumerable GetEnumerableDelegate ( object obj );
private static Dictionary < Type , GetEnumerableDelegate > s_getEnumerableCache = new Dictionary < Type , GetEnumerableDelegate >( 32 );
2016-03-30 14:20:52 -07:00
2016-07-29 13:02:49 -07:00
private static GetEnumerableDelegate GetOrCalculateEnumerable ( Type type )
2016-03-30 14:20:52 -07:00
{
GetEnumerableDelegate getEnumerable = null ;
2016-07-29 13:02:49 -07:00
lock ( s_getEnumerableCache )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
if (! s_getEnumerableCache . TryGetValue ( type , out getEnumerable ))
2016-03-30 14:20:52 -07:00
{
getEnumerable = CalculateGetEnumerable ( type );
2016-07-29 13:02:49 -07:00
s_getEnumerableCache . Add ( type , getEnumerable );
2016-03-30 14:20:52 -07:00
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return getEnumerable ;
}
2016-07-29 13:02:49 -07:00
private static void InitializeGetEnumerableCache ()
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
lock ( s_getEnumerableCache )
2016-03-30 14:20:52 -07:00
{
// PowerShell doesn't treat strings as enumerables so just return null.
// we also want to return null on common numeric types very quickly
2016-07-29 13:02:49 -07:00
s_getEnumerableCache . Clear ();
s_getEnumerableCache . Add ( typeof ( string ), LanguagePrimitives . ReturnNullEnumerable );
s_getEnumerableCache . Add ( typeof ( int ), LanguagePrimitives . ReturnNullEnumerable );
s_getEnumerableCache . Add ( typeof ( double ), LanguagePrimitives . ReturnNullEnumerable );
2016-03-30 14:20:52 -07:00
}
}
internal static bool IsTypeEnumerable ( Type type )
{
2017-10-19 16:44:26 -07:00
if ( type == null ) { return false ; }
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
GetEnumerableDelegate getEnumerable = GetOrCalculateEnumerable ( type );
return ( getEnumerable != LanguagePrimitives . ReturnNullEnumerable );
}
2017-10-19 16:44:26 -07:00
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns True if the language considers obj to be IEnumerable.
2017-10-19 16:44:26 -07:00
/// </summary>
/// <param name="obj">
/// IEnumerable or IEnumerable-like object
/// </param>
[SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj", Justification = "Since V1 code is already shipped, excluding this message.")]
public static bool IsObjectEnumerable ( object obj )
{
return IsTypeEnumerable ( PSObject . Base ( obj )?. GetType ());
}
2016-03-30 14:20:52 -07:00
/// <summary>
2019-01-06 04:40:24 +05:00
/// Retrieves the IEnumerable of obj or null if the language does not consider obj to be IEnumerable.
2016-03-30 14:20:52 -07:00
/// </summary>
/// <param name="obj">
/// IEnumerable or IEnumerable-like object
/// </param>
[SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj", Justification = "Since V1 code is already shipped, excluding this message.")]
public static IEnumerable GetEnumerable ( object obj )
{
2017-10-19 16:44:26 -07:00
obj = PSObject . Base ( obj );
if ( obj == null ) { return null ; }
2018-12-23 22:20:06 -08:00
2017-10-19 16:44:26 -07:00
GetEnumerableDelegate getEnumerable = GetOrCalculateEnumerable ( obj . GetType ());
2016-03-30 14:20:52 -07:00
return getEnumerable ( obj );
}
2016-07-29 13:02:49 -07:00
private static IEnumerable ReturnNullEnumerable ( object obj )
2016-03-30 14:20:52 -07:00
{
return null ;
}
2016-07-29 13:02:49 -07:00
private static IEnumerable DataTableEnumerable ( object obj )
2016-03-30 14:20:52 -07:00
{
return ((( DataTable ) obj ). Rows );
}
2016-07-29 13:02:49 -07:00
private static IEnumerable TypicalEnumerable ( object obj )
2016-03-30 14:20:52 -07:00
{
IEnumerable e = ( IEnumerable ) obj ;
try
{
// Some IEnumerable implementations just return null. Others
// raise an exception. Either of these may have a perfectly
// good generic implementation, so we'll try those if the
// non-generic is no good.
if ( e . GetEnumerator () == null )
{
return GetEnumerableFromIEnumerableT ( obj );
}
return e ;
}
catch ( Exception innerException )
{
e = GetEnumerableFromIEnumerableT ( obj );
if ( e != null )
{
return e ;
}
throw new ExtendedTypeSystemException (
"ExceptionInGetEnumerator" ,
innerException ,
ExtendedTypeSystem . EnumerationException ,
innerException . Message );
}
}
2016-07-29 13:02:49 -07:00
private static GetEnumerableDelegate CalculateGetEnumerable ( Type objectType )
2016-03-30 14:20:52 -07:00
{
if ( typeof ( DataTable ). IsAssignableFrom ( objectType ))
{
return LanguagePrimitives . DataTableEnumerable ;
}
// Don't treat IDictionary or XmlNode as enumerable...
if ( typeof ( IEnumerable ). IsAssignableFrom ( objectType )
&& ! typeof ( IDictionary ). IsAssignableFrom ( objectType )
&& ! typeof ( XmlNode ). IsAssignableFrom ( objectType ))
{
return LanguagePrimitives . TypicalEnumerable ;
}
return LanguagePrimitives . ReturnNullEnumerable ;
}
2016-07-29 13:02:49 -07:00
private static readonly CallSite < Func < CallSite , object , IEnumerator >> s_getEnumeratorSite =
2016-03-30 14:20:52 -07:00
CallSite < Func < CallSite , object , IEnumerator >>. Create ( PSEnumerableBinder . Get ());
/// <summary>
2019-01-06 04:40:24 +05:00
/// Retrieves the IEnumerator of obj or null if the language does not consider obj as capable of returning an IEnumerator.
2016-03-30 14:20:52 -07:00
/// </summary>
/// <param name="obj">
/// IEnumerable or IEnumerable-like object
/// </param>
2018-12-28 16:39:06 +05:00
/// <exception cref="ExtendedTypeSystemException">When the act of getting the enumerator throws an exception.</exception>
2016-03-30 14:20:52 -07:00
[SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj", Justification = "Since V1 code is already shipped, excluding this message for backward compatibility reasons.")]
public static IEnumerator GetEnumerator ( object obj )
{
2016-07-29 13:02:49 -07:00
var result = s_getEnumeratorSite . Target . Invoke ( s_getEnumeratorSite , obj );
2016-03-30 14:20:52 -07:00
return ( result is EnumerableOps . NonEnumerableObjectEnumerator ) ? null : result ;
}
#endregion GetEnumerable / GetEnumerator
/// <summary>
/// This method takes a an arbitrary object and wraps it in a PSDataCollection of PSObject.
/// This simplifies interacting with the PowerShell workflow activities.
/// </summary>
/// <param name="inputValue"></param>
/// <returns></returns>
public static PSDataCollection < PSObject > GetPSDataCollection ( object inputValue )
{
PSDataCollection < PSObject > result = new PSDataCollection < PSObject >();
if ( inputValue != null )
{
IEnumerator e = GetEnumerator ( inputValue );
if ( e != null )
{
while ( e . MoveNext ())
{
result . Add ( e . Current == null ? null : PSObject . AsPSObject ( e . Current ));
}
}
else
{
result . Add ( PSObject . AsPSObject ( inputValue ));
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
result . Complete ();
return result ;
}
/// <summary>
/// Used to compare two objects for equality converting the second to the type of the first, if required.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="first">First object.</param>
/// <param name="second">Object to compare first to.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if first is equal to the second.</returns>
2018-12-20 22:56:05 -08:00
public static new bool Equals ( object first , object second )
2019-06-28 14:39:34 -04:00
=> Equals ( first , second , false , CultureInfo . InvariantCulture );
2016-03-30 14:20:52 -07:00
/// <summary>
/// Used to compare two objects for equality converting the second to the type of the first, if required.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="first">First object.</param>
/// <param name="second">Object to compare first to.</param>
2017-01-16 13:31:14 -08:00
/// <param name="ignoreCase">used only if first and second are strings
2016-03-30 14:20:52 -07:00
/// to specify the type of string comparison </param>
2018-12-25 21:43:03 +05:00
/// <returns>True if first is equal to the second.</returns>
2016-03-30 14:20:52 -07:00
public static bool Equals ( object first , object second , bool ignoreCase )
2019-06-28 14:39:34 -04:00
=> Equals ( first , second , ignoreCase , CultureInfo . InvariantCulture );
2016-03-30 14:20:52 -07:00
/// <summary>
/// Used to compare two objects for equality converting the second to the type of the first, if required.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="first">First object.</param>
/// <param name="second">Object to compare first to.</param>
2017-01-16 13:31:14 -08:00
/// <param name="ignoreCase">used only if first and second are strings
2016-03-30 14:20:52 -07:00
/// to specify the type of string comparison </param>
2017-01-16 13:31:14 -08:00
/// <param name="formatProvider">the format/culture to be used. If this parameter is null,
/// CultureInfo.InvariantCulture will be used.
2016-03-30 14:20:52 -07:00
/// </param>
2018-12-25 21:43:03 +05:00
/// <returns>True if first is equal to the second.</returns>
2016-03-30 14:20:52 -07:00
public static bool Equals ( object first , object second , bool ignoreCase , IFormatProvider formatProvider )
{
// If both first and second are null it returns true.
// If one is null and the other is not it returns false.
// if (first.Equals(second)) it returns true otherwise it goes ahead with type conversion operations.
2018-12-31 20:44:16 +05:00
// If both first and second are strings it returns (string.Compare(firstString, secondString, ignoreCase) == 0).
2016-03-30 14:20:52 -07:00
// If second can be converted to the type of the first, it does so and returns first.Equals(secondConverted)
// Otherwise false is returned
if ( formatProvider == null )
{
formatProvider = CultureInfo . InvariantCulture ;
}
var culture = formatProvider as CultureInfo ;
if ( culture == null )
{
throw PSTraceSource . NewArgumentException ( "formatProvider" );
}
first = PSObject . Base ( first );
second = PSObject . Base ( second );
if ( first == null )
{
2019-06-28 14:39:34 -04:00
return IsNullLike ( second );
2016-03-30 14:20:52 -07:00
}
if ( second == null )
{
2019-06-28 14:39:34 -04:00
return IsNullLike ( first );
2016-03-30 14:20:52 -07:00
}
string secondString ;
2019-06-28 14:39:34 -04:00
if ( first is string firstString )
2016-03-30 14:20:52 -07:00
{
2016-08-02 22:25:13 -07:00
secondString = second as string ?? ( string ) LanguagePrimitives . ConvertTo ( second , typeof ( string ), culture );
2019-06-28 14:39:34 -04:00
return culture . CompareInfo . Compare (
firstString ,
secondString ,
ignoreCase ? CompareOptions . IgnoreCase : CompareOptions . None ) == 0 ;
2016-03-30 14:20:52 -07:00
}
2019-06-28 14:39:34 -04:00
if ( first . Equals ( second ))
{
return true ;
}
2016-03-30 14:20:52 -07:00
Type firstType = first . GetType ();
Type secondType = second . GetType ();
int firstIndex = LanguagePrimitives . TypeTableIndex ( firstType );
int secondIndex = LanguagePrimitives . TypeTableIndex ( secondType );
if (( firstIndex != - 1 ) && ( secondIndex != - 1 ))
{
return LanguagePrimitives . NumericCompare ( first , second , firstIndex , secondIndex ) == 0 ;
}
if ( firstType == typeof ( char ) && ignoreCase )
{
secondString = second as string ;
if ( secondString != null && secondString . Length == 1 )
{
char firstAsUpper = culture . TextInfo . ToUpper (( char ) first );
char secondAsUpper = culture . TextInfo . ToUpper ( secondString [ 0 ]);
return firstAsUpper . Equals ( secondAsUpper );
}
else if ( secondType == typeof ( char ))
{
char firstAsUpper = culture . TextInfo . ToUpper (( char ) first );
char secondAsUpper = culture . TextInfo . ToUpper (( char ) second );
return firstAsUpper . Equals ( secondAsUpper );
}
}
try
{
object secondConverted = LanguagePrimitives . ConvertTo ( second , firstType , culture );
return first . Equals ( secondConverted );
}
catch ( InvalidCastException )
{
}
return false ;
}
2018-08-10 19:20:27 +02:00
/// <summary>
/// Helper method for [Try]Compare to determine object ordering with null.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="value">The numeric value to compare to null.</param>
2019-06-28 14:39:34 -04:00
/// <param name="numberIsRightHandSide">True if the number to compare is on the right hand side in the comparison.</param>
2018-08-10 19:20:27 +02:00
private static int CompareObjectToNull ( object value , bool numberIsRightHandSide )
{
var i = numberIsRightHandSide ? - 1 : 1 ;
// If it's a positive number, including 0, it's greater than null
// for everything else it's less than zero...
2019-06-28 14:39:34 -04:00
return value switch
2018-08-10 19:20:27 +02:00
{
2019-06-28 14:39:34 -04:00
Int16 i16 => Math . Sign ( i16 ) < 0 ? - i : i ,
Int32 i32 => Math . Sign ( i32 ) < 0 ? - i : i ,
Int64 i64 => Math . Sign ( i64 ) < 0 ? - i : i ,
sbyte s => Math . Sign ( s ) < 0 ? - i : i ,
float f => Math . Sign ( f ) < 0 ? - i : i ,
double d => Math . Sign ( d ) < 0 ? - i : i ,
decimal m => Math . Sign ( m ) < 0 ? - i : i ,
_ => IsNullLike ( value ) ? 0 : i
};
2018-08-10 19:20:27 +02:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Compare first and second, converting second to the
/// type of the first, if necessary.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="first">First comparison value.</param>
/// <param name="second">Second comparison value.</param>
2018-12-25 21:43:03 +05:00
/// <returns>Less than zero if first is smaller than second, more than
/// zero if it is greater or zero if they are the same.</returns>
2016-03-30 14:20:52 -07:00
/// <exception cref="System.ArgumentException">
/// <paramref name="first"/> does not implement IComparable or <paramref name="second"/> cannot be converted
/// to the type of <paramref name="first"/>.
/// </exception>
public static int Compare ( object first , object second )
2019-06-28 14:39:34 -04:00
=> LanguagePrimitives . Compare ( first , second , false , CultureInfo . InvariantCulture );
2016-03-30 14:20:52 -07:00
/// <summary>
/// Compare first and second, converting second to the
/// type of the first, if necessary.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="first">First comparison value.</param>
/// <param name="second">Second comparison value.</param>
2018-12-28 16:39:06 +05:00
/// <param name="ignoreCase">Used if both values are strings.</param>
2018-12-25 21:43:03 +05:00
/// <returns>Less than zero if first is smaller than second, more than
/// zero if it is greater or zero if they are the same.</returns>
2016-03-30 14:20:52 -07:00
/// <exception cref="System.ArgumentException">
/// <paramref name="first"/> does not implement IComparable or <paramref name="second"/> cannot be converted
/// to the type of <paramref name="first"/>.
/// </exception>
public static int Compare ( object first , object second , bool ignoreCase )
2019-06-28 14:39:34 -04:00
=> LanguagePrimitives . Compare ( first , second , ignoreCase , CultureInfo . InvariantCulture );
2016-03-30 14:20:52 -07:00
/// <summary>
/// Compare first and second, converting second to the
/// type of the first, if necessary.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="first">First comparison value.</param>
/// <param name="second">Second comparison value.</param>
2018-12-28 16:39:06 +05:00
/// <param name="ignoreCase">Used if both values are strings.</param>
/// <param name="formatProvider">Used in type conversions and if both values are strings.</param>
2018-12-25 21:43:03 +05:00
/// <returns>Less than zero if first is smaller than second, more than
/// zero if it is greater or zero if they are the same.</returns>
2016-03-30 14:20:52 -07:00
/// <exception cref="System.ArgumentException">
/// <paramref name="first"/> does not implement IComparable or <paramref name="second"/> cannot be converted
/// to the type of <paramref name="first"/>.
/// </exception>
public static int Compare ( object first , object second , bool ignoreCase , IFormatProvider formatProvider )
{
2019-06-28 14:39:34 -04:00
formatProvider ??= CultureInfo . InvariantCulture ;
2016-03-30 14:20:52 -07:00
var culture = formatProvider as CultureInfo ;
if ( culture == null )
{
2019-06-28 14:39:34 -04:00
throw PSTraceSource . NewArgumentException ( nameof ( formatProvider ));
2016-03-30 14:20:52 -07:00
}
first = PSObject . Base ( first );
second = PSObject . Base ( second );
if ( first == null )
{
2019-06-28 14:39:34 -04:00
return CompareObjectToNull ( second , true );
2016-03-30 14:20:52 -07:00
}
if ( second == null )
{
2018-08-10 19:20:27 +02:00
return CompareObjectToNull ( first , false );
2016-03-30 14:20:52 -07:00
}
2018-08-03 06:18:05 +02:00
if ( first is string firstString )
2016-03-30 14:20:52 -07:00
{
2019-06-28 14:39:34 -04:00
var secondString = second as string ;
2016-03-30 14:20:52 -07:00
if ( secondString == null )
{
try
{
secondString = ( string ) LanguagePrimitives . ConvertTo ( second , typeof ( string ), culture );
}
catch ( PSInvalidCastException e )
{
2019-06-28 14:39:34 -04:00
throw PSTraceSource . NewArgumentException (
nameof ( second ),
ExtendedTypeSystem . ComparisonFailure ,
first . ToString (),
second . ToString (),
e . Message );
2016-03-30 14:20:52 -07:00
}
}
2018-12-23 22:20:06 -08:00
2019-06-28 14:39:34 -04:00
return culture . CompareInfo . Compare (
firstString ,
secondString ,
ignoreCase ? CompareOptions . IgnoreCase : CompareOptions . None );
2016-03-30 14:20:52 -07:00
}
Type firstType = first . GetType ();
Type secondType = second . GetType ();
int firstIndex = LanguagePrimitives . TypeTableIndex ( firstType );
int secondIndex = LanguagePrimitives . TypeTableIndex ( secondType );
2019-06-28 14:39:34 -04:00
2016-03-30 14:20:52 -07:00
if (( firstIndex != - 1 ) && ( secondIndex != - 1 ))
{
return LanguagePrimitives . NumericCompare ( first , second , firstIndex , secondIndex );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
object secondConverted ;
try
{
secondConverted = LanguagePrimitives . ConvertTo ( second , firstType , culture );
}
catch ( PSInvalidCastException e )
{
2019-06-28 14:39:34 -04:00
throw PSTraceSource . NewArgumentException (
nameof ( second ),
ExtendedTypeSystem . ComparisonFailure ,
first . ToString (),
second . ToString (),
e . Message );
2016-03-30 14:20:52 -07:00
}
2018-08-03 06:18:05 +02:00
if ( first is IComparable firstComparable )
2016-03-30 14:20:52 -07:00
{
return firstComparable . CompareTo ( secondConverted );
}
if ( first . Equals ( second ))
{
return 0 ;
}
// At this point, we know that they aren't equal but we have no way of
// knowing which should compare greater than the other so we throw an exception.
2019-06-28 14:39:34 -04:00
throw PSTraceSource . NewArgumentException ( nameof ( first ), ExtendedTypeSystem . NotIcomparable , first . ToString ());
2016-03-30 14:20:52 -07:00
}
2018-08-10 19:20:27 +02:00
/// <summary>
/// Tries to compare first and second, converting second to the type of the first, if necessary.
/// If a conversion is needed but fails, false is return.
/// </summary>
/// <param name="first">First comparison value.</param>
/// <param name="second">Second comparison value.</param>
/// <param name="result">Less than zero if first is smaller than second, more than
/// zero if it is greater or zero if they are the same.</param>
/// <returns>True if the comparison was successful, false otherwise.</returns>
public static bool TryCompare ( object first , object second , out int result )
2019-06-28 14:39:34 -04:00
=> TryCompare ( first , second , ignoreCase : false , CultureInfo . InvariantCulture , out result );
2018-08-10 19:20:27 +02:00
/// <summary>
/// Tries to compare first and second, converting second to the type of the first, if necessary.
/// If a conversion is needed but fails, false is return.
/// </summary>
/// <param name="first">First comparison value.</param>
/// <param name="second">Second comparison value.</param>
/// <param name="ignoreCase">Used if both values are strings.</param>
/// <param name="result">Less than zero if first is smaller than second, more than zero if it is greater or zero if they are the same.</param>
/// <returns>True if the comparison was successful, false otherwise.</returns>
public static bool TryCompare ( object first , object second , bool ignoreCase , out int result )
2019-06-28 14:39:34 -04:00
=> TryCompare ( first , second , ignoreCase , CultureInfo . InvariantCulture , out result );
2018-08-10 19:20:27 +02:00
/// <summary>
/// Tries to compare first and second, converting second to the type of the first, if necessary.
/// If a conversion is needed but fails, false is return.
/// </summary>
/// <param name="first">First comparison value.</param>
/// <param name="second">Second comparison value.</param>
/// <param name="ignoreCase">Used if both values are strings.</param>
/// <param name="formatProvider">Used in type conversions and if both values are strings.</param>
/// <param name="result">Less than zero if first is smaller than second, more than zero if it is greater or zero if they are the same.</param>
/// <returns>True if the comparison was successful, false otherwise.</returns>
/// <exception cref="ArgumentException">The parameter <paramref name="formatProvider"/> is not a <see cref="CultureInfo"/>.</exception>
public static bool TryCompare ( object first , object second , bool ignoreCase , IFormatProvider formatProvider , out int result )
{
result = 0 ;
2019-06-28 14:39:34 -04:00
formatProvider ??= CultureInfo . InvariantCulture ;
2018-08-10 19:20:27 +02:00
if (!( formatProvider is CultureInfo culture ))
{
throw PSTraceSource . NewArgumentException ( "formatProvider" );
}
first = PSObject . Base ( first );
second = PSObject . Base ( second );
if ( first == null && second == null )
{
result = 0 ;
return true ;
}
if ( first == null )
{
result = CompareObjectToNull ( second , true );
return true ;
}
if ( second == null )
{
// If it's a positive number, including 0, it's greater than null
// for everything else it's less than zero...
result = CompareObjectToNull ( first , false );
return true ;
}
if ( first is string firstString )
{
if (!( second is string secondString ))
{
if (! TryConvertTo ( second , culture , out secondString ))
{
return false ;
}
}
result = culture . CompareInfo . Compare ( firstString , secondString , ignoreCase ? CompareOptions . IgnoreCase : CompareOptions . None );
return true ;
}
Type firstType = first . GetType ();
Type secondType = second . GetType ();
int firstIndex = TypeTableIndex ( firstType );
int secondIndex = TypeTableIndex ( secondType );
if ( firstIndex != - 1 && secondIndex != - 1 )
{
result = NumericCompare ( first , second , firstIndex , secondIndex );
return true ;
}
if (! TryConvertTo ( second , firstType , culture , out object secondConverted ))
{
return false ;
}
if ( first is IComparable firstComparable )
{
result = firstComparable . CompareTo ( secondConverted );
return true ;
}
if ( first . Equals ( second ))
{
result = 0 ;
return true ;
}
// At this point, we know that they aren't equal but we have no way of
// knowing which should compare greater than the other so we return false.
return false ;
}
2016-03-30 14:20:52 -07:00
/// <summary>
2019-01-06 04:40:24 +05:00
/// Returns true if the language considers obj to be true.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="obj">Obj to verify if it is true.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if obj is true.</returns>
2016-03-30 14:20:52 -07:00
[SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj", Justification = "Since V1 code is already shipped, excluding this message for backward compatibility reasons")]
public static bool IsTrue ( object obj )
{
// null is a valid argument - it converts to false...
2019-06-28 14:39:34 -04:00
if ( IsNullLike ( obj ))
{
2016-03-30 14:20:52 -07:00
return false ;
2019-06-28 14:39:34 -04:00
}
2016-03-30 14:20:52 -07:00
obj = PSObject . Base ( obj );
Type objType = obj . GetType ();
if ( objType == typeof ( bool ))
{
return ( bool ) obj ;
}
if ( objType == typeof ( string ))
{
return IsTrue (( string ) obj );
}
if ( LanguagePrimitives . IsNumeric ( LanguagePrimitives . GetTypeCode ( objType )))
{
2018-12-20 23:23:04 -08:00
IConversionData data = GetConversionData ( objType , typeof ( bool )) ??
CacheConversion ( objType , typeof ( bool ), CreateNumericToBoolConverter ( objType ), ConversionRank . Language );
2016-03-30 14:20:52 -07:00
return ( bool ) data . Invoke ( obj , typeof ( bool ), false , null , null , null );
}
if ( objType == typeof ( SwitchParameter ))
return (( SwitchParameter ) obj ). ToBool ();
2019-06-28 14:39:34 -04:00
if ( obj is IList objectArray )
2016-03-30 14:20:52 -07:00
{
return IsTrue ( objectArray );
}
return true ;
}
internal static bool IsTrue ( string s )
{
return ( s . Length != 0 );
}
internal static bool IsTrue ( IList objectArray )
{
switch ( objectArray . Count )
{
2016-07-29 13:02:49 -07:00
// a zero length array is false, so condition is false
case 0 :
return false ;
2016-03-30 14:20:52 -07:00
2016-07-29 13:02:49 -07:00
// if the result is an array of length 1, treat it as a scalar...
case 1 :
2017-01-16 13:31:14 -08:00
// A possible implementation would be just
2016-07-29 13:02:49 -07:00
// return IsTrue(objectArray[0]);
2016-08-28 05:44:55 +00:00
// but since we don't want this to recurse indefinitely
2016-07-29 13:02:49 -07:00
// we explicitly check the case where it would recurse
// and deal with it.
2017-02-27 21:54:07 +03:00
IList firstElement = PSObject . Base ( objectArray [ 0 ]) as IList ;
2016-03-30 14:20:52 -07:00
2016-07-29 13:02:49 -07:00
if ( firstElement == null )
{
return IsTrue ( objectArray [ 0 ]);
}
2016-03-30 14:20:52 -07:00
2016-07-29 13:02:49 -07:00
if ( firstElement . Count < 1 ) return false ;
2016-03-30 14:20:52 -07:00
2016-07-29 13:02:49 -07:00
// the first element is an array with more than zero elements
return true ;
2016-03-30 14:20:52 -07:00
2016-07-29 13:02:49 -07:00
default :
return true ;
2016-03-30 14:20:52 -07:00
}
}
/// <summary>
2019-06-28 14:39:34 -04:00
/// Internal routine that determines if an object meets any of our criteria for true null.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="obj">The object to test.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if the object is null.</returns>
2019-06-28 14:39:34 -04:00
public static bool IsNull ( object obj ) => obj == null || obj == AutomationNull . Value ;
/// <summary>
/// Internal routine that determines if an object meets any of our criteria for null.
/// This method additionally checks for <see cref="NullString.Value"/> and <see cref="DBNull.Value"/>
/// </summary>
/// <param name="obj">The object to test.</param>
/// <returns>True if the object is null.</returns>
2019-10-16 02:38:39 -04:00
public static bool IsNullLike ( object obj ) => IsNull ( obj ) || obj == DBNull . Value || obj == NullString . Value ;
2016-03-30 14:20:52 -07:00
/// <summary>
2019-01-06 04:40:24 +05:00
/// Auxiliary for the cases where we want a new PSObject or null.
2016-03-30 14:20:52 -07:00
/// </summary>
internal static PSObject AsPSObjectOrNull ( object obj )
{
if ( obj == null )
{
return null ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return PSObject . AsPSObject ( obj );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
internal static int TypeTableIndex ( Type type )
{
switch ( LanguagePrimitives . GetTypeCode ( type ))
{
case TypeCode . Int16 : return 0 ;
case TypeCode . Int32 : return 1 ;
case TypeCode . Int64 : return 2 ;
case TypeCode . UInt16 : return 3 ;
case TypeCode . UInt32 : return 4 ;
case TypeCode . UInt64 : return 5 ;
case TypeCode . SByte : return 6 ;
case TypeCode . Byte : return 7 ;
case TypeCode . Single : return 8 ;
case TypeCode . Double : return 9 ;
case TypeCode . Decimal : return 10 ;
default : return - 1 ;
}
}
/// <summary>
2017-01-16 13:31:14 -08:00
/// Table of the largest safe type to which both types can be converted without exceptions.
2016-03-30 14:20:52 -07:00
/// This table is used for numeric comparisons.
/// The 4 entries marked as not used, are explicitly dealt with in NumericCompareDecimal.
2017-01-16 13:31:14 -08:00
/// NumericCompareDecimal exists because doubles and singles can throw
2016-03-30 14:20:52 -07:00
/// an exception when converted to decimal.
/// The order of lines and columns cannot be changed since NumericCompare depends on it.
/// </summary>
internal static Type [][] LargestTypeTable = new Type [][]
{
2017-01-16 13:31:14 -08:00
// System.Int16 System.Int32 System.Int64 System.UInt16 System.UInt32 System.UInt64 System.SByte System.Byte System.Single System.Double System.Decimal
2016-03-30 14:20:52 -07:00
/* System.Int16 */ new Type [] { typeof ( System . Int16 ), typeof ( System . Int32 ), typeof ( System . Int64 ), typeof ( System . Int32 ), typeof ( System . Int64 ), typeof ( System . Double ), typeof ( System . Int16 ), typeof ( System . Int16 ), typeof ( System . Single ), typeof ( System . Double ), typeof ( System . Decimal ) },
/* System.Int32 */ new Type [] { typeof ( System . Int32 ), typeof ( System . Int32 ), typeof ( System . Int64 ), typeof ( System . Int32 ), typeof ( System . Int64 ), typeof ( System . Double ), typeof ( System . Int32 ), typeof ( System . Int32 ), typeof ( System . Double ), typeof ( System . Double ), typeof ( System . Decimal ) },
/* System.Int64 */ new Type [] { typeof ( System . Int64 ), typeof ( System . Int64 ), typeof ( System . Int64 ), typeof ( System . Int64 ), typeof ( System . Int64 ), typeof ( System . Decimal ), typeof ( System . Int64 ), typeof ( System . Int64 ), typeof ( System . Double ), typeof ( System . Double ), typeof ( System . Decimal ) },
/* System.UInt16 */ new Type [] { typeof ( System . Int32 ), typeof ( System . Int32 ), typeof ( System . Int64 ), typeof ( System . UInt16 ), typeof ( System . UInt32 ), typeof ( System . UInt64 ), typeof ( System . Int32 ), typeof ( System . UInt16 ), typeof ( System . Single ), typeof ( System . Double ), typeof ( System . Decimal ) },
/* System.UInt32 */ new Type [] { typeof ( System . Int64 ), typeof ( System . Int64 ), typeof ( System . Int64 ), typeof ( System . UInt32 ), typeof ( System . UInt32 ), typeof ( System . UInt64 ), typeof ( System . Int64 ), typeof ( System . UInt32 ), typeof ( System . Double ), typeof ( System . Double ), typeof ( System . Decimal ) },
/* System.UInt64 */ new Type [] { typeof ( System . Double ), typeof ( System . Double ), typeof ( System . Decimal ), typeof ( System . UInt64 ), typeof ( System . UInt64 ), typeof ( System . UInt64 ), typeof ( System . Double ), typeof ( System . UInt64 ), typeof ( System . Double ), typeof ( System . Double ), typeof ( System . Decimal ) },
/* System.SByte */ new Type [] { typeof ( System . Int16 ), typeof ( System . Int32 ), typeof ( System . Int64 ), typeof ( System . Int32 ), typeof ( System . Int64 ), typeof ( System . Double ), typeof ( System . SByte ), typeof ( System . Int16 ), typeof ( System . Single ), typeof ( System . Double ), typeof ( System . Decimal ) },
/* System.Byte */ new Type [] { typeof ( System . Int16 ), typeof ( System . Int32 ), typeof ( System . Int64 ), typeof ( System . UInt16 ), typeof ( System . UInt32 ), typeof ( System . UInt64 ), typeof ( System . Int16 ), typeof ( System . Byte ), typeof ( System . Single ), typeof ( System . Double ), typeof ( System . Decimal ) },
/* System.Single */ new Type [] { typeof ( System . Single ), typeof ( System . Double ), typeof ( System . Double ), typeof ( System . Single ), typeof ( System . Double ), typeof ( System . Double ), typeof ( System . Single ), typeof ( System . Single ), typeof ( System . Single ), typeof ( System . Double ), null /*not used*/ },
/* System.Double */ new Type [] { typeof ( System . Double ), typeof ( System . Double ), typeof ( System . Double ), typeof ( System . Double ), typeof ( System . Double ), typeof ( System . Double ), typeof ( System . Double ), typeof ( System . Double ), typeof ( System . Double ), typeof ( System . Double ), null /*not used*/ },
/* System.Decimal */ new Type [] { typeof ( System . Decimal ), typeof ( System . Decimal ), typeof ( System . Decimal ), typeof ( System . Decimal ), typeof ( System . Decimal ), typeof ( System . Decimal ), typeof ( System . Decimal ), typeof ( System . Decimal ), null /*not used*/ , null /*not used*/ , typeof ( System . Decimal ) },
};
private static int NumericCompareDecimal ( decimal decimalNumber , object otherNumber )
{
object otherDecimal = null ;
try
{
otherDecimal = Convert . ChangeType ( otherNumber , typeof ( System . Decimal ), CultureInfo . InvariantCulture );
}
catch ( OverflowException )
{
try
{
double wasDecimal = ( double ) Convert . ChangeType ( decimalNumber , typeof ( System . Double ), CultureInfo . InvariantCulture );
double otherDouble = ( double ) Convert . ChangeType ( otherNumber , typeof ( System . Double ), CultureInfo . InvariantCulture );
return (( IComparable ) wasDecimal ). CompareTo ( otherDouble );
}
2016-11-01 15:24:07 -07:00
catch ( Exception ) // We need to catch the generic exception because ChangeType throws unadvertised exceptions
2016-03-30 14:20:52 -07:00
{
return - 1 ;
}
}
2016-11-01 15:24:07 -07:00
catch ( Exception ) // We need to catch the generic exception because ChangeType throws unadvertised exceptions
2016-03-30 14:20:52 -07:00
{
return - 1 ;
}
return (( IComparable ) decimalNumber ). CompareTo ( otherDecimal );
}
private static int NumericCompare ( object number1 , object number2 , int index1 , int index2 )
{
// Conversion from single or double to decimal might throw
2017-01-16 13:31:14 -08:00
// if the double is greater than the decimal's maximum so
2016-03-30 14:20:52 -07:00
// we special case it in NumericCompareDecimal
if (( index1 == 10 ) && (( index2 == 8 ) || ( index2 == 9 )))
{
return NumericCompareDecimal (( decimal ) number1 , number2 );
}
if (( index2 == 10 ) && (( index1 == 8 ) || ( index1 == 9 )))
{
return - NumericCompareDecimal (( decimal ) number2 , number1 );
}
Type commonType = LargestTypeTable [ index1 ][ index2 ];
object number1Converted = Convert . ChangeType ( number1 , commonType , CultureInfo . InvariantCulture );
object number2Converted = Convert . ChangeType ( number2 , commonType , CultureInfo . InvariantCulture );
return (( IComparable ) number1Converted ). CompareTo ( number2Converted );
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Necessary not to return an integer type code for enums.
2016-03-30 14:20:52 -07:00
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
internal static TypeCode GetTypeCode ( Type type )
{
return type . GetTypeCode ();
}
/// <summary>
/// Emulates the "As" C# language primitive, but will unwrap
/// the PSObject if required.
/// </summary>
/// <typeparam name="T">The type for which to convert</typeparam>
2018-12-28 16:39:06 +05:00
/// <param name="castObject">The object from which to convert.</param>
2016-03-30 14:20:52 -07:00
/// <returns>An object of the specified type, if the conversion was successful. Returns null otherwise.</returns>
2019-11-06 08:47:32 +05:00
internal static T FromObjectAs < T >( object castObject )
2016-03-30 14:20:52 -07:00
{
T returnType = default ( T );
// First, see if we can cast the direct type
PSObject wrapperObject = castObject as PSObject ;
if ( wrapperObject == null )
{
try
{
returnType = ( T ) castObject ;
}
catch ( InvalidCastException )
{
returnType = default ( T );
}
}
// Then, see if it is an PSObject wrapping the object
else
{
try
{
returnType = ( T ) wrapperObject . BaseObject ;
}
catch ( InvalidCastException )
{
returnType = default ( T );
}
}
return returnType ;
}
[Flags]
private enum TypeCodeTraits
{
2016-07-29 13:02:49 -07:00
None = 0x00 ,
SignedInteger = 0x01 ,
UnsignedInteger = 0x02 ,
Floating = 0x04 ,
CimIntrinsicType = 0x08 ,
Decimal = 0x10 ,
2016-03-30 14:20:52 -07:00
2016-07-29 13:02:49 -07:00
Integer = SignedInteger | UnsignedInteger ,
Numeric = Integer | Floating | Decimal ,
2016-03-30 14:20:52 -07:00
}
2016-07-29 13:02:49 -07:00
private static readonly TypeCodeTraits [] s_typeCodeTraits = new TypeCodeTraits []
2016-03-30 14:20:52 -07:00
{
/* Empty = 0 */ TypeCodeTraits . None ,
/* Object = 1 */ TypeCodeTraits . None ,
/* DBNull = 2 */ TypeCodeTraits . None ,
/* Boolean = 3 */ TypeCodeTraits . CimIntrinsicType ,
/* Char = 4 */ TypeCodeTraits . CimIntrinsicType ,
/* SByte = 5 */ TypeCodeTraits . SignedInteger | TypeCodeTraits . CimIntrinsicType ,
/* Byte = 6 */ TypeCodeTraits . UnsignedInteger | TypeCodeTraits . CimIntrinsicType ,
/* Int16 = 7 */ TypeCodeTraits . SignedInteger | TypeCodeTraits . CimIntrinsicType ,
/* UInt16 = 8 */ TypeCodeTraits . UnsignedInteger | TypeCodeTraits . CimIntrinsicType ,
/* Int32 = 9 */ TypeCodeTraits . SignedInteger | TypeCodeTraits . CimIntrinsicType ,
/* UInt32 = 10 */ TypeCodeTraits . UnsignedInteger | TypeCodeTraits . CimIntrinsicType ,
/* Int64 = 11 */ TypeCodeTraits . SignedInteger | TypeCodeTraits . CimIntrinsicType ,
/* UInt64 = 12 */ TypeCodeTraits . UnsignedInteger | TypeCodeTraits . CimIntrinsicType ,
/* Single = 13 */ TypeCodeTraits . Floating | TypeCodeTraits . CimIntrinsicType ,
/* Double = 14 */ TypeCodeTraits . Floating | TypeCodeTraits . CimIntrinsicType ,
/* Decimal = 15 */ TypeCodeTraits . Decimal ,
/* DateTime = 16 */ TypeCodeTraits . None | TypeCodeTraits . CimIntrinsicType ,
/* = 17 */ TypeCodeTraits . None ,
/* String = 18 */ TypeCodeTraits . None | TypeCodeTraits . CimIntrinsicType ,
};
/// <summary>
2019-01-06 04:40:24 +05:00
/// Verifies if type is a signed integer.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="typeCode">Type code to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if type is a signed integer, false otherwise.</returns>
2016-03-30 14:20:52 -07:00
internal static bool IsSignedInteger ( TypeCode typeCode )
{
2016-07-29 13:02:49 -07:00
return ( s_typeCodeTraits [( int ) typeCode ] & TypeCodeTraits . SignedInteger ) != 0 ;
2016-03-30 14:20:52 -07:00
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Verifies if type is an unsigned integer.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="typeCode">Type code to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if type is an unsigned integer, false otherwise.</returns>
2016-03-30 14:20:52 -07:00
internal static bool IsUnsignedInteger ( TypeCode typeCode )
{
2016-07-29 13:02:49 -07:00
return ( s_typeCodeTraits [( int ) typeCode ] & TypeCodeTraits . UnsignedInteger ) != 0 ;
2016-03-30 14:20:52 -07:00
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Verifies if type is integer.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="typeCode">Type code to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if type is integer, false otherwise.</returns>
2016-03-30 14:20:52 -07:00
internal static bool IsInteger ( TypeCode typeCode )
{
2016-07-29 13:02:49 -07:00
return ( s_typeCodeTraits [( int ) typeCode ] & TypeCodeTraits . Integer ) != 0 ;
2016-03-30 14:20:52 -07:00
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Verifies if type is a floating point number.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="typeCode">Type code to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if type is floating point, false otherwise.</returns>
2016-03-30 14:20:52 -07:00
internal static bool IsFloating ( TypeCode typeCode )
{
2016-07-29 13:02:49 -07:00
return ( s_typeCodeTraits [( int ) typeCode ] & TypeCodeTraits . Floating ) != 0 ;
2016-03-30 14:20:52 -07:00
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Verifies if type is an integer or floating point number.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="typeCode">Type code to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if type is integer or floating point, false otherwise.</returns>
2016-03-30 14:20:52 -07:00
internal static bool IsNumeric ( TypeCode typeCode )
{
2016-07-29 13:02:49 -07:00
return ( s_typeCodeTraits [( int ) typeCode ] & TypeCodeTraits . Numeric ) != 0 ;
2016-03-30 14:20:52 -07:00
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Verifies if type is a CIM intrinsic type.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="typeCode">Type code to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if type is CIM intrinsic type, false otherwise.</returns>
2016-03-30 14:20:52 -07:00
internal static bool IsCimIntrinsicScalarType ( TypeCode typeCode )
{
2016-07-29 13:02:49 -07:00
return ( s_typeCodeTraits [( int ) typeCode ] & TypeCodeTraits . CimIntrinsicType ) != 0 ;
2016-03-30 14:20:52 -07:00
}
internal static bool IsCimIntrinsicScalarType ( Type type )
{
Dbg . Assert ( type != null , "Caller should verify type != null" );
// using type code we can cover all intrinsic types from the table
// on page 11 of DSP0004, except:
// - TimeSpan part of "datetime"
// - <classname> ref
TypeCode typeCode = LanguagePrimitives . GetTypeCode ( type );
2018-04-13 15:01:41 -07:00
if ( LanguagePrimitives . IsCimIntrinsicScalarType ( typeCode ) && ! type . IsEnum )
2016-03-30 14:20:52 -07:00
{
return true ;
}
if ( type == typeof ( TimeSpan ))
{
return true ;
}
return false ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Verifies if type is one of the boolean types.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="type">Type to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if type is one of boolean types, false otherwise.</returns>
2016-03-30 14:20:52 -07:00
internal static bool IsBooleanType ( Type type )
{
if ( type == typeof ( bool ) ||
2018-12-21 10:07:01 -08:00
type == typeof ( bool? ))
2016-03-30 14:20:52 -07:00
return true ;
else
return false ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Verifies if type is one of switch parameter types.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="type">Type to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if type is one of switch parameter types, false otherwise.</returns>
2016-03-30 14:20:52 -07:00
internal static bool IsSwitchParameterType ( Type type )
{
2018-12-21 10:07:01 -08:00
if ( type == typeof ( SwitchParameter ) || type == typeof ( SwitchParameter ?))
2016-03-30 14:20:52 -07:00
return true ;
else
return false ;
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Verifies if type is one of boolean or switch parameter types.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="type">Type to check.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if type if one of boolean or switch parameter types,
/// false otherwise.</returns>
2016-03-30 14:20:52 -07:00
internal static bool IsBoolOrSwitchParameterType ( Type type )
{
if ( IsBooleanType ( type ) || IsSwitchParameterType ( type ))
return true ;
else
return false ;
}
/// <summary>
/// Do the necessary conversions when using property or array assignment to a generic dictionary:
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// $dict.Prop = value
/// $dict[$Prop] = value
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// The property typically won't need conversion, but it could. The value is more likely in
/// need of conversion.
/// </summary>
/// <param name="dictionary">The dictionary that potentially implement <see cref="IDictionary<TKey,TValue>"/></param>
2018-12-28 16:39:06 +05:00
/// <param name="key">The object representing the key.</param>
/// <param name="value">The value to assign.</param>
2016-03-30 14:20:52 -07:00
internal static void DoConversionsForSetInGenericDictionary ( IDictionary dictionary , ref object key , ref object value )
{
foreach ( Type i in dictionary . GetType (). GetInterfaces ())
{
2018-04-13 15:01:41 -07:00
if ( i . IsGenericType && i . GetGenericTypeDefinition () == typeof ( IDictionary <,>))
2016-03-30 14:20:52 -07:00
{
// If we get here, we know the target implements IDictionary. We will assume
// that the non-generic implementation of the indexer property just forwards
// to the generic version, after checking the types of the key and value.
// This assumption holds for System.Collections.Generic.Dictionary<TKey,TValue>.
// If we did not make this assumption, we would be forced to generate code
2016-08-28 05:44:55 +00:00
// to call the generic indexer directly, somewhat analogous to what we do
2016-03-30 14:20:52 -07:00
// in GetEnumeratorFromIEnumeratorT.
Type [] genericArguments = i . GetGenericArguments ();
key = LanguagePrimitives . ConvertTo ( key , genericArguments [ 0 ], CultureInfo . InvariantCulture );
value = LanguagePrimitives . ConvertTo ( value , genericArguments [ 1 ], CultureInfo . InvariantCulture );
}
}
}
#region type converter
2016-08-02 19:00:50 -07:00
internal static PSTraceSource typeConversion = PSTraceSource . GetTracer ( "TypeConversion" , "Traces the type conversion algorithm" , false );
2018-09-11 16:57:35 -07:00
internal static ConversionData < object > NoConversion = new ConversionData < object >( ConvertNoConversion , ConversionRank . None );
2016-03-30 14:20:52 -07:00
private static TypeConverter GetIntegerSystemConverter ( Type type )
{
if ( type == typeof ( Int16 ))
{
return new Int16Converter ();
}
else if ( type == typeof ( Int32 ))
{
return new Int32Converter ();
}
else if ( type == typeof ( Int64 ))
{
return new Int64Converter ();
}
else if ( type == typeof ( UInt16 ))
{
return new UInt16Converter ();
}
else if ( type == typeof ( UInt32 ))
{
return new UInt32Converter ();
}
else if ( type == typeof ( UInt64 ))
{
return new UInt64Converter ();
}
2019-11-06 08:47:32 +05:00
else if ( type == typeof ( byte ))
2016-03-30 14:20:52 -07:00
{
return new ByteConverter ();
}
2019-11-06 08:47:32 +05:00
else if ( type == typeof ( sbyte ))
2016-03-30 14:20:52 -07:00
{
return new SByteConverter ();
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return null ;
}
/// backupTypeTable:
2017-01-16 13:31:14 -08:00
/// Used by Remoting Rehydration Logic. While Deserializing a remote object,
2016-03-30 14:20:52 -07:00
/// LocalPipeline.ExecutionContextFromTLS() might return null..In which case this
/// TypeTable will be used to do the conversion.
internal static object GetConverter ( Type type , TypeTable backupTypeTable )
{
object typesXmlConverter = null ;
ExecutionContext ecFromTLS = LocalPipeline . GetExecutionContextFromTLS ();
if ( ecFromTLS != null )
{
2016-07-29 13:02:49 -07:00
s_tracer . WriteLine ( "ecFromTLS != null" );
2016-03-30 14:20:52 -07:00
typesXmlConverter = ecFromTLS . TypeTable . GetTypeConverter ( type . FullName );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
if (( typesXmlConverter == null ) && ( backupTypeTable != null ))
{
2016-07-29 13:02:49 -07:00
s_tracer . WriteLine ( "Using provided TypeTable to get the type converter" );
2016-03-30 14:20:52 -07:00
typesXmlConverter = backupTypeTable . GetTypeConverter ( type . FullName );
}
if ( typesXmlConverter != null )
{
2016-07-29 13:02:49 -07:00
s_tracer . WriteLine ( "typesXmlConverter != null" );
2016-03-30 14:20:52 -07:00
return typesXmlConverter ;
}
2018-04-13 15:01:41 -07:00
var typeConverters = type . GetCustomAttributes ( typeof ( TypeConverterAttribute ), false );
2016-03-30 14:20:52 -07:00
foreach ( var typeConverter in typeConverters )
{
var attr = ( TypeConverterAttribute ) typeConverter ;
string assemblyQualifiedtypeName = attr . ConverterTypeName ;
typeConversion . WriteLine ( "{0}'s TypeConverterAttribute points to {1}." , type , assemblyQualifiedtypeName );
// The return statement makes sure we only process the first TypeConverterAttribute
return NewConverterInstance ( assemblyQualifiedtypeName );
}
return null ;
}
private static object NewConverterInstance ( string assemblyQualifiedTypeName )
{
2019-06-26 11:02:07 +05:00
int typeSeparator = assemblyQualifiedTypeName . IndexOf ( ',' );
2016-03-30 14:20:52 -07:00
if ( typeSeparator == - 1 )
{
typeConversion . WriteLine ( "Type name \"{0}\" should be assembly qualified." , assemblyQualifiedTypeName );
return null ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
string assemblyName = assemblyQualifiedTypeName . Substring ( typeSeparator + 2 );
string typeName = assemblyQualifiedTypeName . Substring ( 0 , typeSeparator );
foreach ( Assembly assembly in ClrFacade . GetAssemblies ( typeName ))
{
if ( assembly . FullName == assemblyName )
{
Type converterType = null ;
try
{
converterType = assembly . GetType ( typeName , false , false );
}
catch ( ArgumentException e )
{
typeConversion . WriteLine ( "Assembly \"{0}\" threw an exception when retrieving the type \"{1}\": \"{2}\"." , assemblyName , typeName , e . Message );
return null ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
try
{
return Activator . CreateInstance ( converterType );
}
catch ( Exception e )
{
TargetInvocationException inner = e as TargetInvocationException ;
string message = ( inner == null ) || ( inner . InnerException == null ) ? e . Message : inner . InnerException . Message ;
typeConversion . WriteLine ( "Creating an instance of type \"{0}\" caused an exception to be thrown: \"{1}\"" , assemblyQualifiedTypeName , message );
return null ;
}
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
typeConversion . WriteLine ( "Could not create an instance of type \"{0}\"." , assemblyQualifiedTypeName );
return null ;
}
/// <summary>
/// BUGBUG - brucepay Mar. 2013 - I don't think this is general enough for dynamic keywords to support arbitrary target
/// languages with arbitrary type representations so we may need an extension point here...
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// Maps a .NET or CIM type name string (e.g. SInt32) to the form expected by PowerShell users, namely "[typename]"
/// If there is no mapping, then it returns null.
/// If the string to convert is null or empty then the function returns "[object]" as the default typeless type.
/// </summary>
/// <param name="typeName">The typename string to convert.</param>
2016-08-28 05:44:55 +00:00
/// <returns>The equivalent PowerShell representation of that type.</returns>
2016-03-30 14:20:52 -07:00
public static string ConvertTypeNameToPSTypeName ( string typeName )
{
if ( string . IsNullOrWhiteSpace ( typeName ))
{
return "[object]" ;
}
string mappedType ;
2016-07-29 13:02:49 -07:00
if ( s_nameMap . TryGetValue ( typeName , out mappedType ))
2016-03-30 14:20:52 -07:00
{
return ( '[' + mappedType + ']' );
}
// Then check dot net types
Type dotNetType ;
if ( TypeResolver . TryResolveType ( typeName , out dotNetType ))
{
// Pass through the canonicalize type name we get back from the type converter...
return '[' + LanguagePrimitives . ConvertTo < string >( dotNetType ) + ']' ;
}
// No mapping is found, return null
return null ;
}
//
// CIM name string to .NET namestring mapping table
// (Considered using the MI routines but they didn't do quite the right thing.
//
2016-07-29 13:02:49 -07:00
private static Dictionary < string , string > s_nameMap = new Dictionary < string , string >( StringComparer . OrdinalIgnoreCase ) {
2016-03-30 14:20:52 -07:00
{ "SInt8" , "SByte" },
{ "UInt8" , "Byte" },
{ "SInt16" , "Int16" },
{ "UInt16" , "UInt16" },
{ "SInt32" , "Int32" },
{ "UInt32" , "UInt32" },
{ "SInt64" , "Int64" },
{ "UInt64" , "UInt64" },
{ "Real32" , "Single" },
{ "Real64" , "double" },
{ "Boolean" , "bool" },
{ "String" , "string" },
2016-07-29 13:02:49 -07:00
{ "DateTime" , "DateTime" },
2016-03-30 14:20:52 -07:00
{ "Reference" , "CimInstance" },
{ "Char16" , "char" },
{ "Instance" , "CimInstance" },
{ "BooleanArray" , "bool[]" },
{ "UInt8Array" , "byte[]" },
{ "SInt8Array" , "Sbyte[]" },
{ "UInt16Array" , "uint16[]" },
{ "SInt16Array" , "int64[]" },
{ "UInt32Array" , "UInt32[]" },
{ "SInt32Array" , "Int32[]" },
{ "UInt64Array" , "UInt64[]" },
{ "SInt64Array" , "Int64[]" },
{ "Real32Array" , "Single[]" },
{ "Real64Array" , "double[]" },
{ "Char16Array" , "char[]" },
{ "DateTimeArray" , "DateTime[]" },
{ "StringArray" , "string[]" },
{ "ReferenceArray" , "CimInstance[]" },
{ "InstanceArray" , "CimInstance[]" },
{ "Unknown" , "UnknownType" },
};
#region public type conversion
/// <summary>
2019-01-06 04:40:24 +05:00
/// Converts valueToConvert to resultType.
2016-03-30 14:20:52 -07:00
/// </summary>
/// <remarks>
/// A null valueToConvert can be converted to :
2017-01-16 13:31:14 -08:00
/// string - returns ""
2016-03-30 14:20:52 -07:00
/// char - returns '\0'
/// numeric types - returns 0 converted to the appropriate type
/// boolean - returns LanguagePrimitives.IsTrue(null)
2017-01-16 13:31:14 -08:00
/// PSObject - returns new PSObject())
2016-03-30 14:20:52 -07:00
/// array - returns an array with null in array[0]
/// non value types - returns null
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// The following conversions are considered language standard and cannot be customized:
/// - from derived to base class - returns valueToConvert intact
/// - to PSObject - returns PSObject.AsPSObject(valueToConvert)
/// - to void - returns AutomationNull.Value
/// - from array/IEnumerable to array - tries to convert array/IEnumerable elements
/// - from object of type X to array of X - returns an array with object as its only element
/// - to bool - returns LanguagePrimitives.IsTrue(valueToConvert)
2017-01-16 13:31:14 -08:00
/// - to string - returns a string representation of the object.
/// In the particular case of a number to string,
2016-03-30 14:20:52 -07:00
/// the conversion is culture invariant.
/// - from IDictionary to Hashtable - uses the Hashtable constructor
2017-01-16 13:31:14 -08:00
/// - to XmlDocument - creates a new XmlDocument with the
2016-03-30 14:20:52 -07:00
/// string representation of valueToConvert
/// - from string to char[] - returns ((string)valueToConvert).ToCharArray()
/// - from string to RegEx - creates a new RegEx with the string
/// - from string to Type - looks up the type in the minishell's assemblies
/// - from empty string to numeric - returns 0 converted to the appropriate type
/// - from string to numeric - returns a culture invariant conversion
/// - from ScriptBlock to Delegate - returns a delegate wrapping that scriptblock.
/// - from Integer to Enumeration - Uses Enum.ToObject
2017-01-16 13:31:14 -08:00
/// - to WMI - Instantiate a WMI instance using
2016-03-30 14:20:52 -07:00
/// System.Management.ManagementObject
/// - to WMISearcher - returns objects from running WQL query with the
/// string representation of valueToConvert. The
/// query is run using ManagementObjectSearcher Class.
/// - to WMIClass - returns ManagementClass represented by the
/// string representation of valueToConvert.
2017-09-04 19:45:51 -07:00
/// - to ADSI - returns DirectoryEntry represented by the
2016-03-30 14:20:52 -07:00
/// string representation of valueToConvert.
2016-08-28 05:44:55 +00:00
/// - to ADSISearcher - return DirectorySearcher represented by the
2016-03-30 14:20:52 -07:00
/// string representation of valueToConvert.
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// If none of the cases above is true, the following is considered in order:
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// 1) TypeConverter and PSTypeConverter
/// 2) the Parse methods if the valueToConvert is a string
/// 3) Constructors in resultType that take one parameter with type valueToConvert.GetType()
/// 4) Implicit and explicit cast operators
/// 5) IConvertible
2017-01-16 13:31:14 -08:00
///
/// If any operation above throws an exception, this exception will be wrapped into a
2016-03-30 14:20:52 -07:00
/// PSInvalidCastException and thrown resulting in no further conversion attempt.
/// </remarks>
2018-12-29 08:25:49 +05:00
/// <param name="valueToConvert">Value to be converted and returned.</param>
/// <param name="resultType">Type to convert valueToConvert.</param>
2018-12-25 21:43:03 +05:00
/// <returns>Converted value.</returns>
2018-12-29 08:25:49 +05:00
/// <exception cref="ArgumentNullException">If resultType is null.</exception>
/// <exception cref="PSInvalidCastException">If the conversion failed.</exception>
2016-03-30 14:20:52 -07:00
public static object ConvertTo ( object valueToConvert , Type resultType )
{
return ConvertTo ( valueToConvert , resultType , true , CultureInfo . InvariantCulture , null );
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Converts valueToConvert to resultType possibly considering formatProvider.
2016-03-30 14:20:52 -07:00
/// </summary>
/// <remarks>
/// A null valueToConvert can be converted to :
2017-01-16 13:31:14 -08:00
/// string - returns ""
2016-03-30 14:20:52 -07:00
/// char - returns '\0'
/// numeric types - returns 0 converted to the appropriate type
/// boolean - returns LanguagePrimitives.IsTrue(null)
2017-01-16 13:31:14 -08:00
/// PSObject - returns new PSObject())
2016-03-30 14:20:52 -07:00
/// array - returns an array with null in array[0]
/// non value types - returns null
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// The following conversions are considered language standard and cannot be customized:
/// - from derived to base class - returns valueToConvert intact
/// - to PSObject - returns PSObject.AsPSObject(valueToConvert)
/// - to void - returns AutomationNull.Value
/// - from array/IEnumerable to array - tries to convert array/IEnumerable elements
/// - from object of type X to array of X - returns an array with object as its only element
/// - to bool - returns LanguagePrimitives.IsTrue(valueToConvert)
2017-01-16 13:31:14 -08:00
/// - to string - returns a string representation of the object.
/// In the particular case of a number to string,
2016-03-30 14:20:52 -07:00
/// the conversion is culture invariant.
/// - from IDictionary to Hashtable - uses the Hashtable constructor
2017-01-16 13:31:14 -08:00
/// - to XmlDocument - creates a new XmlDocument with the
2016-03-30 14:20:52 -07:00
/// string representation of valueToConvert
/// - from string to char[] - returns ((string)valueToConvert).ToCharArray()
/// - from string to RegEx - creates a new RegEx with the string
/// - from string to Type - looks up the type in the minishell's assemblies
/// - from empty string to numeric - returns 0 converted to the appropriate type
/// - from string to numeric - returns a culture invariant conversion
/// - from ScriptBlock to Delegate - returns a delegate wrapping that scriptblock.
/// - from Integer to Enumeration - Uses Enum.ToObject
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// If none of the cases above is true, the following is considered in order:
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// 1) TypeConverter and PSTypeConverter
/// 2) the Parse methods if the valueToConvert is a string
/// 3) Constructors in resultType that take one parameter with type valueToConvert.GetType()
/// 4) Implicit and explicit cast operators
/// 5) IConvertible
2017-01-16 13:31:14 -08:00
///
/// If any operation above throws an exception, this exception will be wrapped into a
2016-03-30 14:20:52 -07:00
/// PSInvalidCastException and thrown resulting in no further conversion attempt.
/// </remarks>
2018-12-29 08:25:49 +05:00
/// <param name="valueToConvert">Value to be converted and returned.</param>
/// <param name="resultType">Type to convert valueToConvert.</param>
2018-12-28 16:39:06 +05:00
/// <param name="formatProvider">To be used in custom type conversions, to call parse and to call Convert.ChangeType.</param>
2018-12-25 21:43:03 +05:00
/// <returns>Converted value.</returns>
2018-12-29 08:25:49 +05:00
/// <exception cref="ArgumentNullException">If resultType is null.</exception>
/// <exception cref="PSInvalidCastException">If the conversion failed.</exception>
2016-03-30 14:20:52 -07:00
public static object ConvertTo ( object valueToConvert , Type resultType , IFormatProvider formatProvider )
{
return ConvertTo ( valueToConvert , resultType , true , formatProvider , null );
}
/// <summary>
/// Converts PSObject to resultType.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="valueToConvert">Value to be converted and returned.</param>
/// <param name="resultType">Type to convert psobject.</param>
2016-03-30 14:20:52 -07:00
/// <param name="recursion">Indicates if inner properties have to be recursively converted.</param>
2018-12-28 16:39:06 +05:00
/// <param name="formatProvider">To be used in custom type conversions, to call parse and to call Convert.ChangeType.</param>
2016-08-28 05:44:55 +00:00
/// <param name="ignoreUnknownMembers">Indicates if Unknown members in the psobject have to be ignored if the corresponding members in resultType do not exist.</param>
2018-12-25 21:43:03 +05:00
/// <returns>Converted value.</returns>
2016-03-30 14:20:52 -07:00
public static object ConvertPSObjectToType ( PSObject valueToConvert , Type resultType , bool recursion , IFormatProvider formatProvider , bool ignoreUnknownMembers )
{
2016-07-29 13:02:49 -07:00
if ( valueToConvert != null )
2016-03-30 14:20:52 -07:00
{
2019-03-10 06:53:31 +05:00
ConstructorInfo toConstructor = resultType . GetConstructor ( Type . EmptyTypes );
2016-03-30 14:20:52 -07:00
ConvertViaNoArgumentConstructor noArgumentConstructorConverter = new ConvertViaNoArgumentConstructor ( toConstructor , resultType );
return noArgumentConstructorConverter . Convert ( PSObject . Base ( valueToConvert ), resultType , recursion , ( PSObject ) valueToConvert , formatProvider , null , ignoreUnknownMembers );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return null ;
}
/// <summary>
2016-08-28 05:44:55 +00:00
/// Generic convertto that simplifies working with workflow.
2016-03-30 14:20:52 -07:00
/// </summary>
/// <typeparam name="T">The type of object to return</typeparam>
/// <param name="valueToConvert"></param>
/// <returns></returns>
public static T ConvertTo < T >( object valueToConvert )
{
2018-08-03 06:18:05 +02:00
if ( valueToConvert is T value )
2016-03-30 14:20:52 -07:00
{
2018-08-03 06:18:05 +02:00
return value ;
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 ( T ) ConvertTo ( valueToConvert , typeof ( T ), true , CultureInfo . InvariantCulture , null );
2016-03-30 14:20:52 -07:00
}
/// <summary>
/// Sets result to valueToConvert converted to resultType.
/// </summary>
/// <remarks>
2017-01-16 13:31:14 -08:00
/// This method is a variant of ConvertTo that does not throw exceptions if the conversion fails.
2016-03-30 14:20:52 -07:00
/// </remarks>
2018-12-29 08:25:49 +05:00
/// <param name="valueToConvert">Value to be converted and returned.</param>
/// <param name="result">Result of the conversion. This is valid only if the return is true.</param>
2018-12-25 21:43:03 +05:00
/// <returns>False for conversion failure, true for success.</returns>
2016-03-30 14:20:52 -07:00
public static bool TryConvertTo < T >( object valueToConvert , out T result )
{
2018-08-03 06:18:05 +02:00
if ( valueToConvert is T value )
2016-03-30 14:20:52 -07:00
{
2018-08-03 06:18:05 +02:00
result = value ;
2016-03-30 14:20:52 -07:00
return true ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return TryConvertTo ( valueToConvert , CultureInfo . InvariantCulture , out result );
}
/// <summary>
2017-01-16 13:31:14 -08:00
/// Sets result to valueToConvert converted to resultType considering formatProvider
/// for custom conversions, calling the Parse method and calling Convert.ChangeType.
2016-03-30 14:20:52 -07:00
/// </summary>
/// <remarks>
2017-01-16 13:31:14 -08:00
/// This method is a variant of ConvertTo that does not throw exceptions if the conversion fails.
2016-03-30 14:20:52 -07:00
/// </remarks>
2018-12-29 08:25:49 +05:00
/// <param name="valueToConvert">Value to be converted and returned.</param>
/// <param name="formatProvider">Governing conversion of types.</param>
/// <param name="result">Result of the conversion. This is valid only if the return is true.</param>
2018-12-25 21:43:03 +05:00
/// <returns>False for conversion failure, true for success.</returns>
2016-03-30 14:20:52 -07:00
public static bool TryConvertTo < T >( object valueToConvert , IFormatProvider formatProvider , out T result )
{
result = default ( T );
2018-08-03 06:18:05 +02:00
if ( TryConvertTo ( valueToConvert , typeof ( T ), formatProvider , out object res ))
2016-03-30 14:20:52 -07:00
{
2018-08-03 06:18:05 +02:00
result = ( T ) res ;
return true ;
2016-03-30 14:20:52 -07:00
}
2018-08-03 06:18:05 +02:00
return false ;
2016-03-30 14:20:52 -07:00
}
/// <summary>
/// Sets result to valueToConvert converted to resultType.
/// </summary>
/// <remarks>
2017-01-16 13:31:14 -08:00
/// This method is a variant of ConvertTo that does not throw exceptions if the conversion fails.
2016-03-30 14:20:52 -07:00
/// </remarks>
2018-12-29 08:25:49 +05:00
/// <param name="valueToConvert">Value to be converted and returned.</param>
/// <param name="resultType">Type to convert valueToConvert.</param>
/// <param name="result">Result of the conversion. This is valid only if the return is true.</param>
2018-12-25 21:43:03 +05:00
/// <returns>False for conversion failure, true for success.</returns>
2016-03-30 14:20:52 -07:00
public static bool TryConvertTo ( object valueToConvert , Type resultType , out object result )
{
return TryConvertTo ( valueToConvert , resultType , CultureInfo . InvariantCulture , out result );
}
/// <summary>
2017-01-16 13:31:14 -08:00
/// Sets result to valueToConvert converted to resultType considering formatProvider
/// for custom conversions, calling the Parse method and calling Convert.ChangeType.
2016-03-30 14:20:52 -07:00
/// </summary>
/// <remarks>
2017-01-16 13:31:14 -08:00
/// This method is a variant of ConvertTo that does not throw exceptions if the conversion fails.
2016-03-30 14:20:52 -07:00
/// </remarks>
2018-12-29 08:25:49 +05:00
/// <param name="valueToConvert">Value to be converted and returned.</param>
/// <param name="resultType">Type to convert valueToConvert.</param>
/// <param name="formatProvider">Governing conversion of types.</param>
/// <param name="result">Result of the conversion. This is valid only if the return is true.</param>
2018-12-25 21:43:03 +05:00
/// <returns>False for conversion failure, true for success.</returns>
2016-03-30 14:20:52 -07:00
public static bool TryConvertTo ( object valueToConvert , Type resultType , IFormatProvider formatProvider , out object result )
{
result = null ;
try
{
2018-08-03 06:18:05 +02:00
using ( typeConversion . TraceScope ( "Converting \"{0}\" to \"{1}\"." , valueToConvert , resultType ))
{
if ( resultType == null )
{
return false ;
}
var conversion = FigureConversion ( valueToConvert , resultType , out bool debase );
if ( conversion . Rank == ConversionRank . None )
{
return false ;
}
result = conversion . Invoke (
debase ? PSObject . Base ( valueToConvert ) : valueToConvert ,
resultType ,
recurse : true ,
debase ? ( PSObject ) valueToConvert : null ,
formatProvider ,
backupTable : null );
return true ;
}
2016-03-30 14:20:52 -07:00
}
catch ( InvalidCastException )
{
return false ;
}
}
#endregion public type conversion
internal class EnumMultipleTypeConverter : EnumSingleTypeConverter
{
public override object ConvertFrom ( object sourceValue , Type destinationType , IFormatProvider formatProvider , bool ignoreCase )
{
return EnumSingleTypeConverter . BaseConvertFrom ( sourceValue , destinationType , formatProvider , ignoreCase , true );
}
}
internal class EnumSingleTypeConverter : PSTypeConverter
{
2016-07-29 13:02:49 -07:00
private class EnumHashEntry
2016-03-30 14:20:52 -07:00
{
2018-12-22 01:26:06 -08:00
internal EnumHashEntry ( string [] names , Array values , UInt64 allValues , bool hasNegativeValue , bool hasFlagsAttribute )
2016-03-30 14:20:52 -07:00
{
this . names = names ;
this . values = values ;
this . allValues = allValues ;
this . hasNegativeValue = hasNegativeValue ;
this . hasFlagsAttribute = hasFlagsAttribute ;
}
2018-12-22 01:26:06 -08:00
internal string [] names ;
2016-03-30 14:20:52 -07:00
internal Array values ;
internal UInt64 allValues ;
internal bool hasNegativeValue ;
internal bool hasFlagsAttribute ;
}
// This static is thread safe based on the lock in GetEnumHashEntry
// It can be shared by Runspaces in different MiniShells
2016-07-29 13:02:49 -07:00
private static readonly Dictionary < Type , EnumHashEntry > s_enumTable = new Dictionary < Type , EnumHashEntry >();
private const int maxEnumTableSize = 100 ;
2016-03-30 14:20:52 -07:00
private static EnumHashEntry GetEnumHashEntry ( Type enumType )
{
2016-07-29 13:02:49 -07:00
lock ( s_enumTable )
2016-03-30 14:20:52 -07:00
{
EnumHashEntry returnValue ;
2016-07-29 13:02:49 -07:00
if ( s_enumTable . TryGetValue ( enumType , out returnValue ))
2016-03-30 14:20:52 -07:00
{
return returnValue ;
}
2016-07-29 13:02:49 -07:00
if ( s_enumTable . Count == maxEnumTableSize )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
s_enumTable . Clear ();
2016-03-30 14:20:52 -07:00
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
UInt64 allValues = 0 ;
bool hasNegativeValue = false ;
Array values = Enum . GetValues ( enumType );
// Type.GetTypeCode will return the integer type code for enumType
if ( LanguagePrimitives . IsSignedInteger ( enumType . GetTypeCode ()))
{
foreach ( object value in values )
{
Int64 valueInt64 = Convert . ToInt64 ( value , CultureInfo . CurrentCulture );
// A negative value cannot be flag
if ( valueInt64 < 0 )
{
hasNegativeValue = true ;
break ;
}
// we know the value is not negative, so this conversion
2016-08-28 05:44:55 +00:00
// always succeed
2016-03-30 14:20:52 -07:00
allValues |= Convert . ToUInt64 ( value , CultureInfo . CurrentCulture );
}
}
else
{
foreach ( object value in values )
{
allValues |= Convert . ToUInt64 ( value , CultureInfo . CurrentCulture );
}
}
// See if the [Flag] attribute is set on this type...
// MemberInfo.GetCustomAttributes returns IEnumerable<Attribute> in CoreCLR.
2018-04-13 15:01:41 -07:00
bool hasFlagsAttribute = enumType . GetCustomAttributes ( typeof ( FlagsAttribute ), false ). Any ();
2016-03-30 14:20:52 -07:00
returnValue = new EnumHashEntry ( Enum . GetNames ( enumType ), values , allValues , hasNegativeValue , hasFlagsAttribute );
2016-07-29 13:02:49 -07:00
s_enumTable . Add ( enumType , returnValue );
2016-03-30 14:20:52 -07:00
return returnValue ;
}
}
public override bool CanConvertFrom ( object sourceValue , Type destinationType )
{
2018-04-13 15:01:41 -07:00
return sourceValue is string && destinationType . IsEnum ;
2016-03-30 14:20:52 -07:00
}
/// <summary>
/// Checks if the enumValue is defined or not in enumType.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="enumType">Some enumeration.</param>
/// <param name="enumValue">Supposed to be an integer.</param>
2016-03-30 14:20:52 -07:00
/// <returns>
/// </returns>
private static bool IsDefinedEnum ( object enumValue , Type enumType )
{
bool isDefined ;
do
{
if ( enumValue == null )
{
isDefined = false ;
break ;
}
EnumHashEntry enumHashEntry = EnumSingleTypeConverter . GetEnumHashEntry ( enumType );
// An enumeration with a negative value should not be treated as flags
// so IsValueFlagDefined cannot determine the result, and as far as it knows,
// it is defined.
if ( enumHashEntry . hasNegativeValue )
{
isDefined = true ;
break ;
}
// Type.GetTypeCode will return the integer type code for enumValue.GetType()
if ( LanguagePrimitives . IsSignedInteger ( enumValue . GetType (). GetTypeCode ()))
{
Int64 enumValueInt64 = Convert . ToInt64 ( enumValue , CultureInfo . CurrentCulture );
// A negative value cannot be flag, so we return false
if ( enumValueInt64 < 0 )
{
isDefined = false ;
break ;
}
}
// the if above, guarantees that even if it is an Int64 it is > 0
// so the conversion should always work.
UInt64 enumValueUInt64 = Convert . ToUInt64 ( enumValue , CultureInfo . CurrentCulture );
if ( enumHashEntry . hasFlagsAttribute )
{
// This expression will result in a "1 bit" for bits that are
2017-01-16 13:31:14 -08:00
// set in enumValueInt64 but not set in enumHashEntry.allValues,
2016-03-30 14:20:52 -07:00
// and a "0 bit" otherwise. Any "bit 1" in the result, indicates this is not defined.
isDefined = (( enumValueUInt64 | enumHashEntry . allValues ) ^ enumHashEntry . allValues ) == 0 ;
}
else
{
// If flags is not set, then see if this value is in the list
// of valid values.
if ( Array . IndexOf ( enumHashEntry . values , enumValue ) >= 0 )
isDefined = true ;
else
isDefined = false ;
}
} while ( false );
return isDefined ;
}
/// <summary>
2017-01-16 13:31:14 -08:00
/// Throws if the enumType enumeration has no negative values, but the enumValue is not
2016-03-30 14:20:52 -07:00
/// defined in enumType.
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="enumType">Some enumeration.</param>
/// <param name="enumValue">Supposed to be an integer.</param>
/// <param name="errorId">The error id to be used when throwing an exception.</param>
2016-03-30 14:20:52 -07:00
internal static void ThrowForUndefinedEnum ( string errorId , object enumValue , Type enumType )
{
ThrowForUndefinedEnum ( errorId , enumValue , enumValue , enumType );
}
/// <summary>
2017-01-16 13:31:14 -08:00
/// Throws if the enumType enumeration has no negative values, but the enumValue is not
/// defined in enumType.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="errorId">The error id to be used when throwing an exception.</param>
2018-12-29 08:25:49 +05:00
/// <param name="enumValue">Value to validate.</param>
/// <param name="valueToUseToThrow">Value to use while throwing an exception.</param>
/// <param name="enumType">The enum type to validate the enumValue with.</param>
2016-03-30 14:20:52 -07:00
/// <remarks>
/// <paramref name="valueToUseToThrow"/> is used by those callers who want the exception
/// to contain a different value than the one that is validated.
2017-01-16 13:31:14 -08:00
/// This will enable callers to take different forms of input -> convert to enum using
2016-03-30 14:20:52 -07:00
/// Enum.Object -> then validate using this method.
/// </remarks>
internal static void ThrowForUndefinedEnum ( string errorId , object enumValue , object valueToUseToThrow , Type enumType )
{
if (! IsDefinedEnum ( enumValue , enumType ))
{
typeConversion . WriteLine ( "Value {0} is not defined in the Enum {1}." , valueToUseToThrow , enumType );
throw new PSInvalidCastException ( errorId , null ,
ExtendedTypeSystem . InvalidCastExceptionEnumerationNoValue ,
valueToUseToThrow , enumType , EnumSingleTypeConverter . EnumValues ( enumType ));
}
}
internal static string EnumValues ( Type enumType )
{
EnumHashEntry enumHashEntry = EnumSingleTypeConverter . GetEnumHashEntry ( enumType );
return string . Join ( CultureInfo . CurrentUICulture . TextInfo . ListSeparator , enumHashEntry . names );
}
public override object ConvertFrom ( object sourceValue , Type destinationType , IFormatProvider formatProvider , bool ignoreCase )
{
return EnumSingleTypeConverter . BaseConvertFrom ( sourceValue , destinationType , formatProvider , ignoreCase , false );
}
protected static object BaseConvertFrom ( object sourceValue , Type destinationType , IFormatProvider formatProvider , bool ignoreCase , bool multipleValues )
{
Diagnostics . Assert ( sourceValue != null , "the type converter has a special case for null source values" );
string sourceValueString = sourceValue as string ;
if ( sourceValueString == null )
{
throw new PSInvalidCastException ( "InvalidCastEnumFromTypeNotAString" , null ,
ExtendedTypeSystem . InvalidCastException ,
sourceValue , ObjectToTypeNameString ( sourceValue ), destinationType );
}
2018-12-23 22:20:06 -08:00
2018-04-13 15:01:41 -07:00
Diagnostics . Assert ( destinationType . IsEnum , "EnumSingleTypeConverter is only applied to enumerations" );
2016-03-30 14:20:52 -07:00
if ( sourceValueString . Length == 0 )
{
throw new PSInvalidCastException ( "InvalidCastEnumFromEmptyString" , null ,
ExtendedTypeSystem . InvalidCastException ,
sourceValue , ObjectToTypeNameString ( sourceValue ), destinationType );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
sourceValueString = sourceValueString . Trim ();
if ( sourceValueString . Length == 0 )
{
throw new PSInvalidCastException ( "InvalidEnumCastFromEmptyStringAfterTrim" , null ,
ExtendedTypeSystem . InvalidCastException ,
sourceValue , ObjectToTypeNameString ( sourceValue ), destinationType );
}
2019-01-08 00:40:07 +05:00
if ( char . IsDigit ( sourceValueString [ 0 ]) || sourceValueString [ 0 ] == '+' || sourceValueString [ 0 ] == '-' )
2016-03-30 14:20:52 -07:00
{
Type underlyingType = Enum . GetUnderlyingType ( destinationType );
try
{
object result = Enum . ToObject ( destinationType , Convert . ChangeType ( sourceValueString , underlyingType , formatProvider ));
ThrowForUndefinedEnum ( "UndefinedInEnumSingleTypeConverter" , result , sourceValueString , destinationType );
return result ;
}
2016-11-01 15:24:07 -07:00
catch ( Exception ) // Enum.ToObject and Convert.ChangeType might throw unadvertised exceptions
2016-03-30 14:20:52 -07:00
{
// we still want to try non numeric match
}
}
string [] sourceValueEntries ;
WildcardPattern [] fromValuePatterns ;
if (! multipleValues )
{
if ( sourceValueString . Contains ( "," ))
{
throw new PSInvalidCastException ( "InvalidCastEnumCommaAndNoFlags" , null ,
ExtendedTypeSystem . InvalidCastExceptionEnumerationNoFlagAndComma ,
sourceValue , destinationType );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
sourceValueEntries = new string [] { sourceValueString };
fromValuePatterns = new WildcardPattern [ 1 ];
if ( WildcardPattern . ContainsWildcardCharacters ( sourceValueString ))
{
fromValuePatterns [ 0 ] = WildcardPattern . Get ( sourceValueString , ignoreCase ? WildcardOptions . IgnoreCase : WildcardOptions . None );
}
else
{
fromValuePatterns [ 0 ] = null ;
}
}
else
{
sourceValueEntries = sourceValueString . Split ( Utils . Separators . Comma );
fromValuePatterns = new WildcardPattern [ sourceValueEntries . Length ];
for ( int i = 0 ; i < sourceValueEntries . Length ; i ++)
{
string sourceValueEntry = sourceValueEntries [ i ];
if ( WildcardPattern . ContainsWildcardCharacters ( sourceValueEntry ))
{
fromValuePatterns [ i ] = WildcardPattern . Get ( sourceValueEntry ,
ignoreCase ? WildcardOptions . IgnoreCase : WildcardOptions . None );
}
else
{
fromValuePatterns [ i ] = null ;
}
}
}
EnumHashEntry enumHashEntry = EnumSingleTypeConverter . GetEnumHashEntry ( destinationType );
string [] names = enumHashEntry . names ;
Array values = enumHashEntry . values ;
UInt64 returnUInt64 = 0 ;
StringComparison ignoreCaseOpt ;
if ( ignoreCase )
ignoreCaseOpt = StringComparison . OrdinalIgnoreCase ;
else
ignoreCaseOpt = StringComparison . Ordinal ;
for ( int i = 0 ; i < sourceValueEntries . Length ; i ++)
{
string sourceValueEntry = sourceValueEntries [ i ];
WildcardPattern fromValuePattern = fromValuePatterns [ i ];
bool foundOne = false ;
for ( int j = 0 ; j < names . Length ; j ++)
{
string name = names [ j ];
if ( fromValuePattern != null )
{
if (! fromValuePattern . IsMatch ( name ))
continue ;
}
else
{
2018-12-31 20:44:16 +05:00
if ( string . Compare ( sourceValueEntry , name , ignoreCaseOpt ) != 0 )
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
if (! multipleValues && foundOne )
{
object firstValue = Enum . ToObject ( destinationType , returnUInt64 );
object secondValue = Enum . ToObject ( destinationType , Convert . ToUInt64 ( values . GetValue ( i ), CultureInfo . CurrentCulture ));
throw new PSInvalidCastException ( "InvalidCastEnumTwoStringsFoundAndNoFlags" , null ,
ExtendedTypeSystem . InvalidCastExceptionEnumerationMoreThanOneValue ,
sourceValue , destinationType , firstValue , secondValue );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
foundOne = true ;
returnUInt64 |= Convert . ToUInt64 ( values . GetValue ( j ), CultureInfo . CurrentCulture );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
if (! foundOne )
{
throw new PSInvalidCastException ( "InvalidCastEnumStringNotFound" , null ,
ExtendedTypeSystem . InvalidCastExceptionEnumerationNoValue ,
sourceValueEntry , destinationType , EnumSingleTypeConverter . EnumValues ( destinationType ));
}
}
return Enum . ToObject ( destinationType , returnUInt64 );
}
public override bool CanConvertTo ( object sourceValue , Type destinationType )
{
return false ;
}
public override object ConvertTo ( object sourceValue , Type destinationType , IFormatProvider formatProvider , bool ignoreCase )
{
throw PSTraceSource . NewNotSupportedException ();
}
}
/// <summary>
2017-01-16 13:31:14 -08:00
/// There might be many cast operators in a Type A that take Type A. Each operator will have a
/// different return type. Because of that we cannot call GetMethod since it would cause a
2019-01-06 04:40:24 +05:00
/// AmbiguousMatchException. This auxiliary method calls GetMember to find the right method.
2016-03-30 14:20:52 -07:00
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="methodName">Either op_Explicit or op_Implicit, at the moment.</param>
2018-12-29 08:25:49 +05:00
/// <param name="targetType">The type to look for an operator.</param>
2018-12-28 16:39:06 +05:00
/// <param name="originalType">Type of the only parameter the operator method should have.</param>
/// <param name="resultType">Return type of the operator method.</param>
2018-12-25 21:43:03 +05:00
/// <returns>A cast operator method, or null if not found.</returns>
2016-03-30 14:20:52 -07:00
private static MethodInfo FindCastOperator ( string methodName , Type targetType , Type originalType , Type resultType )
{
using ( typeConversion . TraceScope ( "Looking for \"{0}\" cast operator." , methodName ))
{
// Get multiple matched Public & Static methods
2017-04-17 11:52:38 -07:00
const BindingFlags flagsToUse = BindingFlags . FlattenHierarchy | BindingFlags . Public | BindingFlags . Static | BindingFlags . InvokeMethod ;
var methods = targetType . GetMember ( methodName , flagsToUse );
2016-03-30 14:20:52 -07:00
foreach ( MethodInfo method in methods )
{
if (! resultType . IsAssignableFrom ( method . ReturnType ))
{
continue ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
System . Reflection . ParameterInfo [] parameters = method . GetParameters ();
if ( parameters . Length != 1 || ! parameters [ 0 ]. ParameterType . IsAssignableFrom ( originalType ))
{
continue ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
typeConversion . WriteLine ( "Found \"{0}\" cast operator in type {1}." , methodName , targetType . FullName );
return method ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
typeConversion . TraceScope ( "Cast operator for \"{0}\" not found." , methodName );
return null ;
}
}
private static object ConvertNumericThroughDouble ( object valueToConvert , Type resultType )
{
using ( typeConversion . TraceScope ( "Numeric Conversion through System.Double." ))
{
// Eventual exceptions here are caught by the caller
object intermediate = Convert . ChangeType ( valueToConvert , typeof ( double ),
System . Globalization . CultureInfo . InvariantCulture . NumberFormat );
return Convert . ChangeType ( intermediate , resultType ,
System . Globalization . CultureInfo . InvariantCulture . NumberFormat );
}
}
2018-06-21 09:57:14 -07:00
#if ! UNIX
2016-03-30 14:20:52 -07:00
private static ManagementObject ConvertToWMI ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Standard type conversion to a ManagementObject." );
string valueToConvertString ;
try
{
valueToConvertString = PSObject . ToString ( null , valueToConvert , "\n" , null , null , true , true );
}
catch ( ExtendedTypeSystemException e )
{
typeConversion . WriteLine ( "Exception converting value to string: {0}" , e . Message );
throw new PSInvalidCastException ( "InvalidCastGetStringToWMI" , e ,
ExtendedTypeSystem . InvalidCastExceptionNoStringForConversion ,
resultType . ToString (), e . Message );
}
try
{
ManagementObject wmiObject = new ManagementObject ( valueToConvertString );
// ManagementObject will not throw if path does not contain valid key
if ( wmiObject . SystemProperties [ "__CLASS" ] == null )
{
string message = StringUtil . Format ( ExtendedTypeSystem . InvalidWMIPath , valueToConvertString );
throw new PSInvalidCastException ( message );
}
return wmiObject ;
}
catch ( Exception wmiObjectException )
{
typeConversion . WriteLine ( "Exception creating WMI object: \"{0}\"." , wmiObjectException . Message );
throw new PSInvalidCastException ( "InvalidCastToWMI" , wmiObjectException ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), wmiObjectException . Message );
}
}
private static ManagementObjectSearcher ConvertToWMISearcher ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Standard type conversion to a collection of ManagementObjects." );
string valueToConvertString ;
try
{
valueToConvertString = PSObject . ToString ( null , valueToConvert , "\n" , null , null , true , true );
}
catch ( ExtendedTypeSystemException e )
{
typeConversion . WriteLine ( "Exception converting value to string: {0}" , e . Message );
throw new PSInvalidCastException ( "InvalidCastGetStringToWMISearcher" , e ,
ExtendedTypeSystem . InvalidCastExceptionNoStringForConversion ,
resultType . ToString (), e . Message );
}
try
{
ManagementObjectSearcher objectSearcher = new ManagementObjectSearcher ( valueToConvertString );
return objectSearcher ;
}
catch ( Exception objectSearcherException )
{
typeConversion . WriteLine ( "Exception running WMI object query: \"{0}\"." , objectSearcherException . Message );
throw new PSInvalidCastException ( "InvalidCastToWMISearcher" , objectSearcherException ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), objectSearcherException . Message );
}
}
private static ManagementClass ConvertToWMIClass ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Standard type conversion to a ManagementClass." );
string valueToConvertString ;
try
{
valueToConvertString = PSObject . ToString ( null , valueToConvert , "\n" , null , null , true , true );
}
catch ( ExtendedTypeSystemException e )
{
typeConversion . WriteLine ( "Exception converting value to string: {0}" , e . Message );
throw new PSInvalidCastException ( "InvalidCastGetStringToWMIClass" , e ,
ExtendedTypeSystem . InvalidCastExceptionNoStringForConversion ,
resultType . ToString (), e . Message );
}
try
{
ManagementClass wmiClass = new System . Management . ManagementClass ( valueToConvertString );
2017-01-16 13:31:14 -08:00
// ManagementClass will not throw if the path specified is not
2016-03-30 14:20:52 -07:00
// a valid class.
if ( wmiClass . SystemProperties [ "__CLASS" ] == null )
{
string message = StringUtil . Format ( ExtendedTypeSystem . InvalidWMIClassPath , valueToConvertString );
throw new PSInvalidCastException ( message );
}
return wmiClass ;
}
catch ( Exception wmiClassException )
{
typeConversion . WriteLine ( "Exception creating WMI class: \"{0}\"." , wmiClassException . Message );
throw new PSInvalidCastException ( "InvalidCastToWMIClass" , wmiClassException ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), wmiClassException . Message );
}
}
private static DirectoryEntry ConvertToADSI ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
2017-09-04 19:45:51 -07:00
typeConversion . WriteLine ( "Standard type conversion to DirectoryEntry." );
2016-03-30 14:20:52 -07:00
string valueToConvertString ;
try
{
valueToConvertString = PSObject . ToString ( null , valueToConvert , "\n" , null , null , true , true );
}
catch ( ExtendedTypeSystemException e )
{
typeConversion . WriteLine ( "Exception converting value to string: {0}" , e . Message );
throw new PSInvalidCastException ( "InvalidCastGetStringToADSIClass" , e ,
ExtendedTypeSystem . InvalidCastExceptionNoStringForConversion ,
resultType . ToString (), e . Message );
}
try
{
DirectoryEntry entry = new DirectoryEntry ( valueToConvertString );
return entry ;
}
catch ( Exception adsiClassException )
{
typeConversion . WriteLine ( "Exception creating ADSI class: \"{0}\"." , adsiClassException . Message );
throw new PSInvalidCastException ( "InvalidCastToADSIClass" , adsiClassException ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), adsiClassException . Message );
}
}
private static DirectorySearcher ConvertToADSISearcher ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Standard type conversion to ADSISearcher" );
try
{
return new DirectorySearcher (( string ) valueToConvert );
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Exception creating ADSI searcher: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastToADSISearcher" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
}
#endif
private static StringCollection ConvertToStringCollection ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Standard type conversion to a StringCollection." );
var stringCollection = new StringCollection ();
AddItemsToCollection ( valueToConvert , resultType , formatProvider , backupTable , stringCollection );
return stringCollection ;
}
private static void AddItemsToCollection ( object valueToConvert , Type resultType , IFormatProvider formatProvider , TypeTable backupTable , StringCollection stringCollection )
{
try
{
var stringArrayValue = ( string []) ConvertTo ( valueToConvert , typeof ( string []), false , formatProvider , backupTable );
stringCollection . AddRange ( stringArrayValue );
}
catch ( PSInvalidCastException )
{
typeConversion . WriteLine ( "valueToConvert contains non-string type values" );
var argEx = new ArgumentException ( StringUtil . Format ( ExtendedTypeSystem . CannotConvertValueToStringArray , valueToConvert . ToString ()));
throw new PSInvalidCastException ( StringUtil . Format ( "InvalidCastTo{0}Class" , resultType . Name ), argEx ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException , valueToConvert . ToString (), resultType . ToString (), argEx . Message );
}
catch ( Exception ex )
{
typeConversion . WriteLine ( "Exception creating StringCollection class: \"{0}\"." , ex . Message );
throw new PSInvalidCastException ( StringUtil . Format ( "InvalidCastTo{0}Class" , resultType . Name ), ex ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException , valueToConvert . ToString (), resultType . ToString (), ex . Message );
}
}
private static XmlDocument ConvertToXml ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
using ( typeConversion . TraceScope ( "Standard type conversion to XmlDocument." ))
{
string valueToConvertString ;
try
{
valueToConvertString = PSObject . ToString ( null , valueToConvert , "\n" , null , null , true , true );
}
catch ( ExtendedTypeSystemException e )
{
typeConversion . WriteLine ( "Exception converting value to string: {0}" , e . Message );
throw new PSInvalidCastException ( "InvalidCastGetStringToXmlDocument" , e ,
ExtendedTypeSystem . InvalidCastExceptionNoStringForConversion ,
resultType . ToString (), e . Message );
}
try
{
using ( TextReader textReader = new StringReader ( valueToConvertString ))
{
// Win8: 481571 Enforcing "PreserveWhitespace" breaks XML pretty printing
XmlReaderSettings settings = InternalDeserializer . XmlReaderSettingsForUntrustedXmlDocument . Clone ();
settings . IgnoreWhitespace = true ;
settings . IgnoreProcessingInstructions = false ;
settings . IgnoreComments = false ;
XmlReader xmlReader = XmlReader . Create ( textReader , settings );
XmlDocument xmlDocument = new XmlDocument ();
xmlDocument . PreserveWhitespace = false ;
xmlDocument . Load ( xmlReader );
return xmlDocument ;
}
}
catch ( Exception loadXmlException )
{
typeConversion . WriteLine ( "Exception loading XML: \"{0}\"." , loadXmlException . Message );
throw new PSInvalidCastException ( "InvalidCastToXmlDocument" , loadXmlException ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), loadXmlException . Message );
}
}
}
private static CultureInfo GetCultureFromFormatProvider ( IFormatProvider formatProvider )
{
2016-08-02 22:25:13 -07:00
CultureInfo returnValue = formatProvider as CultureInfo ?? CultureInfo . InvariantCulture ;
2016-03-30 14:20:52 -07:00
return returnValue ;
}
/// backupTypeTable:
2017-01-16 13:31:14 -08:00
/// Used by Remoting Rehydration Logic. While Deserializing a remote object,
2016-08-28 05:44:55 +00:00
/// LocalPipeline.ExecutionContextFromTLS() might return null..In which case this
2016-03-30 14:20:52 -07:00
/// TypeTable will be used to do the conversion.
private static bool IsCustomTypeConversion ( object valueToConvert ,
Type resultType ,
IFormatProvider formatProvider ,
out object result ,
TypeTable backupTypeTable )
{
using ( typeConversion . TraceScope ( "Custom type conversion." ))
{
object baseValueToConvert = PSObject . Base ( valueToConvert );
Type originalType = baseValueToConvert . GetType ();
// first using ConvertTo for the original type
object valueConverter = GetConverter ( originalType , backupTypeTable );
if (( valueConverter != null ))
{
TypeConverter valueTypeConverter = valueConverter as TypeConverter ;
if ( valueTypeConverter != null )
{
typeConversion . WriteLine ( "Original type's converter is TypeConverter." );
if ( valueTypeConverter . CanConvertTo ( resultType ))
{
typeConversion . WriteLine ( "TypeConverter can convert to resultType." );
try
{
result = valueTypeConverter . ConvertTo ( null , GetCultureFromFormatProvider ( formatProvider ), baseValueToConvert , resultType );
return true ;
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Exception converting with Original type's TypeConverter: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastTypeConvertersConvertTo" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
}
else
{
typeConversion . WriteLine ( "TypeConverter cannot convert to resultType." );
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
PSTypeConverter valuePSTypeConverter = valueConverter as PSTypeConverter ;
if ( valuePSTypeConverter != null )
{
typeConversion . WriteLine ( "Original type's converter is PSTypeConverter." );
PSObject psValueToConvert = PSObject . AsPSObject ( valueToConvert );
if ( valuePSTypeConverter . CanConvertTo ( psValueToConvert , resultType ))
{
typeConversion . WriteLine ( "Original type's PSTypeConverter can convert to resultType." );
try
{
result = valuePSTypeConverter . ConvertTo ( psValueToConvert , resultType , formatProvider , true );
return true ;
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Exception converting with Original type's PSTypeConverter: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastPSTypeConvertersConvertTo" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
}
else
{
typeConversion . WriteLine ( "Original type's PSTypeConverter cannot convert to resultType." );
}
}
}
2018-12-23 22:20:06 -08:00
2016-07-29 13:02:49 -07:00
s_tracer . WriteLine ( "No converter found in original type." );
2016-03-30 14:20:52 -07:00
// now ConvertFrom for the destination type
valueConverter = GetConverter ( resultType , backupTypeTable );
if ( valueConverter != null )
{
TypeConverter valueTypeConverter = valueConverter as TypeConverter ;
if ( valueTypeConverter != null )
{
typeConversion . WriteLine ( "Destination type's converter is TypeConverter that can convert from originalType." );
if ( valueTypeConverter . CanConvertFrom ( originalType ))
{
typeConversion . WriteLine ( "Destination type's converter can convert from originalType." );
try
{
result = valueTypeConverter . ConvertFrom ( null , GetCultureFromFormatProvider ( formatProvider ), baseValueToConvert );
return true ;
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Exception converting with Destination type's TypeConverter: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastTypeConvertersConvertFrom" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
}
else
{
typeConversion . WriteLine ( "Destination type's converter cannot convert from originalType." );
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
PSTypeConverter valuePSTypeConverter = valueConverter as PSTypeConverter ;
if ( valuePSTypeConverter != null )
{
typeConversion . WriteLine ( "Destination type's converter is PSTypeConverter." );
PSObject psValueToConvert = PSObject . AsPSObject ( valueToConvert );
if ( valuePSTypeConverter . CanConvertFrom ( psValueToConvert , resultType ))
{
typeConversion . WriteLine ( "Destination type's converter can convert from originalType." );
try
{
result = valuePSTypeConverter . ConvertFrom ( psValueToConvert , resultType , formatProvider , true );
return true ;
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Exception converting with Destination type's PSTypeConverter: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastPSTypeConvertersConvertFrom" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
}
else
{
typeConversion . WriteLine ( "Destination type's converter cannot convert from originalType." );
}
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
result = null ;
return false ;
}
}
2016-08-02 19:00:50 -07:00
private static object ConvertNumericChar ( object valueToConvert ,
2016-03-30 14:20:52 -07:00
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
try
{
// Convert char through int to float/double.
object result = Convert . ChangeType (
Convert . ChangeType ( valueToConvert , typeof ( int ), formatProvider ), resultType , formatProvider );
typeConversion . WriteLine ( "Numeric conversion succeeded." );
return result ;
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Exception converting with IConvertible: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastIConvertible" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
}
2016-08-02 19:00:50 -07:00
private static object ConvertNumeric ( object valueToConvert ,
2016-03-30 14:20:52 -07:00
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
try
{
object result = Convert . ChangeType ( valueToConvert , resultType , formatProvider );
typeConversion . WriteLine ( "Numeric conversion succeeded." );
return result ;
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Exception converting with IConvertible: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastIConvertible" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
}
2016-08-02 19:00:50 -07:00
private static char [] ConvertStringToCharArray ( object valueToConvert ,
2016-03-30 14:20:52 -07:00
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
Diagnostics . Assert ( valueToConvert is string , "Value to convert must be string" );
typeConversion . WriteLine ( "Returning value to convert's ToCharArray()." );
// This conversion is not wrapped in a try/catch because it can't raise an exception
// unless the string object has been corrupted.
return (( string ) valueToConvert ). ToCharArray ();
}
2016-08-02 19:00:50 -07:00
private static Regex ConvertStringToRegex ( object valueToConvert ,
2016-03-30 14:20:52 -07:00
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
Diagnostics . Assert ( valueToConvert is string , "Value to convert must be string" );
typeConversion . WriteLine ( "Returning new RegEx(value to convert)." );
try
{
return new Regex (( string ) valueToConvert );
}
catch ( Exception regexException )
{
typeConversion . WriteLine ( "Exception in RegEx constructor: \"{0}\"." , regexException . Message );
throw new PSInvalidCastException ( "InvalidCastFromStringToRegex" , regexException ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), regexException . Message );
}
}
2016-08-02 19:00:50 -07:00
private static Microsoft . Management . Infrastructure . CimSession ConvertStringToCimSession ( object valueToConvert ,
2016-03-30 14:20:52 -07:00
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
Diagnostics . Assert ( valueToConvert is string , "Value to convert must be string" );
typeConversion . WriteLine ( "Returning CimSession.Create(value to convert)." );
try
{
2016-07-29 13:02:49 -07:00
return Microsoft . Management . Infrastructure . CimSession . Create (( string ) valueToConvert );
2016-03-30 14:20:52 -07:00
}
catch ( Microsoft . Management . Infrastructure . CimException cimException )
{
typeConversion . WriteLine ( "Exception in CimSession.Create: \"{0}\"." , cimException . Message );
throw new PSInvalidCastException ( "InvalidCastFromStringToCimSession" , cimException ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), cimException . Message );
}
}
private static Type ConvertStringToType ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
Diagnostics . Assert ( valueToConvert is string , "Value to convert must be a string" );
Exception exception ;
Type namedType = TypeResolver . ResolveType (( string ) valueToConvert , out exception );
if ( namedType == null )
{
if ( exception is PSInvalidCastException )
{
throw exception ;
}
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
throw new PSInvalidCastException ( "InvalidCastFromStringToType" , exception ,
ExtendedTypeSystem . InvalidCastException ,
valueToConvert . ToString (), ObjectToTypeNameString ( valueToConvert ), resultType . ToString ());
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return namedType ;
}
/// <summary>
2016-08-28 05:44:55 +00:00
/// We need to add this built-in converter because in FullCLR, System.Uri has a TypeConverter attribute
2016-03-30 14:20:52 -07:00
/// declared: [TypeConverter(typeof(UriTypeConverter))], so the conversion from 'string' to 'Uri' is
/// actually taken care of by 'UriTypeConverter'. However, the type 'UriTypeConverter' is not available
/// in CoreCLR, and thus the conversion from 'string' to 'Uri' would show a different behavior.
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// Therefore, we just add this built-in string-to-uri converter using the same logic 'UriTypeConverter'
/// is using in FullCLR, so the conversion behavior will be the same on desktop powershell and powershell core.
/// </summary>
private static Uri ConvertStringToUri ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
try
{
Diagnostics . Assert ( valueToConvert is string , "Value to convert must be a string" );
return new Uri (( string ) valueToConvert , UriKind . RelativeOrAbsolute );
}
catch ( Exception uriException )
{
typeConversion . WriteLine ( "Exception in Uri constructor: \"{0}\"." , uriException . Message );
throw new PSInvalidCastException ( "InvalidCastFromStringToUri" , uriException ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), uriException . Message );
}
}
2019-02-04 15:22:05 -05:00
/// <summary>
/// Attempts to use Parser.ScanNumber to get the value of a numeric string.
/// </summary>
/// <param name="strToConvert">The string to convert to a number.</param>
/// <param name="resultType">The resulting value type to convert to.</param>
/// <param name="result">The resulting numeric value.</param>
/// <returns>
/// True if the parse succeeds, false if a parse exception arises.
/// In all other cases, an exception will be thrown.
/// </returns>
private static bool TryScanNumber ( string strToConvert , Type resultType , out object result )
{
try
{
result = Convert . ChangeType (
Parser . ScanNumber ( strToConvert , resultType , shouldTryCoercion : false ),
resultType ,
System . Globalization . CultureInfo . InvariantCulture . NumberFormat );
return true ;
}
catch ( Exception )
{
// Parse or convert failed
result = null ;
return false ;
}
}
2016-03-30 14:20:52 -07:00
private static object ConvertStringToInteger ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
var strToConvert = valueToConvert as string ;
Diagnostics . Assert ( strToConvert != null , "Value to convert must be a string" );
Diagnostics . Assert ( IsNumeric ( GetTypeCode ( resultType )), "Result type must be numeric" );
if ( strToConvert . Length == 0 )
{
typeConversion . WriteLine ( "Returning numeric zero." );
// This is not wrapped in a try/catch because it can't fail.
return System . Convert . ChangeType ( 0 , resultType , CultureInfo . InvariantCulture );
}
typeConversion . WriteLine ( "Converting to integer." );
TypeConverter integerConverter = LanguagePrimitives . GetIntegerSystemConverter ( resultType );
try
{
2019-02-04 15:22:05 -05:00
if ( TryScanNumber ( strToConvert , resultType , out object result ))
{
return result ;
}
else
{
return integerConverter . ConvertFrom ( strToConvert );
}
2016-03-30 14:20:52 -07:00
}
catch ( Exception e )
{
2017-01-16 13:31:14 -08:00
// This catch has one extra reason to be generic (Exception e).
2016-03-30 14:20:52 -07:00
// integerConverter.ConvertFrom warps its exceptions in a System.Exception.
if ( e . InnerException != null )
{
e = e . InnerException ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
typeConversion . WriteLine ( "Exception converting to integer: \"{0}\"." , e . Message );
if ( e is FormatException )
{
typeConversion . WriteLine ( "Converting to integer passing through double." );
try
{
return ConvertNumericThroughDouble ( strToConvert , resultType );
}
catch ( Exception ex ) // swallow non-severe exceptions
{
typeConversion . WriteLine ( "Exception converting to integer through double: \"{0}\"." , ex . Message );
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
throw new PSInvalidCastException ( "InvalidCastFromStringToInteger" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
strToConvert , resultType . ToString (), e . Message );
}
}
private static object ConvertStringToDecimal ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
Diagnostics . Assert ( valueToConvert is string , "Value to convert must be a string" );
2019-02-04 15:22:05 -05:00
var strToConvert = valueToConvert as string ;
2016-03-30 14:20:52 -07:00
2019-02-04 15:22:05 -05:00
if ( strToConvert . Length == 0 )
2016-03-30 14:20:52 -07:00
{
typeConversion . WriteLine ( "Returning numeric zero." );
// This is not wrapped in a try/catch because it can't fail.
return System . Convert . ChangeType ( 0 , resultType , CultureInfo . InvariantCulture );
}
typeConversion . WriteLine ( "Converting to decimal." );
try
{
2019-02-04 15:22:05 -05:00
typeConversion . WriteLine ( "Parsing string value to account for multipliers and type suffixes" );
if ( TryScanNumber ( strToConvert , resultType , out object result ))
{
return result ;
}
else
{
return Convert . ChangeType ( strToConvert , resultType , CultureInfo . InvariantCulture . NumberFormat );
}
2016-03-30 14:20:52 -07:00
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Exception converting to decimal: \"{0}\". Converting to decimal passing through double." , e . Message );
if ( e is FormatException )
{
try
{
2019-02-04 15:22:05 -05:00
return ConvertNumericThroughDouble ( strToConvert , resultType );
2016-03-30 14:20:52 -07:00
}
catch ( Exception ex )
{
typeConversion . WriteLine ( "Exception converting to integer through double: \"{0}\"." , ex . Message );
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
throw new PSInvalidCastException ( "InvalidCastFromStringToDecimal" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
2019-02-04 15:22:05 -05:00
strToConvert , resultType . ToString (), e . Message );
2016-03-30 14:20:52 -07:00
}
}
private static object ConvertStringToReal ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
Diagnostics . Assert ( valueToConvert is string , "Value to convert must be a string" );
2019-02-04 15:22:05 -05:00
var strToConvert = valueToConvert as string ;
2016-03-30 14:20:52 -07:00
2019-02-04 15:22:05 -05:00
if ( strToConvert . Length == 0 )
2016-03-30 14:20:52 -07:00
{
typeConversion . WriteLine ( "Returning numeric zero." );
// This is not wrapped in a try/catch because it can't fail.
return System . Convert . ChangeType ( 0 , resultType , CultureInfo . InvariantCulture );
}
typeConversion . WriteLine ( "Converting to double or single." );
try
{
2019-02-04 15:22:05 -05:00
typeConversion . WriteLine ( "Parsing string value to account for multipliers and type suffixes" );
if ( TryScanNumber ( strToConvert , resultType , out object result ))
{
return result ;
}
else
{
return Convert . ChangeType ( strToConvert , resultType , CultureInfo . InvariantCulture . NumberFormat );
}
2016-03-30 14:20:52 -07:00
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Exception converting to double or single: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastFromStringToDoubleOrSingle" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
2019-02-04 15:22:05 -05:00
strToConvert , resultType . ToString (), e . Message );
2016-03-30 14:20:52 -07:00
}
}
private static object ConvertAssignableFrom ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Result type is assignable from value to convert's type" );
return valueToConvert ;
}
private static PSObject ConvertToPSObject ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Returning PSObject.AsPSObject(valueToConvert)." );
return PSObject . AsPSObject ( valueToConvert );
}
private static object ConvertToVoid ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "returning AutomationNull.Value." );
return AutomationNull . Value ;
}
2019-06-28 14:39:34 -04:00
private static bool ConvertClassToBool (
object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
2016-03-30 14:20:52 -07:00
{
typeConversion . WriteLine ( "Converting ref to boolean." );
2019-06-28 14:39:34 -04:00
// Both NullString and DBNull should be treated the same as true nulls for the purposes of this conversion.
return ! IsNullLike ( valueToConvert );
2016-03-30 14:20:52 -07:00
}
private static bool ConvertValueToBool ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Converting value to boolean." );
return true ;
}
private static bool ConvertStringToBool ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Converting string to boolean." );
return LanguagePrimitives . IsTrue (( string ) valueToConvert );
}
private static bool ConvertInt16ToBool ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
return (( Int16 ) valueToConvert ) != default ( Int16 );
}
private static bool ConvertInt32ToBool ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
return (( Int32 ) valueToConvert ) != default ( Int32 );
}
private static bool ConvertInt64ToBool ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
return (( Int64 ) valueToConvert ) != default ( Int64 );
}
private static bool ConvertUInt16ToBool ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
return (( UInt16 ) valueToConvert ) != default ( UInt16 );
}
private static bool ConvertUInt32ToBool ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
return (( UInt32 ) valueToConvert ) != default ( UInt32 );
}
private static bool ConvertUInt64ToBool ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
return (( UInt64 ) valueToConvert ) != default ( UInt64 );
}
private static bool ConvertSByteToBool ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
2019-11-06 08:47:32 +05:00
return (( sbyte ) valueToConvert ) != default ( sbyte );
2016-03-30 14:20:52 -07:00
}
private static bool ConvertByteToBool ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
2019-11-06 08:47:32 +05:00
return (( byte ) valueToConvert ) != default ( byte );
2016-03-30 14:20:52 -07:00
}
private static bool ConvertSingleToBool ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
return (( Single ) valueToConvert ) != default ( Single );
}
private static bool ConvertDoubleToBool ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
2019-11-06 08:47:32 +05:00
return (( double ) valueToConvert ) != default ( double );
2016-03-30 14:20:52 -07:00
}
private static bool ConvertDecimalToBool ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
return (( Decimal ) valueToConvert ) != default ( Decimal );
}
2016-07-29 13:02:49 -07:00
private static PSConverter < bool > CreateNumericToBoolConverter ( Type fromType )
2016-03-30 14:20:52 -07:00
{
Diagnostics . Assert ( LanguagePrimitives . IsNumeric ( fromType . GetTypeCode ()), "Can only convert numeric types" );
var valueToConvert = Expression . Parameter ( typeof ( object ));
var parameters = new ParameterExpression []
{
valueToConvert ,
Expression . Parameter ( typeof ( Type )),
Expression . Parameter ( typeof ( bool )),
Expression . Parameter ( typeof ( PSObject )),
Expression . Parameter ( typeof ( IFormatProvider )),
Expression . Parameter ( typeof ( TypeTable ))
};
return Expression . Lambda < PSConverter < bool >>(
Expression . NotEqual ( Expression . Default ( fromType ), valueToConvert . Cast ( fromType )),
parameters ). Compile ();
}
private static bool ConvertCharToBool ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Converting char to boolean." );
char c = ( char ) valueToConvert ;
return c != '\0' ;
}
private static bool ConvertSwitchParameterToBool ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Converting SwitchParameter to boolean." );
return (( SwitchParameter ) valueToConvert ). ToBool ();
}
private static bool ConvertIListToBool ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Converting IList to boolean." );
return IsTrue (( IList ) valueToConvert );
}
private static string ConvertNumericToString ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
if ( originalValueToConvert != null && originalValueToConvert . TokenText != null )
{
return originalValueToConvert . TokenText ;
}
typeConversion . WriteLine ( "Converting numeric to string." );
try
{
// Ignore formatProvider here, the conversion should be culture invariant.
2019-06-18 20:56:43 -07:00
var numberFormat = CultureInfo . InvariantCulture . NumberFormat ;
if ( valueToConvert is double dbl )
{
return dbl . ToString ( DoublePrecision , numberFormat );
}
if ( valueToConvert is float sgl )
{
return sgl . ToString ( SinglePrecision , numberFormat );
}
2016-03-30 14:20:52 -07:00
return ( string ) Convert . ChangeType ( valueToConvert , resultType , CultureInfo . InvariantCulture . NumberFormat );
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Converting numeric to string Exception: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastFromNumericToString" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
}
private static string ConvertNonNumericToString ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
ExecutionContext ecFromTLS = LocalPipeline . GetExecutionContextFromTLS ();
try
{
typeConversion . WriteLine ( "Converting object to string." );
2018-11-14 22:52:44 +01:00
return PSObject . ToStringParser ( ecFromTLS , valueToConvert , formatProvider );
2016-03-30 14:20:52 -07:00
}
catch ( ExtendedTypeSystemException e )
{
typeConversion . WriteLine ( "Converting object to string Exception: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastFromAnyTypeToString" , e ,
ExtendedTypeSystem . InvalidCastCannotRetrieveString );
}
}
private static Hashtable ConvertIDictionaryToHashtable ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Converting to Hashtable." );
return new Hashtable ( valueToConvert as IDictionary );
}
private static PSReference ConvertToPSReference ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Converting to PSReference." );
Dbg . Assert ( valueToConvert != null , "[ref]$null cast should be handler earlier with a separate ConvertNullToPSReference method" );
return PSReference . CreateInstance ( valueToConvert , valueToConvert . GetType ());
}
private static Delegate ConvertScriptBlockToDelegate ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
Exception exception = null ;
try
{
return (( ScriptBlock ) valueToConvert ). GetDelegate ( resultType );
}
catch ( ArgumentNullException e )
{
exception = e ;
}
catch ( ArgumentException e )
{
exception = e ;
}
catch ( MissingMethodException e )
{
exception = e ;
}
catch ( MemberAccessException e )
{
exception = e ;
}
typeConversion . WriteLine ( "Converting script block to delegate Exception: \"{0}\"." , exception . Message );
throw new PSInvalidCastException ( "InvalidCastFromScriptBlockToDelegate" , exception ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), exception . Message );
}
private static object ConvertToNullable ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
// The CLR doesn't support boxed Nullable<T>. Instead, languages convert to T and box.
return ConvertTo ( valueToConvert , Nullable . GetUnderlyingType ( resultType ), recursion , formatProvider , backupTable );
}
private static object ConvertRelatedArrays ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "The element type of result is assignable from the element type of the value to convert" );
var originalAsArray = ( Array ) valueToConvert ;
var newValue = Array . CreateInstance ( resultType . GetElementType (), originalAsArray . Length );
originalAsArray . CopyTo ( newValue , 0 );
return newValue ;
}
private static object ConvertUnrelatedArrays ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
Array valueAsArray = valueToConvert as Array ;
Type resultElementType = resultType . GetElementType ();
Array resultArray = Array . CreateInstance ( resultElementType , valueAsArray . Length );
for ( int i = 0 ; i < valueAsArray . Length ; i ++)
{
object resultElement = ConvertTo ( valueAsArray . GetValue ( i ), resultElementType , false , formatProvider , backupTable );
resultArray . SetValue ( resultElement , i );
}
return resultArray ;
}
private static object ConvertEnumerableToArray ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
try
{
ArrayList result = new ArrayList ();
Type resultElementType = resultType == typeof ( Array ) ? typeof ( object ) : resultType . GetElementType ();
typeConversion . WriteLine ( "Converting elements in the value to convert to the result's element type." );
foreach ( object obj in LanguagePrimitives . GetEnumerable ( valueToConvert ))
{
// false means no further recursions and therefore no cycles
result . Add ( ConvertTo ( obj , resultElementType , false , formatProvider , backupTable ));
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return result . ToArray ( resultElementType );
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Element conversion exception: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastExceptionEnumerableToArray" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
}
private static object ConvertScalarToArray ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Value to convert is scalar." );
if ( originalValueToConvert != null && originalValueToConvert . TokenText != null )
{
valueToConvert = originalValueToConvert ;
}
try
{
Type resultElementType = resultType == typeof ( Array ) ? typeof ( object ) : resultType . GetElementType ();
ArrayList result = new ArrayList ();
// false means no further recursions and therefore no cycles
result . Add ( ConvertTo ( valueToConvert , resultElementType , false , formatProvider , backupTable ));
return result . ToArray ( resultElementType );
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Element conversion exception: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastExceptionScalarToArray" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
}
private static object ConvertIntegerToEnum ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
object result ;
try
{
result = System . Enum . ToObject ( resultType , valueToConvert );
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Integer to System.Enum exception: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastExceptionIntegerToEnum" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
2017-09-04 19:45:51 -07:00
// Check if the result is a defined enum..otherwise throw an error
2016-03-30 14:20:52 -07:00
// valueToConvert is the user supplied value. Use that in the error message.
EnumSingleTypeConverter . ThrowForUndefinedEnum ( "UndefinedIntegerToEnum" , result , valueToConvert , resultType );
return result ;
}
private static object ConvertStringToEnum ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
string valueAsString = valueToConvert as string ;
object result = null ;
typeConversion . WriteLine ( "Calling case sensitive Enum.Parse" );
try
{
result = Enum . Parse ( resultType , valueAsString );
}
catch ( ArgumentException e )
{
typeConversion . WriteLine ( "Enum.Parse Exception: \"{0}\"." , e . Message );
2017-01-16 13:31:14 -08:00
// Enum.Parse will always throw this kind of exception.
2016-03-30 14:20:52 -07:00
// Even when no map exists. We want to try without case sensitivity
2017-01-16 13:31:14 -08:00
// If it works, we will return it, otherwise a new exception will
2016-03-30 14:20:52 -07:00
// be thrown and we will use it to set exceptionToWrap
try
{
typeConversion . WriteLine ( "Calling case insensitive Enum.Parse" );
result = Enum . Parse ( resultType , valueAsString , true );
}
catch ( ArgumentException inner )
{
typeConversion . WriteLine ( "Enum.Parse Exception: \"{0}\"." , inner . Message );
}
catch ( Exception ex ) // Enum.Parse might throw unadvertised exceptions
{
typeConversion . WriteLine ( "Case insensitive Enum.Parse threw an exception." );
throw new PSInvalidCastException ( "CaseInsensitiveEnumParseThrewAnException" , ex ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), ex . Message );
}
}
catch ( Exception e ) // Enum.Parse might throw unadvertised exceptions
{
typeConversion . WriteLine ( "Case Sensitive Enum.Parse threw an exception." );
throw new PSInvalidCastException ( "CaseSensitiveEnumParseThrewAnException" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
if ( result == null )
{
typeConversion . WriteLine ( "Calling substring disambiguation." );
try
{
string enumValue = EnumMinimumDisambiguation . EnumDisambiguate ( valueAsString , resultType );
result = Enum . Parse ( resultType , enumValue );
}
catch ( Exception e ) // Wrap exceptions in type conversion exceptions
{
typeConversion . WriteLine ( "Substring disambiguation threw an exception." );
throw new PSInvalidCastException ( "SubstringDisambiguationEnumParseThrewAnException" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
}
EnumSingleTypeConverter . ThrowForUndefinedEnum ( "EnumParseUndefined" , result , valueToConvert , resultType );
2016-07-29 13:02:49 -07:00
s_tracer . WriteLine ( "returning \"{0}\" from conversion to Enum." , result );
2016-03-30 14:20:52 -07:00
return result ;
}
private static object ConvertEnumerableToEnum ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
IEnumerator e = LanguagePrimitives . GetEnumerator ( valueToConvert );
StringBuilder sbResult = new StringBuilder ();
bool notFirst = false ;
while ( ParserOps . MoveNext ( null , null , e ))
{
if ( notFirst )
sbResult . Append ( ',' );
else
notFirst = true ;
string current = e . Current as string ;
if ( current == null )
{
// If the object wasn't a string, then we'll try and convert it into an enum value,
2017-01-16 13:31:14 -08:00
// then convert the enum back to a string and finally append it to the string builder to
2016-03-30 14:20:52 -07:00
// preserve consistent semantics between quoted and unquoted lists...
object tempResult = ConvertTo ( e . Current , resultType , recursion , formatProvider , backupTable );
if ( tempResult != null )
{
sbResult . Append ( tempResult . ToString ());
}
else
{
throw new PSInvalidCastException ( "InvalidCastEnumStringNotFound" , null ,
ExtendedTypeSystem . InvalidCastExceptionEnumerationNoValue ,
e . Current , resultType , EnumSingleTypeConverter . EnumValues ( resultType ));
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
sbResult . Append ( current );
}
return ConvertStringToEnum ( sbResult . ToString (), resultType , recursion , originalValueToConvert , formatProvider , backupTable );
}
2018-05-15 11:45:04 -07:00
private class PSMethodToDelegateConverter
{
// Index of the matching overload method.
private readonly int _matchIndex ;
// Size of the cache. It's rare to have more than 10 overloads for a method.
private const int CacheSize = 10 ;
private static readonly PSMethodToDelegateConverter [] s_converterCache = new PSMethodToDelegateConverter [ CacheSize ];
private PSMethodToDelegateConverter ( int matchIndex )
{
_matchIndex = matchIndex ;
}
internal static PSMethodToDelegateConverter GetConverter ( int matchIndex )
{
if ( matchIndex >= CacheSize ) { return new PSMethodToDelegateConverter ( matchIndex ); }
var result = s_converterCache [ matchIndex ];
if ( result == null )
{
// If the cache entry is null, generate a new instance for the cache slot.
var converter = new PSMethodToDelegateConverter ( matchIndex );
Threading . Interlocked . CompareExchange ( ref s_converterCache [ matchIndex ], converter , null );
result = s_converterCache [ matchIndex ];
}
return result ;
}
internal Delegate Convert ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
// We can only possibly convert PSMethod instance of the type PSMethod<T>.
// Such a PSMethod essentially represents a set of .NET method overloads.
var psMethod = ( PSMethod ) valueToConvert ;
try
{
var methods = ( MethodCacheEntry ) psMethod . adapterData ;
var isStatic = psMethod . instance is Type ;
var candidate = ( MethodInfo ) methods . methodInformationStructures [ _matchIndex ]. method ;
return isStatic ? candidate . CreateDelegate ( resultType )
: candidate . CreateDelegate ( resultType , psMethod . instance );
}
catch ( Exception e )
{
typeConversion . WriteLine ( "PSMethod to Delegate exception: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastExceptionPSMethodToDelegate" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
}
}
2016-03-30 14:20:52 -07:00
private class ConvertViaParseMethod
{
// TODO - use an ETS wrapper that generates a dynamic method
internal MethodInfo parse ;
internal object ConvertWithCulture ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
try
{
object result = parse . Invoke ( null , new object [ 2 ] { valueToConvert , formatProvider });
typeConversion . WriteLine ( "Parse result: {0}" , result );
return result ;
}
catch ( TargetInvocationException ex )
{
Exception inner = ex . InnerException ?? ex ;
typeConversion . WriteLine ( "Exception calling Parse method with CultureInfo: \"{0}\"." , inner . Message );
throw new PSInvalidCastException ( "InvalidCastParseTargetInvocationWithFormatProvider" , inner ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), inner . Message );
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Exception calling Parse method with CultureInfo: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastParseExceptionWithFormatProvider" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
}
internal object ConvertWithoutCulture ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
try
{
object result = parse . Invoke ( null , new object [ 1 ] { valueToConvert });
typeConversion . WriteLine ( "Parse result: \"{0}\"." , result );
return result ;
}
catch ( TargetInvocationException ex )
{
Exception inner = ex . InnerException ?? ex ;
typeConversion . WriteLine ( "Exception calling Parse method: \"{0}\"." , inner . Message );
throw new PSInvalidCastException ( "InvalidCastParseTargetInvocation" , inner ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), inner . Message );
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Exception calling Parse method: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastParseException" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
}
}
private class ConvertViaConstructor
{
internal Func < object , object > TargetCtorLambda ;
internal object Convert ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
try
{
object result = TargetCtorLambda ( valueToConvert );
typeConversion . WriteLine ( "Constructor result: \"{0}\"." , result );
return result ;
}
catch ( TargetInvocationException ex )
{
Exception inner = ex . InnerException ?? ex ;
typeConversion . WriteLine ( "Exception invoking Constructor: \"{0}\"." , inner . Message );
throw new PSInvalidCastException ( "InvalidCastConstructorTargetInvocationException" , inner ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), inner . Message );
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Exception invoking Constructor: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastConstructorException" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
}
}
/// <summary>
/// Create a IList to hold all elements, and use the IList to create the object of the resultType.
/// The reason for using IList is that it can work on constructors that takes IEnumerable[T], ICollection[T] or IList[T].
/// </summary>
/// <remark>
/// When get to this method, we know the fromType and the toType meet the following two conditions:
/// 1. toType is a closed generic type and it has a constructor that takes IEnumerable[T], ICollection[T] or IList[T]
/// 2. fromType is System.Array, System.Object[] or it's the same as the element type of toType
/// </remark>
private class ConvertViaIEnumerableConstructor
{
internal Func < int , IList > ListCtorLambda ;
internal Func < IList , object > TargetCtorLambda ;
internal Type ElementType ;
internal bool IsScalar ;
internal object Convert ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
IList resultAsList = null ;
Array array = null ;
try
{
2017-02-22 23:10:06 -08:00
int listSize = 0 ;
if ( IsScalar )
2016-03-30 14:20:52 -07:00
{
listSize = 1 ;
}
else
{
2017-02-22 23:10:06 -08:00
// typeof(Array).IsAssignableFrom(typeof(object[])) == true
array = valueToConvert as Array ;
listSize = array != null ? array . Length : 1 ;
2016-03-30 14:20:52 -07:00
}
resultAsList = ListCtorLambda ( listSize );
}
2016-11-01 15:24:07 -07:00
catch ( Exception )
2016-03-30 14:20:52 -07:00
{
ThrowInvalidCastException ( valueToConvert , resultType );
Diagnostics . Assert ( false , "ThrowInvalidCastException always throws" );
return null ;
}
if ( IsScalar )
{
resultAsList . Add ( valueToConvert );
}
2017-02-22 23:10:06 -08:00
else if ( array == null )
2016-03-30 14:20:52 -07:00
{
object convertedValue = LanguagePrimitives . ConvertTo ( valueToConvert , ElementType , formatProvider );
resultAsList . Add ( convertedValue );
}
else
{
foreach ( object item in array )
{
object baseObj = PSObject . Base ( item );
object convertedValue ;
if (! LanguagePrimitives . TryConvertTo ( baseObj , ElementType , formatProvider , out convertedValue ))
{
ThrowInvalidCastException ( valueToConvert , resultType );
Diagnostics . Assert ( false , "ThrowInvalidCastException always throws" );
return null ;
}
else
{
resultAsList . Add ( convertedValue );
}
}
}
try
{
object result = TargetCtorLambda ( resultAsList );
typeConversion . WriteLine ( "IEnumerable Constructor result: \"{0}\"." , result );
return result ;
}
catch ( TargetInvocationException ex )
{
Exception inner = ex . InnerException ?? ex ;
typeConversion . WriteLine ( "Exception invoking IEnumerable Constructor: \"{0}\"." , inner . Message );
throw new PSInvalidCastException ( "InvalidCastConstructorTargetInvocationException" , inner ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), inner . Message );
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Exception invoking IEnumerable Constructor: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastConstructorException" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
}
}
private class ConvertViaNoArgumentConstructor
{
2016-07-29 13:02:49 -07:00
private readonly Func < object > _constructor ;
2016-03-30 14:20:52 -07:00
internal ConvertViaNoArgumentConstructor ( ConstructorInfo constructor , Type type )
{
var newExpr = ( constructor != null ) ? Expression . New ( constructor ) : Expression . New ( type );
2016-07-29 13:02:49 -07:00
_constructor = Expression . Lambda < Func < object >>( newExpr . Cast ( typeof ( object ))). Compile ();
2016-03-30 14:20:52 -07:00
}
internal object Convert ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
return Convert ( valueToConvert , resultType , recursion , originalValueToConvert , formatProvider , backupTable , false );
}
internal object Convert ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable , bool ignoreUnknownMembers )
{
try
{
ExecutionContext ecFromTLS = LocalPipeline . GetExecutionContextFromTLS ();
object result = null ;
2018-11-13 16:16:29 -08:00
// Setting arbitrary properties is dangerous, so we allow this only if
// - It's running on a thread without Runspace; Or
// - It's in FullLanguage but not because it's part of a parameter binding that is transitioning from ConstrainedLanguage to FullLanguage
// When this is invoked from a parameter binding in transition from ConstrainedLanguage environment to FullLanguage command, we disallow
// the property conversion because it's dangerous.
if ( ecFromTLS == null || ( ecFromTLS . LanguageMode == PSLanguageMode . FullLanguage && ! ecFromTLS . LanguageModeTransitionInParameterBinding ))
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
result = _constructor ();
2016-03-30 14:20:52 -07:00
var psobject = valueToConvert as PSObject ;
if ( psobject != null )
{
// Use PSObject properties to perform conversion.
SetObjectProperties ( result , psobject , resultType , CreateMemberNotFoundError , CreateMemberSetValueError , formatProvider , recursion , ignoreUnknownMembers );
}
else
{
// Use provided property dictionary to perform conversion.
// The method invocation is disabled for "Hashtable to Object conversion" (Win8:649519), but we need to keep it enabled for New-Object for compatibility to PSv2
IDictionary properties = valueToConvert as IDictionary ;
SetObjectProperties ( result , properties , resultType , CreateMemberNotFoundError , CreateMemberSetValueError , enableMethodCall : false );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
typeConversion . WriteLine ( "Constructor result: \"{0}\"." , result );
}
else
{
RuntimeException rte = InterpreterError . NewInterpreterException ( valueToConvert , typeof ( RuntimeException ), null ,
"HashtableToObjectConversionNotSupportedInDataSection" , ParserStrings . HashtableToObjectConversionNotSupportedInDataSection , resultType . ToString ());
throw rte ;
}
return result ;
}
catch ( TargetInvocationException ex )
{
Exception inner = ex . InnerException ?? ex ;
typeConversion . WriteLine ( "Exception invoking Constructor: \"{0}\"." , inner . Message );
throw new PSInvalidCastException ( "InvalidCastConstructorTargetInvocationException" , inner ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), inner . Message );
}
catch ( InvalidOperationException e )
{
Exception inner = e . InnerException ?? e ;
throw new PSInvalidCastException ( "ObjectCreationError" , e ,
ExtendedTypeSystem . ObjectCreationError , resultType . ToString (), inner . Message );
}
catch ( SetValueException e )
{
Exception inner = e . InnerException ?? e ;
throw new PSInvalidCastException ( "ObjectCreationError" , inner ,
ExtendedTypeSystem . ObjectCreationError , resultType . ToString (), inner . Message );
}
catch ( RuntimeException ex )
{
Exception inner = ex . InnerException ?? ex ;
typeConversion . WriteLine ( "Exception invoking Constructor: \"{0}\"." , inner . Message );
throw new PSInvalidCastException ( "InvalidCastConstructorException" , inner ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), inner . Message );
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Exception invoking Constructor: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastConstructorException" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
}
}
private class ConvertViaCast
{
internal MethodInfo cast ;
internal object Convert ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
try
{
return cast . Invoke ( null , new object [ 1 ] { valueToConvert });
}
catch ( TargetInvocationException ex )
{
Exception inner = ex . InnerException ?? ex ;
typeConversion . WriteLine ( "Cast operator exception: \"{0}\"." , inner . Message );
throw new PSInvalidCastException ( "InvalidCastTargetInvocationException" + cast . Name , inner ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), inner . Message );
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Cast operator exception: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastException" + cast . Name , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
}
}
private static object ConvertIConvertible ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
try
{
object result = Convert . ChangeType ( valueToConvert , resultType , formatProvider );
typeConversion . WriteLine ( "Conversion using IConvertible succeeded." );
return result ;
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Exception converting with IConvertible: \"{0}\"." , e . Message );
throw new PSInvalidCastException ( "InvalidCastIConvertible" , e ,
ExtendedTypeSystem . InvalidCastExceptionWithInnerException ,
valueToConvert . ToString (), resultType . ToString (), e . Message );
}
}
private static object ConvertNumericIConvertible ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
// If the original object was a number, then try and do a conversion on the string
// equivalent of that number...
if ( originalValueToConvert != null && originalValueToConvert . TokenText != null )
{
return LanguagePrimitives . ConvertTo ( originalValueToConvert . TokenText ,
resultType , recursion , formatProvider , backupTable );
}
else
{
// Convert the source object to a string...
string s = ( string ) LanguagePrimitives . ConvertTo ( valueToConvert ,
typeof ( string ), recursion , formatProvider , backupTable );
// And try and convert that string to the target type...
return LanguagePrimitives . ConvertTo ( s ,
resultType , recursion , formatProvider , backupTable );
}
}
private class ConvertCheckingForCustomConverter
{
internal PSConverter < object > tryfirstConverter ;
internal PSConverter < object > fallbackConverter ;
internal object Convert ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
object result = null ;
if ( tryfirstConverter != null )
{
try
{
return tryfirstConverter ( valueToConvert , resultType , recursion , originalValueToConvert , formatProvider , backupTable );
}
catch ( InvalidCastException )
{
}
}
if ( IsCustomTypeConversion ( originalValueToConvert ?? valueToConvert , resultType , formatProvider , out result , backupTable ))
{
typeConversion . WriteLine ( "Custom Type Conversion succeeded." );
return result ;
}
if ( fallbackConverter != null )
{
return fallbackConverter ( valueToConvert , resultType , recursion , originalValueToConvert , formatProvider , backupTable );
}
throw new PSInvalidCastException ( "ConvertToFinalInvalidCastException" , null ,
ExtendedTypeSystem . InvalidCastException ,
valueToConvert . ToString (), ObjectToTypeNameString ( valueToConvert ), resultType . ToString ());
}
}
#region Delegates converting null
private static object ConvertNullToNumeric ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Converting null to zero." );
// If the destination type is numeric, convert 0 to resultType
return System . Convert . ChangeType ( 0 , resultType , CultureInfo . InvariantCulture );
}
private static char ConvertNullToChar ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Converting null to '0'." );
return '\0' ;
}
private static string ConvertNullToString ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Converting null to \"\"." );
// if the destination type is string, return an empty string...
2018-12-31 20:44:16 +05:00
return string . Empty ;
2016-03-30 14:20:52 -07:00
}
private static PSReference ConvertNullToPSReference ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
return new PSReference < Null >( null );
}
private static object ConvertNullToRef ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
// if the target type is not a value type, return the original
// "null" object. Don't just return null because we want to preserve
// an msh object if possible.
return valueToConvert ;
}
private static bool ConvertNullToBool ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Converting null to boolean." );
return false ;
}
private static object ConvertNullToNullable ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
return null ;
}
private static SwitchParameter ConvertNullToSwitch ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Converting null to SwitchParameter(false)." );
return new SwitchParameter ( false );
}
private static object ConvertNullToVoid ( object valueToConvert ,
Type resultType ,
bool recursion ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
typeConversion . WriteLine ( "Converting null to AutomationNull.Value." );
return AutomationNull . Value ;
}
#endregion Delegates converting null
private static object ConvertNoConversion ( object valueToConvert ,
Type resultType ,
bool recurse ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
ThrowInvalidCastException ( valueToConvert , resultType );
Diagnostics . Assert ( false , "ThrowInvalidCastException always throws" );
return null ;
}
private static object ConvertNotSupportedConversion ( object valueToConvert ,
Type resultType ,
bool recurse ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable )
{
ThrowInvalidConversionException ( valueToConvert , resultType );
Diagnostics . Assert ( false , "ThrowInvalidCastException always throws" );
return null ;
}
2017-12-02 03:40:35 +01:00
[System.Diagnostics.DebuggerDisplay("{from.Name}->{to.Name}")]
2016-03-30 14:20:52 -07:00
private struct ConversionTypePair
{
internal Type from ;
internal Type to ;
internal ConversionTypePair ( Type fromType , Type toType )
{
from = fromType ;
to = toType ;
}
public override int GetHashCode ()
{
unchecked
{
// To prevent to/from == from/to, multiply and add rather than use
// an operation that won't overflow, like bitwise xor.
return from . GetHashCode () + 37 * to . GetHashCode ();
}
}
public override bool Equals ( object other )
{
if (!( other is ConversionTypePair ))
return false ;
2016-07-29 13:02:49 -07:00
var ctp = ( ConversionTypePair ) other ;
2016-03-30 14:20:52 -07:00
return this . from == ctp . from && this . to == ctp . to ;
}
}
internal delegate T PSConverter < T >( object valueToConvert ,
Type resultType ,
bool recurse ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable );
internal delegate object PSNullConverter ( object nullOrAutomationNull );
2018-12-20 23:23:04 -08:00
internal interface IConversionData
2016-03-30 14:20:52 -07:00
{
object Converter { get ; }
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
ConversionRank Rank { get ; }
object Invoke ( object valueToConvert ,
Type resultType ,
bool recurse ,
PSObject originalValueToConvert ,
IFormatProvider formatProvider ,
TypeTable backupTable );
}
2017-12-02 03:40:35 +01:00
[System.Diagnostics.DebuggerDisplay("{_converter.Method.Name}")]
2018-12-20 23:23:04 -08:00
internal class ConversionData < T > : IConversionData
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
private readonly PSConverter < T > _converter ;
2016-03-30 14:20:52 -07:00
public ConversionData ( PSConverter < T > converter , ConversionRank rank )
{
2016-07-29 13:02:49 -07:00
_converter = converter ;
2016-08-02 13:29:09 -07:00
Rank = rank ;
2016-03-30 14:20:52 -07:00
}
public object Converter
{
2016-07-29 13:02:49 -07:00
get { return _converter ; }
2016-03-30 14:20:52 -07:00
}
2016-08-02 13:29:09 -07:00
public ConversionRank Rank { get ; }
2016-03-30 14:20:52 -07:00
public object Invoke ( object valueToConvert , Type resultType , bool recurse , PSObject originalValueToConvert , IFormatProvider formatProvider , TypeTable backupTable )
{
2016-07-29 13:02:49 -07:00
return _converter . Invoke ( valueToConvert , resultType , recurse , originalValueToConvert , formatProvider , backupTable );
2016-03-30 14:20:52 -07:00
}
}
2018-12-20 23:23:04 -08:00
private static Dictionary < ConversionTypePair , IConversionData > s_converterCache = new Dictionary < ConversionTypePair , IConversionData >( 256 );
2016-03-30 14:20:52 -07:00
2018-12-20 23:23:04 -08:00
private static IConversionData CacheConversion < T >( Type fromType , Type toType , PSConverter < T > converter , ConversionRank rank )
2016-03-30 14:20:52 -07:00
{
ConversionTypePair pair = new ConversionTypePair ( fromType , toType );
2018-12-20 23:23:04 -08:00
IConversionData data = null ;
2016-07-29 13:02:49 -07:00
lock ( s_converterCache )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
if (! s_converterCache . TryGetValue ( pair , out data ))
2016-03-30 14:20:52 -07:00
{
data = new ConversionData < T >( converter , rank );
2016-07-29 13:02:49 -07:00
s_converterCache . Add ( pair , data );
2016-03-30 14:20:52 -07:00
}
else
{
Dbg . Assert ((( Delegate )( data . Converter )). GetMethodInfo (). Equals ( converter . GetMethodInfo ()),
"Existing conversion isn't the same as new conversion" );
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return data ;
}
2018-12-20 23:23:04 -08:00
private static IConversionData GetConversionData ( Type fromType , Type toType )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
lock ( s_converterCache )
2016-03-30 14:20:52 -07:00
{
2018-12-20 23:23:04 -08:00
IConversionData result = null ;
2016-07-29 13:02:49 -07:00
s_converterCache . TryGetValue ( new ConversionTypePair ( fromType , toType ), out result );
2016-03-30 14:20:52 -07:00
return result ;
}
}
internal static ConversionRank GetConversionRank ( Type fromType , Type toType )
{
return FigureConversion ( fromType , toType ). Rank ;
}
2016-07-29 13:02:49 -07:00
private static Type [] s_numericTypes = new Type [] {
2016-03-30 14:20:52 -07:00
typeof ( Int16 ), typeof ( Int32 ), typeof ( Int64 ),
typeof ( UInt16 ), typeof ( UInt32 ), typeof ( UInt64 ),
2019-11-06 08:47:32 +05:00
typeof ( sbyte ), typeof ( byte ),
typeof ( Single ), typeof ( double ), typeof ( decimal )
2016-03-30 14:20:52 -07:00
};
2016-07-29 13:02:49 -07:00
private static Type [] s_integerTypes = new Type [] {
2016-03-30 14:20:52 -07:00
typeof ( Int16 ), typeof ( Int32 ), typeof ( Int64 ),
typeof ( UInt16 ), typeof ( UInt32 ), typeof ( UInt64 ),
2019-11-06 08:47:32 +05:00
typeof ( sbyte ), typeof ( byte )
2016-03-30 14:20:52 -07:00
};
// Do not reorder the elements of these arrays, we depend on them being ordered by increasing size.
2019-11-06 08:47:32 +05:00
private static Type [] s_signedIntegerTypes = new Type [] { typeof ( sbyte ), typeof ( Int16 ), typeof ( Int32 ), typeof ( Int64 ) };
private static Type [] s_unsignedIntegerTypes = new Type [] { typeof ( byte ), typeof ( UInt16 ), typeof ( UInt32 ), typeof ( UInt64 ) };
2016-03-30 14:20:52 -07:00
2019-11-06 08:47:32 +05:00
private static Type [] s_realTypes = new Type [] { typeof ( Single ), typeof ( double ), typeof ( decimal ) };
2016-03-30 14:20:52 -07:00
2016-08-02 19:00:50 -07:00
internal static void RebuildConversionCache ()
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
lock ( s_converterCache )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
s_converterCache . Clear ();
2016-03-30 14:20:52 -07:00
Type typeofString = typeof ( string );
Type typeofNull = typeof ( Null );
Type typeofFloat = typeof ( float );
Type typeofDouble = typeof ( double );
2019-11-06 08:47:32 +05:00
Type typeofDecimal = typeof ( decimal );
2016-03-30 14:20:52 -07:00
Type typeofBool = typeof ( bool );
Type typeofChar = typeof ( char );
2016-07-29 13:02:49 -07:00
foreach ( Type type in LanguagePrimitives . s_numericTypes )
2016-03-30 14:20:52 -07:00
{
CacheConversion < string >( type , typeofString , LanguagePrimitives . ConvertNumericToString , ConversionRank . NumericString );
CacheConversion < object >( type , typeofChar , LanguagePrimitives . ConvertIConvertible , ConversionRank . NumericString );
CacheConversion < object >( typeofNull , type , LanguagePrimitives . ConvertNullToNumeric , ConversionRank . NullToValue );
}
CacheConversion < bool >( typeof ( Int16 ), typeofBool , ConvertInt16ToBool , ConversionRank . Language );
CacheConversion < bool >( typeof ( Int32 ), typeofBool , ConvertInt32ToBool , ConversionRank . Language );
CacheConversion < bool >( typeof ( Int64 ), typeofBool , ConvertInt64ToBool , ConversionRank . Language );
CacheConversion < bool >( typeof ( UInt16 ), typeofBool , ConvertUInt16ToBool , ConversionRank . Language );
CacheConversion < bool >( typeof ( UInt32 ), typeofBool , ConvertUInt32ToBool , ConversionRank . Language );
CacheConversion < bool >( typeof ( UInt64 ), typeofBool , ConvertUInt64ToBool , ConversionRank . Language );
2019-11-06 08:47:32 +05:00
CacheConversion < bool >( typeof ( sbyte ), typeofBool , ConvertSByteToBool , ConversionRank . Language );
CacheConversion < bool >( typeof ( byte ), typeofBool , ConvertByteToBool , ConversionRank . Language );
2016-03-30 14:20:52 -07:00
CacheConversion < bool >( typeof ( Single ), typeofBool , ConvertSingleToBool , ConversionRank . Language );
2019-11-06 08:47:32 +05:00
CacheConversion < bool >( typeof ( double ), typeofBool , ConvertDoubleToBool , ConversionRank . Language );
CacheConversion < bool >( typeof ( decimal ), typeofBool , ConvertDecimalToBool , ConversionRank . Language );
2016-03-30 14:20:52 -07:00
2016-07-29 13:02:49 -07:00
for ( int i = 0 ; i < LanguagePrimitives . s_unsignedIntegerTypes . Length ; i ++)
2016-03-30 14:20:52 -07:00
{
// Identical types are an identity conversion.
2016-07-29 13:02:49 -07:00
CacheConversion < object >( s_unsignedIntegerTypes [ i ], s_unsignedIntegerTypes [ i ],
2016-03-30 14:20:52 -07:00
LanguagePrimitives . ConvertAssignableFrom , ConversionRank . Identity );
2016-07-29 13:02:49 -07:00
CacheConversion < object >( s_signedIntegerTypes [ i ], s_signedIntegerTypes [ i ],
2016-03-30 14:20:52 -07:00
LanguagePrimitives . ConvertAssignableFrom , ConversionRank . Identity );
// Unsigned to signed same size is explicit
2016-07-29 13:02:49 -07:00
CacheConversion < object >( s_unsignedIntegerTypes [ i ], s_signedIntegerTypes [ i ],
2016-03-30 14:20:52 -07:00
LanguagePrimitives . ConvertNumeric , ConversionRank . NumericExplicit );
// Signed to unsigned same size is explicit, but better than the reverse (because it is "more specific")
2016-07-29 13:02:49 -07:00
CacheConversion < object >( s_signedIntegerTypes [ i ], s_unsignedIntegerTypes [ i ],
2016-03-30 14:20:52 -07:00
LanguagePrimitives . ConvertNumeric , ConversionRank . NumericExplicit1 );
2016-07-29 13:02:49 -07:00
for ( int j = i + 1 ; j < LanguagePrimitives . s_unsignedIntegerTypes . Length ; j ++)
2016-03-30 14:20:52 -07:00
{
// Conversions where the sign doesn't change, but the size is bigger, is implicit
2016-07-29 13:02:49 -07:00
CacheConversion < object >( s_unsignedIntegerTypes [ i ], s_unsignedIntegerTypes [ j ],
2016-03-30 14:20:52 -07:00
LanguagePrimitives . ConvertNumeric , ConversionRank . NumericImplicit );
2016-07-29 13:02:49 -07:00
CacheConversion < object >( s_signedIntegerTypes [ i ], s_signedIntegerTypes [ j ],
2016-03-30 14:20:52 -07:00
LanguagePrimitives . ConvertNumeric , ConversionRank . NumericImplicit );
// Conversion from smaller unsigned to bigger signed is implicit
2016-07-29 13:02:49 -07:00
CacheConversion < object >( s_unsignedIntegerTypes [ i ], s_signedIntegerTypes [ j ],
2016-03-30 14:20:52 -07:00
LanguagePrimitives . ConvertNumeric , ConversionRank . NumericImplicit );
// Conversion from smaller signed to bigger unsigned is the "better" explicit conversion
2016-07-29 13:02:49 -07:00
CacheConversion < object >( s_signedIntegerTypes [ i ], s_unsignedIntegerTypes [ j ],
2016-03-30 14:20:52 -07:00
LanguagePrimitives . ConvertNumeric , ConversionRank . NumericExplicit1 );
// Conversion to a smaller type is explicit
2016-07-29 13:02:49 -07:00
CacheConversion < object >( s_unsignedIntegerTypes [ j ], s_unsignedIntegerTypes [ i ],
2016-03-30 14:20:52 -07:00
LanguagePrimitives . ConvertNumeric , ConversionRank . NumericExplicit );
2016-07-29 13:02:49 -07:00
CacheConversion < object >( s_signedIntegerTypes [ j ], s_signedIntegerTypes [ i ],
2016-03-30 14:20:52 -07:00
LanguagePrimitives . ConvertNumeric , ConversionRank . NumericExplicit );
2016-07-29 13:02:49 -07:00
CacheConversion < object >( s_unsignedIntegerTypes [ j ], s_signedIntegerTypes [ i ],
2016-03-30 14:20:52 -07:00
LanguagePrimitives . ConvertNumeric , ConversionRank . NumericExplicit );
2016-07-29 13:02:49 -07:00
CacheConversion < object >( s_signedIntegerTypes [ j ], s_unsignedIntegerTypes [ i ],
2016-03-30 14:20:52 -07:00
LanguagePrimitives . ConvertNumeric , ConversionRank . NumericExplicit );
}
}
2018-12-23 22:20:06 -08:00
2016-07-29 13:02:49 -07:00
foreach ( Type integerType in s_integerTypes )
2016-03-30 14:20:52 -07:00
{
CacheConversion < object >( typeofString , integerType , LanguagePrimitives . ConvertStringToInteger , ConversionRank . NumericString );
2016-07-29 13:02:49 -07:00
foreach ( Type realType in s_realTypes )
2016-03-30 14:20:52 -07:00
{
CacheConversion < object >( integerType , realType , LanguagePrimitives . ConvertNumeric , ConversionRank . NumericImplicit );
CacheConversion < object >( realType , integerType , LanguagePrimitives . ConvertNumeric , ConversionRank . NumericExplicit );
}
}
CacheConversion < object >( typeofFloat , typeofDouble , LanguagePrimitives . ConvertNumeric , ConversionRank . NumericImplicit );
CacheConversion < object >( typeofDouble , typeofFloat , LanguagePrimitives . ConvertNumeric , ConversionRank . NumericExplicit );
CacheConversion < object >( typeofFloat , typeofDecimal , LanguagePrimitives . ConvertNumeric , ConversionRank . NumericExplicit );
CacheConversion < object >( typeofDouble , typeofDecimal , LanguagePrimitives . ConvertNumeric , ConversionRank . NumericExplicit );
CacheConversion < object >( typeofDecimal , typeofFloat , LanguagePrimitives . ConvertNumeric , ConversionRank . NumericExplicit1 );
CacheConversion < object >( typeofDecimal , typeofDouble , LanguagePrimitives . ConvertNumeric , ConversionRank . NumericExplicit1 );
CacheConversion < Regex >( typeofString , typeof ( Regex ), LanguagePrimitives . ConvertStringToRegex , ConversionRank . Language );
CacheConversion < char []>( typeofString , typeof ( char []), LanguagePrimitives . ConvertStringToCharArray , ConversionRank . StringToCharArray );
CacheConversion < Type >( typeofString , typeof ( Type ), LanguagePrimitives . ConvertStringToType , ConversionRank . Language );
2016-07-29 13:02:49 -07:00
CacheConversion < Uri >( typeofString , typeof ( Uri ), LanguagePrimitives . ConvertStringToUri , ConversionRank . Language );
2016-03-30 14:20:52 -07:00
CacheConversion < object >( typeofString , typeofDecimal , LanguagePrimitives . ConvertStringToDecimal , ConversionRank . NumericString );
CacheConversion < object >( typeofString , typeofFloat , LanguagePrimitives . ConvertStringToReal , ConversionRank . NumericString );
CacheConversion < object >( typeofString , typeofDouble , LanguagePrimitives . ConvertStringToReal , ConversionRank . NumericString );
CacheConversion < object >( typeofChar , typeofFloat , LanguagePrimitives . ConvertNumericChar , ConversionRank . Language );
CacheConversion < object >( typeofChar , typeofDouble , LanguagePrimitives . ConvertNumericChar , ConversionRank . Language );
CacheConversion < bool >( typeofChar , typeofBool , LanguagePrimitives . ConvertCharToBool , ConversionRank . Language );
// Conversions from null
CacheConversion < char >( typeofNull , typeofChar , LanguagePrimitives . ConvertNullToChar , ConversionRank . NullToValue );
CacheConversion < string >( typeofNull , typeofString , LanguagePrimitives . ConvertNullToString , ConversionRank . ToString );
CacheConversion < bool >( typeofNull , typeofBool , LanguagePrimitives . ConvertNullToBool , ConversionRank . NullToValue );
CacheConversion < PSReference >( typeofNull , typeof ( PSReference ), LanguagePrimitives . ConvertNullToPSReference , ConversionRank . NullToRef );
CacheConversion < SwitchParameter >( typeofNull , typeof ( SwitchParameter ), LanguagePrimitives . ConvertNullToSwitch , ConversionRank . NullToValue );
CacheConversion < object >( typeofNull , typeof ( void ), LanguagePrimitives . ConvertNullToVoid , ConversionRank . NullToValue );
// Conversions to bool
CacheConversion < object >( typeofBool , typeofBool , LanguagePrimitives . ConvertAssignableFrom , ConversionRank . Identity );
CacheConversion < bool >( typeofString , typeofBool , LanguagePrimitives . ConvertStringToBool , ConversionRank . Language );
CacheConversion < bool >( typeof ( SwitchParameter ), typeofBool , LanguagePrimitives . ConvertSwitchParameterToBool , ConversionRank . Language );
2018-06-21 09:57:14 -07:00
#if ! UNIX
// Conversions to WMI and ADSI
2016-03-30 14:20:52 -07:00
CacheConversion < ManagementObjectSearcher >( typeofString , typeof ( ManagementObjectSearcher ), LanguagePrimitives . ConvertToWMISearcher , ConversionRank . Language );
CacheConversion < ManagementClass >( typeofString , typeof ( ManagementClass ), LanguagePrimitives . ConvertToWMIClass , ConversionRank . Language );
CacheConversion < ManagementObject >( typeofString , typeof ( ManagementObject ), LanguagePrimitives . ConvertToWMI , ConversionRank . Language );
CacheConversion < DirectoryEntry >( typeofString , typeof ( DirectoryEntry ), LanguagePrimitives . ConvertToADSI , ConversionRank . Language );
CacheConversion < DirectorySearcher >( typeofString , typeof ( DirectorySearcher ), LanguagePrimitives . ConvertToADSISearcher , ConversionRank . Language );
#endif
}
}
internal static PSObject SetObjectProperties ( object o , PSObject psObject , Type resultType , MemberNotFoundError memberNotFoundErrorAction , MemberSetValueError memberSetValueErrorAction , IFormatProvider formatProvider , bool recursion = false , bool ignoreUnknownMembers = false )
{
// Type conversion from object properties only supported for deserialized types.
if ( Deserializer . IsDeserializedInstanceOfType ( psObject , resultType ))
{
try
{
Dictionary < string , object > properties = new Dictionary < string , object >();
foreach ( var item in psObject . Properties )
{
if ( item is PSProperty )
{
properties . Add ( item . Name , item . Value );
}
}
// Win8:649519
return SetObjectProperties ( o , properties , resultType , memberNotFoundErrorAction , memberSetValueErrorAction , enableMethodCall : false );
}
catch ( SetValueException )
{ }
catch ( InvalidOperationException )
{ }
}
else
{
object baseObj = PSObject . Base ( psObject );
var dictionary = baseObj as IDictionary ;
if ( dictionary != null )
{
// Win8:649519
return SetObjectProperties ( o , dictionary , resultType , memberNotFoundErrorAction , memberSetValueErrorAction , enableMethodCall : false );
}
else
{
// Support PSObject to Strong type conversion.
PSObject psBaseObject = baseObj as PSObject ;
if ( psBaseObject != null )
{
Dictionary < string , object > properties = new Dictionary < string , object >();
foreach ( var item in psBaseObject . Properties )
{
properties . Add ( item . Name , item . Value );
}
try
{
return SetObjectProperties ( o , properties , resultType , memberNotFoundErrorAction , memberSetValueErrorAction , false , formatProvider , recursion , ignoreUnknownMembers );
}
catch ( InvalidOperationException exception )
{
throw new PSInvalidCastException ( "ConvertToFinalInvalidCastException" , exception ,
ExtendedTypeSystem . InvalidCastException ,
psObject . ToString (), ObjectToTypeNameString ( psObject ),
resultType . ToString ());
}
}
}
}
ThrowInvalidCastException ( psObject , resultType );
return null ;
}
internal static PSObject SetObjectProperties ( object o , IDictionary properties , Type resultType , MemberNotFoundError memberNotFoundErrorAction , MemberSetValueError memberSetValueErrorAction , bool enableMethodCall )
{
return SetObjectProperties ( o , properties , resultType , memberNotFoundErrorAction , memberSetValueErrorAction , enableMethodCall , CultureInfo . InvariantCulture , false , false );
}
internal static PSObject SetObjectProperties ( object o , IDictionary properties , Type resultType , MemberNotFoundError memberNotFoundErrorAction , MemberSetValueError memberSetValueErrorAction , bool enableMethodCall , IFormatProvider formatProvider , bool recursion = false , bool ignoreUnknownMembers = false )
{
PSObject pso = PSObject . AsPSObject ( o );
if ( properties != null )
{
foreach ( DictionaryEntry prop in properties )
{
PSMethodInfo method = enableMethodCall ? pso . Methods [ prop . Key . ToString ()] : null ;
try
{
if ( method != null )
{
method . Invoke ( new object [] { prop . Value });
}
else
{
PSPropertyInfo property = pso . Properties [ prop . Key . ToString ()];
if ( property != null )
{
object propValue = prop . Value ;
if ( recursion && prop . Value != null )
{
Type propType ;
if ( TypeResolver . TryResolveType ( property . TypeNameOfValue , out propType ))
{
if ( formatProvider == null ) { formatProvider = CultureInfo . InvariantCulture ; }
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
try
{
PSObject propertyValue = prop . Value as PSObject ;
if ( propertyValue != null )
{
propValue = LanguagePrimitives . ConvertPSObjectToType ( propertyValue , propType , recursion , formatProvider , ignoreUnknownMembers );
}
else if ( prop . Value is PSCustomObject )
{
propValue = LanguagePrimitives . ConvertPSObjectToType ( new PSObject ( prop . Value ), propType , recursion , formatProvider , ignoreUnknownMembers );
}
else
{
propValue = LanguagePrimitives . ConvertTo ( prop . Value , propType , recursion , formatProvider , null );
}
}
catch ( SetValueException )
{
// We don't care. We will assign the value as is.
}
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
property . Value = propValue ;
}
else
{
if ( pso . BaseObject is PSCustomObject )
{
var key = prop . Key as string ;
var value = prop . Value as string ;
if ( key != null && value != null && key . Equals ( "PSTypeName" , StringComparison . OrdinalIgnoreCase ))
{
pso . TypeNames . Insert ( 0 , value );
}
else
{
pso . Properties . Add ( new PSNoteProperty ( prop . Key . ToString (), prop . Value ));
}
}
else
{
if (! ignoreUnknownMembers )
{
memberNotFoundErrorAction ( pso , prop , resultType );
}
}
}
}
}
catch ( SetValueException e )
{
memberSetValueErrorAction ( e );
}
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return pso ;
}
2018-12-22 01:26:06 -08:00
private static string GetAvailableProperties ( PSObject pso )
2016-03-30 14:20:52 -07:00
{
StringBuilder availableProperties = new StringBuilder ();
bool first = true ;
if ( pso != null && pso . Properties != null )
{
foreach ( PSPropertyInfo p in pso . Properties )
{
if ( first == false )
{
availableProperties . Append ( " , " );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
availableProperties . Append ( "[" + p . Name + " <" + p . TypeNameOfValue + ">]" );
if ( first == true )
{
first = false ;
}
}
}
return availableProperties . ToString ();
}
2018-12-20 23:23:04 -08:00
internal static IConversionData FigureConversion ( object valueToConvert , Type resultType , out bool debase )
2016-03-30 14:20:52 -07:00
{
PSObject valueAsPsObj ;
Type originalType ;
2019-06-28 14:39:34 -04:00
if ( IsNull ( valueToConvert ))
2016-03-30 14:20:52 -07:00
{
originalType = typeof ( Null );
2019-06-28 14:39:34 -04:00
valueAsPsObj = null ;
2016-03-30 14:20:52 -07:00
}
else
{
valueAsPsObj = valueToConvert as PSObject ;
originalType = valueToConvert . GetType ();
}
debase = false ;
2018-12-20 23:23:04 -08:00
IConversionData data = FigureConversion ( originalType , resultType );
2016-03-30 14:20:52 -07:00
if ( data . Rank != ConversionRank . None )
{
return data ;
}
if ( valueAsPsObj != null )
{
debase = true ;
// Now try converting PSObject.Base instead.
valueToConvert = PSObject . Base ( valueToConvert );
Dbg . Assert ( valueToConvert != AutomationNull . Value , "PSObject.Base converts AutomationNull.Value to null" );
if ( valueToConvert == null )
{
originalType = typeof ( Null );
}
else
{
// If the original value was a property bag (empty PSObject), we won't find a conversion because
// all PSObject conversions have already been checked.
//
// Still, there are many valid conversions to allow, such as PSObject to bool, void, or
// a custom type converter. To find those, we consider InternalPSObject=>resultType instead.
//
// We use a different type because we can't keep PSObject as the from type in the cache.
originalType = ( valueToConvert is PSObject ) ? typeof ( InternalPSObject ) : valueToConvert . GetType ();
}
data = FigureConversion ( originalType , resultType );
}
return data ;
}
/// <summary>
/// </summary>
2018-12-29 08:25:49 +05:00
/// <param name="valueToConvert">The same as in the public version.</param>
/// <param name="resultType">The same as in the public version.</param>
/// <param name="recursion">True if we should perform any recursive calls to ConvertTo.</param>
/// <param name="formatProvider">Governing conversion of types.</param>
2016-03-30 14:20:52 -07:00
/// <param name="backupTypeTable">
2017-01-16 13:31:14 -08:00
/// Used by Remoting Rehydration Logic. While Deserializing a remote object,
2016-08-28 05:44:55 +00:00
/// LocalPipeline.ExecutionContextFromTLS() might return null..In which case this
2016-03-30 14:20:52 -07:00
/// TypeTable will be used to do the conversion.
/// </param>
2018-12-25 21:43:03 +05:00
/// <returns>The value converted.</returns>
2018-12-29 08:25:49 +05:00
/// <exception cref="ArgumentNullException">If resultType is null.</exception>
/// <exception cref="PSInvalidCastException">If the conversion failed.</exception>
2016-03-30 14:20:52 -07:00
internal static object ConvertTo ( object valueToConvert ,
Type resultType ,
bool recursion ,
IFormatProvider formatProvider ,
TypeTable backupTypeTable )
{
using ( typeConversion . TraceScope ( "Converting \"{0}\" to \"{1}\"." , valueToConvert , resultType ))
{
if ( resultType == null )
{
throw PSTraceSource . NewArgumentNullException ( "resultType" );
}
bool debase ;
var conversion = FigureConversion ( valueToConvert , resultType , out debase );
2018-08-26 12:50:58 -07:00
return conversion . Invoke (
debase ? PSObject . Base ( valueToConvert ) : valueToConvert ,
resultType ,
recursion ,
debase ? ( PSObject ) valueToConvert : null ,
formatProvider ,
backupTypeTable );
2016-03-30 14:20:52 -07:00
}
}
/// <summary>
2019-01-06 04:40:24 +05:00
/// Get the errorId and errorMessage for an InvalidCastException.
2016-03-30 14:20:52 -07:00
/// </summary>
/// <param name="valueToConvert"></param>
/// <param name="resultType"></param>
/// <returns>
/// A two-element tuple indicating [errorId, errorMsg]
/// </returns>
internal static Tuple < string , string > GetInvalidCastMessages ( object valueToConvert , Type resultType )
{
string errorId , errorMsg ;
2018-08-26 12:50:58 -07:00
if ( resultType . IsByRefLike )
{
typeConversion . WriteLine ( "Cannot convert to ByRef-Like types as they should be used on stack only." );
errorId = nameof ( ExtendedTypeSystem . InvalidCastToByRefLikeType );
errorMsg = StringUtil . Format ( ExtendedTypeSystem . InvalidCastToByRefLikeType , resultType );
return Tuple . Create ( errorId , errorMsg );
}
2016-03-30 14:20:52 -07:00
if ( PSObject . Base ( valueToConvert ) == null )
{
2018-04-13 15:01:41 -07:00
if ( resultType . IsEnum )
2016-03-30 14:20:52 -07:00
{
typeConversion . WriteLine ( "Issuing an error message about not being able to convert null to an Enum type." );
// a nice error message specifically for null being converted to enum
errorId = "nullToEnumInvalidCast" ;
errorMsg = StringUtil . Format ( ExtendedTypeSystem . InvalidCastExceptionEnumerationNull , resultType ,
EnumSingleTypeConverter . EnumValues ( resultType ));
return Tuple . Create ( errorId , errorMsg );
}
typeConversion . WriteLine ( "Cannot convert null." );
// finally throw of all other value types...
errorId = "nullToObjectInvalidCast" ;
errorMsg = StringUtil . Format ( ExtendedTypeSystem . InvalidCastFromNull , resultType . ToString ());
return Tuple . Create ( errorId , errorMsg );
}
typeConversion . WriteLine ( "Type Conversion failed." );
errorId = "ConvertToFinalInvalidCastException" ;
errorMsg = StringUtil . Format ( ExtendedTypeSystem . InvalidCastException , valueToConvert . ToString (),
ObjectToTypeNameString ( valueToConvert ), resultType . ToString ());
return Tuple . Create ( errorId , errorMsg );
}
// Even though this never returns, expression trees expect non-void values in places, and so it's easier
// to claim it returns object than to add extra expressions to keep the trees type safe.
internal static object ThrowInvalidCastException ( object valueToConvert , Type resultType )
{
// Get exception messages (in order): errorId, errorMsg
var errorMsgTuple = GetInvalidCastMessages ( valueToConvert , resultType );
throw new PSInvalidCastException ( errorMsgTuple . Item1 , errorMsgTuple . Item2 , innerException : null );
}
// Even though this never returns, expression trees expect non-void values in places, and so it's easier
// to claim it returns object than to add extra expressions to keep the trees type safe.
internal static object ThrowInvalidConversionException ( object valueToConvert , Type resultType )
{
typeConversion . WriteLine ( "Issuing an error message about not being able to convert to non-core type." );
throw new PSInvalidCastException ( "ConversionSupportedOnlyToCoreTypes" , null , ExtendedTypeSystem . InvalidCastExceptionNonCoreType , resultType . ToString ());
}
2018-12-20 23:23:04 -08:00
private static IConversionData FigureLanguageConversion ( Type fromType , Type toType ,
2016-03-30 14:20:52 -07:00
out PSConverter < object > valueDependentConversion ,
out ConversionRank valueDependentRank )
{
valueDependentConversion = null ;
valueDependentRank = ConversionRank . None ;
Type underlyingType = Nullable . GetUnderlyingType ( toType );
if ( underlyingType != null )
{
2018-12-20 23:23:04 -08:00
IConversionData nullableConversion = FigureConversion ( fromType , underlyingType );
2016-03-30 14:20:52 -07:00
if ( nullableConversion . Rank != ConversionRank . None )
{
return CacheConversion < object >( fromType , toType , LanguagePrimitives . ConvertToNullable , nullableConversion . Rank );
}
}
if ( toType == typeof ( void ))
{
return CacheConversion < object >( fromType , toType , LanguagePrimitives . ConvertToVoid , ConversionRank . Language );
}
if ( toType == typeof ( bool ))
{
PSConverter < bool > converter ;
if ( typeof ( IList ). IsAssignableFrom ( fromType ))
{
converter = LanguagePrimitives . ConvertIListToBool ;
}
2018-04-13 15:01:41 -07:00
else if ( fromType . IsEnum )
2016-03-30 14:20:52 -07:00
{
converter = LanguagePrimitives . CreateNumericToBoolConverter ( fromType );
}
2018-04-13 15:01:41 -07:00
else if ( fromType . IsValueType )
2016-03-30 14:20:52 -07:00
{
converter = LanguagePrimitives . ConvertValueToBool ;
}
else
{
converter = LanguagePrimitives . ConvertClassToBool ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return CacheConversion < bool >( fromType , toType , converter , ConversionRank . Language );
}
if ( toType == typeof ( string ))
{
2018-04-13 15:01:41 -07:00
Dbg . Assert (! LanguagePrimitives . IsNumeric ( LanguagePrimitives . GetTypeCode ( fromType )) || fromType . IsEnum ,
2016-03-30 14:20:52 -07:00
"Number to string should be cached on initialization of cache table" );
return CacheConversion < string >( fromType , toType , LanguagePrimitives . ConvertNonNumericToString , ConversionRank . ToString );
}
if ( toType . IsArray )
{
Type toElementType = toType . GetElementType ();
if ( fromType . IsArray )
{
if ( toElementType . IsAssignableFrom ( fromType . GetElementType ()))
{
return CacheConversion < object >( fromType , toType , LanguagePrimitives . ConvertRelatedArrays , ConversionRank . Language );
}
return CacheConversion < object >( fromType , toType , LanguagePrimitives . ConvertUnrelatedArrays , ConversionRank . UnrelatedArrays );
}
if ( LanguagePrimitives . IsTypeEnumerable ( fromType ))
{
return CacheConversion < object >( fromType , toType , LanguagePrimitives . ConvertEnumerableToArray , ConversionRank . Language );
}
2018-12-20 23:23:04 -08:00
IConversionData data = FigureConversion ( fromType , toElementType );
2016-03-30 14:20:52 -07:00
if ( data . Rank != ConversionRank . None )
{
valueDependentRank = data . Rank & ConversionRank . ValueDependent ;
valueDependentConversion = LanguagePrimitives . ConvertScalarToArray ;
return null ;
}
}
if ( toType == typeof ( Array ))
{
if ( fromType . IsArray || fromType == typeof ( Array ))
{
return CacheConversion < object >( fromType , toType , LanguagePrimitives . ConvertAssignableFrom , ConversionRank . Assignable );
}
if ( LanguagePrimitives . IsTypeEnumerable ( fromType ))
{
return CacheConversion < object >( fromType , toType , LanguagePrimitives . ConvertEnumerableToArray , ConversionRank . Language );
}
valueDependentRank = ConversionRank . Assignable & ConversionRank . ValueDependent ;
valueDependentConversion = LanguagePrimitives . ConvertScalarToArray ;
return null ;
}
if ( toType == typeof ( Hashtable ))
{
if ( typeof ( IDictionary ). IsAssignableFrom ( fromType ))
{
return CacheConversion < Hashtable >( fromType , toType , LanguagePrimitives . ConvertIDictionaryToHashtable , ConversionRank . Language );
}
else
{
return null ;
}
}
if ( toType == typeof ( PSReference ))
{
return CacheConversion < PSReference >( fromType , toType , LanguagePrimitives . ConvertToPSReference , ConversionRank . Language );
}
if ( toType == typeof ( XmlDocument ))
{
return CacheConversion < XmlDocument >( fromType , toType , LanguagePrimitives . ConvertToXml , ConversionRank . Language );
}
if ( toType == typeof ( StringCollection ))
{
ConversionRank rank = ( fromType . IsArray || IsTypeEnumerable ( fromType )) ? ConversionRank . Language : ConversionRank . LanguageS2A ;
return CacheConversion < StringCollection >( fromType , toType , LanguagePrimitives . ConvertToStringCollection , rank );
}
if ( toType . IsSubclassOf ( typeof ( System . Delegate ))
&& ( fromType == typeof ( ScriptBlock ) || fromType . IsSubclassOf ( typeof ( ScriptBlock ))))
{
return CacheConversion < Delegate >( fromType , toType , LanguagePrimitives . ConvertScriptBlockToDelegate , ConversionRank . Language );
}
if ( toType == typeof ( InternalPSCustomObject ))
{
Type actualResultType = typeof ( PSObject );
2019-03-10 06:53:31 +05:00
ConstructorInfo resultConstructor = actualResultType . GetConstructor ( Type . EmptyTypes );
2016-03-30 14:20:52 -07:00
var converterObj = new ConvertViaNoArgumentConstructor ( resultConstructor , actualResultType );
return CacheConversion ( fromType , toType , converterObj . Convert , ConversionRank . Language );
}
TypeCode fromTypeCode = LanguagePrimitives . GetTypeCode ( fromType );
2018-04-13 15:01:41 -07:00
if ( LanguagePrimitives . IsInteger ( fromTypeCode ) && toType . IsEnum )
2016-03-30 14:20:52 -07:00
{
return CacheConversion < object >( fromType , toType , LanguagePrimitives . ConvertIntegerToEnum , ConversionRank . Language );
}
2018-05-15 11:45:04 -07:00
if ( fromType . IsSubclassOf ( typeof ( PSMethod )) && toType . IsSubclassOf ( typeof ( Delegate )) && ! toType . IsAbstract )
2017-12-02 03:40:35 +01:00
{
2018-05-15 11:45:04 -07:00
var targetMethod = toType . GetMethod ( "Invoke" );
var comparator = new SignatureComparator ( targetMethod );
2017-12-02 03:40:35 +01:00
var signatureEnumerator = new PSMethodSignatureEnumerator ( fromType );
2018-05-15 11:45:04 -07:00
int index = - 1 , matchedIndex = - 1 ;
2017-12-02 03:40:35 +01:00
while ( signatureEnumerator . MoveNext ())
{
2018-05-15 11:45:04 -07:00
index ++;
var signatureType = signatureEnumerator . Current ;
// Skip the non-bindable signatures
if ( signatureType == typeof ( Func < PSNonBindableType >)) { continue ; }
Type [] argumentTypes = signatureType . GenericTypeArguments ;
if ( comparator . ProjectedSignatureMatchesTarget ( argumentTypes , out bool signaturesMatchExactly ))
2017-12-02 03:40:35 +01:00
{
2018-05-15 11:45:04 -07:00
if ( signaturesMatchExactly )
{
// We prefer the signature that exactly matches the target delegate.
matchedIndex = index ;
break ;
}
// If there is no exact match, then we use the first compatible signature we found.
if ( matchedIndex == - 1 ) { matchedIndex = index ; }
2017-12-02 03:40:35 +01:00
}
}
2018-05-15 11:45:04 -07:00
if ( matchedIndex > - 1 )
{
// We got the index of the matching method signature based on the PSMethod<..> type.
// Signatures in PSMethod<..> type were constructed based on the array of method overloads,
// in the exact order. So we can use this index directly to locate the matching overload in
// the converter, without having to compare the signature again.
var converter = PSMethodToDelegateConverter . GetConverter ( matchedIndex );
return CacheConversion < Delegate >( fromType , toType , converter . Convert , ConversionRank . Language );
}
2017-12-02 03:40:35 +01:00
}
2016-03-30 14:20:52 -07:00
return null ;
}
2018-05-15 11:45:04 -07:00
private struct SignatureComparator
2017-12-02 03:40:35 +01:00
{
2018-05-15 11:45:04 -07:00
enum TypeMatchingContext
2017-12-02 03:40:35 +01:00
{
2018-05-15 11:45:04 -07:00
ReturnType ,
ParameterType ,
OutParameterType
2017-12-02 03:40:35 +01:00
}
2018-05-15 11:45:04 -07:00
private readonly ParameterInfo [] targetParameters ;
private readonly Type targetReturnType ;
internal SignatureComparator ( MethodInfo targetMethodInfo )
2017-12-02 03:40:35 +01:00
{
2018-05-15 11:45:04 -07:00
targetReturnType = targetMethodInfo . ReturnType ;
targetParameters = targetMethodInfo . GetParameters ();
2017-12-02 03:40:35 +01:00
}
2018-05-15 11:45:04 -07:00
/// <summary>
/// Check if a projected signature matches the target method.
/// </summary>
/// <param name="argumentTypes">
/// The type arguments from the metadata type 'Func[..]' that represents the projected signature.
/// It contains the return type as the last item in the array.
/// </param>
/// <param name="signaturesMatchExactly">
/// Set by this method to indicate if it's an exact match.
/// </param>
internal bool ProjectedSignatureMatchesTarget ( Type [] argumentTypes , out bool signaturesMatchExactly )
2017-12-02 03:40:35 +01:00
{
2018-05-15 11:45:04 -07:00
signaturesMatchExactly = false ;
int length = argumentTypes . Length ;
if ( length != targetParameters . Length + 1 ) { return false ; }
bool typesMatchExactly , allTypesMatchExactly ;
Type sourceReturnType = argumentTypes [ length - 1 ];
if ( ProjectedTypeMatchesTargetType ( sourceReturnType , targetReturnType , TypeMatchingContext . ReturnType , out typesMatchExactly ))
{
allTypesMatchExactly = typesMatchExactly ;
for ( int i = 0 ; i < targetParameters . Length ; i ++)
{
var targetParam = targetParameters [ i ];
var sourceType = argumentTypes [ i ];
var matchContext = targetParam . IsOut ? TypeMatchingContext . OutParameterType : TypeMatchingContext . ParameterType ;
if (! ProjectedTypeMatchesTargetType ( sourceType , targetParam . ParameterType , matchContext , out typesMatchExactly ))
{
return false ;
}
2018-12-23 22:20:06 -08:00
2018-05-15 11:45:04 -07:00
allTypesMatchExactly &= typesMatchExactly ;
}
signaturesMatchExactly = allTypesMatchExactly ;
return true ;
}
return false ;
2017-12-02 03:40:35 +01:00
}
2018-05-15 11:45:04 -07:00
private static bool ProjectedTypeMatchesTargetType ( Type sourceType , Type targetType , TypeMatchingContext matchContext , out bool matchExactly )
2017-12-02 03:40:35 +01:00
{
2018-05-15 11:45:04 -07:00
matchExactly = false ;
if ( targetType . IsByRef || targetType . IsPointer )
{
if (! sourceType . IsGenericType ) { return false ; }
var sourceTypeDef = sourceType . GetGenericTypeDefinition ();
bool isOutParameter = matchContext == TypeMatchingContext . OutParameterType ;
if ( targetType . IsByRef && sourceTypeDef == ( isOutParameter ? typeof ( PSOutParameter <>) : typeof ( PSReference <>)) ||
targetType . IsPointer && sourceTypeDef == typeof ( PSPointer <>))
{
// For ref/out parameter types and pointer types, the element types need to match exactly.
if ( targetType . GetElementType () == sourceType . GenericTypeArguments [ 0 ])
{
matchExactly = true ;
return true ;
}
}
2018-12-23 22:20:06 -08:00
2018-05-15 11:45:04 -07:00
return false ;
}
if ( targetType == sourceType ||
targetType == typeof ( void ) && sourceType == typeof ( VOID ) ||
targetType == typeof ( TypedReference ) && sourceType == typeof ( PSTypedReference ))
{
matchExactly = true ;
return true ;
}
if ( targetType == typeof ( void ) || targetType == typeof ( TypedReference ))
2017-12-02 03:40:35 +01:00
{
return false ;
}
2018-05-15 11:45:04 -07:00
return matchContext == TypeMatchingContext . ReturnType
? targetType . IsAssignableFrom ( sourceType )
: sourceType . IsAssignableFrom ( targetType );
2017-12-02 03:40:35 +01:00
}
}
2016-03-30 14:20:52 -07:00
private static PSConverter < object > FigureStaticCreateMethodConversion ( Type fromType , Type toType )
{
// after discussing this with Jason, we decided that for now we only want to support string->CimSession conversion
// and we don't want to add a Parse-like conversion based on a static Create method
if ( fromType == typeof ( string ) && toType == typeof ( Microsoft . Management . Infrastructure . CimSession ))
{
return LanguagePrimitives . ConvertStringToCimSession ;
}
return null ;
}
private static PSConverter < object > FigureParseConversion ( Type fromType , Type toType )
{
2018-04-13 15:01:41 -07:00
if ( toType . IsEnum )
2016-03-30 14:20:52 -07:00
{
if ( fromType == typeof ( string ))
{
return LanguagePrimitives . ConvertStringToEnum ;
}
if ( LanguagePrimitives . IsTypeEnumerable ( fromType ))
{
return LanguagePrimitives . ConvertEnumerableToEnum ;
}
}
else if ( fromType == typeof ( string ))
{
const BindingFlags parseFlags = BindingFlags . FlattenHierarchy | BindingFlags . Public | BindingFlags . Static ;
// GetMethod could throw for more than one match, for instance
MethodInfo parse = null ;
try
{
parse = toType . GetMethod ( "Parse" , parseFlags , null , new Type [ 2 ] { typeof ( string ), typeof ( IFormatProvider ) }, null );
}
catch ( AmbiguousMatchException e )
{
typeConversion . WriteLine ( "Exception finding Parse method with CultureInfo: \"{0}\"." , e . Message );
}
catch ( ArgumentException e )
{
typeConversion . WriteLine ( "Exception finding Parse method with CultureInfo: \"{0}\"." , e . Message );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
if ( parse != null )
{
ConvertViaParseMethod converter = new ConvertViaParseMethod ();
converter . parse = parse ;
return converter . ConvertWithCulture ;
}
try
{
parse = toType . GetMethod ( "Parse" , parseFlags , null , new Type [ 1 ] { typeof ( string ) }, null );
}
catch ( AmbiguousMatchException e )
{
typeConversion . WriteLine ( "Exception finding Parse method: \"{0}\"." , e . Message );
}
catch ( ArgumentException e )
{
typeConversion . WriteLine ( "Exception finding Parse method: \"{0}\"." , e . Message );
}
if ( parse != null )
{
ConvertViaParseMethod converter = new ConvertViaParseMethod ();
converter . parse = parse ;
return converter . ConvertWithoutCulture ;
}
}
return null ;
}
/// <summary>
/// Figure conversion when following conditions are satisfied:
/// 1. toType is a closed generic type and it has a constructor that takes IEnumerable[T], ICollection[T] or IList[T]
2019-01-06 04:40:24 +05:00
/// 2. fromType is System.Array, System.Object[] or it's the same as the element type of toType.
2016-03-30 14:20:52 -07:00
/// </summary>
/// <param name="fromType"></param>
/// <param name="toType"></param>
/// <returns></returns>
internal static Tuple < PSConverter < object >, ConversionRank > FigureIEnumerableConstructorConversion ( Type fromType , Type toType )
{
// Win8: 653180. If toType is an Abstract type then we cannot construct it anyway. So, bailing out fast.
2018-04-13 15:01:41 -07:00
if ( toType . IsAbstract == true )
2016-03-30 14:20:52 -07:00
{
return null ;
}
try
{
bool result = false ;
bool isScalar = false ;
Type elementType = null ;
ConstructorInfo resultConstructor = null ;
2018-04-13 15:01:41 -07:00
if ( toType . IsGenericType && ! toType . ContainsGenericParameters &&
2016-07-29 13:02:49 -07:00
( typeof ( IList ). IsAssignableFrom ( toType ) ||
typeof ( ICollection ). IsAssignableFrom ( toType ) ||
typeof ( IEnumerable ). IsAssignableFrom ( toType )))
2016-03-30 14:20:52 -07:00
{
Type [] argTypes = toType . GetGenericArguments ();
if ( argTypes . Length != 1 )
{
typeConversion
. WriteLine (
"toType has more than one generic arguments. Here we only care about the toType which contains only one generic argument and whose constructor takes IEnumerable<T>, ICollection<T> or IList<T>." );
return null ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
elementType = argTypes [ 0 ];
2016-07-29 13:02:49 -07:00
if ( typeof ( Array ) == fromType || typeof ( object []) == fromType ||
2016-03-30 14:20:52 -07:00
elementType . IsAssignableFrom ( fromType ) ||
// WinBlue: 423899 : To support scenario like [list[int]]"4"
( FigureConversion ( fromType , elementType ) != null ))
{
isScalar = elementType . IsAssignableFrom ( fromType );
ConstructorInfo [] ctors = toType . GetConstructors ();
2016-07-29 13:02:49 -07:00
Type iEnumerableClosedType = typeof ( IEnumerable <>). MakeGenericType ( elementType );
Type iCollectionClosedType = typeof ( ICollection <>). MakeGenericType ( elementType );
Type iListClosedType = typeof ( IList <>). MakeGenericType ( elementType );
2016-03-30 14:20:52 -07:00
foreach ( var ctor in ctors )
{
ParameterInfo [] param = ctor . GetParameters ();
if ( param . Length != 1 )
continue ;
Type paramType = param [ 0 ]. ParameterType ;
if ( iEnumerableClosedType == paramType ||
iCollectionClosedType == paramType ||
iListClosedType == paramType )
{
resultConstructor = ctor ;
result = true ;
break ;
}
}
}
}
if ( result )
{
var converter = new ConvertViaIEnumerableConstructor ();
try
{
2016-07-29 13:02:49 -07:00
Type listClosedType = typeof ( List <>). MakeGenericType ( elementType );
ConstructorInfo listCtor = listClosedType . GetConstructor ( new Type [] { typeof ( int ) });
converter . ListCtorLambda = CreateCtorLambdaClosure < int , IList >( listCtor , typeof ( int ), false );
2016-03-30 14:20:52 -07:00
ParameterInfo [] targetParams = resultConstructor . GetParameters ();
Type targetParamType = targetParams [ 0 ]. ParameterType ;
converter . TargetCtorLambda = CreateCtorLambdaClosure < IList , object >( resultConstructor ,
targetParamType , false );
converter . ElementType = elementType ;
converter . IsScalar = isScalar ;
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Exception building constructor lambda: \"{0}\"" , e . Message );
return null ;
}
ConversionRank rank = isScalar ? ConversionRank . ConstructorS2A : ConversionRank . Constructor ;
typeConversion . WriteLine ( "Conversion is figured out. Conversion rank: \"{0}\"" , rank );
return new Tuple < PSConverter < object >, ConversionRank >( converter . Convert , rank );
}
else
{
typeConversion . WriteLine ( "Fail to figure out the conversion from \"{0}\" to \"{1}\"" ,
fromType . FullName , toType . FullName );
return null ;
}
}
2016-07-29 13:02:49 -07:00
catch ( ArgumentException ae )
2016-03-30 14:20:52 -07:00
{
typeConversion . WriteLine ( "Exception finding IEnumerable conversion: \"{0}\"." , ae . Message );
}
2016-07-29 13:02:49 -07:00
catch ( InvalidOperationException ie )
2016-03-30 14:20:52 -07:00
{
typeConversion . WriteLine ( "Exception finding IEnumerable conversion: \"{0}\"." , ie . Message );
}
catch ( NotSupportedException ne )
{
typeConversion . WriteLine ( "Exception finding IEnumerable conversion: \"{0}\"." , ne . Message );
}
return null ;
}
private static Func < T1 , T2 > CreateCtorLambdaClosure < T1 , T2 >( ConstructorInfo ctor , Type realParamType , bool useExplicitConversion )
{
2016-07-29 13:02:49 -07:00
ParameterExpression paramExpr = Expression . Parameter ( typeof ( T1 ), "args" );
2016-03-30 14:20:52 -07:00
Expression castParamExpr = useExplicitConversion
? ( Expression ) Expression . Call ( CachedReflectionInfo . Convert_ChangeType , paramExpr , Expression . Constant ( realParamType , typeof ( Type )))
: Expression . Convert ( paramExpr , realParamType );
NewExpression ctorExpr = Expression . New ( ctor , castParamExpr . Cast ( realParamType ));
2016-07-29 13:02:49 -07:00
return Expression . Lambda < Func < T1 , T2 >>( ctorExpr . Cast ( typeof ( T2 )), paramExpr ). Compile ();
2016-03-30 14:20:52 -07:00
}
internal static PSConverter < object > FigureConstructorConversion ( Type fromType , Type toType )
{
if ( IsIntegralType ( fromType ) &&
( typeof ( IList ). IsAssignableFrom ( toType ) || typeof ( ICollection ). IsAssignableFrom ( toType )))
{
typeConversion . WriteLine ( "Ignoring the collection constructor that takes an integer, since this is not semantically a conversion." );
return null ;
}
ConstructorInfo resultConstructor = null ;
try
{
resultConstructor = toType . GetConstructor ( new Type [] { fromType });
}
catch ( AmbiguousMatchException e )
{
typeConversion . WriteLine ( "Exception finding Constructor: \"{0}\"." , e . Message );
}
catch ( ArgumentException e )
{
typeConversion . WriteLine ( "Exception finding Constructor: \"{0}\"." , e . Message );
}
if ( resultConstructor == null )
{
return null ;
}
typeConversion . WriteLine ( "Found Constructor." );
var converter = new ConvertViaConstructor ();
try
{
ParameterInfo [] targetParams = resultConstructor . GetParameters ();
Type targetParamType = targetParams [ 0 ]. ParameterType ;
2018-04-13 15:01:41 -07:00
bool useExplicitConversion = targetParamType . IsValueType && fromType != targetParamType && Nullable . GetUnderlyingType ( targetParamType ) == null ;
2016-03-30 14:20:52 -07:00
converter . TargetCtorLambda = CreateCtorLambdaClosure < object , object >( resultConstructor , targetParamType , useExplicitConversion );
}
catch ( Exception e )
{
typeConversion . WriteLine ( "Exception building constructor lambda: \"{0}\"" , e . Message );
return null ;
}
typeConversion . WriteLine ( "Conversion is figured out." );
return converter . Convert ;
}
private static bool IsIntegralType ( Type type )
{
return
type == typeof ( sbyte ) ||
type == typeof ( byte ) ||
type == typeof ( short ) ||
type == typeof ( ushort ) ||
type == typeof ( int ) ||
type == typeof ( uint ) ||
type == typeof ( long ) ||
type == typeof ( ulong );
}
internal static PSConverter < object > FigurePropertyConversion ( Type fromType , Type toType , ref ConversionRank rank )
{
2018-04-13 15:01:41 -07:00
if ((! typeof ( PSObject ). IsAssignableFrom ( fromType )) || ( toType . IsAbstract ))
2016-03-30 14:20:52 -07:00
{
return null ;
}
ConstructorInfo toConstructor = null ;
try
{
2019-03-10 06:53:31 +05:00
toConstructor = toType . GetConstructor ( Type . EmptyTypes );
2016-03-30 14:20:52 -07:00
}
catch ( AmbiguousMatchException e )
{
typeConversion . WriteLine ( "Exception finding Constructor: \"{0}\"." , e . Message );
}
catch ( ArgumentException e )
{
typeConversion . WriteLine ( "Exception finding Constructor: \"{0}\"." , e . Message );
}
2018-04-13 15:01:41 -07:00
if ( toConstructor == null && ! toType . IsValueType )
2016-03-30 14:20:52 -07:00
{
return null ;
}
2016-07-29 13:02:49 -07:00
if ( toType . GetProperties ( BindingFlags . Public | BindingFlags . Instance ). Length == 0 &&
toType . GetFields ( BindingFlags . Public | BindingFlags . Instance ). Length == 0 )
{
// fromType is PSObject, toType has no properties/fields to set, so conversion should fail.
return null ;
}
2016-03-30 14:20:52 -07:00
typeConversion . WriteLine ( "Found Constructor." );
try
{
ConvertViaNoArgumentConstructor noArgumentConstructorConverter =
new ConvertViaNoArgumentConstructor ( toConstructor , toType );
rank = ConversionRank . Constructor ;
return noArgumentConstructorConverter . Convert ;
}
catch ( ArgumentException ae )
{
typeConversion . WriteLine ( "Exception converting via no argument constructor: \"{0}\"." , ae . Message );
}
catch ( InvalidOperationException ie )
{
typeConversion . WriteLine ( "Exception converting via no argument constructor: \"{0}\"." , ie . Message );
}
rank = ConversionRank . None ;
return null ;
}
internal static PSConverter < object > FigureCastConversion ( Type fromType , Type toType , ref ConversionRank rank )
{
MethodInfo castOperator = FindCastOperator ( "op_Implicit" , toType , fromType , toType );
if ( castOperator == null )
{
castOperator = FindCastOperator ( "op_Explicit" , toType , fromType , toType );
if ( castOperator == null )
{
2016-08-02 22:25:13 -07:00
castOperator = FindCastOperator ( "op_Implicit" , fromType , fromType , toType ) ??
FindCastOperator ( "op_Explicit" , fromType , fromType , toType );
2016-03-30 14:20:52 -07:00
}
}
if ( castOperator != null )
{
rank = castOperator . Name . Equals ( "op_Implicit" , StringComparison . OrdinalIgnoreCase )
? ConversionRank . ImplicitCast : ConversionRank . ExplicitCast ;
ConvertViaCast converter = new ConvertViaCast ();
converter . cast = castOperator ;
return converter . Convert ;
}
return null ;
}
private static bool TypeConverterPossiblyExists ( Type type )
{
2016-07-29 13:02:49 -07:00
lock ( s_possibleTypeConverter )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
if ( s_possibleTypeConverter . ContainsKey ( type . FullName ))
2016-03-30 14:20:52 -07:00
{
return true ;
}
}
// GetCustomAttributes returns IEnumerable<Attribute> in CoreCLR
2018-04-13 15:01:41 -07:00
var typeConverters = type . GetCustomAttributes ( typeof ( TypeConverterAttribute ), false );
2016-03-30 14:20:52 -07:00
if ( typeConverters . Any ())
{
return true ;
}
return false ;
}
2016-07-29 13:02:49 -07:00
private static Dictionary < string , bool > s_possibleTypeConverter = new Dictionary < string , bool >( 16 );
2016-03-30 14:20:52 -07:00
// This is the internal dummy type used when an IDictionary is converted to a pscustomobject
// PS C:\> $ps = [pscustomobject]@{a=10;b=5}
// PS C:\> $ps = [pscustomobject][ordered]@{a=10;b=5}
// Whenever we see a conversion to PSCustomObject, we represent it as a conversion to InternalPSCustomObject
2017-01-16 13:31:14 -08:00
// This is introduced to avoid breaking PSObject behavior.
2016-03-30 14:20:52 -07:00
// (Because PSCustomObject is a typeaccelerator for PSObject, we needed a separate type to represent type conversions to PSCustomObject)
internal class InternalPSCustomObject
{
}
internal class InternalPSObject : PSObject { }
2018-12-23 22:20:06 -08:00
2018-12-20 23:23:04 -08:00
internal static IConversionData FigureConversion ( Type fromType , Type toType )
2016-03-30 14:20:52 -07:00
{
2018-12-20 23:23:04 -08:00
IConversionData data = GetConversionData ( fromType , toType );
2016-03-30 14:20:52 -07:00
if ( data != null )
{
return data ;
}
if ( fromType == typeof ( Null ))
{
return FigureConversionFromNull ( toType );
}
if ( toType . IsAssignableFrom ( fromType ))
{
return CacheConversion < object >( fromType , toType , LanguagePrimitives . ConvertAssignableFrom ,
toType == fromType ? ConversionRank . Identity : ConversionRank . Assignable );
}
2018-08-26 12:50:58 -07:00
if ( fromType . IsByRefLike || toType . IsByRefLike )
{
// ByRef-like types are not boxable and should be used on stack only.
return CacheConversion ( fromType , toType , ConvertNoConversion , ConversionRank . None );
}
2016-03-30 14:20:52 -07:00
if ( typeof ( PSObject ). IsAssignableFrom ( fromType ) && typeof ( InternalPSObject ) != fromType )
{
// We don't attempt converting PSObject (or derived) to anything else,
// instead we go straight to PSObject.Base (which is only a PSObject
// when no object is wrapped, in which case we try conversions from object)
// and convert that instead.
return CacheConversion ( fromType , toType , ConvertNoConversion , ConversionRank . None );
}
if ( toType == typeof ( PSObject ))
{
return CacheConversion < PSObject >( fromType , toType , LanguagePrimitives . ConvertToPSObject , ConversionRank . PSObject );
}
PSConverter < object > converter = null ;
ConversionRank rank = ConversionRank . None ;
// If we've ever used ConstrainedLanguage, check if the target type is allowed
if ( ExecutionContext . HasEverUsedConstrainedLanguage )
{
var context = LocalPipeline . GetExecutionContextFromTLS ();
if (( context != null ) && ( context . LanguageMode == PSLanguageMode . ConstrainedLanguage ))
{
2019-11-06 08:47:32 +05:00
if (( toType != typeof ( object )) &&
( toType != typeof ( object [])) &&
2016-03-30 14:20:52 -07:00
(! CoreTypes . Contains ( toType )))
{
converter = ConvertNotSupportedConversion ;
rank = ConversionRank . None ;
return CacheConversion ( fromType , toType , converter , rank );
}
}
}
2018-04-13 15:01:41 -07:00
2016-03-30 14:20:52 -07:00
// Assemblies in CoreCLR might not allow reflection execution on their internal types.
2018-04-13 15:01:41 -07:00
if (! TypeResolver . IsPublic ( toType ) && DotNetAdapter . DisallowPrivateReflection ( toType ))
2016-03-30 14:20:52 -07:00
{
// If the type is non-public and reflection execution is not allowed on it, then we return
2017-01-16 13:31:14 -08:00
// 'ConvertNoConversion', because we won't be able to invoke constructor, methods or set
2016-03-30 14:20:52 -07:00
// properties on an instance of this type through reflection.
return CacheConversion ( fromType , toType , ConvertNoConversion , ConversionRank . None );
}
2018-04-13 15:01:41 -07:00
2016-03-30 14:20:52 -07:00
PSConverter < object > valueDependentConversion = null ;
ConversionRank valueDependentRank = ConversionRank . None ;
2018-12-20 23:23:04 -08:00
IConversionData conversionData = FigureLanguageConversion ( fromType , toType , out valueDependentConversion , out valueDependentRank );
2016-03-30 14:20:52 -07:00
if ( conversionData != null )
{
return conversionData ;
}
rank = valueDependentConversion != null ? ConversionRank . Language : ConversionRank . None ;
converter = FigureParseConversion ( fromType , toType );
if ( converter == null )
{
converter = FigureStaticCreateMethodConversion ( fromType , toType );
if ( converter == null )
{
converter = FigureConstructorConversion ( fromType , toType );
rank = ConversionRank . Constructor ;
if ( converter == null )
{
converter = FigureCastConversion ( fromType , toType , ref rank );
if ( converter == null )
{
if ( typeof ( IConvertible ). IsAssignableFrom ( fromType ))
{
2018-04-13 15:01:41 -07:00
if ( LanguagePrimitives . IsNumeric ( GetTypeCode ( fromType )) && ! fromType . IsEnum )
2016-03-30 14:20:52 -07:00
{
if (! toType . IsArray )
{
if ( GetConversionRank ( typeof ( string ), toType ) != ConversionRank . None )
{
converter = LanguagePrimitives . ConvertNumericIConvertible ;
rank = ConversionRank . IConvertible ;
}
}
}
else if ( fromType != typeof ( string ))
{
converter = LanguagePrimitives . ConvertIConvertible ;
rank = ConversionRank . IConvertible ;
}
}
else if ( typeof ( IDictionary ). IsAssignableFrom ( fromType ))
{
// We need to call the null argument constructor only if the following 2 conditions satisfy
// 1) if the fromType is either a hashtable or OrderedDictionary
// 2) if the ToType does not already have a constructor that takes a hashtable or OrderedDictionary. (This is to avoid breaking existing apps.)
2017-01-16 13:31:14 -08:00
// If the ToType has a constructor that takes a hashtable or OrderedDictionary,
2016-03-30 14:20:52 -07:00
// then it would have been returned as the constructor during FigureConstructorConversion
// So, we need to check only for the first condition
2019-03-10 06:53:31 +05:00
ConstructorInfo resultConstructor = toType . GetConstructor ( Type . EmptyTypes );
2016-03-30 14:20:52 -07:00
2018-04-13 15:01:41 -07:00
if ( resultConstructor != null || ( toType . IsValueType && ! toType . IsPrimitive ))
2016-03-30 14:20:52 -07:00
{
ConvertViaNoArgumentConstructor noArgumentConstructorConverter = new ConvertViaNoArgumentConstructor ( resultConstructor , toType );
converter = noArgumentConstructorConverter . Convert ;
rank = ConversionRank . Constructor ;
}
}
}
}
else
{
rank = ConversionRank . Constructor ;
}
}
else
{
rank = ConversionRank . Create ;
}
}
else
{
rank = ConversionRank . Parse ;
}
if ( converter == null )
{
var tuple = FigureIEnumerableConstructorConversion ( fromType , toType );
if ( tuple != null )
{
converter = tuple . Item1 ;
rank = tuple . Item2 ;
}
}
if ( converter == null )
{
converter = FigurePropertyConversion ( fromType , toType , ref rank );
}
if ( TypeConverterPossiblyExists ( fromType ) || TypeConverterPossiblyExists ( toType )
|| ( converter != null && valueDependentConversion != null ))
{
ConvertCheckingForCustomConverter customConverter = new ConvertCheckingForCustomConverter ();
customConverter . tryfirstConverter = valueDependentConversion ;
customConverter . fallbackConverter = converter ;
converter = customConverter . Convert ;
if ( valueDependentRank > rank )
{
rank = valueDependentRank ;
}
else if ( rank == ConversionRank . None )
{
rank = ConversionRank . Custom ;
}
}
else if ( valueDependentConversion != null )
{
converter = valueDependentConversion ;
rank = valueDependentRank ;
}
if ( converter == null )
{
converter = ConvertNoConversion ;
rank = ConversionRank . None ;
}
return CacheConversion ( fromType , toType , converter , rank );
}
internal class Null { };
2018-12-20 23:23:04 -08:00
private static IConversionData FigureConversionFromNull ( Type toType )
2016-03-30 14:20:52 -07:00
{
2018-12-20 23:23:04 -08:00
IConversionData data = GetConversionData ( typeof ( Null ), toType );
2016-03-30 14:20:52 -07:00
if ( data != null )
{
return data ;
}
if ( Nullable . GetUnderlyingType ( toType ) != null )
{
return CacheConversion < object >( typeof ( Null ), toType , LanguagePrimitives . ConvertNullToNullable , ConversionRank . NullToValue );
}
2018-04-13 15:01:41 -07:00
else if (! toType . IsValueType )
2016-03-30 14:20:52 -07:00
{
return CacheConversion < object >( typeof ( Null ), toType , LanguagePrimitives . ConvertNullToRef , ConversionRank . NullToRef );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return CacheConversion ( typeof ( Null ), toType , ConvertNoConversion , ConversionRank . None );
}
internal static string ObjectToTypeNameString ( object o )
{
if ( o == null )
{
return "null" ;
}
PSObject pso = PSObject . AsPSObject ( o );
var typeNames = pso . InternalTypeNames ;
if (( typeNames != null ) && ( typeNames . Count > 0 ))
{
return typeNames [ 0 ];
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return Microsoft . PowerShell . ToStringCodeMethods . Type ( o . GetType ());
}
#if ! CORECLR // AppDomain Not In CoreCLR
2016-07-29 13:02:49 -07:00
private static Assembly AssemblyResolveHelper ( object sender , ResolveEventArgs args )
2016-03-30 14:20:52 -07:00
{
// Dynamic assemblies don't get resolved properly by the CLR, so we
// resolve them ourselves by looking through the loaded assemblies.
foreach ( Assembly assem in ClrFacade . GetAssemblies ())
{
if ( assem . FullName == args . Name )
{
return assem ;
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return null ;
}
#endif
#endregion type converter
}
}
#pragma warning restore 56500