Skip to content

Commit bde9e1b

Browse files
Fix Add-PrivateMember (#8)
The instances were not being changed correctly on cloned member info objects. This change adds a DLR binder to properly update instances.
1 parent 042c7b9 commit bde9e1b

6 files changed

Lines changed: 119 additions & 5 deletions

File tree

src/ImpliedReflection/Cache.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ namespace ImpliedReflection
88
{
99
internal static class Cache
1010
{
11+
public static ConstructorInfo RuntimeException_ctor = typeof(RuntimeException).
12+
GetConstructor(
13+
Bind.Public.Instance,
14+
binder: null,
15+
new[] { typeof(string) },
16+
modifiers: null);
17+
1118
public static MethodInfo PropertyInfo_GetGetMethod = typeof(PropertyInfo)
1219
.GetMethod(
1320
nameof(PropertyInfo.GetGetMethod),

src/ImpliedReflection/Commands/AddPrivateMemberCommand.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,17 @@ protected override void ProcessRecord()
4848

4949
foreach (PSMemberInfo member in memberTable.Members)
5050
{
51-
InputObject.Members.Add(member, preValidated: true);
52-
if (member is PSPropertyInfo property)
51+
PSMemberInfo clonedMember = member.Clone();
52+
clonedMember.ReplicateInstance(InputObject.BaseObject);
53+
InputObject.Members.Add(clonedMember, preValidated: true);
54+
if (clonedMember is PSPropertyInfo property)
5355
{
54-
InputObject.Properties.Add(property, preValidated: true);
56+
InputObject.Properties.Add(property.Clone(), preValidated: true);
5557
}
5658

57-
if (member is PSMethodInfo method)
59+
if (clonedMember is PSMethodInfo method)
5860
{
59-
InputObject.Methods.Add(method, preValidated: true);
61+
InputObject.Methods.Add(method.Clone(), preValidated: true);
6062
}
6163
}
6264

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
using System.Management.Automation;
3+
using System.Runtime.CompilerServices;
4+
5+
namespace ImpliedReflection
6+
{
7+
internal static class PSMemberInfoExtensions
8+
{
9+
private static readonly Action<PSMemberInfo, object> s_replicateInstance;
10+
11+
public static readonly CallSite<Action<CallSite, PSMemberInfo, object>> s_callsite
12+
= CallSite<Action<CallSite, PSMemberInfo, object>>.Create(ReplicateInstanceBinder.Instance);
13+
14+
public static TMemberInfo Clone<TMemberInfo>(this TMemberInfo member)
15+
where TMemberInfo : PSMemberInfo
16+
{
17+
return (TMemberInfo)member.Copy();
18+
}
19+
20+
public static void ReplicateInstance(this PSMemberInfo member, object instance)
21+
=> s_callsite.Update(s_callsite, member, instance);
22+
}
23+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using System;
2+
using System.Dynamic;
3+
using System.Reflection;
4+
5+
using BindFlags = ImpliedReflection.Bind;
6+
7+
using static System.Linq.Expressions.Expression;
8+
using System.Runtime.CompilerServices;
9+
using System.Management.Automation;
10+
using System.Linq.Expressions;
11+
12+
namespace ImpliedReflection
13+
{
14+
internal class ReplicateInstanceBinder : InvokeMemberBinder
15+
{
16+
public readonly static ReplicateInstanceBinder Instance = new ReplicateInstanceBinder();
17+
18+
private ReplicateInstanceBinder()
19+
: base(nameof(PSMemberInfoExtensions.ReplicateInstance), ignoreCase: false, new CallInfo(1, "instance"))
20+
{
21+
}
22+
23+
public override DynamicMetaObject FallbackInvoke(
24+
DynamicMetaObject target,
25+
DynamicMetaObject[] args,
26+
DynamicMetaObject errorSuggestion)
27+
{
28+
throw new System.NotImplementedException();
29+
}
30+
31+
public override DynamicMetaObject FallbackInvokeMember(
32+
DynamicMetaObject target,
33+
DynamicMetaObject[] args,
34+
DynamicMetaObject errorSuggestion)
35+
{
36+
Type type = target.RuntimeType;
37+
MethodInfo method = type.GetMethod(
38+
VersionSpecificMemberNames.PrivateMemberNames.PSMemberInfo_ReplicateInstance,
39+
BindFlags.Any.Instance,
40+
null,
41+
new[] { typeof(object) },
42+
null);
43+
44+
if (method == null)
45+
{
46+
return new DynamicMetaObject(
47+
Block(
48+
Throw(New(Cache.RuntimeException_ctor, Constant(ImpliedReflectionStrings.InvalidMemberInfo))),
49+
Default(typeof(object))),
50+
BindingRestrictions.GetTypeRestriction(target.Expression, type));
51+
}
52+
53+
Expression instance = Convert(target.Expression, type);
54+
Expression callExpr = Call(instance, method, args[0].Expression);
55+
56+
if (typeof(PSProperty).IsAssignableFrom(type))
57+
{
58+
// PSProperty doesn't override ReplicateInstance, so the call doesn't actually change
59+
// it's private baseObject field.
60+
FieldInfo baseObject = typeof(PSProperty).GetField(
61+
nameof(baseObject),
62+
BindFlags.NonPublic.Instance);
63+
64+
return new DynamicMetaObject(
65+
Block(callExpr, Assign(Field(instance, baseObject), args[0].Expression), Default(typeof(object))),
66+
BindingRestrictions.GetTypeRestriction(target.Expression, type));
67+
}
68+
69+
return new DynamicMetaObject(
70+
Block(callExpr, Default(typeof(object))),
71+
BindingRestrictions.GetTypeRestriction(target.Expression, type));
72+
}
73+
}
74+
}

src/ImpliedReflection/VersionSpecificMemberNames.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,5 +178,10 @@ private static VersionSpecificMemberNames CreateInstance()
178178
/// Gets a string similar to "CollectionNameForTracing".
179179
/// </summary>
180180
public virtual string CollectionEntry_CollectionNameForTracing => "CollectionNameForTracing";
181+
182+
/// <summary>
183+
/// Gets a string similar to "ReplicateInstance".
184+
/// </summary>
185+
public virtual string PSMemberInfo_ReplicateInstance => "ReplicateInstance";
181186
}
182187
}

src/ImpliedReflection/resources/ImpliedReflectionStrings.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,7 @@ This can have unintended consequences, are you sure you want to continue?</value
137137
<data name="UnknownPowerShellVersion" xml:space="preserve">
138138
<value>Unable to determine the current PowerShell version. If this is a custom build of PowerShell, please file a GitHub issue for guidance.</value>
139139
</data>
140+
<data name="InvalidMemberInfo" xml:space="preserve">
141+
<value>An attempt to clone a PSMemberInfo object failed because no suitable "ReplicateInstance" method was found.</value>
142+
</data>
140143
</root>

0 commit comments

Comments
 (0)