Files
PowerShell/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.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

367 lines
13 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Internal;
using System.Management.Automation.Language;
using System.Runtime.CompilerServices;
namespace Microsoft.PowerShell.Commands
{
/// <summary>
/// Class that represents the results from evaluating a PSPropertyExpression against an object.
/// </summary>
public class PSPropertyExpressionResult
{
/// <summary>
/// Create a property expression result containing the original object, matching property expression
/// and any exception generated during the match process.
/// </summary>
public PSPropertyExpressionResult(object res, PSPropertyExpression re, Exception e)
{
Result = res;
ResolvedExpression = re;
Exception = e;
}
/// <summary>
/// The value of the object property matched by this property expression.
/// </summary>
public object Result { get; } = null;
/// <summary>
/// The original property expression fully resolved.
/// </summary>
public PSPropertyExpression ResolvedExpression { get; } = null;
/// <summary>
/// Any exception thrown while evaluating the expression.
/// </summary>
public Exception Exception { get; } = null;
}
/// <summary>
/// PSPropertyExpression class. This class is used to get the names and/or values of properties
/// on an object. A property expression can be constructed using either a wildcard expression string
/// or a scriptblock to use to get the property value.
/// </summary>
public class PSPropertyExpression
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="s">Expression.</param>
/// <exception cref="ArgumentNullException"></exception>
public PSPropertyExpression(string s)
: this(s, false)
{
}
/// <summary>
/// Create a property expression with a wildcard pattern.
/// </summary>
/// <param name="s">Property name pattern to match.</param>
/// <param name="isResolved"><c>true</c> if no further attempts should be made to resolve wildcards.</param>
/// <exception cref="ArgumentNullException"></exception>
public PSPropertyExpression(string s, bool isResolved)
{
if (string.IsNullOrEmpty(s))
{
throw PSTraceSource.NewArgumentNullException(nameof(s));
}
_stringValue = s;
_isResolved = isResolved;
}
/// <summary>
/// Create a property expression with a ScriptBlock.
/// </summary>
/// <param name="scriptBlock">ScriptBlock to evaluate when retrieving the property value from an object.</param>
/// <exception cref="ArgumentNullException"></exception>
public PSPropertyExpression(ScriptBlock scriptBlock)
{
if (scriptBlock == null)
{
throw PSTraceSource.NewArgumentNullException(nameof(scriptBlock));
}
Script = scriptBlock;
}
/// <summary>
/// The ScriptBlock for this expression to use when matching.
/// </summary>
public ScriptBlock Script { get; } = null;
/// <summary>
/// ToString() implementation for the property expression.
/// </summary>
public override string ToString()
{
if (Script != null)
return Script.ToString();
return _stringValue;
}
/// <summary>
/// Resolve the names matched by the expression.
/// </summary>
/// <param name="target">The object to apply the expression against.</param>
public List<PSPropertyExpression> ResolveNames(PSObject target)
{
return ResolveNames(target, true);
}
/// <summary>
/// Indicates if the pattern has wildcard characters in it. If the supplied pattern was
/// a scriptblock, this will be false.
/// </summary>
public bool HasWildCardCharacters
{
get
{
if (Script != null)
return false;
return WildcardPattern.ContainsWildcardCharacters(_stringValue);
}
}
/// <summary>
/// Resolve the names matched by the expression.
/// </summary>
/// <param name="target">The object to apply the expression against.</param>
/// <param name="expand">If the matched properties are property sets, expand them.</param>
public List<PSPropertyExpression> ResolveNames(PSObject target, bool expand)
{
List<PSPropertyExpression> retVal = new List<PSPropertyExpression>();
if (_isResolved)
{
retVal.Add(this);
return retVal;
}
if (Script != null)
{
// script block, just add it to the list and be done
PSPropertyExpression ex = new PSPropertyExpression(Script);
ex._isResolved = true;
retVal.Add(ex);
return retVal;
}
// If the object passed in is a hashtable, then turn it into a PSCustomObject so
// that property expressions can work on it.
target = IfHashtableWrapAsPSCustomObject(target);
// we have a string value
IEnumerable<PSMemberInfo> members = null;
if (HasWildCardCharacters)
{
// get the members first: this will expand the globbing on each parameter
members = target.Members.Match(_stringValue,
PSMemberTypes.Properties | PSMemberTypes.PropertySet | PSMemberTypes.Dynamic);
}
else
{
// we have no globbing: try an exact match, because this is quicker.
PSMemberInfo x = target.Members[_stringValue];
if ((x == null) && (target.BaseObject is System.Dynamic.IDynamicMetaObjectProvider))
{
// We could check if GetDynamicMemberNames includes the name... but
// GetDynamicMemberNames is only a hint, not a contract, so we'd want
// to attempt the binding whether it's in there or not.
x = new PSDynamicMember(_stringValue);
}
List<PSMemberInfo> temp = new List<PSMemberInfo>();
if (x != null)
{
temp.Add(x);
}
members = temp;
}
// we now have a list of members, we have to expand property sets
// and remove duplicates
List<PSMemberInfo> temporaryMemberList = new List<PSMemberInfo>();
foreach (PSMemberInfo member in members)
{
// it can be a property set
PSPropertySet propertySet = member as PSPropertySet;
if (propertySet != null)
{
if (expand)
{
// NOTE: we expand the property set under the
// assumption that it contains property names that
// do not require any further expansion
Collection<string> references = propertySet.ReferencedPropertyNames;
for (int j = 0; j < references.Count; j++)
{
ReadOnlyPSMemberInfoCollection<PSPropertyInfo> propertyMembers =
target.Properties.Match(references[j]);
for (int jj = 0; jj < propertyMembers.Count; jj++)
{
temporaryMemberList.Add(propertyMembers[jj]);
}
}
}
}
// it can be a property
else if (member is PSPropertyInfo)
{
temporaryMemberList.Add(member);
}
// it can be a dynamic member
else if (member is PSDynamicMember)
{
temporaryMemberList.Add(member);
}
}
Hashtable hash = new Hashtable();
// build the list of unique values: remove the possible duplicates
// from property set expansion
foreach (PSMemberInfo m in temporaryMemberList)
{
if (!hash.ContainsKey(m.Name))
{
PSPropertyExpression ex = new PSPropertyExpression(m.Name);
ex._isResolved = true;
retVal.Add(ex);
hash.Add(m.Name, null);
}
}
return retVal;
}
/// <summary>
/// Gets the values of the object properties matched by this expression.
/// </summary>
/// <param name="target">The object to match against.</param>
public List<PSPropertyExpressionResult> GetValues(PSObject target)
{
return GetValues(target, true, true);
}
/// <summary>
/// Gets the values of the object properties matched by this expression.
/// </summary>
/// <param name="target">The object to match against.</param>
/// <param name="expand">If the matched properties are parameter sets, expand them.</param>
/// <param name="eatExceptions">If true, any exceptions that occur during the match process are ignored.</param>
public List<PSPropertyExpressionResult> GetValues(PSObject target, bool expand, bool eatExceptions)
{
List<PSPropertyExpressionResult> retVal = new List<PSPropertyExpressionResult>();
// If the object passed in is a hashtable, then turn it into a PSCustomObject so
// that property expressions can work on it.
target = IfHashtableWrapAsPSCustomObject(target);
// process the script case
if (Script != null)
{
PSPropertyExpression scriptExpression = new PSPropertyExpression(Script);
PSPropertyExpressionResult r = scriptExpression.GetValue(target, eatExceptions);
retVal.Add(r);
return retVal;
}
// process the expression
List<PSPropertyExpression> resolvedExpressionList = this.ResolveNames(target, expand);
foreach (PSPropertyExpression re in resolvedExpressionList)
{
PSPropertyExpressionResult r = re.GetValue(target, eatExceptions);
retVal.Add(r);
}
return retVal;
}
#region Private Members
private CallSite<Func<CallSite, object, object>> _getValueDynamicSite;
private PSPropertyExpressionResult GetValue(PSObject target, bool eatExceptions)
{
try
{
object result = null;
if (Script != null)
{
result = Script.DoInvokeReturnAsIs(
useLocalScope: true,
errorHandlingBehavior: ScriptBlock.ErrorHandlingBehavior.WriteToExternalErrorPipe,
dollarUnder: target,
input: AutomationNull.Value,
scriptThis: AutomationNull.Value,
args: Array.Empty<object>());
}
else
{
if (_getValueDynamicSite == null)
{
_getValueDynamicSite =
CallSite<Func<CallSite, object, object>>.Create(
PSGetMemberBinder.Get(
_stringValue,
classScope: (Type)null,
@static: false));
}
result = _getValueDynamicSite.Target.Invoke(_getValueDynamicSite, target);
}
return new PSPropertyExpressionResult(result, this, null);
}
catch (RuntimeException e)
{
if (eatExceptions)
{
return new PSPropertyExpressionResult(null, this, e);
}
else
{
throw;
}
}
}
private PSObject IfHashtableWrapAsPSCustomObject(PSObject target)
{
// If the object passed in is a hashtable, then turn it into a PSCustomObject so
// that property expressions can work on it.
if (PSObject.Base(target) is Hashtable targetAsHash)
{
target = (PSObject)(LanguagePrimitives.ConvertPSObjectToType(targetAsHash, typeof(PSObject), false, null, true));
}
return target;
}
// private members
private string _stringValue;
private bool _isResolved = false;
#endregion Private Members
}
}