From f4733307f4c84f678f35f0af5d0df032919f154f Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 14 Jul 2018 21:14:22 +0200 Subject: [PATCH] Move search-related files to Search subfolder. --- .../Builtin}/TypeExtensionMethodsAnalyzer.cs | 0 ILSpy/ILSpy.csproj | 15 +- ILSpy/Search/LiteralSearchStrategy.cs | 215 +++++++ ILSpy/Search/MemberSearchStrategy.cs | 49 ++ ILSpy/Search/MetadataTokenSearchStrategy.cs | 24 + ILSpy/{ => Search}/SearchPane.cs | 0 ILSpy/{ => Search}/SearchPane.xaml | 0 ILSpy/Search/SearchStrategies.cs | 215 +++++++ ILSpy/Search/TypeAndMemberSearchStrategy.cs | 53 ++ ILSpy/Search/TypeSearchStrategy.cs | 34 ++ ILSpy/SearchStrategies.cs | 564 ------------------ 11 files changed, 600 insertions(+), 569 deletions(-) rename ILSpy/{ => Analyzers/Builtin}/TypeExtensionMethodsAnalyzer.cs (100%) create mode 100644 ILSpy/Search/LiteralSearchStrategy.cs create mode 100644 ILSpy/Search/MemberSearchStrategy.cs create mode 100644 ILSpy/Search/MetadataTokenSearchStrategy.cs rename ILSpy/{ => Search}/SearchPane.cs (100%) rename ILSpy/{ => Search}/SearchPane.xaml (100%) create mode 100644 ILSpy/Search/SearchStrategies.cs create mode 100644 ILSpy/Search/TypeAndMemberSearchStrategy.cs create mode 100644 ILSpy/Search/TypeSearchStrategy.cs delete mode 100644 ILSpy/SearchStrategies.cs diff --git a/ILSpy/TypeExtensionMethodsAnalyzer.cs b/ILSpy/Analyzers/Builtin/TypeExtensionMethodsAnalyzer.cs similarity index 100% rename from ILSpy/TypeExtensionMethodsAnalyzer.cs rename to ILSpy/Analyzers/Builtin/TypeExtensionMethodsAnalyzer.cs diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 70e14dbf1..8d3a10ac0 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -153,9 +153,12 @@ + + + @@ -196,12 +199,12 @@ - + SearchPane.xaml Code - + @@ -232,7 +235,9 @@ - + + + LGPL.txt @@ -311,7 +316,7 @@ - + @@ -382,7 +387,7 @@ - + diff --git a/ILSpy/Search/LiteralSearchStrategy.cs b/ILSpy/Search/LiteralSearchStrategy.cs new file mode 100644 index 000000000..905733b6b --- /dev/null +++ b/ILSpy/Search/LiteralSearchStrategy.cs @@ -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; + } + } +} diff --git a/ILSpy/Search/MemberSearchStrategy.cs b/ILSpy/Search/MemberSearchStrategy.cs new file mode 100644 index 000000000..6794eea27 --- /dev/null +++ b/ILSpy/Search/MemberSearchStrategy.cs @@ -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 + } +} diff --git a/ILSpy/Search/MetadataTokenSearchStrategy.cs b/ILSpy/Search/MetadataTokenSearchStrategy.cs new file mode 100644 index 000000000..99d87edf8 --- /dev/null +++ b/ILSpy/Search/MetadataTokenSearchStrategy.cs @@ -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; + } + } +} diff --git a/ILSpy/SearchPane.cs b/ILSpy/Search/SearchPane.cs similarity index 100% rename from ILSpy/SearchPane.cs rename to ILSpy/Search/SearchPane.cs diff --git a/ILSpy/SearchPane.xaml b/ILSpy/Search/SearchPane.xaml similarity index 100% rename from ILSpy/SearchPane.xaml rename to ILSpy/Search/SearchPane.xaml diff --git a/ILSpy/Search/SearchStrategies.cs b/ILSpy/Search/SearchStrategies.cs new file mode 100644 index 000000000..423e5b80e --- /dev/null +++ b/ILSpy/Search/SearchStrategies.cs @@ -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 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(Func> itemsGetter, ITypeDefinition type, Language language, Action addResult, Func matcher, Func image) where T : IEntity + { + IEnumerable items = Enumerable.Empty(); + 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 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; + } + } + } +} diff --git a/ILSpy/Search/TypeAndMemberSearchStrategy.cs b/ILSpy/Search/TypeAndMemberSearchStrategy.cs new file mode 100644 index 000000000..6f5433052 --- /dev/null +++ b/ILSpy/Search/TypeAndMemberSearchStrategy.cs @@ -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 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); + } + } +} diff --git a/ILSpy/Search/TypeSearchStrategy.cs b/ILSpy/Search/TypeSearchStrategy.cs new file mode 100644 index 000000000..5826120ba --- /dev/null +++ b/ILSpy/Search/TypeSearchStrategy.cs @@ -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 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); + } + } + } +} diff --git a/ILSpy/SearchStrategies.cs b/ILSpy/SearchStrategies.cs deleted file mode 100644 index d868ddfdb..000000000 --- a/ILSpy/SearchStrategies.cs +++ /dev/null @@ -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 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(Func> itemsGetter, ITypeDefinition type, Language language, Action addResult, Func matcher, Func image) where T : IEntity - { - IEnumerable items = Enumerable.Empty(); - 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 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 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 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); - } - } -}