/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ using System.Collections; using System.Collections.ObjectModel; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Management.Automation.Internal; using System.Management.Automation.Language; using System.Reflection; using System.Globalization; using System.Diagnostics.CodeAnalysis; using Dbg = System.Management.Automation.Diagnostics; namespace System.Management.Automation { /// /// Flags /// [Flags] internal enum ParameterBindingFlags { /// /// No flags specified /// None = 0, /// /// Set when the argument should be converted to the parameter type /// ShouldCoerceType = 0x01, /// /// Set when the argument should not be validated or recorded in BoundParameters /// IsDefaultValue = 0x02, /// /// Set when script blocks can be bound as a script block parameter instead of a normal argument /// DelayBindScriptBlock = 0x04, /// /// Set when an exception will be thrown if a matching parameter could not be found. /// ThrowOnParameterNotFound = 0x08, } /// /// An abstract class used by the CommandProcessor to bind parameters to a bindable object. /// Derived classes are used to provide specific binding behavior for different object types, /// like Cmdlet, PsuedoParameterCollection, and dynamic parameter objects. /// [DebuggerDisplay("Command = {command}")] internal abstract class ParameterBinderBase { #region tracer [TraceSource("ParameterBinderBase", "A abstract helper class for the CommandProcessor that binds parameters to the specified object.")] private static PSTraceSource s_tracer = PSTraceSource.GetTracer("ParameterBinderBase", "A abstract helper class for the CommandProcessor that binds parameters to the specified object."); [TraceSource("ParameterBinding", "Traces the process of binding the arguments to the parameters of cmdlets, scripts, and applications.")] internal static PSTraceSource bindingTracer = PSTraceSource.GetTracer( "ParameterBinding", "Traces the process of binding the arguments to the parameters of cmdlets, scripts, and applications.", false); #endregion tracer #region ctor /// /// Constructs the parameter binder with the specified type metadata. The binder is only valid /// for a single instance of a bindable object and only for the duration of a command. /// /// /// /// The target object that the parameter values will be bound to. /// /// /// /// The invocation information for the code that is being bound. /// /// /// /// The context of the currently running engine. /// /// /// /// The command that the parameter binder is binding to. The command can be null. /// /// internal ParameterBinderBase( object target, InvocationInfo invocationInfo, ExecutionContext context, InternalCommand command) { Diagnostics.Assert(target != null, "caller to verify target is not null."); Diagnostics.Assert(invocationInfo != null, "caller to verify invocationInfo is not null."); Diagnostics.Assert(context != null, "caller to verify context is not null."); bindingTracer.ShowHeaders = false; _command = command; _target = target; _invocationInfo = invocationInfo; _context = context; _engine = context.EngineIntrinsics; _isTranscribing = context.EngineHostInterface.UI.IsTranscribing; } /// /// Constructs the parameter binder with the specified type metadata. The binder is only valid /// for a single instance of a bindable object and only for the duration of a command. /// /// /// /// The invocation information for the code that is being bound. /// /// /// /// The context of the currently running engine. /// /// /// /// The command that the parameter binder is binding to. The command can be null. /// /// internal ParameterBinderBase( InvocationInfo invocationInfo, ExecutionContext context, InternalCommand command) { Diagnostics.Assert(invocationInfo != null, "caller to verify invocationInfo is not null."); Diagnostics.Assert(context != null, "caller to verify context is not null."); bindingTracer.ShowHeaders = false; _command = command; _invocationInfo = invocationInfo; _context = context; _engine = context.EngineIntrinsics; _isTranscribing = context.EngineHostInterface.UI.IsTranscribing; } #endregion ctor #region internal members /// /// Gets or sets the bindable object that the binder will bind parameters to. /// /// internal object Target { get { Diagnostics.Assert( _target != null, "The target should always be set for the binder"); return _target; } set { _target = value; } } /// /// The bindable object that parameters will be bound to. /// private object _target; /// /// Holds the set of parameters that have been bound from the command line... /// internal CommandLineParameters CommandLineParameters { // Setter is needed to pass into RuntimeParameterBinder instances set { _commandLineParameters = value; } get { return _commandLineParameters ?? (_commandLineParameters = new CommandLineParameters()); } } private CommandLineParameters _commandLineParameters; /// /// If this is true, then we want to record the list of bound parameters... /// internal bool RecordBoundParameters = true; /// /// Full Qualified ID for the obsolete parameter warning /// internal const string FQIDParameterObsolete = "ParameterObsolete"; #region Parameter default values /// /// Derived classes must override this method to get the default parameter /// value so that it can be restored between pipeline input /// /// /// /// The name of the parameter to get the default value of. /// /// /// /// The value of the parameter specified by name. /// /// internal abstract object GetDefaultParameterValue(string name); #endregion Parameter default values #region Parameter binding /// /// Derived classes define this method to bind the specified value /// to the specified parameter. /// /// /// The name of the parameter to bind the value to. /// /// /// The value to bind to the parameter. It should be assumed by /// derived classes that the proper type coercion has already taken /// place and that any validation metadata has been satisfied. /// /// internal abstract void BindParameter(string name, object value, CompiledCommandParameter parameterMetadata); private void ValidatePSTypeName( CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, bool retryOtherBindingAfterFailure, object parameterValue) { Dbg.Assert(parameter != null, "Caller should verify parameter != null"); Dbg.Assert(parameterMetadata != null, "Caller should verify parameterMetadata != null"); if (parameterValue == null) { return; } IEnumerable psTypeNamesOfArgumentValue = PSObject.AsPSObject(parameterValue).InternalTypeNames; string psTypeNameRequestedByParameter = parameterMetadata.PSTypeName; if (!psTypeNamesOfArgumentValue.Contains(psTypeNameRequestedByParameter, StringComparer.OrdinalIgnoreCase)) { // win8: 228176..The callers know when to ignore and when not to ignore invalid cast exceptions. PSInvalidCastException e = new PSInvalidCastException(ErrorCategory.InvalidArgument.ToString(), null, ParameterBinderStrings.MismatchedPSTypeName, (null != _invocationInfo) && (null != _invocationInfo.MyCommand) ? _invocationInfo.MyCommand.Name : string.Empty, parameterMetadata.Name, parameterMetadata.Type, parameterValue.GetType(), 0, 0, psTypeNameRequestedByParameter); ParameterBindingException parameterBindingException; if (!retryOtherBindingAfterFailure) { parameterBindingException = new ParameterBindingArgumentTransformationException( e, ErrorCategory.InvalidArgument, this.InvocationInfo, GetErrorExtent(parameter), parameterMetadata.Name, parameterMetadata.Type, parameterValue.GetType(), ParameterBinderStrings.MismatchedPSTypeName, "MismatchedPSTypeName", psTypeNameRequestedByParameter); } else { parameterBindingException = new ParameterBindingException( e, ErrorCategory.InvalidArgument, this.InvocationInfo, GetErrorExtent(parameter), parameterMetadata.Name, parameterMetadata.Type, parameterValue.GetType(), ParameterBinderStrings.MismatchedPSTypeName, "MismatchedPSTypeName", psTypeNameRequestedByParameter); } throw parameterBindingException; } } /// /// Does all the type coercion, data generation, and validation necessary to bind the /// parameter, then calls the protected BindParameter method to have /// the derived class do the actual binding. /// /// /// /// The parameter to be bound. /// /// /// /// The metadata for the parameter to use in guiding the binding. /// /// /// /// Flags for type coercion and validation. /// /// /// /// True if the parameter was successfully bound. False if /// is false and the type does not match the parameter type. /// /// /// /// The binding algorithm goes as follows: /// 1. The data generation attributes are run /// 2. The data is coerced into the correct type /// 3. The data if validated using the validation attributes /// 4. The data is encoded into the bindable object using the /// protected BindParameter method. /// /// /// /// If or is null. /// /// /// /// If argument transformation fails. /// or /// The argument could not be coerced to the appropriate type for the parameter. /// or /// The parameter argument transformation, prerequisite, or validation failed. /// or /// If the binding to the parameter fails. /// /// internal virtual bool BindParameter( CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, ParameterBindingFlags flags) { bool result = false; bool coerceTypeIfNeeded = (flags & ParameterBindingFlags.ShouldCoerceType) != 0; bool isDefaultValue = (flags & ParameterBindingFlags.IsDefaultValue) != 0; if (parameter == null) { throw PSTraceSource.NewArgumentNullException("parameter"); } if (parameterMetadata == null) { throw PSTraceSource.NewArgumentNullException("parameterMetadata"); } using (bindingTracer.TraceScope( "BIND arg [{0}] to parameter [{1}]", parameter.ArgumentValue, parameterMetadata.Name)) { // Set the complete parameter name parameter.ParameterName = parameterMetadata.Name; object parameterValue = parameter.ArgumentValue; do // false loop { // Now call any argument transformation attributes that might be present on the parameter ScriptParameterBinder spb = this as ScriptParameterBinder; bool usesCmdletBinding = false; if (spb != null) { usesCmdletBinding = spb.Script.UsesCmdletBinding; } // Now do the argument transformation. No transformation is done for the default values in script that meet the following 2 conditions: // 1. the default value is not specified by the user, but is the powershell default value. // e.g. the powershell default value for a class type is null, for the string type is string.empty. // 2. the powershell default value is null. // This is to prevent ArgumentTransformationAttributes from making a non-mandatory parameter behave like a mandatory one. // Example: // ## without the fix, 'CredentialAttribute' would make $Credential like a mandatory parameter // ## 'PS> test-credential' would prompt for credential input // function test-credential { // param( // [System.Management.Automation.CredentialAttribute()] // $Credential // ) // $Credential // } // foreach (ArgumentTransformationAttribute dma in parameterMetadata.ArgumentTransformationAttributes) { using (bindingTracer.TraceScope( "Executing DATA GENERATION metadata: [{0}]", dma.GetType())) { try { ArgumentTypeConverterAttribute argumentTypeConverter = dma as ArgumentTypeConverterAttribute; if (argumentTypeConverter != null) { if (coerceTypeIfNeeded) { parameterValue = argumentTypeConverter.Transform(_engine, parameterValue, true, usesCmdletBinding); } } else { // Only apply argument transformation when the argument is not null, is mandatory, or disallows null as a value. // // If we are binding default value for an unbound script parameter, this parameter is guaranteed not mandatory // in the chosen parameter set. This is because: // 1. If we use cmdlet binding for this script (CmdletParameterBinderController is used), then binding // default value to unbound parameters won't happen until after all mandatory parameters from the // chosen parameter set are handled. Therefore, the unbound parameter we are dealing with here won't // be mandatory in the chosen parameter set. // 2. If we use script binding (ScriptParameterBinderController is used), then parameters won't have the // ParameterAttribute declared for them, and thus are definitely not mandatory. // So we check 'IsParameterMandatory' only if we are not binding default values. if ((parameterValue != null) || (!isDefaultValue && (parameterMetadata.IsMandatoryInSomeParameterSet || parameterMetadata.CannotBeNull || dma.TransformNullOptionalParameters))) { parameterValue = dma.Transform(_engine, parameterValue); } } bindingTracer.WriteLine( "result returned from DATA GENERATION: {0}", parameterValue); } catch (Exception e) // Catch-all OK, 3rd party callout { CommandProcessorBase.CheckForSevereException(e); bindingTracer.WriteLine( "ERROR: DATA GENERATION: {0}", e.Message); ParameterBindingException bindingException = new ParameterBindingArgumentTransformationException( e, ErrorCategory.InvalidData, this.InvocationInfo, GetErrorExtent(parameter), parameterMetadata.Name, parameterMetadata.Type, (parameterValue == null) ? null : parameterValue.GetType(), ParameterBinderStrings.ParameterArgumentTransformationError, "ParameterArgumentTransformationError", e.Message); throw bindingException; } } } // Only try to coerce the type if asked. If not asked, // see if the value type matches or is a subclass of // the parameter type. if (coerceTypeIfNeeded) { // Now do the type coercion parameterValue = CoerceTypeAsNeeded( parameter, parameterMetadata.Name, parameterMetadata.Type, parameterMetadata.CollectionTypeInformation, parameterValue); } else { if (!ShouldContinueUncoercedBind(parameter, parameterMetadata, flags, ref parameterValue)) { // Don't attempt the bind because the value // is not of the correct // type for the parameter. break; } } if ((parameterMetadata.PSTypeName != null) && (parameterValue != null)) { IEnumerable parameterValueAsEnumerable = LanguagePrimitives.GetEnumerable(parameterValue); if (parameterValueAsEnumerable != null) { foreach (object o in parameterValueAsEnumerable) { this.ValidatePSTypeName(parameter, parameterMetadata, !coerceTypeIfNeeded, o); } } else { this.ValidatePSTypeName(parameter, parameterMetadata, !coerceTypeIfNeeded, parameterValue); } } // Now do the data validation. No validation is done for default values in script as that is // one way for people to have a known "bad" value to detect unspecified parameters. if (!isDefaultValue) { for (int i = 0; i < parameterMetadata.ValidationAttributes.Length; i++) { var validationAttribute = parameterMetadata.ValidationAttributes[i]; using (bindingTracer.TraceScope( "Executing VALIDATION metadata: [{0}]", validationAttribute.GetType())) { try { validationAttribute.InternalValidate(parameterValue, _engine); } catch (Exception e) // Catch-all OK, 3rd party callout { CommandProcessorBase.CheckForSevereException(e); bindingTracer.WriteLine( "ERROR: VALIDATION FAILED: {0}", e.Message); ParameterBindingValidationException bindingException = new ParameterBindingValidationException( e, ErrorCategory.InvalidData, this.InvocationInfo, GetErrorExtent(parameter), parameterMetadata.Name, parameterMetadata.Type, (parameterValue == null) ? null : parameterValue.GetType(), ParameterBinderStrings.ParameterArgumentValidationError, "ParameterArgumentValidationError", e.Message); throw bindingException; } s_tracer.WriteLine("Validation attribute on {0} returned {1}.", parameterMetadata.Name, result); } } // If the is null, an empty string, or an empty collection, // check the parameter metadata to ensure that binding can continue // This method throws an appropriate ParameterBindingException // if binding cannot continue. If it returns then binding can // proceed. if (parameterMetadata.IsMandatoryInSomeParameterSet) { ValidateNullOrEmptyArgument(parameter, parameterMetadata, parameterMetadata.Type, parameterValue, true); } } // Write out obsolete parameter warning only if // 1. We are binding parameters for a simple function/script // 2. We are not binding a default parameter value if (parameterMetadata.ObsoleteAttribute != null && (!isDefaultValue) && spb != null && !usesCmdletBinding) { string obsoleteWarning = String.Format( CultureInfo.InvariantCulture, ParameterBinderStrings.UseOfDeprecatedParameterWarning, parameterMetadata.Name, parameterMetadata.ObsoleteAttribute.Message); var mshCommandRuntime = this.Command.commandRuntime as MshCommandRuntime; if (mshCommandRuntime != null) { // Write out warning only if we are in the context of MshCommandRuntime. // This is because // 1. The overload method WriteWarning(WarningRecord) is only available in MshCommandRuntime; // 2. We write out warnings for obsolete commands and obsolete cmdlet parameters only when in // the context of MshCommandRuntime. So we do it here to keep consistency. mshCommandRuntime.WriteWarning(new WarningRecord(FQIDParameterObsolete, obsoleteWarning)); } } // Finally bind the argument to the parameter Exception bindError = null; try { BindParameter(parameter.ParameterName, parameterValue, parameterMetadata); result = true; } catch (SetValueException setValueException) { bindError = setValueException; } if (bindError != null) { Type specifiedType = (parameterValue == null) ? null : parameterValue.GetType(); ParameterBindingException bindingException = new ParameterBindingException( bindError, ErrorCategory.WriteError, this.InvocationInfo, GetErrorExtent(parameter), parameterMetadata.Name, parameterMetadata.Type, specifiedType, ParameterBinderStrings.ParameterBindingFailed, "ParameterBindingFailed", bindError.Message); throw bindingException; } } while (false); bindingTracer.WriteLine( "BIND arg [{0}] to param [{1}] {2}", parameterValue, parameter.ParameterName, (result) ? "SUCCESSFUL" : "SKIPPED"); if (result) { // Add this name to the set of bound parameters... if (RecordBoundParameters) { this.CommandLineParameters.Add(parameter.ParameterName, parameterValue); } MshCommandRuntime cmdRuntime = this.Command.commandRuntime as MshCommandRuntime; if ((cmdRuntime != null) && (cmdRuntime.LogPipelineExecutionDetail || _isTranscribing) && (cmdRuntime.PipelineProcessor != null)) { string stringToPrint = null; try { // Unroll parameter value IEnumerable values = LanguagePrimitives.GetEnumerable(parameterValue); if (values != null) { var sb = new Text.StringBuilder(256); var sep = ""; foreach (var value in values) { sb.Append(sep); sep = ", "; sb.Append(value); // For better performance, avoid logging too much if (sb.Length > 256) { sb.Append(", ..."); break; } } stringToPrint = sb.ToString(); } else if (parameterValue != null) { stringToPrint = parameterValue.ToString(); } } catch (Exception e) // Catch-all OK, 3rd party callout { CommandProcessorBase.CheckForSevereException(e); } if (stringToPrint != null) { cmdRuntime.PipelineProcessor.LogExecutionParameterBinding(this.InvocationInfo, parameter.ParameterName, stringToPrint); } } } return result; } } // BindParameter /// /// This method ensures that if the parameter is mandatory, and AllowNull, AllowEmptyString, /// and/or AllowEmptyCollection is not specified, then argument is not null or empty. /// /// /// /// The argument token. /// /// /// /// The metadata for the parameter. /// /// /// /// The type of the argument to validate against. /// /// /// /// The value that will be bound to the parameter. /// /// /// /// If true, then elements of collections will be validated against the metadata. /// /// private void ValidateNullOrEmptyArgument( CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, Type argumentType, object parameterValue, bool recurseIntoCollections) { if (parameterValue == null && argumentType != typeof(bool?)) { if (!parameterMetadata.AllowsNullArgument) { bindingTracer.WriteLine("ERROR: Argument cannot be null"); ParameterBindingValidationException bindingException = new ParameterBindingValidationException( ErrorCategory.InvalidData, this.InvocationInfo, GetErrorExtent(parameter), parameterMetadata.Name, argumentType, null, ParameterBinderStrings.ParameterArgumentValidationErrorNullNotAllowed, "ParameterArgumentValidationErrorNullNotAllowed"); throw bindingException; } return; } if (argumentType == typeof(string)) { // Since the parameter is of type string, verify that either the argument // is not null and not empty or that the parameter can accept null or empty. string stringParamValue = parameterValue as string; Diagnostics.Assert( stringParamValue != null, "Type coercion should have already converted the argument value to a string"); if (stringParamValue.Length == 0 && !parameterMetadata.AllowsEmptyStringArgument) { bindingTracer.WriteLine("ERROR: Argument cannot be an empty string"); ParameterBindingValidationException bindingException = new ParameterBindingValidationException( ErrorCategory.InvalidData, this.InvocationInfo, GetErrorExtent(parameter), parameterMetadata.Name, parameterMetadata.Type, (parameterValue == null) ? null : parameterValue.GetType(), ParameterBinderStrings.ParameterArgumentValidationErrorEmptyStringNotAllowed, "ParameterArgumentValidationErrorEmptyStringNotAllowed"); throw bindingException; } return; } if (!recurseIntoCollections) return; switch (parameterMetadata.CollectionTypeInformation.ParameterCollectionType) { case ParameterCollectionType.IList: case ParameterCollectionType.Array: case ParameterCollectionType.ICollectionGeneric: break; default: // not a recognized collection, no need to recurse return; } // All these collection types implement IEnumerable IEnumerator ienum = LanguagePrimitives.GetEnumerator(parameterValue); Diagnostics.Assert( ienum != null, "Type coercion should have already converted the argument value to an IEnumerator"); // Ensure that each element abides by the metadata bool isEmpty = true; // Note - we explicitly don't pass the context here because we don't want // the overhead of the calls that check for stopping. while (ParserOps.MoveNext(null, null, ienum)) { object element = ParserOps.Current(null, ienum); isEmpty = false; ValidateNullOrEmptyArgument( parameter, parameterMetadata, parameterMetadata.CollectionTypeInformation.ElementType, element, false); } if (isEmpty && !parameterMetadata.AllowsEmptyCollectionArgument) { bindingTracer.WriteLine("ERROR: Argument cannot be an empty collection"); string errorId, resourceString; if (parameterMetadata.CollectionTypeInformation.ParameterCollectionType == ParameterCollectionType.Array) { errorId = "ParameterArgumentValidationErrorEmptyArrayNotAllowed"; resourceString = ParameterBinderStrings.ParameterArgumentValidationErrorEmptyArrayNotAllowed; } else { errorId = "ParameterArgumentValidationErrorEmptyCollectionNotAllowed"; resourceString = ParameterBinderStrings.ParameterArgumentValidationErrorEmptyCollectionNotAllowed; } ParameterBindingValidationException bindingException = new ParameterBindingValidationException( ErrorCategory.InvalidData, this.InvocationInfo, GetErrorExtent(parameter), parameterMetadata.Name, parameterMetadata.Type, (parameterValue == null) ? null : parameterValue.GetType(), resourceString, errorId); throw bindingException; } } private bool ShouldContinueUncoercedBind( CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, ParameterBindingFlags flags, ref object parameterValue) { bool isDefaultValue = (flags & ParameterBindingFlags.IsDefaultValue) != 0; Type parameterType = parameterMetadata.Type; if (parameterValue == null) { return parameterType == null || isDefaultValue || (!parameterType.GetTypeInfo().IsValueType && parameterType != typeof(string)); } // If the types are not a direct match, or // the value type is not a subclass of the parameter type, or // the value is an PSObject and the parameter type is not object and // the PSObject.BaseObject type does not match or is not a subclass // of the parameter type, or // the value must be encoded into a collection but it is not of the correct element type // // then return false if (parameterType.IsInstanceOfType(parameterValue)) { return true; } var psobj = parameterValue as PSObject; if (psobj != null && !psobj.immediateBaseObjectIsEmpty) { // See if the base object is of the same type or // as subclass of the parameter parameterValue = psobj.BaseObject; if (parameterType.IsInstanceOfType(parameterValue)) { return true; } } // Maybe the parameter type is a collection and the value needs to // be encoded if (parameterMetadata.CollectionTypeInformation.ParameterCollectionType != ParameterCollectionType.NotCollection) { // See if the value needs to be encoded in a collection bool coercionRequired; object encodedValue = EncodeCollection( parameter, parameterMetadata.Name, parameterMetadata.CollectionTypeInformation, parameterType, parameterValue, false, out coercionRequired); if (encodedValue == null || coercionRequired) { // Don't attempt the bind because the // PSObject BaseObject is not of the correct // type for the parameter. return false; } parameterValue = encodedValue; return true; } return false; } #endregion Parameter binding /// /// The invocation information for the code that is being bound /// private InvocationInfo _invocationInfo; internal InvocationInfo InvocationInfo { get { return _invocationInfo; } } /// /// The context of the currently running engine /// private ExecutionContext _context; internal ExecutionContext Context { get { return _context; } } /// /// An instance of InternalCommand that the binder is binding to. /// private InternalCommand _command; internal InternalCommand Command { get { return _command; } } /// /// The engine APIs that need to be passed the attributes when evaluated. /// private EngineIntrinsics _engine; private bool _isTranscribing; #endregion internal members #region Private helpers /// /// Coerces the argument type to the parameter value type as needed. /// /// /// /// The argument as was specified by the command line. /// /// /// /// The name of the parameter that the coercion is taking place to bind to. It is /// used only for error reporting. /// /// /// /// The type to coerce the value to. /// /// /// /// The information about the collection type, like element type, etc. /// /// /// /// The current value of the argument. /// /// /// /// The value of the argument in the type of the parameter. /// /// /// /// If or is null. /// /// /// /// If the argument value is missing and the parameter is not a bool or SwitchParameter. /// or /// If the argument value could not be converted to the parameter type. /// /// private object CoerceTypeAsNeeded( CommandParameterInternal argument, string parameterName, Type toType, ParameterCollectionTypeInformation collectionTypeInfo, object currentValue) { if (argument == null) { throw PSTraceSource.NewArgumentNullException("argument"); } if (toType == null) { throw PSTraceSource.NewArgumentNullException("toType"); } // Construct the collection type information if it wasn't passed in. if (collectionTypeInfo == null) { collectionTypeInfo = new ParameterCollectionTypeInformation(toType); } object result = currentValue; using (bindingTracer.TraceScope( "COERCE arg to [{0}]", toType)) { Type argumentType = null; try { do // false loop { if (IsNullParameterValue(currentValue)) { result = HandleNullParameterForSpecialTypes(argument, parameterName, toType, currentValue); break; } // Do the coercion argumentType = currentValue.GetType(); // If the types are identical (or can be cast directly,) then no coercion // needs to be done if (toType.IsAssignableFrom(argumentType)) { bindingTracer.WriteLine( "Parameter and arg types the same, no coercion is needed."); result = currentValue; break; } bindingTracer.WriteLine("Trying to convert argument value from {0} to {1}", argumentType, toType); // Likewise shortcircuit the case were the user has asked for a shell object. // He always gets a shell object regardless of the actual type of the object. if (toType == typeof(PSObject)) { // It may be the case that we're binding the current pipeline object // as is to a PSObject parameter in which case, we want to make // sure that we're using the same shell object instead of creating an // alias object. if (_command != null && currentValue == _command.CurrentPipelineObject.BaseObject) { currentValue = _command.CurrentPipelineObject; } bindingTracer.WriteLine( "The parameter is of type [{0}] and the argument is an PSObject, so the parameter value is the argument value wrapped into an PSObject.", toType); result = LanguagePrimitives.AsPSObjectOrNull(currentValue); break; } // NTRAID#Windows OS Bugs-1064175-2004/02/28-JeffJon // If we have an PSObject with null base and we are trying to // convert to a string, then we need to use null instead of // calling LanguagePrimitives.ConvertTo as that will return // String.Empty. if (toType == typeof(string) && argumentType == typeof(PSObject)) { PSObject currentValueAsPSObject = (PSObject)currentValue; if (currentValueAsPSObject == AutomationNull.Value) { bindingTracer.WriteLine( "CONVERT a null PSObject to a null string."); result = null; break; } } // NTRAID#Windows OS Bugs - - Nana // If we have a boolean, we have to ensure that it can // only take parameters of type boolean or numbers with // 0 indicating false and everything else indicating true // Anything else passed should be reported as an error if (toType == typeof(bool) || toType == typeof(SwitchParameter) || toType == typeof(Nullable)) { Type boType = null; if (argumentType == typeof(PSObject)) { // Unwrap the PSObject at this point... PSObject currentValueAsPSObject = (PSObject)currentValue; currentValue = currentValueAsPSObject.BaseObject; if (currentValue is SwitchParameter) { currentValue = ((SwitchParameter)currentValue).IsPresent; } boType = currentValue.GetType(); } else { boType = argumentType; } if (boType == typeof(bool)) { if (LanguagePrimitives.IsBooleanType(toType)) result = ParserOps.BoolToObject((bool)currentValue); else result = new SwitchParameter((bool)currentValue); } else if (boType == typeof(Int32)) { if ((Int32)LanguagePrimitives.ConvertTo(currentValue, typeof(Int32), CultureInfo.InvariantCulture) != 0) { if (LanguagePrimitives.IsBooleanType(toType)) result = ParserOps.BoolToObject(true); else result = new SwitchParameter(true); } else { if (LanguagePrimitives.IsBooleanType(toType)) result = ParserOps.BoolToObject(false); else result = new SwitchParameter(false); } } else if (LanguagePrimitives.IsNumeric(boType.GetTypeCode())) { Double currentValueAsDouble = (Double)LanguagePrimitives.ConvertTo( currentValue, typeof(Double), CultureInfo.InvariantCulture); if (currentValueAsDouble != 0) { if (LanguagePrimitives.IsBooleanType(toType)) result = ParserOps.BoolToObject(true); else result = new SwitchParameter(true); } else { if (LanguagePrimitives.IsBooleanType(toType)) result = ParserOps.BoolToObject(false); else result = new SwitchParameter(false); } } else { // Invalid types which cannot be associated with a bool // Since there is a catch block which appropriately // handles this situation we just throw an exception here //throw new PSInvalidCastException(); ParameterBindingException pbe = new ParameterBindingException( ErrorCategory.InvalidArgument, this.InvocationInfo, GetErrorExtent(argument), parameterName, toType, argumentType, ParameterBinderStrings.CannotConvertArgument, "CannotConvertArgument", boType, ""); throw pbe; } break; } // NTRAID#Windows OS Bugs-1009284-2004/05/05-JeffJon // Need to handle other collection types here as well // Before attempting to encode a collection, we check if we can convert the argument directly via // a restricted set of conversions (as the general conversion mechanism is far too general, it may // succeed where it shouldn't. We don't bother checking arrays parameters because they won't have // conversions we're allowing. if (collectionTypeInfo.ParameterCollectionType == ParameterCollectionType.ICollectionGeneric || collectionTypeInfo.ParameterCollectionType == ParameterCollectionType.IList) { object currentValueToConvert = PSObject.Base(currentValue); if (currentValueToConvert != null) { ConversionRank rank = LanguagePrimitives.GetConversionRank(currentValueToConvert.GetType(), toType); if (rank == ConversionRank.Constructor || rank == ConversionRank.ImplicitCast || rank == ConversionRank.ExplicitCast) { // This conversion will fail in the common case, and when it does, we'll use EncodeCollection below. if (LanguagePrimitives.TryConvertTo(currentValue, toType, CultureInfo.CurrentCulture, out result)) { break; } } } } if (collectionTypeInfo.ParameterCollectionType != ParameterCollectionType.NotCollection) { bindingTracer.WriteLine( "ENCODING arg into collection"); bool ignored = false; result = EncodeCollection( argument, parameterName, collectionTypeInfo, toType, currentValue, (collectionTypeInfo.ElementType != null), out ignored); break; } else { // Check to see if the current value is a collection. If so, fail because // we don't want to attempt to bind a collection to a scalar unless // the parameter type is Object or PSObject or enum. TypeInfo toTypeInfo = toType.GetTypeInfo(); if (GetIList(currentValue) != null && toType != typeof(Object) && toType != typeof(PSObject) && toType != typeof(PSListModifier) && (!toTypeInfo.IsGenericType || toTypeInfo.GetGenericTypeDefinition() != typeof(PSListModifier<>)) && (!toTypeInfo.IsGenericType || toTypeInfo.GetGenericTypeDefinition() != typeof(FlagsExpression<>)) && !toTypeInfo.IsEnum) { throw new NotSupportedException(); } } bindingTracer.WriteLine( "CONVERT arg type to param type using LanguagePrimitives.ConvertTo"); // If we are in constrained language mode and the target command is trusted, // allow type conversion to the target command's parameter type. // Don't allow Hashtable-to-Object conversion (PSObject and IDictionary), though, // as those can lead to property setters that probably aren't expected. bool changeLanguageModeForTrustedCommand = false; if (_context.LanguageMode == PSLanguageMode.ConstrainedLanguage) { var basedObject = PSObject.Base(currentValue); var supportsPropertyConversion = basedObject is PSObject; var supportsIDictionaryConversion = (basedObject != null) && (typeof(IDictionary).IsAssignableFrom(basedObject.GetType())); changeLanguageModeForTrustedCommand = (this.Command.CommandInfo.DefiningLanguageMode == PSLanguageMode.FullLanguage) && (!supportsPropertyConversion) && (!supportsIDictionaryConversion); } try { if (changeLanguageModeForTrustedCommand) { _context.LanguageMode = PSLanguageMode.FullLanguage; } result = LanguagePrimitives.ConvertTo(currentValue, toType, CultureInfo.CurrentCulture); } finally { if (changeLanguageModeForTrustedCommand) { _context.LanguageMode = PSLanguageMode.ConstrainedLanguage; } } bindingTracer.WriteLine( "CONVERT SUCCESSFUL using LanguagePrimitives.ConvertTo: [{0}]", (result == null) ? "null" : result.ToString()); } while (false); } catch (NotSupportedException notSupported) { bindingTracer.TraceError( "ERROR: COERCE FAILED: arg [{0}] could not be converted to the parameter type [{1}]", result ?? "null", toType); ParameterBindingException pbe = new ParameterBindingException( notSupported, ErrorCategory.InvalidArgument, this.InvocationInfo, GetErrorExtent(argument), parameterName, toType, argumentType, ParameterBinderStrings.CannotConvertArgument, "CannotConvertArgument", result ?? "null", notSupported.Message); throw pbe; } catch (PSInvalidCastException invalidCast) { bindingTracer.TraceError( "ERROR: COERCE FAILED: arg [{0}] could not be converted to the parameter type [{1}]", result ?? "null", toType); ParameterBindingException pbe = new ParameterBindingException( invalidCast, ErrorCategory.InvalidArgument, this.InvocationInfo, GetErrorExtent(argument), parameterName, toType, argumentType, ParameterBinderStrings.CannotConvertArgumentNoMessage, "CannotConvertArgumentNoMessage", invalidCast.Message); throw pbe; } } // TraceScope return result; } // CoerceTypeAsNeeded private static bool IsNullParameterValue(object currentValue) { bool result = false; if (currentValue == null || currentValue == AutomationNull.Value || currentValue == UnboundParameter.Value) { result = true; } return result; } private object HandleNullParameterForSpecialTypes( CommandParameterInternal argument, string parameterName, Type toType, object currentValue) { object result = null; // The presence of the name switch for SwitchParameters (and not booleans) // makes them true. if (toType == typeof(bool)) { bindingTracer.WriteLine( "ERROR: No argument is specified for parameter and parameter type is BOOL"); ParameterBindingException exception = new ParameterBindingException( ErrorCategory.InvalidArgument, this.InvocationInfo, GetErrorExtent(argument), parameterName, toType, null, ParameterBinderStrings.ParameterArgumentValidationErrorNullNotAllowed, "ParameterArgumentValidationErrorNullNotAllowed", ""); throw exception; } else if (toType == typeof(SwitchParameter)) { bindingTracer.WriteLine( "Arg is null or not present, parameter type is SWITCHPARAMTER, value is true."); result = SwitchParameter.Present; } else if (currentValue == UnboundParameter.Value) { bindingTracer.TraceError( "ERROR: No argument was specified for the parameter and the parameter is not of type bool"); ParameterBindingException exception = new ParameterBindingException( ErrorCategory.InvalidArgument, this.InvocationInfo, GetParameterErrorExtent(argument), parameterName, toType, null, ParameterBinderStrings.MissingArgument, "MissingArgument"); throw exception; } else { bindingTracer.WriteLine( "Arg is null, parameter type not bool or SwitchParameter, value is null."); result = null; } return result; } /// /// Takes the current value specified and converts or adds it to /// a collection of the appropriate type. /// /// /// /// The argument the current value comes from. Used for error reporting. /// /// /// /// The name of the parameter. /// /// /// /// The collection type information to which the current value will be /// encoded. /// /// /// /// The type the current value will be converted to. /// /// /// /// The value to be encoded. /// /// /// /// If true, the element will be coerced into the appropriate type /// for the collection. If false, and the element isn't of the appropriate /// type then the out parameter will /// be true. /// /// /// /// This out parameter will be true if /// is true and the value could not be encoded into the collection because it /// requires coercion to the element type. /// /// /// /// A collection of the appropriate type containing the specified value. /// /// /// /// If is a collection and one of its values /// cannot be coerced into the appropriate type. /// or /// A collection of the appropriate /// could not be created. /// /// [SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode")] [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Consider Simplyfing it")] private object EncodeCollection( CommandParameterInternal argument, string parameterName, ParameterCollectionTypeInformation collectionTypeInformation, Type toType, object currentValue, bool coerceElementTypeIfNeeded, out bool coercionRequired) { object result = null; coercionRequired = false; do // false loop { bindingTracer.WriteLine( "Binding collection parameter {0}: argument type [{1}], parameter type [{2}], collection type {3}, element type [{4}], {5}", parameterName, (null == currentValue) ? "null" : currentValue.GetType().Name, toType, collectionTypeInformation.ParameterCollectionType, collectionTypeInformation.ElementType, coerceElementTypeIfNeeded ? "coerceElementType" : "no coerceElementType"); if (currentValue == null) { break; } int numberOfElements = 1; Type collectionElementType = collectionTypeInformation.ElementType; // If the current value is an IList, get the count of the elements // Or if it is an PSObject which wraps an IList IList currentValueAsIList = GetIList(currentValue); if (currentValueAsIList != null) { numberOfElements = currentValueAsIList.Count; s_tracer.WriteLine("current value is an IList with {0} elements", numberOfElements); bindingTracer.WriteLine( "Arg is IList with {0} elements", numberOfElements); } object resultCollection = null; IList resultAsIList = null; MethodInfo addMethod = null; // We must special case System.Array to be like an object array since it is an // abstract base class and cannot be created in the IList path below. bool isSystemDotArray = (toType == typeof(System.Array)); if (collectionTypeInformation.ParameterCollectionType == ParameterCollectionType.Array || isSystemDotArray) { if (isSystemDotArray) { // If System.Array is the type we are encoding to, then // the element type should be System.Object. collectionElementType = typeof(System.Object); } bindingTracer.WriteLine( "Creating array with element type [{0}] and {1} elements", collectionElementType, numberOfElements); // Since the destination is an array we will have to create an array // of the element type with the correct length resultCollection = resultAsIList = (IList)Array.CreateInstance( collectionElementType, numberOfElements); } else if (collectionTypeInformation.ParameterCollectionType == ParameterCollectionType.IList || collectionTypeInformation.ParameterCollectionType == ParameterCollectionType.ICollectionGeneric) { bindingTracer.WriteLine( "Creating collection [{0}]", toType); // Create an instance of the parameter type // NTRAID#Windows Out Of Band Releases-906820-2005/09/01 // This code previously used the ctor(int) ctor form. // System.Collections.ObjectModel.Collection does not // support this ctor form. More generally, there is no // guarantee that the ctor parameter has the semantic // meaning of "likely list size". Blindly calling the // parameterless ctor is also risky, but seems like a // safer choice. bool errorOccurred = false; Exception error = null; try { resultCollection = Activator.CreateInstance( toType, 0, null, new object[] { }, System.Globalization.CultureInfo.InvariantCulture); if (collectionTypeInformation.ParameterCollectionType == ParameterCollectionType.IList) resultAsIList = (IList)resultCollection; else { Diagnostics.Assert( collectionTypeInformation.ParameterCollectionType == ParameterCollectionType.ICollectionGeneric, "invalid collection type" ); // extract the ICollection::Add(T) method const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance; Type elementType = collectionTypeInformation.ElementType; Diagnostics.Assert(null != elementType, "null ElementType"); Exception getMethodError = null; try { addMethod = toType.GetMethod("Add", bindingFlags, null, new Type[1] { elementType }, null); } catch (AmbiguousMatchException e) { bindingTracer.WriteLine("Ambiguous match to Add(T) for type {0}: {1}", toType.FullName, e.Message); getMethodError = e; } catch (ArgumentException e) { bindingTracer.WriteLine( "ArgumentException matching Add(T) for type {0}: {1}", toType.FullName, e.Message); getMethodError = e; } if (null == addMethod) { ParameterBindingException bindingException = new ParameterBindingException( getMethodError, ErrorCategory.InvalidArgument, this.InvocationInfo, GetErrorExtent(argument), parameterName, toType, currentValue.GetType(), ParameterBinderStrings.CannotExtractAddMethod, "CannotExtractAddMethod", (getMethodError == null) ? "" : getMethodError.Message); throw bindingException; } } } catch (ArgumentException argException) { errorOccurred = true; error = argException; } catch (NotSupportedException notSupported) { errorOccurred = true; error = notSupported; } catch (TargetInvocationException targetInvocationException) { errorOccurred = true; error = targetInvocationException; } #if !CORECLR // MethodAccessException not in CoreCLR catch (MethodAccessException methodAccessException) { errorOccurred = true; error = methodAccessException; } #endif catch (MemberAccessException memberAccessException) { errorOccurred = true; error = memberAccessException; } catch (System.Runtime.InteropServices.InvalidComObjectException invalidComObject) { errorOccurred = true; error = invalidComObject; } catch (System.Runtime.InteropServices.COMException comException) { errorOccurred = true; error = comException; } catch (TypeLoadException typeLoadException) { errorOccurred = true; error = typeLoadException; } if (errorOccurred) { // Throw a ParameterBindingException ParameterBindingException bindingException = new ParameterBindingException( error, ErrorCategory.InvalidArgument, this.InvocationInfo, GetErrorExtent(argument), parameterName, toType, currentValue.GetType(), ParameterBinderStrings.CannotConvertArgument, "CannotConvertArgument", "null", error.Message); throw bindingException; } } else { Diagnostics.Assert( false, "This method should not be called for a parameter that is not a collection"); break; } // NTRAID#Windows OS Bugs-966440-2004/05/05-JeffJon // This coercion can only go to a collection type. It cannot take a // collection type and coerce it into a scalar type. // Now that the new collection instance has been created, coerce each element type // of the current value to the element type of the property value and add it if (currentValueAsIList != null) { // Since arrays don't support the Add method, we must use indexing // to set the value. int arrayIndex = 0; bindingTracer.WriteLine( "Argument type {0} is IList", currentValue.GetType()); foreach (object valueElement in currentValueAsIList) { object currentValueElement = PSObject.Base(valueElement); if (coerceElementTypeIfNeeded) { bindingTracer.WriteLine( "COERCE collection element from type {0} to type {1}", (null == valueElement) ? "null" : valueElement.GetType().Name, collectionElementType); // Coerce the element to the appropriate type. // Note, this may be recursive if the element is a // collection itself. currentValueElement = CoerceTypeAsNeeded( argument, parameterName, collectionElementType, null, valueElement); } else if (null != collectionElementType && null != currentValueElement) { Type currentValueElementType = currentValueElement.GetType(); Type desiredElementType = collectionElementType; if (currentValueElementType != desiredElementType && !currentValueElementType.IsSubclassOf(desiredElementType)) { bindingTracer.WriteLine( "COERCION REQUIRED: Did not attempt to coerce collection element from type {0} to type {1}", (null == valueElement) ? "null" : valueElement.GetType().Name, collectionElementType); coercionRequired = true; break; } } // Add() will fail with ArgumentException // for Collection with the wrong type. try { if (collectionTypeInformation.ParameterCollectionType == ParameterCollectionType.Array || isSystemDotArray) { bindingTracer.WriteLine( "Adding element of type {0} to array position {1}", (null == currentValueElement) ? "null" : currentValueElement.GetType().Name, arrayIndex); resultAsIList[arrayIndex++] = currentValueElement; } else if (collectionTypeInformation.ParameterCollectionType == ParameterCollectionType.IList) { bindingTracer.WriteLine( "Adding element of type {0} via IList.Add", (null == currentValueElement) ? "null" : currentValueElement.GetType().Name); resultAsIList.Add(currentValueElement); } else { bindingTracer.WriteLine( "Adding element of type {0} via ICollection::Add()", (null == currentValueElement) ? "null" : currentValueElement.GetType().Name); addMethod.Invoke(resultCollection, new object[1] { currentValueElement }); } } catch (Exception error) // OK, we catch all here by design { CommandProcessorBase.CheckForSevereException(error); // The inner exception to TargetInvocationException // (if present) has a better Message if (error is TargetInvocationException && null != error.InnerException) { error = error.InnerException; } ParameterBindingException bindingException = new ParameterBindingException( error, ErrorCategory.InvalidArgument, this.InvocationInfo, GetErrorExtent(argument), parameterName, toType, (currentValueElement == null) ? null : currentValueElement.GetType(), ParameterBinderStrings.CannotConvertArgument, "CannotConvertArgument", currentValueElement ?? "null", error.Message); throw bindingException; } } } else // (currentValueAsIList == null) { bindingTracer.WriteLine( "Argument type {0} is not IList, treating this as scalar", currentValue.GetType().Name); if (collectionElementType != null) { if (coerceElementTypeIfNeeded) { bindingTracer.WriteLine( "Coercing scalar arg value to type {1}", collectionElementType); // Coerce the scalar type into the collection currentValue = CoerceTypeAsNeeded( argument, parameterName, collectionElementType, null, currentValue); } else { Type currentValueElementType = currentValue.GetType(); Type desiredElementType = collectionElementType; if (currentValueElementType != desiredElementType && !currentValueElementType.IsSubclassOf(desiredElementType)) { bindingTracer.WriteLine( "COERCION REQUIRED: Did not coerce scalar arg value to type {1}", collectionElementType); coercionRequired = true; break; } } } // Add() will fail with ArgumentException // for Collection with the wrong type. try { if (collectionTypeInformation.ParameterCollectionType == ParameterCollectionType.Array || isSystemDotArray) { bindingTracer.WriteLine( "Adding scalar element of type {0} to array position {1}", (null == currentValue) ? "null" : currentValue.GetType().Name, 0); resultAsIList[0] = currentValue; } else if (collectionTypeInformation.ParameterCollectionType == ParameterCollectionType.IList) { bindingTracer.WriteLine( "Adding scalar element of type {0} via IList.Add", (null == currentValue) ? "null" : currentValue.GetType().Name); resultAsIList.Add(currentValue); } else { bindingTracer.WriteLine( "Adding scalar element of type {0} via ICollection::Add()", (null == currentValue) ? "null" : currentValue.GetType().Name); addMethod.Invoke(resultCollection, new object[1] { currentValue }); } } catch (Exception error) // OK, we catch all here by design { CommandProcessorBase.CheckForSevereException(error); // The inner exception to TargetInvocationException // (if present) has a better Message if (error is TargetInvocationException && null != error.InnerException) { error = error.InnerException; } ParameterBindingException bindingException = new ParameterBindingException( error, ErrorCategory.InvalidArgument, this.InvocationInfo, GetErrorExtent(argument), parameterName, toType, (currentValue == null) ? null : currentValue.GetType(), ParameterBinderStrings.CannotConvertArgument, "CannotConvertArgument", currentValue ?? "null", error.Message); throw bindingException; } } // (currentValueAsIList == null) if (!coercionRequired) { result = resultCollection; } } while (false); return result; } // EncodeCollection internal static IList GetIList(object value) { var baseObj = PSObject.Base(value); var result = baseObj as IList; if (result != null) { // Reference comparison to determine if 'value' is a PSObject s_tracer.WriteLine(baseObj == value ? "argument is IList" : "argument is PSObject with BaseObject as IList"); } return result; } // GetIList protected IScriptExtent GetErrorExtent(CommandParameterInternal cpi) { var result = cpi.ErrorExtent; if (result == PositionUtilities.EmptyExtent) result = InvocationInfo.ScriptPosition; // Can't use this assertion - we don't have useful positions when invoked via PowerShell API //Diagnostics.Assert(result != PositionUtilities.EmptyExtent, "We are missing a valid position somewhere"); return result; } protected IScriptExtent GetParameterErrorExtent(CommandParameterInternal cpi) { var result = cpi.ParameterExtent; if (result == PositionUtilities.EmptyExtent) result = InvocationInfo.ScriptPosition; // Can't use this assertion - we don't have useful positions when invoked via PowerShell API //Diagnostics.Assert(result != PositionUtilities.EmptyExtent, "We are missing a valid position somewhere"); return result; } #endregion private helpers } // ParameterBinderBase /// /// Represents an unbound parameter object in the engine. It's similar to /// AutomationNull.Value however AutomationNull.Value as a parameter value /// is used to say "use the default value for this object" whereas UnboundParameter /// says "this parameter is unbound, use the default only if the target type /// supports permits this." /// /// It's a singleton class. Sealed to prevent subclassing internal sealed class UnboundParameter { #region ctor // Private constructor private UnboundParameter() { } #endregion ctor #region private_members // Private member for Value. #endregion private_members #region public_property /// /// Represents an object of the same class (singleton class). /// internal static object Value { get; } = new object(); #endregion public_property } // This class is a thin wrapper around Dictionary, but adds a member BoundPositionally. // $PSBoundParameters used to be a PSObject with an instance member, but that was quite // slow for a relatively common case, this class should work identically, except maybe // if somebody depends on the typename being the same. internal sealed class PSBoundParametersDictionary : Dictionary { internal PSBoundParametersDictionary() : base(StringComparer.OrdinalIgnoreCase) { BoundPositionally = new List(); ImplicitUsingParameters = s_emptyUsingParameters; } private static readonly IDictionary s_emptyUsingParameters = new ReadOnlyDictionary(new Dictionary()); public List BoundPositionally { get; private set; } internal IDictionary ImplicitUsingParameters { get; set; } } internal sealed class CommandLineParameters { private readonly PSBoundParametersDictionary _dictionary = new PSBoundParametersDictionary(); internal bool ContainsKey(string name) { Dbg.Assert(!string.IsNullOrEmpty(name), "parameter names should not be empty"); return _dictionary.ContainsKey(name); } internal void Add(string name, object value) { Dbg.Assert(!string.IsNullOrEmpty(name), "parameter names should not be empty"); _dictionary[name] = value; } internal void MarkAsBoundPositionally(string name) { Dbg.Assert(!string.IsNullOrEmpty(name), "parameter names should not be empty"); _dictionary.BoundPositionally.Add(name); } internal void SetPSBoundParametersVariable(ExecutionContext context) { Dbg.Assert(context != null, "caller should verify that context != null"); context.SetVariable(SpecialVariables.PSBoundParametersVarPath, _dictionary); } internal void SetImplicitUsingParameters(object obj) { _dictionary.ImplicitUsingParameters = PSObject.Base(obj) as IDictionary; if (_dictionary.ImplicitUsingParameters == null) { // Handle downlevel V4 case where using parameters are passed as an array list. IList implictArrayUsingParameters = PSObject.Base(obj) as IList; if ((implictArrayUsingParameters != null) && (implictArrayUsingParameters.Count > 0)) { // Convert array to hash table. _dictionary.ImplicitUsingParameters = new Hashtable(); for (int index = 0; index < implictArrayUsingParameters.Count; index++) { _dictionary.ImplicitUsingParameters.Add(index, implictArrayUsingParameters[index]); } } } } internal IDictionary GetImplicitUsingParameters() { return _dictionary.ImplicitUsingParameters; } internal object GetValueToBindToPSBoundParameters() { return _dictionary; } internal void UpdateInvocationInfo(InvocationInfo invocationInfo) { Dbg.Assert(invocationInfo != null, "caller should verify that invocationInfo != null"); invocationInfo.BoundParameters = _dictionary; } internal HashSet CopyBoundPositionalParameters() { HashSet result = new HashSet(StringComparer.CurrentCultureIgnoreCase); foreach (string item in _dictionary.BoundPositionally) { result.Add(item); } return result; } } } // namespace System.Management.Automation