MegaBites-AI/Windows-powershell
0308
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.Collections.Generic;6using System.Collections.Specialized;7using System.ComponentModel;8using System.Data;9using System.Diagnostics.CodeAnalysis;10using System.Globalization;11using System.IO;12using System.Linq.Expressions;13using System.Management.Automation.Internal;14using System.Management.Automation.Language;15using System.Management.Automation.Runspaces;16using System.Management.Automation.Security;17using System.Numerics;18using System.Reflection;19using System.Reflection.Emit;20using System.Runtime.CompilerServices;21using System.Text;22using System.Text.RegularExpressions;23using System.Xml;24using System.Security;25 26using Dbg = System.Management.Automation.Diagnostics;27using MethodCacheEntry = System.Management.Automation.DotNetAdapter.MethodCacheEntry;28#if !UNIX29using System.DirectoryServices;30#endif31 32#pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings33#pragma warning disable 5650034 35namespace System.Management.Automation36{37 #region public type converters38 /// <summary>39 /// Defines a base class implemented when you need to customize the type conversion for a target class.40 /// </summary>41 /// <remarks>42 /// There are two ways of associating the PSTypeConverter with its target class:43 /// - Through the type configuration file.44 /// - By applying a TypeConverterAttribute to the target class.45 ///46 /// Unlike System.ComponentModel.TypeConverter, PSTypeConverter can be applied to a family of types (like all types derived from System.Enum).47 /// PSTypeConverter has two main differences from TypeConverter:48 /// - It can be applied to a family of types and not only the one type as in TypeConverter. In order to do that49 /// ConvertFrom and CanConvertFrom receive destinationType to know to which type specifically we are converting sourceValue.50 /// - ConvertTo and ConvertFrom receive formatProvider and ignoreCase.51 /// Other differences to System.ComponentModel.TypeConverter:52 /// - There is no ITypeDescriptorContext.53 /// - This class is abstract54 /// </remarks>55 public abstract class PSTypeConverter56 {57 private static object GetSourceValueAsObject(PSObject sourceValue)58 {59 if (sourceValue == null)60 {61 return null;62 }63 64 if (sourceValue.BaseObject is PSCustomObject)65 {66 return sourceValue;67 }68 else69 {70 return PSObject.Base(sourceValue);71 }72 }73 74 /// <summary>75 /// Determines if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter.76 /// </summary>77 /// <param name="sourceValue">Value supposedly *not* of the types supported by this converted to be converted to the <paramref name="destinationType"/> parameter.</param>78 /// <param name="destinationType">One of the types supported by this converter to which the <paramref name="sourceValue"/> parameter should be converted.</param>79 /// <returns>True if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter, otherwise false.</returns>80 public abstract bool CanConvertFrom(object sourceValue, Type destinationType);81 82 /// <summary>83 /// Determines if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter.84 /// </summary>85 /// <param name="sourceValue">Value supposedly *not* of the types supported by this converted to be converted to the <paramref name="destinationType"/> parameter.</param>86 /// <param name="destinationType">One of the types supported by this converter to which the <paramref name="sourceValue"/> parameter should be converted.</param>87 /// <returns>True if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter, otherwise false.</returns>88 public virtual bool CanConvertFrom(PSObject sourceValue, Type destinationType)89 {90 return this.CanConvertFrom(GetSourceValueAsObject(sourceValue), destinationType);91 }92 93 /// <summary>94 /// Converts the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.95 /// </summary>96 /// <param name="sourceValue">Value supposedly *not* of the types supported by this converted to be converted to the <paramref name="destinationType"/> parameter.</param>97 /// <param name="destinationType">One of the types supported by this converter to which the <paramref name="sourceValue"/> parameter should be converted to.</param>98 /// <param name="formatProvider">The format provider to use like in IFormattable's ToString.</param>99 /// <param name="ignoreCase">True if case should be ignored.</param>100 /// <returns>The <paramref name="sourceValue"/> parameter converted to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.</returns>101 /// <exception cref="InvalidCastException">If no conversion was possible.</exception>102 public abstract object ConvertFrom(object sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase);103 104 /// <summary>105 /// Converts the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.106 /// </summary>107 /// <param name="sourceValue">Value supposedly *not* of the types supported by this converted to be converted to the <paramref name="destinationType"/> parameter.</param>108 /// <param name="destinationType">One of the types supported by this converter to which the <paramref name="sourceValue"/> parameter should be converted to.</param>109 /// <param name="formatProvider">The format provider to use like in IFormattable's ToString.</param>110 /// <param name="ignoreCase">True if case should be ignored.</param>111 /// <returns>The <paramref name="sourceValue"/> parameter converted to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.</returns>112 /// <exception cref="InvalidCastException">If no conversion was possible.</exception>113 public virtual object ConvertFrom(PSObject sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase)114 {115 return this.ConvertFrom(GetSourceValueAsObject(sourceValue), destinationType, formatProvider, ignoreCase);116 }117 118 /// <summary>119 /// Returns true if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter.120 /// </summary>121 /// <param name="sourceValue">Value supposedly from one of the types supported by this converter to be converted to the <paramref name="destinationType"/> parameter.</param>122 /// <param name="destinationType">Type to convert the <paramref name="sourceValue"/> parameter, supposedly not one of the types supported by the converter.</param>123 /// <returns>True if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter, otherwise false.</returns>124 public abstract bool CanConvertTo(object sourceValue, Type destinationType);125 126 /// <summary>127 /// Returns true if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter.128 /// </summary>129 /// <param name="sourceValue">Value supposedly from one of the types supported by this converter to be converted to the <paramref name="destinationType"/> parameter.</param>130 /// <param name="destinationType">Type to convert the <paramref name="sourceValue"/> parameter, supposedly not one of the types supported by the converter.</param>131 /// <returns>True if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter, otherwise false.</returns>132 public virtual bool CanConvertTo(PSObject sourceValue, Type destinationType)133 {134 return this.CanConvertTo(GetSourceValueAsObject(sourceValue), destinationType);135 }136 137 /// <summary>138 /// Converts the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.139 /// </summary>140 /// <param name="sourceValue">Value supposedly from one of the types supported by this converter to be converted to the <paramref name="destinationType"/> parameter.</param>141 /// <param name="destinationType">Type to convert the <paramref name="sourceValue"/> parameter, supposedly not one of the types supported by the converter.</param>142 /// <param name="formatProvider">The format provider to use like in IFormattable's ToString.</param>143 /// <param name="ignoreCase">True if case should be ignored.</param>144 /// <returns>SourceValue converted to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.</returns>145 /// <exception cref="InvalidCastException">If no conversion was possible.</exception>146 public abstract object ConvertTo(object sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase);147 148 /// <summary>149 /// Converts the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.150 /// </summary>151 /// <param name="sourceValue">Value supposedly from one of the types supported by this converter to be converted to the <paramref name="destinationType"/> parameter.</param>152 /// <param name="destinationType">Type to convert the <paramref name="sourceValue"/> parameter, supposedly not one of the types supported by the converter.</param>153 /// <param name="formatProvider">The format provider to use like in IFormattable's ToString.</param>154 /// <param name="ignoreCase">True if case should be ignored.</param>155 /// <returns>SourceValue converted to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.</returns>156 /// <exception cref="InvalidCastException">If no conversion was possible.</exception>157 public virtual object ConvertTo(PSObject sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase)158 {159 return this.ConvertTo(GetSourceValueAsObject(sourceValue), destinationType, formatProvider, ignoreCase);160 }161 }162 163 /// <summary>164 /// Enables a type that only has conversion from string to be converted from all other165 /// types through string.166 /// </summary>167 /// <remarks>168 /// It is permitted to subclass <see cref="ConvertThroughString"/>169 /// but there is no established scenario for doing this, nor has it been tested.170 /// </remarks>171 public class ConvertThroughString : PSTypeConverter172 {173 /// <summary>174 /// This will return false only if sourceValue is string.175 /// </summary>176 /// <param name="sourceValue">Value to convert from.</param>177 /// <param name="destinationType">Ignored.</param>178 /// <returns>False only if sourceValue is string.</returns>179 public override bool CanConvertFrom(object sourceValue, Type destinationType)180 {181 // This if avoids infinite recursion.182 if (sourceValue is string)183 {184 return false;185 }186 187 return true;188 }189 190 /// <summary>191 /// Converts to destinationType by first converting sourceValue to string192 /// and then converting the result to destinationType.193 /// </summary>194 /// <param name="sourceValue">The value to convert from.</param>195 /// <param name="destinationType">The type this converter is associated with.</param>196 /// <param name="formatProvider">The IFormatProvider to use.</param>197 /// <param name="ignoreCase">True if case should be ignored.</param>198 /// <returns>SourceValue converted to destinationType.</returns>199 /// <exception cref="PSInvalidCastException">When no conversion was possible.</exception>200 public override object ConvertFrom(object sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase)201 {202 string sourceAsString = (string)LanguagePrimitives.ConvertTo(sourceValue, typeof(string), formatProvider);203 return LanguagePrimitives.ConvertTo(sourceAsString, destinationType, formatProvider);204 }205 /// <summary>206 /// Returns false, since this converter is not designed to be used to207 /// convert from the type associated with the converted to other types.208 /// </summary>209 /// <param name="sourceValue">The value to convert from.</param>210 /// <param name="destinationType">The value to convert from.</param>211 /// <returns>False.</returns>212 public override bool CanConvertTo(object sourceValue, Type destinationType)213 {214 return false;215 }216 217 /// <summary>218 /// Throws NotSupportedException, since this converter is not designed to be used to219 /// convert from the type associated with the converted to other types.220 /// </summary>221 /// <param name="sourceValue">The value to convert from.</param>222 /// <param name="destinationType">The value to convert from.</param>223 /// <param name="formatProvider">The IFormatProvider to use.</param>224 /// <param name="ignoreCase">True if case should be ignored.</param>225 /// <returns>This method does not return a value.</returns>226 /// <exception cref="NotSupportedException">NotSupportedException is always thrown.</exception>227 public override object ConvertTo(object sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase)228 {229 throw PSTraceSource.NewNotSupportedException();230 }231 }232 #endregion public type converters233 234 /// <summary>235 /// The ranking of versions for comparison purposes (used in overload resolution.)236 /// A larger value means the conversion is better.237 ///238 /// Note that the lower nibble is all ones for named conversion ranks. This allows for239 /// conversions with rankings in between the named values. For example, int=>string[]240 /// is value dependent, if the conversion from int=>string succeeds, then an array is241 /// created, otherwise we try some other conversion. The int=>string[] conversion should242 /// be worse than int=>string, but it is probably better than many other conversions, so243 /// we want it to be only slightly worse than int=>string.244 /// </summary>245 /// <remarks>246 /// ValueDependent is a flag, but we don't mark the enum as flags because it really isn't247 /// a flags enum.248 /// To make debugging easier, the "in between" conversions are all named, though there are249 /// no references in the code. They all use the suffix S2A which means "scalar to array".250 /// </remarks>251 internal enum ConversionRank252 {253 None = 0x0000,254 UnrelatedArraysS2A = 0x0007,255 UnrelatedArrays = 0x000F,256 ToStringS2A = 0x0017,257 ToString = 0x001F,258 CustomS2A = 0x0027,259 Custom = 0x002F,260 IConvertibleS2A = 0x0037,261 IConvertible = 0x003F,262 ImplicitCastS2A = 0x0047,263 ImplicitCast = 0x004F,264 ExplicitCastS2A = 0x0057,265 ExplicitCast = 0x005F,266 ConstructorS2A = 0x0067,267 Constructor = 0x006F,268 Create = 0x0073,269 ParseS2A = 0x0077,270 Parse = 0x007F,271 PSObjectS2A = 0x0087,272 PSObject = 0x008F,273 LanguageS2A = 0x0097,274 Language = 0x009F,275 NullToValue = 0x00AF,276 NullToRef = 0x00BF,277 NumericExplicitS2A = 0x00C7,278 NumericExplicit = 0x00CF,279 NumericExplicit1S2A = 0x00D7,280 NumericExplicit1 = 0x00DF,281 NumericStringS2A = 0x00E7,282 NumericString = 0x00EF,283 NumericImplicitS2A = 0x00F7,284 NumericImplicit = 0x00FF,285 AssignableS2A = 0x0107,286 Assignable = 0x010F,287 IdentityS2A = 0x0117,288 StringToCharArray = 0x011A,289 Identity = 0x011F,290 291 ValueDependent = 0xFFF7,292 }293 294 /// <summary>295 /// Defines language support methods.296 /// </summary>297 [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.")]298 public static class LanguagePrimitives299 {300 [TraceSource("ETS", "Extended Type System")]301 private static readonly PSTraceSource s_tracer = PSTraceSource.GetTracer("ETS", "Extended Type System");302 303 internal delegate void MemberNotFoundError(PSObject pso, DictionaryEntry property, Type resultType);304 305 internal delegate void MemberSetValueError(SetValueException e);306 307 internal const string OrderedAttribute = "ordered";308 internal const string DoublePrecision = "G15";309 internal const string SinglePrecision = "G7";310 311 internal static void CreateMemberNotFoundError(PSObject pso, DictionaryEntry property, Type resultType)312 {313 string settableProperties = GetSettableProperties(pso);314 315 string message = settableProperties == string.Empty316 ? StringUtil.Format(ExtendedTypeSystem.NoSettableProperty, property.Key.ToString(), resultType.FullName)317 : StringUtil.Format(ExtendedTypeSystem.PropertyNotFound, property.Key.ToString(), resultType.FullName, settableProperties);318 319 typeConversion.WriteLine("Issuing an error message about not being able to create an object from hashtable.");320 throw new InvalidOperationException(message);321 }322 323 internal static void CreateMemberSetValueError(SetValueException e)324 {325 typeConversion.WriteLine("Issuing an error message about not being able to set the properties for an object.");326 throw e;327 }328 329 static LanguagePrimitives()330 {331 RebuildConversionCache();332 InitializeGetEnumerableCache();333 334#if !CORECLR // AppDomain Not In CoreCLR335 AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolveHelper;336#endif337 }338 339 internal static void UpdateTypeConvertFromTypeTable(string typeName)340 {341 lock (s_converterCache)342 {343 foreach (var key in s_converterCache.Keys)344 {345 if (string.Equals(key.to.FullName, typeName, StringComparison.OrdinalIgnoreCase)346 || string.Equals(key.from.FullName, typeName, StringComparison.OrdinalIgnoreCase))347 {348 s_converterCache.Remove(key);349 }350 }351 352 // Note we do not clear possibleTypeConverter even when removing.353 //354 // The conversion cache and the possibleTypeConverter cache are process wide, but355 // the type table used for any specific conversion is runspace specific.356 s_possibleTypeConverter[typeName] = true;357 }358 }359 360 #region GetEnumerable/GetEnumerator361 362 /// <summary>363 /// This is a wrapper class that allows us to use the generic IEnumerable364 /// implementation of an object when we can't use it's non-generic365 /// implementation.366 /// </summary>367 private sealed class EnumerableTWrapper : IEnumerable368 {369 private readonly object _enumerable;370 private readonly Type _enumerableType;371 private DynamicMethod _getEnumerator;372 373 internal EnumerableTWrapper(object enumerable, Type enumerableType)374 {375 _enumerable = enumerable;376 _enumerableType = enumerableType;377 CreateGetEnumerator();378 }379 380 private void CreateGetEnumerator()381 {382 _getEnumerator = new DynamicMethod("GetEnumerator", typeof(object),383 new Type[] { typeof(object) }, typeof(LanguagePrimitives).Module, true);384 385 ILGenerator emitter = _getEnumerator.GetILGenerator();386 387 emitter.Emit(OpCodes.Ldarg_0);388 emitter.Emit(OpCodes.Castclass, _enumerableType);389 MethodInfo methodInfo = _enumerableType.GetMethod("GetEnumerator", Array.Empty<Type>());390 emitter.Emit(OpCodes.Callvirt, methodInfo);391 emitter.Emit(OpCodes.Ret);392 }393 394 #region IEnumerable Members395 396 public IEnumerator GetEnumerator()397 {398 return (IEnumerator)_getEnumerator.Invoke(null, new object[] { _enumerable });399 }400 401 #endregion402 }403 404 private static IEnumerable GetEnumerableFromIEnumerableT(object obj)405 {406 foreach (Type i in obj.GetType().GetInterfaces())407 {408 if (i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>))409 {410 return new EnumerableTWrapper(obj, i);411 }412 }413 414 return null;415 }416 417 private delegate IEnumerable GetEnumerableDelegate(object obj);418 419 private static readonly Dictionary<Type, GetEnumerableDelegate> s_getEnumerableCache = new Dictionary<Type, GetEnumerableDelegate>(32);420 421 private static GetEnumerableDelegate GetOrCalculateEnumerable(Type type)422 {423 GetEnumerableDelegate getEnumerable = null;424 lock (s_getEnumerableCache)425 {426 if (!s_getEnumerableCache.TryGetValue(type, out getEnumerable))427 {428 getEnumerable = CalculateGetEnumerable(type);429 s_getEnumerableCache.Add(type, getEnumerable);430 }431 }432 433 return getEnumerable;434 }435 436 private static void InitializeGetEnumerableCache()437 {438 lock (s_getEnumerableCache)439 {440 // PowerShell doesn't treat strings as enumerables so just return null.441 // we also want to return null on common numeric types very quickly442 s_getEnumerableCache.Clear();443 s_getEnumerableCache.Add(typeof(string), LanguagePrimitives.ReturnNullEnumerable);444 s_getEnumerableCache.Add(typeof(int), LanguagePrimitives.ReturnNullEnumerable);445 s_getEnumerableCache.Add(typeof(double), LanguagePrimitives.ReturnNullEnumerable);446 }447 }448 449 internal static bool IsTypeEnumerable(Type type)450 {451 if (type == null) { return false; }452 453 GetEnumerableDelegate getEnumerable = GetOrCalculateEnumerable(type);454 return (getEnumerable != LanguagePrimitives.ReturnNullEnumerable);455 }456 457 /// <summary>458 /// Returns True if the language considers obj to be IEnumerable.459 /// </summary>460 /// <param name="obj">461 /// IEnumerable or IEnumerable-like object462 /// </param>463 [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj", Justification = "Since V1 code is already shipped, excluding this message.")]464 public static bool IsObjectEnumerable(object obj)465 {466 return IsTypeEnumerable(PSObject.Base(obj)?.GetType());467 }468 469 /// <summary>470 /// Retrieves the IEnumerable of obj or null if the language does not consider obj to be IEnumerable.471 /// </summary>472 /// <param name="obj">473 /// IEnumerable or IEnumerable-like object474 /// </param>475 [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj", Justification = "Since V1 code is already shipped, excluding this message.")]476 public static IEnumerable GetEnumerable(object obj)477 {478 obj = PSObject.Base(obj);479 if (obj == null) { return null; }480 481 GetEnumerableDelegate getEnumerable = GetOrCalculateEnumerable(obj.GetType());482 return getEnumerable(obj);483 }484 485 private static IEnumerable ReturnNullEnumerable(object obj)486 {487 return null;488 }489 490 private static IEnumerable DataTableEnumerable(object obj)491 {492 return (((DataTable)obj).Rows);493 }494 495 private static IEnumerable TypicalEnumerable(object obj)496 {497 IEnumerable e = (IEnumerable)obj;498 try499 {500 // Some IEnumerable implementations just return null. Others501 // raise an exception. Either of these may have a perfectly502 // good generic implementation, so we'll try those if the503 // non-generic is no good.504 if (e.GetEnumerator() == null)505 {506 return GetEnumerableFromIEnumerableT(obj);507 }508 509 return e;510 }511 catch (Exception innerException)512 {513 e = GetEnumerableFromIEnumerableT(obj);514 if (e != null)515 {516 return e;517 }518 519 throw new ExtendedTypeSystemException(520 "ExceptionInGetEnumerator",521 innerException,522 ExtendedTypeSystem.EnumerationException,523 innerException.Message);524 }525 }526 527 private static GetEnumerableDelegate CalculateGetEnumerable(Type objectType)528 {529 if (typeof(DataTable).IsAssignableFrom(objectType))530 {531 return LanguagePrimitives.DataTableEnumerable;532 }533 534 // Don't treat IDictionary or XmlNode as enumerable...535 if (typeof(IEnumerable).IsAssignableFrom(objectType)536 && !typeof(IDictionary).IsAssignableFrom(objectType)537 && !typeof(XmlNode).IsAssignableFrom(objectType))538 {539 return LanguagePrimitives.TypicalEnumerable;540 }541 542 return LanguagePrimitives.ReturnNullEnumerable;543 }544 545 private static readonly CallSite<Func<CallSite, object, IEnumerator>> s_getEnumeratorSite =546 CallSite<Func<CallSite, object, IEnumerator>>.Create(PSEnumerableBinder.Get());547 548 /// <summary>549 /// Retrieves the IEnumerator of obj or null if the language does not consider obj as capable of returning an IEnumerator.550 /// </summary>551 /// <param name="obj">552 /// IEnumerable or IEnumerable-like object553 /// </param>554 /// <exception cref="ExtendedTypeSystemException">When the act of getting the enumerator throws an exception.</exception>555 [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj", Justification = "Since V1 code is already shipped, excluding this message for backward compatibility reasons.")]556 public static IEnumerator GetEnumerator(object obj)557 {558 var result = s_getEnumeratorSite.Target.Invoke(s_getEnumeratorSite, obj);559 return (result is EnumerableOps.NonEnumerableObjectEnumerator) ? null : result;560 }561 562 #endregion GetEnumerable/GetEnumerator563 564 /// <summary>565 /// This method takes a an arbitrary object and wraps it in a PSDataCollection of PSObject.566 /// This simplifies interacting with the PowerShell workflow activities.567 /// </summary>568 /// <param name="inputValue"></param>569 /// <returns></returns>570 public static PSDataCollection<PSObject> GetPSDataCollection(object inputValue)571 {572 PSDataCollection<PSObject> result = new PSDataCollection<PSObject>();573 if (inputValue != null)574 {575 IEnumerator e = GetEnumerator(inputValue);576 if (e != null)577 {578 while (e.MoveNext())579 {580 result.Add(e.Current == null ? null : PSObject.AsPSObject(e.Current));581 }582 }583 else584 {585 result.Add(PSObject.AsPSObject(inputValue));586 }587 }588 589 result.Complete();590 return result;591 }592 593 /// <summary>594 /// Used to compare two objects for equality converting the second to the type of the first, if required.595 /// </summary>596 /// <param name="first">First object.</param>597 /// <param name="second">Object to compare first to.</param>598 /// <returns>True if first is equal to the second.</returns>599 public static new bool Equals(object first, object second)600 {601 return Equals(first, second, false, CultureInfo.InvariantCulture);602 }603 604 /// <summary>605 /// Used to compare two objects for equality converting the second to the type of the first, if required.606 /// </summary>607 /// <param name="first">First object.</param>608 /// <param name="second">Object to compare first to.</param>609 /// <param name="ignoreCase">used only if first and second are strings610 /// to specify the type of string comparison </param>611 /// <returns>True if first is equal to the second.</returns>612 public static bool Equals(object first, object second, bool ignoreCase)613 {614 return Equals(first, second, ignoreCase, CultureInfo.InvariantCulture);615 }616 617 /// <summary>618 /// Used to compare two objects for equality converting the second to the type of the first, if required.619 /// </summary>620 /// <param name="first">First object.</param>621 /// <param name="second">Object to compare first to.</param>622 /// <param name="ignoreCase">used only if first and second are strings623 /// to specify the type of string comparison </param>624 /// <param name="formatProvider">the format/culture to be used. If this parameter is null,625 /// CultureInfo.InvariantCulture will be used.626 /// </param>627 /// <returns>True if first is equal to the second.</returns>628 public static bool Equals(object first, object second, bool ignoreCase, IFormatProvider formatProvider)629 {630 // If both first and second are null it returns true.631 // If one is null and the other is not it returns false.632 // if (first.Equals(second)) it returns true otherwise it goes ahead with type conversion operations.633 // If both first and second are strings it returns (string.Compare(firstString, secondString, ignoreCase) == 0).634 // If second can be converted to the type of the first, it does so and returns first.Equals(secondConverted)635 // Otherwise false is returned636 637 formatProvider ??= CultureInfo.InvariantCulture;638 639 if (formatProvider is not CultureInfo culture)640 {641 throw PSTraceSource.NewArgumentException(nameof(formatProvider));642 }643 644 first = PSObject.Base(first);645 second = PSObject.Base(second);646 647 if (first == null)648 {649 return second == null;650 }651 652 if (second == null)653 {654 return false; // first is not null655 }656 657 string firstString = first as string;658 string secondString;659 if (firstString != null)660 {661 secondString = second as string ?? (string)LanguagePrimitives.ConvertTo(second, typeof(string), culture);662 return (culture.CompareInfo.Compare(firstString, secondString,663 ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None) == 0);664 }665 666 if (first.Equals(second)) return true;667 668 Type firstType = first.GetType();669 Type secondType = second.GetType();670 int firstIndex = LanguagePrimitives.TypeTableIndex(firstType);671 int secondIndex = LanguagePrimitives.TypeTableIndex(secondType);672 if ((firstIndex != -1) && (secondIndex != -1))673 {674 return LanguagePrimitives.NumericCompare(first, second, firstIndex, secondIndex) == 0;675 }676 677 if (firstType == typeof(char) && ignoreCase)678 {679 secondString = second as string;680 if (secondString != null && secondString.Length == 1)681 {682 char firstAsUpper = culture.TextInfo.ToUpper((char)first);683 char secondAsUpper = culture.TextInfo.ToUpper(secondString[0]);684 return firstAsUpper.Equals(secondAsUpper);685 }686 else if (secondType == typeof(char))687 {688 char firstAsUpper = culture.TextInfo.ToUpper((char)first);689 char secondAsUpper = culture.TextInfo.ToUpper((char)second);690 return firstAsUpper.Equals(secondAsUpper);691 }692 }693 694 try695 {696 object secondConverted = LanguagePrimitives.ConvertTo(second, firstType, culture);697 return first.Equals(secondConverted);698 }699 catch (InvalidCastException)700 {701 }702 703 return false;704 }705 706 /// <summary>707 /// Helper method for [Try]Compare to determine object ordering with null.708 /// </summary>709 /// <param name="value">The numeric value to compare to null.</param>710 /// <param name="numberIsRightHandSide">True if the number to compare is on the right hand side if the comparison.</param>711 private static int CompareObjectToNull(object value, bool numberIsRightHandSide)712 {713 var i = numberIsRightHandSide ? -1 : 1;714 715 // If it's a positive number, including 0, it's greater than null716 // for everything else it's less than zero...717 switch (value)718 {719 case Int16 i16: return Math.Sign(i16) < 0 ? -i : i;720 case Int32 i32: return Math.Sign(i32) < 0 ? -i : i;721 case Int64 i64: return Math.Sign(i64) < 0 ? -i : i;722 case sbyte sby: return Math.Sign(sby) < 0 ? -i : i;723 case float f: return Math.Sign(f) < 0 ? -i : i;724 case double d: return Math.Sign(d) < 0 ? -i : i;725 case decimal de: return Math.Sign(de) < 0 ? -i : i;726 default: return i;727 }728 }729 730 /// <summary>731 /// Compare first and second, converting second to the732 /// type of the first, if necessary.733 /// </summary>734 /// <param name="first">First comparison value.</param>735 /// <param name="second">Second comparison value.</param>736 /// <returns>Less than zero if first is smaller than second, more than737 /// zero if it is greater or zero if they are the same.</returns>738 /// <exception cref="System.ArgumentException">739 /// <paramref name="first"/> does not implement IComparable or <paramref name="second"/> cannot be converted740 /// to the type of <paramref name="first"/>.741 /// </exception>742 public static int Compare(object first, object second)743 {744 return LanguagePrimitives.Compare(first, second, false, CultureInfo.InvariantCulture);745 }746 747 /// <summary>748 /// Compare first and second, converting second to the749 /// type of the first, if necessary.750 /// </summary>751 /// <param name="first">First comparison value.</param>752 /// <param name="second">Second comparison value.</param>753 /// <param name="ignoreCase">Used if both values are strings.</param>754 /// <returns>Less than zero if first is smaller than second, more than755 /// zero if it is greater or zero if they are the same.</returns>756 /// <exception cref="System.ArgumentException">757 /// <paramref name="first"/> does not implement IComparable or <paramref name="second"/> cannot be converted758 /// to the type of <paramref name="first"/>.759 /// </exception>760 public static int Compare(object first, object second, bool ignoreCase)761 {762 return LanguagePrimitives.Compare(first, second, ignoreCase, CultureInfo.InvariantCulture);763 }764 765 /// <summary>766 /// Compare first and second, converting second to the767 /// type of the first, if necessary.768 /// </summary>769 /// <param name="first">First comparison value.</param>770 /// <param name="second">Second comparison value.</param>771 /// <param name="ignoreCase">Used if both values are strings.</param>772 /// <param name="formatProvider">Used in type conversions and if both values are strings.</param>773 /// <returns>Less than zero if first is smaller than second, more than774 /// zero if it is greater or zero if they are the same.</returns>775 /// <exception cref="System.ArgumentException">776 /// <paramref name="first"/> does not implement IComparable or <paramref name="second"/> cannot be converted777 /// to the type of <paramref name="first"/>.778 /// </exception>779 public static int Compare(object first, object second, bool ignoreCase, IFormatProvider formatProvider)780 {781 formatProvider ??= CultureInfo.InvariantCulture;782 783 if (formatProvider is not CultureInfo culture)784 {785 throw PSTraceSource.NewArgumentException(nameof(formatProvider));786 }787 788 first = PSObject.Base(first);789 second = PSObject.Base(second);790 791 if (first == null)792 {793 return second == null ? 0 : CompareObjectToNull(second, true);794 }795 796 if (second == null)797 {798 return CompareObjectToNull(first, false);799 }800 801 if (first is string firstString)802 {803 string secondString = second as string;804 if (secondString == null)805 {806 try807 {808 secondString = (string)LanguagePrimitives.ConvertTo(second, typeof(string), culture);809 }810 catch (PSInvalidCastException e)811 {812 throw PSTraceSource.NewArgumentException(nameof(second), ExtendedTypeSystem.ComparisonFailure,813 first.ToString(), second.ToString(), e.Message);814 }815 }816 817 return culture.CompareInfo.Compare(firstString, secondString,818 ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);819 }820 821 Type firstType = first.GetType();822 Type secondType = second.GetType();823 int firstIndex = LanguagePrimitives.TypeTableIndex(firstType);824 int secondIndex = LanguagePrimitives.TypeTableIndex(secondType);825 if ((firstIndex != -1) && (secondIndex != -1))826 {827 return LanguagePrimitives.NumericCompare(first, second, firstIndex, secondIndex);828 }829 830 object secondConverted;831 try832 {833 secondConverted = LanguagePrimitives.ConvertTo(second, firstType, culture);834 }835 catch (PSInvalidCastException e)836 {837 throw PSTraceSource.NewArgumentException(nameof(second), ExtendedTypeSystem.ComparisonFailure,838 first.ToString(), second.ToString(), e.Message);839 }840 841 if (first is IComparable firstComparable)842 {843 return firstComparable.CompareTo(secondConverted);844 }845 846 if (first.Equals(second))847 {848 return 0;849 }850 851 // At this point, we know that they aren't equal but we have no way of852 // knowing which should compare greater than the other so we throw an exception.853 throw PSTraceSource.NewArgumentException(nameof(first), ExtendedTypeSystem.NotIcomparable, first.ToString());854 }855 856 /// <summary>857 /// Tries to compare first and second, converting second to the type of the first, if necessary.858 /// If a conversion is needed but fails, false is return.859 /// </summary>860 /// <param name="first">First comparison value.</param>861 /// <param name="second">Second comparison value.</param>862 /// <param name="result">Less than zero if first is smaller than second, more than863 /// zero if it is greater or zero if they are the same.</param>864 /// <returns>True if the comparison was successful, false otherwise.</returns>865 public static bool TryCompare(object first, object second, out int result)866 {867 return TryCompare(first, second, ignoreCase: false, CultureInfo.InvariantCulture, out result);868 }869 870 /// <summary>871 /// Tries to compare first and second, converting second to the type of the first, if necessary.872 /// If a conversion is needed but fails, false is return.873 /// </summary>874 /// <param name="first">First comparison value.</param>875 /// <param name="second">Second comparison value.</param>876 /// <param name="ignoreCase">Used if both values are strings.</param>877 /// <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>878 /// <returns>True if the comparison was successful, false otherwise.</returns>879 public static bool TryCompare(object first, object second, bool ignoreCase, out int result)880 {881 return TryCompare(first, second, ignoreCase, CultureInfo.InvariantCulture, out result);882 }883 884 /// <summary>885 /// Tries to compare first and second, converting second to the type of the first, if necessary.886 /// If a conversion is needed but fails, false is return.887 /// </summary>888 /// <param name="first">First comparison value.</param>889 /// <param name="second">Second comparison value.</param>890 /// <param name="ignoreCase">Used if both values are strings.</param>891 /// <param name="formatProvider">Used in type conversions and if both values are strings.</param>892 /// <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>893 /// <returns>True if the comparison was successful, false otherwise.</returns>894 /// <exception cref="ArgumentException">The parameter <paramref name="formatProvider"/> is not a <see cref="CultureInfo"/>.</exception>895 public static bool TryCompare(object first, object second, bool ignoreCase, IFormatProvider formatProvider, out int result)896 {897 result = 0;898 formatProvider ??= CultureInfo.InvariantCulture;899 900 if (formatProvider is not CultureInfo culture)901 {902 throw PSTraceSource.NewArgumentException(nameof(formatProvider));903 }904 905 first = PSObject.Base(first);906 second = PSObject.Base(second);907 908 if (first == null && second == null)909 {910 result = 0;911 return true;912 }913 914 if (first == null)915 {916 result = CompareObjectToNull(second, true);917 return true;918 }919 920 if (second == null)921 {922 // If it's a positive number, including 0, it's greater than null923 // for everything else it's less than zero...924 result = CompareObjectToNull(first, false);925 return true;926 }927 928 if (first is string firstString)929 {930 if (second is not string secondString)931 {932 if (!TryConvertTo(second, culture, out secondString))933 {934 return false;935 }936 }937 938 result = culture.CompareInfo.Compare(firstString, secondString, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);939 return true;940 }941 942 Type firstType = first.GetType();943 Type secondType = second.GetType();944 int firstIndex = TypeTableIndex(firstType);945 int secondIndex = TypeTableIndex(secondType);946 if (firstIndex != -1 && secondIndex != -1)947 {948 result = NumericCompare(first, second, firstIndex, secondIndex);949 return true;950 }951 952 if (!TryConvertTo(second, firstType, culture, out object secondConverted))953 {954 return false;955 }956 957 if (first is IComparable firstComparable)958 {959 result = firstComparable.CompareTo(secondConverted);960 return true;961 }962 963 if (first.Equals(second))964 {965 result = 0;966 return true;967 }968 969 // At this point, we know that they aren't equal but we have no way of970 // knowing which should compare greater than the other so we return false.971 return false;972 }973 974 /// <summary>975 /// Returns true if the language considers obj to be true.976 /// </summary>977 /// <param name="obj">Obj to verify if it is true.</param>978 /// <returns>True if obj is true.</returns>979 [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj", Justification = "Since V1 code is already shipped, excluding this message for backward compatibility reasons")]980 public static bool IsTrue(object obj)981 {982 // null is a valid argument - it converts to false...983 if (obj == null || obj == AutomationNull.Value)984 return false;985 986 obj = PSObject.Base(obj);987 988 Type objType = obj.GetType();989 990 if (objType == typeof(bool))991 {992 return (bool)obj;993 }994 995 if (objType == typeof(string))996 {997 return IsTrue((string)obj);998 }999 1000 if (LanguagePrimitives.IsNumeric(LanguagePrimitives.GetTypeCode(objType)))1001 {1002 IConversionData data = GetConversionData(objType, typeof(bool)) ??1003 CacheConversion(objType, typeof(bool), CreateNumericToBoolConverter(objType), ConversionRank.Language);1004 return (bool)data.Invoke(obj, typeof(bool), false, null, null, null);1005 }1006 1007 if (objType == typeof(SwitchParameter))1008 return ((SwitchParameter)obj).ToBool();1009 1010 IList objectArray = obj as IList;1011 if (objectArray != null)1012 {1013 return IsTrue(objectArray);1014 }1015 1016 return true;1017 }1018 1019 internal static bool IsTrue(string s)1020 {1021 return (s.Length != 0);1022 }1023 1024 internal static bool IsTrue(IList objectArray)1025 {1026 switch (objectArray.Count)1027 {1028 // a zero length array is false, so condition is false1029 case 0:1030 return false;1031 1032 // if the result is an array of length 1, treat it as a scalar...1033 case 1:1034 // A possible implementation would be just1035 // return IsTrue(objectArray[0]);1036 // but since we don't want this to recurse indefinitely1037 // we explicitly check the case where it would recurse1038 // and deal with it.1039 if (PSObject.Base(objectArray[0]) is not IList firstElement)1040 {1041 return IsTrue(objectArray[0]);1042 }1043 1044 if (firstElement.Count < 1) return false;1045 1046 // the first element is an array with more than zero elements1047 return true;1048 1049 default:1050 return true;1051 }1052 }1053 1054 /// <summary>1055 /// Internal routine that determines if an object meets any of our criteria for null.1056 /// </summary>1057 /// <param name="obj">The object to test.</param>1058 /// <returns>True if the object is null.</returns>1059 internal static bool IsNull(object obj)1060 {1061 return (obj == null || obj == AutomationNull.Value);1062 }1063 1064 /// <summary>1065 /// Auxiliary for the cases where we want a new PSObject or null.1066 /// </summary>1067 internal static PSObject AsPSObjectOrNull(object obj)1068 {1069 if (obj == null)1070 {1071 return null;1072 }1073 1074 return PSObject.AsPSObject(obj);1075 }1076 1077 internal static int TypeTableIndex(Type type)1078 {1079 switch (LanguagePrimitives.GetTypeCode(type))1080 {1081 case TypeCode.Int16: return 0;1082 case TypeCode.Int32: return 1;1083 case TypeCode.Int64: return 2;1084 case TypeCode.UInt16: return 3;1085 case TypeCode.UInt32: return 4;1086 case TypeCode.UInt64: return 5;1087 case TypeCode.SByte: return 6;1088 case TypeCode.Byte: return 7;1089 case TypeCode.Single: return 8;1090 case TypeCode.Double: return 9;1091 case TypeCode.Decimal: return 10;1092 default: return -1;1093 }1094 }1095 1096 /// <summary>1097 /// Table of the largest safe type to which both types can be converted without exceptions.1098 /// This table is used for numeric comparisons.1099 /// The 4 entries marked as not used, are explicitly dealt with in NumericCompareDecimal.1100 /// NumericCompareDecimal exists because doubles and singles can throw1101 /// an exception when converted to decimal.1102 /// The order of lines and columns cannot be changed since NumericCompare depends on it.1103 /// </summary>1104 internal static readonly Type[][] LargestTypeTable = new Type[][]1105 {1106 // System.Int16 System.Int32 System.Int64 System.UInt16 System.UInt32 System.UInt64 System.SByte System.Byte System.Single System.Double System.Decimal1107 /* 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) },1108 /* 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) },1109 /* 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) },1110 /* 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) },1111 /* 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) },1112 /* 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) },1113 /* 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) },1114 /* 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) },1115 /* 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*/ },1116 /* 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*/ },1117 /* 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) },1118 };1119 1120 private static int NumericCompareDecimal(decimal decimalNumber, object otherNumber)1121 {1122 object otherDecimal = null;1123 try1124 {1125 otherDecimal = Convert.ChangeType(otherNumber, typeof(System.Decimal), CultureInfo.InvariantCulture);1126 }1127 catch (OverflowException)1128 {1129 try1130 {1131 double wasDecimal = (double)Convert.ChangeType(decimalNumber, typeof(System.Double), CultureInfo.InvariantCulture);1132 double otherDouble = (double)Convert.ChangeType(otherNumber, typeof(System.Double), CultureInfo.InvariantCulture);1133 return ((IComparable)wasDecimal).CompareTo(otherDouble);1134 }1135 catch (Exception) // We need to catch the generic exception because ChangeType throws unadvertised exceptions1136 {1137 return -1;1138 }1139 }1140 catch (Exception) // We need to catch the generic exception because ChangeType throws unadvertised exceptions1141 {1142 return -1;1143 }1144 1145 return ((IComparable)decimalNumber).CompareTo(otherDecimal);1146 }1147 1148 private static int NumericCompare(object number1, object number2, int index1, int index2)1149 {1150 // Conversion from single or double to decimal might throw1151 // if the double is greater than the decimal's maximum so1152 // we special case it in NumericCompareDecimal1153 if ((index1 == 10) && ((index2 == 8) || (index2 == 9)))1154 {1155 return NumericCompareDecimal((decimal)number1, number2);1156 }1157 1158 if ((index2 == 10) && ((index1 == 8) || (index1 == 9)))1159 {1160 return -NumericCompareDecimal((decimal)number2, number1);1161 }1162 1163 Type commonType = LargestTypeTable[index1][index2];1164 object number1Converted = Convert.ChangeType(number1, commonType, CultureInfo.InvariantCulture);1165 object number2Converted = Convert.ChangeType(number2, commonType, CultureInfo.InvariantCulture);1166 return ((IComparable)number1Converted).CompareTo(number2Converted);1167 }1168 1169 /// <summary>1170 /// Necessary not to return an integer type code for enums.1171 /// </summary>1172 /// <param name="type"></param>1173 /// <returns></returns>1174 internal static TypeCode GetTypeCode(Type type)1175 {1176 return type.GetTypeCode();1177 }1178 1179 /// <summary>1180 /// Emulates the "As" C# language primitive, but will unwrap1181 /// the PSObject if required.1182 /// </summary>1183 /// <typeparam name="T">The type for which to convert</typeparam>1184 /// <param name="castObject">The object from which to convert.</param>1185 /// <returns>An object of the specified type, if the conversion was successful. Returns null otherwise.</returns>1186 internal static T FromObjectAs<T>(object castObject)1187 {1188 T returnType = default(T);1189 1190 // First, see if we can cast the direct type1191 PSObject wrapperObject = castObject as PSObject;1192 if (wrapperObject == null)1193 {1194 try1195 {1196 returnType = (T)castObject;1197 }1198 catch (InvalidCastException)1199 {1200 returnType = default(T);