Skip to content

Commit 0c691d0

Browse files
Add signature parameter to Find-Member (#53)
Also add a `sig` member signature to match against a full method signature. Smudge the signature interfaces a bit to make it easier to handle in one class.
1 parent 2b436ce commit 0c691d0

19 files changed

+535
-233
lines changed

src/ClassExplorer/Commands/FindMemberCommand.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,10 @@ public SwitchParameter Extension
181181
set => _options.Extension = value;
182182
}
183183

184+
[Parameter]
185+
[Alias("sig")]
186+
public ScriptBlockStringOrType? Signature { get; set; }
187+
184188
private MemberSearch<PipelineEmitter<MemberInfo>> _search = null!;
185189

186190
private protected override void OnNoInput()
@@ -212,6 +216,7 @@ protected override void InitializeFilters()
212216
_options.ResolutionMap = resolutionMap;
213217
_options.AccessView = AccessView;
214218
_options.Decoration = Decoration;
219+
_options.Signature = Signature;
215220
_search = Search.Members(_options, new PipelineEmitter<MemberInfo>(this));
216221
}
217222
}

src/ClassExplorer/Commands/FindTypeCommand.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -153,24 +153,24 @@ protected override void InitializeFilters()
153153
Dictionary<string, ScriptBlockStringOrType>? resolutionMap = InitializeResolutionMap();
154154

155155
var parser = new SignatureParser(resolutionMap);
156-
var signatures = ImmutableArray.CreateBuilder<ITypeSignature>();
157-
ITypeSignature? signature = null;
156+
var signatures = ImmutableArray.CreateBuilder<ISignature>();
157+
ISignature? signature = null;
158158
if (Signature is not null)
159159
{
160-
signature = Signature.Resolve(parser);
160+
signature = Signature.ResolveType(parser);
161161
signatures.Add(signature);
162162
}
163163

164164
if (InheritsType is not null)
165165
{
166-
ITypeSignature inheritsTypeSignature = InheritsType.Resolve(parser, excludeSelf: true);
166+
ISignature inheritsTypeSignature = InheritsType.ResolveType(parser, excludeSelf: true);
167167
signatures.Add(inheritsTypeSignature);
168168
signature ??= inheritsTypeSignature;
169169
}
170170

171171
if (ImplementsInterface is not null)
172172
{
173-
ITypeSignature implementsSignature = ImplementsInterface.Resolve(parser, excludeSelf: true);
173+
ISignature implementsSignature = ImplementsInterface.ResolveType(parser, excludeSelf: true);
174174
signatures.Add(implementsSignature);
175175
signature ??= implementsSignature;
176176
}

src/ClassExplorer/MemberSearch.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,21 +268,21 @@ protected override void InitializeOtherFilters(List<Filter<MemberInfo>> filters,
268268
if (_options.GenericParameter is not null)
269269
{
270270
filters.AddFilter(
271-
new GenericParameterTypeSignature(_options.GenericParameter.Resolve(parser)),
271+
new GenericParameterTypeSignature(_options.GenericParameter.ResolveType(parser)),
272272
static (member, signature) => signature.IsMatch(member));
273273
}
274274

275275
if (_options.ParameterType is not null)
276276
{
277277
filters.AddFilter(
278-
new ParameterTypeSignature(_options.ParameterType.Resolve(parser)),
278+
new ParameterTypeSignature(_options.ParameterType.ResolveType(parser)),
279279
static (member, signature) => signature.IsMatch(member));
280280
}
281281

282282
if (_options.ReturnType is not null)
283283
{
284284
filters.AddFilter(
285-
new ReturnTypeSignature(_options.ReturnType.Resolve(parser)),
285+
new ReturnTypeSignature(_options.ReturnType.ResolveType(parser)),
286286
static (member, signature) => signature.IsMatch(member));
287287
}
288288

@@ -295,5 +295,12 @@ protected override void InitializeOtherFilters(List<Filter<MemberInfo>> filters,
295295
_options.ResolutionMap)),
296296
static (member, signature) => signature.IsMatch(member));
297297
}
298+
299+
if (_options.Signature is not null)
300+
{
301+
filters.AddFilter(
302+
(IMemberSignature)_options.Signature.Resolve(parser, SignatureKind.Member),
303+
static (member, signature) => signature.IsMatch(member));
304+
}
298305
}
299306
}

src/ClassExplorer/MemberSearchOptions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,6 @@ internal class MemberSearchOptions : ReflectionSearchOptions
3333
public bool RecurseNestedType { get; set; }
3434

3535
public bool Extension { get; set; }
36+
37+
public ScriptBlockStringOrType? Signature { get; set; }
3638
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using System;
2+
using System.Linq.Expressions;
3+
using System.Management.Automation;
4+
using System.Reflection;
5+
6+
namespace ClassExplorer;
7+
8+
internal static class ReflectionCache
9+
{
10+
private static readonly Type? ExecutionContextType;
11+
12+
private static readonly MethodInfo? GetExecutionContextFromTLSMethod;
13+
14+
private static readonly Func<string[]>? GetUsingNamespacesFunc;
15+
16+
static ReflectionCache()
17+
{
18+
ExecutionContextType = typeof(PSObject).Assembly.GetType("System.Management.Automation.ExecutionContext");
19+
if (ExecutionContextType is null) return;
20+
21+
GetExecutionContextFromTLSMethod = typeof(PSObject).Assembly.GetType("System.Management.Automation.Runspaces.LocalPipeline")
22+
?.GetMethod("GetExecutionContextFromTLS", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
23+
24+
if (GetExecutionContextFromTLSMethod is null) return;
25+
26+
GetUsingNamespacesFunc = CreateGetUsingNamespaces();
27+
}
28+
29+
public static string[] GetUsingNamespacesFromTLS()
30+
{
31+
return GetUsingNamespacesFunc?.Invoke() ?? ["System"];
32+
}
33+
34+
private static Func<string[]>? CreateGetUsingNamespaces()
35+
{
36+
const BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
37+
38+
if (GetExecutionContextFromTLSMethod is null) return null;
39+
40+
PropertyInfo? getEngineSessionState = ExecutionContextType?.GetProperty("EngineSessionState", flags);
41+
if (getEngineSessionState is null) return null;
42+
43+
PropertyInfo? currentScope = getEngineSessionState.GetReturnType()?.GetProperty("CurrentScope", flags);
44+
if (currentScope is null) return null;
45+
46+
PropertyInfo? typeRes = currentScope.GetReturnType()?.GetProperty("TypeResolutionState", flags);
47+
if (typeRes is null) return null;
48+
49+
FieldInfo? namespaces = typeRes.GetReturnType()?.GetField("namespaces", flags);
50+
if (namespaces is null) return null;
51+
if (namespaces.FieldType != typeof(string[])) return null;
52+
53+
return Expression.Lambda<Func<string[]>>(
54+
Expression.Field(
55+
Expression.Call(
56+
Expression.Call(
57+
Expression.Call(
58+
Expression.Call(GetExecutionContextFromTLSMethod),
59+
getEngineSessionState.GetGetMethod(nonPublic: true)),
60+
currentScope.GetGetMethod(nonPublic: true)),
61+
typeRes.GetGetMethod(nonPublic: true)),
62+
namespaces),
63+
"GetUsingNamespacesDynamically",
64+
[])
65+
.Compile();
66+
}
67+
}

src/ClassExplorer/SR.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@
129129
<data name="DecorationBadArgs" xml:space="preserve">
130130
<value>The keyword "decoration" requires exactly one generic argument (Example: [decoration[ParameterAttribute]]). See https://seemingly.dev/about-type-signatures or help about_Type_Signatures.</value>
131131
</data>
132+
<data name="SigBadArgs" xml:space="preserve">
133+
<value>The keyword "sig" requires at least one generic argument representing the return type (Example: [sig[Arg1, Arg2, void]]). See https://seemingly.dev/about-type-signatures or help about_Type_Signatures.</value>
134+
</data>
132135
<data name="TypeNotFound" xml:space="preserve">
133136
<value>Unable to find type [{0}].</value>
134137
</data>

src/ClassExplorer/ScriptBlockStringOrType.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22
using System.Collections.Immutable;
33
using System.Management.Automation;
44
using System.Management.Automation.Language;
5-
using System.Runtime.CompilerServices;
65
using ClassExplorer.Signatures;
76

87
namespace ClassExplorer
98
{
109
public sealed class ScriptBlockStringOrType
1110
{
12-
private ITypeSignature? _cachedType;
11+
private ISignature? _cachedType;
1312

1413
public ScriptBlockStringOrType(string? typeName) => Value = typeName;
1514

@@ -21,7 +20,10 @@ public sealed class ScriptBlockStringOrType
2120

2221
internal object? Value { get; }
2322

24-
internal ITypeSignature Resolve(SignatureParser parser, bool isForMap = false, bool excludeSelf = false)
23+
internal ISignature ResolveType(SignatureParser parser, bool isForMap = false, bool excludeSelf = false)
24+
=> Resolve(parser, SignatureKind.Type, isForMap, excludeSelf);
25+
26+
internal ISignature Resolve(SignatureParser parser, SignatureKind expected, bool isForMap = false, bool excludeSelf = false)
2527
{
2628
if (_cachedType is not null)
2729
{
@@ -30,7 +32,7 @@ internal ITypeSignature Resolve(SignatureParser parser, bool isForMap = false, b
3032

3133
if (Value is Type exactType)
3234
{
33-
static ITypeSignature SignatureForType(Type type, bool isForMap, bool excludeSelf)
35+
static ISignature SignatureForType(Type type, SignatureKind expected, bool isForMap, bool excludeSelf)
3436
{
3537
if (isForMap)
3638
{
@@ -40,20 +42,20 @@ static ITypeSignature SignatureForType(Type type, bool isForMap, bool excludeSel
4042
if (excludeSelf)
4143
{
4244
return new AllOfTypeSignature(
43-
ImmutableArray.Create<ITypeSignature>(
45+
ImmutableArray.Create<ISignature>(
4446
new AssignableTypeSignature(type),
4547
new NotTypeSignature(new ExactTypeSignature(type))));
4648
}
4749

4850
return new ContainsSignature(new AssignableTypeSignature(type));
4951
}
5052

51-
return _cachedType = SignatureForType(exactType, isForMap, excludeSelf);
53+
return _cachedType = SignatureForType(exactType, expected, isForMap, excludeSelf);
5254
}
5355

5456
if (Value is ScriptBlock scriptBlock)
5557
{
56-
return _cachedType = parser.Parse(scriptBlock);
58+
return _cachedType = parser.Parse(scriptBlock, expected);
5759
}
5860

5961
if (Value is string typeName)
@@ -75,7 +77,7 @@ static ScriptBlockAst GetAstForString(string typeName)
7577
}
7678

7779
ScriptBlockAst ast = GetAstForString(typeName);
78-
ITypeSignature signature = parser.Parse(ast);
80+
ISignature signature = parser.Parse(ast, expected);
7981
if (isForMap)
8082
{
8183
return _cachedType = signature;
@@ -87,7 +89,7 @@ static ScriptBlockAst GetAstForString(string typeName)
8789
{
8890
return _cachedType =
8991
new AllOfTypeSignature(
90-
ImmutableArray.Create<ITypeSignature>(
92+
ImmutableArray.Create<ISignature>(
9193
assignable,
9294
new NotTypeSignature(new ExactTypeSignature(assignable.Type))));
9395
}
@@ -98,7 +100,7 @@ static ScriptBlockAst GetAstForString(string typeName)
98100
return _cachedType = new ContainsSignature(signature);
99101
}
100102

101-
return Unreachable.Code<ITypeSignature>();
103+
return Unreachable.Code<ISignature>();
102104
}
103105
}
104106
}

src/ClassExplorer/SignatureWriter.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,6 +1066,17 @@ public SignatureWriter MaybeNewLine(ref bool isFirst)
10661066
return NewLineNoIndent().NewLine();
10671067
}
10681068

1069+
public SignatureWriter WriteMember(MemberInfo member)
1070+
=> member switch
1071+
{
1072+
MethodBase m => Member(m),
1073+
EventInfo m => Member(m),
1074+
PropertyInfo m => Member(m),
1075+
FieldInfo m => Member(m),
1076+
Type m => Member(m),
1077+
_ => throw new ArgumentOutOfRangeException(nameof(member)),
1078+
};
1079+
10691080
public SignatureWriter Member(Type type)
10701081
{
10711082
type = type.UnwrapConstruction();

src/ClassExplorer/Signatures/AllOfSignature.cs

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44

55
namespace ClassExplorer.Signatures
66
{
7-
internal sealed class AllOfTypeSignature : TypeSignature
7+
internal sealed class AllOfTypeSignature : UniversialSignature
88
{
9-
internal AllOfTypeSignature(ImmutableArray<ITypeSignature> elements)
9+
internal AllOfTypeSignature(ImmutableArray<ISignature> elements)
1010
{
1111
Poly.Assert(!elements.IsDefaultOrEmpty);
1212
Elements = elements;
1313
}
1414

15-
public ImmutableArray<ITypeSignature> Elements { get; }
15+
public ImmutableArray<ISignature> Elements { get; }
1616

1717
public override bool IsMatch(ParameterInfo parameter)
1818
{
@@ -39,23 +39,12 @@ public override bool IsMatch(Type type)
3939

4040
return true;
4141
}
42-
}
43-
44-
internal sealed class AllOfMemberSignature : MemberSignature
45-
{
46-
internal AllOfMemberSignature(ImmutableArray<IMemberSignature> elements)
47-
{
48-
Poly.Assert(!elements.IsDefaultOrEmpty);
49-
Elements = elements;
50-
}
51-
52-
public ImmutableArray<IMemberSignature> Elements { get; }
5342

54-
public override bool IsMatch(MemberInfo member)
43+
public override bool IsMatch(MemberInfo subject)
5544
{
5645
foreach (IMemberSignature signature in Elements)
5746
{
58-
if (!signature.IsMatch(member))
47+
if (!signature.IsMatch(subject))
5948
{
6049
return false;
6150
}

src/ClassExplorer/Signatures/AnyOfSignature.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44

55
namespace ClassExplorer.Signatures
66
{
7-
internal sealed class AnyOfSignature : TypeSignature
7+
internal sealed class AnyOfSignature : UniversialSignature
88
{
9-
internal AnyOfSignature(ImmutableArray<ITypeSignature> elements)
9+
internal AnyOfSignature(ImmutableArray<ISignature> elements)
1010
{
1111
Poly.Assert(!elements.IsDefaultOrEmpty);
1212
Elements = elements;
1313
}
1414

15-
internal ImmutableArray<ITypeSignature> Elements { get; }
15+
internal ImmutableArray<ISignature> Elements { get; }
1616

1717
public override bool IsMatch(ParameterInfo parameter)
1818
{
@@ -39,5 +39,18 @@ public override bool IsMatch(Type type)
3939

4040
return false;
4141
}
42+
43+
public override bool IsMatch(MemberInfo subject)
44+
{
45+
foreach (IMemberSignature signature in Elements)
46+
{
47+
if (signature.IsMatch(subject))
48+
{
49+
return true;
50+
}
51+
}
52+
53+
return false;
54+
}
4255
}
4356
}

0 commit comments

Comments
 (0)