Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/ClassExplorer/Commands/FindMemberCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,10 @@ public SwitchParameter Extension
set => _options.Extension = value;
}

[Parameter]
[Alias("sig")]
public ScriptBlockStringOrType? Signature { get; set; }

private MemberSearch<PipelineEmitter<MemberInfo>> _search = null!;

private protected override void OnNoInput()
Expand Down Expand Up @@ -212,6 +216,7 @@ protected override void InitializeFilters()
_options.ResolutionMap = resolutionMap;
_options.AccessView = AccessView;
_options.Decoration = Decoration;
_options.Signature = Signature;
_search = Search.Members(_options, new PipelineEmitter<MemberInfo>(this));
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/ClassExplorer/Commands/FindTypeCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,24 +153,24 @@ protected override void InitializeFilters()
Dictionary<string, ScriptBlockStringOrType>? resolutionMap = InitializeResolutionMap();

var parser = new SignatureParser(resolutionMap);
var signatures = ImmutableArray.CreateBuilder<ITypeSignature>();
ITypeSignature? signature = null;
var signatures = ImmutableArray.CreateBuilder<ISignature>();
ISignature? signature = null;
if (Signature is not null)
{
signature = Signature.Resolve(parser);
signature = Signature.ResolveType(parser);
signatures.Add(signature);
}

if (InheritsType is not null)
{
ITypeSignature inheritsTypeSignature = InheritsType.Resolve(parser, excludeSelf: true);
ISignature inheritsTypeSignature = InheritsType.ResolveType(parser, excludeSelf: true);
signatures.Add(inheritsTypeSignature);
signature ??= inheritsTypeSignature;
}

if (ImplementsInterface is not null)
{
ITypeSignature implementsSignature = ImplementsInterface.Resolve(parser, excludeSelf: true);
ISignature implementsSignature = ImplementsInterface.ResolveType(parser, excludeSelf: true);
signatures.Add(implementsSignature);
signature ??= implementsSignature;
}
Expand Down
13 changes: 10 additions & 3 deletions src/ClassExplorer/MemberSearch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -268,21 +268,21 @@ protected override void InitializeOtherFilters(List<Filter<MemberInfo>> filters,
if (_options.GenericParameter is not null)
{
filters.AddFilter(
new GenericParameterTypeSignature(_options.GenericParameter.Resolve(parser)),
new GenericParameterTypeSignature(_options.GenericParameter.ResolveType(parser)),
static (member, signature) => signature.IsMatch(member));
}

if (_options.ParameterType is not null)
{
filters.AddFilter(
new ParameterTypeSignature(_options.ParameterType.Resolve(parser)),
new ParameterTypeSignature(_options.ParameterType.ResolveType(parser)),
static (member, signature) => signature.IsMatch(member));
}

if (_options.ReturnType is not null)
{
filters.AddFilter(
new ReturnTypeSignature(_options.ReturnType.Resolve(parser)),
new ReturnTypeSignature(_options.ReturnType.ResolveType(parser)),
static (member, signature) => signature.IsMatch(member));
}

Expand All @@ -295,5 +295,12 @@ protected override void InitializeOtherFilters(List<Filter<MemberInfo>> filters,
_options.ResolutionMap)),
static (member, signature) => signature.IsMatch(member));
}

if (_options.Signature is not null)
{
filters.AddFilter(
(IMemberSignature)_options.Signature.Resolve(parser, SignatureKind.Member),
static (member, signature) => signature.IsMatch(member));
}
}
}
2 changes: 2 additions & 0 deletions src/ClassExplorer/MemberSearchOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,6 @@ internal class MemberSearchOptions : ReflectionSearchOptions
public bool RecurseNestedType { get; set; }

public bool Extension { get; set; }

public ScriptBlockStringOrType? Signature { get; set; }
}
67 changes: 67 additions & 0 deletions src/ClassExplorer/ReflectionCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System;
using System.Linq.Expressions;
using System.Management.Automation;
using System.Reflection;

namespace ClassExplorer;

internal static class ReflectionCache
{
private static readonly Type? ExecutionContextType;

private static readonly MethodInfo? GetExecutionContextFromTLSMethod;

private static readonly Func<string[]>? GetUsingNamespacesFunc;

static ReflectionCache()
{
ExecutionContextType = typeof(PSObject).Assembly.GetType("System.Management.Automation.ExecutionContext");
if (ExecutionContextType is null) return;

GetExecutionContextFromTLSMethod = typeof(PSObject).Assembly.GetType("System.Management.Automation.Runspaces.LocalPipeline")
?.GetMethod("GetExecutionContextFromTLS", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);

if (GetExecutionContextFromTLSMethod is null) return;

GetUsingNamespacesFunc = CreateGetUsingNamespaces();
}

public static string[] GetUsingNamespacesFromTLS()
{
return GetUsingNamespacesFunc?.Invoke() ?? ["System"];
}

private static Func<string[]>? CreateGetUsingNamespaces()
{
const BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;

if (GetExecutionContextFromTLSMethod is null) return null;

PropertyInfo? getEngineSessionState = ExecutionContextType?.GetProperty("EngineSessionState", flags);
if (getEngineSessionState is null) return null;

PropertyInfo? currentScope = getEngineSessionState.GetReturnType()?.GetProperty("CurrentScope", flags);
if (currentScope is null) return null;

PropertyInfo? typeRes = currentScope.GetReturnType()?.GetProperty("TypeResolutionState", flags);
if (typeRes is null) return null;

FieldInfo? namespaces = typeRes.GetReturnType()?.GetField("namespaces", flags);
if (namespaces is null) return null;
if (namespaces.FieldType != typeof(string[])) return null;

return Expression.Lambda<Func<string[]>>(
Expression.Field(
Expression.Call(
Expression.Call(
Expression.Call(
Expression.Call(GetExecutionContextFromTLSMethod),
getEngineSessionState.GetGetMethod(nonPublic: true)),
currentScope.GetGetMethod(nonPublic: true)),
typeRes.GetGetMethod(nonPublic: true)),
namespaces),
"GetUsingNamespacesDynamically",
[])
.Compile();
}
}
3 changes: 3 additions & 0 deletions src/ClassExplorer/SR.resx
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@
<data name="DecorationBadArgs" xml:space="preserve">
<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>
</data>
<data name="SigBadArgs" xml:space="preserve">
<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>
</data>
<data name="TypeNotFound" xml:space="preserve">
<value>Unable to find type [{0}].</value>
</data>
Expand Down
22 changes: 12 additions & 10 deletions src/ClassExplorer/ScriptBlockStringOrType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@
using System.Collections.Immutable;
using System.Management.Automation;
using System.Management.Automation.Language;
using System.Runtime.CompilerServices;
using ClassExplorer.Signatures;

namespace ClassExplorer
{
public sealed class ScriptBlockStringOrType
{
private ITypeSignature? _cachedType;
private ISignature? _cachedType;

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

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

internal object? Value { get; }

internal ITypeSignature Resolve(SignatureParser parser, bool isForMap = false, bool excludeSelf = false)
internal ISignature ResolveType(SignatureParser parser, bool isForMap = false, bool excludeSelf = false)
=> Resolve(parser, SignatureKind.Type, isForMap, excludeSelf);

internal ISignature Resolve(SignatureParser parser, SignatureKind expected, bool isForMap = false, bool excludeSelf = false)
{
if (_cachedType is not null)
{
Expand All @@ -30,7 +32,7 @@ internal ITypeSignature Resolve(SignatureParser parser, bool isForMap = false, b

if (Value is Type exactType)
{
static ITypeSignature SignatureForType(Type type, bool isForMap, bool excludeSelf)
static ISignature SignatureForType(Type type, SignatureKind expected, bool isForMap, bool excludeSelf)
{
if (isForMap)
{
Expand All @@ -40,20 +42,20 @@ static ITypeSignature SignatureForType(Type type, bool isForMap, bool excludeSel
if (excludeSelf)
{
return new AllOfTypeSignature(
ImmutableArray.Create<ITypeSignature>(
ImmutableArray.Create<ISignature>(
new AssignableTypeSignature(type),
new NotTypeSignature(new ExactTypeSignature(type))));
}

return new ContainsSignature(new AssignableTypeSignature(type));
}

return _cachedType = SignatureForType(exactType, isForMap, excludeSelf);
return _cachedType = SignatureForType(exactType, expected, isForMap, excludeSelf);
}

if (Value is ScriptBlock scriptBlock)
{
return _cachedType = parser.Parse(scriptBlock);
return _cachedType = parser.Parse(scriptBlock, expected);
}

if (Value is string typeName)
Expand All @@ -75,7 +77,7 @@ static ScriptBlockAst GetAstForString(string typeName)
}

ScriptBlockAst ast = GetAstForString(typeName);
ITypeSignature signature = parser.Parse(ast);
ISignature signature = parser.Parse(ast, expected);
if (isForMap)
{
return _cachedType = signature;
Expand All @@ -87,7 +89,7 @@ static ScriptBlockAst GetAstForString(string typeName)
{
return _cachedType =
new AllOfTypeSignature(
ImmutableArray.Create<ITypeSignature>(
ImmutableArray.Create<ISignature>(
assignable,
new NotTypeSignature(new ExactTypeSignature(assignable.Type))));
}
Expand All @@ -98,7 +100,7 @@ static ScriptBlockAst GetAstForString(string typeName)
return _cachedType = new ContainsSignature(signature);
}

return Unreachable.Code<ITypeSignature>();
return Unreachable.Code<ISignature>();
}
}
}
11 changes: 11 additions & 0 deletions src/ClassExplorer/SignatureWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,17 @@ public SignatureWriter MaybeNewLine(ref bool isFirst)
return NewLineNoIndent().NewLine();
}

public SignatureWriter WriteMember(MemberInfo member)
=> member switch
{
MethodBase m => Member(m),
EventInfo m => Member(m),
PropertyInfo m => Member(m),
FieldInfo m => Member(m),
Type m => Member(m),
_ => throw new ArgumentOutOfRangeException(nameof(member)),
};

public SignatureWriter Member(Type type)
{
type = type.UnwrapConstruction();
Expand Down
21 changes: 5 additions & 16 deletions src/ClassExplorer/Signatures/AllOfSignature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

namespace ClassExplorer.Signatures
{
internal sealed class AllOfTypeSignature : TypeSignature
internal sealed class AllOfTypeSignature : UniversialSignature
{
internal AllOfTypeSignature(ImmutableArray<ITypeSignature> elements)
internal AllOfTypeSignature(ImmutableArray<ISignature> elements)
{
Poly.Assert(!elements.IsDefaultOrEmpty);
Elements = elements;
}

public ImmutableArray<ITypeSignature> Elements { get; }
public ImmutableArray<ISignature> Elements { get; }

public override bool IsMatch(ParameterInfo parameter)
{
Expand All @@ -39,23 +39,12 @@ public override bool IsMatch(Type type)

return true;
}
}

internal sealed class AllOfMemberSignature : MemberSignature
{
internal AllOfMemberSignature(ImmutableArray<IMemberSignature> elements)
{
Poly.Assert(!elements.IsDefaultOrEmpty);
Elements = elements;
}

public ImmutableArray<IMemberSignature> Elements { get; }

public override bool IsMatch(MemberInfo member)
public override bool IsMatch(MemberInfo subject)
{
foreach (IMemberSignature signature in Elements)
{
if (!signature.IsMatch(member))
if (!signature.IsMatch(subject))
{
return false;
}
Expand Down
19 changes: 16 additions & 3 deletions src/ClassExplorer/Signatures/AnyOfSignature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

namespace ClassExplorer.Signatures
{
internal sealed class AnyOfSignature : TypeSignature
internal sealed class AnyOfSignature : UniversialSignature
{
internal AnyOfSignature(ImmutableArray<ITypeSignature> elements)
internal AnyOfSignature(ImmutableArray<ISignature> elements)
{
Poly.Assert(!elements.IsDefaultOrEmpty);
Elements = elements;
}

internal ImmutableArray<ITypeSignature> Elements { get; }
internal ImmutableArray<ISignature> Elements { get; }

public override bool IsMatch(ParameterInfo parameter)
{
Expand All @@ -39,5 +39,18 @@ public override bool IsMatch(Type type)

return false;
}

public override bool IsMatch(MemberInfo subject)
{
foreach (IMemberSignature signature in Elements)
{
if (signature.IsMatch(subject))
{
return true;
}
}

return false;
}
}
}
Loading
Loading