Skip to content

Commit

Permalink
Add -Decoration to Find-Type (#44)
Browse files Browse the repository at this point in the history
Fixes #43

* Move the decoration parameter to be a shared parameter for both
`Find-Member` and `Find-Type`.

* Also fallback to a global search when resolving attribute type if the
  type is not in the BCL or embedded in the target assembly.
  • Loading branch information
SeeminglyScience authored May 3, 2022
1 parent dde4c34 commit 673958a
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 47 deletions.
36 changes: 18 additions & 18 deletions docs/en-US/Find-Member.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ Find properties, methods, fields, etc that fit specific criteria.

```powershell
Find-Member [[-FilterScript] <scriptblock>] [-ParameterType <ScriptBlockStringOrType>] [-GenericParameter <ScriptBlockStringOrType>] [-ParameterCount <RangeExpression[]>] [-GenericParameterCount <RangeExpression[]>] [-ReturnType <ScriptBlockStringOrType>] [-IncludeSpecialName] [-Decoration <ScriptBlockStringOrType>] [-MemberType <MemberTypes>] [-Static] [-Instance] [-Abstract] [-Virtual] [-Declared] [-IncludeObject] [-RecurseNestedType] [-Extension] [-Name <string>] [-Force] [-RegularExpression] [-InputObject <psobject>] [-Not] [-ResolutionMap <hashtable>] [-AccessView <AccessView>] [<CommonParameters>]
Find-Member [[-FilterScript] <scriptblock>] [-ParameterType <ScriptBlockStringOrType>] [-GenericParameter <ScriptBlockStringOrType>] [-ParameterCount <RangeExpression[]>] [-GenericParameterCount <RangeExpression[]>] [-ReturnType <ScriptBlockStringOrType>] [-IncludeSpecialName] [-MemberType <MemberTypes>] [-Static] [-Instance] [-Abstract] [-Virtual] [-Declared] [-IncludeObject] [-RecurseNestedType] [-Extension] [-Name <string>] [-Force] [-RegularExpression] [-InputObject <psobject>] [-Not] [-ResolutionMap <hashtable>] [-AccessView <AccessView>] [-Decoration <ScriptBlockStringOrType>] [<CommonParameters>]
```

### ByName

```powershell
Find-Member [[-Name] <string>] [-ParameterType <ScriptBlockStringOrType>] [-GenericParameter <ScriptBlockStringOrType>] [-ParameterCount <RangeExpression[]>] [-GenericParameterCount <RangeExpression[]>] [-ReturnType <ScriptBlockStringOrType>] [-IncludeSpecialName] [-Decoration <ScriptBlockStringOrType>] [-MemberType <MemberTypes>] [-Static] [-Instance] [-Abstract] [-Virtual] [-Declared] [-IncludeObject] [-RecurseNestedType] [-Extension] [-FilterScript <scriptblock>] [-Force] [-RegularExpression] [-InputObject <psobject>] [-Not] [-ResolutionMap <hashtable>] [-AccessView <AccessView>] [<CommonParameters>]
Find-Member [[-Name] <string>] [-ParameterType <ScriptBlockStringOrType>] [-GenericParameter <ScriptBlockStringOrType>] [-ParameterCount <RangeExpression[]>] [-GenericParameterCount <RangeExpression[]>] [-ReturnType <ScriptBlockStringOrType>] [-IncludeSpecialName] [-MemberType <MemberTypes>] [-Static] [-Instance] [-Abstract] [-Virtual] [-Declared] [-IncludeObject] [-RecurseNestedType] [-Extension] [-FilterScript <scriptblock>] [-Force] [-RegularExpression] [-InputObject <psobject>] [-Not] [-ResolutionMap <hashtable>] [-AccessView <AccessView>] [-Decoration <ScriptBlockStringOrType>] [<CommonParameters>]
```

## DESCRIPTION
Expand All @@ -36,7 +36,7 @@ The Find-Member cmdlet searches the process for type members that fit specified
```powershell
Find-Member GetPowerShell
# ReflectedType: ScriptBlock
# ReflectedType: System.Management.Automation.ScriptBlock
#
# Name MemberType Definition
# ---- ---------- ----------
Expand All @@ -54,7 +54,7 @@ Find all members in the AppDomain with the name "GetPowerShell"
```powershell
[System.IO.Stream] | Find-Member -ParameterType { [anyof[Span[any], Memory[any]]] }
# ReflectedType: Stream
# ReflectedType: System.IO.Stream
#
# Name MemberType Definition
# ---- ---------- ----------
Expand All @@ -69,19 +69,19 @@ Find all members that take a `Span<>` or a `Memory<>` as a parameter.
```powershell
Find-Member -ParameterCount 0 -GenericParameter { [T[new]] }
# ReflectedType: InlineParserList
# ReflectedType: Markdig.Parsers.InlineParserList
#
# Name MemberType Definition
# ---- ---------- ----------
# AddIfNotAlready Method public void AddIfNotAlready<TItem>();
#
# ReflectedType: ParserList<T, TState>
# ReflectedType: Markdig.Parsers.ParserList<T, TState>
#
# Name MemberType Definition
# ---- ---------- ----------
# AddIfNotAlready Method public void AddIfNotAlready<TItem>();
#
# ReflectedType: OrderedList<T>
# ReflectedType: Markdig.Parsers.OrderedList<T>
#
# Name MemberType Definition
# ---- ---------- ----------
Expand All @@ -95,13 +95,13 @@ Find all methods with no parameters and with a generic parameter with the `new`
```powershell
Find-Member Emit -ParameterCount ..1, 7..8, 10..
# ReflectedType: ILGenerator
# ReflectedType: System.Reflection.Emit.ILGenerator
#
# Name MemberType Definition
# ---- ---------- ----------
# Emit Method public virtual void Emit(OpCode opcode);
#
# ReflectedType: Compilation
# ReflectedType: Microsoft.CodeAnalysis.Compilation
#
# Name MemberType Definition
# ---- ---------- ----------
Expand All @@ -110,11 +110,11 @@ Find-Member Emit -ParameterCount ..1, 7..8, 10..
# Emit Method public EmitResult Emit(Stream peStream, St…
# Emit Method public EmitResult Emit(Stream peStream, St…
#
# ReflectedType: FileSystemExtensions
# ReflectedType: Microsoft.CodeAnalysis.FileSystemExtensions
#
# Name MemberType Definition
# ---- ---------- ----------
# Emit Method public static EmitResult Emit(Compilation
# Emit Method public static EmitResult Emit(this Compila
```

Find all methods named `Emit` whose parameter count is any of the following:
Expand All @@ -128,19 +128,19 @@ Find all methods named `Emit` whose parameter count is any of the following:
```powershell
Find-Member -ReturnType System.Management.Automation.Language.Ast -Static
# ReflectedType: CommandCompletion
# ReflectedType: System.Management.Automation.CommandCompletion
#
# Name MemberType Definition
# ---- ---------- ----------
# MapStringInputToPars… Method public static Tuple<Ast, Token[], IScriptPosition> MapStringI…
#
# ReflectedType: UsingExpressionAst
# ReflectedType: System.Management.Automation.Language.UsingExpressionAst
#
# Name MemberType Definition
# ---- ---------- ----------
# ExtractUsingVariable Method public static VariableExpressionAst ExtractUsingVariable(Usin…
#
# ReflectedType: Parser
# ReflectedType: System.Management.Automation.Language.Parser
#
# Name MemberType Definition
# ---- ---------- ----------
Expand All @@ -156,13 +156,13 @@ Find all static members in the AppDomain that return any type of AST.
```powershell
Find-Member -ParameterType runspace -Virtual
# ReflectedType: IHostSupportsInteractiveSession
# ReflectedType: System.Management.Automation.Host.IHostSupportsInteractiveSession
#
# Name MemberType Definition
# ---- ---------- ----------
# PushRunspace Method public abstract void PushRunspace(Runspace runspace);
#
# ReflectedType: IPSConsoleReadLineMockableMethods
# ReflectedType: Microsoft.PowerShell.Internal.IPSConsoleReadLineMockableMethods
#
# Name MemberType Definition
# ---- ---------- ----------
Expand All @@ -176,7 +176,7 @@ Find all virtual members in the AppDomain that take any runspace type as a param
```powershell
Find-Member Parse* -ParameterType System.Management.Automation.Language.Token
# ReflectedType: Parser
# ReflectedType: System.Management.Automation.Language.Parser
#
# Name MemberType Definition
# ---- ---------- ----------
Expand All @@ -193,7 +193,7 @@ demonstrates how this will even match the element of a type that is both an arra
```powershell
[runspace] | Find-Member -Force -Abstract | Find-Member -Not -AccessView Child
# ReflectedType: Runspace
# ReflectedType: System.Management.Automation.Runspaces.Runspace
#
# Name MemberType Definition
# ---- ---------- ----------
Expand Down
22 changes: 20 additions & 2 deletions docs/en-US/Find-Type.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ Find .NET classes in the AppDomain.
### ByFilter (Default)

```powershell
Find-Type [[-FilterScript] <scriptblock>] [[-Namespace] <string>] [-Name <string>] [-FullName <string>] [-InheritsType <ScriptBlockStringOrType>] [-ImplementsInterface <ScriptBlockStringOrType>] [-Signature <ScriptBlockStringOrType>] [-Abstract] [-Static] [-Sealed] [-Interface] [-ValueType] [-Force] [-RegularExpression] [-InputObject <psobject>] [-Not] [-ResolutionMap <hashtable>] [-AccessView <AccessView>] [<CommonParameters>]
Find-Type [[-FilterScript] <scriptblock>] [[-Namespace] <string>] [-Name <string>] [-FullName <string>] [-InheritsType <ScriptBlockStringOrType>] [-ImplementsInterface <ScriptBlockStringOrType>] [-Signature <ScriptBlockStringOrType>] [-Abstract] [-Static] [-Sealed] [-Interface] [-ValueType] [-Force] [-RegularExpression] [-InputObject <psobject>] [-Not] [-ResolutionMap <hashtable>] [-AccessView <AccessView>] [-Decoration <ScriptBlockStringOrType>] [<CommonParameters>]
```

### ByName

```powershell
Find-Type [[-Name] <string>] [[-Namespace] <string>] [-FullName <string>] [-InheritsType <ScriptBlockStringOrType>] [-ImplementsInterface <ScriptBlockStringOrType>] [-Signature <ScriptBlockStringOrType>] [-Abstract] [-Static] [-Sealed] [-Interface] [-ValueType] [-FilterScript <scriptblock>] [-Force] [-RegularExpression] [-InputObject <psobject>] [-Not] [-ResolutionMap <hashtable>] [-AccessView <AccessView>] [<CommonParameters>]
Find-Type [[-Name] <string>] [[-Namespace] <string>] [-FullName <string>] [-InheritsType <ScriptBlockStringOrType>] [-ImplementsInterface <ScriptBlockStringOrType>] [-Signature <ScriptBlockStringOrType>] [-Abstract] [-Static] [-Sealed] [-Interface] [-ValueType] [-FilterScript <scriptblock>] [-Force] [-RegularExpression] [-InputObject <psobject>] [-Not] [-ResolutionMap <hashtable>] [-AccessView <AccessView>] [-Decoration <ScriptBlockStringOrType>] [<CommonParameters>]
```

## DESCRIPTION
Expand Down Expand Up @@ -389,6 +389,24 @@ Accept pipeline input: False
Accept wildcard characters: False
```

### -Decoration

Specifies that a type must be decorated with this attribute for it to be included in results. This search will be done based on type name rather than strict type identity so it is safe to use for embedded attributes.

This can also be a type signature (see [about_Type_Signatures](https://seemingly.dev/about-type-signatures)).

```yaml
Type: Type
Parameter Sets: (All)
Aliases: HasAttr, attr
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```

### -ResolutionMap

Specifies a hashtable of `name` to `ScriptBlockStringOrType` to create your own keywords and/or override type resolution for any signature in this command.
Expand Down
10 changes: 1 addition & 9 deletions src/ClassExplorer/Commands/FindMemberCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,6 @@ public SwitchParameter IncludeSpecialName
set => _options.IncludeSpecialName = value;
}

[Parameter]
[Alias("HasAttr", "attr")]
[ArgumentCompleter(typeof(TypeFullNameArgumentCompleter))]
public ScriptBlockStringOrType? Decoration
{
get => _options.Decoration;
set => _options.Decoration = value;
}

/// <summary>
/// Gets or sets the member type to match.
/// </summary>
Expand Down Expand Up @@ -220,6 +211,7 @@ protected override void InitializeFilters()
_options.RegularExpression = RegularExpression;
_options.ResolutionMap = resolutionMap;
_options.AccessView = AccessView;
_options.Decoration = Decoration;
_search = Search.Members(_options, new PipelineEmitter<MemberInfo>(this));
}
}
Expand Down
21 changes: 6 additions & 15 deletions src/ClassExplorer/Commands/FindReflectionObjectCommandBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ public abstract class FindReflectionObjectCommandBase<TMemberType> : PSCmdlet
[Alias("as")]
public virtual AccessView AccessView { get; set; }

[Parameter]
[Alias("HasAttr", "attr")]
[ArgumentCompleter(typeof(TypeFullNameArgumentCompleter))]
public ScriptBlockStringOrType? Decoration { get; set; }

private bool _hadError;

/// <summary>
Expand Down Expand Up @@ -120,7 +125,7 @@ protected override void ProcessRecord()
// you probably aren't passing the entire AppDomain like this.
if (InputObject.BaseObject is IList list)
{
foreach (var item in list)
foreach (object? item in list)
{
ProcessSingleObject(PSObject.AsPSObject(item));
}
Expand Down Expand Up @@ -170,20 +175,6 @@ protected override void ProcessRecord()
continue;
}

// if (!LanguagePrimitives.TryConvertTo(entry.Value, out Type value))
// {
// WriteError(
// new ErrorRecord(
// new PSInvalidCastException(
// SR.Format(
// "Cannot convert the \"{0}\" value of type \"{1}\" to type \"{1}\".",
// entry.Value,
// entry.Value.GetType().FullName)),
// "InvalidTypeResolutionMap",
// ErrorCategory.InvalidArgument,
// entry));
// }

resolutionMap[key] = LanguagePrimitives.ConvertTo<ScriptBlockStringOrType>(entry.Value);
}

Expand Down
1 change: 1 addition & 0 deletions src/ClassExplorer/Commands/FindTypeCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ protected override void InitializeFilters()
_options.ResolutionMap = resolutionMap;
_options.Signature = signature;
_options.AccessView = AccessView;
_options.Decoration = Decoration;
_search = Search.Types(_options, new PipelineEmitter<Type>(this));
}
}
Expand Down
2 changes: 0 additions & 2 deletions src/ClassExplorer/MemberSearchOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ internal class MemberSearchOptions : ReflectionSearchOptions

public bool IncludeSpecialName { get; set; }

public ScriptBlockStringOrType? Decoration { get; set; }

public MemberTypes MemberType { get; set; }

public bool Static { get; set; }
Expand Down
2 changes: 2 additions & 0 deletions src/ClassExplorer/ReflectionSearchOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ internal abstract class ReflectionSearchOptions
public Dictionary<string, ScriptBlockStringOrType>? ResolutionMap { get; set; }

public AccessView AccessView { get; set; }

public ScriptBlockStringOrType? Decoration { get; set; }
}
40 changes: 39 additions & 1 deletion src/ClassExplorer/Signatures/DecorationSignature.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Reflection;

namespace ClassExplorer.Signatures
Expand All @@ -6,10 +7,29 @@ internal sealed class DecorationSignature : UniversialSignature
{
private readonly string _typeName;

private readonly Lazy<Type?> _fallbackType;

internal DecorationSignature(string typeName)
{
Poly.Assert(typeName is not null or { Length: < 1 });
_typeName = typeName;
_fallbackType = new Lazy<Type?>(() =>
{
Type? publicResult = Search.FirstType(
new TypeSearchOptions() { FullName = _typeName });

if (publicResult is not null)
{
return publicResult;
}

return Search.FirstType(
new TypeSearchOptions()
{
FullName = _typeName,
AccessView = AccessView.This,
});
});
}

public override bool IsMatch(ParameterInfo parameter)
Expand All @@ -19,9 +39,27 @@ public override bool IsMatch(ParameterInfo parameter)
return true;
}

if (_fallbackType.Value is Type type && parameter.IsDefined(type, inherit: true))
{
return true;
}

return IsMatch(parameter.ParameterType);
}

public override bool IsMatch(MemberInfo member) => member.IsDefined(_typeName);
public override bool IsMatch(MemberInfo member)
{
if (member.IsDefined(_typeName))
{
return true;
}

if (_fallbackType.Value is Type type && member.IsDefined(type, inherit: true))
{
return true;
}

return false;
}
}
}
10 changes: 10 additions & 0 deletions src/ClassExplorer/TypeSearch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,5 +136,15 @@ protected override void InitializeOtherFilters(List<Filter<Type>> filters, Signa
_options.Signature,
static (type, signature) => signature.IsMatch(type));
}

if (_options.Decoration is not null)
{
filters.AddFilter(
new DecorationSignature(
SignatureParser.ResolveAttributeTypeName(
_options.Decoration,
_options.ResolutionMap)),
static (type, signature) => signature.IsMatch(type));
}
}
}

0 comments on commit 673958a

Please sign in to comment.