Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
CompiledCommandParameter.cs684 linesDownload Raw Back to engine
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.Collections.Generic;6using System.Collections.ObjectModel;7using System.Linq;8using System.Reflection;9 10namespace System.Management.Automation11{12    /// <summary>13    /// The metadata associated with a parameter.14    /// </summary>15    internal class CompiledCommandParameter16    {17        #region ctor18 19        /// <summary>20        /// Constructs an instance of the CompiledCommandAttribute using the specified21        /// runtime-defined parameter.22        /// </summary>23        /// <param name="runtimeDefinedParameter">24        /// A runtime defined parameter that contains the definition of the parameter and its metadata.25        /// </param>26        /// <param name="processingDynamicParameters">27        /// True if dynamic parameters are being processed, or false otherwise.28        /// </param>29        /// <exception cref="ArgumentNullException">30        /// If <paramref name="runtimeDefinedParameter"/> is null.31        /// </exception>32        /// <exception cref="MetadataException">33        /// If the parameter has more than one <see cref="ParameterAttribute">ParameterAttribute</see>34        /// that defines the same parameter-set name.35        /// </exception>36        internal CompiledCommandParameter(RuntimeDefinedParameter runtimeDefinedParameter, bool processingDynamicParameters)37        {38            if (runtimeDefinedParameter == null)39            {40                throw PSTraceSource.NewArgumentNullException(nameof(runtimeDefinedParameter));41            }42 43            this.Name = runtimeDefinedParameter.Name;44            this.Type = runtimeDefinedParameter.ParameterType;45            this.IsDynamic = processingDynamicParameters;46 47            this.CollectionTypeInformation = new ParameterCollectionTypeInformation(runtimeDefinedParameter.ParameterType);48 49            this.CompiledAttributes = new Collection<Attribute>();50 51            this.ParameterSetData = new Dictionary<string, ParameterSetSpecificMetadata>(StringComparer.OrdinalIgnoreCase);52 53            Collection<ValidateArgumentsAttribute> validationAttributes = null;54            Collection<ArgumentTransformationAttribute> argTransformationAttributes = null;55            string[] aliases = null;56 57            // First, process attributes that aren't type conversions58            foreach (Attribute attribute in runtimeDefinedParameter.Attributes)59            {60                if (processingDynamicParameters)61                {62                    // When processing dynamic parameters, the attribute list may contain experimental attributes63                    // and disabled parameter attributes. We should ignore those attributes.64                    // When processing non-dynamic parameters, the experimental attributes and disabled parameter65                    // attributes have already been filtered out when constructing the RuntimeDefinedParameter.66                    if (attribute is ExperimentalAttribute || attribute is ParameterAttribute param && param.ToHide)67                    {68                        continue;69                    }70                }71 72                if (attribute is not ArgumentTypeConverterAttribute)73                {74                    ProcessAttribute(runtimeDefinedParameter.Name, attribute, ref validationAttributes, ref argTransformationAttributes, ref aliases);75                }76            }77 78            // If this is a PSCredential type and they haven't added any argument transformation attributes,79            // add one for credential transformation80            if ((this.Type == typeof(PSCredential)) && argTransformationAttributes == null)81            {82                ProcessAttribute(runtimeDefinedParameter.Name, new CredentialAttribute(), ref validationAttributes, ref argTransformationAttributes, ref aliases);83            }84 85            // Now process type converters86            foreach (var attribute in runtimeDefinedParameter.Attributes.OfType<ArgumentTypeConverterAttribute>())87            {88                ProcessAttribute(runtimeDefinedParameter.Name, attribute, ref validationAttributes, ref argTransformationAttributes, ref aliases);89            }90 91            this.ValidationAttributes = validationAttributes == null92                ? Array.Empty<ValidateArgumentsAttribute>()93                : validationAttributes.ToArray();94            this.ArgumentTransformationAttributes = argTransformationAttributes == null95                ? Array.Empty<ArgumentTransformationAttribute>()96                : argTransformationAttributes.ToArray();97            this.Aliases = aliases == null98                ? Array.Empty<string>()99                : aliases.ToArray();100        }101 102        /// <summary>103        /// Constructs an instance of the CompiledCommandAttribute using the reflection information retrieved104        /// from the enclosing bindable object type.105        /// </summary>106        /// <param name="member">107        /// The member information for the parameter108        /// </param>109        /// <param name="processingDynamicParameters">110        /// True if dynamic parameters are being processed, or false otherwise.111        /// </param>112        /// <exception cref="ArgumentNullException">113        /// If <paramref name="member"/> is null.114        /// </exception>115        /// <exception cref="ArgumentException">116        /// If <paramref name="member"/> is not a field or a property.117        /// </exception>118        /// <exception cref="MetadataException">119        /// If the member has more than one <see cref="ParameterAttribute">ParameterAttribute</see>120        /// that defines the same parameter-set name.121        /// </exception>122        internal CompiledCommandParameter(MemberInfo member, bool processingDynamicParameters)123        {124            if (member == null)125            {126                throw PSTraceSource.NewArgumentNullException(nameof(member));127            }128 129            this.Name = member.Name;130            this.DeclaringType = member.DeclaringType;131            this.IsDynamic = processingDynamicParameters;132 133            var propertyInfo = member as PropertyInfo;134            if (propertyInfo != null)135            {136                this.Type = propertyInfo.PropertyType;137            }138            else139            {140                var fieldInfo = member as FieldInfo;141                if (fieldInfo != null)142                {143                    this.Type = fieldInfo.FieldType;144                }145                else146                {147                    ArgumentException e =148                    PSTraceSource.NewArgumentException(149                        nameof(member),150                        DiscoveryExceptions.CompiledCommandParameterMemberMustBeFieldOrProperty);151 152                    throw e;153                }154            }155 156            this.CollectionTypeInformation = new ParameterCollectionTypeInformation(this.Type);157            this.CompiledAttributes = new Collection<Attribute>();158            this.ParameterSetData = new Dictionary<string, ParameterSetSpecificMetadata>(StringComparer.OrdinalIgnoreCase);159 160            // We do not want to get the inherited custom attributes, only the attributes exposed161            // directly on the member162 163            var memberAttributes = member.GetCustomAttributes(false);164 165            Collection<ValidateArgumentsAttribute> validationAttributes = null;166            Collection<ArgumentTransformationAttribute> argTransformationAttributes = null;167            string[] aliases = null;168 169            foreach (Attribute attr in memberAttributes)170            {171                switch (attr)172                {173                    case ExperimentalAttribute _:174                    case ParameterAttribute param when param.ToHide:175                        break;176                    default:177                        ProcessAttribute(member.Name, attr, ref validationAttributes, ref argTransformationAttributes, ref aliases);178                        break;179                }180            }181 182            this.ValidationAttributes = validationAttributes == null183                ? Array.Empty<ValidateArgumentsAttribute>()184                : validationAttributes.ToArray();185            this.ArgumentTransformationAttributes = argTransformationAttributes == null186                ? Array.Empty<ArgumentTransformationAttribute>()187                : argTransformationAttributes.ToArray();188            this.Aliases = aliases ?? Array.Empty<string>();189        }190 191        #endregion ctor192 193        /// <summary>194        /// Gets the name of the parameter.195        /// </summary>196        internal string Name { get; }197 198        /// <summary>199        /// The PSTypeName from a PSTypeNameAttribute.200        /// </summary>201        internal string PSTypeName { get; private set; }202 203        /// <summary>204        /// Gets the Type information of the attribute.205        /// </summary>206        internal Type Type { get; }207 208        /// <summary>209        /// Gets the Type information of the attribute.210        /// </summary>211        internal Type DeclaringType { get; }212 213        /// <summary>214        /// Gets whether the parameter is a dynamic parameter or not.215        /// </summary>216        internal bool IsDynamic { get; }217 218        /// <summary>219        /// Gets the parameter collection type information.220        /// </summary>221        internal ParameterCollectionTypeInformation CollectionTypeInformation { get; }222 223        /// <summary>224        /// A collection of the attributes found on the member. The attributes have been compiled into225        /// a format that easier to digest by the metadata processor.226        /// </summary>227        internal Collection<Attribute> CompiledAttributes { get; }228 229        /// <summary>230        /// Gets the collection of data generation attributes on this parameter.231        /// </summary>232        internal ArgumentTransformationAttribute[] ArgumentTransformationAttributes { get; }233 234        /// <summary>235        /// Gets the collection of data validation attributes on this parameter.236        /// </summary>237        internal ValidateArgumentsAttribute[] ValidationAttributes { get; }238 239        /// <summary>240        /// Get and private set the obsolete attribute on this parameter.241        /// </summary>242        internal ObsoleteAttribute ObsoleteAttribute { get; private set; }243 244        /// <summary>245        /// If true, null can be bound to the parameter even if the parameter is mandatory.246        /// </summary>247        internal bool AllowsNullArgument { get; private set; }248 249        /// <summary>250        /// If true, null cannot be bound to the parameter (ValidateNotNull251        /// and/or ValidateNotNullOrEmpty has been specified).252        /// </summary>253        internal bool CannotBeNull { get; private set; }254 255        /// <summary>256        /// If true, an empty string can be bound to the string parameter257        /// even if the parameter is mandatory.258        /// </summary>259        internal bool AllowsEmptyStringArgument { get; private set; }260 261        /// <summary>262        /// If true, an empty collection can be bound to the collection/array parameter263        /// even if the parameter is mandatory.264        /// </summary>265        internal bool AllowsEmptyCollectionArgument { get; private set; }266 267        /// <summary>268        /// Gets or sets the value that tells whether this parameter269        /// is for the "all" parameter set.270        /// </summary>271        internal bool IsInAllSets { get; set; }272 273        /// <summary>274        /// Returns true if this parameter is ValueFromPipeline or ValueFromPipelineByPropertyName275        /// in one or more (but not necessarily all) parameter sets.276        /// </summary>277        internal bool IsPipelineParameterInSomeParameterSet { get; private set; }278 279        /// <summary>280        /// Returns true if this parameter is Mandatory in one or more (but not necessarily all) parameter sets.281        /// </summary>282        internal bool IsMandatoryInSomeParameterSet { get; private set; }283 284        /// <summary>285        /// Gets or sets the parameter set flags that map the parameter sets286        /// for this parameter to the parameter set names.287        /// </summary>288        /// <remarks>289        /// This is a bit-field that maps the parameter sets in this parameter290        /// to the parameter sets for the rest of the command.291        /// </remarks>292        internal uint ParameterSetFlags { get; set; }293 294        /// <summary>295        /// A delegate that can set the property.296        /// </summary>297        internal Action<object, object> Setter { get; set; }298 299        /// <summary>300        /// A dictionary of the parameter sets and the parameter set specific data for this parameter.301        /// </summary>302        internal Dictionary<string, ParameterSetSpecificMetadata> ParameterSetData { get; }303 304        /// <summary>305        /// The alias names for this parameter.306        /// </summary>307        internal string[] Aliases { get; }308 309        /// <summary>310        /// Determines if this parameter takes pipeline input for any of the specified311        /// parameter set flags.312        /// </summary>313        /// <param name="validParameterSetFlags">314        /// The flags for the parameter sets to check to see if the parameter takes315        /// pipeline input.316        /// </param>317        /// <returns>318        /// True if the parameter takes pipeline input in any of the specified parameter319        /// sets, or false otherwise.320        /// </returns>321        internal bool DoesParameterSetTakePipelineInput(uint validParameterSetFlags)322        {323            if (!IsPipelineParameterInSomeParameterSet)324            {325                return false;326            }327 328            // Loop through each parameter set the parameter is in to see if that parameter set is329            // still valid.  If so, and the parameter takes pipeline input in that parameter set,330            // then return true331 332            foreach (ParameterSetSpecificMetadata parameterSetData in ParameterSetData.Values)333            {334                if ((parameterSetData.IsInAllSets ||335                    (parameterSetData.ParameterSetFlag & validParameterSetFlags) != 0) &&336                    (parameterSetData.ValueFromPipeline ||337                     parameterSetData.ValueFromPipelineByPropertyName))338                {339                    return true;340                }341            }342 343            return false;344        }345 346        /// <summary>347        /// Gets the parameter set data for this parameter for the specified parameter set.348        /// </summary>349        /// <param name="parameterSetFlag">350        /// The parameter set to get the parameter set data for.351        /// </param>352        /// <returns>353        /// The parameter set specified data for the specified parameter set.354        /// </returns>355        internal ParameterSetSpecificMetadata GetParameterSetData(uint parameterSetFlag)356        {357            ParameterSetSpecificMetadata result = null;358 359            foreach (ParameterSetSpecificMetadata setData in ParameterSetData.Values)360            {361                // If the parameter is in all sets, then remember the data, but362                // try to find a more specific match363 364                if (setData.IsInAllSets)365                {366                    result = setData;367                }368                else369                {370                    if ((setData.ParameterSetFlag & parameterSetFlag) != 0)371                    {372                        result = setData;373                        break;374                    }375                }376            }377 378            return result;379        }380 381        /// <summary>382        /// Gets the parameter set data for this parameter for the specified parameter sets.383        /// </summary>384        /// <param name="parameterSetFlags">385        /// The parameter sets to get the parameter set data for.386        /// </param>387        /// <returns>388        /// A collection for all parameter set specified data for the parameter sets specified by389        /// the <paramref name="parameterSetFlags"/>.390        /// </returns>391        internal IEnumerable<ParameterSetSpecificMetadata> GetMatchingParameterSetData(uint parameterSetFlags)392        {393            foreach (ParameterSetSpecificMetadata setData in ParameterSetData.Values)394            {395                // If the parameter is in all sets, then remember the data, but396                // try to find a more specific match397 398                if (setData.IsInAllSets)399                {400                    yield return setData;401                }402                else403                {404                    if ((setData.ParameterSetFlag & parameterSetFlags) != 0)405                    {406                        yield return setData;407                    }408                }409            }410        }411 412        #region helper methods413 414        /// <summary>415        /// Processes the Attribute metadata to generate a CompiledCommandAttribute.416        /// </summary>417        /// <exception cref="MetadataException">418        /// If the attribute is a parameter attribute and another parameter attribute419        /// has been processed with the same parameter-set name.420        /// </exception>421        private void ProcessAttribute(422            string memberName,423            Attribute attribute,424            ref Collection<ValidateArgumentsAttribute> validationAttributes,425            ref Collection<ArgumentTransformationAttribute> argTransformationAttributes,426            ref string[] aliases)427        {428            if (attribute == null)429                return;430 431            CompiledAttributes.Add(attribute);432 433            // Now process the attribute based on it's type434            if (attribute is ParameterAttribute paramAttr)435            {436                ProcessParameterAttribute(memberName, paramAttr);437                return;438            }439 440            ValidateArgumentsAttribute validateAttr = attribute as ValidateArgumentsAttribute;441            if (validateAttr != null)442            {443                validationAttributes ??= new Collection<ValidateArgumentsAttribute>();444                validationAttributes.Add(validateAttr);445                if ((attribute is ValidateNotNullAttribute) || (attribute is ValidateNotNullOrEmptyAttribute))446                {447                    this.CannotBeNull = true;448                }449 450                return;451            }452 453            AliasAttribute aliasAttr = attribute as AliasAttribute;454            if (aliasAttr != null)455            {456                if (aliases == null)457                {458                    aliases = aliasAttr.aliasNames;459                }460                else461                {462                    var prevAliasNames = aliases;463                    var newAliasNames = aliasAttr.aliasNames;464                    aliases = new string[prevAliasNames.Length + newAliasNames.Length];465                    Array.Copy(prevAliasNames, aliases, prevAliasNames.Length);466                    Array.Copy(newAliasNames, 0, aliases, prevAliasNames.Length, newAliasNames.Length);467                }468 469                return;470            }471 472            ArgumentTransformationAttribute argumentAttr = attribute as ArgumentTransformationAttribute;473            if (argumentAttr != null)474            {475                argTransformationAttributes ??= new Collection<ArgumentTransformationAttribute>();476                argTransformationAttributes.Add(argumentAttr);477                return;478            }479 480            AllowNullAttribute allowNullAttribute = attribute as AllowNullAttribute;481            if (allowNullAttribute != null)482            {483                this.AllowsNullArgument = true;484                return;485            }486 487            AllowEmptyStringAttribute allowEmptyStringAttribute = attribute as AllowEmptyStringAttribute;488            if (allowEmptyStringAttribute != null)489            {490                this.AllowsEmptyStringArgument = true;491                return;492            }493 494            AllowEmptyCollectionAttribute allowEmptyCollectionAttribute = attribute as AllowEmptyCollectionAttribute;495            if (allowEmptyCollectionAttribute != null)496            {497                this.AllowsEmptyCollectionArgument = true;498                return;499            }500 501            ObsoleteAttribute obsoleteAttr = attribute as ObsoleteAttribute;502            if (obsoleteAttr != null)503            {504                ObsoleteAttribute = obsoleteAttr;505                return;506            }507 508            PSTypeNameAttribute psTypeNameAttribute = attribute as PSTypeNameAttribute;509            if (psTypeNameAttribute != null)510            {511                this.PSTypeName = psTypeNameAttribute.PSTypeName;512            }513        }514 515        /// <summary>516        /// Extracts the data from the ParameterAttribute and creates the member data as necessary.517        /// </summary>518        /// <param name="parameterName">519        /// The name of the parameter.520        /// </param>521        /// <param name="parameter">522        /// The instance of the ParameterAttribute to extract the data from.523        /// </param>524        /// <exception cref="MetadataException">525        /// If a parameter set name has already been declared on this parameter.526        /// </exception>527        private void ProcessParameterAttribute(528            string parameterName,529            ParameterAttribute parameter)530        {531            // If the parameter set name already exists on this parameter and the set name is the default parameter532            // set name, it is an error.533 534            if (ParameterSetData.ContainsKey(parameter.ParameterSetName))535            {536                MetadataException e =537                    new MetadataException(538                        "ParameterDeclaredInParameterSetMultipleTimes",539                        null,540                        DiscoveryExceptions.ParameterDeclaredInParameterSetMultipleTimes,541                        parameterName,542                        parameter.ParameterSetName);543 544                throw e;545            }546 547            if (parameter.ValueFromPipeline || parameter.ValueFromPipelineByPropertyName)548            {549                IsPipelineParameterInSomeParameterSet = true;550            }551 552            if (parameter.Mandatory)553            {554                IsMandatoryInSomeParameterSet = true;555            }556 557            // Construct an instance of the parameter set specific data558            ParameterSetSpecificMetadata parameterSetSpecificData = new ParameterSetSpecificMetadata(parameter);559            ParameterSetData.Add(parameter.ParameterSetName, parameterSetSpecificData);560        }561 562        public override string ToString()563        {564            return Name;565        }566 567        #endregion helper methods568    }569 570    /// <summary>571    /// The types of collections that are supported as parameter types.572    /// </summary>573    internal enum ParameterCollectionType574    {575        NotCollection,576        IList,577        Array,578        ICollectionGeneric579    }580 581    /// <summary>582    /// Contains the collection type information for a parameter.583    /// </summary>584    internal class ParameterCollectionTypeInformation585    {586        /// <summary>587        /// Constructs a parameter collection type information object588        /// which exposes the specified Type's collection type in a589        /// simple way.590        /// </summary>591        /// <param name="type">592        /// The type to determine the collection information for.593        /// </param>594        internal ParameterCollectionTypeInformation(Type type)595        {596            ParameterCollectionType = ParameterCollectionType.NotCollection;597            Diagnostics.Assert(type != null, "Caller to verify type argument");598 599            // NTRAID#Windows OS Bugs-1009284-2004/05/11-JeffJon600            // What other collection types should be supported?601 602            // Look for array types603 604            // NTRAID#Windows Out of Band Releases-906820-2005/09/07605            // According to MSDN, IsSubclassOf returns false if the types are exactly equal.606            // Should this include ==?607            if (type.IsSubclassOf(typeof(Array)))608            {609                ParameterCollectionType = ParameterCollectionType.Array;610                ElementType = type.GetElementType();611                return;612            }613 614            if (typeof(IDictionary).IsAssignableFrom(type))615            {616                return;617            }618 619            Type[] interfaces = type.GetInterfaces();620            if (interfaces.Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>))621                || (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>)))622            {623                return;624            }625 626            bool implementsIList = (type.GetInterface(nameof(IList)) != null);627 628            // Look for class Collection<T>.  Collection<T> implements IList, and also IList629            // is more efficient to bind than ICollection<T>.  This optimization630            // retrieves the element type so that we can coerce the elements.631            // Otherwise they must already be the right type.632            if (implementsIList && type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(Collection<>)))633            {634                ParameterCollectionType = ParameterCollectionType.IList;635                // figure out elementType636                Type[] elementTypes = type.GetGenericArguments();637                Diagnostics.Assert(638                    elementTypes.Length == 1,639                    "Expected 1 generic argument, got " + elementTypes.Length);640                ElementType = elementTypes[0];641                return;642            }643 644            // Look for interface ICollection<T>.  Note that Collection<T>645            // does not implement ICollection<T>, and also, ICollection<T>646            // does not derive from IList.  The only way to add elements647            // to an ICollection<T> is via reflected calls to Add(T),648            // but the advantage over plain IList is that we can typecast the elements.649            Type interfaceICollection =650                Array.Find(interfaces, static i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICollection<>));651            if (interfaceICollection != null)652            {653                // We only deal with the first type for which ICollection<T> is implemented654                ParameterCollectionType = ParameterCollectionType.ICollectionGeneric;655                // figure out elementType656                Type[] elementTypes = interfaceICollection.GetGenericArguments();657                Diagnostics.Assert(658                    elementTypes.Length == 1,659                    "Expected 1 generic argument, got " + elementTypes.Length);660                ElementType = elementTypes[0];661                return;662            }663 664            // Look for IList665            if (implementsIList)666            {667                ParameterCollectionType = ParameterCollectionType.IList;668                // elementType remains null669                return;670            }671        }672 673        /// <summary>674        /// The collection type of the parameter.675        /// </summary>676        internal ParameterCollectionType ParameterCollectionType { get; }677 678        /// <summary>679        /// The type of the elements in the collection.680        /// </summary>681        internal Type ElementType { get; }682    }683}684