PowerShell/src/System.Management.Automation/engine/ArgumentTypeConverterAttribute.cs
xtqqczze 6c03776d74
Use is null syntax (#13277)
Replace `== null` with `is null`
2020-07-30 18:04:03 +05:00

214 lines
9.3 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Collections;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Dbg = System.Management.Automation.Diagnostics;
namespace System.Management.Automation
{
/// <summary>
/// This is used for automatic conversions to be performed in shell variables.
/// </summary>
internal sealed class ArgumentTypeConverterAttribute : ArgumentTransformationAttribute
{
/// <summary>
/// This ctor form is used to initialize shell variables
/// whose type is not permitted to change.
/// </summary>
/// <param name="types"></param>
internal ArgumentTypeConverterAttribute(params Type[] types)
{
_convertTypes = types;
}
private Type[] _convertTypes;
internal Type TargetType
{
get
{
return _convertTypes is null
? null
: _convertTypes.LastOrDefault();
}
}
public override object Transform(EngineIntrinsics engineIntrinsics, object inputData)
{
return Transform(engineIntrinsics, inputData, false, false);
}
internal object Transform(EngineIntrinsics engineIntrinsics, object inputData, bool bindingParameters, bool bindingScriptCmdlet)
{
if (_convertTypes is null)
return inputData;
object result = inputData;
try
{
for (int i = 0; i < _convertTypes.Length; i++)
{
if (bindingParameters)
{
// We should not be doing a conversion here if [ref] is the last type.
// When [ref] appears in an argument list, it is used for checking only.
// No Conversion should be done.
if (_convertTypes[i].Equals(typeof(System.Management.Automation.PSReference)))
{
object temp;
PSObject mshObject = result as PSObject;
if (mshObject != null)
temp = mshObject.BaseObject;
else
temp = result;
PSReference reference = temp as PSReference;
if (reference is null)
{
throw new PSInvalidCastException("InvalidCastExceptionReferenceTypeExpected", null,
ExtendedTypeSystem.ReferenceTypeExpected);
}
}
else
{
object temp;
PSObject mshObject = result as PSObject;
if (mshObject != null)
temp = mshObject.BaseObject;
else
temp = result;
// If a non-ref type is expected but currently passed in is a ref, do an implicit dereference.
PSReference reference = temp as PSReference;
if (reference != null)
{
result = reference.Value;
}
if (bindingScriptCmdlet && _convertTypes[i] == typeof(string))
{
// Don't allow conversion from array to string in script w/ cmdlet binding. Allow
// the conversion for ordinary script parameter binding for V1 compatibility.
temp = PSObject.Base(result);
if (temp != null && temp.GetType().IsArray)
{
throw new PSInvalidCastException("InvalidCastFromAnyTypeToString", null,
ExtendedTypeSystem.InvalidCastCannotRetrieveString);
}
}
}
}
// BUGBUG
// NTRAID#Windows Out of Band Releases - 930116 - 03/14/06
// handling special case for boolean, switchparameter and Nullable<bool>
// These parameter types will not be converted if the incoming value types are not
// one of the accepted categories - $true/$false or numbers (0 or otherwise)
if (LanguagePrimitives.IsBoolOrSwitchParameterType(_convertTypes[i]))
{
CheckBoolValue(result, _convertTypes[i]);
}
if (bindingScriptCmdlet)
{
// Check for conversion to something like bool[] or ICollection<bool>, but only for cmdlet binding
// to stay compatible with V1.
ParameterCollectionTypeInformation collectionTypeInfo = new ParameterCollectionTypeInformation(_convertTypes[i]);
if (collectionTypeInfo.ParameterCollectionType != ParameterCollectionType.NotCollection
&& LanguagePrimitives.IsBoolOrSwitchParameterType(collectionTypeInfo.ElementType))
{
IList currentValueAsIList = ParameterBinderBase.GetIList(result);
if (currentValueAsIList != null)
{
foreach (object val in currentValueAsIList)
{
CheckBoolValue(val, collectionTypeInfo.ElementType);
}
}
else
{
CheckBoolValue(result, collectionTypeInfo.ElementType);
}
}
}
result = LanguagePrimitives.ConvertTo(result, _convertTypes[i], CultureInfo.InvariantCulture);
// Do validation of invalid direct variable assignments which are allowed to
// be used for parameters.
//
// Note - this is duplicated in ExecutionContext.cs as parameter binding for script cmdlets can avoid this code path.
if ((!bindingScriptCmdlet) && (!bindingParameters))
{
// ActionPreference.Suspend is reserved for future use and is not supported as a preference variable.
if (_convertTypes[i] == typeof(ActionPreference))
{
ActionPreference resultPreference = (ActionPreference)result;
if (resultPreference == ActionPreference.Suspend)
{
throw new PSInvalidCastException("InvalidActionPreference", null, ErrorPackage.ActionPreferenceReservedForFutureUseError, resultPreference);
}
}
}
}
}
catch (PSInvalidCastException e)
{
throw new ArgumentTransformationMetadataException(e.Message, e);
}
// Track the flow of untrusted object during the conversion when it's called directly from ParameterBinderBase.
// When it's called from the override Transform method, the tracking is taken care of in the base type.
if (bindingParameters || bindingScriptCmdlet)
{
ExecutionContext.PropagateInputSource(inputData, result, engineIntrinsics.SessionState.Internal.LanguageMode);
}
return result;
}
private static void CheckBoolValue(object value, Type boolType)
{
if (value != null)
{
Type resultType = value.GetType();
if (resultType == typeof(PSObject))
resultType = ((PSObject)value).BaseObject.GetType();
if (!(LanguagePrimitives.IsNumeric(resultType.GetTypeCode()) ||
LanguagePrimitives.IsBoolOrSwitchParameterType(resultType)))
{
ThrowPSInvalidBooleanArgumentCastException(resultType, boolType);
}
}
else
{
bool isNullable = boolType.IsGenericType &&
boolType.GetGenericTypeDefinition() == typeof(Nullable<>);
if (!isNullable && LanguagePrimitives.IsBooleanType(boolType))
{
ThrowPSInvalidBooleanArgumentCastException(null, boolType);
}
}
}
internal static void ThrowPSInvalidBooleanArgumentCastException(Type resultType, Type convertType)
{
throw new PSInvalidCastException("InvalidCastExceptionUnsupportedParameterType", null,
ExtendedTypeSystem.InvalidCastExceptionForBooleanArgumentValue,
resultType, convertType);
}
}
}