diff --git a/ILSpy/SearchPane.cs b/ILSpy/SearchPane.cs index 90b121388..e39e79988 100644 --- a/ILSpy/SearchPane.cs +++ b/ILSpy/SearchPane.cs @@ -20,7 +20,6 @@ using System; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; -using System.IO; using System.Threading; using System.Windows; using System.Windows.Controls; @@ -53,9 +52,9 @@ namespace ICSharpCode.ILSpy } } - const int SearchMode_Type = 0; - const int SearchMode_Member = 1; - const int SearchMode_Literal = 2; + public const int SearchMode_Type = 0; + public const int SearchMode_Member = 1; + public const int SearchMode_Literal = 2; private SearchPane() { @@ -183,7 +182,12 @@ namespace ICSharpCode.ILSpy listBox.SelectedIndex = 0; } } - + + internal interface ISearch + { + void Search(TypeDefinition type, Language language, Action addResult); + } + sealed class RunningSearch { readonly Dispatcher dispatcher; @@ -195,9 +199,6 @@ namespace ICSharpCode.ILSpy public readonly ObservableCollection Results = new ObservableCollection(); int resultCount; - TypeCode searchTermLiteralType = TypeCode.Empty; - object searchTermLiteralValue; - public RunningSearch(LoadedAssembly[] assemblies, string searchTerm, int searchMode, Language language) { this.dispatcher = Dispatcher.CurrentDispatcher; @@ -217,44 +218,17 @@ namespace ICSharpCode.ILSpy public void Run() { try { - if (searchMode == SearchMode_Literal) { - if (1 == searchTerm.Length) - { - CSharpParser parser = new CSharpParser(); - PrimitiveExpression pe = parser.ParseExpression(searchTerm[0]) as PrimitiveExpression; - if (pe != null && pe.Value != null) { - TypeCode peValueType = Type.GetTypeCode(pe.Value.GetType()); - switch (peValueType) { - 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, pe.Value, false); - break; - case TypeCode.Single: - case TypeCode.Double: - case TypeCode.String: - searchTermLiteralType = peValueType; - searchTermLiteralValue = pe.Value; - break; - } - } - } - } - - foreach (var loadedAssembly in assemblies) { + var searcher = ResolveSearcher(searchMode, searchTerm); + foreach (var loadedAssembly in assemblies) + { ModuleDefinition module = loadedAssembly.ModuleDefinition; if (module == null) continue; CancellationToken cancellationToken = cts.Token; + foreach (TypeDefinition type in module.Types) { cancellationToken.ThrowIfCancellationRequested(); - PerformSearch(type); + searcher.Search(type, language, AddResult); } } } catch (OperationCanceledException) { @@ -277,254 +251,401 @@ namespace ICSharpCode.ILSpy new Action(delegate { this.Results.Insert(this.Results.Count - 1, result); })); cts.Token.ThrowIfCancellationRequested(); } - - bool IsMatch(string text) + + private ISearch ResolveSearcher(int mode, string[] terms) { - for (int i = 0; i < searchTerm.Length; ++i) { - // How to handle overlapping matches? - if (text.IndexOf(searchTerm[i], StringComparison.OrdinalIgnoreCase) < 0) - return false; - } - return true; + if (terms.Length == 1) + { + if (terms[0].StartsWith("t:")) + return new TypeSearcher(terms); + + if (terms[0].StartsWith("m:")) + return new MemberSearcher(terms); + + if (terms[0].StartsWith("c:")) + return new LiteralSearcher(terms); + } + + switch (mode) + { + case SearchMode_Type: return new TypeSearcher(terms); + case SearchMode_Member: return new MemberSearcher(terms); + case SearchMode_Literal: return new LiteralSearcher(terms); + } + + return null; + } + } + + internal sealed class SearchResult : INotifyPropertyChanged, IMemberTreeNode + { + event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged { + add { } + remove { } } - void PerformSearch(TypeDefinition type) + public MemberReference Member { get; set; } + + public string Location { get; set; } + public string Name { get; set; } + public ImageSource Image { get; set; } + public ImageSource LocationImage { get; set; } + + public override string ToString() { - if (searchMode == SearchMode_Type && IsMatch(type.Name)) { - AddResult(new SearchResult { - Member = type, - Image = TypeTreeNode.GetIcon(type), - Name = language.TypeToString(type, includeNamespace: false), - LocationImage = type.DeclaringType != null ? TypeTreeNode.GetIcon(type.DeclaringType) : Images.Namespace, - Location = type.DeclaringType != null ? language.TypeToString(type.DeclaringType, includeNamespace: true) : type.Namespace - }); - } - - foreach (TypeDefinition nestedType in type.NestedTypes) { - PerformSearch(nestedType); - } - - if (searchMode == SearchMode_Type) - return; + return Name; + } + } + } + + internal class LiteralSearcher : SearcherBase + { + private readonly TypeCode searchTermLiteralType; + private readonly object searchTermLiteralValue; + + public LiteralSearcher(string[] terms) : base(terms) + { + if (1 == searchTerm.Length) + { + var parser = new CSharpParser(); + var pe = parser.ParseExpression(searchTerm[0]) as PrimitiveExpression; - foreach (FieldDefinition field in type.Fields) { - if (IsMatch(field)) { - AddResult(new SearchResult { - Member = field, - Image = FieldTreeNode.GetIcon(field), - Name = field.Name, - LocationImage = TypeTreeNode.GetIcon(type), - Location = language.TypeToString(type, includeNamespace: true) - }); + if (pe != null && pe.Value != null) + { + TypeCode peValueType = Type.GetTypeCode(pe.Value.GetType()); + switch (peValueType) + { + 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, pe.Value, false); + break; + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.String: + searchTermLiteralType = peValueType; + searchTermLiteralValue = pe.Value; + break; } } - foreach (PropertyDefinition property in type.Properties) { - if (IsMatch(property)) { - AddResult(new SearchResult { - Member = property, - Image = PropertyTreeNode.GetIcon(property), - Name = property.Name, - LocationImage = TypeTreeNode.GetIcon(type), - Location = language.TypeToString(type, includeNamespace: true) - }); + } + } + + protected override bool IsMatch(FieldDefinition field) + { + return IsLiteralMatch(field.Constant); + } + + protected override bool IsMatch(PropertyDefinition property) + { + return MethodIsLiteralMatch(property.GetMethod) || MethodIsLiteralMatch(property.SetMethod); + } + + protected override bool IsMatch(EventDefinition ev) + { + return MethodIsLiteralMatch(ev.AddMethod) || MethodIsLiteralMatch(ev.RemoveMethod) || MethodIsLiteralMatch(ev.InvokeMethod); + } + + protected override bool IsMatch(MethodDefinition m) + { + 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(val.ToString()); + } + } + + bool MethodIsLiteralMatch(MethodDefinition m) + { + if (m == null) + return false; + var body = m.Body; + if (body == null) + return false; + if (searchTermLiteralType == TypeCode.Int64) + { + long val = (long)searchTermLiteralValue; + foreach (var inst in body.Instructions) + { + switch (inst.OpCode.Code) + { + case Code.Ldc_I8: + if (val == (long)inst.Operand) + return true; + break; + case Code.Ldc_I4: + if (val == (int)inst.Operand) + return true; + break; + case Code.Ldc_I4_S: + if (val == (sbyte)inst.Operand) + return true; + break; + case Code.Ldc_I4_M1: + if (val == -1) + return true; + break; + case Code.Ldc_I4_0: + if (val == 0) + return true; + break; + case Code.Ldc_I4_1: + if (val == 1) + return true; + break; + case Code.Ldc_I4_2: + if (val == 2) + return true; + break; + case Code.Ldc_I4_3: + if (val == 3) + return true; + break; + case Code.Ldc_I4_4: + if (val == 4) + return true; + break; + case Code.Ldc_I4_5: + if (val == 5) + return true; + break; + case Code.Ldc_I4_6: + if (val == 6) + return true; + break; + case Code.Ldc_I4_7: + if (val == 7) + return true; + break; + case Code.Ldc_I4_8: + if (val == 8) + return true; + break; } } - foreach (EventDefinition ev in type.Events) { - if (IsMatch(ev)) { - AddResult(new SearchResult { - Member = ev, - Image = EventTreeNode.GetIcon(ev), - Name = ev.Name, - LocationImage = TypeTreeNode.GetIcon(type), - Location = language.TypeToString(type, includeNamespace: true) - }); - } + } + else if (searchTermLiteralType != TypeCode.Empty) + { + Code expectedCode; + switch (searchTermLiteralType) + { + case TypeCode.Single: + expectedCode = Code.Ldc_R4; + break; + case TypeCode.Double: + expectedCode = Code.Ldc_R8; + break; + case TypeCode.String: + expectedCode = Code.Ldstr; + break; + default: + throw new InvalidOperationException(); } - foreach (MethodDefinition method in type.Methods) { - switch (method.SemanticsAttributes) { - case MethodSemanticsAttributes.Setter: - case MethodSemanticsAttributes.Getter: - case MethodSemanticsAttributes.AddOn: - case MethodSemanticsAttributes.RemoveOn: - case MethodSemanticsAttributes.Fire: - continue; - } - if (IsMatch(method)) { - AddResult(new SearchResult { - Member = method, - Image = MethodTreeNode.GetIcon(method), - Name = method.Name, - LocationImage = TypeTreeNode.GetIcon(type), - Location = language.TypeToString(type, includeNamespace: true) - }); - } + foreach (var inst in body.Instructions) + { + if (inst.OpCode.Code == expectedCode && searchTermLiteralValue.Equals(inst.Operand)) + return true; } } - - bool IsMatch(FieldDefinition field) + else { - if (searchMode == SearchMode_Literal) - return IsLiteralMatch(field.Constant); - else - return IsMatch(field.Name); + foreach (var inst in body.Instructions) + { + if (inst.OpCode.Code == Code.Ldstr && IsMatch((string)inst.Operand)) + return true; + } } - - bool IsMatch(PropertyDefinition property) + return false; + } + } + + internal class MemberSearcher : SearcherBase + { + public MemberSearcher(string[] terms) : base(terms) + { + } + + protected override bool IsMatch(FieldDefinition field) + { + return IsMatch(field.Name); + } + + protected override bool IsMatch(PropertyDefinition property) + { + return IsMatch(property.Name); + } + + protected override bool IsMatch(EventDefinition ev) + { + return IsMatch(ev.Name); + } + + protected override bool IsMatch(MethodDefinition m) + { + return IsMatch(m.Name); + } + } + + internal abstract class SearcherBase : SearchPane.ISearch + { + protected string[] searchTerm; + + protected SearcherBase(string[] terms) + { + if (terms.Length == 1 && terms[0].Length > 2 && terms[0][1] == ':') + terms[0] = terms[0].Substring(2); + + searchTerm = terms; + } + + protected bool IsMatch(string text) + { + for (int i = 0; i < searchTerm.Length; ++i) { - if (searchMode == SearchMode_Literal) - return MethodIsLiteralMatch(property.GetMethod) || MethodIsLiteralMatch(property.SetMethod); - else - return IsMatch(property.Name); + // How to handle overlapping matches? + if (text.IndexOf(searchTerm[i], StringComparison.OrdinalIgnoreCase) < 0) + return false; } - - bool IsMatch(EventDefinition ev) + return true; + } + + protected virtual bool IsMatch(FieldDefinition field) + { + return false; + } + + protected virtual bool IsMatch(PropertyDefinition property) + { + return false; + } + + protected virtual bool IsMatch(EventDefinition ev) + { + return false; + } + + protected virtual bool IsMatch(MethodDefinition m) + { + return false; + } + + public virtual void Search(TypeDefinition type, Language language, Action addResult) + { + foreach (FieldDefinition field in type.Fields) { - if (searchMode == SearchMode_Literal) - return MethodIsLiteralMatch(ev.AddMethod) || MethodIsLiteralMatch(ev.RemoveMethod) || MethodIsLiteralMatch(ev.InvokeMethod); - else - return IsMatch(ev.Name); + if (IsMatch(field)) + { + addResult(new SearchPane.SearchResult + { + Member = field, + Image = FieldTreeNode.GetIcon(field), + Name = field.Name, + LocationImage = TypeTreeNode.GetIcon(type), + Location = language.TypeToString(type, includeNamespace: true) + }); + } } - - bool IsMatch(MethodDefinition m) + foreach (PropertyDefinition property in type.Properties) { - if (searchMode == SearchMode_Literal) - return MethodIsLiteralMatch(m); - else - return IsMatch(m.Name); + if (IsMatch(property)) + { + addResult(new SearchPane.SearchResult + { + Member = property, + Image = PropertyTreeNode.GetIcon(property), + Name = property.Name, + LocationImage = TypeTreeNode.GetIcon(type), + Location = language.TypeToString(type, includeNamespace: true) + }); + } } - - bool IsLiteralMatch(object val) + foreach (EventDefinition ev in type.Events) { - 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(val.ToString()); + if (IsMatch(ev)) + { + addResult(new SearchPane.SearchResult + { + Member = ev, + Image = EventTreeNode.GetIcon(ev), + Name = ev.Name, + LocationImage = TypeTreeNode.GetIcon(type), + Location = language.TypeToString(type, includeNamespace: true) + }); } } - - bool MethodIsLiteralMatch(MethodDefinition m) + foreach (MethodDefinition method in type.Methods) { - if (m == null) - return false; - var body = m.Body; - if (body == null) - return false; - if (searchTermLiteralType == TypeCode.Int64) { - long val = (long)searchTermLiteralValue; - foreach (var inst in body.Instructions) { - switch (inst.OpCode.Code) { - case Code.Ldc_I8: - if (val == (long)inst.Operand) - return true; - break; - case Code.Ldc_I4: - if (val == (int)inst.Operand) - return true; - break; - case Code.Ldc_I4_S: - if (val == (sbyte)inst.Operand) - return true; - break; - case Code.Ldc_I4_M1: - if (val == -1) - return true; - break; - case Code.Ldc_I4_0: - if (val == 0) - return true; - break; - case Code.Ldc_I4_1: - if (val == 1) - return true; - break; - case Code.Ldc_I4_2: - if (val == 2) - return true; - break; - case Code.Ldc_I4_3: - if (val == 3) - return true; - break; - case Code.Ldc_I4_4: - if (val == 4) - return true; - break; - case Code.Ldc_I4_5: - if (val == 5) - return true; - break; - case Code.Ldc_I4_6: - if (val == 6) - return true; - break; - case Code.Ldc_I4_7: - if (val == 7) - return true; - break; - case Code.Ldc_I4_8: - if (val == 8) - return true; - break; - } - } - } else if (searchTermLiteralType != TypeCode.Empty) { - Code expectedCode; - switch (searchTermLiteralType) { - case TypeCode.Single: - expectedCode = Code.Ldc_R4; - break; - case TypeCode.Double: - expectedCode = Code.Ldc_R8; - break; - case TypeCode.String: - expectedCode = Code.Ldstr; - break; - default: - throw new InvalidOperationException(); - } - foreach (var inst in body.Instructions) { - if (inst.OpCode.Code == expectedCode && searchTermLiteralValue.Equals(inst.Operand)) - return true; - } - } else { - foreach (var inst in body.Instructions) { - if (inst.OpCode.Code == Code.Ldstr && IsMatch((string)inst.Operand)) - return true; - } + switch (method.SemanticsAttributes) + { + case MethodSemanticsAttributes.Setter: + case MethodSemanticsAttributes.Getter: + case MethodSemanticsAttributes.AddOn: + case MethodSemanticsAttributes.RemoveOn: + case MethodSemanticsAttributes.Fire: + continue; + } + if (IsMatch(method)) + { + addResult(new SearchPane.SearchResult + { + Member = method, + Image = MethodTreeNode.GetIcon(method), + Name = method.Name, + LocationImage = TypeTreeNode.GetIcon(type), + Location = language.TypeToString(type, includeNamespace: true) + }); } - return false; } } - - sealed class SearchResult : INotifyPropertyChanged, IMemberTreeNode + } + + internal class TypeSearcher : SearcherBase + { + public TypeSearcher(string[] terms) : base(terms) { - event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged { - add { } - remove { } + } + + public override void Search(TypeDefinition type, Language language, Action addResult) + { + if (IsMatch(type.Name)) + { + addResult(new SearchPane.SearchResult + { + Member = type, + Image = TypeTreeNode.GetIcon(type), + Name = language.TypeToString(type, includeNamespace: false), + LocationImage = type.DeclaringType != null ? TypeTreeNode.GetIcon(type.DeclaringType) : Images.Namespace, + Location = type.DeclaringType != null ? language.TypeToString(type.DeclaringType, includeNamespace: true) : type.Namespace + }); } - - public MemberReference Member { get; set; } - - public string Location { get; set; } - public string Name { get; set; } - public ImageSource Image { get; set; } - public ImageSource LocationImage { get; set; } - - public override string ToString() + + foreach (TypeDefinition nestedType in type.NestedTypes) { - return Name; + Search(nestedType, language, addResult); } } }