/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Management.Automation.Host; using System.Management.Automation.Internal; using System.Management.Automation.Internal.Host; using System.Management.Automation.Runspaces; using System.Runtime.CompilerServices; using Microsoft.PowerShell; using System.Reflection; using System.Security; using System.Diagnostics.CodeAnalysis; using Microsoft.PowerShell.Commands.Internal.Format; namespace System.Management.Automation { /// /// This class contains the execution context that gets passed /// around to commands. This is all of the information that lets you get /// at session state and the host interfaces. /// internal class ExecutionContext { #region Properties /// /// The events received by this runspace /// internal PSLocalEventManager Events { get; private set; } internal HashSet AutoLoadingModuleInProgress { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); /// /// The debugger for the interpreter /// internal ScriptDebugger Debugger { get { return _debugger; } } internal ScriptDebugger _debugger; internal int _debuggingMode; /// /// Reset or clear the various context managers so the runspace can be reused without contamination. /// internal void ResetManagers() { if (_debugger != null) { _debugger.ResetDebugger(); } if (Events != null) { Events.Dispose(); } Events = new PSLocalEventManager(this); if (this.transactionManager != null) { this.transactionManager.Dispose(); } this.transactionManager = new PSTransactionManager(); } /// /// The tracing mode for the interpreter. /// /// True if tracing is turned on, false if it's turned off. internal int PSDebugTraceLevel { get { // Pretend that tracing is off if ignoreScriptDebug is true return IgnoreScriptDebug ? 0 : _debugTraceLevel; } set { _debugTraceLevel = value; } } private int _debugTraceLevel; /// /// The step mode for the interpreter. /// /// True of stepping is turned on, false if it's turned off. internal bool PSDebugTraceStep { get { // Pretend that tracing is off if ignoreScriptDebug is true return !IgnoreScriptDebug && _debugTraceStep; } set { _debugTraceStep = value; } } private bool _debugTraceStep; // Helper for generated code to handle running w/ no execution context internal static bool IsStrictVersion(ExecutionContext context, int majorVersion) { if (context == null) { context = LocalPipeline.GetExecutionContextFromTLS(); } return (context != null) ? context.IsStrictVersion(majorVersion) : false; } /// /// Check to see a specific version of strict mode is enabled. The check is always scoped, /// even though in version 1 the check was engine wide. /// /// The version for a strict check about to be performed. /// internal bool IsStrictVersion(int majorVersion) { SessionStateScope scope = EngineSessionState.CurrentScope; while (scope != null) { // If StrictModeVersion is null, we haven't seen set-strictmode, so check the parent scope. if (scope.StrictModeVersion != null) { // If StrictModeVersion is not null, just check the major version. A version of 0 // is the same as off to make this a simple check. return (scope.StrictModeVersion.Major >= majorVersion); } // We shouldn't check global scope if we were in a module. if (scope == EngineSessionState.ModuleScope) { break; } scope = scope.Parent; } // set-strictmode hasn't been used. return false; } /// /// Is true if the current statement in the interpreter should be traced... /// internal bool ShouldTraceStatement { get { // Pretend that tracing is off if ignoreScriptDebug is true return !IgnoreScriptDebug && (_debugTraceLevel > 0 || _debugTraceStep); } } /// /// If true, then a script command processor should rethrow the exit exception instead of /// simply capturing it. This is used by the -file option on the console host. /// internal bool ScriptCommandProcessorShouldRethrowExit { get; set; } = false; /// /// If this flag is set to true, script trace output /// will not be generated regardless of the state of the /// trace flag. /// /// The current state of the IgnoreScriptDebug flag. internal bool IgnoreScriptDebug { set; get; } = true; /// /// Gets the automation engine instance. /// internal AutomationEngine Engine { get; private set; } /// /// Get the RunspaceConfiguration instance /// internal RunspaceConfiguration RunspaceConfiguration { get; } internal InitialSessionState InitialSessionState { get; } /// /// True if the RunspaceConfiguration/InitialSessionState is for a single shell or false otherwise. /// /// internal bool IsSingleShell { get { RunspaceConfigForSingleShell runSpace = RunspaceConfiguration as RunspaceConfigForSingleShell; return runSpace != null || InitialSessionState != null; } } /// /// Added for Win8: 336382 /// Contains the name of the previous module that was processed. This /// allows you to skip this module when doing a lookup. /// internal string PreviousModuleProcessed { get; set; } /// /// Added for 4980967 /// Contains the name of the latest module that was imported, /// Allows "module\function" to call the function from latest imported module instead of randomly choosing the first module in the moduletable. /// internal Hashtable previousModuleImported { get; set; } = new Hashtable(); /// /// Contains the name of the module currently being processed. This /// allows you to skip this module when doing a lookup. /// internal string ModuleBeingProcessed { get; set; } private bool _responsibilityForModuleAnalysisAppDomainOwned; internal bool TakeResponsibilityForModuleAnalysisAppDomain() { if (_responsibilityForModuleAnalysisAppDomainOwned) { return false; } Diagnostics.Assert(AppDomainForModuleAnalysis == null, "Invalid module analysis app domain state"); _responsibilityForModuleAnalysisAppDomainOwned = true; return true; } internal void ReleaseResponsibilityForModuleAnalysisAppDomain() { Diagnostics.Assert(_responsibilityForModuleAnalysisAppDomainOwned, "Invalid module analysis app domain state"); if (AppDomainForModuleAnalysis != null) { AppDomain.Unload(AppDomainForModuleAnalysis); AppDomainForModuleAnalysis = null; } _responsibilityForModuleAnalysisAppDomainOwned = false; } /// /// The AppDomain currently being used for module analysis. It should only be created if needed, /// but various callers need to take responsbility for unloading the domain via /// the TakeResponsibilityForModuleAnalysisAppDomain. /// internal AppDomain AppDomainForModuleAnalysis { get; set; } /// /// Authorization manager for this runspace /// internal AuthorizationManager AuthorizationManager { get; private set; } /// /// Gets the appropriate provider names for the default /// providers based on the type of the shell /// (single shell or custom shell). /// /// internal ProviderNames ProviderNames { get { if (_providerNames == null) { if (IsSingleShell) { _providerNames = new SingleShellProviderNames(); } else { _providerNames = new CustomShellProviderNames(); } } return _providerNames; } } private ProviderNames _providerNames; /// /// The module information for this engine... /// internal ModuleIntrinsics Modules { get; private set; } /// /// Get the shellID for this runspace... /// internal string ShellID { get { if (_shellId == null) { // Use the ShellID from PSAuthorizationManager before everything else because that's what's used // to check execution policy... if (AuthorizationManager is PSAuthorizationManager && !String.IsNullOrEmpty(AuthorizationManager.ShellId)) { _shellId = AuthorizationManager.ShellId; } else if (RunspaceConfiguration != null && !String.IsNullOrEmpty(RunspaceConfiguration.ShellId)) { // Otherwise fall back to the runspace shell id if it's there... _shellId = RunspaceConfiguration.ShellId; } else { // Finally fall back to the default shell id... _shellId = Utils.DefaultPowerShellShellID; } } return _shellId; } } private string _shellId; /// /// Session State with which this instance of engine works /// /// internal SessionStateInternal EngineSessionState { get; set; } /// /// The default or top-level session state instance for the /// engine. /// internal SessionStateInternal TopLevelSessionState { get; private set; } /// /// Get the SessionState facade for the internal session state APIs /// /// internal SessionState SessionState { get { return EngineSessionState.PublicSessionState; } } /// /// Get/set constraints for this execution environemnt /// internal PSLanguageMode LanguageMode { get { return _languageMode; } set { // If we're moving to ConstrainedLanguage, invalidate the binding // caches. After that, the binding rules encode the language mode. if (value == PSLanguageMode.ConstrainedLanguage) { ExecutionContext.HasEverUsedConstrainedLanguage = true; HasRunspaceEverUsedConstrainedLanguageMode = true; System.Management.Automation.Language.PSSetMemberBinder.InvalidateCache(); System.Management.Automation.Language.PSInvokeMemberBinder.InvalidateCache(); System.Management.Automation.Language.PSConvertBinder.InvalidateCache(); System.Management.Automation.Language.PSBinaryOperationBinder.InvalidateCache(); System.Management.Automation.Language.PSGetIndexBinder.InvalidateCache(); System.Management.Automation.Language.PSSetIndexBinder.InvalidateCache(); System.Management.Automation.Language.PSCreateInstanceBinder.InvalidateCache(); } // Conversion caches don't have version info / binding rules, so must be // cleared every time. LanguagePrimitives.RebuildConversionCache(); _languageMode = value; } } private PSLanguageMode _languageMode = PSLanguageMode.FullLanguage; /// /// True if this runspace has ever used constrained language mode /// internal bool HasRunspaceEverUsedConstrainedLanguageMode { get; private set; } /// /// True if we've ever used ConstrainedLanguage. If this is the case, then the binding restrictions /// need to also validate against the language mode. /// internal static bool HasEverUsedConstrainedLanguage { get; private set; } /// /// If true the PowerShell debugger will use FullLanguage mode, otherwise it will use the current language mode /// internal bool UseFullLanguageModeInDebugger { get { return InitialSessionState != null ? InitialSessionState.UseFullLanguageModeInDebugger : false; } } internal static List ModulesWithJobSourceAdapters = new List { Utils.WorkflowModule, Utils.ScheduledJobModuleName, }; /// /// Is true the PSScheduledJob and PSWorkflow modules are loaded for this runspace /// internal bool IsModuleWithJobSourceAdapterLoaded { get; set; } /// /// Gets the location globber for the session state for /// this instance of the runspace. /// /// internal LocationGlobber LocationGlobber { get { _locationGlobber = new LocationGlobber(this.SessionState); return _locationGlobber; } } private LocationGlobber _locationGlobber; /// /// The assemblies that have been loaded for this runspace /// /// internal Dictionary AssemblyCache { get; private set; } #endregion Properties #region Engine State /// /// The state for current engine that is running. /// /// /// internal EngineState EngineState { get; set; } = EngineState.None; #endregion #region GetSetVariable methods /// /// Get a variable out of session state. /// internal object GetVariableValue(VariablePath path) { CmdletProviderContext context; SessionStateScope scope; return EngineSessionState.GetVariableValue(path, out context, out scope); } /// /// Get a variable out of session state. This calls GetVariable(name) and returns the /// value unless it is null in which case it returns the defaultValue provided by the caller /// internal object GetVariableValue(VariablePath path, object defaultValue) { CmdletProviderContext context; SessionStateScope scope; return EngineSessionState.GetVariableValue(path, out context, out scope) ?? defaultValue; } /// /// Set a variable in session state. /// internal void SetVariable(VariablePath path, object newValue) { EngineSessionState.SetVariable(path, newValue, true, CommandOrigin.Internal); } // SetVariable internal T GetEnumPreference(VariablePath preferenceVariablePath, T defaultPref, out bool defaultUsed) { CmdletProviderContext context = null; SessionStateScope scope = null; object val = EngineSessionState.GetVariableValue(preferenceVariablePath, out context, out scope); if (val is T) { // We don't want to support "Ignore" as action preferences, as it leads to bad // scripting habits. They are only supported as cmdlet overrides. if (val is ActionPreference) { ActionPreference preference = (ActionPreference)val; if ((preference == ActionPreference.Ignore) || (preference == ActionPreference.Suspend)) { // Reset the variable value EngineSessionState.SetVariableValue(preferenceVariablePath.UserPath, defaultPref); string message = StringUtil.Format(ErrorPackage.UnsupportedPreferenceError, preference); throw new NotSupportedException(message); } } T convertedResult = (T)val; defaultUsed = false; return convertedResult; } defaultUsed = true; T result = defaultPref; if (val != null) { try { string valString = val as string; if (valString != null) { result = (T)Enum.Parse(typeof(T), valString, true); defaultUsed = false; } else { result = (T)PSObject.Base(val); defaultUsed = false; } } catch (InvalidCastException) { // default value is used } catch (ArgumentException) { // default value is used } } return result; } /// /// Same as GetEnumPreference, but for boolean values /// /// /// /// /// internal bool GetBooleanPreference(VariablePath preferenceVariablePath, bool defaultPref, out bool defaultUsed) { CmdletProviderContext context = null; SessionStateScope scope = null; object val = EngineSessionState.GetVariableValue(preferenceVariablePath, out context, out scope); if (val == null) { defaultUsed = true; return defaultPref; } bool converted = defaultPref; defaultUsed = !LanguagePrimitives.TryConvertTo (val, out converted); return (defaultUsed) ? defaultPref : converted; } #endregion GetSetVariable methods #region HelpSystem /// /// Help system for this engine context. /// /// internal HelpSystem HelpSystem { get { return _helpSystem ?? (_helpSystem = new HelpSystem(this)); } } private HelpSystem _helpSystem; #endregion #region FormatAndOutput internal Object FormatInfo { get; set; } #endregion internal Dictionary CustomArgumentCompleters { get; set; } internal Dictionary NativeArgumentCompleters { get; set; } private CommandFactory _commandFactory; /// /// Routine to create a command(processor) instance using the factory. /// /// The name of the command to lookup /// /// The command processor object internal CommandProcessorBase CreateCommand(string command, bool dotSource) { if (_commandFactory == null) { _commandFactory = new CommandFactory(this); } CommandProcessorBase commandProcessor = _commandFactory.CreateCommand(command, this.EngineSessionState.CurrentScope.ScopeOrigin, !dotSource); // Reset the command origin for script commands... //BUGBUG - dotting can get around command origin checks??? if (commandProcessor != null && commandProcessor is ScriptCommandProcessorBase) commandProcessor.Command.CommandOriginInternal = CommandOrigin.Internal; return commandProcessor; } /// /// Hold the current command. /// /// Reference to command discovery internal CommandProcessorBase CurrentCommandProcessor { get; set; } /// /// Redirect to the CommandDiscovery in the engine. /// /// Reference to command discovery internal CommandDiscovery CommandDiscovery { get { return Engine.CommandDiscovery; } } /// /// Interface that should be used for interaction with host /// internal InternalHost EngineHostInterface { get; private set; // set not provided: it's not meaningful to change the host post-construction. } /// /// Interface to be used for interaction with internal /// host. InternalHost wraps the host supplied /// during construction. Use this wrapper to access /// functionality specific to InternalHost. /// internal InternalHost InternalHost { get { return EngineHostInterface; } } /// /// Interface to the public API for the engine /// internal EngineIntrinsics EngineIntrinsics { get { return _engineIntrinsics ?? (_engineIntrinsics = new EngineIntrinsics(this)); } } private EngineIntrinsics _engineIntrinsics; /// /// Log context cache /// internal LogContextCache LogContextCache { get; } = new LogContextCache(); #region Output pipes /// /// The PipelineWriter provided by the connection object for success output /// internal PipelineWriter ExternalSuccessOutput { get; set; } /// /// The PipelineWriter provided by the connection object for error output /// internal PipelineWriter ExternalErrorOutput { get; set; } /// /// The PipelineWriter provided by the connection object for progress output /// internal PipelineWriter ExternalProgressOutput { get; set; } internal class SavedContextData { private bool _stepScript; private bool _ignoreScriptDebug; private int _PSDebug; private Pipe _shellFunctionErrorOutputPipe; public SavedContextData(ExecutionContext context) { _stepScript = context.PSDebugTraceStep; _ignoreScriptDebug = context.IgnoreScriptDebug; _PSDebug = context.PSDebugTraceLevel; _shellFunctionErrorOutputPipe = context.ShellFunctionErrorOutputPipe; } public void RestoreContextData(ExecutionContext context) { context.PSDebugTraceStep = _stepScript; context.IgnoreScriptDebug = _ignoreScriptDebug; context.PSDebugTraceLevel = _PSDebug; context.ShellFunctionErrorOutputPipe = _shellFunctionErrorOutputPipe; } } /// /// Host uses this to saves context data when entering a nested prompt /// /// internal SavedContextData SaveContextData() { return new SavedContextData(this); } internal void ResetShellFunctionErrorOutputPipe() { ShellFunctionErrorOutputPipe = null; } internal Pipe RedirectErrorPipe(Pipe newPipe) { Pipe oldPipe = ShellFunctionErrorOutputPipe; ShellFunctionErrorOutputPipe = newPipe; return oldPipe; } internal void RestoreErrorPipe(Pipe pipe) { ShellFunctionErrorOutputPipe = pipe; } /// /// Reset all of the redirection book keeping variables. This routine should be called when starting to /// execute a script. /// internal void ResetRedirection() { ShellFunctionErrorOutputPipe = null; } /// /// Function and Script command processors will route their error output to /// this pipe if set, unless explicitly routed elsewhere. We also keep track /// of the first time this value is set so we can know if it's the default /// error output or not. /// internal Pipe ShellFunctionErrorOutputPipe { get; set; } /// /// Supports expression Warning output redirection. /// internal Pipe ExpressionWarningOutputPipe { get; set; } /// /// Supports expression Verbose output redirection. /// internal Pipe ExpressionVerboseOutputPipe { get; set; } /// /// Supports expression Verbose output redirection. /// internal Pipe ExpressionDebugOutputPipe { get; set; } /// /// Supports expression Information output redirection. /// internal Pipe ExpressionInformationOutputPipe { get; set; } #endregion Output pipes #region Append to $error /// /// Appends the object to $global:error if it's an error record or exception. /// /// /// ErrorRecord or Exception to be written to $global:error /// /// /// (get-only) An error occurred accessing $ERROR. /// internal void AppendDollarError(object obj) { ErrorRecord objAsErrorRecord = obj as ErrorRecord; if (objAsErrorRecord == null && !(obj is Exception)) { Diagnostics.Assert(false, "Object to append was neither an ErrorRecord nor an Exception in ExecutionContext.AppendDollarError"); return; } object old = this.DollarErrorVariable; ArrayList arraylist = old as ArrayList; if (null == arraylist) { Diagnostics.Assert(false, "$error should be a global constant ArrayList"); return; } // Don't add the same exception twice... if (arraylist.Count > 0) { // There may be exceptions stored directly in which case // the direc comparison will catch them... if (arraylist[0] == obj) return; // otherwise check the exception members of the error records... ErrorRecord er1 = arraylist[0] as ErrorRecord; if (er1 != null && objAsErrorRecord != null && er1.Exception == objAsErrorRecord.Exception) return; } // 1045384-2004/12/14-JonN impementing $MaximumErrorCount object maxcountobj = EngineSessionState.CurrentScope.ErrorCapacity.FastValue; if (null != maxcountobj) { try { maxcountobj = LanguagePrimitives.ConvertTo(maxcountobj, typeof(int), CultureInfo.InvariantCulture); } catch (PSInvalidCastException) { } catch (System.OverflowException) { } catch (Exception e) { Diagnostics.Assert(false, "Unexpected exception in LanguagePrimitives.ConvertTo: " + e.GetType().FullName); throw; } } int maxErrorCount = (maxcountobj is int) ? (int)maxcountobj : 256; if (0 > maxErrorCount) maxErrorCount = 0; else if (32768 < maxErrorCount) maxErrorCount = 32768; if (0 >= maxErrorCount) { arraylist.Clear(); return; } int numToErase = arraylist.Count - (maxErrorCount - 1); if (0 < numToErase) { arraylist.RemoveRange( maxErrorCount - 1, numToErase); } arraylist.Insert(0, obj); } // AppendDollarError #endregion #region Scope or Commands (in pipeline) Depth Count /// /// Check if the stack would overflow soon, if so, throw ScriptCallDepthException. /// /// /// If the stack would overflow soon. /// internal static void CheckStackDepth() { try { RuntimeHelpers.EnsureSufficientExecutionStack(); } catch (InsufficientExecutionStackException) { throw new ScriptCallDepthException(); } } #endregion /// /// The current connection object /// private Runspace _currentRunspace; //This should be internal, but it need to be friend of remoting dll. /// /// The current connection object /// internal Runspace CurrentRunspace { get { return _currentRunspace; } set { _currentRunspace = value; } } /// /// Each pipeline has a stack of pipeline processor. This method /// pushes pp in to stack for currently executing pipeline. /// /// internal void PushPipelineProcessor(PipelineProcessor pp) { if (_currentRunspace == null) return; LocalPipeline lpl = (LocalPipeline)((RunspaceBase)_currentRunspace).GetCurrentlyRunningPipeline(); if (lpl == null) return; lpl.Stopper.Push(pp); } /// /// Each pipeline has a stack of pipeline processor. This method pops the /// top item from the stack /// internal void PopPipelineProcessor(bool fromSteppablePipeline) { if (_currentRunspace == null) return; LocalPipeline lpl = (LocalPipeline)((RunspaceBase)_currentRunspace).GetCurrentlyRunningPipeline(); if (lpl == null) return; lpl.Stopper.Pop(fromSteppablePipeline); } /// /// This flag is checked by parser to stop loops etc. /// /// internal bool CurrentPipelineStopping { get { if (_currentRunspace == null) return false; LocalPipeline lpl = (LocalPipeline)((RunspaceBase)_currentRunspace).GetCurrentlyRunningPipeline(); if (lpl == null) return false; return lpl.IsStopping; } } /// /// True means one of these: /// 1) there is a trap statement in a dynamically enclosing statement block that might catch an exception. /// 2) execution happens inside a PS class and exceptions should be propagated all the way up, even if there is no enclosing try-catch-finally. /// /// internal bool PropagateExceptionsToEnclosingStatementBlock { get; set; } internal RuntimeException CurrentExceptionBeingHandled { get; set; } /// /// Shortcut to get at $? /// /// The current value of $? internal bool QuestionMarkVariableValue { get; set; } = true; /// /// Shortcut to get at $error /// /// The current value of $global:error internal object DollarErrorVariable { get { CmdletProviderContext context = null; SessionStateScope scope = null; object resultItem = null; if (!Events.IsExecutingEventAction) { resultItem = EngineSessionState.GetVariableValue( SpecialVariables.ErrorVarPath, out context, out scope); } else { resultItem = EngineSessionState.GetVariableValue( SpecialVariables.EventErrorVarPath, out context, out scope); } return resultItem; } set { EngineSessionState.SetVariable( SpecialVariables.ErrorVarPath, value, true, CommandOrigin.Internal); } } internal ActionPreference DebugPreferenceVariable { get { bool defaultUsed = false; return this.GetEnumPreference( SpecialVariables.DebugPreferenceVarPath, InitialSessionState.defaultDebugPreference, out defaultUsed); } set { this.EngineSessionState.SetVariable( SpecialVariables.DebugPreferenceVarPath, LanguagePrimitives.ConvertTo(value, typeof(ActionPreference), CultureInfo.InvariantCulture), true, CommandOrigin.Internal); } } internal ActionPreference VerbosePreferenceVariable { get { bool defaultUsed = false; return this.GetEnumPreference( SpecialVariables.VerbosePreferenceVarPath, InitialSessionState.defaultVerbosePreference, out defaultUsed); } set { this.EngineSessionState.SetVariable( SpecialVariables.VerbosePreferenceVarPath, LanguagePrimitives.ConvertTo(value, typeof(ActionPreference), CultureInfo.InvariantCulture), true, CommandOrigin.Internal); } } internal ActionPreference ErrorActionPreferenceVariable { get { bool defaultUsed = false; return this.GetEnumPreference( SpecialVariables.ErrorActionPreferenceVarPath, InitialSessionState.defaultErrorActionPreference, out defaultUsed); } set { this.EngineSessionState.SetVariable( SpecialVariables.ErrorActionPreferenceVarPath, LanguagePrimitives.ConvertTo(value, typeof(ActionPreference), CultureInfo.InvariantCulture), true, CommandOrigin.Internal); } } internal ActionPreference WarningActionPreferenceVariable { get { bool defaultUsed = false; return this.GetEnumPreference( SpecialVariables.WarningPreferenceVarPath, InitialSessionState.defaultWarningPreference, out defaultUsed); } set { this.EngineSessionState.SetVariable( SpecialVariables.WarningPreferenceVarPath, LanguagePrimitives.ConvertTo(value, typeof(ActionPreference), CultureInfo.InvariantCulture), true, CommandOrigin.Internal); } } internal ActionPreference InformationActionPreferenceVariable { get { bool defaultUsed = false; return this.GetEnumPreference( SpecialVariables.InformationPreferenceVarPath, InitialSessionState.defaultInformationPreference, out defaultUsed); } set { this.EngineSessionState.SetVariable( SpecialVariables.InformationPreferenceVarPath, LanguagePrimitives.ConvertTo(value, typeof(ActionPreference), CultureInfo.InvariantCulture), true, CommandOrigin.Internal); } } internal object WhatIfPreferenceVariable { get { CmdletProviderContext context = null; SessionStateScope scope = null; object resultItem = this.EngineSessionState.GetVariableValue( SpecialVariables.WhatIfPreferenceVarPath, out context, out scope); return resultItem; } set { this.EngineSessionState.SetVariable( SpecialVariables.WhatIfPreferenceVarPath, value, true, CommandOrigin.Internal); } } internal ConfirmImpact ConfirmPreferenceVariable { get { bool defaultUsed = false; return this.GetEnumPreference( SpecialVariables.ConfirmPreferenceVarPath, InitialSessionState.defaultConfirmPreference, out defaultUsed); } set { this.EngineSessionState.SetVariable( SpecialVariables.ConfirmPreferenceVarPath, LanguagePrimitives.ConvertTo(value, typeof(ConfirmImpact), CultureInfo.InvariantCulture), true, CommandOrigin.Internal); } } internal void RunspaceClosingNotification() { if (this.RunspaceConfiguration != null) { this.RunspaceConfiguration.Unbind(this); } EngineSessionState.RunspaceClosingNotification(); if (_debugger != null) { _debugger.Dispose(); } if (Events != null) { Events.Dispose(); } Events = null; if (this.transactionManager != null) { this.transactionManager.Dispose(); } this.transactionManager = null; } /// /// Gets the type table instance for this engine. This is somewhat /// complicated by the need to have a single type table in RunspaceConfig /// shared across all bound runspaces, as well as individual tables for /// instances created from InitialSessionState. /// internal TypeTable TypeTable { get { if (_typeTable == null) { // Always use the type table from the RunspaceConfig if there is one, otherwise create a default one _typeTable = (this.RunspaceConfiguration != null && RunspaceConfiguration.TypeTable != null) ? RunspaceConfiguration.TypeTable : new TypeTable(); _typeTableWeakReference = new WeakReference(_typeTable); } return _typeTable; } // This needs to exist so that RunspaceConfiguration can // push it's shared type table into ExecutionContext set { if (this.RunspaceConfiguration != null) throw new NotImplementedException("set_TypeTable()"); _typeTable = value; _typeTableWeakReference = value != null ? new WeakReference(value) : null; } } /// /// Here for PSObject, should probably not be used elsewhere, maybe not even in PSObject. /// internal WeakReference TypeTableWeakReference { get { if (_typeTable == null) { var unused = TypeTable; } return _typeTableWeakReference; } } private TypeTable _typeTable; private WeakReference _typeTableWeakReference; /// /// Gets the format info database for this engine. This is significantly /// complicated by the need to have a single type table in RunspaceConfig /// shared across all bound runspaces, as well as individual tables for /// instances created from InitialSessionState. /// internal TypeInfoDataBaseManager FormatDBManager { get { // Use the format DB from the RunspaceConfig if there is one. if (this.RunspaceConfiguration != null && RunspaceConfiguration.FormatDBManager != null) { return RunspaceConfiguration.FormatDBManager; } if (_formatDBManager == null) { // If no Formatter database has been created, then // create and initialize an empty one. _formatDBManager = new TypeInfoDataBaseManager(); _formatDBManager.Update(this.AuthorizationManager, this.EngineHostInterface); if (this.InitialSessionState != null) { // Win8:418011: Set DisableFormatTableUpdates only after performing the initial update. Otherwise, formatDBManager will be // in bad state. _formatDBManager.DisableFormatTableUpdates = this.InitialSessionState.DisableFormatUpdates; } } return _formatDBManager; } // This needs to exist so that RunspaceConfiguration can // push it's shared format database table into ExecutionContext set { if (this.RunspaceConfiguration != null) throw new NotImplementedException("set_FormatDBManager()"); _formatDBManager = value; } } private TypeInfoDataBaseManager _formatDBManager; /// /// Gets the TransactionManager instance that controlls transactions in the current /// instance. /// internal PSTransactionManager TransactionManager { get { return transactionManager; } } internal PSTransactionManager transactionManager; private bool _assemblyCacheInitialized = false; /// /// This function is called by RunspaceConfiguration.Assemblies.Update call back. /// It's not used when constructing a runspace from an InitialSessionState object. /// internal void UpdateAssemblyCache() { string errors = ""; if (this.RunspaceConfiguration != null) { if (!_assemblyCacheInitialized) { foreach (AssemblyConfigurationEntry entry in this.RunspaceConfiguration.Assemblies) { Exception error = null; AddAssembly(entry.Name, entry.FileName, out error); if (error != null) { errors += "\n" + error.Message; } } _assemblyCacheInitialized = true; } else { foreach (AssemblyConfigurationEntry entry in this.RunspaceConfiguration.Assemblies.UpdateList) { switch (entry.Action) { case UpdateAction.Add: Exception error = null; AddAssembly(entry.Name, entry.FileName, out error); if (error != null) { errors += "\n" + error.Message; } break; case UpdateAction.Remove: RemoveAssembly(entry.Name); break; default: break; } } } if (!String.IsNullOrEmpty(errors)) { string message = StringUtil.Format(MiniShellErrors.UpdateAssemblyErrors, errors); throw new RuntimeException(message); } } } internal Assembly AddAssembly(string name, string filename, out Exception error) { Assembly loadedAssembly = LoadAssembly(name, filename, out error); if (loadedAssembly == null) return null; if (AssemblyCache.ContainsKey(loadedAssembly.FullName)) { // we should ignore this assembly. return loadedAssembly; } // We will cache the assembly by both full name and // file name AssemblyCache.Add(loadedAssembly.FullName, loadedAssembly); if (AssemblyCache.ContainsKey(loadedAssembly.GetName().Name)) { // we should ignore this assembly. return loadedAssembly; } AssemblyCache.Add(loadedAssembly.GetName().Name, loadedAssembly); return loadedAssembly; } internal void RemoveAssembly(string name) { Assembly loadedAssembly; if (AssemblyCache.TryGetValue(name, out loadedAssembly) && loadedAssembly != null) { AssemblyCache.Remove(name); AssemblyCache.Remove(loadedAssembly.GetName().Name); } } [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadWithPartialName")] [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")] internal static Assembly LoadAssembly(string name, string filename, out Exception error) { // First we try to load the assembly based on the given name Assembly loadedAssembly = null; error = null; string fixedName = null; if (!String.IsNullOrEmpty(name)) { // Remove the '.dll' if it's there... fixedName = name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) ? Path.GetFileNameWithoutExtension(name) : name; var assemblyString = Utils.IsPowerShellAssembly(fixedName) ? Utils.GetPowerShellAssemblyStrongName(fixedName) : fixedName; try { loadedAssembly = Assembly.Load(new AssemblyName(assemblyString)); } catch (FileNotFoundException fileNotFound) { error = fileNotFound; } catch (FileLoadException fileLoadException) { error = fileLoadException; // this is a legitamate error on CoreCLR for a newly emited with Add-Type assemblies // they cannot be loaded by name, but we are only interested in importing them by path } catch (BadImageFormatException badImage) { error = badImage; return null; } catch (SecurityException securityException) { error = securityException; return null; } } if (loadedAssembly != null) return loadedAssembly; if (!String.IsNullOrEmpty(filename)) { error = null; try { loadedAssembly = ClrFacade.LoadFrom(filename); return loadedAssembly; } catch (FileNotFoundException fileNotFound) { error = fileNotFound; } catch (FileLoadException fileLoadException) { error = fileLoadException; return null; } catch (BadImageFormatException badImage) { error = badImage; return null; } catch (SecurityException securityException) { error = securityException; return null; } } #if !CORECLR// Assembly.LoadWithPartialName is not in CoreCLR. In CoreCLR, 'LoadWithPartialName' can be replaced by Assembly.Load with the help of AssemblyLoadContext. // Finally try with partial name... if (!String.IsNullOrEmpty(fixedName)) { try { // This is a deprecated API, use of this API needs to be // reviewed periodically. #pragma warning disable 0618 loadedAssembly = Assembly.LoadWithPartialName(fixedName); if (loadedAssembly != null) { // In the past, LoadWithPartialName would just return null in most cases when the assembly could not be found or loaded. // In addition to this, the error was always cleared. So now, clear the error variable only if the assembly was loaded. error = null; } return loadedAssembly; } // Expected exceptions are ArgumentNullException and BadImageFormatException. See https://msdn.microsoft.com/en-us/library/12xc5368(v=vs.110).aspx catch (BadImageFormatException badImage) { error = badImage; } } #endif return null; } /// /// Report an initialization-time error. /// /// resource string /// arguments internal void ReportEngineStartupError(string resourceString, params object[] arguments) { try { Cmdlet currentRunningModuleCommand; string errorId; if (IsModuleCommandCurrentlyRunning(out currentRunningModuleCommand, out errorId)) { RuntimeException rte = InterpreterError.NewInterpreterException(null, typeof(RuntimeException), null, errorId, resourceString, arguments); currentRunningModuleCommand.WriteError(new ErrorRecord(rte.ErrorRecord, rte)); } else { PSHost host = EngineHostInterface; if (null == host) return; PSHostUserInterface ui = host.UI; if (null == ui) return; ui.WriteErrorLine( StringUtil.Format(resourceString, arguments)); } } catch (Exception ex) // swallow all exceptions { CommandProcessorBase.CheckForSevereException(ex); } } /// /// Report an initialization-time error /// /// error to report internal void ReportEngineStartupError(string error) { try { Cmdlet currentRunningModuleCommand; string errorId; if (IsModuleCommandCurrentlyRunning(out currentRunningModuleCommand, out errorId)) { RuntimeException rte = InterpreterError.NewInterpreterException(null, typeof(RuntimeException), null, errorId, "{0}", error); currentRunningModuleCommand.WriteError(new ErrorRecord(rte.ErrorRecord, rte)); } else { PSHost host = EngineHostInterface; if (null == host) return; PSHostUserInterface ui = host.UI; if (null == ui) return; ui.WriteErrorLine(error); } } catch (Exception ex) // swallow all exceptions { CommandProcessorBase.CheckForSevereException(ex); } } /// /// Report an initialization-time error /// /// internal void ReportEngineStartupError(Exception e) { try { Cmdlet currentRunningModuleCommand; string errorId; if (IsModuleCommandCurrentlyRunning(out currentRunningModuleCommand, out errorId)) { ErrorRecord error = null; var rte = e as RuntimeException; error = rte != null ? new ErrorRecord(rte.ErrorRecord, rte) : new ErrorRecord(e, errorId, ErrorCategory.OperationStopped, null); currentRunningModuleCommand.WriteError(error); } else { PSHost host = EngineHostInterface; if (null == host) return; PSHostUserInterface ui = host.UI; if (null == ui) return; ui.WriteErrorLine(e.Message); } } catch (Exception ex) // swallow all exceptions { CommandProcessorBase.CheckForSevereException(ex); } } /// /// Report an initialization-time error /// /// internal void ReportEngineStartupError(ErrorRecord errorRecord) { try { Cmdlet currentRunningModuleCommand; string unused; if (IsModuleCommandCurrentlyRunning(out currentRunningModuleCommand, out unused)) { currentRunningModuleCommand.WriteError(errorRecord); } else { PSHost host = EngineHostInterface; if (null == host) return; PSHostUserInterface ui = host.UI; if (null == ui) return; ui.WriteErrorLine(errorRecord.ToString()); } } catch (Exception ex) // swallow all exceptions { CommandProcessorBase.CheckForSevereException(ex); } } private bool IsModuleCommandCurrentlyRunning(out Cmdlet command, out string errorId) { command = null; errorId = null; bool result = false; if (this.CurrentCommandProcessor != null) { CommandInfo cmdletInfo = this.CurrentCommandProcessor.CommandInfo; if ((String.Equals(cmdletInfo.Name, "Import-Module", StringComparison.OrdinalIgnoreCase) || String.Equals(cmdletInfo.Name, "Remove-Module", StringComparison.OrdinalIgnoreCase)) && cmdletInfo.CommandType.Equals(CommandTypes.Cmdlet) && InitialSessionState.CoreModule.Equals(cmdletInfo.ModuleName, StringComparison.OrdinalIgnoreCase)) { result = true; command = (Cmdlet)this.CurrentCommandProcessor.Command; errorId = String.Equals(cmdletInfo.Name, "Import-Module", StringComparison.OrdinalIgnoreCase) ? "Module_ImportModuleError" : "Module_RemoveModuleError"; } } return result; } /// /// Constructs an Execution context object for Automation Engine /// /// /// /// Engine that hosts this execution context /// /// /// Interface that should be used for interaction with host /// /// /// RunspaceConfiguration information /// internal ExecutionContext(AutomationEngine engine, PSHost hostInterface, RunspaceConfiguration runspaceConfiguration) { RunspaceConfiguration = runspaceConfiguration; AuthorizationManager = runspaceConfiguration.AuthorizationManager; InitializeCommon(engine, hostInterface); } /// /// Constructs an Execution context object for Automation Engine /// /// /// /// Engine that hosts this execution context /// /// /// Interface that should be used for interaction with host /// /// /// InitialSessionState information /// internal ExecutionContext(AutomationEngine engine, PSHost hostInterface, InitialSessionState initialSessionState) { InitialSessionState = initialSessionState; AuthorizationManager = initialSessionState.AuthorizationManager; InitializeCommon(engine, hostInterface); } private void InitializeCommon(AutomationEngine engine, PSHost hostInterface) { Engine = engine; #if !CORECLR// System.AppDomain is not in CoreCLR // Set the assembly resolve handler if it isn't already set... if (!_assemblyEventHandlerSet) { // we only want to set the event handler once for the entire app domain... lock (lockObject) { // Need to check again inside the lock due to possibliity of a race condition... if (!_assemblyEventHandlerSet) { AppDomain currentAppDomain = AppDomain.CurrentDomain; currentAppDomain.AssemblyResolve += new ResolveEventHandler(PowerShellAssemblyResolveHandler); _assemblyEventHandlerSet = true; } } } #endif Events = new PSLocalEventManager(this); transactionManager = new PSTransactionManager(); _debugger = new ScriptDebugger(this); EngineHostInterface = hostInterface as InternalHost ?? new InternalHost(hostInterface, this); // Hook up the assembly cache AssemblyCache = new Dictionary(); // Initialize the fixed toplevel session state and the current session state TopLevelSessionState = EngineSessionState = new SessionStateInternal(this); if (AuthorizationManager == null) { // if authorizationmanager==null, this means the configuration // explicitly asked for dummy authorization manager. AuthorizationManager = new AuthorizationManager(null); } // Set up the module intrinsics Modules = new ModuleIntrinsics(this); } #if !CORECLR // System.AppDomain is not in CoreCLR private static bool _assemblyEventHandlerSet = false; private static object lockObject = new Object(); /// /// AssemblyResolve event handler that will look in the assembly cache to see /// if the named assembly has been loaded. This is necessary so that assemblies loaded /// with LoadFrom, which are in a different loaded context than Load, can still be used to /// resolve types. /// /// The event sender /// The event args /// The resolve assembly or null if not found private static Assembly PowerShellAssemblyResolveHandler(object sender, ResolveEventArgs args) { ExecutionContext ecFromTLS = Runspaces.LocalPipeline.GetExecutionContextFromTLS(); if (ecFromTLS != null) { if (ecFromTLS.AssemblyCache != null) { Assembly assembly; ecFromTLS.AssemblyCache.TryGetValue(args.Name, out assembly); return assembly; } } return null; } #endif } /// /// Enum that defines state of monad engine. /// /// internal enum EngineState { /// /// Engine state is not defined or initialized. /// None = 0, /// /// Engine available /// Available = 1, /// /// Engine service is degraded /// Degraded = 2, /// /// Engine is out of service /// OutOfService = 3, /// /// Engine is stopped /// Stopped = 4 }; }