Files
PowerShell-PowerShell/src/System.Management.Automation/engine/SessionState.cs
T
xtqqczze 9212aac0fa Use nameof operator (#12716)
# PR Summary

Using *Roslynator Command Line Tool version 0.1.0.4*

* Fix RCS1015:
  * `"argument"` → `nameof(argument)`
  * `enum.ToString()` → `nameof(enum)`

[RCS1015.log](https://github.com/PowerShell/PowerShell/files/4646102/RCS1015.log)


## PR Context

<!-- Provide a little reasoning as to why this Pull Request helps and why you have opened it. -->

## PR Checklist

- [x] [PR has a meaningful title](https://github.com/PowerShell/PowerShell/blob/master/.github/CONTRIBUTING.md#pull-request---submission)
    - Use the present tense and imperative mood when describing your changes
- [x] [Summarized changes](https://github.com/PowerShell/PowerShell/blob/master/.github/CONTRIBUTING.md#pull-request---submission)
- [x] [Make sure all `.h`, `.cpp`, `.cs`, `.ps1` and `.psm1` files have the correct copyright header](https://github.com/PowerShell/PowerShell/blob/master/.github/CONTRIBUTING.md#pull-request---submission)
- [x] This PR is ready to merge and is not [Work in Progress](https://github.com/PowerShell/PowerShell/blob/master/.github/CONTRIBUTING.md#pull-request---work-in-progress).
    - If the PR is work in progress, please add the prefix `WIP:` or `[ WIP ]` to the beginning of the title (the `WIP` bot will keep its status check at `Pending` while the prefix is present) and remove the prefix when the PR is ready.
- **[Breaking changes](https://github.com/PowerShell/PowerShell/blob/master/.github/CONTRIBUTING.md#making-breaking-changes)**
    - [x] None
    - **OR**
    - [ ] [Experimental feature(s) needed](https://github.com/MicrosoftDocs/PowerShell-Docs/blob/staging/reference/6/Microsoft.PowerShell.Core/About/about_Experimental_Features.md)
        - [ ] Experimental feature name(s): <!-- Experimental feature name(s) here -->
- **User-facing changes**
    - [x] Not Applicable
    - **OR**
    - [ ] [Documentation needed](https://github.com/PowerShell/PowerShell/blob/master/.github/CONTRIBUTING.md#pull-request---submission)
        - [ ] Issue filed: <!-- Number/link of that issue here -->
- **Testing - New and feature**
    - [x] N/A or can only be tested interactively
    - **OR**
    - [ ] [Make sure you've added a new test if existing tests do not effectively test the code changed](https://github.com/PowerShell/PowerShell/blob/master/.github/CONTRIBUTING.md#before-submitting)
- **Tooling**
    - [x] I have considered the user experience from a tooling perspective and don't believe tooling will be impacted.
    - **OR**
    - [ ] I have considered the user experience from a tooling perspective and enumerated concerns in the summary. This may include:
        - Impact on [PowerShell Editor Services](https://github.com/PowerShell/PowerShellEditorServices) which is used in the [PowerShell extension](https://github.com/PowerShell/vscode-powershell) for VSCode (which runs in a different PS Host).
        - Impact on Completions (both in the console and in editors) - one of PowerShell's most powerful features.
        - Impact on [PSScriptAnalyzer](https://github.com/PowerShell/PSScriptAnalyzer) (which provides linting & formatting in the editor extensions).
        - Impact on [EditorSyntax](https://github.com/PowerShell/EditorSyntax) (which provides syntax highlighting with in VSCode, GitHub, and many other editors).
2020-05-19 18:57:52 +00:00

546 lines
22 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Management.Automation.Internal;
using System.Management.Automation.Language;
using System.Management.Automation.Runspaces;
using System.Security;
using Dbg = System.Management.Automation;
namespace System.Management.Automation
{
/// <summary>
/// Holds the state of a Monad Shell session.
/// </summary>
[SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "This is a bridge class between internal classes and a public interface. It requires this much coupling.")]
internal sealed partial class SessionStateInternal
{
#region tracer
/// <summary>
/// An instance of the PSTraceSource class used for trace output
/// using "SessionState" as the category.
/// </summary>
[Dbg.TraceSourceAttribute(
"SessionState",
"SessionState Class")]
private static Dbg.PSTraceSource s_tracer =
Dbg.PSTraceSource.GetTracer("SessionState",
"SessionState Class");
#endregion tracer
#region Constructor
/// <summary>
/// Constructor for session state object.
/// </summary>
/// <param name="context">
/// The context for the runspace to which this session state object belongs.
/// </param>
/// <exception cref="ArgumentNullException">
/// if <paramref name="context"/> is null.
/// </exception>
internal SessionStateInternal(ExecutionContext context) : this(null, false, context)
{
}
internal SessionStateInternal(SessionStateInternal parent, bool linkToGlobal, ExecutionContext context)
{
if (context == null)
{
throw PSTraceSource.NewArgumentNullException(nameof(context));
}
ExecutionContext = context;
// Create the working directory stack. This
// is used for the pushd and popd commands
_workingLocationStack = new Dictionary<string, Stack<PathInfo>>(StringComparer.OrdinalIgnoreCase);
// Conservative choice to limit the Set-Location history in order to limit memory impact in case of a regression.
const uint locationHistoryLimit = 20;
_setLocationHistory = new HistoryStack<PathInfo>(locationHistoryLimit);
GlobalScope = new SessionStateScope(null);
ModuleScope = GlobalScope;
_currentScope = GlobalScope;
InitializeSessionStateInternalSpecialVariables(false);
// Create the push the global scope on as
// the starting script scope. That way, if you dot-source a script
// that uses variables qualified by script: it works.
GlobalScope.ScriptScope = GlobalScope;
if (parent != null)
{
GlobalScope.Parent = parent.GlobalScope;
// Copy the drives and providers from the parent...
CopyProviders(parent);
// During loading of core modules, providers are not populated.
// We set the drive information later
if (Providers != null && Providers.Count > 0)
{
CurrentDrive = parent.CurrentDrive;
}
// Link it to the global scope...
if (linkToGlobal)
{
GlobalScope = parent.GlobalScope;
}
}
else
{
_currentScope.LocalsTuple = MutableTuple.MakeTuple(Compiler.DottedLocalsTupleType, Compiler.DottedLocalsNameIndexMap);
}
}
/// <summary>
/// Add any special variables to the session state variable table. This routine
/// must be called at construction time or if the variable table is reset.
/// </summary>
internal void InitializeSessionStateInternalSpecialVariables(bool clearVariablesTable)
{
if (clearVariablesTable)
{
// Clear the variable table
GlobalScope.Variables.Clear();
// Add in the per-scope default variables.
GlobalScope.AddSessionStateScopeDefaultVariables();
}
// Set variable $Error
PSVariable errorvariable = new PSVariable("Error", new ArrayList(), ScopedItemOptions.Constant);
GlobalScope.SetVariable(errorvariable.Name, errorvariable, false, false, this, fastPath: true);
// Set variable $PSDefaultParameterValues
Collection<Attribute> attributes = new Collection<Attribute>();
attributes.Add(new ArgumentTypeConverterAttribute(typeof(System.Management.Automation.DefaultParameterDictionary)));
PSVariable psDefaultParameterValuesVariable = new PSVariable(SpecialVariables.PSDefaultParameterValues,
new DefaultParameterDictionary(),
ScopedItemOptions.None, attributes,
RunspaceInit.PSDefaultParameterValuesDescription);
GlobalScope.SetVariable(psDefaultParameterValuesVariable.Name, psDefaultParameterValuesVariable, false, false, this, fastPath: true);
}
#endregion Constructor
#region Private data
/// <summary>
/// Provides all the path manipulation and globbing for Monad paths.
/// </summary>
internal LocationGlobber Globber
{
get { return _globberPrivate ?? (_globberPrivate = ExecutionContext.LocationGlobber); }
}
private LocationGlobber _globberPrivate;
/// <summary>
/// The context of the runspace to which this session state object belongs.
/// </summary>
internal ExecutionContext ExecutionContext { get; }
/// <summary>
/// Returns the public session state facade object for this session state instance.
/// </summary>
internal SessionState PublicSessionState
{
get { return _publicSessionState ?? (_publicSessionState = new SessionState(this)); }
set { _publicSessionState = value; }
}
private SessionState _publicSessionState;
/// <summary>
/// Gets the engine APIs to access providers.
/// </summary>
internal ProviderIntrinsics InvokeProvider
{
get { return _invokeProvider ?? (_invokeProvider = new ProviderIntrinsics(this)); }
}
private ProviderIntrinsics _invokeProvider;
/// <summary>
/// The module info object associated with this session state.
/// </summary>
internal PSModuleInfo Module { get; set; } = null;
// This is used to maintain the order in which modules were imported.
// This is used by Get-Command -All to order by last imported
internal List<string> ModuleTableKeys = new List<string>();
/// <summary>
/// The private module table for this session state object...
/// </summary>
internal Dictionary<string, PSModuleInfo> ModuleTable { get; } = new Dictionary<string, PSModuleInfo>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Get/set constraints for this execution environment.
/// </summary>
internal PSLanguageMode LanguageMode
{
get
{
return ExecutionContext.LanguageMode;
}
set
{
ExecutionContext.LanguageMode = value;
}
}
/// <summary>
/// If true the PowerShell debugger will use FullLanguage mode, otherwise it will use the current language mode.
/// </summary>
internal bool UseFullLanguageModeInDebugger
{
get
{
return ExecutionContext.UseFullLanguageModeInDebugger;
}
}
/// <summary>
/// The list of scripts that are allowed to be run. If the name "*"
/// is in the list, then all scripts can be run. (This is the default.)
/// </summary>
public List<string> Scripts { get; } = new List<string>(new string[] { "*" });
/// <summary>
/// See if a script is allowed to be run.
/// </summary>
/// <param name="scriptPath">Path to check.</param>
/// <returns>True if script is allowed.</returns>
internal SessionStateEntryVisibility CheckScriptVisibility(string scriptPath)
{
return checkPathVisibility(Scripts, scriptPath);
}
/// <summary>
/// The list of applications that are allowed to be run. If the name "*"
/// is in the list, then all applications can be run. (This is the default.)
/// </summary>
public List<string> Applications { get; } = new List<string>(new string[] { "*" });
/// <summary>
/// List of functions/filters to export from this session state object...
/// </summary>
internal List<CmdletInfo> ExportedCmdlets { get; } = new List<CmdletInfo>();
/// <summary>
/// Defines the default command visibility for this session state. Binding an InitialSessionState instance
/// with private members will set this to Private.
/// </summary>
internal SessionStateEntryVisibility DefaultCommandVisibility = SessionStateEntryVisibility.Public;
/// <summary>
/// Add an new SessionState cmdlet entry to this session state object...
/// </summary>
/// <param name="entry">The entry to add.</param>
internal void AddSessionStateEntry(SessionStateCmdletEntry entry)
{
AddSessionStateEntry(entry, /*local*/false);
}
/// <summary>
/// Add an new SessionState cmdlet entry to this session state object...
/// </summary>
/// <param name="entry">The entry to add.</param>
/// <param name="local">If local, add cmdlet to current scope. Else, add to module scope.</param>
internal void AddSessionStateEntry(SessionStateCmdletEntry entry, bool local)
{
ExecutionContext.CommandDiscovery.AddSessionStateCmdletEntryToCache(entry, local);
}
/// <summary>
/// Add an new SessionState cmdlet entry to this session state object...
/// </summary>
/// <param name="entry">The entry to add.</param>
internal void AddSessionStateEntry(SessionStateApplicationEntry entry)
{
this.Applications.Add(entry.Path);
}
/// <summary>
/// Add an new SessionState cmdlet entry to this session state object...
/// </summary>
/// <param name="entry">The entry to add.</param>
internal void AddSessionStateEntry(SessionStateScriptEntry entry)
{
this.Scripts.Add(entry.Path);
}
/// <summary>
/// Add the variables that must always be present in a SessionState instance...
/// </summary>
internal void InitializeFixedVariables()
{
//
// BUGBUG
//
// String resources for aliases are currently associated with Runspace init
//
// $Host
PSVariable v = new PSVariable(
SpecialVariables.Host,
ExecutionContext.EngineHostInterface,
ScopedItemOptions.Constant | ScopedItemOptions.AllScope,
RunspaceInit.PSHostDescription);
this.GlobalScope.SetVariable(v.Name, v, asValue: false, force: true, this, CommandOrigin.Internal, fastPath: true);
// $HOME - indicate where a user's home directory is located in the file system.
// -- %USERPROFILE% on windows
// -- %HOME% on unix
string home = Environment.GetEnvironmentVariable(Platform.CommonEnvVariableNames.Home) ?? string.Empty;
v = new PSVariable(SpecialVariables.Home,
home,
ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope,
RunspaceInit.HOMEDescription);
this.GlobalScope.SetVariable(v.Name, v, asValue: false, force: true, this, CommandOrigin.Internal, fastPath: true);
// $ExecutionContext
v = new PSVariable(SpecialVariables.ExecutionContext,
ExecutionContext.EngineIntrinsics,
ScopedItemOptions.Constant | ScopedItemOptions.AllScope,
RunspaceInit.ExecutionContextDescription);
this.GlobalScope.SetVariable(v.Name, v, asValue: false, force: true, this, CommandOrigin.Internal, fastPath: true);
// $PSVersionTable
v = new PSVariable(SpecialVariables.PSVersionTable,
PSVersionInfo.GetPSVersionTable(),
ScopedItemOptions.Constant | ScopedItemOptions.AllScope,
RunspaceInit.PSVersionTableDescription);
this.GlobalScope.SetVariable(v.Name, v, asValue: false, force: true, this, CommandOrigin.Internal, fastPath: true);
// $PSEdition
v = new PSVariable(SpecialVariables.PSEdition,
PSVersionInfo.PSEditionValue,
ScopedItemOptions.Constant | ScopedItemOptions.AllScope,
RunspaceInit.PSEditionDescription);
this.GlobalScope.SetVariable(v.Name, v, asValue: false, force: true, this, CommandOrigin.Internal, fastPath: true);
// $PID
Process currentProcess = Process.GetCurrentProcess();
v = new PSVariable(
SpecialVariables.PID,
currentProcess.Id,
ScopedItemOptions.Constant | ScopedItemOptions.AllScope,
RunspaceInit.PIDDescription);
this.GlobalScope.SetVariable(v.Name, v, asValue: false, force: true, this, CommandOrigin.Internal, fastPath: true);
// $PSCulture
v = new PSCultureVariable();
this.GlobalScope.SetVariableForce(v, this);
// $PSUICulture
v = new PSUICultureVariable();
this.GlobalScope.SetVariableForce(v, this);
// $?
v = new QuestionMarkVariable(this.ExecutionContext);
this.GlobalScope.SetVariableForce(v, this);
// $ShellId - if there is no runspace config, use the default string
string shellId = ExecutionContext.ShellID;
v = new PSVariable(SpecialVariables.ShellId, shellId,
ScopedItemOptions.Constant | ScopedItemOptions.AllScope,
RunspaceInit.MshShellIdDescription);
this.GlobalScope.SetVariable(v.Name, v, asValue: false, force: true, this, CommandOrigin.Internal, fastPath: true);
// $PSHOME
string applicationBase = Utils.DefaultPowerShellAppBase;
v = new PSVariable(SpecialVariables.PSHome, applicationBase,
ScopedItemOptions.Constant | ScopedItemOptions.AllScope,
RunspaceInit.PSHOMEDescription);
this.GlobalScope.SetVariable(v.Name, v, asValue: false, force: true, this, CommandOrigin.Internal, fastPath: true);
// $EnabledExperimentalFeatures
v = new PSVariable(SpecialVariables.EnabledExperimentalFeatures,
ExperimentalFeature.EnabledExperimentalFeatureNames,
ScopedItemOptions.Constant | ScopedItemOptions.AllScope,
RunspaceInit.EnabledExperimentalFeatures);
this.GlobalScope.SetVariable(v.Name, v, asValue: false, force: true, this, CommandOrigin.Internal, fastPath: true);
}
/// <summary>
/// Check to see if an application is allowed to be run.
/// </summary>
/// <param name="applicationPath">The path to the application to check.</param>
/// <returns>True if application is permitted.</returns>
internal SessionStateEntryVisibility CheckApplicationVisibility(string applicationPath)
{
return checkPathVisibility(Applications, applicationPath);
}
private SessionStateEntryVisibility checkPathVisibility(List<string> list, string path)
{
if (list == null || list.Count == 0) return SessionStateEntryVisibility.Private;
if (string.IsNullOrEmpty(path)) return SessionStateEntryVisibility.Private;
if (list.Contains("*")) return SessionStateEntryVisibility.Public;
foreach (string p in list)
{
if (string.Equals(p, path, StringComparison.OrdinalIgnoreCase))
return SessionStateEntryVisibility.Public;
if (WildcardPattern.ContainsWildcardCharacters(p))
{
WildcardPattern pattern = WildcardPattern.Get(p, WildcardOptions.IgnoreCase);
if (pattern.IsMatch(path))
{
return SessionStateEntryVisibility.Public;
}
}
}
return SessionStateEntryVisibility.Private;
}
#endregion Private data
/// <summary>
/// Notification for SessionState to do cleanup
/// before runspace is closed.
/// </summary>
internal void RunspaceClosingNotification()
{
if (this != ExecutionContext.TopLevelSessionState && Providers.Count > 0)
{
// Remove all providers at the top level...
CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext);
foreach (string providerName in Providers.Keys)
{
// All errors are ignored.
RemoveProvider(providerName, true, context);
}
}
}
#region Errors
/// <summary>
/// Constructs a new instance of a ProviderInvocationException
/// using the specified data.
/// </summary>
/// <param name="resourceId">
/// The resource ID to use as the format message for the error.
/// </param>
/// <param name="resourceStr">
/// This is the message template string.
/// </param>
/// <param name="provider">
/// The provider information used when formatting the error message.
/// </param>
/// <param name="path">
/// The path used when formatting the error message.
/// </param>
/// <param name="e">
/// The exception that was thrown by the provider. This will be set as
/// the ProviderInvocationException's InnerException and the message will
/// be used when formatting the error message.
/// </param>
/// <returns>
/// A new instance of a ProviderInvocationException.
/// </returns>
/// <exception cref="ProviderInvocationException">
/// Wraps <paramref name="e"/> in a ProviderInvocationException
/// and then throws it.
/// </exception>
internal ProviderInvocationException NewProviderInvocationException(
string resourceId,
string resourceStr,
ProviderInfo provider,
string path,
Exception e)
{
return NewProviderInvocationException(resourceId, resourceStr, provider, path, e, true);
}
/// <summary>
/// Constructs a new instance of a ProviderInvocationException
/// using the specified data.
/// </summary>
/// <param name="resourceId">
/// The resource ID to use as the format message for the error.
/// </param>
/// <param name="resourceStr">
/// This is the message template string.
/// </param>
/// <param name="provider">
/// The provider information used when formatting the error message.
/// </param>
/// <param name="path">
/// The path used when formatting the error message.
/// </param>
/// <param name="e">
/// The exception that was thrown by the provider. This will be set as
/// the ProviderInvocationException's InnerException and the message will
/// be used when formatting the error message.
/// </param>
/// <param name="useInnerExceptionErrorMessage">
/// If true, the error record from the inner exception will be used if it contains one.
/// If false, the error message specified by the resourceId will be used.
/// </param>
/// <returns>
/// A new instance of a ProviderInvocationException.
/// </returns>
/// <exception cref="ProviderInvocationException">
/// Wraps <paramref name="e"/> in a ProviderInvocationException
/// and then throws it.
/// </exception>
internal ProviderInvocationException NewProviderInvocationException(
string resourceId,
string resourceStr,
ProviderInfo provider,
string path,
Exception e,
bool useInnerExceptionErrorMessage)
{
// If the innerException was itself thrown by
// ProviderBase.ThrowTerminatingError, it is already a
// ProviderInvocationException, and we don't want to
// re-wrap it.
ProviderInvocationException pie = e as ProviderInvocationException;
if (pie != null)
{
pie._providerInfo = provider;
return pie;
}
pie = new ProviderInvocationException(resourceId, resourceStr, provider, path, e, useInnerExceptionErrorMessage);
// Log a provider health event
MshLog.LogProviderHealthEvent(
ExecutionContext,
provider.Name,
pie,
Severity.Warning);
return pie;
}
#endregion Errors
}
}