Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes308downloads
ShellVariable.cs917 linesDownload Raw Back to engine
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.Collections.ObjectModel;6using System.Management.Automation.Language;7using System.Management.Automation.Runspaces;8using System.Runtime.CompilerServices;9 10namespace System.Management.Automation11{12    /// <summary>13    /// Represents a variable in the PowerShell language.14    /// </summary>15    public class PSVariable : IHasSessionStateEntryVisibility16    {17        #region Ctor18 19        /// <summary>20        /// Constructs a variable with the given name.21        /// </summary>22        /// <param name="name">23        /// The name of the variable.24        /// </param>25        /// <exception cref="ArgumentException">26        /// If <paramref name="name"/> is null or empty.27        /// </exception>28        public PSVariable(string name)29            : this(name, null, ScopedItemOptions.None, (Collection<Attribute>)null)30        {31        }32 33        /// <summary>34        /// Constructs a variable with the given name, and value.35        /// </summary>36        /// <param name="name">37        /// The name of the variable.38        /// </param>39        /// <param name="value">40        /// The value of the variable.41        /// </param>42        /// <exception cref="ArgumentException">43        /// If <paramref name="name"/> is null or empty.44        /// </exception>45        public PSVariable(string name, object value)46            : this(name, value, ScopedItemOptions.None, (Collection<Attribute>)null)47        {48        }49 50        /// <summary>51        /// Constructs a variable with the given name, value, and options.52        /// </summary>53        /// <param name="name">54        /// The name of the variable.55        /// </param>56        /// <param name="value">57        /// The value of the variable.58        /// </param>59        /// <param name="options">60        /// The constraints of the variable. Note, variables can only be made constant61        /// in the constructor.62        /// </param>63        /// <exception cref="ArgumentException">64        /// If <paramref name="name"/> is null or empty.65        /// </exception>66        public PSVariable(string name, object value, ScopedItemOptions options)67            : this(name, value, options, (Collection<Attribute>)null)68        {69        }70 71        /// <summary>72        /// Constructs a variable with the given name, value, options, and description.73        /// </summary>74        /// <param name="name">75        /// The name of the variable.76        /// </param>77        /// <param name="value">78        /// The value of the variable.79        /// </param>80        /// <param name="options">81        /// The constraints of the variable. Note, variables can only be made constant82        /// in the constructor.83        /// </param>84        /// <param name="description">85        /// The description for the variable.86        /// </param>87        /// <exception cref="ArgumentException">88        /// If <paramref name="name"/> is null or empty.89        /// </exception>90        internal PSVariable(string name, object value, ScopedItemOptions options, string description)91            : this(name, value, options, (Collection<Attribute>)null)92        {93            _description = description;94        }95 96        /// <summary>97        /// Constructs a variable with the given name, value, options, and description.98        /// </summary>99        /// <param name="name">100        /// The name of the variable.101        /// </param>102        /// <param name="value">103        /// The value of the variable.104        /// </param>105        /// <param name="options">106        /// The constraints of the variable. Note, variables can only be made constant107        /// in the constructor.108        /// </param>109        /// <param name="attributes">110        /// The attributes for the variable. ValidateArgumentsAttribute and derived types111        /// will be used to validate a value before setting it.112        /// </param>113        /// <param name="description">114        /// The description for the variable.115        /// </param>116        /// <exception cref="ArgumentException">117        /// If <paramref name="name"/> is null or empty.118        /// </exception>119        internal PSVariable(120            string name,121            object value,122            ScopedItemOptions options,123            Collection<Attribute> attributes,124            string description)125                : this(name, value, options, attributes)126        {127            _description = description;128        }129 130        /// <summary>131        /// Constructs a variable with the given name, value, options, and attributes.132        /// </summary>133        /// <param name="name">134        /// The name of the variable.135        /// </param>136        /// <param name="value">137        /// The value of the variable.138        /// </param>139        /// <param name="options">140        /// The constraints of the variable. Note, variables can only be made constant141        /// in the constructor.142        /// </param>143        /// <param name="attributes">144        /// The attributes for the variable. ValidateArgumentsAttribute and derived types145        /// will be used to validate a value before setting it.146        /// </param>147        /// <exception cref="ArgumentException">148        /// If <paramref name="name"/> is null or empty.149        /// </exception>150        /// <exception cref="ValidationMetadataException">151        /// If the validation metadata identified in <paramref name="attributes"/>152        /// throws an exception.153        /// </exception>154        public PSVariable(155            string name,156            object value,157            ScopedItemOptions options,158            Collection<Attribute> attributes)159        {160            if (string.IsNullOrEmpty(name))161            {162                throw PSTraceSource.NewArgumentException(nameof(name));163            }164 165            Name = name;166 167            _attributes = new PSVariableAttributeCollection(this);168 169            // Note, it is OK to set the value before setting the attributes170            // because each attribute will be validated as it is set.171 172            SetValueRawImpl(value, true);173 174            if (attributes != null)175            {176                foreach (Attribute attribute in attributes)177                {178                    _attributes.Add(attribute);179                }180            }181 182            // Set the options after setting the initial value.183            _options = options;184 185            if (IsAllScope)186            {187                Language.VariableAnalysis.NoteAllScopeVariable(name);188            }189        }190 191        // Should be protected, but that makes it public which we don't want.192        // The dummy parameter is to make the signature distinct from the public constructor taking a string.193        // This constructor exists to avoid calling SetValueRaw, which when overridden, might not work because194        // the derived class isn't fully constructed yet.195        internal PSVariable(string name, bool dummy)196        {197            Name = name;198        }199 200        #endregion ctor201 202        /// <summary>203        /// Gets the name of the variable.204        /// </summary>205        public string Name { get; } = string.Empty;206 207        /// <summary>208        /// Gets or sets the description of the variable.209        /// </summary>210        public virtual string Description211        {212            get213            {214                return _description;215            }216 217            set218            {219                _description = value;220            }221        }222 223        private string _description = string.Empty;224 225        internal void DebuggerCheckVariableRead()226        {227            var context = SessionState != null228                              ? SessionState.ExecutionContext229                              : LocalPipeline.GetExecutionContextFromTLS();230            if (context != null && context._debuggingMode > 0)231            {232                context.Debugger.CheckVariableRead(Name);233            }234        }235 236        internal void DebuggerCheckVariableWrite()237        {238            var context = SessionState != null239                              ? SessionState.ExecutionContext240                              : LocalPipeline.GetExecutionContextFromTLS();241            if (context != null && context._debuggingMode > 0)242            {243                context.Debugger.CheckVariableWrite(Name);244            }245        }246 247        /// <summary>248        /// Gets the value without triggering debugger check.249        /// </summary>250        internal virtual object GetValueRaw()251        {252            return _value;253        }254 255        /// <summary>256        /// Gets or sets the value of the variable.257        /// </summary>258        /// <exception cref="SessionStateUnauthorizedAccessException">259        /// If the variable is read-only or constant upon call to set.260        /// </exception>261        /// <exception cref="ValidationMetadataException">262        /// <paramref name="value"/> is not valid according to one or more263        /// of the attributes of this shell variable.264        /// </exception>265        public virtual object Value266        {267            get268            {269                DebuggerCheckVariableRead();270                return _value;271            }272 273            set274            {275                SetValue(value);276            }277        }278 279        private object _value;280 281        /// <summary>282        /// If true, then this variable is visible outside the runspace.283        /// </summary>284        public SessionStateEntryVisibility Visibility { get; set; } = SessionStateEntryVisibility.Public;285 286        /// <summary>287        /// The module where this variable was defined.288        /// </summary>289        public PSModuleInfo Module { get; private set; }290 291        internal void SetModule(PSModuleInfo module)292        {293            Module = module;294        }295 296        /// <summary>297        /// The name of the module that defined this variable.298        /// </summary>299        public string ModuleName300        {301            get302            {303                if (Module != null)304                    return Module.Name;305                return string.Empty;306            }307        }308 309        /// <summary>310        /// Gets or sets the scope options on the variable.311        /// </summary>312        /// <exception cref="SessionStateUnauthorizedAccessException">313        /// Upon set, if the variable is constant or if <paramref name="value"/>314        /// contains the constant flag.315        /// </exception>316        public virtual ScopedItemOptions Options317        {318            get319            {320                return _options;321            }322 323            set324            {325                SetOptions(value, false);326            }327        }328 329        internal void SetOptions(ScopedItemOptions newOptions, bool force)330        {331            // Check to see if the variable is constant or readonly, if so332            // throw an exception because the options cannot be changed.333 334            if (IsConstant || (!force && IsReadOnly))335            {336                SessionStateUnauthorizedAccessException e =337                    new SessionStateUnauthorizedAccessException(338                            Name,339                            SessionStateCategory.Variable,340                            "VariableNotWritable",341                            SessionStateStrings.VariableNotWritable);342 343                throw e;344            }345 346            // Now check to see if the caller is trying to set347            // the options to constant. This is only allowed at348            // variable creation349 350            if ((newOptions & ScopedItemOptions.Constant) != 0)351            {352                // user is trying to set the variable to constant after353                // creating the variable. Do not allow this (as per spec).354 355                SessionStateUnauthorizedAccessException e =356                    new SessionStateUnauthorizedAccessException(357                            Name,358                            SessionStateCategory.Variable,359                            "VariableCannotBeMadeConstant",360                            SessionStateStrings.VariableCannotBeMadeConstant);361 362                throw e;363            }364 365            // Now check to see if the caller is trying to366            // remove the AllScope option. This is not allowed367            // at any time.368 369            if (IsAllScope && ((newOptions & ScopedItemOptions.AllScope) == 0))370            {371                // user is trying to remove the AllScope option from the variable.372                // Do not allow this (as per spec).373 374                SessionStateUnauthorizedAccessException e =375                    new SessionStateUnauthorizedAccessException(376                            Name,377                            SessionStateCategory.Variable,378                            "VariableAllScopeOptionCannotBeRemoved",379                            SessionStateStrings.VariableAllScopeOptionCannotBeRemoved);380 381                throw e;382            }383 384            _options = newOptions;385        }386 387        private ScopedItemOptions _options = ScopedItemOptions.None;388 389        /// <summary>390        /// Gets the collection that contains the attributes for the variable.391        /// </summary>392        /// <remarks>393        /// To add or remove attributes, get the collection and then add or remove394        /// attributes to that collection.395        /// </remarks>396        public Collection<Attribute> Attributes397        {398            get { return _attributes ??= new PSVariableAttributeCollection(this); }399        }400 401        private PSVariableAttributeCollection _attributes;402 403        /// <summary>404        /// Checks if the given value meets the validation attribute constraints on the PSVariable.405        /// </summary>406        /// <param name="value">407        /// value which needs to be checked408        /// </param>409        /// <remarks>410        /// If <paramref name="value"/> is null or if no attributes are set, then411        /// the value is deemed valid.412        /// </remarks>413        /// <exception cref="ValidationMetadataException">414        /// If the validation metadata throws an exception.415        /// </exception>416        public virtual bool IsValidValue(object value)417        {418            return IsValidValue(_attributes, value);419        }420 421        internal static bool IsValidValue(IEnumerable<Attribute> attributes, object value)422        {423            if (attributes != null)424            {425                foreach (Attribute attribute in attributes)426                {427                    if (!IsValidValue(value, attribute))428                    {429                        return false;430                    }431                }432            }433 434            return true;435        }436 437        /// <summary>438        /// Determines if the value is valid for the specified attribute.439        /// </summary>440        /// <param name="value">441        /// The variable value to validate.442        /// </param>443        /// <param name="attribute">444        /// The attribute to use to validate that value.445        /// </param>446        /// <returns>447        /// True if the value is valid with respect to the attribute, or false otherwise.448        /// </returns>449        internal static bool IsValidValue(object value, Attribute attribute)450        {451            bool result = true;452 453            ValidateArgumentsAttribute validationAttribute = attribute as ValidateArgumentsAttribute;454            if (validationAttribute != null)455            {456                try457                {458                    // Get an EngineIntrinsics instance using the context of the thread.459 460                    ExecutionContext context = Runspaces.LocalPipeline.GetExecutionContextFromTLS();461                    EngineIntrinsics engine = null;462 463                    if (context != null)464                    {465                        engine = context.EngineIntrinsics;466                    }467 468                    validationAttribute.InternalValidate(value, engine);469                }470                catch (ValidationMetadataException)471                {472                    result = false;473                }474            }475 476            return result;477        }478 479        /// <summary>480        /// Runs all ArgumentTransformationAttributes that are specified in the Attributes481        /// collection on the given value in the order that they are in the collection.482        /// </summary>483        /// <param name="attributes">484        /// The attributes to use to transform the value.485        /// </param>486        /// <param name="value">487        /// The value to be transformed.488        /// </param>489        /// <returns>490        /// The transformed value.491        /// </returns>492        /// <exception cref="ArgumentTransformationMetadataException">493        /// If the argument transformation fails.494        /// </exception>495        internal static object TransformValue(IEnumerable<Attribute> attributes, object value)496        {497            Diagnostics.Assert(attributes != null, "caller to verify attributes is not null");498 499            object result = value;500 501            // Get an EngineIntrinsics instance using the context of the thread.502 503            ExecutionContext context = Runspaces.LocalPipeline.GetExecutionContextFromTLS();504            EngineIntrinsics engine = null;505 506            if (context != null)507            {508                engine = context.EngineIntrinsics;509            }510 511            foreach (Attribute attribute in attributes)512            {513                ArgumentTransformationAttribute transformationAttribute =514                    attribute as ArgumentTransformationAttribute;515                if (transformationAttribute != null)516                {517                    result = transformationAttribute.TransformInternal(engine, result);518                }519            }520 521            return result;522        }523 524        /// <summary>525        /// Parameter binding does the checking and conversions as specified by the526        /// attributes, so repeating that process is slow and wrong.  This function527        /// applies the attributes without repeating the checks.528        /// </summary>529        /// <param name="attributes">The list of attributes to add.</param>530        internal void AddParameterAttributesNoChecks(Collection<Attribute> attributes)531        {532            foreach (Attribute attribute in attributes)533            {534                _attributes.AddAttributeNoCheck(attribute);535            }536        }537 538        #region internal members539 540        /// <summary>541        /// Returns true if the PSVariable is constant (only visible in the542        /// current scope), false otherwise.543        /// </summary>544        internal bool IsConstant545        {546            get547            {548                return (_options & ScopedItemOptions.Constant) != 0;549            }550        }551 552        /// <summary>553        /// Returns true if the PSVariable is readonly (only visible in the554        /// current scope), false otherwise.555        /// </summary>556        internal bool IsReadOnly557        {558            get559            {560                return (_options & ScopedItemOptions.ReadOnly) != 0;561            }562        }563 564        /// <summary>565        /// Returns true if the PSVariable is private (only visible in the566        /// current scope), false otherwise.567        /// </summary>568        internal bool IsPrivate569        {570            get571            {572                return (_options & ScopedItemOptions.Private) != 0;573            }574        }575 576        /// <summary>577        /// Returns true if the PSVariable is propagated to all scopes578        /// when the scope is created.579        /// </summary>580        internal bool IsAllScope581        {582            get583            {584                return (_options & ScopedItemOptions.AllScope) != 0;585            }586        }587 588        /// <summary>589        /// Indicates that the variable has been removed from session state590        /// and should no longer be considered valid. This is necessary because591        /// we surface variable references and can consequently not maintain592        /// transparent integrity.593        /// </summary>594        internal bool WasRemoved595        {596            get597            {598                return _wasRemoved;599            }600 601            set602            {603                _wasRemoved = value;604                // If set to true, clean up the variable...605                if (value)606                {607                    _options = ScopedItemOptions.None;608                    _value = null;609                    _wasRemoved = true;610                    _attributes = null;611                }612            }613        }614 615        private bool _wasRemoved;616 617        internal SessionStateInternal SessionState { get; set; }618 619        #endregion internal members620 621        /// <summary>622        /// Verifies the constraints and attributes before setting the value.623        /// </summary>624        /// <param name="value">625        /// The value to be set.626        /// </param>627        /// <exception cref="SessionStateUnauthorizedAccessException">628        /// If the variable is read-only or constant.629        /// </exception>630        /// <exception cref="ValidationMetadataException">631        /// If the validation metadata throws an exception or the value doesn't632        /// pass the validation metadata.633        /// </exception>634        private void SetValue(object value)635        {636            // Check to see if the variable is writable637 638            if ((_options & (ScopedItemOptions.ReadOnly | ScopedItemOptions.Constant)) != ScopedItemOptions.None)639            {640                SessionStateUnauthorizedAccessException e =641                    new SessionStateUnauthorizedAccessException(642                            Name,643                            SessionStateCategory.Variable,644                            "VariableNotWritable",645                            SessionStateStrings.VariableNotWritable);646 647                throw e;648            }649 650            // Now perform all ArgumentTransformations that are needed651            object transformedValue = value;652            if (_attributes != null && _attributes.Count > 0)653            {654                transformedValue = TransformValue(_attributes, value);655 656                // Next check to make sure the value is valid657 658                if (!IsValidValue(transformedValue))659                {660                    ValidationMetadataException e = new ValidationMetadataException(661                        "ValidateSetFailure",662                        null,663                        Metadata.InvalidValueFailure,664                        Name,665                        ((transformedValue != null) ? transformedValue.ToString() : "$null"));666 667                    throw e;668                }669            }670 671            if (transformedValue != null)672            {673                transformedValue = CopyMutableValues(transformedValue);674            }675 676            // Set the value before triggering any write breakpoints677            _value = transformedValue;678 679            DebuggerCheckVariableWrite();680        }681 682        private void SetValueRawImpl(object newValue, bool preserveValueTypeSemantics)683        {684            if (preserveValueTypeSemantics)685            {686                newValue = CopyMutableValues(newValue);687            }688 689            _value = newValue;690        }691 692        internal virtual void SetValueRaw(object newValue, bool preserveValueTypeSemantics)693        {694            SetValueRawImpl(newValue, preserveValueTypeSemantics);695        }696 697        private readonly CallSite<Func<CallSite, object, object>> _copyMutableValueSite =698            CallSite<Func<CallSite, object, object>>.Create(PSVariableAssignmentBinder.Get());699 700        internal object CopyMutableValues(object o)701        {702            // The variable assignment binder copies mutable values and returns other values as is.703            return _copyMutableValueSite.Target.Invoke(_copyMutableValueSite, o);704        }705 706        internal void WrapValue()707        {708            if (!this.IsConstant)709            {710                if (_value != null)711                {712                    _value = PSObject.AsPSObject(_value);713                }714            }715        }716 717#if FALSE718        // Replaced with a DLR based binder - but code is preserved in case that approach doesn't719        // work well performance wise.720 721        // See if it's a value type being assigned and722        // make a copy if it is...723        private static object PreserveValueType(object value)724        {725            if (value == null)726                return null;727 728            // Primitive types are immutable so just return them...729            Type valueType = value.GetType();730            if (valueType.IsPrimitive)731                return value;732 733            PSObject valueAsPSObject = value as PSObject;734            if (valueAsPSObject != null)735            {736                object baseObject = valueAsPSObject.BaseObject;737                if (baseObject != null)738                {739                    valueType = baseObject.GetType();740                    if (valueType.IsValueType && !valueType.IsPrimitive)741                    {742                        return valueAsPSObject.Copy();743                    }744                }745            }746            else if (valueType.IsValueType)747            {748                return PSObject.CopyValueType(value);749            }750 751            return value;752        }753#endif754    }755 756    internal class LocalVariable : PSVariable757    {758        private readonly MutableTuple _tuple;759        private readonly int _tupleSlot;760 761        public LocalVariable(string name, MutableTuple tuple, int tupleSlot)762            : base(name, false)763        {764            _tuple = tuple;765            _tupleSlot = tupleSlot;766        }767 768        public override ScopedItemOptions Options769        {770            get771            {772                return base.Options;773            }774 775            set776            {777                // Throw, but only if someone is actually changing the options.778                if (value != base.Options)779                {780                    SessionStateUnauthorizedAccessException e =781                        new SessionStateUnauthorizedAccessException(782                            Name,783                            SessionStateCategory.Variable,784                            "VariableOptionsNotSettable",785                            SessionStateStrings.VariableOptionsNotSettable);786 787                    throw e;788                }789            }790        }791 792        public override object Value793        {794            get795            {796                DebuggerCheckVariableRead();797                return _tuple.GetValue(_tupleSlot);798            }799 800            set801            {802                _tuple.SetValue(_tupleSlot, value);803                DebuggerCheckVariableWrite();804            }805        }806 807        internal override object GetValueRaw()808        {809            return _tuple.GetValue(_tupleSlot);810        }811 812        internal override void SetValueRaw(object newValue, bool preserveValueTypeSemantics)813        {814            if (preserveValueTypeSemantics)815            {816                newValue = CopyMutableValues(newValue);817            }818 819            this.Value = newValue;820        }821    }822 823    /// <summary>824    /// This class is used for $null.  It always returns null as a value and accepts825    /// any value when it is set and throws it away.826    /// </summary>827    internal class NullVariable : PSVariable828    {829        /// <summary>830        /// Constructor that calls the base class constructor with name "null" and831        /// value null.832        /// </summary>833        internal NullVariable() : base(StringLiterals.Null, null, ScopedItemOptions.Constant | ScopedItemOptions.AllScope)834        {835        }836 837        /// <summary>838        /// Always returns null from get, and always accepts839        /// but ignores the value on set.840        /// </summary>841        public override object Value842        {843            get844            {845                return null;846            }847 848            set849            {850                // All values are just ignored851            }852        }853 854        /// <summary>855        /// Gets the description for $null.856        /// </summary>857        public override string Description858        {859            get { return _description ??= SessionStateStrings.DollarNullDescription; }860 861            set { /* Do nothing */ }862        }863 864        private string _description;865 866        /// <summary>867        /// Gets the scope options for $null which is always None.868        /// </summary>869        public override ScopedItemOptions Options870        {871            get { return ScopedItemOptions.None; }872 873            set { /* Do nothing */ }874        }875    }876 877    /// <summary>878    /// The options that define some of the constraints for session state items like879    /// variables, aliases, and functions.880    /// </summary>881    [Flags]882    public enum ScopedItemOptions883    {884        /// <summary>885        /// There are no constraints on the item.886        /// </summary>887        None = 0,888 889        /// <summary>890        /// The item is readonly. It can be removed but cannot be changed.891        /// </summary>892        ReadOnly = 0x1,893 894        /// <summary>895        /// The item cannot be removed or changed.896        /// This flag can only be set a variable creation.897        /// </summary>898        Constant = 0x2,899 900        /// <summary>901        /// The item is private to the scope it was created in and902        /// cannot be seen from child scopes.903        /// </summary>904        Private = 0x4,905 906        /// <summary>907        /// The item is propagated to each new child scope created.908        /// </summary>909        AllScope = 0x8,910 911        /// <summary>912        /// The option is not specified by the user.913        /// </summary>914        Unspecified = 0x10915    }916}917