mirror of
https://github.com/aphrody-labs/PowerShell.git
synced 2026-10-18 05:05:36 +00:00
Remove the code that spins up our own assembly load context. Keep the code that registers our `Resolve` method to the default loader's `Resolving` event, so that we can continue to do special assembly resolution as needed (such as the GAC probing logic needed for consuming FullCLR PS modules). Essentially, the assembly `Microsoft.PowerShell.CoreCLR.AssemblyLoadContext.dll` is not needed anymore, the remaining code should be moved to S.M.A.dll. However, that will break DSC and other native hosts that are hosting powershell. So this assembly is kept for now.
1726 lines
62 KiB
C#
1726 lines
62 KiB
C#
/********************************************************************++
|
|
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
|
|
{
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
internal class ExecutionContext
|
|
{
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// The events received by this runspace
|
|
/// </summary>
|
|
internal PSLocalEventManager Events { get; private set; }
|
|
|
|
internal HashSet<String> AutoLoadingModuleInProgress { get; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
/// <summary>
|
|
/// The debugger for the interpreter
|
|
/// </summary>
|
|
internal ScriptDebugger Debugger
|
|
{
|
|
get { return _debugger; }
|
|
}
|
|
internal ScriptDebugger _debugger;
|
|
|
|
internal int _debuggingMode;
|
|
|
|
/// <summary>
|
|
/// Reset or clear the various context managers so the runspace can be reused without contamination.
|
|
/// </summary>
|
|
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();
|
|
}
|
|
/// <summary>
|
|
/// The tracing mode for the interpreter.
|
|
/// </summary>
|
|
/// <value>True if tracing is turned on, false if it's turned off.</value>
|
|
internal int PSDebugTraceLevel
|
|
{
|
|
get
|
|
{
|
|
// Pretend that tracing is off if ignoreScriptDebug is true
|
|
return IgnoreScriptDebug ? 0 : _debugTraceLevel;
|
|
}
|
|
set { _debugTraceLevel = value; }
|
|
}
|
|
private int _debugTraceLevel;
|
|
|
|
/// <summary>
|
|
/// The step mode for the interpreter.
|
|
/// </summary>
|
|
/// <value>True of stepping is turned on, false if it's turned off.</value>
|
|
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;
|
|
}
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <param name="majorVersion">The version for a strict check about to be performed.</param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Is true if the current statement in the interpreter should be traced...
|
|
/// </summary>
|
|
internal bool ShouldTraceStatement
|
|
{
|
|
get
|
|
{
|
|
// Pretend that tracing is off if ignoreScriptDebug is true
|
|
return !IgnoreScriptDebug && (_debugTraceLevel > 0 || _debugTraceStep);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
internal bool ScriptCommandProcessorShouldRethrowExit { get; set; } = false;
|
|
|
|
/// <summary>
|
|
/// If this flag is set to true, script trace output
|
|
/// will not be generated regardless of the state of the
|
|
/// trace flag.
|
|
/// </summary>
|
|
/// <value>The current state of the IgnoreScriptDebug flag.</value>
|
|
internal bool IgnoreScriptDebug { set; get; } = true;
|
|
|
|
/// <summary>
|
|
/// Gets the automation engine instance.
|
|
/// </summary>
|
|
internal AutomationEngine Engine { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Get the RunspaceConfiguration instance
|
|
/// </summary>
|
|
internal RunspaceConfiguration RunspaceConfiguration { get; }
|
|
|
|
internal InitialSessionState InitialSessionState { get; }
|
|
|
|
/// <summary>
|
|
/// True if the RunspaceConfiguration/InitialSessionState is for a single shell or false otherwise.
|
|
/// </summary>
|
|
///
|
|
internal bool IsSingleShell
|
|
{
|
|
get
|
|
{
|
|
RunspaceConfigForSingleShell runSpace = RunspaceConfiguration as RunspaceConfigForSingleShell;
|
|
return runSpace != null || InitialSessionState != null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
internal string PreviousModuleProcessed { get; set; }
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
internal Hashtable previousModuleImported { get; set; } = new Hashtable();
|
|
|
|
/// <summary>
|
|
/// Contains the name of the module currently being processed. This
|
|
/// allows you to skip this module when doing a lookup.
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The AppDomain currently being used for module analysis. It should only be created if needed,
|
|
/// but various callers need to take responsibility for unloading the domain via
|
|
/// the TakeResponsibilityForModuleAnalysisAppDomain.
|
|
/// </summary>
|
|
internal AppDomain AppDomainForModuleAnalysis { get; set; }
|
|
|
|
/// <summary>
|
|
/// Authorization manager for this runspace
|
|
/// </summary>
|
|
internal AuthorizationManager AuthorizationManager { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Gets the appropriate provider names for the default
|
|
/// providers based on the type of the shell
|
|
/// (single shell or custom shell).
|
|
/// </summary>
|
|
///
|
|
internal ProviderNames ProviderNames
|
|
{
|
|
get
|
|
{
|
|
if (_providerNames == null)
|
|
{
|
|
if (IsSingleShell)
|
|
{
|
|
_providerNames = new SingleShellProviderNames();
|
|
}
|
|
else
|
|
{
|
|
_providerNames = new CustomShellProviderNames();
|
|
}
|
|
}
|
|
return _providerNames;
|
|
}
|
|
}
|
|
private ProviderNames _providerNames;
|
|
|
|
/// <summary>
|
|
/// The module information for this engine...
|
|
/// </summary>
|
|
internal ModuleIntrinsics Modules { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Get the shellID for this runspace...
|
|
/// </summary>
|
|
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;
|
|
|
|
/// <summary>
|
|
/// Session State with which this instance of engine works
|
|
/// </summary>
|
|
///
|
|
internal SessionStateInternal EngineSessionState { get; set; }
|
|
|
|
/// <summary>
|
|
/// The default or top-level session state instance for the
|
|
/// engine.
|
|
/// </summary>
|
|
internal SessionStateInternal TopLevelSessionState { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Get the SessionState facade for the internal session state APIs
|
|
/// </summary>
|
|
///
|
|
internal SessionState SessionState
|
|
{
|
|
get
|
|
{
|
|
return EngineSessionState.PublicSessionState;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get/set constraints for this execution environment
|
|
/// </summary>
|
|
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;
|
|
|
|
/// <summary>
|
|
/// True if this runspace has ever used constrained language mode
|
|
/// </summary>
|
|
internal bool HasRunspaceEverUsedConstrainedLanguageMode { get; private set; }
|
|
|
|
/// <summary>
|
|
/// True if we've ever used ConstrainedLanguage. If this is the case, then the binding restrictions
|
|
/// need to also validate against the language mode.
|
|
/// </summary>
|
|
internal static bool HasEverUsedConstrainedLanguage { get; private set; }
|
|
|
|
/// <summary>
|
|
/// If true the PowerShell debugger will use FullLanguage mode, otherwise it will use the current language mode
|
|
/// </summary>
|
|
internal bool UseFullLanguageModeInDebugger
|
|
{
|
|
get
|
|
{
|
|
return InitialSessionState != null ? InitialSessionState.UseFullLanguageModeInDebugger : false;
|
|
}
|
|
}
|
|
|
|
internal static List<string> ModulesWithJobSourceAdapters = new List<string>
|
|
{
|
|
Utils.WorkflowModule,
|
|
Utils.ScheduledJobModuleName,
|
|
};
|
|
|
|
/// <summary>
|
|
/// Is true the PSScheduledJob and PSWorkflow modules are loaded for this runspace
|
|
/// </summary>
|
|
internal bool IsModuleWithJobSourceAdapterLoaded
|
|
{
|
|
get; set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the location globber for the session state for
|
|
/// this instance of the runspace.
|
|
/// </summary>
|
|
///
|
|
internal LocationGlobber LocationGlobber
|
|
{
|
|
get
|
|
{
|
|
_locationGlobber = new LocationGlobber(this.SessionState);
|
|
return _locationGlobber;
|
|
}
|
|
}
|
|
private LocationGlobber _locationGlobber;
|
|
|
|
/// <summary>
|
|
/// The assemblies that have been loaded for this runspace
|
|
/// </summary>
|
|
///
|
|
internal Dictionary<string, Assembly> AssemblyCache { get; private set; }
|
|
|
|
#endregion Properties
|
|
|
|
#region Engine State
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// The state for current engine that is running.
|
|
/// </summary>
|
|
/// <value></value>
|
|
///
|
|
internal EngineState EngineState { get; set; } = EngineState.None;
|
|
|
|
#endregion
|
|
|
|
#region GetSetVariable methods
|
|
|
|
/// <summary>
|
|
/// Get a variable out of session state.
|
|
/// </summary>
|
|
internal object GetVariableValue(VariablePath path)
|
|
{
|
|
CmdletProviderContext context;
|
|
SessionStateScope scope;
|
|
return EngineSessionState.GetVariableValue(path, out context, out scope);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
internal object GetVariableValue(VariablePath path, object defaultValue)
|
|
{
|
|
CmdletProviderContext context;
|
|
SessionStateScope scope;
|
|
return EngineSessionState.GetVariableValue(path, out context, out scope) ?? defaultValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set a variable in session state.
|
|
/// </summary>
|
|
internal void SetVariable(VariablePath path, object newValue)
|
|
{
|
|
EngineSessionState.SetVariable(path, newValue, true, CommandOrigin.Internal);
|
|
} // SetVariable
|
|
|
|
internal T GetEnumPreference<T>(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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Same as GetEnumPreference, but for boolean values
|
|
/// </summary>
|
|
/// <param name="preferenceVariablePath"></param>
|
|
/// <param name="defaultPref"></param>
|
|
/// <param name="defaultUsed"></param>
|
|
/// <returns></returns>
|
|
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<bool>
|
|
(val, out converted);
|
|
return (defaultUsed) ? defaultPref : converted;
|
|
}
|
|
#endregion GetSetVariable methods
|
|
|
|
#region HelpSystem
|
|
|
|
/// <summary>
|
|
/// Help system for this engine context.
|
|
/// </summary>
|
|
/// <value></value>
|
|
internal HelpSystem HelpSystem
|
|
{
|
|
get { return _helpSystem ?? (_helpSystem = new HelpSystem(this)); }
|
|
}
|
|
private HelpSystem _helpSystem;
|
|
|
|
#endregion
|
|
|
|
#region FormatAndOutput
|
|
internal Object FormatInfo { get; set; }
|
|
|
|
#endregion
|
|
|
|
internal Dictionary<string, ScriptBlock> CustomArgumentCompleters { get; set; }
|
|
internal Dictionary<string, ScriptBlock> NativeArgumentCompleters { get; set; }
|
|
|
|
private CommandFactory _commandFactory;
|
|
|
|
/// <summary>
|
|
/// Routine to create a command(processor) instance using the factory.
|
|
/// </summary>
|
|
/// <param name="command">The name of the command to lookup</param>
|
|
/// <param name="dotSource"></param>
|
|
/// <returns>The command processor object</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Hold the current command.
|
|
/// </summary>
|
|
/// <value>Reference to command discovery</value>
|
|
internal CommandProcessorBase CurrentCommandProcessor { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
/// Redirect to the CommandDiscovery in the engine.
|
|
/// </summary>
|
|
/// <value>Reference to command discovery</value>
|
|
internal CommandDiscovery CommandDiscovery
|
|
{
|
|
get
|
|
{
|
|
return Engine.CommandDiscovery;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Interface that should be used for interaction with host
|
|
/// </summary>
|
|
internal InternalHost EngineHostInterface { get; private set;
|
|
|
|
// set not provided: it's not meaningful to change the host post-construction.
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
internal InternalHost InternalHost
|
|
{
|
|
get { return EngineHostInterface; }
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Interface to the public API for the engine
|
|
/// </summary>
|
|
internal EngineIntrinsics EngineIntrinsics
|
|
{
|
|
get { return _engineIntrinsics ?? (_engineIntrinsics = new EngineIntrinsics(this)); }
|
|
}
|
|
private EngineIntrinsics _engineIntrinsics;
|
|
|
|
/// <summary>
|
|
/// Log context cache
|
|
/// </summary>
|
|
internal LogContextCache LogContextCache { get; } = new LogContextCache();
|
|
|
|
#region Output pipes
|
|
/// <summary>
|
|
/// The PipelineWriter provided by the connection object for success output
|
|
/// </summary>
|
|
internal PipelineWriter ExternalSuccessOutput { get; set; }
|
|
|
|
/// <summary>
|
|
/// The PipelineWriter provided by the connection object for error output
|
|
/// </summary>
|
|
internal PipelineWriter ExternalErrorOutput { get; set; }
|
|
|
|
/// <summary>
|
|
/// The PipelineWriter provided by the connection object for progress output
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Host uses this to saves context data when entering a nested prompt
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reset all of the redirection book keeping variables. This routine should be called when starting to
|
|
/// execute a script.
|
|
/// </summary>
|
|
internal void ResetRedirection()
|
|
{
|
|
ShellFunctionErrorOutputPipe = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
internal Pipe ShellFunctionErrorOutputPipe { get; set; }
|
|
|
|
/// <summary>
|
|
/// Supports expression Warning output redirection.
|
|
/// </summary>
|
|
internal Pipe ExpressionWarningOutputPipe { get; set; }
|
|
|
|
/// <summary>
|
|
/// Supports expression Verbose output redirection.
|
|
/// </summary>
|
|
internal Pipe ExpressionVerboseOutputPipe { get; set; }
|
|
|
|
/// <summary>
|
|
/// Supports expression Verbose output redirection.
|
|
/// </summary>
|
|
internal Pipe ExpressionDebugOutputPipe { get; set; }
|
|
|
|
/// <summary>
|
|
/// Supports expression Information output redirection.
|
|
/// </summary>
|
|
internal Pipe ExpressionInformationOutputPipe { get; set; }
|
|
|
|
#endregion Output pipes
|
|
|
|
#region Append to $error
|
|
/// <summary>
|
|
/// Appends the object to $global:error if it's an error record or exception.
|
|
/// </summary>
|
|
/// <param name="obj">
|
|
/// ErrorRecord or Exception to be written to $global:error
|
|
/// </param>
|
|
/// <exception cref="ExtendedTypeSystemException">
|
|
/// (get-only) An error occurred accessing $ERROR.
|
|
/// </exception>
|
|
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 direct 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;
|
|
}
|
|
|
|
const int maxErrorCount = 256;
|
|
|
|
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
|
|
|
|
/// <summary>
|
|
/// Check if the stack would overflow soon, if so, throw ScriptCallDepthException.
|
|
/// </summary>
|
|
/// <exception cref="ScriptCallDepthException">
|
|
/// If the stack would overflow soon.
|
|
/// </exception>
|
|
internal static void CheckStackDepth()
|
|
{
|
|
try
|
|
{
|
|
RuntimeHelpers.EnsureSufficientExecutionStack();
|
|
}
|
|
catch (InsufficientExecutionStackException)
|
|
{
|
|
throw new ScriptCallDepthException();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// The current connection object
|
|
/// </summary>
|
|
private Runspace _currentRunspace;
|
|
//This should be internal, but it need to be friend of remoting dll.
|
|
/// <summary>
|
|
/// The current connection object
|
|
/// </summary>
|
|
internal Runspace CurrentRunspace
|
|
{
|
|
get { return _currentRunspace; }
|
|
set { _currentRunspace = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Each pipeline has a stack of pipeline processor. This method
|
|
/// pushes pp in to stack for currently executing pipeline.
|
|
/// </summary>
|
|
/// <param name="pp"></param>
|
|
internal void PushPipelineProcessor(PipelineProcessor pp)
|
|
{
|
|
if (_currentRunspace == null)
|
|
return;
|
|
LocalPipeline lpl = (LocalPipeline)((RunspaceBase)_currentRunspace).GetCurrentlyRunningPipeline();
|
|
if (lpl == null)
|
|
return;
|
|
lpl.Stopper.Push(pp);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Each pipeline has a stack of pipeline processor. This method pops the
|
|
/// top item from the stack
|
|
/// </summary>
|
|
internal void PopPipelineProcessor(bool fromSteppablePipeline)
|
|
{
|
|
if (_currentRunspace == null)
|
|
return;
|
|
LocalPipeline lpl = (LocalPipeline)((RunspaceBase)_currentRunspace).GetCurrentlyRunningPipeline();
|
|
if (lpl == null)
|
|
return;
|
|
lpl.Stopper.Pop(fromSteppablePipeline);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This flag is checked by parser to stop loops etc.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
internal bool CurrentPipelineStopping
|
|
{
|
|
get
|
|
{
|
|
if (_currentRunspace == null)
|
|
return false;
|
|
LocalPipeline lpl = (LocalPipeline)((RunspaceBase)_currentRunspace).GetCurrentlyRunningPipeline();
|
|
if (lpl == null)
|
|
return false;
|
|
return lpl.IsStopping;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <value></value>
|
|
internal bool PropagateExceptionsToEnclosingStatementBlock { get; set; }
|
|
|
|
internal RuntimeException CurrentExceptionBeingHandled { get; set; }
|
|
|
|
/// <summary>
|
|
/// Shortcut to get at $?
|
|
/// </summary>
|
|
/// <value>The current value of $? </value>
|
|
internal bool QuestionMarkVariableValue { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Shortcut to get at $error
|
|
/// </summary>
|
|
/// <value>The current value of $global:error </value>
|
|
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<ActionPreference>(
|
|
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<ActionPreference>(
|
|
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<ActionPreference>(
|
|
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<ActionPreference>(
|
|
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<ActionPreference>(
|
|
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<ConfirmImpact>(
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
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>(_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<TypeTable>(value) : null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Here for PSObject, should probably not be used elsewhere, maybe not even in PSObject.
|
|
/// </summary>
|
|
internal WeakReference<TypeTable> TypeTableWeakReference
|
|
{
|
|
get
|
|
{
|
|
if (_typeTable == null)
|
|
{
|
|
var unused = TypeTable;
|
|
}
|
|
return _typeTableWeakReference;
|
|
}
|
|
}
|
|
|
|
private TypeTable _typeTable;
|
|
private WeakReference<TypeTable> _typeTableWeakReference;
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
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;
|
|
|
|
/// <summary>
|
|
/// Gets the TransactionManager instance that controls transactions in the current
|
|
/// instance.
|
|
/// </summary>
|
|
internal PSTransactionManager TransactionManager
|
|
{
|
|
get
|
|
{
|
|
return transactionManager;
|
|
}
|
|
}
|
|
internal PSTransactionManager transactionManager;
|
|
|
|
|
|
private bool _assemblyCacheInitialized = false;
|
|
|
|
/// <summary>
|
|
/// This function is called by RunspaceConfiguration.Assemblies.Update call back.
|
|
/// It's not used when constructing a runspace from an InitialSessionState object.
|
|
/// </summary>
|
|
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 legitimate 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 = Assembly.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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Report an initialization-time error.
|
|
/// </summary>
|
|
/// <param name="resourceString">resource string</param>
|
|
/// <param name="arguments">arguments</param>
|
|
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) // swallow all exceptions
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Report an initialization-time error
|
|
/// </summary>
|
|
/// <param name="error">error to report</param>
|
|
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) // swallow all exceptions
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Report an initialization-time error
|
|
/// </summary>
|
|
/// <param name="e"></param>
|
|
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) // swallow all exceptions
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Report an initialization-time error
|
|
/// </summary>
|
|
/// <param name="errorRecord"></param>
|
|
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) // swallow all exceptions
|
|
{
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructs an Execution context object for Automation Engine
|
|
/// </summary>
|
|
///
|
|
/// <param name="engine">
|
|
/// Engine that hosts this execution context
|
|
/// </param>
|
|
/// <param name="hostInterface">
|
|
/// Interface that should be used for interaction with host
|
|
/// </param>
|
|
/// <param name="runspaceConfiguration">
|
|
/// RunspaceConfiguration information
|
|
/// </param>
|
|
internal ExecutionContext(AutomationEngine engine, PSHost hostInterface, RunspaceConfiguration runspaceConfiguration)
|
|
{
|
|
RunspaceConfiguration = runspaceConfiguration;
|
|
AuthorizationManager = runspaceConfiguration.AuthorizationManager;
|
|
|
|
InitializeCommon(engine, hostInterface);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructs an Execution context object for Automation Engine
|
|
/// </summary>
|
|
///
|
|
/// <param name="engine">
|
|
/// Engine that hosts this execution context
|
|
/// </param>
|
|
/// <param name="hostInterface">
|
|
/// Interface that should be used for interaction with host
|
|
/// </param>
|
|
/// <param name="initialSessionState">
|
|
/// InitialSessionState information
|
|
/// </param>
|
|
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 possibility 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<string, Assembly>();
|
|
|
|
// 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();
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <param name="sender">The event sender</param>
|
|
/// <param name="args">The event args</param>
|
|
/// <returns>The resolve assembly or null if not found</returns>
|
|
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
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enum that defines state of monad engine.
|
|
/// </summary>
|
|
///
|
|
internal enum EngineState
|
|
{
|
|
/// <summary>
|
|
/// Engine state is not defined or initialized.
|
|
/// </summary>
|
|
None = 0,
|
|
|
|
/// <summary>
|
|
/// Engine available
|
|
/// </summary>
|
|
Available = 1,
|
|
|
|
/// <summary>
|
|
/// Engine service is degraded
|
|
/// </summary>
|
|
Degraded = 2,
|
|
|
|
/// <summary>
|
|
/// Engine is out of service
|
|
/// </summary>
|
|
OutOfService = 3,
|
|
|
|
/// <summary>
|
|
/// Engine is stopped
|
|
/// </summary>
|
|
Stopped = 4
|
|
};
|
|
}
|