From 0b498df891d335435b2992fcc14503acc125b6e8 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 15 Jul 2018 19:25:36 +0200 Subject: [PATCH] Fix performance of SearchStrategies. --- ILSpy/Languages/CSharpLanguage.cs | 82 +++++++++++++++++++++++++- ILSpy/Languages/Language.cs | 45 +++++++++++++- ILSpy/Search/AbstractSearchStrategy.cs | 19 ++---- ILSpy/Search/LiteralSearchStrategy.cs | 4 +- ILSpy/Search/MemberSearchStrategy.cs | 70 +++++----------------- 5 files changed, 146 insertions(+), 74 deletions(-) diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index 5e5178cf6..4dd8b7430 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -40,6 +40,7 @@ using ICSharpCode.Decompiler.Util; using System.Reflection; using ICSharpCode.Decompiler.Disassembler; using GenericContext = ICSharpCode.Decompiler.Metadata.GenericContext; +using System.Text; namespace ICSharpCode.ILSpy { @@ -555,9 +556,86 @@ namespace ICSharpCode.ILSpy return buffer.ToString(); } - public override bool SearchCanUseILNames(string text) + string ToCSharpString(MetadataReader metadata, TypeDefinitionHandle handle, bool fullName) { - return !text.Contains("<"); + StringBuilder builder = new StringBuilder(); + var currentTypeDefHandle = handle; + var typeDef = metadata.GetTypeDefinition(currentTypeDefHandle); + + while (!currentTypeDefHandle.IsNil) { + if (builder.Length > 0) + builder.Insert(0, '.'); + typeDef = metadata.GetTypeDefinition(currentTypeDefHandle); + var part = ReflectionHelper.SplitTypeParameterCountFromReflectionName(metadata.GetString(typeDef.Name), out int typeParamCount); + var genericParams = typeDef.GetGenericParameters(); + if (genericParams.Count > 0) { + builder.Insert(0, '>'); + int firstIndex = genericParams.Count - typeParamCount; + for (int i = genericParams.Count - 1; i >= genericParams.Count - typeParamCount; i--) { + builder.Insert(0, metadata.GetString(metadata.GetGenericParameter(genericParams[i]).Name)); + builder.Insert(0, i == firstIndex ? '<' : ','); + } + } + builder.Insert(0, part); + currentTypeDefHandle = typeDef.GetDeclaringType(); + if (!fullName) break; + } + + if (fullName && !typeDef.Namespace.IsNil) { + builder.Insert(0, '.'); + builder.Insert(0, metadata.GetString(typeDef.Namespace)); + } + + return builder.ToString(); + } + + public override string GetEntityName(PEFile module, EntityHandle handle, bool fullName) + { + MetadataReader metadata = module.Metadata; + switch (handle.Kind) { + case HandleKind.TypeDefinition: + return ToCSharpString(metadata, (TypeDefinitionHandle)handle, fullName); + case HandleKind.FieldDefinition: + var fd = metadata.GetFieldDefinition((FieldDefinitionHandle)handle); + var declaringType = fd.GetDeclaringType(); + if (fullName) + return ToCSharpString(metadata, declaringType, fullName) + "." + metadata.GetString(fd.Name); + return metadata.GetString(fd.Name); + case HandleKind.MethodDefinition: + var md = metadata.GetMethodDefinition((MethodDefinitionHandle)handle); + declaringType = md.GetDeclaringType(); + string methodName = metadata.GetString(md.Name); + var genericParams = md.GetGenericParameters(); + if (genericParams.Count > 0) { + methodName += "<"; + int i = 0; + foreach (var h in genericParams) { + if (i > 0) + methodName += ","; + var gp = metadata.GetGenericParameter(h); + methodName += metadata.GetString(gp.Name); + } + methodName += ">"; + } + + if (fullName) + return ToCSharpString(metadata, declaringType, fullName) + "." + methodName; + return methodName; + case HandleKind.EventDefinition: + var ed = metadata.GetEventDefinition((EventDefinitionHandle)handle); + declaringType = metadata.GetMethodDefinition(ed.GetAccessors().GetAny()).GetDeclaringType(); + if (fullName) + return ToCSharpString(metadata, declaringType, fullName) + "." + metadata.GetString(ed.Name); + return metadata.GetString(ed.Name); + case HandleKind.PropertyDefinition: + var pd = metadata.GetPropertyDefinition((PropertyDefinitionHandle)handle); + declaringType = metadata.GetMethodDefinition(pd.GetAccessors().GetAny()).GetDeclaringType(); + if (fullName) + return ToCSharpString(metadata, declaringType, fullName) + "." + metadata.GetString(pd.Name); + return metadata.GetString(pd.Name); + default: + return null; + } } public override bool ShowMember(IEntity member) diff --git a/ILSpy/Languages/Language.cs b/ILSpy/Languages/Language.cs index 0759deb24..e371366bb 100644 --- a/ILSpy/Languages/Language.cs +++ b/ILSpy/Languages/Language.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; @@ -266,9 +267,49 @@ namespace ICSharpCode.ILSpy return true; } - public virtual bool SearchCanUseILNames(string text) + /// + /// This should produce a string representation of the entity for search to match search strings against. + /// + public virtual string GetEntityName(PEFile module, EntityHandle handle, bool fullName) { - return true; + MetadataReader metadata = module.Metadata; + switch (handle.Kind) { + case HandleKind.TypeDefinition: + if (fullName) + return ((TypeDefinitionHandle)handle).GetFullTypeName(metadata).ToILNameString(); + var td = metadata.GetTypeDefinition((TypeDefinitionHandle)handle); + return metadata.GetString(td.Name); + case HandleKind.FieldDefinition: + var fd = metadata.GetFieldDefinition((FieldDefinitionHandle)handle); + var declaringType = fd.GetDeclaringType(); + if (fullName) + return fd.GetDeclaringType().GetFullTypeName(metadata).ToILNameString() + "." + metadata.GetString(fd.Name); + return metadata.GetString(fd.Name); + case HandleKind.MethodDefinition: + var md = metadata.GetMethodDefinition((MethodDefinitionHandle)handle); + declaringType = md.GetDeclaringType(); + string methodName = metadata.GetString(md.Name); + int genericParamCount = md.GetGenericParameters().Count; + if (genericParamCount > 0) + methodName += "``" + genericParamCount; + if (fullName) + return md.GetDeclaringType().GetFullTypeName(metadata).ToILNameString() + "." + methodName; + return methodName; + case HandleKind.EventDefinition: + var ed = metadata.GetEventDefinition((EventDefinitionHandle)handle); + declaringType = metadata.GetMethodDefinition(ed.GetAccessors().GetAny()).GetDeclaringType(); + if (fullName) + return declaringType.GetFullTypeName(metadata).ToILNameString() + "." + metadata.GetString(ed.Name); + return metadata.GetString(ed.Name); + case HandleKind.PropertyDefinition: + var pd = metadata.GetPropertyDefinition((PropertyDefinitionHandle)handle); + declaringType = metadata.GetMethodDefinition(pd.GetAccessors().GetAny()).GetDeclaringType(); + if (fullName) + return declaringType.GetFullTypeName(metadata).ToILNameString() + "." + metadata.GetString(pd.Name); + return metadata.GetString(pd.Name); + default: + return null; + } } public virtual CodeMappingInfo GetCodeMappingInfo(PEFile module, SRM.EntityHandle member) diff --git a/ILSpy/Search/AbstractSearchStrategy.cs b/ILSpy/Search/AbstractSearchStrategy.cs index 75f0ebb7f..c0de48398 100644 --- a/ILSpy/Search/AbstractSearchStrategy.cs +++ b/ILSpy/Search/AbstractSearchStrategy.cs @@ -15,10 +15,9 @@ namespace ICSharpCode.ILSpy.Search { abstract class AbstractSearchStrategy { - protected readonly (string Term, bool CanUseILNames)[] searchTerm; + protected readonly string[] searchTerm; protected readonly Regex regex; protected readonly bool fullNameSearch; - protected readonly bool needsLanguageSpecificNames; protected readonly Language language; protected readonly Action addResult; @@ -35,17 +34,11 @@ namespace ICSharpCode.ILSpy.Search if (regexString.EndsWith("/", StringComparison.Ordinal)) regexString = regexString.Substring(0, regexString.Length - 1); regex = SafeNewRegex(regexString); - needsLanguageSpecificNames = true; - searchTerm = new[] { (search, false) }; } else { fullNameSearch = search.Contains("."); - needsLanguageSpecificNames = fullNameSearch || !language.SearchCanUseILNames(search); - searchTerm = new[] { (search, !needsLanguageSpecificNames) }; } - } else { - searchTerm = terms.SelectArray(t => (t, language.SearchCanUseILNames(t))); - needsLanguageSpecificNames = searchTerm.Any(t => !t.CanUseILNames); } + searchTerm = terms; } protected float CalculateFitness(IEntity member) @@ -69,17 +62,17 @@ namespace ICSharpCode.ILSpy.Search return 1.0f / text.Length; } - protected virtual bool IsMatch(MetadataReader metadata, StringHandle nameHandle, string languageSpecificName) + protected virtual bool IsMatch(string entityName) { if (regex != null) { - return regex.IsMatch(languageSpecificName); + return regex.IsMatch(entityName); } for (int i = 0; i < searchTerm.Length; ++i) { // How to handle overlapping matches? - var term = searchTerm[i].Term; + var term = searchTerm[i]; if (string.IsNullOrEmpty(term)) continue; - string text = nameHandle.IsNil || !searchTerm[i].CanUseILNames || term.Contains(".") ? languageSpecificName : metadata.GetString(nameHandle); + string text = entityName; switch (term[0]) { case '+': // must contain term = term.Substring(1); diff --git a/ILSpy/Search/LiteralSearchStrategy.cs b/ILSpy/Search/LiteralSearchStrategy.cs index 6c8ed2feb..4eb7d6707 100644 --- a/ILSpy/Search/LiteralSearchStrategy.cs +++ b/ILSpy/Search/LiteralSearchStrategy.cs @@ -96,7 +96,7 @@ namespace ICSharpCode.ILSpy.Search return searchTermLiteralValue.Equals(val); default: // substring search with searchTerm - return IsMatch(metadata, MetadataTokens.StringHandle(0), val.ToString()); + return IsMatch(val.ToString()); } } @@ -208,7 +208,7 @@ namespace ICSharpCode.ILSpy.Search ILParser.SkipOperand(ref blob, code); continue; } - if (IsMatch(module.Metadata, MetadataTokens.StringHandle(0), ILParser.DecodeUserString(ref blob, module.Metadata))) + if (IsMatch(ILParser.DecodeUserString(ref blob, module.Metadata))) return true; } } diff --git a/ILSpy/Search/MemberSearchStrategy.cs b/ILSpy/Search/MemberSearchStrategy.cs index 6aeddf7fe..010a1fe61 100644 --- a/ILSpy/Search/MemberSearchStrategy.cs +++ b/ILSpy/Search/MemberSearchStrategy.cs @@ -27,18 +27,10 @@ namespace ICSharpCode.ILSpy.Search if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Type) { foreach (var handle in metadata.TypeDefinitions) { - var td = metadata.GetTypeDefinition(handle); - string languageSpecificName = null; - ITypeDefinition type = null; - if (needsLanguageSpecificNames) { - type = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); - languageSpecificName = GetLanguageSpecificName(type, fullName: true); - } - if (!IsMatch(module.Metadata, td.Name, languageSpecificName)) + string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); + if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; - if (type == null) { - type = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); - } + var type = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); addResult(ResultFromEntity(type)); } } @@ -46,72 +38,40 @@ namespace ICSharpCode.ILSpy.Search if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Method) { foreach (var handle in metadata.MethodDefinitions) { // TODO use method semantics to skip accessors - var md = metadata.GetMethodDefinition(handle); - string languageSpecificName = null; - IMethod method = null; - if (needsLanguageSpecificNames) { - method = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); - languageSpecificName = GetLanguageSpecificName(method, fullName: true); - } - if (!IsMatch(module.Metadata, md.Name, languageSpecificName)) + string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); + if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; - if (method == null) { - method = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); - } + var method = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); addResult(ResultFromEntity(method)); } } if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Field) { foreach (var handle in metadata.FieldDefinitions) { - var fd = metadata.GetFieldDefinition(handle); - string languageSpecificName = null; - IField field = null; - if (needsLanguageSpecificNames) { - field = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); - languageSpecificName = GetLanguageSpecificName(field, fullName: true); - } - if (!IsMatch(module.Metadata, fd.Name, languageSpecificName)) + string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); + if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; - if (field == null) { - field = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); - } + var field = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); addResult(ResultFromEntity(field)); } } if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Property) { foreach (var handle in metadata.PropertyDefinitions) { - var pd = metadata.GetPropertyDefinition(handle); - string languageSpecificName = null; - IProperty property = null; - if (needsLanguageSpecificNames) { - property = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); - languageSpecificName = GetLanguageSpecificName(property, fullName: true); - } - if (!IsMatch(module.Metadata, pd.Name, languageSpecificName)) + string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); + if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; - if (property == null) { - property = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); - } + var property = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); addResult(ResultFromEntity(property)); } } if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Event) { foreach (var handle in metadata.EventDefinitions) { - var ed = metadata.GetEventDefinition(handle); - string languageSpecificName = null; - IEvent @event = null; - if (needsLanguageSpecificNames) { - @event = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); - languageSpecificName = GetLanguageSpecificName(@event, fullName: true); - } - if (!IsMatch(module.Metadata, ed.Name, languageSpecificName)) + string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); + if (!IsMatch(languageSpecificName)) continue; - if (@event == null) { - @event = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); - } + var @event = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); addResult(ResultFromEntity(@event)); } }