MegaBites-AI/Windows-powershell
0308
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 