mirror of https://github.com/icsharpcode/ILSpy.git
11 changed files with 600 additions and 569 deletions
@ -0,0 +1,215 @@
@@ -0,0 +1,215 @@
|
||||
using System; |
||||
using ICSharpCode.Decompiler.Util; |
||||
using ICSharpCode.Decompiler.Disassembler; |
||||
using SRM = System.Reflection.Metadata; |
||||
using ILOpCode = System.Reflection.Metadata.ILOpCode; |
||||
using ICSharpCode.Decompiler; |
||||
|
||||
using static System.Reflection.Metadata.PEReaderExtensions; |
||||
using ICSharpCode.Decompiler.TypeSystem; |
||||
|
||||
namespace ICSharpCode.ILSpy |
||||
{ |
||||
class LiteralSearchStrategy : AbstractSearchStrategy |
||||
{ |
||||
readonly TypeCode searchTermLiteralType; |
||||
readonly object searchTermLiteralValue; |
||||
|
||||
public LiteralSearchStrategy(params string[] terms) |
||||
: base(terms) |
||||
{ |
||||
if (searchTerm.Length == 1) { |
||||
var lexer = new Lexer(new LATextReader(new System.IO.StringReader(searchTerm[0]))); |
||||
var value = lexer.NextToken(); |
||||
|
||||
if (value != null && value.LiteralValue != null) { |
||||
TypeCode valueType = Type.GetTypeCode(value.LiteralValue.GetType()); |
||||
switch (valueType) { |
||||
case TypeCode.Byte: |
||||
case TypeCode.SByte: |
||||
case TypeCode.Int16: |
||||
case TypeCode.UInt16: |
||||
case TypeCode.Int32: |
||||
case TypeCode.UInt32: |
||||
case TypeCode.Int64: |
||||
case TypeCode.UInt64: |
||||
searchTermLiteralType = TypeCode.Int64; |
||||
searchTermLiteralValue = CSharpPrimitiveCast.Cast(TypeCode.Int64, value.LiteralValue, false); |
||||
break; |
||||
case TypeCode.Single: |
||||
case TypeCode.Double: |
||||
case TypeCode.String: |
||||
searchTermLiteralType = valueType; |
||||
searchTermLiteralValue = value.LiteralValue; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
protected override bool IsMatch(IField field, Language language) |
||||
{ |
||||
return IsLiteralMatch(field.ConstantValue); |
||||
} |
||||
|
||||
protected override bool IsMatch(IProperty property, Language language) |
||||
{ |
||||
return MethodIsLiteralMatch(property.Getter) || MethodIsLiteralMatch(property.Setter); |
||||
} |
||||
|
||||
protected override bool IsMatch(IEvent ev, Language language) |
||||
{ |
||||
return MethodIsLiteralMatch(ev.AddAccessor) || MethodIsLiteralMatch(ev.RemoveAccessor) || MethodIsLiteralMatch(ev.InvokeAccessor); |
||||
} |
||||
|
||||
protected override bool IsMatch(IMethod m, Language language) |
||||
{ |
||||
return MethodIsLiteralMatch(m); |
||||
} |
||||
|
||||
bool IsLiteralMatch(object val) |
||||
{ |
||||
if (val == null) |
||||
return false; |
||||
switch (searchTermLiteralType) { |
||||
case TypeCode.Int64: |
||||
TypeCode tc = Type.GetTypeCode(val.GetType()); |
||||
if (tc >= TypeCode.SByte && tc <= TypeCode.UInt64) |
||||
return CSharpPrimitiveCast.Cast(TypeCode.Int64, val, false).Equals(searchTermLiteralValue); |
||||
else |
||||
return false; |
||||
case TypeCode.Single: |
||||
case TypeCode.Double: |
||||
case TypeCode.String: |
||||
return searchTermLiteralValue.Equals(val); |
||||
default: |
||||
// substring search with searchTerm
|
||||
return IsMatch(t => val.ToString()); |
||||
} |
||||
} |
||||
|
||||
bool MethodIsLiteralMatch(IMethod method) |
||||
{ |
||||
if (method == null) |
||||
return false; |
||||
var module = ((MetadataModule)method.ParentModule).PEFile; |
||||
var m = (SRM.MethodDefinitionHandle)method.MetadataToken; |
||||
if (m.IsNil) |
||||
return false; |
||||
var methodDefinition = module.Metadata.GetMethodDefinition(m); |
||||
if (!methodDefinition.HasBody()) |
||||
return false; |
||||
var blob = module.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress).GetILReader(); |
||||
if (searchTermLiteralType == TypeCode.Int64) { |
||||
long val = (long)searchTermLiteralValue; |
||||
while (blob.RemainingBytes > 0) { |
||||
ILOpCode code; |
||||
switch (code = ILParser.DecodeOpCode(ref blob)) { |
||||
case ILOpCode.Ldc_i8: |
||||
if (val == blob.ReadInt64()) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4: |
||||
if (val == blob.ReadInt32()) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_s: |
||||
if (val == blob.ReadSByte()) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_m1: |
||||
if (val == -1) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_0: |
||||
if (val == 0) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_1: |
||||
if (val == 1) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_2: |
||||
if (val == 2) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_3: |
||||
if (val == 3) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_4: |
||||
if (val == 4) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_5: |
||||
if (val == 5) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_6: |
||||
if (val == 6) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_7: |
||||
if (val == 7) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_8: |
||||
if (val == 8) |
||||
return true; |
||||
break; |
||||
default: |
||||
ILParser.SkipOperand(ref blob, code); |
||||
break; |
||||
} |
||||
} |
||||
} else if (searchTermLiteralType != TypeCode.Empty) { |
||||
ILOpCode expectedCode; |
||||
switch (searchTermLiteralType) { |
||||
case TypeCode.Single: |
||||
expectedCode = ILOpCode.Ldc_r4; |
||||
break; |
||||
case TypeCode.Double: |
||||
expectedCode = ILOpCode.Ldc_r8; |
||||
break; |
||||
case TypeCode.String: |
||||
expectedCode = ILOpCode.Ldstr; |
||||
break; |
||||
default: |
||||
throw new InvalidOperationException(); |
||||
} |
||||
while (blob.RemainingBytes > 0) { |
||||
var code = ILParser.DecodeOpCode(ref blob); |
||||
if (code != expectedCode) { |
||||
ILParser.SkipOperand(ref blob, code); |
||||
continue; |
||||
} |
||||
switch (code) { |
||||
case ILOpCode.Ldc_r4: |
||||
if ((float)searchTermLiteralValue == blob.ReadSingle()) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_r8: |
||||
if ((double)searchTermLiteralValue == blob.ReadDouble()) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldstr: |
||||
if ((string)searchTermLiteralValue == ILParser.DecodeUserString(ref blob, module.Metadata)) |
||||
return true; |
||||
break; |
||||
} |
||||
} |
||||
} else { |
||||
while (blob.RemainingBytes > 0) { |
||||
var code = ILParser.DecodeOpCode(ref blob); |
||||
if (code != ILOpCode.Ldstr) { |
||||
ILParser.SkipOperand(ref blob, code); |
||||
continue; |
||||
} |
||||
if (base.IsMatch(t => ILParser.DecodeUserString(ref blob, module.Metadata))) |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
using ICSharpCode.Decompiler.TypeSystem; |
||||
|
||||
namespace ICSharpCode.ILSpy |
||||
{ |
||||
class MemberSearchStrategy : AbstractSearchStrategy |
||||
{ |
||||
MemberSearchKind searchKind; |
||||
|
||||
public MemberSearchStrategy(string term, MemberSearchKind searchKind = MemberSearchKind.All) |
||||
: this(new[] { term }, searchKind) |
||||
{ |
||||
} |
||||
|
||||
public MemberSearchStrategy(string[] terms, MemberSearchKind searchKind = MemberSearchKind.All) |
||||
: base(terms) |
||||
{ |
||||
this.searchKind = searchKind; |
||||
} |
||||
|
||||
protected override bool IsMatch(IField field, Language language) |
||||
{ |
||||
return (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Field) && MatchName(field, language); |
||||
} |
||||
|
||||
protected override bool IsMatch(IProperty property, Language language) |
||||
{ |
||||
return (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Property) && MatchName(property, language); |
||||
} |
||||
|
||||
protected override bool IsMatch(IEvent ev, Language language) |
||||
{ |
||||
return (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Event) && MatchName(ev, language); |
||||
} |
||||
|
||||
protected override bool IsMatch(IMethod m, Language language) |
||||
{ |
||||
return (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Method) && MatchName(m, language); |
||||
} |
||||
} |
||||
|
||||
enum MemberSearchKind |
||||
{ |
||||
All, |
||||
Field, |
||||
Property, |
||||
Event, |
||||
Method |
||||
} |
||||
} |
||||
@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
|
||||
using System.Globalization; |
||||
using SRM = System.Reflection.Metadata; |
||||
using ICSharpCode.Decompiler.TypeSystem; |
||||
|
||||
namespace ICSharpCode.ILSpy |
||||
{ |
||||
class MetadataTokenSearchStrategy : TypeAndMemberSearchStrategy |
||||
{ |
||||
readonly int searchTermToken; |
||||
|
||||
public MetadataTokenSearchStrategy(params string[] terms) |
||||
: base(terms) |
||||
{ |
||||
if (searchTerm.Length == 1) { |
||||
int.TryParse(searchTerm[0], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out searchTermToken); |
||||
} |
||||
} |
||||
|
||||
protected override bool MatchName(IEntity m, Language language) |
||||
{ |
||||
return SRM.Ecma335.MetadataTokens.GetToken(m.MetadataToken) == searchTermToken; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,215 @@
@@ -0,0 +1,215 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Text.RegularExpressions; |
||||
using System.Windows.Media; |
||||
using ICSharpCode.ILSpy.TreeNodes; |
||||
using ICSharpCode.Decompiler.Metadata; |
||||
using System.Reflection; |
||||
using ICSharpCode.Decompiler.TypeSystem; |
||||
|
||||
namespace ICSharpCode.ILSpy |
||||
{ |
||||
abstract class AbstractSearchStrategy |
||||
{ |
||||
protected string[] searchTerm; |
||||
protected Regex regex; |
||||
protected bool fullNameSearch; |
||||
|
||||
protected AbstractSearchStrategy(params string[] terms) |
||||
{ |
||||
if (terms.Length == 1 && terms[0].Length > 2) { |
||||
var search = terms[0]; |
||||
if (search.StartsWith("/", StringComparison.Ordinal) && search.Length > 4) { |
||||
var regexString = search.Substring(1, search.Length - 1); |
||||
fullNameSearch = search.Contains("\\."); |
||||
if (regexString.EndsWith("/", StringComparison.Ordinal)) |
||||
regexString = regexString.Substring(0, regexString.Length - 1); |
||||
regex = SafeNewRegex(regexString); |
||||
} else { |
||||
fullNameSearch = search.Contains("."); |
||||
} |
||||
|
||||
terms[0] = search; |
||||
} |
||||
|
||||
searchTerm = terms; |
||||
} |
||||
|
||||
protected float CalculateFitness(IEntity member) |
||||
{ |
||||
string text = member.Name; |
||||
|
||||
// Probably compiler generated types without meaningful names, show them last
|
||||
if (text.StartsWith("<")) { |
||||
return 0; |
||||
} |
||||
|
||||
// Constructors always have the same name in IL:
|
||||
// Use type name instead
|
||||
if (text == ".cctor" || text == ".ctor") { |
||||
text = member.DeclaringType.Name; |
||||
} |
||||
|
||||
// Ignore generic arguments, it not possible to search based on them either
|
||||
text = ReflectionHelper.SplitTypeParameterCountFromReflectionName(text); |
||||
|
||||
return 1.0f / text.Length; |
||||
} |
||||
|
||||
protected virtual bool IsMatch(IField field, Language language) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
protected virtual bool IsMatch(IProperty property, Language language) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
protected virtual bool IsMatch(IEvent ev, Language language) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
protected virtual bool IsMatch(IMethod m, Language language) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
protected virtual bool MatchName(IEntity m, Language language) |
||||
{ |
||||
return IsMatch(t => GetLanguageSpecificName(language, m, regex != null ? fullNameSearch : t.Contains("."))); |
||||
} |
||||
|
||||
protected virtual bool IsMatch(Func<string, string> getText) |
||||
{ |
||||
if (regex != null) { |
||||
return regex.IsMatch(getText("")); |
||||
} |
||||
|
||||
for (int i = 0; i < searchTerm.Length; ++i) { |
||||
// How to handle overlapping matches?
|
||||
var term = searchTerm[i]; |
||||
if (string.IsNullOrEmpty(term)) continue; |
||||
string text = getText(term); |
||||
switch (term[0]) { |
||||
case '+': // must contain
|
||||
term = term.Substring(1); |
||||
goto default; |
||||
case '-': // should not contain
|
||||
if (term.Length > 1 && text.IndexOf(term.Substring(1), StringComparison.OrdinalIgnoreCase) >= 0) |
||||
return false; |
||||
break; |
||||
case '=': // exact match
|
||||
{ |
||||
var equalCompareLength = text.IndexOf('`'); |
||||
if (equalCompareLength == -1) |
||||
equalCompareLength = text.Length; |
||||
|
||||
if (term.Length > 1 && String.Compare(term, 1, text, 0, Math.Max(term.Length, equalCompareLength), StringComparison.OrdinalIgnoreCase) != 0) |
||||
return false; |
||||
} |
||||
break; |
||||
case '~': |
||||
if (term.Length > 1 && !IsNoncontiguousMatch(text.ToLower(), term.Substring(1).ToLower())) |
||||
return false; |
||||
break; |
||||
default: |
||||
if (text.IndexOf(term, StringComparison.OrdinalIgnoreCase) < 0) |
||||
return false; |
||||
break; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
bool IsNoncontiguousMatch(string text, string searchTerm) |
||||
{ |
||||
if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(searchTerm)) { |
||||
return false; |
||||
} |
||||
var textLength = text.Length; |
||||
if (searchTerm.Length > textLength) { |
||||
return false; |
||||
} |
||||
var i = 0; |
||||
for (int searchIndex = 0; searchIndex < searchTerm.Length;) { |
||||
while (i != textLength) { |
||||
if (text[i] == searchTerm[searchIndex]) { |
||||
// Check if all characters in searchTerm have been matched
|
||||
if (searchTerm.Length == ++searchIndex) |
||||
return true; |
||||
i++; |
||||
break; |
||||
} |
||||
i++; |
||||
} |
||||
if (i == textLength) |
||||
return false; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
string GetLanguageSpecificName(Language language, IEntity member, bool fullName = false) |
||||
{ |
||||
switch (member) { |
||||
case ITypeDefinition t: |
||||
return language.TypeToString(t, includeNamespace: fullName); |
||||
case IField f: |
||||
return language.FieldToString(f, fullName, fullName); |
||||
case IProperty p: |
||||
return language.PropertyToString(p, fullName, fullName); |
||||
case IMethod m: |
||||
return language.MethodToString(m, fullName, fullName); |
||||
case IEvent e: |
||||
return language.EventToString(e, fullName, fullName); |
||||
default: |
||||
throw new NotSupportedException(member?.GetType() + " not supported!"); |
||||
} |
||||
} |
||||
|
||||
void Add<T>(Func<IEnumerable<T>> itemsGetter, ITypeDefinition type, Language language, Action<SearchResult> addResult, Func<T, Language, bool> matcher, Func<T, ImageSource> image) where T : IEntity |
||||
{ |
||||
IEnumerable<T> items = Enumerable.Empty<T>(); |
||||
try { |
||||
items = itemsGetter(); |
||||
} catch (Exception ex) { |
||||
System.Diagnostics.Debug.Print(ex.ToString()); |
||||
} |
||||
foreach (var item in items) { |
||||
if (matcher(item, language)) { |
||||
addResult(new SearchResult { |
||||
Member = item, |
||||
Fitness = CalculateFitness(item), |
||||
Image = image(item), |
||||
Name = GetLanguageSpecificName(language, item), |
||||
LocationImage = TypeTreeNode.GetIcon(type), |
||||
Location = language.TypeToString(type, includeNamespace: true) |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public virtual void Search(ITypeDefinition type, Language language, Action<SearchResult> addResult) |
||||
{ |
||||
Add(() => type.Fields, type, language, addResult, IsMatch, FieldTreeNode.GetIcon); |
||||
Add(() => type.Properties, type, language, addResult, IsMatch, p => PropertyTreeNode.GetIcon(p)); |
||||
Add(() => type.Events, type, language, addResult, IsMatch, EventTreeNode.GetIcon); |
||||
Add(() => type.Methods.Where(m => !m.IsAccessor), type, language, addResult, IsMatch, MethodTreeNode.GetIcon); |
||||
|
||||
foreach (var nestedType in type.NestedTypes) { |
||||
Search(nestedType, language, addResult); |
||||
} |
||||
} |
||||
|
||||
Regex SafeNewRegex(string unsafePattern) |
||||
{ |
||||
try { |
||||
return new Regex(unsafePattern, RegexOptions.Compiled); |
||||
} catch (ArgumentException) { |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
using System; |
||||
using ICSharpCode.ILSpy.TreeNodes; |
||||
using ICSharpCode.Decompiler.TypeSystem; |
||||
|
||||
namespace ICSharpCode.ILSpy |
||||
{ |
||||
class TypeAndMemberSearchStrategy : AbstractSearchStrategy |
||||
{ |
||||
public TypeAndMemberSearchStrategy(params string[] terms) |
||||
: base(terms) |
||||
{ |
||||
} |
||||
|
||||
public override void Search(ITypeDefinition type, Language language, Action<SearchResult> addResult) |
||||
{ |
||||
if (MatchName(type, language)) |
||||
{ |
||||
string name = language.TypeToString(type, includeNamespace: false); |
||||
var declaringType = type.DeclaringTypeDefinition; |
||||
addResult(new SearchResult { |
||||
Member = type, |
||||
Image = TypeTreeNode.GetIcon(type), |
||||
Fitness = CalculateFitness(type), |
||||
Name = name, |
||||
LocationImage = declaringType != null ? TypeTreeNode.GetIcon(declaringType) : Images.Namespace, |
||||
Location = declaringType != null ? language.TypeToString(declaringType, includeNamespace: true) : type.Namespace |
||||
}); |
||||
} |
||||
|
||||
base.Search(type, language, addResult); |
||||
} |
||||
|
||||
protected override bool IsMatch(IField field, Language language) |
||||
{ |
||||
return MatchName(field, language); |
||||
} |
||||
|
||||
protected override bool IsMatch(IProperty property, Language language) |
||||
{ |
||||
return MatchName(property, language); |
||||
} |
||||
|
||||
protected override bool IsMatch(IEvent ev, Language language) |
||||
{ |
||||
return MatchName(ev, language); |
||||
} |
||||
|
||||
protected override bool IsMatch(IMethod m, Language language) |
||||
{ |
||||
return MatchName(m, language); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
using System; |
||||
using ICSharpCode.ILSpy.TreeNodes; |
||||
using ICSharpCode.Decompiler.TypeSystem; |
||||
|
||||
namespace ICSharpCode.ILSpy |
||||
{ |
||||
class TypeSearchStrategy : AbstractSearchStrategy |
||||
{ |
||||
public TypeSearchStrategy(params string[] terms) |
||||
: base(terms) |
||||
{ |
||||
} |
||||
|
||||
public override void Search(ITypeDefinition type, Language language, Action<SearchResult> addResult) |
||||
{ |
||||
if (MatchName(type, language)) { |
||||
string name = language.TypeToString(type, includeNamespace: false); |
||||
var declaringType = type.DeclaringTypeDefinition; |
||||
addResult(new SearchResult { |
||||
Member = type, |
||||
Fitness = CalculateFitness(type), |
||||
Image = TypeTreeNode.GetIcon(type), |
||||
Name = name, |
||||
LocationImage = declaringType != null ? TypeTreeNode.GetIcon(declaringType) : Images.Namespace, |
||||
Location = declaringType != null ? language.TypeToString(declaringType, includeNamespace: true) : type.Namespace |
||||
}); |
||||
} |
||||
|
||||
foreach (var nestedType in type.NestedTypes) { |
||||
Search(nestedType, language, addResult); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -1,564 +0,0 @@
@@ -1,564 +0,0 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Globalization; |
||||
using System.Linq; |
||||
using System.Text.RegularExpressions; |
||||
using System.Windows.Media; |
||||
using ICSharpCode.Decompiler.Util; |
||||
using ICSharpCode.ILSpy.TreeNodes; |
||||
using ICSharpCode.Decompiler.Metadata; |
||||
using System.Reflection; |
||||
using ICSharpCode.Decompiler.Disassembler; |
||||
using SRM = System.Reflection.Metadata; |
||||
using ILOpCode = System.Reflection.Metadata.ILOpCode; |
||||
using ICSharpCode.Decompiler; |
||||
|
||||
using static System.Reflection.Metadata.PEReaderExtensions; |
||||
using ICSharpCode.Decompiler.TypeSystem; |
||||
|
||||
namespace ICSharpCode.ILSpy |
||||
{ |
||||
abstract class AbstractSearchStrategy |
||||
{ |
||||
protected string[] searchTerm; |
||||
protected Regex regex; |
||||
protected bool fullNameSearch; |
||||
|
||||
protected AbstractSearchStrategy(params string[] terms) |
||||
{ |
||||
if (terms.Length == 1 && terms[0].Length > 2) { |
||||
var search = terms[0]; |
||||
if (search.StartsWith("/", StringComparison.Ordinal) && search.Length > 4) { |
||||
var regexString = search.Substring(1, search.Length - 1); |
||||
fullNameSearch = search.Contains("\\."); |
||||
if (regexString.EndsWith("/", StringComparison.Ordinal)) |
||||
regexString = regexString.Substring(0, regexString.Length - 1); |
||||
regex = SafeNewRegex(regexString); |
||||
} else { |
||||
fullNameSearch = search.Contains("."); |
||||
} |
||||
|
||||
terms[0] = search; |
||||
} |
||||
|
||||
searchTerm = terms; |
||||
} |
||||
|
||||
protected float CalculateFitness(IEntity member) |
||||
{ |
||||
string text = member.Name; |
||||
|
||||
// Probably compiler generated types without meaningful names, show them last
|
||||
if (text.StartsWith("<")) { |
||||
return 0; |
||||
} |
||||
|
||||
// Constructors always have the same name in IL:
|
||||
// Use type name instead
|
||||
if (text == ".cctor" || text == ".ctor") { |
||||
text = member.DeclaringType.Name; |
||||
} |
||||
|
||||
// Ignore generic arguments, it not possible to search based on them either
|
||||
text = ReflectionHelper.SplitTypeParameterCountFromReflectionName(text); |
||||
|
||||
return 1.0f / text.Length; |
||||
} |
||||
|
||||
protected virtual bool IsMatch(IField field, Language language) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
protected virtual bool IsMatch(IProperty property, Language language) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
protected virtual bool IsMatch(IEvent ev, Language language) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
protected virtual bool IsMatch(IMethod m, Language language) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
protected virtual bool MatchName(IEntity m, Language language) |
||||
{ |
||||
return IsMatch(t => GetLanguageSpecificName(language, m, regex != null ? fullNameSearch : t.Contains("."))); |
||||
} |
||||
|
||||
protected virtual bool IsMatch(Func<string, string> getText) |
||||
{ |
||||
if (regex != null) { |
||||
return regex.IsMatch(getText("")); |
||||
} |
||||
|
||||
for (int i = 0; i < searchTerm.Length; ++i) { |
||||
// How to handle overlapping matches?
|
||||
var term = searchTerm[i]; |
||||
if (string.IsNullOrEmpty(term)) continue; |
||||
string text = getText(term); |
||||
switch (term[0]) { |
||||
case '+': // must contain
|
||||
term = term.Substring(1); |
||||
goto default; |
||||
case '-': // should not contain
|
||||
if (term.Length > 1 && text.IndexOf(term.Substring(1), StringComparison.OrdinalIgnoreCase) >= 0) |
||||
return false; |
||||
break; |
||||
case '=': // exact match
|
||||
{ |
||||
var equalCompareLength = text.IndexOf('`'); |
||||
if (equalCompareLength == -1) |
||||
equalCompareLength = text.Length; |
||||
|
||||
if (term.Length > 1 && String.Compare(term, 1, text, 0, Math.Max(term.Length, equalCompareLength), StringComparison.OrdinalIgnoreCase) != 0) |
||||
return false; |
||||
} |
||||
break; |
||||
case '~': |
||||
if (term.Length > 1 && !IsNoncontiguousMatch(text.ToLower(), term.Substring(1).ToLower())) |
||||
return false; |
||||
break; |
||||
default: |
||||
if (text.IndexOf(term, StringComparison.OrdinalIgnoreCase) < 0) |
||||
return false; |
||||
break; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
bool IsNoncontiguousMatch(string text, string searchTerm) |
||||
{ |
||||
if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(searchTerm)) { |
||||
return false; |
||||
} |
||||
var textLength = text.Length; |
||||
if (searchTerm.Length > textLength) { |
||||
return false; |
||||
} |
||||
var i = 0; |
||||
for (int searchIndex = 0; searchIndex < searchTerm.Length;) { |
||||
while (i != textLength) { |
||||
if (text[i] == searchTerm[searchIndex]) { |
||||
// Check if all characters in searchTerm have been matched
|
||||
if (searchTerm.Length == ++searchIndex) |
||||
return true; |
||||
i++; |
||||
break; |
||||
} |
||||
i++; |
||||
} |
||||
if (i == textLength) |
||||
return false; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
string GetLanguageSpecificName(Language language, IEntity member, bool fullName = false) |
||||
{ |
||||
switch (member) { |
||||
case ITypeDefinition t: |
||||
return language.TypeToString(t, includeNamespace: fullName); |
||||
case IField f: |
||||
return language.FieldToString(f, fullName, fullName); |
||||
case IProperty p: |
||||
return language.PropertyToString(p, fullName, fullName); |
||||
case IMethod m: |
||||
return language.MethodToString(m, fullName, fullName); |
||||
case IEvent e: |
||||
return language.EventToString(e, fullName, fullName); |
||||
default: |
||||
throw new NotSupportedException(member?.GetType() + " not supported!"); |
||||
} |
||||
} |
||||
|
||||
void Add<T>(Func<IEnumerable<T>> itemsGetter, ITypeDefinition type, Language language, Action<SearchResult> addResult, Func<T, Language, bool> matcher, Func<T, ImageSource> image) where T : IEntity |
||||
{ |
||||
IEnumerable<T> items = Enumerable.Empty<T>(); |
||||
try { |
||||
items = itemsGetter(); |
||||
} catch (Exception ex) { |
||||
System.Diagnostics.Debug.Print(ex.ToString()); |
||||
} |
||||
foreach (var item in items) { |
||||
if (matcher(item, language)) { |
||||
addResult(new SearchResult { |
||||
Member = item, |
||||
Fitness = CalculateFitness(item), |
||||
Image = image(item), |
||||
Name = GetLanguageSpecificName(language, item), |
||||
LocationImage = TypeTreeNode.GetIcon(type), |
||||
Location = language.TypeToString(type, includeNamespace: true) |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public virtual void Search(ITypeDefinition type, Language language, Action<SearchResult> addResult) |
||||
{ |
||||
Add(() => type.Fields, type, language, addResult, IsMatch, FieldTreeNode.GetIcon); |
||||
Add(() => type.Properties, type, language, addResult, IsMatch, p => PropertyTreeNode.GetIcon(p)); |
||||
Add(() => type.Events, type, language, addResult, IsMatch, EventTreeNode.GetIcon); |
||||
Add(() => type.Methods.Where(m => !m.IsAccessor), type, language, addResult, IsMatch, MethodTreeNode.GetIcon); |
||||
|
||||
foreach (var nestedType in type.NestedTypes) { |
||||
Search(nestedType, language, addResult); |
||||
} |
||||
} |
||||
|
||||
Regex SafeNewRegex(string unsafePattern) |
||||
{ |
||||
try { |
||||
return new Regex(unsafePattern, RegexOptions.Compiled); |
||||
} catch (ArgumentException) { |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
|
||||
class MetadataTokenSearchStrategy : TypeAndMemberSearchStrategy |
||||
{ |
||||
readonly int searchTermToken; |
||||
|
||||
public MetadataTokenSearchStrategy(params string[] terms) |
||||
: base(terms) |
||||
{ |
||||
if (searchTerm.Length == 1) { |
||||
int.TryParse(searchTerm[0], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out searchTermToken); |
||||
} |
||||
} |
||||
|
||||
protected override bool MatchName(IEntity m, Language language) |
||||
{ |
||||
return SRM.Ecma335.MetadataTokens.GetToken(m.MetadataToken) == searchTermToken; |
||||
} |
||||
} |
||||
|
||||
class LiteralSearchStrategy : AbstractSearchStrategy |
||||
{ |
||||
readonly TypeCode searchTermLiteralType; |
||||
readonly object searchTermLiteralValue; |
||||
|
||||
public LiteralSearchStrategy(params string[] terms) |
||||
: base(terms) |
||||
{ |
||||
if (searchTerm.Length == 1) { |
||||
var lexer = new Lexer(new LATextReader(new System.IO.StringReader(searchTerm[0]))); |
||||
var value = lexer.NextToken(); |
||||
|
||||
if (value != null && value.LiteralValue != null) { |
||||
TypeCode valueType = Type.GetTypeCode(value.LiteralValue.GetType()); |
||||
switch (valueType) { |
||||
case TypeCode.Byte: |
||||
case TypeCode.SByte: |
||||
case TypeCode.Int16: |
||||
case TypeCode.UInt16: |
||||
case TypeCode.Int32: |
||||
case TypeCode.UInt32: |
||||
case TypeCode.Int64: |
||||
case TypeCode.UInt64: |
||||
searchTermLiteralType = TypeCode.Int64; |
||||
searchTermLiteralValue = CSharpPrimitiveCast.Cast(TypeCode.Int64, value.LiteralValue, false); |
||||
break; |
||||
case TypeCode.Single: |
||||
case TypeCode.Double: |
||||
case TypeCode.String: |
||||
searchTermLiteralType = valueType; |
||||
searchTermLiteralValue = value.LiteralValue; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
protected override bool IsMatch(IField field, Language language) |
||||
{ |
||||
return IsLiteralMatch(field.ConstantValue); |
||||
} |
||||
|
||||
protected override bool IsMatch(IProperty property, Language language) |
||||
{ |
||||
return MethodIsLiteralMatch(property.Getter) || MethodIsLiteralMatch(property.Setter); |
||||
} |
||||
|
||||
protected override bool IsMatch(IEvent ev, Language language) |
||||
{ |
||||
return MethodIsLiteralMatch(ev.AddAccessor) || MethodIsLiteralMatch(ev.RemoveAccessor) || MethodIsLiteralMatch(ev.InvokeAccessor); |
||||
} |
||||
|
||||
protected override bool IsMatch(IMethod m, Language language) |
||||
{ |
||||
return MethodIsLiteralMatch(m); |
||||
} |
||||
|
||||
bool IsLiteralMatch(object val) |
||||
{ |
||||
if (val == null) |
||||
return false; |
||||
switch (searchTermLiteralType) { |
||||
case TypeCode.Int64: |
||||
TypeCode tc = Type.GetTypeCode(val.GetType()); |
||||
if (tc >= TypeCode.SByte && tc <= TypeCode.UInt64) |
||||
return CSharpPrimitiveCast.Cast(TypeCode.Int64, val, false).Equals(searchTermLiteralValue); |
||||
else |
||||
return false; |
||||
case TypeCode.Single: |
||||
case TypeCode.Double: |
||||
case TypeCode.String: |
||||
return searchTermLiteralValue.Equals(val); |
||||
default: |
||||
// substring search with searchTerm
|
||||
return IsMatch(t => val.ToString()); |
||||
} |
||||
} |
||||
|
||||
bool MethodIsLiteralMatch(IMethod method) |
||||
{ |
||||
if (method == null) |
||||
return false; |
||||
var module = ((MetadataModule)method.ParentModule).PEFile; |
||||
var m = (SRM.MethodDefinitionHandle)method.MetadataToken; |
||||
if (m.IsNil) |
||||
return false; |
||||
var methodDefinition = module.Metadata.GetMethodDefinition(m); |
||||
if (!methodDefinition.HasBody()) |
||||
return false; |
||||
var blob = module.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress).GetILReader(); |
||||
if (searchTermLiteralType == TypeCode.Int64) { |
||||
long val = (long)searchTermLiteralValue; |
||||
while (blob.RemainingBytes > 0) { |
||||
ILOpCode code; |
||||
switch (code = ILParser.DecodeOpCode(ref blob)) { |
||||
case ILOpCode.Ldc_i8: |
||||
if (val == blob.ReadInt64()) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4: |
||||
if (val == blob.ReadInt32()) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_s: |
||||
if (val == blob.ReadSByte()) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_m1: |
||||
if (val == -1) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_0: |
||||
if (val == 0) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_1: |
||||
if (val == 1) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_2: |
||||
if (val == 2) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_3: |
||||
if (val == 3) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_4: |
||||
if (val == 4) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_5: |
||||
if (val == 5) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_6: |
||||
if (val == 6) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_7: |
||||
if (val == 7) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_i4_8: |
||||
if (val == 8) |
||||
return true; |
||||
break; |
||||
default: |
||||
ILParser.SkipOperand(ref blob, code); |
||||
break; |
||||
} |
||||
} |
||||
} else if (searchTermLiteralType != TypeCode.Empty) { |
||||
ILOpCode expectedCode; |
||||
switch (searchTermLiteralType) { |
||||
case TypeCode.Single: |
||||
expectedCode = ILOpCode.Ldc_r4; |
||||
break; |
||||
case TypeCode.Double: |
||||
expectedCode = ILOpCode.Ldc_r8; |
||||
break; |
||||
case TypeCode.String: |
||||
expectedCode = ILOpCode.Ldstr; |
||||
break; |
||||
default: |
||||
throw new InvalidOperationException(); |
||||
} |
||||
while (blob.RemainingBytes > 0) { |
||||
var code = ILParser.DecodeOpCode(ref blob); |
||||
if (code != expectedCode) { |
||||
ILParser.SkipOperand(ref blob, code); |
||||
continue; |
||||
} |
||||
switch (code) { |
||||
case ILOpCode.Ldc_r4: |
||||
if ((float)searchTermLiteralValue == blob.ReadSingle()) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldc_r8: |
||||
if ((double)searchTermLiteralValue == blob.ReadDouble()) |
||||
return true; |
||||
break; |
||||
case ILOpCode.Ldstr: |
||||
if ((string)searchTermLiteralValue == ILParser.DecodeUserString(ref blob, module.Metadata)) |
||||
return true; |
||||
break; |
||||
} |
||||
} |
||||
} else { |
||||
while (blob.RemainingBytes > 0) { |
||||
var code = ILParser.DecodeOpCode(ref blob); |
||||
if (code != ILOpCode.Ldstr) { |
||||
ILParser.SkipOperand(ref blob, code); |
||||
continue; |
||||
} |
||||
if (base.IsMatch(t => ILParser.DecodeUserString(ref blob, module.Metadata))) |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
enum MemberSearchKind |
||||
{ |
||||
All, |
||||
Field, |
||||
Property, |
||||
Event, |
||||
Method |
||||
} |
||||
|
||||
class MemberSearchStrategy : AbstractSearchStrategy |
||||
{ |
||||
MemberSearchKind searchKind; |
||||
|
||||
public MemberSearchStrategy(string term, MemberSearchKind searchKind = MemberSearchKind.All) |
||||
: this(new[] { term }, searchKind) |
||||
{ |
||||
} |
||||
|
||||
public MemberSearchStrategy(string[] terms, MemberSearchKind searchKind = MemberSearchKind.All) |
||||
: base(terms) |
||||
{ |
||||
this.searchKind = searchKind; |
||||
} |
||||
|
||||
protected override bool IsMatch(IField field, Language language) |
||||
{ |
||||
return (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Field) && MatchName(field, language); |
||||
} |
||||
|
||||
protected override bool IsMatch(IProperty property, Language language) |
||||
{ |
||||
return (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Property) && MatchName(property, language); |
||||
} |
||||
|
||||
protected override bool IsMatch(IEvent ev, Language language) |
||||
{ |
||||
return (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Event) && MatchName(ev, language); |
||||
} |
||||
|
||||
protected override bool IsMatch(IMethod m, Language language) |
||||
{ |
||||
return (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Method) && MatchName(m, language); |
||||
} |
||||
} |
||||
|
||||
class TypeSearchStrategy : AbstractSearchStrategy |
||||
{ |
||||
public TypeSearchStrategy(params string[] terms) |
||||
: base(terms) |
||||
{ |
||||
} |
||||
|
||||
public override void Search(ITypeDefinition type, Language language, Action<SearchResult> addResult) |
||||
{ |
||||
if (MatchName(type, language)) { |
||||
string name = language.TypeToString(type, includeNamespace: false); |
||||
var declaringType = type.DeclaringTypeDefinition; |
||||
addResult(new SearchResult { |
||||
Member = type, |
||||
Fitness = CalculateFitness(type), |
||||
Image = TypeTreeNode.GetIcon(type), |
||||
Name = name, |
||||
LocationImage = declaringType != null ? TypeTreeNode.GetIcon(declaringType) : Images.Namespace, |
||||
Location = declaringType != null ? language.TypeToString(declaringType, includeNamespace: true) : type.Namespace |
||||
}); |
||||
} |
||||
|
||||
foreach (var nestedType in type.NestedTypes) { |
||||
Search(nestedType, language, addResult); |
||||
} |
||||
} |
||||
} |
||||
|
||||
class TypeAndMemberSearchStrategy : AbstractSearchStrategy |
||||
{ |
||||
public TypeAndMemberSearchStrategy(params string[] terms) |
||||
: base(terms) |
||||
{ |
||||
} |
||||
|
||||
public override void Search(ITypeDefinition type, Language language, Action<SearchResult> addResult) |
||||
{ |
||||
if (MatchName(type, language)) |
||||
{ |
||||
string name = language.TypeToString(type, includeNamespace: false); |
||||
var declaringType = type.DeclaringTypeDefinition; |
||||
addResult(new SearchResult { |
||||
Member = type, |
||||
Image = TypeTreeNode.GetIcon(type), |
||||
Fitness = CalculateFitness(type), |
||||
Name = name, |
||||
LocationImage = declaringType != null ? TypeTreeNode.GetIcon(declaringType) : Images.Namespace, |
||||
Location = declaringType != null ? language.TypeToString(declaringType, includeNamespace: true) : type.Namespace |
||||
}); |
||||
} |
||||
|
||||
base.Search(type, language, addResult); |
||||
} |
||||
|
||||
protected override bool IsMatch(IField field, Language language) |
||||
{ |
||||
return MatchName(field, language); |
||||
} |
||||
|
||||
protected override bool IsMatch(IProperty property, Language language) |
||||
{ |
||||
return MatchName(property, language); |
||||
} |
||||
|
||||
protected override bool IsMatch(IEvent ev, Language language) |
||||
{ |
||||
return MatchName(ev, language); |
||||
} |
||||
|
||||
protected override bool IsMatch(IMethod m, Language language) |
||||
{ |
||||
return MatchName(m, language); |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue