mirror of
https://github.com/aphrody-labs/PowerShell.git
synced 2026-10-23 18:28:11 +00:00
# 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).
367 lines
13 KiB
C#
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
|
|
}
|
|
}
|
|
|