Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes308downloads
Pipe.cs623 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.Management.Automation.Runspaces;7 8namespace System.Management.Automation.Internal9{10    /// <summary>11    /// Corresponds to -OutputVariable, -ErrorVariable, -WarningVariable, and -InformationVariable.12    /// </summary>13    internal enum VariableStreamKind14    {15        Output,16        Error,17        Warning,18        Information19    }20 21    /// <summary>22    /// Pipe provides a way to stitch two commands.23    /// </summary>24    /// <remarks>25    /// The Pipe class is not thread-safe, so methods such as26    /// AddItems and Retrieve should not be called simultaneously.27    /// ExternalReader and ExternalWriter can provide thread-safe buffering.28    /// </remarks>29    internal class Pipe30    {31        private readonly ExecutionContext _context;32 33        // If a pipeline object has been added, then34        // write objects to it, stepping one at a time...35        internal PipelineProcessor PipelineProcessor { get; }36 37        /// <summary>38        /// This is the downstream cmdlet in the "streamlet model"39        /// which is invoked during each call to Add/AddItems.40        /// </summary>41        internal CommandProcessorBase DownstreamCmdlet42        {43            get44            {45                return _downstreamCmdlet;46            }47 48            set49            {50                Diagnostics.Assert(_resultList == null, "Tried to set downstream cmdlet when _resultList not null");51                _downstreamCmdlet = value;52            }53        }54 55        private CommandProcessorBase _downstreamCmdlet;56 57        /// <summary>58        /// This is the upstream external object source.  If this is set,59        /// Retrieve() will attempt to read objects from the upstream source60        /// before indicating that the pipe is empty.61        /// <remarks>62        /// It is improper to change this once the pipeline has started63        /// executing, although the checks for this are in the64        /// PipelineProcessor class and not here.65        /// </remarks>66        /// </summary>67        internal PipelineReader<object> ExternalReader { get; set; }68 69        /// <summary>70        /// This is the downstream object recipient.  If this is set,71        /// Add() and AddItems() write to this recipient instead of72        /// to the internal queue.  This also disables the73        /// DownstreamCmdlet.74        /// <remarks>75        /// It is improper to change this once the pipeline has started76        /// executing, although the checks for this are in the77        /// PipelineProcessor class and not here.78        /// </remarks>79        /// </summary>80        internal PipelineWriter ExternalWriter81        {82            get83            {84                return _externalWriter;85            }86 87            set88            {89                Diagnostics.Assert(_resultList == null, "Tried to set Pipe ExternalWriter when resultList not null");90                _externalWriter = value;91            }92        }93 94        private PipelineWriter _externalWriter;95 96        /// <summary>97        /// For diagnostic purposes.98        /// </summary>99        /// <returns></returns>100        public override string ToString()101        {102            if (_downstreamCmdlet != null)103                return _downstreamCmdlet.ToString();104            return base.ToString();105        }106 107        /// <summary>108        /// OutBufferCount configures the number of objects to buffer before calling the downstream Cmdlet.109        /// </summary>110        internal int OutBufferCount { get; set; } = 0;111 112        /// <summary>113        /// Gets whether the out variable list should be ignored.114        /// This is used for scenarios like the `clean` block, where writing to output stream is intentionally115        /// disabled and thus out variables should also be ignored.116        /// </summary>117        internal bool IgnoreOutVariableList { get; set; }118 119        /// <summary>120        /// If true, then all input added to this pipe will simply be discarded...121        /// </summary>122        internal bool NullPipe123        {124            get125            {126                return _nullPipe;127            }128 129            set130            {131                _isRedirected = true;132                _nullPipe = value;133            }134        }135 136        private bool _nullPipe;137 138        /// <summary>139        /// A queue that is shared between commands on either side of the pipe to transfer objects.140        /// </summary>141        internal Queue<object> ObjectQueue { get; }142 143        /// <summary>144        /// True if there are items in this pipe that need processing...145        /// <remarks>146        /// This does not take into account the presence of ExternalInput;147        /// it only indicates whether there is currently any data queued up148        /// or if there is data in the enumerator...149        /// </remarks>150        /// </summary>151        internal bool Empty152        {153            get154            {155                if (_enumeratorToProcess != null)156                    return _enumeratorToProcessIsEmpty;157 158                if (ObjectQueue != null)159                    return ObjectQueue.Count == 0;160                return true;161            }162        }163 164        /// <summary>165        /// Is true if there is someone consuming this pipe already, either through166        /// a Pipe object that processes it's output or there is downstream cmdlet...167        /// </summary>168        internal bool IsRedirected169        {170            get { return _downstreamCmdlet != null || _isRedirected; }171        }172 173        private bool _isRedirected;174 175        /// <summary>176        /// If non-null, output written to the pipe are also added to this list.177        /// </summary>178        private List<IList> _outVariableList;179 180        /// <summary>181        /// If non-null, errors written to the pipe are also added to this list.182        /// </summary>183        private List<IList> _errorVariableList;184 185        /// <summary>186        /// If non-null, warnings written to the pipe are also added to this list.187        /// </summary>188        private List<IList> _warningVariableList;189 190        /// <summary>191        /// If non-null, information objects written to the pipe are also added to this list.192        /// </summary>193        private List<IList> _informationVariableList;194 195        /// <summary>196        /// If non-null, the current object being written to the pipe is stored in197        /// this variable.198        /// </summary>199        private PSVariable _pipelineVariableObject;200 201        private static void AddToVarList(List<IList> varList, object obj)202        {203            if (varList != null && varList.Count > 0)204            {205                for (int i = 0; i < varList.Count; i++)206                {207                    varList[i].Add(obj);208                }209            }210        }211 212        internal void AppendVariableList(VariableStreamKind kind, object obj)213        {214            switch (kind)215            {216                case VariableStreamKind.Error:217                    AddToVarList(_errorVariableList, obj);218                    break;219                case VariableStreamKind.Warning:220                    AddToVarList(_warningVariableList, obj);221                    break;222                case VariableStreamKind.Output:223                    AddToVarList(_outVariableList, obj);224                    break;225                case VariableStreamKind.Information:226                    AddToVarList(_informationVariableList, obj);227                    break;228            }229        }230 231        internal void AddVariableList(VariableStreamKind kind, IList list)232        {233            switch (kind)234            {235                case VariableStreamKind.Error:236                    _errorVariableList ??= new List<IList>();237 238                    _errorVariableList.Add(list);239                    break;240                case VariableStreamKind.Warning:241                    _warningVariableList ??= new List<IList>();242 243                    _warningVariableList.Add(list);244                    break;245                case VariableStreamKind.Output:246                    _outVariableList ??= new List<IList>();247 248                    _outVariableList.Add(list);249                    break;250                case VariableStreamKind.Information:251                    _informationVariableList ??= new List<IList>();252 253                    _informationVariableList.Add(list);254                    break;255            }256        }257 258        internal void SetPipelineVariable(PSVariable pipelineVariable)259        {260            _pipelineVariableObject = pipelineVariable;261        }262 263        internal void RemoveVariableList(VariableStreamKind kind, IList list)264        {265            switch (kind)266            {267                case VariableStreamKind.Error:268                    _errorVariableList.Remove(list);269                    break;270                case VariableStreamKind.Warning:271                    _warningVariableList.Remove(list);272                    break;273                case VariableStreamKind.Output:274                    _outVariableList.Remove(list);275                    break;276                case VariableStreamKind.Information:277                    _informationVariableList.Remove(list);278                    break;279            }280        }281 282        internal void RemovePipelineVariable()283        {284            if (_pipelineVariableObject != null)285            {286                _pipelineVariableObject.Value = null;287                _pipelineVariableObject = null;288            }289        }290 291        /// <summary>292        /// When a temporary pipe is used in the middle of execution, then we need to pass along293        /// the error and warning variable list to hold the errors and warnings get written out294        /// while the temporary pipe is being used.295        ///296        /// We don't need to pass along the out variable list because we don't care about the output297        /// generated in the middle of execution.298        /// </summary>299        internal void SetVariableListForTemporaryPipe(Pipe tempPipe)300        {301            CopyVariableToTempPipe(VariableStreamKind.Error, _errorVariableList, tempPipe);302            CopyVariableToTempPipe(VariableStreamKind.Warning, _warningVariableList, tempPipe);303            CopyVariableToTempPipe(VariableStreamKind.Information, _informationVariableList, tempPipe);304        }305 306        private static void CopyVariableToTempPipe(VariableStreamKind streamKind, List<IList> variableList, Pipe tempPipe)307        {308            if (variableList != null && variableList.Count > 0)309            {310                for (int i = 0; i < variableList.Count; i++)311                {312                    tempPipe.AddVariableList(streamKind, variableList[i]);313                }314            }315        }316 317        #region ctor318 319        /// <summary>320        /// Default constructor - Creates the object queue.321        /// </summary>322        /// <remarks>323        /// The initial Queue capacity is 1, but it will grow automatically.324        /// </remarks>325        internal Pipe()326        {327            ObjectQueue = new Queue<object>();328        }329 330        /// <summary>331        /// This overload causes output to be written into a List.332        /// </summary>333        /// <param name="resultList"></param>334        internal Pipe(List<object> resultList)335        {336            Diagnostics.Assert(resultList != null, "resultList cannot be null");337            _isRedirected = true;338            _resultList = resultList;339        }340 341        private readonly List<object> _resultList;342 343        /// <summary>344        /// This overload causes output to be345        /// written onto an Collection[PSObject] which is more useful346        /// in many circumstances than arraylist.347        /// </summary>348        /// <param name="resultCollection">The collection to write into.</param>349        internal Pipe(System.Collections.ObjectModel.Collection<PSObject> resultCollection)350        {351            Diagnostics.Assert(resultCollection != null, "resultCollection cannot be null");352            _isRedirected = true;353            _resultCollection = resultCollection;354        }355 356        private readonly System.Collections.ObjectModel.Collection<PSObject> _resultCollection;357 358        /// <summary>359        /// This pipe writes into another pipeline processor allowing360        /// pipelines to be chained together...361        /// </summary>362        /// <param name="context">The execution context object for this engine instance.</param>363        /// <param name="outputPipeline">The pipeline to write into...</param>364        internal Pipe(ExecutionContext context, PipelineProcessor outputPipeline)365        {366            Diagnostics.Assert(outputPipeline != null, "outputPipeline cannot be null");367            Diagnostics.Assert(outputPipeline != null, "context cannot be null");368            _isRedirected = true;369            _context = context;370            PipelineProcessor = outputPipeline;371        }372 373        /// <summary>374        /// Read from an enumerator instead of a pipeline reader...375        /// </summary>376        /// <param name="enumeratorToProcess">The enumerator to process...</param>377        internal Pipe(IEnumerator enumeratorToProcess)378        {379            Diagnostics.Assert(enumeratorToProcess != null, "enumeratorToProcess cannot be null");380            _enumeratorToProcess = enumeratorToProcess;381 382            // since there is an enumerator specified, we383            // assume that there is some stuff to read384            _enumeratorToProcessIsEmpty = false;385        }386 387        private readonly IEnumerator _enumeratorToProcess;388        private bool _enumeratorToProcessIsEmpty;389 390        #endregion ctor391 392        /// <summary>393        /// Writes an object to the pipe.  This could recursively call to the394        /// downstream cmdlet, or write the object to the external output.395        /// </summary>396        /// <param name="obj">The object to add to the pipe.</param>397        /// <remarks>398        /// AutomationNull.Value is ignored399        /// </remarks>400        /// <exception cref="PipelineStoppedException">401        /// a terminating error occurred, or the pipeline was otherwise stopped402        /// </exception>403        /// <exception cref="PipelineClosedException">404        /// The ExternalWriter stream is closed405        /// </exception>406        internal void Add(object obj)407        {408            if (obj == AutomationNull.Value)409                return;410 411            // OutVariable is appended for null pipes so that the following works:412            //     foo -OutVariable bar > $null413            AddToVarList(_outVariableList, obj);414 415            if (_nullPipe)416                return;417 418            // Store the current pipeline variable419            if (_pipelineVariableObject != null)420            {421                _pipelineVariableObject.Value = obj;422            }423 424            AddToPipe(obj);425        }426 427        internal void AddWithoutAppendingOutVarList(object obj)428        {429            if (obj == AutomationNull.Value || _nullPipe)430                return;431 432            AddToPipe(obj);433        }434 435        private void AddToPipe(object obj)436        {437            if (PipelineProcessor != null)438            {439                // Put the pipeline on the notification stack for stop.440                _context.PushPipelineProcessor(PipelineProcessor);441                PipelineProcessor.Step(obj);442                _context.PopPipelineProcessor(false);443            }444            else if (_resultCollection != null)445            {446                _resultCollection.Add(obj != null ? PSObject.AsPSObject(obj) : null);447            }448            else if (_resultList != null)449            {450                _resultList.Add(obj);451            }452            else if (_externalWriter != null)453            {454                _externalWriter.Write(obj);455            }456            else if (ObjectQueue != null)457            {458                ObjectQueue.Enqueue(obj);459 460                // This is the "streamlet" recursive call461                if (_downstreamCmdlet != null && ObjectQueue.Count > OutBufferCount)462                {463                    _downstreamCmdlet.DoExecute();464                }465            }466        }467 468        /// <summary>469        /// Writes a set of objects to the pipe.  This could recursively470        /// call to the downstream cmdlet, or write the objects to the471        /// external output.472        /// </summary>473        /// <param name="objects">474        /// Each of the objects are added to the pipe475        /// </param>476        /// <exception cref="PipelineStoppedException">477        /// The pipeline has already been stopped,478        /// or a terminating error occurred in a downstream cmdlet.479        /// </exception>480        /// <exception cref="PipelineClosedException">481        /// The ExternalWriter stream is closed482        /// </exception>483        internal void AddItems(object objects)484        {485            // Use the extended type system to try and get an enumerator for the object being added.486            // If we get an enumerator, then add the individual elements. If the object isn't487            // enumerable (i.e. the call returned null) then add the object to the pipe488            // as a single element.489            IEnumerator ie = LanguagePrimitives.GetEnumerator(objects);490            try491            {492                if (ie == null)493                {494                    Add(objects);495                }496                else497                {498                    while (ParserOps.MoveNext(_context, null, ie))499                    {500                        object o = ParserOps.Current(null, ie);501 502                        // Slip over any instance of AutomationNull.Value in the pipeline...503                        if (o == AutomationNull.Value)504                        {505                            continue;506                        }507 508                        Add(o);509                    }510                }511            }512            finally513            {514                // If our object came from GetEnumerator (and hence is not IEnumerator), then we need to dispose515                // Otherwise, we don't own the object, so don't dispose.516                var disposable = ie as IDisposable;517                if (disposable != null && objects is not IEnumerator)518                {519                    disposable.Dispose();520                }521            }522 523            if (_externalWriter != null)524                return;525 526            // If there are objects waiting for the downstream command527            // call it now528            if (_downstreamCmdlet != null && ObjectQueue != null && ObjectQueue.Count > OutBufferCount)529            {530                _downstreamCmdlet.DoExecute();531            }532        }533 534        /// <summary>535        /// Returns an object from the pipe. If pipe is empty returns null.536        /// This will try the ExternalReader if there are no queued objects.537        /// </summary>538        /// <returns>539        /// object that is retrieved, or AutomationNull.Value if none540        /// </returns>541        internal object Retrieve()542        {543            if (ObjectQueue != null && ObjectQueue.Count != 0)544            {545                return ObjectQueue.Dequeue();546            }547            else if (_enumeratorToProcess != null)548            {549                if (_enumeratorToProcessIsEmpty)550                {551                    return AutomationNull.Value;552                }553 554                while (true)555                {556                    if (!ParserOps.MoveNext(_context, errorPosition: null, _enumeratorToProcess))557                    {558                        _enumeratorToProcessIsEmpty = true;559                        return AutomationNull.Value;560                    }561 562                    object retValue = ParserOps.Current(errorPosition: null, _enumeratorToProcess);563                    if (retValue == AutomationNull.Value)564                    {565                        // 'AutomationNull.Value' from the enumerator won't be sent to the pipeline.566                        // We try to get the next value in this case.567                        continue;568                    }569 570                    return retValue;571                }572            }573            else if (ExternalReader != null)574            {575                try576                {577                    object o = ExternalReader.Read();578                    if (AutomationNull.Value == o)579                    {580                        // NOTICE-2004/06/08-JonN 963367581                        // The fix to this bug involves making one last582                        // attempt to read from the pipeline in DoComplete.583                        // We should be sure to not hit the ExternalReader584                        // again if it already reported completion.585                        ExternalReader = null;586                    }587 588                    return o;589                }590                catch (PipelineClosedException)591                {592                    return AutomationNull.Value;593                }594                catch (ObjectDisposedException)595                {596                    return AutomationNull.Value;597                }598            }599            else600                return AutomationNull.Value;601        }602 603        /// <summary>604        /// Removes all the objects from the Pipe.605        /// </summary>606        internal void Clear() => ObjectQueue?.Clear();607 608        /// <summary>609        /// Returns the currently queued items in the pipe.  Note that this will610        /// not block on ExternalInput, and it does not modify the contents of611        /// the pipe.612        /// </summary>613        /// <returns>Possibly empty array of objects, but not null.</returns>614        internal object[] ToArray()615        {616            if (ObjectQueue == null || ObjectQueue.Count == 0)617                return MshCommandRuntime.StaticEmptyArray;618 619            return ObjectQueue.ToArray();620        }621    }622}623