Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
scriptblock.cs1480 linesDownload Raw Back to lang
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.Collections.Concurrent;6using System.Collections.Generic;7using System.Collections.ObjectModel;8using System.Diagnostics.CodeAnalysis;9using System.Globalization;10using System.IO;11using System.Linq;12using System.Linq.Expressions;13using System.Management.Automation.Internal;14using System.Management.Automation.Language;15using System.Management.Automation.Runspaces;16using System.Reflection;17using System.Runtime.CompilerServices;18using System.Runtime.ExceptionServices;19using System.Runtime.Serialization;20 21namespace System.Management.Automation22{23    /// <summary>24    /// An object representing a pre-compiled block of powershell script.25    /// </summary>26    /// <remarks>27    /// This class track a block of script in a compiled form. It is also28    /// used for direct invocation of the script block.29    ///30    /// 1. Overview31    ///32    /// Script block comes in two forms,33    ///34    /// a. Full form (cmdlet form)35    ///36    /// This comes in following format37    ///38    /// {39    ///     begin40    ///     {41    ///         statementlist;42    ///     }43    ///     process44    ///     {45    ///         statementlist;46    ///     }47    ///     end48    ///     {49    ///         statementlist;50    ///     }51    /// }52    ///53    /// This form is used for running the script in a pipeline like54    /// a cmdlet.55    ///56    /// b. Simple form57    ///58    /// This comes in following format59    ///60    /// {61    ///     statementlist;62    /// }63    ///64    /// 2. Script block execution65    ///66    /// For the full form (or cmdlet form) of script block, the script67    /// block itself is part of a pipeline. Its execution is handled through68    /// ScriptCommandProcessor, which involves execution of begin/process/end69    /// blocks like a cmdlet. If a scriptblock in simple form is used in70    /// a pipeline, its execution is done through ScriptCommandProcessor71    /// also, with some of begin/process/end blocks default to be empty.72    ///73    /// A script block in simple form can be directly invoked (outside74    /// of a pipeline context). For example,75    ///76    ///     {"text"}.Invoke()77    ///78    /// A scriptblock can be directly invoked internally or externally through79    /// runspace API.80    ///81    /// This class will handle the logic for direct invocation of script blocks.82    /// </remarks>83    public partial class ScriptBlock84    {85        /// <summary>86        /// Create a script block object based on a script string to be parsed immediately.87        /// </summary>88        /// <param name="context">Engine context for this script block.</param>89        /// <param name="script">The string to compile.</param>90        internal static ScriptBlock Create(ExecutionContext context, string script)91        {92            ScriptBlock sb = Create(context.Engine.EngineParser, null, script);93            if (context.EngineSessionState != null && context.EngineSessionState.Module != null)94            {95                sb.SessionStateInternal = context.EngineSessionState;96            }97 98            return sb;99        }100 101        /// <summary>102        /// Create a script block based on a script to be parsed when execution103        /// context is provided.104        /// </summary>105        /// <param name="script">The string to compile.</param>106        public static ScriptBlock Create(string script) => Create(107            parser: new Parser(),108            fileName: null,109            fileContents: script);110 111        internal static ScriptBlock CreateDelayParsedScriptBlock(string script, bool isProductCode)112            => new ScriptBlock(new CompiledScriptBlockData(script, isProductCode)) { DebuggerHidden = true };113 114        /// <summary>115        /// Returns a new scriptblock bound to a module. Any local variables in the116        /// callers context will be copied into the module.117        /// </summary>118        /// <returns></returns>119        public ScriptBlock GetNewClosure()120        {121            PSModuleInfo m = new PSModuleInfo(true);122            m.CaptureLocals();123            return m.NewBoundScriptBlock(this);124        }125 126        /// <summary>127        /// Returns PowerShell object representing the pipeline contained in this ScriptBlock.128        /// </summary>129        /// <remarks>130        /// Some ScriptBlocks are too complicated to be converted into a PowerShell object.131        /// For those ScriptBlocks a <see cref="ScriptBlockToPowerShellNotSupportedException"/> is thrown.132        ///133        /// ScriptBlock cannot be converted into a PowerShell object if134        /// - It contains more than one statement135        /// - It references variables undeclared in <c>param(...)</c> block136        /// - It uses redirection to a file137        /// - It uses dot sourcing138        /// - Command names can't be resolved (i.e. if an element of a pipeline is another scriptblock)139        ///140        /// Declaration of variables in a <c>param(...)</c> block is enforced,141        /// because undeclared variables are assumed to be variables from a remoting server.142        /// Since we need to fully evaluate parameters of commands of a PowerShell object's143        /// we reject all variables references that refer to a variable from a remoting server.144        /// </remarks>145        /// <param name="args">146        /// arguments for the ScriptBlock (providing values for variables used within the ScriptBlock);147        /// can be null148        /// </param>149        /// <returns>150        /// PowerShell object representing the pipeline contained in this ScriptBlock151        /// </returns>152        /// <exception cref="ScriptBlockToPowerShellNotSupportedException">153        /// Thrown when this ScriptBlock cannot be expressed as a PowerShell object.154        /// For example thrown when there is more than one statement, if there155        /// are undeclared variables, if redirection to a file is used.156        /// </exception>157        /// <exception cref="RuntimeException">158        /// Thrown when evaluation of command arguments results in an exception.159        /// Might depend on the value of $errorActionPreference variable.160        /// For example trying to translate the following ScriptBlock will result in this exception:161        /// <c>$errorActionPreference = "stop"; $sb = { get-foo $( throw ) }; $sb.GetPowerShell()</c>162        /// </exception>163        /// <exception cref="InvalidOperationException">164        /// Thrown when there is no ExecutionContext associated with this ScriptBlock object.165        /// </exception>166        public PowerShell GetPowerShell(params object[] args) => GetPowerShellImpl(167            context: LocalPipeline.GetExecutionContextFromTLS(),168            variables: null,169            isTrustedInput: false,170            filterNonUsingVariables: false,171            createLocalScope: null,172            args);173 174        /// <summary>175        /// Returns PowerShell object representing the pipeline contained in this ScriptBlock,176        /// similar to the GetPowerShell() method. If the 'isTrustedInput' flag parameter is set177        /// to True, then the GetPowerShell() implementation supports extended conversion operations178        /// (such as replacing variable values with their current values) that might otherwise179        /// be unsafe if applied to untrusted input.180        /// </summary>181        /// <param name="isTrustedInput">182        /// Specifies whether the scriptblock being converted comes from a trusted source.183        /// The default is False.184        /// </param>185        /// <param name="args">186        /// arguments for the ScriptBlock (providing values for variables used within the ScriptBlock);187        /// can be null188        /// </param>189        public PowerShell GetPowerShell(bool isTrustedInput, params object[] args)190            => GetPowerShellImpl(191                context: LocalPipeline.GetExecutionContextFromTLS(),192                variables: null,193                isTrustedInput,194                filterNonUsingVariables: false,195                createLocalScope: null,196                args);197 198        /// <summary>199        /// Returns PowerShell object representing the pipeline contained in this ScriptBlock, using variables200        /// supplied in the dictionary.201        /// </summary>202        /// <param name="variables">203        /// variables to be supplied as context to the ScriptBlock (providing values for variables explicitly204        /// requested by the 'using:' prefix.205        /// </param>206        /// <param name="args">207        /// arguments for the ScriptBlock (providing values for variables used within the ScriptBlock);208        /// can be null209        /// </param>210        /// <returns>211        /// PowerShell object representing the pipeline contained in this ScriptBlock212        /// </returns>213        /// <exception cref="ScriptBlockToPowerShellNotSupportedException">214        /// Thrown when this ScriptBlock cannot be expressed as a PowerShell object.215        /// For example thrown when there is more than one statement, if there216        /// are undeclared variables, if redirection to a file is used.217        /// </exception>218        /// <exception cref="RuntimeException">219        /// Thrown when evaluation of command arguments results in an exception.220        /// Might depend on the value of $errorActionPreference variable.221        /// For example trying to translate the following ScriptBlock will result in this exception:222        /// <c>$errorActionPreference = "stop"; $sb = { get-foo $( throw ) }; $sb.GetPowerShell()</c>223        /// </exception>224        /// <exception cref="InvalidOperationException">225        /// Thrown when there is no ExecutionContext associated with this ScriptBlock object and no226        /// variables are supplied.227        /// </exception>228        public PowerShell GetPowerShell(Dictionary<string, object> variables, params object[] args)229        {230            ExecutionContext context = LocalPipeline.GetExecutionContextFromTLS();231            Dictionary<string, object> suppliedVariables = null;232 233            if (variables != null)234            {235                suppliedVariables = new Dictionary<string, object>(variables, StringComparer.OrdinalIgnoreCase);236                context = null;237            }238 239            return GetPowerShellImpl(context, suppliedVariables, false, false, null, args);240        }241 242        /// <summary>243        /// Returns PowerShell object representing the pipeline contained in this ScriptBlock, using variables244        /// supplied in the dictionary.245        /// </summary>246        /// <param name="variables">247        /// variables to be supplied as context to the ScriptBlock (providing values for variables explicitly248        /// requested by the 'using:' prefix.249        /// </param>250        /// <param name="usingVariables">251        /// key-value pairs from the <para>variables</para> that actually get used by the 'using:' prefix variables252        /// </param>253        /// <param name="args">254        /// arguments for the ScriptBlock (providing values for variables used within the ScriptBlock);255        /// can be null256        /// </param>257        /// <returns>258        /// PowerShell object representing the pipeline contained in this ScriptBlock259        /// </returns>260        /// <exception cref="ScriptBlockToPowerShellNotSupportedException">261        /// Thrown when this ScriptBlock cannot be expressed as a PowerShell object.262        /// For example thrown when there is more than one statement, if there263        /// are undeclared variables, if redirection to a file is used.264        /// </exception>265        /// <exception cref="RuntimeException">266        /// Thrown when evaluation of command arguments results in an exception.267        /// Might depend on the value of $errorActionPreference variable.268        /// For example trying to translate the following ScriptBlock will result in this exception:269        /// <c>$errorActionPreference = "stop"; $sb = { get-foo $( throw ) }; $sb.GetPowerShell()</c>270        /// </exception>271        /// <exception cref="InvalidOperationException">272        /// Thrown when there is no ExecutionContext associated with this ScriptBlock object and no273        /// variables are supplied.274        /// </exception>275        public PowerShell GetPowerShell(276            Dictionary<string, object> variables,277            out Dictionary<string, object> usingVariables,278            params object[] args)279            => GetPowerShell(variables, out usingVariables, isTrustedInput: false, args);280 281        /// <summary>282        /// Returns PowerShell object representing the pipeline contained in this ScriptBlock, using variables283        /// supplied in the dictionary.284        /// </summary>285        /// <param name="variables">286        /// variables to be supplied as context to the ScriptBlock (providing values for variables explicitly287        /// requested by the 'using:' prefix.288        /// </param>289        /// <param name="usingVariables">290        /// key-value pairs from the <para>variables</para> that actually get used by the 'using:' prefix variables291        /// </param>292        /// <param name="args">293        /// arguments for the ScriptBlock (providing values for variables used within the ScriptBlock);294        /// can be null295        /// </param>296        /// <param name="isTrustedInput">297        /// Specifies whether the scriptblock being converted comes from a trusted source.298        /// The default is False.299        /// </param>300        /// <returns>301        /// PowerShell object representing the pipeline contained in this ScriptBlock302        /// </returns>303        /// <exception cref="ScriptBlockToPowerShellNotSupportedException">304        /// Thrown when this ScriptBlock cannot be expressed as a PowerShell object.305        /// For example thrown when there is more than one statement, if there306        /// are undeclared variables, if redirection to a file is used.307        /// </exception>308        /// <exception cref="RuntimeException">309        /// Thrown when evaluation of command arguments results in an exception.310        /// Might depend on the value of $errorActionPreference variable.311        /// For example trying to translate the following ScriptBlock will result in this exception:312        /// <c>$errorActionPreference = "stop"; $sb = { get-foo $( throw ) }; $sb.GetPowerShell()</c>313        /// </exception>314        /// <exception cref="InvalidOperationException">315        /// Thrown when there is no ExecutionContext associated with this ScriptBlock object and no316        /// variables are supplied.317        /// </exception>318        public PowerShell GetPowerShell(319            Dictionary<string, object> variables,320            out Dictionary<string, object> usingVariables,321            bool isTrustedInput,322            params object[] args)323        {324            ExecutionContext context = LocalPipeline.GetExecutionContextFromTLS();325            Dictionary<string, object> suppliedVariables = null;326 327            if (variables != null)328            {329                suppliedVariables = new Dictionary<string, object>(variables, StringComparer.OrdinalIgnoreCase);330                context = null;331            }332 333            PowerShell powershell = GetPowerShellImpl(context, suppliedVariables, isTrustedInput, true, null, args);334            usingVariables = suppliedVariables;335 336            return powershell;337        }338 339        internal PowerShell GetPowerShell(340            ExecutionContext context,341            bool isTrustedInput,342            bool? useLocalScope,343            object[] args)344            => GetPowerShellImpl(345                context,346                variables: null,347                isTrustedInput,348                filterNonUsingVariables: false,349                useLocalScope,350                args);351 352        /// <summary>353        /// Get a steppable pipeline object.354        /// </summary>355        /// <returns>A steppable pipeline object.</returns>356        [SuppressMessage(357            "Microsoft.Naming",358            "CA1704:IdentifiersShouldBeSpelledCorrectly",359            MessageId = "Steppable",360            Justification = "Review this during API naming")]361        public SteppablePipeline GetSteppablePipeline()362            => GetSteppablePipelineImpl(commandOrigin: CommandOrigin.Internal, args: null);363 364        /// <summary>365        /// Get a steppable pipeline object.366        /// </summary>367        /// <returns>A steppable pipeline object.</returns>368        [SuppressMessage(369            "Microsoft.Naming",370            "CA1704:IdentifiersShouldBeSpelledCorrectly",371            MessageId = "Steppable",372            Justification = "Review this during API naming")]373        public SteppablePipeline GetSteppablePipeline(CommandOrigin commandOrigin)374            => GetSteppablePipelineImpl(commandOrigin, args: null);375 376        /// <summary>377        /// Get a steppable pipeline object.378        /// </summary>379        /// <returns>A steppable pipeline object.</returns>380        [SuppressMessage(381            "Microsoft.Naming",382            "CA1704:IdentifiersShouldBeSpelledCorrectly",383            MessageId = "Steppable",384            Justification = "Review this during API naming")]385        public SteppablePipeline GetSteppablePipeline(CommandOrigin commandOrigin, object[] args)386            => GetSteppablePipelineImpl(commandOrigin, args);387 388        /// <summary>389        /// Execute this node with the specified arguments. The arguments show390        /// up in the script as $args with $_ being the first argument.391        /// </summary>392        /// <param name="args">The arguments to this script.</param>393        /// <returns>The object(s) generated during the execution of394        /// the script block returned as a collection of PSObjects.</returns>395        /// <exception cref="RuntimeException">Thrown if a script runtime exceptionexception occurred.</exception>396        /// <exception cref="FlowControlException">An internal (non-public) exception from a flow control statement.</exception>397        public Collection<PSObject> Invoke(params object[] args) =>398            DoInvoke(dollarUnder: AutomationNull.Value, input: AutomationNull.Value, args);399 400        /// <summary>401        /// A method that allows a scriptblock to be invoked with additional context in the form of a402        /// set of local functions and variables to be defined in the scriptblock's scope. The list of403        /// variables may include the special variables $input, $_ and $this.404        ///405        /// This overload of the function takes a hashtable and converts it to the406        /// required dictionary which makes the API easier to use from within a PowerShell script.407        /// </summary>408        /// <param name="functionsToDefine">A dictionary of functions to define.</param>409        /// <param name="variablesToDefine">A list of variables to define.</param>410        /// <param name="args">The arguments to the actual scriptblock.</param>411        /// <returns></returns>412        public Collection<PSObject> InvokeWithContext(413            IDictionary functionsToDefine,414            List<PSVariable> variablesToDefine,415            params object[] args)416        {417            Dictionary<string, ScriptBlock> functionsToDefineDictionary = null;418            if (functionsToDefine != null)419            {420                functionsToDefineDictionary = new Dictionary<string, ScriptBlock>();421                foreach (DictionaryEntry pair in functionsToDefine)422                {423                    string functionName = pair.Key as string;424                    if (string.IsNullOrWhiteSpace(functionName))425                    {426                        PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(427                            ParserStrings.EmptyFunctionNameInFunctionDefinitionDictionary);428 429                        e.SetErrorId("EmptyFunctionNameInFunctionDefinitionDictionary");430                        throw e;431                    }432 433                    ScriptBlock functionBody = pair.Value as ScriptBlock;434                    // null check for functionBody is done at the lower layer.435                    functionsToDefineDictionary.Add(functionName, functionBody);436                }437            }438 439            return InvokeWithContext(440                functionsToDefineDictionary,441                variablesToDefine,442                args);443        }444 445        /// <summary>446        /// A method that allows a scriptblock to be invoked with additional context in the form of a447        /// set of local functions and variables to be defined in the scriptblock's scope. The list of448        /// variables may include the special variables $input, $_ and $this.449        /// </summary>450        /// <param name="functionsToDefine">A dictionary of functions to define.</param>451        /// <param name="variablesToDefine">A list of variables to define.</param>452        /// <param name="args">The arguments to the actual scriptblock.</param>453        /// <returns></returns>454        public Collection<PSObject> InvokeWithContext(455            Dictionary<string, ScriptBlock> functionsToDefine,456            List<PSVariable> variablesToDefine,457            params object[] args)458        {459            object input = AutomationNull.Value;460            object dollarUnder = AutomationNull.Value;461            object scriptThis = AutomationNull.Value;462 463            if (variablesToDefine != null)464            {465                // Extract the special variables "this", "input" and "_"466                PSVariable located = variablesToDefine.Find(467                    v => string.Equals(v.Name, "this", StringComparison.OrdinalIgnoreCase));468                if (located != null)469                {470                    scriptThis = located.Value;471                    variablesToDefine.Remove(located);472                }473 474                located = variablesToDefine.Find(475                    v => string.Equals(v.Name, "_", StringComparison.Ordinal));476                if (located != null)477                {478                    dollarUnder = located.Value;479                    variablesToDefine.Remove(located);480                }481 482                located = variablesToDefine.Find(483                    v => string.Equals(v.Name, "input", StringComparison.OrdinalIgnoreCase));484                if (located != null)485                {486                    input = located.Value;487                    variablesToDefine.Remove(located);488                }489            }490 491            List<object> result = new List<object>();492            Pipe outputPipe = new Pipe(result);493 494            InvokeWithPipe(495                useLocalScope: true,496                functionsToDefine: functionsToDefine,497                variablesToDefine: variablesToDefine,498                errorHandlingBehavior: ErrorHandlingBehavior.WriteToCurrentErrorPipe,499                dollarUnder: dollarUnder,500                input: input,501                scriptThis: scriptThis,502                outputPipe: outputPipe,503                invocationInfo: null,504                args: args);505            return GetWrappedResult(result);506        }507 508        /// <summary>509        /// Execute this node with the specified arguments. The arguments show510        /// up in the script as $args. This overload return the raw (unwrapped) result511        /// so it can be more efficient.512        /// </summary>513        /// <param name="args">The arguments to pass to this scriptblock.</param>514        /// <returns>The object(s) generated during the execution of the515        /// script block. They may or may not be wrapped in PSObject. It's up to the caller to check.</returns>516        /// <exception cref="RuntimeException">Thrown if a script runtime exceptionexception occurred.</exception>517        /// <exception cref="FlowControlException">An internal (non-public) exception from a flow control statement.</exception>518        public object InvokeReturnAsIs(params object[] args)519            => DoInvokeReturnAsIs(520                useLocalScope: true,521                errorHandlingBehavior: ErrorHandlingBehavior.WriteToExternalErrorPipe,522                dollarUnder: AutomationNull.Value,523                input: AutomationNull.Value,524                scriptThis: AutomationNull.Value,525                args: args);526 527        internal T InvokeAsMemberFunctionT<T>(object instance, object[] args)528        {529            List<object> result = new List<object>();530            Pipe pipe = new Pipe(result);531 532            InvokeWithPipe(533                useLocalScope: true,534                errorHandlingBehavior: ErrorHandlingBehavior.WriteToExternalErrorPipe,535                dollarUnder: AutomationNull.Value,536                input: AutomationNull.Value,537                scriptThis: instance ?? AutomationNull.Value,538                outputPipe: pipe,539                invocationInfo: null,540                propagateAllExceptionsToTop: true,541                args: args);542 543            // This is needed only for the case where the544            // method returns [object]. If the argument to 'return'545            // is a pipeline that emits nothing then result.Count will546            // be zero so we catch that and "convert" it to null. Note that547            // the return statement is still required in the method, it548            // just receives nothing from it's argument.549            if (result.Count == 0)550            {551                return default(T);552            }553 554            return (T)result[0];555        }556 557        internal void InvokeAsMemberFunction(object instance, object[] args)558        {559            List<object> result = new List<object>();560            Pipe pipe = new Pipe(result);561 562            InvokeWithPipe(563                useLocalScope: true,564                errorHandlingBehavior: ErrorHandlingBehavior.WriteToCurrentErrorPipe,565                dollarUnder: AutomationNull.Value,566                input: AutomationNull.Value,567                scriptThis: instance ?? AutomationNull.Value,568                outputPipe: pipe,569                invocationInfo: null,570                propagateAllExceptionsToTop: true,571                args: args);572            Diagnostics.Assert(result.Count == 0, "Code generation ensures we return the correct type");573        }574 575        /// <summary>576        /// Return all attributes on a script block.577        /// </summary>578        public List<Attribute> Attributes { get => GetAttributes(); }579 580        /// <summary>581        /// The script file that defined this script block.582        /// </summary>583        public string File { get => GetFileName(); }584 585        /// <summary>586        /// Get/set whether this scriptblock is a filter.587        /// </summary>588        public bool IsFilter { get => _scriptBlockData.IsFilter; }589 590        /// <summary>591        /// Get/set whether this scriptblock is a Configuration.592        /// </summary>593        public bool IsConfiguration { get => _scriptBlockData.GetIsConfiguration(); }594 595        /// <summary>596        /// Get the PSModuleInfo object for the module that defined this597        /// scriptblock.598        /// </summary>599        public PSModuleInfo Module { get => SessionStateInternal?.Module; }600 601        /// <summary>602        /// Return the PSToken object for this function definition...603        /// </summary>604        public PSToken StartPosition { get => GetStartPosition(); }605 606        // LanguageMode is a nullable PSLanguageMode enumeration because script blocks607        // need to inherit the language mode from the context in which they are executing.608        // We can't assume FullLanguage by default when there is no context, as there are609        // script blocks (such as the script blocks used in Workflow activities) that are610        // created by the host without a "current language mode" to inherit. They ultimately611        // get their language mode set when they are finally invoked in a constrained612        // language runspace.613        // Script blocks that should always be run under FullLanguage mode (i.e.: set in614        // InitialSessionState, etc.) should explicitly set the LanguageMode to FullLanguage615        // when they are created.616        internal PSLanguageMode? LanguageMode { get; set; }617 618        internal enum ErrorHandlingBehavior619        {620            WriteToCurrentErrorPipe = 1,621            WriteToExternalErrorPipe = 2,622            SwallowErrors = 3,623        }624 625        internal ReadOnlyCollection<PSTypeName> OutputType626        {627            get628            {629                List<PSTypeName> result = new List<PSTypeName>();630                foreach (Attribute attribute in Attributes)631                {632                    OutputTypeAttribute outputType = attribute as OutputTypeAttribute;633                    if (outputType != null)634                    {635                        result.AddRange(outputType.Type);636                    }637                }638 639                return new ReadOnlyCollection<PSTypeName>(result);640            }641        }642 643        /// <summary>644        /// This is a helper function to process script invocation result.645        /// </summary>646        /// <remarks>647        /// This does normal array reduction in the case of a one-element array.648        /// </remarks>649        internal static object GetRawResult(List<object> result, bool wrapToPSObject)650        {651            switch (result.Count)652            {653                case 0:654                    return AutomationNull.Value;655                case 1:656                    return wrapToPSObject ? LanguagePrimitives.AsPSObjectOrNull(result[0]) : result[0];657                default:658                    object resultArray = result.ToArray();659                    return wrapToPSObject ? LanguagePrimitives.AsPSObjectOrNull(resultArray) : resultArray;660            }661        }662 663        internal void InvokeUsingCmdlet(664            Cmdlet contextCmdlet,665            bool useLocalScope,666            ErrorHandlingBehavior errorHandlingBehavior,667            object dollarUnder,668            object input,669            object scriptThis,670            object[] args)671        {672            Diagnostics.Assert(contextCmdlet != null, "caller to verify contextCmdlet parameter");673 674            Pipe outputPipe = ((MshCommandRuntime)contextCmdlet.CommandRuntime).OutputPipe;675            ExecutionContext context = GetContextFromTLS();676            var myInv = context.EngineSessionState.CurrentScope.GetAutomaticVariableValue(AutomaticVariable.MyInvocation);677            InvocationInfo inInfo = myInv == AutomationNull.Value ? null : (InvocationInfo)myInv;678            InvokeWithPipe(679                useLocalScope,680                errorHandlingBehavior,681                dollarUnder,682                input,683                scriptThis,684                outputPipe,685                inInfo,686                propagateAllExceptionsToTop: false,687                args: args);688        }689 690        /// <summary>691        /// The internal session state object associated with this scriptblock.692        /// </summary>693        internal SessionStateInternal SessionStateInternal { get; set; }694 695        /// <summary>696        /// The session state instance that should be used when evaluating697        /// this scriptblock.698        /// </summary>699        internal SessionState SessionState700        {701            get702            {703                if (SessionStateInternal == null)704                {705                    ExecutionContext context = LocalPipeline.GetExecutionContextFromTLS();706                    if (context != null)707                    {708                        SessionStateInternal = context.EngineSessionState.PublicSessionState.Internal;709                    }710                }711 712                return SessionStateInternal?.PublicSessionState;713            }714 715            set716            {717                if (value == null)718                {719                    throw PSTraceSource.NewArgumentNullException(nameof(value));720                }721 722                SessionStateInternal = value.Internal;723            }724        }725 726        #region Delegates727 728        private static readonly ConditionalWeakTable<ScriptBlock, ConcurrentDictionary<Type, Delegate>> s_delegateTable =729            new ConditionalWeakTable<ScriptBlock, ConcurrentDictionary<Type, Delegate>>();730 731        internal Delegate GetDelegate(Type delegateType)732            => s_delegateTable.GetOrCreateValue(this).GetOrAdd(delegateType, CreateDelegate);733 734        /// <summary>735        /// Get the delegate method as a call back.736        /// </summary>737        internal Delegate CreateDelegate(Type delegateType)738        {739            MethodInfo invokeMethod = delegateType.GetMethod("Invoke");740            ParameterInfo[] parameters = invokeMethod.GetParameters();741            if (invokeMethod.ContainsGenericParameters)742            {743                throw new ScriptBlockToPowerShellNotSupportedException(744                    "CantConvertScriptBlockToOpenGenericType",745                    null,746                    "AutomationExceptions",747                    "CantConvertScriptBlockToOpenGenericType",748                    delegateType);749            }750 751            var parameterExprs = new List<ParameterExpression>();752            foreach (var parameter in parameters)753            {754                parameterExprs.Add(Expression.Parameter(parameter.ParameterType));755            }756 757            bool returnsSomething = !invokeMethod.ReturnType.Equals(typeof(void));758 759            Expression dollarUnderExpr;760            Expression dollarThisExpr;761            if (parameters.Length == 2 && !returnsSomething)762            {763                // V1 was designed for System.EventHandler and not much else.764                // The first arg (sender) was bound to $this, the second (e or EventArgs) was bound to $_.765                // We do this for backwards compatibility, but we also bind the parameters (or $args) for766                // consistency w/ delegates that take more or fewer parameters.767                dollarUnderExpr = parameterExprs[1].Cast(typeof(object));768                dollarThisExpr = parameterExprs[0].Cast(typeof(object));769            }770            else771            {772                dollarUnderExpr = ExpressionCache.AutomationNullConstant;773                dollarThisExpr = ExpressionCache.AutomationNullConstant;774            }775 776            Expression call = Expression.Call(777                Expression.Constant(this),778                CachedReflectionInfo.ScriptBlock_InvokeAsDelegateHelper,779                dollarUnderExpr,780                dollarThisExpr,781                Expression.NewArrayInit(typeof(object), parameterExprs.Select(static p => p.Cast(typeof(object)))));782            if (returnsSomething)783            {784                call = DynamicExpression.Dynamic(785                    PSConvertBinder.Get(invokeMethod.ReturnType),786                    invokeMethod.ReturnType,787                    call);788            }789 790            return Expression.Lambda(delegateType, call, parameterExprs).Compile();791        }792 793        internal object InvokeAsDelegateHelper(object dollarUnder, object dollarThis, object[] args)794        {795            // Retrieve context and current runspace to ensure that we throw exception, if this is non-default runspace.796            ExecutionContext context = GetContextFromTLS();797            RunspaceBase runspace = (RunspaceBase)context.CurrentRunspace;798 799            List<object> rawResult = new List<object>();800            Pipe outputPipe = new Pipe(rawResult);801            InvokeWithPipe(802                useLocalScope: true,803                errorHandlingBehavior: ErrorHandlingBehavior.WriteToCurrentErrorPipe,804                dollarUnder: dollarUnder,805                input: null,806                scriptThis: dollarThis,807                outputPipe: outputPipe,808                invocationInfo: null,809                args: args);810            return GetRawResult(rawResult, wrapToPSObject: false);811        }812 813        #endregion814 815        /// <summary>816        /// Returns the current execution context from TLS, or raises an exception if it is null.817        /// </summary>818        /// <exception cref="InvalidOperationException">819        /// An attempt was made to use the scriptblock outside the engine.820        /// </exception>821        internal ExecutionContext GetContextFromTLS()822        {823            ExecutionContext context = LocalPipeline.GetExecutionContextFromTLS();824 825            // If ExecutionContext from TLS is null then we are not in powershell engine thread.826            if (context == null)827            {828                string scriptText = this.ToString();829 830                scriptText = ErrorCategoryInfo.Ellipsize(CultureInfo.CurrentUICulture, scriptText);831 832                PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(833                    ParserStrings.ScriptBlockDelegateInvokedFromWrongThread,834                    scriptText);835 836                e.SetErrorId("ScriptBlockDelegateInvokedFromWrongThread");837                throw e;838            }839 840            return context;841        }842 843        /// <summary>844        /// Execute this node with the specified arguments. The arguments show845        /// up in the script as $args with $_ being the first argument.846        /// </summary>847        /// <param name="dollarUnder">848        /// The value of the $_ variable for the script block. If AutomationNull.Value,849        /// the $_ variable is not created.850        /// </param>851        /// <param name="input">852        /// The value of the $input variable for the script block. If AutomationNull.Value,853        /// the $input variable is not created.854        /// </param>855        /// <param name="args">The arguments to this script.</param>856        /// <returns>The object(s) generated during the execution of857        /// the script block returned as a collection of PSObjects.</returns>858        /// <exception cref="RuntimeException">A script exception occurred.</exception>859        /// <exception cref="FlowControlException">Internal exception from a flow control statement.</exception>860        internal Collection<PSObject> DoInvoke(object dollarUnder, object input, object[] args)861        {862            List<object> result = new List<object>();863            Pipe outputPipe = new Pipe(result);864            InvokeWithPipe(865                useLocalScope: true,866                errorHandlingBehavior: ErrorHandlingBehavior.WriteToExternalErrorPipe,867                dollarUnder: dollarUnder,868                input: input,869                scriptThis: AutomationNull.Value,870                outputPipe: outputPipe,871                invocationInfo: null,872                args: args);873            return GetWrappedResult(result);874        }875 876        /// <summary>877        /// This is a helper function to wrap script execution results878        /// in PSObjects.879        /// </summary>880        /// <param name="result"></param>881        /// <returns></returns>882        private static Collection<PSObject> GetWrappedResult(List<object> result)883        {884            if (result == null || result.Count == 0)885            {886                return new Collection<PSObject>();887            }888 889            Collection<PSObject> wrappedResult = new Collection<PSObject>();890            for (int i = 0; i < result.Count; i++)891            {892                wrappedResult.Add(LanguagePrimitives.AsPSObjectOrNull(result[i]));893            }894 895            return wrappedResult;896        }897 898        /// <summary>899        /// Execute this node with the specified arguments. The arguments show900        /// up in the script as $args with $_ being the first argument.901        /// </summary>902        /// <param name="useLocalScope"></param>903        /// <param name="errorHandlingBehavior"></param>904        /// <param name="dollarUnder">905        ///   The value of the $_ variable for the script block. If AutomationNull.Value,906        ///   the $_ variable is not created.907        /// </param>908        /// <param name="input">909        ///   The value of the $input variable for the script block. If AutomationNull.Value,910        ///   the $input variable is not created.911        /// </param>912        /// <param name="scriptThis"></param>913        /// <param name="args">The arguments to this script.</param>914        /// <returns>The object(s) generated during the execution of915        /// the script block returned as a collection of PSObjects.</returns>916        /// <exception cref="RuntimeException">A script exception occurred.</exception>917        /// <exception cref="FlowControlException">Internal exception from a flow control statement.</exception>918        internal object DoInvokeReturnAsIs(919            bool useLocalScope,920            ErrorHandlingBehavior errorHandlingBehavior,921            object dollarUnder,922            object input,923            object scriptThis,924            object[] args)925        {926            List<object> result = new List<object>();927            Pipe outputPipe = new Pipe(result);928            InvokeWithPipe(929                useLocalScope: useLocalScope,930                errorHandlingBehavior: errorHandlingBehavior,931                dollarUnder: dollarUnder,932                input: input,933                scriptThis: scriptThis,934                outputPipe: outputPipe,935                invocationInfo: null,936                args: args);937            return GetRawResult(result, wrapToPSObject: true);938        }939 940        internal void InvokeWithPipe(941            bool useLocalScope,942            ErrorHandlingBehavior errorHandlingBehavior,943            object dollarUnder,944            object input,945            object scriptThis,946            Pipe outputPipe,947            InvocationInfo invocationInfo,948            bool propagateAllExceptionsToTop = false,949            List<PSVariable> variablesToDefine = null,950            Dictionary<string, ScriptBlock> functionsToDefine = null,951            object[] args = null)952        {953            bool shouldGenerateEvent = false;954            bool oldPropagateExceptions = false;955            ExecutionContext context = LocalPipeline.GetExecutionContextFromTLS();956 957            if (SessionStateInternal != null && SessionStateInternal.ExecutionContext != context)958            {959                context = SessionStateInternal.ExecutionContext;960                shouldGenerateEvent = true;961            }962            else if (context == null)963            {964                // This will throw.965                GetContextFromTLS();966            }967            else968            {969                if (propagateAllExceptionsToTop)970                {971                    oldPropagateExceptions = context.PropagateExceptionsToEnclosingStatementBlock;972                    context.PropagateExceptionsToEnclosingStatementBlock = true;973                }974 975                try976                {977                    var runspace = (RunspaceBase)context.CurrentRunspace;978                    if (runspace.CanRunActionInCurrentPipeline())979                    {980                        InvokeWithPipeImpl(981                            useLocalScope,982                            functionsToDefine,983                            variablesToDefine,984                            errorHandlingBehavior,985                            dollarUnder,986                            input,987                            scriptThis,988                            outputPipe,989                            invocationInfo,990                            args);991                    }992                    else993                    {994                        shouldGenerateEvent = true;995                    }996                }997                finally998                {999                    if (propagateAllExceptionsToTop)1000                    {1001                        context.PropagateExceptionsToEnclosingStatementBlock = oldPropagateExceptions;1002                    }1003                }1004            }1005 1006            if (shouldGenerateEvent)1007            {1008                context.Events.SubscribeEvent(1009                    source: null,1010                    eventName: PSEngineEvent.OnScriptBlockInvoke,1011                    sourceIdentifier: PSEngineEvent.OnScriptBlockInvoke,1012                    data: null,1013                    handlerDelegate: new PSEventReceivedEventHandler(OnScriptBlockInvokeEventHandler),1014                    supportEvent: true,1015                    forwardEvent: false,1016                    shouldQueueAndProcessInExecutionThread: true,1017                    maxTriggerCount: 1);1018 1019                var scriptBlockInvocationEventArgs = new ScriptBlockInvocationEventArgs(1020                    scriptBlock: this,1021                    useLocalScope,1022                    errorHandlingBehavior,1023                    dollarUnder,1024                    input,1025                    scriptThis,1026                    outputPipe,1027                    invocationInfo,1028                    args);1029 1030                context.Events.GenerateEvent(1031                    sourceIdentifier: PSEngineEvent.OnScriptBlockInvoke,1032                    sender: null,1033                    args: new object[1] { scriptBlockInvocationEventArgs },1034                    extraData: null,1035                    processInCurrentThread: true,1036                    waitForCompletionInCurrentThread: true);1037 1038                scriptBlockInvocationEventArgs.Exception?.Throw();1039            }1040        }1041 1042        /// <summary>1043        /// Handles OnScriptBlockInvoke event, this is called by the event manager.1044        /// </summary>1045        private static void OnScriptBlockInvokeEventHandler(object sender, PSEventArgs args)1046        {1047            var eventArgs = (object)args.SourceEventArgs as ScriptBlockInvocationEventArgs;1048            Diagnostics.Assert(eventArgs != null,1049                "Event Arguments to OnScriptBlockInvokeEventHandler should not be null");1050 1051            try1052            {1053                ScriptBlock sb = eventArgs.ScriptBlock;1054                sb.InvokeWithPipeImpl(1055                    eventArgs.UseLocalScope,1056                    functionsToDefine: null,1057                    variablesToDefine: null,1058                    eventArgs.ErrorHandlingBehavior,1059                    eventArgs.DollarUnder,1060                    eventArgs.Input,1061                    eventArgs.ScriptThis,1062                    eventArgs.OutputPipe,1063                    eventArgs.InvocationInfo,1064                    eventArgs.Args);1065            }1066            catch (Exception e)1067            {1068                eventArgs.Exception = ExceptionDispatchInfo.Capture(e);1069            }1070        }1071 1072        internal void SetPSScriptRootAndPSCommandPath(MutableTuple locals, ExecutionContext context)1073        {1074            var psScriptRoot = string.Empty;1075            var psCommandPath = string.Empty;1076            if (!string.IsNullOrEmpty(File))1077            {1078                psScriptRoot = Path.GetDirectoryName(File);1079                psCommandPath = File;1080            }1081 1082            locals.SetAutomaticVariable(AutomaticVariable.PSScriptRoot, psScriptRoot, context);1083            locals.SetAutomaticVariable(AutomaticVariable.PSCommandPath, psCommandPath, context);1084        }1085    }1086 1087    /// <summary>1088    /// A steppable pipeline wrapper object...1089    /// </summary>1090    [SuppressMessage(1091        "Microsoft.Naming",1092        "CA1704:IdentifiersShouldBeSpelledCorrectly",1093        MessageId = "Steppable",1094        Justification = "Consider Name change during API review")]1095    public sealed class SteppablePipeline : IDisposable1096    {1097        internal SteppablePipeline(ExecutionContext context, PipelineProcessor pipeline)1098        {1099            ArgumentNullException.ThrowIfNull(pipeline);1100 1101            ArgumentNullException.ThrowIfNull(context);1102 1103            _pipeline = pipeline;1104            _context = context;1105        }1106 1107        private readonly PipelineProcessor _pipeline;1108        private readonly ExecutionContext _context;1109        private bool _expectInput;1110 1111        /// <summary>1112        /// Begin execution of a steppable pipeline. This overload doesn't reroute output and error pipes.1113        /// </summary>1114        /// <param name="expectInput"><see langword="true"/> if you plan to write input into this pipe; <see langword="false"/> otherwise.</param>1115        public void Begin(bool expectInput) => Begin(expectInput, commandRuntime: (ICommandRuntime)null);1116 1117        /// <summary>1118        /// Begin execution of a steppable pipeline, using the command running currently in the specified context to figure1119        /// out how to route the output and errors.1120        /// </summary>1121        /// <param name="expectInput"><see langword="true"/> if you plan to write input into this pipe; <see langword="false"/> otherwise.</param>1122        /// <param name="contextToRedirectTo">Context used to figure out how to route the output and errors.</param>1123        public void Begin(bool expectInput, EngineIntrinsics contextToRedirectTo)1124        {1125            ArgumentNullException.ThrowIfNull(contextToRedirectTo);1126 1127            ExecutionContext executionContext = contextToRedirectTo.SessionState.Internal.ExecutionContext;1128            CommandProcessorBase commandProcessor = executionContext.CurrentCommandProcessor;1129            ICommandRuntime crt = commandProcessor?.CommandRuntime;1130            Begin(expectInput, crt);1131        }1132 1133        /// <summary>1134        /// Begin execution of a steppable pipeline, using the calling command to figure1135        /// out how to route the output and errors. This is the most effective1136        /// way to start stepping.1137        /// </summary>1138        /// <param name="command">The command you're calling this from (i.e. instance of PSCmdlet or value of $PSCmdlet variable).</param>1139        public void Begin(InternalCommand command)1140        {1141            if (command is null || command.MyInvocation is null)1142            {1143                throw new ArgumentNullException(nameof(command));1144            }1145 1146            Begin(command.MyInvocation.ExpectingInput, command.commandRuntime);1147        }1148 1149        private void Begin(bool expectInput, ICommandRuntime commandRuntime)1150        {1151            try1152            {1153                _pipeline.ExecutionScope = _context.EngineSessionState.CurrentScope;1154                _context.PushPipelineProcessor(_pipeline);1155                _expectInput = expectInput;1156 1157                // Start the pipeline, if the command calling this pipeline is1158                // not expecting input (as indicated by it's position in the pipeline1159                // then neither should we.1160                MshCommandRuntime crt = commandRuntime as MshCommandRuntime;1161                if (crt != null)1162                {1163                    if (crt.OutputPipe != null)1164                    {1165                        _pipeline.LinkPipelineSuccessOutput(crt.OutputPipe);1166                    }1167 1168                    if (crt.ErrorOutputPipe != null)1169                    {1170                        _pipeline.LinkPipelineErrorOutput(crt.ErrorOutputPipe);1171                    }1172                }1173 1174                _pipeline.StartStepping(_expectInput);1175            }1176            finally1177            {1178                // then pop this pipeline...1179                _context.PopPipelineProcessor(true);1180            }1181        }1182 1183        /// <summary>1184        /// Process a single input object.1185        /// </summary>1186        /// <param name="input">The object to process.</param>1187        /// <returns>A collection of 0 or more result objects.</returns>1188        public Array Process(object input)1189        {1190            try1191            {1192                _context.PushPipelineProcessor(_pipeline);1193                if (_expectInput)1194                {1195                    return _pipeline.Step(input);1196                }1197                else1198                {1199                    return _pipeline.Step(AutomationNull.Value);1200                }

Showing the first 1,200 of 1480 lines. Download the file for the rest.