Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
InternalHost.cs582 linesDownload Raw Back to hostifaces
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.Globalization;6using System.Management.Automation.Host;7using System.Management.Automation.Remoting;8using System.Management.Automation.Runspaces;9 10using Dbg = System.Management.Automation.Diagnostics;11 12#pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings13 14namespace System.Management.Automation.Internal.Host15{16    /// <summary>17    /// Wraps PSHost instances to provide a shim layer18    /// between InternalCommand and the host-supplied PSHost instance.19    ///20    /// This class exists for the purpose of ensuring that an externally-supplied PSHost meets the minimum proper required21    /// implementation, and also to provide a leverage point at which the monad engine can hook the interaction between the engine,22    /// cmdlets, and that external host.23    ///24    /// That leverage may be necessary to manage concurrent access between multiple pipelines sharing the same instance of25    /// PSHost.26    /// </summary>27    internal class InternalHost : PSHost, IHostSupportsInteractiveSession28    {29        /// <summary>30        /// There should only be one instance of InternalHost per runspace (i.e. per engine), and all engine use of the host31        /// should be through that single instance.  If we ever accidentally create more than one instance of InternalHost per32        /// runspace, then some of the internal state checks that InternalHost makes, like checking the nestedPromptCounter, can33        /// be messed up.34        ///35        /// To ensure that this constraint is met, I wanted to make this class a singleton.  However, Hitesh rightly pointed out36        /// that a singleton would be appdomain-global, which would prevent having multiple runspaces per appdomain. So we will37        /// just have to be careful not to create extra instances of InternalHost per runspace.38        /// </summary>39        internal InternalHost(PSHost externalHost, ExecutionContext executionContext)40        {41            Dbg.Assert(externalHost != null, "must supply an PSHost");42            Dbg.Assert(externalHost is not InternalHost, "try to create an InternalHost from another InternalHost");43 44            Dbg.Assert(executionContext != null, "must supply an ExecutionContext");45 46            _externalHostRef = new ObjectRef<PSHost>(externalHost);47            Context = executionContext;48 49            PSHostUserInterface ui = externalHost.UI;50 51            _internalUIRef = new ObjectRef<InternalHostUserInterface>(new InternalHostUserInterface(ui, this));52            _zeroGuid = new Guid(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);53            _idResult = _zeroGuid;54        }55 56        /// <summary>57        /// See base class.58        /// </summary>59        /// <value></value>60        /// <exception cref="NotImplementedException">61        ///  when the external host's Name is null or empty.62        /// </exception>63        public override string Name64        {65            get66            {67                if (string.IsNullOrEmpty(_nameResult))68                {69                    _nameResult = _externalHostRef.Value.Name;70 71#pragma warning disable 5650372                    if (string.IsNullOrEmpty(_nameResult))73                    {74                        throw PSTraceSource.NewNotImplementedException();75                    }76#pragma warning restore 5650377                }78 79                return _nameResult;80            }81        }82 83        /// <summary>84        /// See base class.85        /// </summary>86        /// <value></value>87        /// <exception cref="NotImplementedException">88        ///  when the external host's Version is null.89        /// </exception>90        public override System.Version Version91        {92            get93            {94                if (_versionResult == null)95                {96                    _versionResult = _externalHostRef.Value.Version;97 98#pragma warning disable 5650399                    if (_versionResult == null)100                    {101                        throw PSTraceSource.NewNotImplementedException();102                    }103#pragma warning restore 56503104                }105 106                return _versionResult;107            }108        }109 110        /// <summary>111        /// See base class.112        /// </summary>113        /// <value></value>114        /// <exception cref="NotImplementedException">115        ///  when the external host's InstanceId is a zero Guid.116        /// </exception>117        public override System.Guid InstanceId118        {119            get120            {121                if (_idResult == _zeroGuid)122                {123                    _idResult = _externalHostRef.Value.InstanceId;124 125#pragma warning disable 56503126                    if (_idResult == _zeroGuid)127                    {128                        throw PSTraceSource.NewNotImplementedException();129                    }130#pragma warning restore 56503131                }132 133                return _idResult;134            }135        }136 137        /// <summary>138        /// See base class.139        /// </summary>140        /// <value>141        /// </value>142        public override System.Management.Automation.Host.PSHostUserInterface UI143        {144            get145            {146                return _internalUIRef.Value;147            }148        }149 150        /// <summary>151        /// Interface to be used for interaction with internal152        /// host UI. InternalHostUserInterface wraps the host UI153        /// supplied during construction. Use this wrapper to access154        /// functionality specific to InternalHost.155        /// </summary>156        internal InternalHostUserInterface InternalUI157        {158            get159            {160                return _internalUIRef.Value;161            }162        }163 164        /// <summary>165        /// See base class.166        /// </summary>167        /// <value>168        /// </value>169        /// <exception cref="NotImplementedException">170        ///  when the external host's CurrentCulture is null.171        /// </exception>172        public override System.Globalization.CultureInfo CurrentCulture173        {174            get175            {176                CultureInfo ci = _externalHostRef.Value.CurrentCulture ?? CultureInfo.InvariantCulture;177 178                return ci;179            }180        }181 182        /// <summary>183        /// See base class.184        /// </summary>185        /// <value>186        /// </value>187        /// <exception cref="NotImplementedException">188        /// If the external host's CurrentUICulture is null.189        /// </exception>190        public override CultureInfo CurrentUICulture191        {192            get193            {194                CultureInfo ci = _externalHostRef.Value.CurrentUICulture ?? CultureInfo.InstalledUICulture;195                return ci;196            }197        }198 199        /// <summary>200        /// See base class.201        /// </summary>202        /// <param name="exitCode"></param>203        public override void SetShouldExit(int exitCode)204        {205            _externalHostRef.Value.SetShouldExit(exitCode);206        }207 208        /// <summary>209        /// See base class210        /// <seealso cref="ExitNestedPrompt"/>211        /// </summary>212        public override void EnterNestedPrompt()213        {214            EnterNestedPrompt(null);215        }216 217        private struct PromptContextData218        {219            public object SavedCurrentlyExecutingCommandVarValue;220            public object SavedPSBoundParametersVarValue;221            public ExecutionContext.SavedContextData SavedContextData;222            public RunspaceAvailability RunspaceAvailability;223            public PSLanguageMode LanguageMode;224        }225 226        /// <summary>227        /// Internal proxy for EnterNestedPrompt.228        /// </summary>229        /// <param name="callingCommand"></param>230        internal void EnterNestedPrompt(InternalCommand callingCommand)231        {232            // Ensure we are in control of the pipeline233            LocalRunspace localRunspace = null;234 235            // This needs to be in a try / catch, since the LocalRunspace cast236            // tries to verify that the host supports interactive sessions.237            // Tests hosts do not.238            try { localRunspace = this.Runspace as LocalRunspace; }239            catch (PSNotImplementedException) { }240 241            if (localRunspace != null)242            {243                Pipeline currentlyRunningPipeline = this.Runspace.GetCurrentlyRunningPipeline();244 245                if ((currentlyRunningPipeline != null) &&246                    (currentlyRunningPipeline == localRunspace.PulsePipeline))247                    throw new InvalidOperationException();248            }249 250            // NTRAID#Windows OS Bugs-986407-2004/07/29 When kumarp has done the configuration work in the engine, it251            // should include setting a bit that indicates that the initialization is complete, and code should be252            // added here to throw an exception if this function is called before that bit is set.253 254            if (NestedPromptCount < 0)255            {256                Dbg.Assert(false, "nested prompt counter should never be negative.");257                throw PSTraceSource.NewInvalidOperationException(258                    InternalHostStrings.EnterExitNestedPromptOutOfSync);259            }260 261            // Increment our nesting counter.  When we set the value of the variable, we will replace any existing variable262            // of the same name.  This is good, as any existing value is either 1) ours, and we have claim to replace it, or263            // 2) is a squatter, and we have claim to clobber it.264 265            ++NestedPromptCount;266            Context.SetVariable(SpecialVariables.NestedPromptCounterVarPath, NestedPromptCount);267 268            // On entering a subshell, save and reset values of certain bits of session state269 270            PromptContextData contextData = new PromptContextData();271            contextData.SavedContextData = Context.SaveContextData();272            contextData.SavedCurrentlyExecutingCommandVarValue = Context.GetVariableValue(SpecialVariables.CurrentlyExecutingCommandVarPath);273            contextData.SavedPSBoundParametersVarValue = Context.GetVariableValue(SpecialVariables.PSBoundParametersVarPath);274            contextData.RunspaceAvailability = this.Context.CurrentRunspace.RunspaceAvailability;275            contextData.LanguageMode = Context.LanguageMode;276 277            PSPropertyInfo commandInfoProperty = null;278            PSPropertyInfo stackTraceProperty = null;279            object oldCommandInfo = null;280            object oldStackTrace = null;281            if (callingCommand != null)282            {283                Dbg.Assert(callingCommand.Context == Context, "I expect that the contexts should match");284 285                // Populate $CurrentlyExecutingCommand to facilitate debugging.  One of the gotchas is that we are going to want286                // to expose more and more debug info. We could just populate more and more local variables but that is probably287                // a lousy approach as it pollutes the namespace.  A better way to do it is to add NOTES to the variable value288                // object.289 290                PSObject newValue = PSObject.AsPSObject(callingCommand);291 292                commandInfoProperty = newValue.Properties["CommandInfo"];293                if (commandInfoProperty == null)294                {295                    newValue.Properties.Add(new PSNoteProperty("CommandInfo", callingCommand.CommandInfo));296                }297                else298                {299                    oldCommandInfo = commandInfoProperty.Value;300                    commandInfoProperty.Value = callingCommand.CommandInfo;301                }302 303                stackTraceProperty = newValue.Properties["StackTrace"];304                if (stackTraceProperty == null)305                {306                    newValue.Properties.Add(new PSNoteProperty("StackTrace", new System.Diagnostics.StackTrace()));307                }308                else309                {310                    oldStackTrace = stackTraceProperty.Value;311                    stackTraceProperty.Value = new System.Diagnostics.StackTrace();312                }313 314                Context.SetVariable(SpecialVariables.CurrentlyExecutingCommandVarPath, newValue);315            }316 317            _contextStack.Push(contextData);318            Dbg.Assert(_contextStack.Count == NestedPromptCount, "number of saved contexts should equal nesting count");319 320            Context.PSDebugTraceStep = false;321            Context.PSDebugTraceLevel = 0;322            Context.ResetShellFunctionErrorOutputPipe();323 324            // Lock down the language in the nested prompt325            if (Context.HasRunspaceEverUsedConstrainedLanguageMode)326            {327                Context.LanguageMode = PSLanguageMode.ConstrainedLanguage;328            }329 330            this.Context.CurrentRunspace.UpdateRunspaceAvailability(RunspaceAvailability.AvailableForNestedCommand, true);331 332            try333            {334                _externalHostRef.Value.EnterNestedPrompt();335            }336            catch337            {338                // So where things really go south is this path; which is possible for hosts (like our ConsoleHost)339                // that don't return from EnterNestedPrompt immediately.340                //      EnterNestedPrompt() starts341                //          ExitNestedPrompt() called342                //          EnterNestedPrompt throws343 344                ExitNestedPromptHelper();345                throw;346            }347            finally348            {349                if (commandInfoProperty != null)350                {351                    commandInfoProperty.Value = oldCommandInfo;352                }353 354                if (stackTraceProperty != null)355                {356                    stackTraceProperty.Value = oldStackTrace;357                }358            }359 360            Dbg.Assert(NestedPromptCount >= 0, "nestedPromptCounter should be greater than or equal to 0");361        }362 363        private void ExitNestedPromptHelper()364        {365            --NestedPromptCount;366            Context.SetVariable(SpecialVariables.NestedPromptCounterVarPath, NestedPromptCount);367 368            // restore the saved context369 370            Dbg.Assert(_contextStack.Count > 0, "ExitNestedPrompt: called without any saved context");371 372            if (_contextStack.Count > 0)373            {374                PromptContextData pcd = _contextStack.Pop();375 376                pcd.SavedContextData.RestoreContextData(Context);377                Context.LanguageMode = pcd.LanguageMode;378                Context.SetVariable(SpecialVariables.CurrentlyExecutingCommandVarPath, pcd.SavedCurrentlyExecutingCommandVarValue);379                Context.SetVariable(SpecialVariables.PSBoundParametersVarPath, pcd.SavedPSBoundParametersVarValue);380                this.Context.CurrentRunspace.UpdateRunspaceAvailability(pcd.RunspaceAvailability, true);381            }382 383            Dbg.Assert(_contextStack.Count == NestedPromptCount, "number of saved contexts should equal nesting count");384        }385 386        /// <summary>387        /// See base class388        /// <seealso cref="EnterNestedPrompt()"/>389        /// </summary>390        public override void ExitNestedPrompt()391        {392            Dbg.Assert(NestedPromptCount >= 0, "nestedPromptCounter should be greater than or equal to 0");393 394            if (NestedPromptCount == 0)395                return;396 397            try398            {399                _externalHostRef.Value.ExitNestedPrompt();400            }401            finally402            {403                ExitNestedPromptHelper();404            }405 406            ExitNestedPromptException enpe = new ExitNestedPromptException();407            throw enpe;408        }409 410        /// <summary>411        /// See base class.412        /// </summary>413        public override PSObject PrivateData414        {415            get416            {417                PSObject result = _externalHostRef.Value.PrivateData;418                return result;419            }420        }421 422        /// <summary>423        /// See base class424        /// <seealso cref="NotifyEndApplication"/>425        /// </summary>426        public override void NotifyBeginApplication()427        {428            _externalHostRef.Value.NotifyBeginApplication();429        }430 431        /// <summary>432        /// Called by the engine to notify the host that the execution of a legacy command has completed.433        /// <seealso cref="NotifyBeginApplication"/>434        /// </summary>435        public override void NotifyEndApplication()436        {437            _externalHostRef.Value.NotifyEndApplication();438        }439 440        /// <summary>441        /// This property enables and disables the host debugger if debugging is supported.442        /// </summary>443        public override bool DebuggerEnabled { get; set; } = true;444 445        /// <summary>446        /// Gets the external host as an IHostSupportsInteractiveSession if it implements this interface;447        /// throws an exception otherwise.448        /// </summary>449        private IHostSupportsInteractiveSession GetIHostSupportsInteractiveSession()450        {451            if (_externalHostRef.Value is not IHostSupportsInteractiveSession host)452            {453                throw new PSNotImplementedException();454            }455 456            return host;457        }458 459        /// <summary>460        /// Called by the engine to notify the host that a runspace push has been requested.461        /// </summary>462        /// <seealso cref="PopRunspace"/>463        public void PushRunspace(System.Management.Automation.Runspaces.Runspace runspace)464        {465            IHostSupportsInteractiveSession host = GetIHostSupportsInteractiveSession();466            host.PushRunspace(runspace);467        }468 469        /// <summary>470        /// Called by the engine to notify the host that a runspace pop has been requested.471        /// </summary>472        /// <seealso cref="PushRunspace"/>473        public void PopRunspace()474        {475            IHostSupportsInteractiveSession host = GetIHostSupportsInteractiveSession();476            host.PopRunspace();477        }478 479        /// <summary>480        /// True if a runspace is pushed; false otherwise.481        /// </summary>482        public bool IsRunspacePushed483        {484            get485            {486                IHostSupportsInteractiveSession host = GetIHostSupportsInteractiveSession();487                return host.IsRunspacePushed;488            }489        }490 491        /// <summary>492        /// Returns the current runspace associated with this host.493        /// </summary>494        public Runspace Runspace495        {496            get497            {498                IHostSupportsInteractiveSession host = GetIHostSupportsInteractiveSession();499                return host.Runspace;500            }501        }502 503        /// <summary>504        /// Checks if the host is in a nested prompt.505        /// </summary>506        /// <returns>True, if host in nested prompt507        /// false, otherwise.</returns>508        internal bool HostInNestedPrompt()509        {510            if (NestedPromptCount > 0)511            {512                return true;513            }514            else515            {516                return false;517            }518        }519 520        /// <summary>521        /// Sets the reference to the external host and the internal UI to a temporary522        /// new host and its UI. This exists so that if the PowerShell/Pipeline523        /// object has a different host from the runspace it can set it's host during its524        /// invocation, and then revert it after the invocation is completed.525        /// </summary>526        /// <seealso cref="RevertHostRef"/> and527        internal void SetHostRef(PSHost psHost)528        {529            _externalHostRef.Override(psHost);530            _internalUIRef.Override(new InternalHostUserInterface(psHost.UI, this));531        }532 533        /// <summary>534        /// Reverts the temporary host set by SetHost. If no host was temporarily set, this has no effect.535        /// </summary>536        /// <seealso cref="SetHostRef"/> and537        internal void RevertHostRef()538        {539            // nothing to revert if Host reference is not set.540            if (!IsHostRefSet)541            {542                return;543            }544 545            _externalHostRef.Revert();546            _internalUIRef.Revert();547        }548 549        /// <summary>550        /// Returns true if the external host reference is temporarily set to another host, masking the original host.551        /// </summary>552        internal bool IsHostRefSet553        {554            get { return _externalHostRef.IsOverridden; }555        }556 557        internal ExecutionContext Context { get; }558 559        internal PSHost ExternalHost560        {561            get562            {563                return _externalHostRef.Value;564            }565        }566 567        internal int NestedPromptCount { get; private set; }568 569        // Masked variables.570        private readonly ObjectRef<PSHost> _externalHostRef;571        private readonly ObjectRef<InternalHostUserInterface> _internalUIRef;572 573        // Private variables.574        private string _nameResult;575        private Version _versionResult;576        private Guid _idResult;577        private readonly Stack<PromptContextData> _contextStack = new Stack<PromptContextData>();578 579        private readonly Guid _zeroGuid;580    }581}582