Skip to content

Commit

Permalink
Make Select-Object/ForEach-Object/Where-Object see dynamic properties (
Browse files Browse the repository at this point in the history
…PowerShell#6898)

Dynamic (DLR) objects work in some places today, but not others.  This change expands that support to ForEach-Object, Where-Object and the family of cmdlets that use 'MshExpression' (Select-Object, etc.).

This change addresses both wildcard and non-wildcard cases. In wildcard cases, it uses the existing support of generating PSDynamicMember objects for names returned by GetDynamicMemberNames.

In non-wildcard cases, a dynamic property access is attempted whether or not the name shows up in GetDynamicMemberNames, but a truly "blind" access is only attempted if we see that the base object is an IDMOP. If the dynamic access fails, you'll get the same or a similar error experience as before ("The property 'Blarg' cannot be found", or no error at all, depending on the cmdlet and the strict mode setting).

The included test coverage includes a stub for the.ForEach operator--once people are happy with this change, I can continue by adding support there.

This change should allegedly also have positive perf impact, though in actual perf testing, although it does seem ever-so-slightly faster, I found it difficult to measure much difference at all.
  • Loading branch information
jazzdelightsme authored and daxian-dbw committed Jun 13, 2018
1 parent 3a5a3e4 commit ce3cb68
Show file tree
Hide file tree
Showing 7 changed files with 401 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
using System;
using System.Management.Automation;
using System.Management.Automation.Internal;
using System.Management.Automation.Language;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;

namespace Microsoft.PowerShell.Commands.Internal.Format
{
Expand Down Expand Up @@ -123,13 +125,20 @@ internal List<MshExpression> ResolveNames(PSObject target, bool expand)
{
// get the members first: this will expand the globbing on each parameter
members = target.Members.Match(_stringValue,
PSMemberTypes.Properties | PSMemberTypes.PropertySet);
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)
{
Expand Down Expand Up @@ -165,10 +174,14 @@ internal List<MshExpression> ResolveNames(PSObject target, bool expand)
}
}
}
continue;
}
// it can be a property
if (member is PSPropertyInfo)
else if (member is PSPropertyInfo)
{
temporaryMemberList.Add(member);
}
// it can be a dynamic member
else if (member is PSDynamicMember)
{
temporaryMemberList.Add(member);
}
Expand Down Expand Up @@ -225,11 +238,13 @@ internal List<MshExpressionResult> GetValues(PSObject target, bool expand, bool

#region Private Members

private CallSite<Func<CallSite, object, object>> _getValueDynamicSite;

private MshExpressionResult GetValue(PSObject target, bool eatExceptions)
{
try
{
object result;
object result = null;

if (Script != null)
{
Expand All @@ -243,12 +258,16 @@ private MshExpressionResult GetValue(PSObject target, bool eatExceptions)
}
else
{
PSMemberInfo member = target.Properties[_stringValue];
if (member == null)
if (_getValueDynamicSite == null)
{
return new MshExpressionResult(null, this, null);
_getValueDynamicSite =
CallSite<Func<CallSite, object, object>>.Create(
PSGetMemberBinder.Get(
_stringValue,
classScope: (Type) null,
@static: false));
}
result = member.Value;
result = _getValueDynamicSite.Target.Invoke(_getValueDynamicSite, target);
}

return new MshExpressionResult(result, this, null);
Expand Down
Loading

0 comments on commit ce3cb68

Please sign in to comment.