From ee1096958c1915d7495ff3d5723179cd68a14510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Ku=C4=8Dera?= Date: Sat, 7 Sep 2019 23:17:02 +0100 Subject: [PATCH 01/62] Add resource search strategy --- ILSpy/ContextMenuEntry.cs | 2 +- ILSpy/ILSpy.csproj | 8 ++ ILSpy/Search/AbstractEntitySearchStrategy.cs | 125 +++++++++++++++++++ ILSpy/Search/AbstractSearchStrategy.cs | 103 +-------------- ILSpy/Search/LiteralSearchStrategy.cs | 2 +- ILSpy/Search/MemberSearchStrategy.cs | 2 +- ILSpy/Search/MetadataTokenSearchStrategy.cs | 2 +- ILSpy/Search/ResourceSearchStrategy.cs | 93 ++++++++++++++ ILSpy/Search/SearchPane.cs | 17 ++- 9 files changed, 246 insertions(+), 108 deletions(-) create mode 100644 ILSpy/Search/AbstractEntitySearchStrategy.cs create mode 100644 ILSpy/Search/ResourceSearchStrategy.cs diff --git a/ILSpy/ContextMenuEntry.cs b/ILSpy/ContextMenuEntry.cs index ce1f104d6..9b0bbe36d 100644 --- a/ILSpy/ContextMenuEntry.cs +++ b/ILSpy/ContextMenuEntry.cs @@ -78,7 +78,7 @@ namespace ICSharpCode.ILSpy if (textView != null) reference = textView.GetReferenceSegmentAtMousePosition(); else if (listBox?.SelectedItem is SearchResult result) - reference = new ReferenceSegment { Reference = result.Member }; + reference = new ReferenceSegment { Reference = result.Reference }; else reference = null; var position = textView != null ? textView.GetPositionFromMousePosition() : null; diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 224a13594..07a789a4a 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -167,6 +167,10 @@ True Resources.resx + + Code + MSBuild:Compile + @@ -204,6 +208,10 @@ + + Code + MSBuild:Compile + SearchPane.xaml diff --git a/ILSpy/Search/AbstractEntitySearchStrategy.cs b/ILSpy/Search/AbstractEntitySearchStrategy.cs new file mode 100644 index 000000000..57382846b --- /dev/null +++ b/ILSpy/Search/AbstractEntitySearchStrategy.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Concurrent; +using System.Text.RegularExpressions; +using System.Threading; +using System.Windows.Media; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.ILSpy.TreeNodes; + +namespace ICSharpCode.ILSpy.Search +{ + abstract class AbstractEntitySearchStrategy : AbstractSearchStrategy + { + protected readonly Language language; + protected readonly ApiVisibility apiVisibility; + + protected AbstractEntitySearchStrategy(Language language, ApiVisibility apiVisibility, IProducerConsumerCollection resultQueue, params string[] terms) + : base(resultQueue, terms) + { + this.language = language; + this.apiVisibility = apiVisibility; + } + + protected bool CheckVisibility(IEntity entity) + { + if (apiVisibility == ApiVisibility.All) + return true; + + do { + if (apiVisibility == ApiVisibility.PublicOnly) { + if (!(entity.Accessibility == Accessibility.Public || + entity.Accessibility == Accessibility.Protected || + entity.Accessibility == Accessibility.ProtectedOrInternal)) + return false; + } else if (apiVisibility == ApiVisibility.PublicAndInternal) { + if (!language.ShowMember(entity)) + return false; + } + entity = entity.DeclaringTypeDefinition; + } + while (entity != null); + + return true; + } + + protected void OnFoundResult(IEntity entity) + { + var result = ResultFromEntity(entity); + OnFoundResult(result); + } + + SearchResult ResultFromEntity(IEntity item) + { + var declaringType = item.DeclaringTypeDefinition; + return new SearchResult { + Reference = item, + Fitness = CalculateFitness(item), + Image = GetIcon(item), + Name = GetLanguageSpecificName(item), + LocationImage = declaringType != null ? TypeTreeNode.GetIcon(declaringType) : Images.Namespace, + Location = declaringType != null ? language.TypeToString(declaringType, includeNamespace: true) : item.Namespace, + AssemblyImage = Images.Assembly, + Assembly = item.ParentModule.FullAssemblyName, + ToolTip = item.ParentModule.PEFile?.FileName + }; + } + + 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; + } + + string GetLanguageSpecificName(IEntity member) + { + switch (member) { + case ITypeDefinition t: + return language.TypeToString(t, false); + case IField f: + return language.FieldToString(f, true, false, false); + case IProperty p: + return language.PropertyToString(p, true, false, false); + case IMethod m: + return language.MethodToString(m, true, false, false); + case IEvent e: + return language.EventToString(e, true, false, false); + default: + throw new NotSupportedException(member?.GetType() + " not supported!"); + } + } + + ImageSource GetIcon(IEntity member) + { + switch (member) { + case ITypeDefinition t: + return TypeTreeNode.GetIcon(t); + case IField f: + return FieldTreeNode.GetIcon(f); + case IProperty p: + return PropertyTreeNode.GetIcon(p); + case IMethod m: + return MethodTreeNode.GetIcon(m); + case IEvent e: + return EventTreeNode.GetIcon(e); + default: + throw new NotSupportedException(member?.GetType() + " not supported!"); + } + } + } +} diff --git a/ILSpy/Search/AbstractSearchStrategy.cs b/ILSpy/Search/AbstractSearchStrategy.cs index 80291ae31..b87bc3aed 100644 --- a/ILSpy/Search/AbstractSearchStrategy.cs +++ b/ILSpy/Search/AbstractSearchStrategy.cs @@ -15,14 +15,10 @@ namespace ICSharpCode.ILSpy.Search protected readonly Regex regex; protected readonly bool fullNameSearch; protected readonly bool omitGenerics; - protected readonly Language language; - protected readonly ApiVisibility apiVisibility; private readonly IProducerConsumerCollection resultQueue; - protected AbstractSearchStrategy(Language language, ApiVisibility apiVisibility, IProducerConsumerCollection resultQueue, params string[] terms) + protected AbstractSearchStrategy(IProducerConsumerCollection resultQueue, params string[] terms) { - this.language = language; - this.apiVisibility = apiVisibility; this.resultQueue = resultQueue; if (terms.Length == 1 && terms[0].Length > 2) { @@ -87,28 +83,6 @@ namespace ICSharpCode.ILSpy.Search return true; } - protected bool CheckVisibility(IEntity entity) - { - if (apiVisibility == ApiVisibility.All) - return true; - - do { - if (apiVisibility == ApiVisibility.PublicOnly) { - if (!(entity.Accessibility == Accessibility.Public || - entity.Accessibility == Accessibility.Protected || - entity.Accessibility == Accessibility.ProtectedOrInternal)) - return false; - } else if (apiVisibility == ApiVisibility.PublicAndInternal) { - if (!language.ShowMember(entity)) - return false; - } - entity = entity.DeclaringTypeDefinition; - } - while (entity != null); - - return true; - } - bool IsNoncontiguousMatch(string text, string searchTerm) { if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(searchTerm)) { @@ -135,10 +109,9 @@ namespace ICSharpCode.ILSpy.Search } return false; } - - protected void OnFoundResult(IEntity entity) + + protected void OnFoundResult(SearchResult result) { - var result = ResultFromEntity(entity); resultQueue.TryAdd(result); } @@ -150,75 +123,5 @@ namespace ICSharpCode.ILSpy.Search return null; } } - - SearchResult ResultFromEntity(IEntity item) - { - var declaringType = item.DeclaringTypeDefinition; - return new SearchResult { - Member = item, - Fitness = CalculateFitness(item), - Name = GetLanguageSpecificName(item), - Location = declaringType != null ? language.TypeToString(declaringType, includeNamespace: true) : item.Namespace, - Assembly = item.ParentModule.FullAssemblyName, - ToolTip = item.ParentModule.PEFile?.FileName - }; - } - - 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; - } - - string GetLanguageSpecificName(IEntity member) - { - switch (member) { - case ITypeDefinition t: - return language.TypeToString(t, false); - case IField f: - return language.FieldToString(f, true, false, false); - case IProperty p: - return language.PropertyToString(p, true, false, false); - case IMethod m: - return language.MethodToString(m, true, false, false); - case IEvent e: - return language.EventToString(e, true, false, false); - default: - throw new NotSupportedException(member?.GetType() + " not supported!"); - } - } - - internal static ImageSource GetIcon(IEntity member) - { - switch (member) { - case ITypeDefinition t: - return TypeTreeNode.GetIcon(t); - case IField f: - return FieldTreeNode.GetIcon(f); - case IProperty p: - return PropertyTreeNode.GetIcon(p); - case IMethod m: - return MethodTreeNode.GetIcon(m); - case IEvent e: - return EventTreeNode.GetIcon(e); - default: - throw new NotSupportedException(member?.GetType() + " not supported!"); - } - } } } diff --git a/ILSpy/Search/LiteralSearchStrategy.cs b/ILSpy/Search/LiteralSearchStrategy.cs index 83c802d6a..c369d8f6f 100644 --- a/ILSpy/Search/LiteralSearchStrategy.cs +++ b/ILSpy/Search/LiteralSearchStrategy.cs @@ -15,7 +15,7 @@ using System.Collections.Concurrent; namespace ICSharpCode.ILSpy.Search { - class LiteralSearchStrategy : AbstractSearchStrategy + class LiteralSearchStrategy : AbstractEntitySearchStrategy { readonly TypeCode searchTermLiteralType; readonly object searchTermLiteralValue; diff --git a/ILSpy/Search/MemberSearchStrategy.cs b/ILSpy/Search/MemberSearchStrategy.cs index 4a631986b..fa1a11662 100644 --- a/ILSpy/Search/MemberSearchStrategy.cs +++ b/ILSpy/Search/MemberSearchStrategy.cs @@ -6,7 +6,7 @@ using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpy.Search { - class MemberSearchStrategy : AbstractSearchStrategy + class MemberSearchStrategy : AbstractEntitySearchStrategy { readonly MemberSearchKind searchKind; diff --git a/ILSpy/Search/MetadataTokenSearchStrategy.cs b/ILSpy/Search/MetadataTokenSearchStrategy.cs index 7ccec19b2..b2eec37df 100644 --- a/ILSpy/Search/MetadataTokenSearchStrategy.cs +++ b/ILSpy/Search/MetadataTokenSearchStrategy.cs @@ -9,7 +9,7 @@ using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpy.Search { - class MetadataTokenSearchStrategy : AbstractSearchStrategy + class MetadataTokenSearchStrategy : AbstractEntitySearchStrategy { readonly EntityHandle searchTermToken; diff --git a/ILSpy/Search/ResourceSearchStrategy.cs b/ILSpy/Search/ResourceSearchStrategy.cs new file mode 100644 index 000000000..b3da8b8f7 --- /dev/null +++ b/ILSpy/Search/ResourceSearchStrategy.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Threading; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.Util; +using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.TreeView; + +namespace ICSharpCode.ILSpy.Search +{ + class ResourceSearchStrategy : AbstractSearchStrategy + { + protected readonly bool searchInside; + protected readonly ApiVisibility apiVisibility; + + public ResourceSearchStrategy(ApiVisibility apiVisibility, IProducerConsumerCollection resultQueue, string term) + : this(apiVisibility, resultQueue, new[] { term }) + { + } + + public ResourceSearchStrategy(ApiVisibility apiVisibility, IProducerConsumerCollection resultQueue, string[] terms) + : base(resultQueue, terms) + { + this.apiVisibility = apiVisibility; + this.searchInside = true; + } + + protected bool CheckVisibility(Resource resource) + { + if (apiVisibility == ApiVisibility.All) + return true; + + if (apiVisibility == ApiVisibility.PublicOnly && (resource.Attributes & ManifestResourceAttributes.VisibilityMask) == ManifestResourceAttributes.Private) + return false; + + return true; + } + + public override void Search(PEFile module, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + var resourcesNode = new ResourceListTreeNode(module); + + resourcesNode.EnsureLazyChildren(); + foreach (var node in resourcesNode.Children) + Search(module, null, resourcesNode, node, cancellationToken); + return; + } + + void Search(PEFile module, object reference, SharpTreeNode parent, SharpTreeNode node, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (node is ResourceTreeNode treeNode) { + if (!CheckVisibility(treeNode.Resource)) + return; + reference = treeNode.Resource; + } + + if (node.Text != null && IsMatch((string)node.Text)) + OnFoundResult(module, reference, node, parent); + + if (!searchInside) + return; + + node.EnsureLazyChildren(); + foreach (var child in node.Children) + Search(module, reference, node, child, cancellationToken); + } + + void OnFoundResult(PEFile module, object reference, SharpTreeNode node, SharpTreeNode parent) + { + var result = new SearchResult { + Reference = reference, + Fitness = 1f, + Image = (ImageSource)node.Icon, + Name = (string)node.Text, + LocationImage = (ImageSource)parent.Icon, + Location = (string)parent.Text, + AssemblyImage = Images.Assembly, + Assembly = module.Name, + ToolTip = module.FileName, + }; + OnFoundResult(result); + } + } +} diff --git a/ILSpy/Search/SearchPane.cs b/ILSpy/Search/SearchPane.cs index 3eb492740..5901b47ca 100644 --- a/ILSpy/Search/SearchPane.cs +++ b/ILSpy/Search/SearchPane.cs @@ -76,6 +76,7 @@ namespace ICSharpCode.ILSpy searchModeComboBox.Items.Add(new { Image = Images.Event, Name = "Event" }); searchModeComboBox.Items.Add(new { Image = Images.Literal, Name = "Constant" }); searchModeComboBox.Items.Add(new { Image = Images.Library, Name = "Metadata Token" }); + searchModeComboBox.Items.Add(new { Image = Images.ResourceResourcesFile, Name = "Resource" }); ContextMenuProvider.Add(listBox); MainWindow.Instance.CurrentAssemblyListChanged += MainWindow_Instance_CurrentAssemblyListChanged; @@ -263,7 +264,7 @@ namespace ICSharpCode.ILSpy void JumpToSelectedItem() { if (listBox.SelectedItem is SearchResult result) { - MainWindow.Instance.JumpToReference(result.Member); + MainWindow.Instance.JumpToReference(result.Reference); } } @@ -342,6 +343,9 @@ namespace ICSharpCode.ILSpy if (searchTerm[0].StartsWith("@", StringComparison.Ordinal)) return new MetadataTokenSearchStrategy(language, apiVisibility, resultQueue, searchTerm[0].Substring(1)); + + if (searchTerm[0].StartsWith("r:", StringComparison.Ordinal)) + return new ResourceSearchStrategy(apiVisibility, resultQueue, searchTerm[0].Substring(2)); } switch (searchMode) @@ -364,6 +368,8 @@ namespace ICSharpCode.ILSpy return new MemberSearchStrategy(language, apiVisibility, resultQueue, searchTerm, MemberSearchKind.Event); case SearchMode.Token: return new MetadataTokenSearchStrategy(language, apiVisibility, resultQueue, searchTerm); + case SearchMode.Resource: + return new ResourceSearchStrategy(apiVisibility, resultQueue, searchTerm); } return null; @@ -371,14 +377,16 @@ namespace ICSharpCode.ILSpy } } - public sealed class SearchResult : IMemberTreeNode + public sealed class SearchResult // : IMemberTreeNode { + public static readonly System.Collections.Generic.IComparer Comparer = new SearchResultComparer(); + ImageSource image; ImageSource locationImage; public static readonly IComparer Comparer = new SearchResultComparer(); - public IEntity Member { get; set; } + public object Reference { get; set; } public float Fitness { get; set; } public string Assembly { get; set; } @@ -447,6 +455,7 @@ namespace ICSharpCode.ILSpy Property, Event, Literal, - Token + Token, + Resource } } \ No newline at end of file From 8dbf1b6a17259ca2ede19eab7a5ba92d1e102196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Ku=C4=8Dera?= Date: Sun, 8 Sep 2019 00:02:18 +0100 Subject: [PATCH 02/62] Basic resource search strategy fitness --- ILSpy/Search/ResourceSearchStrategy.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ILSpy/Search/ResourceSearchStrategy.cs b/ILSpy/Search/ResourceSearchStrategy.cs index b3da8b8f7..5faea4ba4 100644 --- a/ILSpy/Search/ResourceSearchStrategy.cs +++ b/ILSpy/Search/ResourceSearchStrategy.cs @@ -76,11 +76,12 @@ namespace ICSharpCode.ILSpy.Search void OnFoundResult(PEFile module, object reference, SharpTreeNode node, SharpTreeNode parent) { + var name = (string)node.Text; var result = new SearchResult { Reference = reference, - Fitness = 1f, + Fitness = 1.0f / name.Length, Image = (ImageSource)node.Icon, - Name = (string)node.Text, + Name = name, LocationImage = (ImageSource)parent.Icon, Location = (string)parent.Text, AssemblyImage = Images.Assembly, From 146b3e7f4b276cd02813dc799634a75cf82393cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Ku=C4=8Dera?= Date: Sun, 22 Sep 2019 14:13:17 +0100 Subject: [PATCH 03/62] Accomodate result image caching, split search result, jump to resource children, clean-up --- ILSpy/ILSpy.csproj | 11 +-- ILSpy/MainWindow.xaml.cs | 2 + ILSpy/Search/AbstractEntitySearchStrategy.cs | 9 +- ILSpy/Search/AbstractSearchStrategy.cs | 6 +- ILSpy/Search/ResourceSearchStrategy.cs | 25 +++-- ILSpy/Search/SearchPane.cs | 58 +----------- ILSpy/Search/SearchResult.cs | 97 ++++++++++++++++++++ ILSpy/TreeNodes/AssemblyListTreeNode.cs | 12 ++- 8 files changed, 131 insertions(+), 89 deletions(-) create mode 100644 ILSpy/Search/SearchResult.cs diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 07a789a4a..072b2c3ea 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -167,10 +167,7 @@ True Resources.resx - - Code - MSBuild:Compile - + @@ -208,15 +205,13 @@ - - Code - MSBuild:Compile - + SearchPane.xaml + diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 059d9ec14..d388d2887 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -713,6 +713,8 @@ namespace ICSharpCode.ILSpy return assemblyListTreeNode.FindAssemblyNode(asm); case Resource res: return assemblyListTreeNode.FindResourceNode(res); + case ValueTuple resName: + return assemblyListTreeNode.FindResourceNode(resName.Item1, resName.Item2); case ITypeDefinition type: return assemblyListTreeNode.FindTypeNode(type); case IField fd: diff --git a/ILSpy/Search/AbstractEntitySearchStrategy.cs b/ILSpy/Search/AbstractEntitySearchStrategy.cs index 57382846b..e969c69c3 100644 --- a/ILSpy/Search/AbstractEntitySearchStrategy.cs +++ b/ILSpy/Search/AbstractEntitySearchStrategy.cs @@ -52,14 +52,11 @@ namespace ICSharpCode.ILSpy.Search SearchResult ResultFromEntity(IEntity item) { var declaringType = item.DeclaringTypeDefinition; - return new SearchResult { - Reference = item, + return new MemberSearchResult { + Member = item, Fitness = CalculateFitness(item), - Image = GetIcon(item), Name = GetLanguageSpecificName(item), - LocationImage = declaringType != null ? TypeTreeNode.GetIcon(declaringType) : Images.Namespace, Location = declaringType != null ? language.TypeToString(declaringType, includeNamespace: true) : item.Namespace, - AssemblyImage = Images.Assembly, Assembly = item.ParentModule.FullAssemblyName, ToolTip = item.ParentModule.PEFile?.FileName }; @@ -104,7 +101,7 @@ namespace ICSharpCode.ILSpy.Search } } - ImageSource GetIcon(IEntity member) + static internal ImageSource GetIcon(IEntity member) { switch (member) { case ITypeDefinition t: diff --git a/ILSpy/Search/AbstractSearchStrategy.cs b/ILSpy/Search/AbstractSearchStrategy.cs index b87bc3aed..7af565262 100644 --- a/ILSpy/Search/AbstractSearchStrategy.cs +++ b/ILSpy/Search/AbstractSearchStrategy.cs @@ -40,17 +40,17 @@ namespace ICSharpCode.ILSpy.Search public abstract void Search(PEFile module, CancellationToken cancellationToken); - protected virtual bool IsMatch(string entityName) + protected virtual bool IsMatch(string name) { if (regex != null) { - return regex.IsMatch(entityName); + return regex.IsMatch(name); } for (int i = 0; i < searchTerm.Length; ++i) { // How to handle overlapping matches? var term = searchTerm[i]; if (string.IsNullOrEmpty(term)) continue; - string text = entityName; + string text = name; switch (term[0]) { case '+': // must contain term = term.Substring(1); diff --git a/ILSpy/Search/ResourceSearchStrategy.cs b/ILSpy/Search/ResourceSearchStrategy.cs index 5faea4ba4..5824afd96 100644 --- a/ILSpy/Search/ResourceSearchStrategy.cs +++ b/ILSpy/Search/ResourceSearchStrategy.cs @@ -46,46 +46,43 @@ namespace ICSharpCode.ILSpy.Search { cancellationToken.ThrowIfCancellationRequested(); var resourcesNode = new ResourceListTreeNode(module); - - resourcesNode.EnsureLazyChildren(); - foreach (var node in resourcesNode.Children) - Search(module, null, resourcesNode, node, cancellationToken); - return; + + foreach (Resource resource in module.Resources) + Search(module, resource, resourcesNode, ResourceTreeNode.Create(resource), cancellationToken); } - void Search(PEFile module, object reference, SharpTreeNode parent, SharpTreeNode node, CancellationToken cancellationToken) + void Search(PEFile module, Resource resource, SharpTreeNode parent, SharpTreeNode node, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (node is ResourceTreeNode treeNode) { if (!CheckVisibility(treeNode.Resource)) return; - reference = treeNode.Resource; + resource = treeNode.Resource; } if (node.Text != null && IsMatch((string)node.Text)) - OnFoundResult(module, reference, node, parent); + OnFoundResult(module, resource, node, parent); if (!searchInside) return; node.EnsureLazyChildren(); foreach (var child in node.Children) - Search(module, reference, node, child, cancellationToken); + Search(module, resource, node, child, cancellationToken); } - void OnFoundResult(PEFile module, object reference, SharpTreeNode node, SharpTreeNode parent) + void OnFoundResult(PEFile module, Resource resource, SharpTreeNode node, SharpTreeNode parent) { var name = (string)node.Text; - var result = new SearchResult { - Reference = reference, + var result = new ResourceSearchResult { + Resource = resource, Fitness = 1.0f / name.Length, Image = (ImageSource)node.Icon, Name = name, LocationImage = (ImageSource)parent.Icon, Location = (string)parent.Text, - AssemblyImage = Images.Assembly, - Assembly = module.Name, + Assembly = module.FullName, ToolTip = module.FileName, }; OnFoundResult(result); diff --git a/ILSpy/Search/SearchPane.cs b/ILSpy/Search/SearchPane.cs index 5901b47ca..f911fd3a6 100644 --- a/ILSpy/Search/SearchPane.cs +++ b/ILSpy/Search/SearchPane.cs @@ -32,7 +32,6 @@ using System.Windows.Media; using System.Windows.Threading; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.Search; -using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy { @@ -76,7 +75,7 @@ namespace ICSharpCode.ILSpy searchModeComboBox.Items.Add(new { Image = Images.Event, Name = "Event" }); searchModeComboBox.Items.Add(new { Image = Images.Literal, Name = "Constant" }); searchModeComboBox.Items.Add(new { Image = Images.Library, Name = "Metadata Token" }); - searchModeComboBox.Items.Add(new { Image = Images.ResourceResourcesFile, Name = "Resource" }); + searchModeComboBox.Items.Add(new { Image = Images.Resource, Name = "Resource" }); ContextMenuProvider.Add(listBox); MainWindow.Instance.CurrentAssemblyListChanged += MainWindow_Instance_CurrentAssemblyListChanged; @@ -377,61 +376,6 @@ namespace ICSharpCode.ILSpy } } - public sealed class SearchResult // : IMemberTreeNode - { - public static readonly System.Collections.Generic.IComparer Comparer = new SearchResultComparer(); - - ImageSource image; - ImageSource locationImage; - - public static readonly IComparer Comparer = new SearchResultComparer(); - - public object Reference { get; set; } - public float Fitness { get; set; } - - public string Assembly { get; set; } - public string Location { get; set; } - public string Name { get; set; } - public object ToolTip { get; set; } - - public ImageSource Image { - get { - if (image == null) { - image = AbstractSearchStrategy.GetIcon(Member); - } - return image; - } - } - - public ImageSource LocationImage { - get { - if (locationImage == null) { - locationImage = Member.DeclaringTypeDefinition != null ? TypeTreeNode.GetIcon(Member.DeclaringTypeDefinition) : Images.Namespace; - } - return locationImage; - } - } - - public ImageSource AssemblyImage { - get { - return Images.Assembly; - } - } - - public override string ToString() - { - return Name; - } - - class SearchResultComparer : System.Collections.Generic.IComparer - { - public int Compare(SearchResult x, SearchResult y) - { - return StringComparer.Ordinal.Compare(x?.Name ?? "", y?.Name ?? ""); - } - } - } - [ExportMainMenuCommand(Menu = nameof(Properties.Resources._View), Header =nameof(Properties.Resources.Search), MenuIcon = "Images/Search", MenuCategory = nameof(Properties.Resources.View), MenuOrder = 100)] [ExportToolbarCommand(ToolTip = nameof(Properties.Resources.SearchCtrlShiftFOrCtrlE), ToolbarIcon = "Images/Search", ToolbarCategory = nameof(Properties.Resources.View), ToolbarOrder = 100)] sealed class ShowSearchCommand : CommandWrapper diff --git a/ILSpy/Search/SearchResult.cs b/ILSpy/Search/SearchResult.cs new file mode 100644 index 000000000..eb78c0bef --- /dev/null +++ b/ILSpy/Search/SearchResult.cs @@ -0,0 +1,97 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Windows.Media; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.ILSpy.Search; +using ICSharpCode.ILSpy.TreeNodes; + +namespace ICSharpCode.ILSpy +{ + public class SearchResult + { + public static readonly IComparer Comparer = new SearchResultComparer(); + + public virtual object Reference { + get { + return null; + } + } + + public float Fitness { get; set; } + + public string Name { get; set; } + public string Location { get; set; } + public string Assembly { get; set; } + public object ToolTip { get; set; } + public virtual ImageSource Image { get; set; } + public virtual ImageSource LocationImage { get; set; } + + public ImageSource AssemblyImage { + get { + return Images.Assembly; + } + } + + public override string ToString() + { + return Name; + } + + class SearchResultComparer : IComparer + { + public int Compare(SearchResult x, SearchResult y) + { + return StringComparer.Ordinal.Compare(x?.Name ?? "", y?.Name ?? ""); + } + } + } + + public class MemberSearchResult : SearchResult + { + public IEntity Member { get; set; } + public override object Reference => Member; + + public override ImageSource Image { + get { + if (base.Image == null) { + base.Image = AbstractEntitySearchStrategy.GetIcon(Member); + } + return base.Image; + } + } + + public override ImageSource LocationImage { + get { + if (base.LocationImage == null) { + base.LocationImage = Member.DeclaringTypeDefinition != null ? TypeTreeNode.GetIcon(Member.DeclaringTypeDefinition) : Images.Namespace; + } + return base.LocationImage; + } + } + } + + public class ResourceSearchResult : SearchResult + { + public Resource Resource { get; set; } + public override object Reference => ValueTuple.Create(Resource, Name); + } +} \ No newline at end of file diff --git a/ILSpy/TreeNodes/AssemblyListTreeNode.cs b/ILSpy/TreeNodes/AssemblyListTreeNode.cs index e53a3a883..b579b2930 100644 --- a/ILSpy/TreeNodes/AssemblyListTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyListTreeNode.cs @@ -155,7 +155,7 @@ namespace ICSharpCode.ILSpy.TreeNodes #region Find*Node public ILSpyTreeNode FindResourceNode(Resource resource) { - if (resource == null) + if (resource == null || resource.IsNil) return null; foreach (AssemblyTreeNode node in this.Children) { @@ -177,6 +177,16 @@ namespace ICSharpCode.ILSpy.TreeNodes return null; } + public ILSpyTreeNode FindResourceNode(Resource resource, string name) + { + var resourceNode = FindResourceNode(resource); + if (resourceNode == null || name == null || name.Equals(resourceNode.Text)) + return resourceNode; + + resourceNode.EnsureLazyChildren(); + return resourceNode.Children.OfType().Where(x => name.Equals(x.Text)).FirstOrDefault() ?? resourceNode; + } + public AssemblyTreeNode FindAssemblyNode(IModule module) { return FindAssemblyNode(module.PEFile); From 486f6f6c8a7b1ac96d33f47458086db77855af9b Mon Sep 17 00:00:00 2001 From: Andreas Weizel Date: Sun, 22 Sep 2019 20:12:58 +0200 Subject: [PATCH 04/62] Completed some missing sources in list of ILSpy icons --- ILSpy/Images/README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ILSpy/Images/README.md b/ILSpy/Images/README.md index 0bf5fadf4..e258db488 100644 --- a/ILSpy/Images/README.md +++ b/ILSpy/Images/README.md @@ -14,7 +14,7 @@ Icons used in ILSpy: | Constructor | x | x | based on VS 2017 Icon Pack (Method) using a different colour | | | Copy | x | x | VS 2017 Icon Pack (Copy) | | | Delegate | x | x | VS 2017 Icon Pack (Delegate) | | -| Delete | x | x | | | +| Delete | x | x | VS 2017 Icon Pack (Remove_color) | | | Enum | x | x | VS 2017 Icon Pack (Enumerator) | | | EnumValue | x | x | VS 2017 Icon Pack (EnumItem) | | | Event | x | x | VS 2017 Icon Pack (Event) | | @@ -45,10 +45,10 @@ Icons used in ILSpy: | Property | x | x | VS 2017 Icon Pack (Property) | | | ReferenceFolder | x | x | combined VS 2017 Icon Pack (Reference) two times | | | Refresh | x | x | VS 2017 Icon Pack (Refresh) | | -| Resource | x | x | | | -| ResourceImage | x | x | | | -| ResourceResourcesFile | x | x | | | -| ResourceXml | x | x | | | +| Resource | x | x | VS 2017 Icon Pack (Document) | | +| ResourceImage | x | x | VS 2017 Icon Pack (Image) | | +| ResourceResourcesFile | x | x | VS 2017 Icon Pack (LocalResources) | | +| ResourceXml | x | x | VS 2017 Icon Pack (XMLFile) | | | ResourceXsd | x | x | combined VS 2017 Icon Pack (XMLSchema) with the "file symbol in ResourceXslt | | | ResourceXsl | x | x | VS 2017 Icon Pack (XMLTransformation) | | | ResourceXslt | x | x | VS 2017 Icon Pack (XSLTTemplate) | | @@ -57,12 +57,12 @@ Icons used in ILSpy: | SearchMsdn | x | x | based on VS 2017 Icon Pack (Search) + Microsoft Logo | | | ShowAll | x | x | combined PublicOnly, OverlayPrivate, OverlayProtected, OverlayInternal | | | ShowPrivateInternal | x | x | combined OverlayPrivate and OverlayInternal | | -| ShowPublicOnly | x | x | | | +| ShowPublicOnly | x | x | VS 2017 Icon Pack (Type) | | | Sort | x | x | VS 2017 Icon Pack (SortAscending) | | | Struct | x | x | VS 2017 Icon Pack (Structure) | | -| SubTypes | x | x | | | -| SuperTypes | x | x | | | -| ViewCode | x | x | | | +| SubTypes | x | x | based on VS 2017 Icon Pack (BaseType) rotated +90° | | +| SuperTypes | x | x | based on VS 2017 Icon Pack (BaseType) rotated -90° | | +| ViewCode | x | x | VS 2017 Icon Pack (GoToSourceCode) | | | VirtualMethod | x | x | combined VS 2017 Icon Pack (Method) two times | | | Warning | x | x | VS 2017 Icon Pack (StatusWarning) | | From 06d343bd7e3bf1ebd863822102dea9db9c8714a5 Mon Sep 17 00:00:00 2001 From: Andreas Weizel Date: Sun, 22 Sep 2019 20:28:44 +0200 Subject: [PATCH 05/62] Removed Viewbox from Copy icon causing an exception when opening context menu --- ILSpy/Images/Copy.xaml | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/ILSpy/Images/Copy.xaml b/ILSpy/Images/Copy.xaml index 155cead10..1ec44e951 100644 --- a/ILSpy/Images/Copy.xaml +++ b/ILSpy/Images/Copy.xaml @@ -1,20 +1,10 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + From a53cf0a0e251a590ba7de4947819ae89db192fe1 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Mon, 23 Sep 2019 09:39:04 +0200 Subject: [PATCH 06/62] Addin: Change Package References --- ILSpy.AddIn/ILSpy.AddIn.csproj | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/ILSpy.AddIn/ILSpy.AddIn.csproj b/ILSpy.AddIn/ILSpy.AddIn.csproj index c0d961a09..1f26eaea4 100644 --- a/ILSpy.AddIn/ILSpy.AddIn.csproj +++ b/ILSpy.AddIn/ILSpy.AddIn.csproj @@ -44,27 +44,15 @@ - - + - - - - - - - - - - - From c9b1a85282413cde666d1329298e9dce7ca4dfb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Ku=C4=8Dera?= Date: Sun, 22 Sep 2019 16:35:40 +0100 Subject: [PATCH 07/62] Add assembly search strategy --- ILSpy/ILSpy.csproj | 1 + ILSpy/Search/AssemblySearchStrategy.cs | 49 ++++++++++++++++++++++++++ ILSpy/Search/SearchPane.cs | 9 ++++- ILSpy/Search/SearchResult.cs | 9 +++++ 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 ILSpy/Search/AssemblySearchStrategy.cs diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 072b2c3ea..d4edc73a3 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -168,6 +168,7 @@ Resources.resx + diff --git a/ILSpy/Search/AssemblySearchStrategy.cs b/ILSpy/Search/AssemblySearchStrategy.cs new file mode 100644 index 000000000..fc27fa905 --- /dev/null +++ b/ILSpy/Search/AssemblySearchStrategy.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Threading; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.Util; +using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.TreeView; + +namespace ICSharpCode.ILSpy.Search +{ + class AssemblySearchStrategy : AbstractSearchStrategy + { + public AssemblySearchStrategy(IProducerConsumerCollection resultQueue, string term) + : this(resultQueue, new[] { term }) + { + } + + public AssemblySearchStrategy(IProducerConsumerCollection resultQueue, string[] terms) + : base(resultQueue, terms) + { + } + + public override void Search(PEFile module, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + if (IsMatch(module.FullName)) + OnFoundResult(module); + } + + void OnFoundResult(PEFile module) + { + var result = new AssemblySearchResult { + Module = module, + Fitness = 1.0f / module.Name.Length, + Name = module.Name, + Location = module.FileName, + Assembly = module.FullName, + ToolTip = module.FileName, + }; + OnFoundResult(result); + } + } +} diff --git a/ILSpy/Search/SearchPane.cs b/ILSpy/Search/SearchPane.cs index f911fd3a6..e4139836f 100644 --- a/ILSpy/Search/SearchPane.cs +++ b/ILSpy/Search/SearchPane.cs @@ -76,6 +76,7 @@ namespace ICSharpCode.ILSpy searchModeComboBox.Items.Add(new { Image = Images.Literal, Name = "Constant" }); searchModeComboBox.Items.Add(new { Image = Images.Library, Name = "Metadata Token" }); searchModeComboBox.Items.Add(new { Image = Images.Resource, Name = "Resource" }); + searchModeComboBox.Items.Add(new { Image = Images.Assembly, Name = "Assembly" }); ContextMenuProvider.Add(listBox); MainWindow.Instance.CurrentAssemblyListChanged += MainWindow_Instance_CurrentAssemblyListChanged; @@ -345,6 +346,9 @@ namespace ICSharpCode.ILSpy if (searchTerm[0].StartsWith("r:", StringComparison.Ordinal)) return new ResourceSearchStrategy(apiVisibility, resultQueue, searchTerm[0].Substring(2)); + + if (searchTerm[0].StartsWith("a:", StringComparison.Ordinal)) + return new AssemblySearchStrategy(resultQueue, searchTerm[0].Substring(2)); } switch (searchMode) @@ -369,6 +373,8 @@ namespace ICSharpCode.ILSpy return new MetadataTokenSearchStrategy(language, apiVisibility, resultQueue, searchTerm); case SearchMode.Resource: return new ResourceSearchStrategy(apiVisibility, resultQueue, searchTerm); + case SearchMode.Assembly: + return new AssemblySearchStrategy(resultQueue, searchTerm); } return null; @@ -400,6 +406,7 @@ namespace ICSharpCode.ILSpy Event, Literal, Token, - Resource + Resource, + Assembly } } \ No newline at end of file diff --git a/ILSpy/Search/SearchResult.cs b/ILSpy/Search/SearchResult.cs index eb78c0bef..d632129f2 100644 --- a/ILSpy/Search/SearchResult.cs +++ b/ILSpy/Search/SearchResult.cs @@ -94,4 +94,13 @@ namespace ICSharpCode.ILSpy public Resource Resource { get; set; } public override object Reference => ValueTuple.Create(Resource, Name); } + + public class AssemblySearchResult : SearchResult + { + public PEFile Module { get; set; } + public override object Reference => Module; + + public override ImageSource Image => Images.Assembly; + public override ImageSource LocationImage => Images.Library; + } } \ No newline at end of file From b342dd6b117b8b406123f43d39fbdc398d0ee357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Ku=C4=8Dera?= Date: Mon, 23 Sep 2019 13:15:58 +0100 Subject: [PATCH 08/62] Assembly search kind, default to short name --- .../Metadata/MetadataExtensions.cs | 11 +++- ILSpy/Search/AssemblySearchStrategy.cs | 60 +++++++++++++++++-- ILSpy/Search/SearchPane.cs | 25 +++++++- 3 files changed, 89 insertions(+), 7 deletions(-) diff --git a/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs b/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs index 561c25704..7d9d2ca5b 100644 --- a/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs +++ b/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs @@ -49,7 +49,7 @@ namespace ICSharpCode.Decompiler.Metadata return publicKeyTokenBytes.TakeLast(8).Reverse().ToHexString(8); } - public static string GetFullAssemblyName(this MetadataReader reader) + public static string GetPublicKeyToken(this MetadataReader reader) { if (!reader.IsAssembly) return string.Empty; @@ -59,6 +59,15 @@ namespace ICSharpCode.Decompiler.Metadata // AssemblyFlags.PublicKey does not apply to assembly definitions publicKey = CalculatePublicKeyToken(asm.PublicKey, reader); } + return publicKey; + } + + public static string GetFullAssemblyName(this MetadataReader reader) + { + if (!reader.IsAssembly) + return string.Empty; + var asm = reader.GetAssemblyDefinition(); + string publicKey = reader.GetPublicKeyToken(); return $"{reader.GetString(asm.Name)}, " + $"Version={asm.Version}, " + $"Culture={(asm.Culture.IsNil ? "neutral" : reader.GetString(asm.Culture))}, " + diff --git a/ILSpy/Search/AssemblySearchStrategy.cs b/ILSpy/Search/AssemblySearchStrategy.cs index fc27fa905..6adaebfb1 100644 --- a/ILSpy/Search/AssemblySearchStrategy.cs +++ b/ILSpy/Search/AssemblySearchStrategy.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Reflection; +using System.Reflection.Metadata; using System.Threading; using System.Windows.Media; using System.Windows.Media.Imaging; @@ -16,23 +17,62 @@ namespace ICSharpCode.ILSpy.Search { class AssemblySearchStrategy : AbstractSearchStrategy { - public AssemblySearchStrategy(IProducerConsumerCollection resultQueue, string term) - : this(resultQueue, new[] { term }) + readonly AssemblySearchKind searchKind; + + public AssemblySearchStrategy(string term, IProducerConsumerCollection resultQueue, AssemblySearchKind searchKind) + : this(resultQueue, new[] { term }, searchKind) { } - public AssemblySearchStrategy(IProducerConsumerCollection resultQueue, string[] terms) + public AssemblySearchStrategy(IProducerConsumerCollection resultQueue, string[] terms, AssemblySearchKind searchKind) : base(resultQueue, terms) { + this.searchKind = searchKind; } public override void Search(PEFile module, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - if (IsMatch(module.FullName)) + string name = GetNameToMatch(module); + if (IsMatch(name)) OnFoundResult(module); } + string GetNameToMatch(PEFile module) + { + switch (searchKind) { + case AssemblySearchKind.FullName: + return module.FullName; + case AssemblySearchKind.Name: + return module.Name; + case AssemblySearchKind.FileName: + return module.FileName; + } + + if (!module.IsAssembly) + return null; + + var metadata = module.Metadata; + var definition = module.Metadata.GetAssemblyDefinition(); + + switch (searchKind) { + case AssemblySearchKind.Culture: + if (definition.Culture.IsNil) + return "neutral"; + return metadata.GetString(definition.Culture); + case AssemblySearchKind.Version: + return definition.Version.ToString(); + case AssemblySearchKind.PublicKey: + return module.Metadata.GetPublicKeyToken(); + case AssemblySearchKind.HashAlgorithm: + return definition.HashAlgorithm.ToString(); + case AssemblySearchKind.Flags: + return definition.Flags.ToString(); + } + + return null; + } + void OnFoundResult(PEFile module) { var result = new AssemblySearchResult { @@ -46,4 +86,16 @@ namespace ICSharpCode.ILSpy.Search OnFoundResult(result); } } + + enum AssemblySearchKind + { + Name, + FullName, + FileName, + Culture, + Version, + PublicKey, + HashAlgorithm, + Flags + } } diff --git a/ILSpy/Search/SearchPane.cs b/ILSpy/Search/SearchPane.cs index e4139836f..1b0aa24ac 100644 --- a/ILSpy/Search/SearchPane.cs +++ b/ILSpy/Search/SearchPane.cs @@ -348,7 +348,28 @@ namespace ICSharpCode.ILSpy return new ResourceSearchStrategy(apiVisibility, resultQueue, searchTerm[0].Substring(2)); if (searchTerm[0].StartsWith("a:", StringComparison.Ordinal)) - return new AssemblySearchStrategy(resultQueue, searchTerm[0].Substring(2)); + return new AssemblySearchStrategy(searchTerm[0].Substring(2), resultQueue, AssemblySearchKind.Name); + + if (searchTerm[0].StartsWith("afn:", StringComparison.Ordinal)) + return new AssemblySearchStrategy(searchTerm[0].Substring(4), resultQueue, AssemblySearchKind.FullName); + + if (searchTerm[0].StartsWith("af:", StringComparison.Ordinal)) + return new AssemblySearchStrategy(searchTerm[0].Substring(3), resultQueue, AssemblySearchKind.FileName); + + if (searchTerm[0].StartsWith("ac:", StringComparison.Ordinal)) + return new AssemblySearchStrategy(searchTerm[0].Substring(3), resultQueue, AssemblySearchKind.Culture); + + if (searchTerm[0].StartsWith("av:", StringComparison.Ordinal)) + return new AssemblySearchStrategy(searchTerm[0].Substring(3), resultQueue, AssemblySearchKind.Version); + + if (searchTerm[0].StartsWith("apk:", StringComparison.Ordinal)) + return new AssemblySearchStrategy(searchTerm[0].Substring(4), resultQueue, AssemblySearchKind.PublicKey); + + if (searchTerm[0].StartsWith("aha:", StringComparison.Ordinal)) + return new AssemblySearchStrategy(searchTerm[0].Substring(4), resultQueue, AssemblySearchKind.HashAlgorithm); + + if (searchTerm[0].StartsWith("afl:", StringComparison.Ordinal)) + return new AssemblySearchStrategy(searchTerm[0].Substring(4), resultQueue, AssemblySearchKind.Flags); } switch (searchMode) @@ -374,7 +395,7 @@ namespace ICSharpCode.ILSpy case SearchMode.Resource: return new ResourceSearchStrategy(apiVisibility, resultQueue, searchTerm); case SearchMode.Assembly: - return new AssemblySearchStrategy(resultQueue, searchTerm); + return new AssemblySearchStrategy(resultQueue, searchTerm, AssemblySearchKind.Name); } return null; From 1f3cb2c0768f4900aa927b1bc3fc3b4affb5ee73 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Tue, 24 Sep 2019 17:31:56 +0200 Subject: [PATCH 09/62] Update to AvalonEdit 6.0 RTM Update AppVeyor script to no longer install netcore3 SDK (VS 16.3 includes that) Set global.json to netcore3 RTM --- ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj | 2 +- ILSpy/ILSpy.csproj | 2 +- appveyor.yml | 2 -- global.json | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj b/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj index ee81f61fd..7d1208b65 100644 --- a/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj +++ b/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj @@ -37,7 +37,7 @@ - + diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 072b2c3ea..1dd22579f 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -48,7 +48,7 @@ - + diff --git a/appveyor.yml b/appveyor.yml index c3efe5a38..49c50f75c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,7 +7,6 @@ configuration: image: Visual Studio 2019 install: -- cmd: choco install dotnetcore-sdk --pre - git submodule update --init --recursive - ps: .\BuildTools\appveyor-install.ps1 @@ -43,7 +42,6 @@ for: - branches: only: - master - - 3.2.x artifacts: - path: ILSpy_binaries.zip name: ILSpy %APPVEYOR_REPO_BRANCH% %ILSPY_VERSION_NUMBER% binaries diff --git a/global.json b/global.json index 05c896433..6ba836210 100644 --- a/global.json +++ b/global.json @@ -3,6 +3,6 @@ "MSBuild.Sdk.Extras": "2.0.24" }, "sdk": { - "version": "3.0.100-rc1" + "version": "3.0.100" } } From 5d4a4b81daa359020314899d650c096c36ecd847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Ku=C4=8Dera?= Date: Wed, 25 Sep 2019 11:46:55 +0100 Subject: [PATCH 10/62] Assembly search option limited to NameOrFileName, FullName and FilePath --- ILSpy/Search/AssemblySearchStrategy.cs | 22 ++++++++++++++++------ ILSpy/Search/SearchPane.cs | 25 +++++-------------------- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/ILSpy/Search/AssemblySearchStrategy.cs b/ILSpy/Search/AssemblySearchStrategy.cs index 6adaebfb1..30859b3a2 100644 --- a/ILSpy/Search/AssemblySearchStrategy.cs +++ b/ILSpy/Search/AssemblySearchStrategy.cs @@ -33,19 +33,28 @@ namespace ICSharpCode.ILSpy.Search public override void Search(PEFile module, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - string name = GetNameToMatch(module); + + if (searchKind == AssemblySearchKind.NameOrFileName) { + string localName = GetNameToMatch(module, AssemblySearchKind.Name); + string fileName = Path.GetFileName(GetNameToMatch(module, AssemblySearchKind.FilePath)); + if (IsMatch(localName) || IsMatch(fileName)) + OnFoundResult(module); + return; + } + + string name = GetNameToMatch(module, searchKind); if (IsMatch(name)) OnFoundResult(module); } - string GetNameToMatch(PEFile module) + string GetNameToMatch(PEFile module, AssemblySearchKind kind) { - switch (searchKind) { + switch (kind) { case AssemblySearchKind.FullName: return module.FullName; case AssemblySearchKind.Name: return module.Name; - case AssemblySearchKind.FileName: + case AssemblySearchKind.FilePath: return module.FileName; } @@ -55,7 +64,7 @@ namespace ICSharpCode.ILSpy.Search var metadata = module.Metadata; var definition = module.Metadata.GetAssemblyDefinition(); - switch (searchKind) { + switch (kind) { case AssemblySearchKind.Culture: if (definition.Culture.IsNil) return "neutral"; @@ -89,9 +98,10 @@ namespace ICSharpCode.ILSpy.Search enum AssemblySearchKind { + NameOrFileName, Name, FullName, - FileName, + FilePath, Culture, Version, PublicKey, diff --git a/ILSpy/Search/SearchPane.cs b/ILSpy/Search/SearchPane.cs index 1b0aa24ac..bf2eda16c 100644 --- a/ILSpy/Search/SearchPane.cs +++ b/ILSpy/Search/SearchPane.cs @@ -348,28 +348,13 @@ namespace ICSharpCode.ILSpy return new ResourceSearchStrategy(apiVisibility, resultQueue, searchTerm[0].Substring(2)); if (searchTerm[0].StartsWith("a:", StringComparison.Ordinal)) - return new AssemblySearchStrategy(searchTerm[0].Substring(2), resultQueue, AssemblySearchKind.Name); - - if (searchTerm[0].StartsWith("afn:", StringComparison.Ordinal)) - return new AssemblySearchStrategy(searchTerm[0].Substring(4), resultQueue, AssemblySearchKind.FullName); + return new AssemblySearchStrategy(searchTerm[0].Substring(2), resultQueue, AssemblySearchKind.NameOrFileName); if (searchTerm[0].StartsWith("af:", StringComparison.Ordinal)) - return new AssemblySearchStrategy(searchTerm[0].Substring(3), resultQueue, AssemblySearchKind.FileName); - - if (searchTerm[0].StartsWith("ac:", StringComparison.Ordinal)) - return new AssemblySearchStrategy(searchTerm[0].Substring(3), resultQueue, AssemblySearchKind.Culture); - - if (searchTerm[0].StartsWith("av:", StringComparison.Ordinal)) - return new AssemblySearchStrategy(searchTerm[0].Substring(3), resultQueue, AssemblySearchKind.Version); - - if (searchTerm[0].StartsWith("apk:", StringComparison.Ordinal)) - return new AssemblySearchStrategy(searchTerm[0].Substring(4), resultQueue, AssemblySearchKind.PublicKey); - - if (searchTerm[0].StartsWith("aha:", StringComparison.Ordinal)) - return new AssemblySearchStrategy(searchTerm[0].Substring(4), resultQueue, AssemblySearchKind.HashAlgorithm); + return new AssemblySearchStrategy(searchTerm[0].Substring(3), resultQueue, AssemblySearchKind.FilePath); - if (searchTerm[0].StartsWith("afl:", StringComparison.Ordinal)) - return new AssemblySearchStrategy(searchTerm[0].Substring(4), resultQueue, AssemblySearchKind.Flags); + if (searchTerm[0].StartsWith("an:", StringComparison.Ordinal)) + return new AssemblySearchStrategy(searchTerm[0].Substring(3), resultQueue, AssemblySearchKind.FullName); } switch (searchMode) @@ -395,7 +380,7 @@ namespace ICSharpCode.ILSpy case SearchMode.Resource: return new ResourceSearchStrategy(apiVisibility, resultQueue, searchTerm); case SearchMode.Assembly: - return new AssemblySearchStrategy(resultQueue, searchTerm, AssemblySearchKind.Name); + return new AssemblySearchStrategy(resultQueue, searchTerm, AssemblySearchKind.NameOrFileName); } return null; From ef477c5c289703786753b4c4907dd73b3e503606 Mon Sep 17 00:00:00 2001 From: Andreas Weizel Date: Wed, 25 Sep 2019 21:41:44 +0200 Subject: [PATCH 11/62] Fix #1706: Handle unloaded projects correctly to avoid NotImplementedException --- ILSpy.AddIn/Commands/OpenILSpyCommand.cs | 35 +++++++++++++++--------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/ILSpy.AddIn/Commands/OpenILSpyCommand.cs b/ILSpy.AddIn/Commands/OpenILSpyCommand.cs index 898393005..35a2cc29f 100644 --- a/ILSpy.AddIn/Commands/OpenILSpyCommand.cs +++ b/ILSpy.AddIn/Commands/OpenILSpyCommand.cs @@ -77,10 +77,10 @@ namespace ICSharpCode.ILSpy.AddIn.Commands string assemblyName = assemblyDef.Name.Name; if (AssemblyFileFinder.IsReferenceAssembly(assemblyDef, reference.Display)) { string resolvedAssemblyFile = AssemblyFileFinder.FindAssemblyFile(assemblyDef, reference.Display); - dict.Add(assemblyName, + dict.Add(assemblyName, new DetectedReference(assemblyName, resolvedAssemblyFile, false)); } else { - dict.Add(assemblyName, + dict.Add(assemblyName, new DetectedReference(assemblyName, reference.Display, false)); } } @@ -89,7 +89,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands var roslynProject = owner.Workspace.CurrentSolution.GetProject(projectReference.ProjectId); var project = FindProject(owner.DTE.Solution.Projects.OfType(), roslynProject.FilePath); if (roslynProject != null && project != null) - dict.Add(roslynProject.AssemblyName, + dict.Add(roslynProject.AssemblyName, new DetectedReference(roslynProject.AssemblyName, Utils.GetProjectOutputAssembly(project, roslynProject), true)); } return dict; @@ -98,16 +98,25 @@ namespace ICSharpCode.ILSpy.AddIn.Commands protected EnvDTE.Project FindProject(IEnumerable projects, string projectFile) { foreach (var project in projects) { - if (project.Kind == DTEConstants.vsProjectKindSolutionItems) { - // This is a solution folder -> search in sub-projects - var subProject = FindProject( - project.ProjectItems.OfType().Select(pi => pi.SubProject).OfType(), - projectFile); - if (subProject != null) - return subProject; - } else { - if (project.FileName == projectFile) - return project; + switch (project.Kind) { + case DTEConstants.vsProjectKindSolutionItems: + // This is a solution folder -> search in sub-projects + var subProject = FindProject( + project.ProjectItems.OfType().Select(pi => pi.SubProject).OfType(), + projectFile); + if (subProject != null) + return subProject; + break; + + case DTEConstants.vsProjectKindUnmodeled: + // Skip unloaded projects completely + break; + + default: + // Match by project's file name + if (project.FileName == projectFile) + return project; + break; } } From 1d11d0efa4f3ab342f97cd3bdf90b1af4b7a67de Mon Sep 17 00:00:00 2001 From: Andreas Weizel Date: Wed, 25 Sep 2019 21:41:44 +0200 Subject: [PATCH 12/62] Fix #1706: Handle unloaded projects correctly to avoid NotImplementedException --- ILSpy.AddIn/Commands/OpenILSpyCommand.cs | 35 +++++++++++++++--------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/ILSpy.AddIn/Commands/OpenILSpyCommand.cs b/ILSpy.AddIn/Commands/OpenILSpyCommand.cs index 898393005..35a2cc29f 100644 --- a/ILSpy.AddIn/Commands/OpenILSpyCommand.cs +++ b/ILSpy.AddIn/Commands/OpenILSpyCommand.cs @@ -77,10 +77,10 @@ namespace ICSharpCode.ILSpy.AddIn.Commands string assemblyName = assemblyDef.Name.Name; if (AssemblyFileFinder.IsReferenceAssembly(assemblyDef, reference.Display)) { string resolvedAssemblyFile = AssemblyFileFinder.FindAssemblyFile(assemblyDef, reference.Display); - dict.Add(assemblyName, + dict.Add(assemblyName, new DetectedReference(assemblyName, resolvedAssemblyFile, false)); } else { - dict.Add(assemblyName, + dict.Add(assemblyName, new DetectedReference(assemblyName, reference.Display, false)); } } @@ -89,7 +89,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands var roslynProject = owner.Workspace.CurrentSolution.GetProject(projectReference.ProjectId); var project = FindProject(owner.DTE.Solution.Projects.OfType(), roslynProject.FilePath); if (roslynProject != null && project != null) - dict.Add(roslynProject.AssemblyName, + dict.Add(roslynProject.AssemblyName, new DetectedReference(roslynProject.AssemblyName, Utils.GetProjectOutputAssembly(project, roslynProject), true)); } return dict; @@ -98,16 +98,25 @@ namespace ICSharpCode.ILSpy.AddIn.Commands protected EnvDTE.Project FindProject(IEnumerable projects, string projectFile) { foreach (var project in projects) { - if (project.Kind == DTEConstants.vsProjectKindSolutionItems) { - // This is a solution folder -> search in sub-projects - var subProject = FindProject( - project.ProjectItems.OfType().Select(pi => pi.SubProject).OfType(), - projectFile); - if (subProject != null) - return subProject; - } else { - if (project.FileName == projectFile) - return project; + switch (project.Kind) { + case DTEConstants.vsProjectKindSolutionItems: + // This is a solution folder -> search in sub-projects + var subProject = FindProject( + project.ProjectItems.OfType().Select(pi => pi.SubProject).OfType(), + projectFile); + if (subProject != null) + return subProject; + break; + + case DTEConstants.vsProjectKindUnmodeled: + // Skip unloaded projects completely + break; + + default: + // Match by project's file name + if (project.FileName == projectFile) + return project; + break; } } From e8015aac373678868620eea631585ed75417efb8 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Thu, 26 Sep 2019 07:21:51 +0200 Subject: [PATCH 13/62] Link to test repository of VS Addin --- ILSpy.AddIn/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 ILSpy.AddIn/README.md diff --git a/ILSpy.AddIn/README.md b/ILSpy.AddIn/README.md new file mode 100644 index 000000000..7c363f311 --- /dev/null +++ b/ILSpy.AddIn/README.md @@ -0,0 +1,3 @@ +Test cases for various types of projects as well as corresponding test plans are located in a separate repository. + +[https://github.com/icsharpcode/ILSpy-Addin-tests](https://github.com/icsharpcode/ILSpy-Addin-tests) \ No newline at end of file From ba22906b3bf7e8b93a4f49c7ea8999b157b328fa Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Thu, 26 Sep 2019 12:20:21 +0200 Subject: [PATCH 14/62] Fix obvious .Result errors (there are more instances of calling .Result that would need investigation) --- ILSpy/App.xaml.cs | 4 ++-- ILSpy/LoadedAssembly.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ILSpy/App.xaml.cs b/ILSpy/App.xaml.cs index 6ff97a8ab..1417af930 100644 --- a/ILSpy/App.xaml.cs +++ b/ILSpy/App.xaml.cs @@ -94,7 +94,7 @@ namespace ICSharpCode.ILSpy var name = Path.GetFileNameWithoutExtension(plugin); try { var asm = Assembly.Load(name); - var parts = discovery.CreatePartsAsync(asm).Result; + var parts = discovery.CreatePartsAsync(asm).GetAwaiter().GetResult(); catalog = catalog.AddParts(parts); } catch (Exception ex) { StartupExceptions.Add(new ExceptionData { Exception = ex, PluginName = name }); @@ -102,7 +102,7 @@ namespace ICSharpCode.ILSpy } } // Add the built-in parts - catalog = catalog.AddParts(discovery.CreatePartsAsync(Assembly.GetExecutingAssembly()).Result); + catalog = catalog.AddParts(discovery.CreatePartsAsync(Assembly.GetExecutingAssembly()).GetAwaiter().GetResult()); // If/When the project switches to .NET Standard/Core, this will be needed to allow metadata interfaces (as opposed // to metadata classes). When running on .NET Framework, it's automatic. // catalog.WithDesktopSupport(); diff --git a/ILSpy/LoadedAssembly.cs b/ILSpy/LoadedAssembly.cs index af67109cd..d4d697f89 100644 --- a/ILSpy/LoadedAssembly.cs +++ b/ILSpy/LoadedAssembly.cs @@ -87,7 +87,7 @@ namespace ICSharpCode.ILSpy public PEFile GetPEFileOrNull() { try { - return GetPEFileAsync().Result; + return GetPEFileAsync().GetAwaiter().GetResult(); } catch (Exception ex) { System.Diagnostics.Trace.TraceError(ex.ToString()); return null; From 0d3a3bee82449a479b16d4ce134d930ab6ad1b44 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 27 Sep 2019 16:33:03 +0200 Subject: [PATCH 15/62] Fix #1703: Support async methods that never return normally. Closes #1678. --- .../TestCases/Pretty/Async.cs | 18 +++++++++++++++ .../TestCases/Pretty/CustomTaskType.cs | 4 +++- .../IL/ControlFlow/AsyncAwaitDecompiler.cs | 22 ++++++++++++++++--- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs index 9f6b5c58a..7d51bc732 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs @@ -167,6 +167,24 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty }; } + public static async Task AlwaysThrow() + { + throw null; + } + + public static async Task InfiniteLoop() + { + while (true) { + } + } + + public static async Task InfiniteLoopWithAwait() + { + while (true) { + await Task.Delay(10); + } + } + #if CS70 public static async Task AsyncLocalFunctions() { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs index 65309ac46..80164a385 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs @@ -1,4 +1,6 @@ -using System; +#pragma warning disable 1998 + +using System; using System.Collections.Generic; using System.Threading.Tasks; diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs index ec20ef25c..63610c57e 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs @@ -85,6 +85,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow TryCatch mainTryCatch; Block setResultAndExitBlock; // block that is jumped to for return statements int finalState; // final state after the setResultAndExitBlock + bool finalStateKnown; ILVariable resultVar; // the variable that gets returned by the setResultAndExitBlock ILVariable doFinallyBodies; @@ -352,7 +353,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow moveNextFunction = YieldReturnDecompiler.CreateILAst(moveNextMethod, context); if (!(moveNextFunction.Body is BlockContainer blockContainer)) throw new SymbolicAnalysisFailedException(); - if (blockContainer.Blocks.Count != 2) + if (blockContainer.Blocks.Count != 2 && blockContainer.Blocks.Count != 1) throw new SymbolicAnalysisFailedException(); if (blockContainer.EntryPoint.IncomingEdgeCount != 1) throw new SymbolicAnalysisFailedException(); @@ -389,7 +390,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow doFinallyBodies = initDoFinallyBodies.Variable; } - setResultAndExitBlock = blockContainer.Blocks[1]; + setResultAndExitBlock = blockContainer.Blocks.ElementAtOrDefault(1); + if (setResultAndExitBlock == null) { + // This block can be absent if the function never exits normally, + // but always throws an exception/loops infinitely. + resultVar = null; + finalStateKnown = false; // final state will be detected in ValidateCatchBlock() instead + return; + } // stobj System.Int32(ldflda [Field ICSharpCode.Decompiler.Tests.TestCases.Pretty.Async+d__7.<>1__state](ldloc this), ldc.i4 -2) // call SetResult(ldflda [Field ICSharpCode.Decompiler.Tests.TestCases.Pretty.Async+d__7.<>t__builder](ldloc this), ldloc result) // leave IL_0000 @@ -397,6 +405,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow throw new SymbolicAnalysisFailedException(); if (!MatchStateAssignment(setResultAndExitBlock.Instructions[0], out finalState)) throw new SymbolicAnalysisFailedException(); + finalStateKnown = true; if (!MatchCall(setResultAndExitBlock.Instructions[1], "SetResult", out var args)) throw new SymbolicAnalysisFailedException(); if (!IsBuilderFieldOnThis(args[0])) @@ -442,8 +451,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow if (!stloc.Value.MatchLdLoc(handler.Variable)) throw new SymbolicAnalysisFailedException(); // stfld <>1__state(ldloc this, ldc.i4 -2) - if (!MatchStateAssignment(catchBlock.Instructions[1], out int newState) || newState != finalState) + if (!MatchStateAssignment(catchBlock.Instructions[1], out int newState)) throw new SymbolicAnalysisFailedException(); + if (finalStateKnown) { + if (newState != finalState) + throw new SymbolicAnalysisFailedException(); + } else { + finalState = newState; + finalStateKnown = true; + } // call SetException(ldfld <>t__builder(ldloc this), ldloc exception) if (!MatchCall(catchBlock.Instructions[2], "SetException", out var args)) throw new SymbolicAnalysisFailedException(); From fb70a2861e0f96b007c1f32dd9a97b3328d78ac9 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 27 Sep 2019 17:34:55 +0200 Subject: [PATCH 16/62] Fix #1656: Disable CopyPropagation for split variables. The code reordering done by copy propagation could cause the lifetimes of different parts of a split variable to start overlapping. This caused incorrect C# to be generated when the variable was recombined. --- .../Correctness/FloatingPointArithmetic.cs | 17 ++++++++++ .../IL/Transforms/CopyPropagation.cs | 32 +++++++++++-------- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/FloatingPointArithmetic.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/FloatingPointArithmetic.cs index f057f77bb..72c0bbc72 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/FloatingPointArithmetic.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/FloatingPointArithmetic.cs @@ -7,6 +7,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness public static int Main(string[] args) { Issue999(); + Issue1656(); return 0; } @@ -20,5 +21,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { return 0.99f * v + 0.01f; } + + static void Issue1656() + { + double primary = 'B'; + CxAssert((++primary) == 'C'); + CxAssert((--primary) == 'B'); + CxAssert((primary++) == 'B'); + CxAssert((primary--) == 'C'); + } + + static void CxAssert(bool v) + { + if (!v) { + throw new InvalidOperationException(); + } + } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs b/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs index 3ccf25e02..b96909efc 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs @@ -18,6 +18,7 @@ using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -31,7 +32,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// then we can replace the variable with the argument. /// 2) assignments of address-loading instructions to local variables /// - public class CopyPropagation : IBlockTransform, IILTransform + public class CopyPropagation : IILTransform { public static void Propagate(StLoc store, ILTransformContext context) { @@ -41,26 +42,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms DoPropagate(store.Variable, store.Value, block, ref i, context); } - public void Run(Block block, BlockTransformContext context) - { - RunOnBlock(block, context); - } - public void Run(ILFunction function, ILTransformContext context) { + var splitVariables = new HashSet(ILVariableEqualityComparer.Instance); + foreach (var g in function.Variables.GroupBy(v => v, ILVariableEqualityComparer.Instance)) { + if (g.Count() > 1) { + splitVariables.Add(g.Key); + } + } foreach (var block in function.Descendants.OfType()) { if (block.Kind != BlockKind.ControlFlow) continue; - RunOnBlock(block, context); + RunOnBlock(block, context, splitVariables); } } - static void RunOnBlock(Block block, ILTransformContext context) + static void RunOnBlock(Block block, ILTransformContext context, HashSet splitVariables = null) { for (int i = 0; i < block.Instructions.Count; i++) { - ILVariable v; - ILInstruction copiedExpr; - if (block.Instructions[i].MatchStLoc(out v, out copiedExpr)) { + if (block.Instructions[i].MatchStLoc(out ILVariable v, out ILInstruction copiedExpr)) { if (v.IsSingleDefinition && v.LoadCount == 0 && v.Kind == VariableKind.StackSlot) { // dead store to stack if (SemanticHelper.IsPure(copiedExpr.Flags)) { @@ -76,18 +76,21 @@ namespace ICSharpCode.Decompiler.IL.Transforms copiedExpr.AddILRange(block.Instructions[i]); block.Instructions[i] = copiedExpr; } - } else if (v.IsSingleDefinition && CanPerformCopyPropagation(v, copiedExpr)) { + } else if (v.IsSingleDefinition && CanPerformCopyPropagation(v, copiedExpr, splitVariables)) { DoPropagate(v, copiedExpr, block, ref i, context); } } } } - static bool CanPerformCopyPropagation(ILVariable target, ILInstruction value) + static bool CanPerformCopyPropagation(ILVariable target, ILInstruction value, HashSet splitVariables) { Debug.Assert(target.StackType == value.ResultType); if (target.Type.IsSmallIntegerType()) return false; + if (splitVariables != null && splitVariables.Contains(target)) { + return false; // non-local code move might change semantics when there's split variables + } switch (value.OpCode) { case OpCode.LdLoca: // case OpCode.LdElema: @@ -100,6 +103,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; case OpCode.LdLoc: var v = ((LdLoc)value).Variable; + if (splitVariables != null && splitVariables.Contains(v)) { + return false; // non-local code move might change semantics when there's split variables + } switch (v.Kind) { case VariableKind.Parameter: // Parameters can be copied only if they aren't assigned to (directly or indirectly via ldarga) From 9fff0438c0e65bf265021da81ee6e440cb25c416 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 27 Sep 2019 18:10:47 +0200 Subject: [PATCH 17/62] Fix #1675: Emit __ldftn/__ldvirtftn pseudo expressions, if delegate construction is not supported. --- .../ICSharpCode.Decompiler.Tests.csproj | 2 + .../Ugly/NoExtensionMethods.Expected.cs | 23 ++++++ .../TestCases/Ugly/NoExtensionMethods.cs | 17 ++++ .../Ugly/NoExtensionMethods.opt.roslyn.il | 69 ++++++++++++++++ .../Ugly/NoExtensionMethods.roslyn.il | 81 +++++++++++++++++++ .../UglyTestRunner.cs | 8 ++ ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 32 ++++++-- .../CSharp/ExpressionBuilder.cs | 16 ++++ 8 files changed, 240 insertions(+), 8 deletions(-) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.Expected.cs create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.cs create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.opt.roslyn.il create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.roslyn.il diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 37b69bf18..7141a9995 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -82,6 +82,8 @@ + + diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.Expected.cs b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.Expected.cs new file mode 100644 index 000000000..dfe0e184b --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.Expected.cs @@ -0,0 +1,23 @@ +using System; +using System.Runtime.CompilerServices; + +[assembly: Extension] + +namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly +{ + [Extension] + internal static class NoExtensionMethods + { + [Extension] + internal static Func AsFunc(T value) where T : class + { + return new Func(value, __ldftn(Return)); + } + + [Extension] + private static T Return(T value) + { + return value; + } + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.cs b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.cs new file mode 100644 index 000000000..61b9b43bb --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.cs @@ -0,0 +1,17 @@ +using System; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly +{ + internal static class NoExtensionMethods + { + internal static Func AsFunc(this T value) where T : class + { + return new Func(value.Return); + } + + private static T Return(this T value) + { + return value; + } + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.opt.roslyn.il new file mode 100644 index 000000000..41b3e7b87 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.opt.roslyn.il @@ -0,0 +1,69 @@ + + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly NoExtensionMethods +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + + // --- The following custom attribute is added automatically, do not uncomment ------- + // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) + + .permissionset reqmin + = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module NoExtensionMethods.dll +.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) +.imagebase 0x10000000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class private abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + .method assembly hidebysig static class [mscorlib]System.Func`1 + AsFunc(!!T 'value') cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 18 (0x12) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: box !!T + IL_0006: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) + IL_000c: newobj instance void class [mscorlib]System.Func`1::.ctor(object, + native int) + IL_0011: ret + } // end of method NoExtensionMethods::AsFunc + + .method private hidebysig static !!T Return(!!T 'value') cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ret + } // end of method NoExtensionMethods::Return + +} // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.roslyn.il new file mode 100644 index 000000000..60af94216 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.roslyn.il @@ -0,0 +1,81 @@ + + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly NoExtensionMethods +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + + // --- The following custom attribute is added automatically, do not uncomment ------- + // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) + + .permissionset reqmin + = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module NoExtensionMethods.dll +.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) +.imagebase 0x10000000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class private abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + .method assembly hidebysig static class [mscorlib]System.Func`1 + AsFunc(!!T 'value') cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 23 (0x17) + .maxstack 2 + .locals init (class [mscorlib]System.Func`1 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: box !!T + IL_0007: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) + IL_000d: newobj instance void class [mscorlib]System.Func`1::.ctor(object, + native int) + IL_0012: stloc.0 + IL_0013: br.s IL_0015 + + IL_0015: ldloc.0 + IL_0016: ret + } // end of method NoExtensionMethods::AsFunc + + .method private hidebysig static !!T Return(!!T 'value') cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 7 (0x7) + .maxstack 1 + .locals init (!!T V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: br.s IL_0005 + + IL_0005: ldloc.0 + IL_0006: ret + } // end of method NoExtensionMethods::Return + +} // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** diff --git a/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs b/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs index dff8da418..63ebcf6dd 100644 --- a/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs @@ -83,6 +83,14 @@ namespace ICSharpCode.Decompiler.Tests }); } + [Test] + public void NoExtensionMethods([ValueSource("roslynOnlyOptions")] CompilerOptions cscOptions) + { + RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings { + ExtensionMethods = false + }); + } + void RunForLibrary([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, DecompilerSettings decompilerSettings = null) { Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, decompilerSettings); diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index ad581e53e..7b6c1b2bf 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -1235,6 +1235,11 @@ namespace ICSharpCode.Decompiler.CSharp default: throw new ArgumentException($"Unknown instruction type: {func.OpCode}"); } + if (method.IsStatic && !method.IsExtensionMethod) { + var argumentList = BuildArgumentList(expectedTargetDetails, null, inst.Method, + 0, inst.Arguments, null); + return HandleConstructorCall(new ExpectedTargetDetails { CallOpCode = OpCode.NewObj }, null, inst.Method, argumentList).WithILInstruction(inst); + } return HandleDelegateConstruction(inst.Method.DeclaringType, method, expectedTargetDetails, thisArg, inst); } @@ -1243,9 +1248,15 @@ namespace ICSharpCode.Decompiler.CSharp return HandleDelegateConstruction(inst.Type, inst.Method, new ExpectedTargetDetails { CallOpCode = OpCode.CallVirt }, inst.Argument, inst); } - TranslatedExpression HandleDelegateConstruction(IType delegateType, IMethod method, ExpectedTargetDetails expectedTargetDetails, ILInstruction thisArg, ILInstruction inst) + internal ExpressionWithResolveResult BuildMethodReference(IMethod method, bool isVirtual) + { + var expr = BuildDelegateReference(method, invokeMethod: null, new ExpectedTargetDetails { CallOpCode = isVirtual ? OpCode.CallVirt : OpCode.Call }, thisArg: null); + expr.Expression.RemoveAnnotations(); + return expr.Expression.WithRR(new MemberResolveResult(null, method)); + } + + ExpressionWithResolveResult BuildDelegateReference(IMethod method, IMethod invokeMethod, ExpectedTargetDetails expectedTargetDetails, ILInstruction thisArg) { - var invokeMethod = delegateType.GetDelegateInvokeMethod(); TranslatedExpression target; IType targetType; bool requireTarget; @@ -1331,27 +1342,32 @@ namespace ICSharpCode.Decompiler.CSharp } } requireTarget = !method.IsLocalFunction && (step & 1) != 0; - Expression targetExpression; + ExpressionWithResolveResult targetExpression; Debug.Assert(result != null); if (requireTarget) { Debug.Assert(target.Expression != null); var mre = new MemberReferenceExpression(target, methodName); if ((step & 2) != 0) mre.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); - mre.WithRR(result); - targetExpression = mre; + targetExpression = mre.WithRR(result); } else { var ide = new IdentifierExpression(methodName); if ((step & 2) != 0) ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); - ide.WithRR(result); - targetExpression = ide; + targetExpression = ide.WithRR(result); } + return targetExpression; + } + + TranslatedExpression HandleDelegateConstruction(IType delegateType, IMethod method, ExpectedTargetDetails expectedTargetDetails, ILInstruction thisArg, ILInstruction inst) + { + var invokeMethod = delegateType.GetDelegateInvokeMethod(); + var targetExpression = BuildDelegateReference(method, invokeMethod, expectedTargetDetails, thisArg); var oce = new ObjectCreateExpression(expressionBuilder.ConvertType(delegateType), targetExpression) .WithILInstruction(inst) .WithRR(new ConversionResolveResult( delegateType, - result, + targetExpression.ResolveResult, Conversion.MethodGroupConversion(method, expectedTargetDetails.CallOpCode == OpCode.CallVirt, false))); return oce; } diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 0d7d7b9c6..fcbf2ce6c 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -3134,6 +3134,22 @@ namespace ICSharpCode.Decompiler.CSharp .WithRR(new OperatorResolveResult(SpecialType.Dynamic, inst.Operation, new[] { target.ResolveResult, value.ResolveResult })); } + protected internal override TranslatedExpression VisitLdFtn(LdFtn inst, TranslationContext context) + { + ExpressionWithResolveResult delegateRef = new CallBuilder(this, typeSystem, settings).BuildMethodReference(inst.Method, isVirtual: false); + return new InvocationExpression(new IdentifierExpression("__ldftn"), delegateRef) + .WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.IntPtr))) + .WithILInstruction(inst); + } + + protected internal override TranslatedExpression VisitLdVirtFtn(LdVirtFtn inst, TranslationContext context) + { + ExpressionWithResolveResult delegateRef = new CallBuilder(this, typeSystem, settings).BuildMethodReference(inst.Method, isVirtual: true); + return new InvocationExpression(new IdentifierExpression("__ldvirtftn"), delegateRef) + .WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.IntPtr))) + .WithILInstruction(inst); + } + protected internal override TranslatedExpression VisitInvalidBranch(InvalidBranch inst, TranslationContext context) { string message = "Error"; From 28ef4111094e0192dfa765f9c0aa44e396cdfedc Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 27 Sep 2019 18:17:50 +0200 Subject: [PATCH 18/62] Set version = 5.0.1 --- BuildTools/appveyor-install.ps1 | 2 +- BuildTools/pipelines-install.ps1 | 2 +- BuildTools/update-assemblyinfo.ps1 | 2 +- ILSpy/Properties/AssemblyInfo.template.cs | 2 +- appveyor.yml | 2 +- azure-pipelines.yml | 5 ++--- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/BuildTools/appveyor-install.ps1 b/BuildTools/appveyor-install.ps1 index db3950f43..0b922deb8 100644 --- a/BuildTools/appveyor-install.ps1 +++ b/BuildTools/appveyor-install.ps1 @@ -4,7 +4,7 @@ $baseCommit = "d779383cb85003d6dabeb976f0845631e07bf463"; $baseCommitRev = 1; # make sure this list matches artifacts-only branches list in appveyor.yml! -$masterBranches = @("master", "3.2.x"); +$masterBranches = @("master", "5.0.x"); $globalAssemblyInfoTemplateFile = "ILSpy/Properties/AssemblyInfo.template.cs"; diff --git a/BuildTools/pipelines-install.ps1 b/BuildTools/pipelines-install.ps1 index 31d5b634b..4e3dda35e 100644 --- a/BuildTools/pipelines-install.ps1 +++ b/BuildTools/pipelines-install.ps1 @@ -4,7 +4,7 @@ $baseCommit = "d779383cb85003d6dabeb976f0845631e07bf463"; $baseCommitRev = 1; # make sure this list matches artifacts-only branches list in azure-pipelines.yml! -$masterBranches = @("master", "3.2.x"); +$masterBranches = @("master", "5.0.x"); $globalAssemblyInfoTemplateFile = "ILSpy/Properties/AssemblyInfo.template.cs"; diff --git a/BuildTools/update-assemblyinfo.ps1 b/BuildTools/update-assemblyinfo.ps1 index d901e7fea..a716d15a7 100644 --- a/BuildTools/update-assemblyinfo.ps1 +++ b/BuildTools/update-assemblyinfo.ps1 @@ -4,7 +4,7 @@ $baseCommit = "d779383cb85003d6dabeb976f0845631e07bf463"; $baseCommitRev = 1; # make sure this list matches artifacts-only branches list in appveyor.yml! -$masterBranches = @("master", "3.2.x"); +$masterBranches = @("master", "5.0.x"); $globalAssemblyInfoTemplateFile = "ILSpy/Properties/AssemblyInfo.template.cs"; diff --git a/ILSpy/Properties/AssemblyInfo.template.cs b/ILSpy/Properties/AssemblyInfo.template.cs index ec209f686..8791a0275 100644 --- a/ILSpy/Properties/AssemblyInfo.template.cs +++ b/ILSpy/Properties/AssemblyInfo.template.cs @@ -37,7 +37,7 @@ internal static class RevisionClass { public const string Major = "5"; public const string Minor = "0"; - public const string Build = "0"; + public const string Build = "1"; public const string Revision = "$INSERTREVISION$"; public const string VersionName = null; diff --git a/appveyor.yml b/appveyor.yml index 13806b0ce..537d49fd9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -43,7 +43,7 @@ for: - branches: only: - master - - 3.2.x + - 5.0.x artifacts: - path: ILSpy_binaries.zip name: ILSpy %APPVEYOR_REPO_BRANCH% %ILSPY_VERSION_NUMBER% binaries diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 16465568f..fbb7ead18 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,11 +1,10 @@ trigger: - master -- msix -- 3.2.x +- 5.0.x pr: - master -- 3.2.x +- 5.0.x variables: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true From d43d4785cb9b7d571f13d4009e52da87addffbb3 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 27 Sep 2019 18:21:52 +0200 Subject: [PATCH 19/62] #1711: Support /navigateTo:none on command-line to disable the automatic navigation on startup. --- ILSpy/MainWindow.xaml.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 4563dd602..594cb4f2c 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -323,6 +323,10 @@ namespace ICSharpCode.ILSpy } } } + } else if (navigateTo == "none") { + // Don't navigate anywhere; start empty. + // Used by ILSpy VS addin, it'll send us the real location to navigate to via IPC. + found = true; } else { IEntity mr = await Task.Run(() => FindEntityInRelevantAssemblies(navigateTo, relevantAssemblies)); if (mr != null && mr.ParentModule.PEFile != null) { @@ -346,15 +350,15 @@ namespace ICSharpCode.ILSpy } } else if (spySettings != null) { SharpTreeNode node = null; - if (sessionSettings.ActiveTreeViewPath?.Length > 0) { + if (activeTreeViewPath?.Length > 0) { foreach (var asm in assemblyList.GetAssemblies()) { - if (asm.FileName == sessionSettings.ActiveTreeViewPath[0]) { + if (asm.FileName == activeTreeViewPath[0]) { // FindNodeByPath() blocks the UI if the assembly is not yet loaded, // so use an async wait instead. await asm.GetPEFileAsync().Catch(ex => { }); } } - node = FindNodeByPath(sessionSettings.ActiveTreeViewPath, true); + node = FindNodeByPath(activeTreeViewPath, true); } if (treeView.SelectedItem == initialSelection) { if (node != null) { From 9ab83e71deee0854323e3ffb03a984a049dbc931 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 27 Sep 2019 18:52:58 +0200 Subject: [PATCH 20/62] Reset version back to 6.0.0. --- ILSpy/Properties/AssemblyInfo.template.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ILSpy/Properties/AssemblyInfo.template.cs b/ILSpy/Properties/AssemblyInfo.template.cs index 0cf41b0d0..f4a337c24 100644 --- a/ILSpy/Properties/AssemblyInfo.template.cs +++ b/ILSpy/Properties/AssemblyInfo.template.cs @@ -37,7 +37,7 @@ internal static class RevisionClass { public const string Major = "6"; public const string Minor = "0"; - public const string Build = "1"; + public const string Build = "0"; public const string Revision = "$INSERTREVISION$"; public const string VersionName = "alpha1"; From 9b3692eda27a7b607842b4bbbf5672af14d459a7 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Fri, 27 Sep 2019 22:13:07 +0200 Subject: [PATCH 21/62] Use .NET Core 3.0 final SDK on Azure Pipelines --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 707a6de24..3c1755b9a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -39,7 +39,7 @@ jobs: - task: DotNetCoreInstaller@0 inputs: - version: '3.0.100-preview9-014004' + version: '3.0.100' - powershell: .\BuildTools\pipelines-install.ps1 displayName: Install From 115f8ab0d9485f61a31bbf3dd989070147bb84d0 Mon Sep 17 00:00:00 2001 From: Andreas Weizel Date: Fri, 27 Sep 2019 22:28:40 +0200 Subject: [PATCH 22/62] AddIn: Pass /navigateTo:none to ILSpy as command-line parameter --- ILSpy.AddIn/ILSpyInstance.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ILSpy.AddIn/ILSpyInstance.cs b/ILSpy.AddIn/ILSpyInstance.cs index 0cc23f773..bd8c56cb7 100644 --- a/ILSpy.AddIn/ILSpyInstance.cs +++ b/ILSpy.AddIn/ILSpyInstance.cs @@ -42,7 +42,8 @@ namespace ICSharpCode.ILSpy.AddIn var process = new Process() { StartInfo = new ProcessStartInfo() { FileName = GetILSpyPath(), - UseShellExecute = false + UseShellExecute = false, + Arguments = "/navigateTo:none" } }; process.Start(); From b45f21e71430d2fb6921a643989fe5b936c3b37a Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 27 Sep 2019 22:33:49 +0200 Subject: [PATCH 23/62] #1675: Fix converting static method to method group --- .../Ugly/NoExtensionMethods.Expected.cs | 10 ++++++ .../TestCases/Ugly/NoExtensionMethods.cs | 10 ++++++ .../Ugly/NoExtensionMethods.opt.roslyn.il | 24 +++++++++++++ .../Ugly/NoExtensionMethods.roslyn.il | 36 +++++++++++++++++++ ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 30 ++++++++++++++-- 5 files changed, 108 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.Expected.cs b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.Expected.cs index dfe0e184b..72d6c3b3e 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.Expected.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.Expected.cs @@ -19,5 +19,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { return value; } + + internal static Func ExtensionMethodAsStaticFunc() + { + return Return; + } + + internal static Func ExtensionMethodBoundToNull() + { + return new Func(null, __ldftn(Return)); + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.cs b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.cs index 61b9b43bb..294b71938 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.cs @@ -13,5 +13,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly { return value; } + + internal static Func ExtensionMethodAsStaticFunc() + { + return Return; + } + + internal static Func ExtensionMethodBoundToNull() + { + return ((object)null).Return; + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.opt.roslyn.il index 41b3e7b87..12d81400b 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.opt.roslyn.il @@ -61,6 +61,30 @@ IL_0001: ret } // end of method NoExtensionMethods::Return + .method assembly hidebysig static class [mscorlib]System.Func`2 + ExtensionMethodAsStaticFunc() cil managed + { + // Code size 13 (0xd) + .maxstack 8 + IL_0000: ldnull + IL_0001: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) + IL_0007: newobj instance void class [mscorlib]System.Func`2::.ctor(object, + native int) + IL_000c: ret + } // end of method NoExtensionMethods::ExtensionMethodAsStaticFunc + + .method assembly hidebysig static class [mscorlib]System.Func`1 + ExtensionMethodBoundToNull() cil managed + { + // Code size 13 (0xd) + .maxstack 8 + IL_0000: ldnull + IL_0001: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) + IL_0007: newobj instance void class [mscorlib]System.Func`1::.ctor(object, + native int) + IL_000c: ret + } // end of method NoExtensionMethods::ExtensionMethodBoundToNull + } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.roslyn.il index 60af94216..a740ada26 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.roslyn.il @@ -73,6 +73,42 @@ IL_0006: ret } // end of method NoExtensionMethods::Return + .method assembly hidebysig static class [mscorlib]System.Func`2 + ExtensionMethodAsStaticFunc() cil managed + { + // Code size 18 (0x12) + .maxstack 2 + .locals init (class [mscorlib]System.Func`2 V_0) + IL_0000: nop + IL_0001: ldnull + IL_0002: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) + IL_0008: newobj instance void class [mscorlib]System.Func`2::.ctor(object, + native int) + IL_000d: stloc.0 + IL_000e: br.s IL_0010 + + IL_0010: ldloc.0 + IL_0011: ret + } // end of method NoExtensionMethods::ExtensionMethodAsStaticFunc + + .method assembly hidebysig static class [mscorlib]System.Func`1 + ExtensionMethodBoundToNull() cil managed + { + // Code size 18 (0x12) + .maxstack 2 + .locals init (class [mscorlib]System.Func`1 V_0) + IL_0000: nop + IL_0001: ldnull + IL_0002: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return(!!0) + IL_0008: newobj instance void class [mscorlib]System.Func`1::.ctor(object, + native int) + IL_000d: stloc.0 + IL_000e: br.s IL_0010 + + IL_0010: ldloc.0 + IL_0011: ret + } // end of method NoExtensionMethods::ExtensionMethodBoundToNull + } // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 7b6c1b2bf..f7e2b5a67 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -1235,12 +1235,38 @@ namespace ICSharpCode.Decompiler.CSharp default: throw new ArgumentException($"Unknown instruction type: {func.OpCode}"); } - if (method.IsStatic && !method.IsExtensionMethod) { + if (CanUseDelegateConstruction(method, thisArg, inst.Method.DeclaringType.GetDelegateInvokeMethod())) { + return HandleDelegateConstruction(inst.Method.DeclaringType, method, expectedTargetDetails, thisArg, inst); + } else { var argumentList = BuildArgumentList(expectedTargetDetails, null, inst.Method, 0, inst.Arguments, null); return HandleConstructorCall(new ExpectedTargetDetails { CallOpCode = OpCode.NewObj }, null, inst.Method, argumentList).WithILInstruction(inst); } - return HandleDelegateConstruction(inst.Method.DeclaringType, method, expectedTargetDetails, thisArg, inst); + } + + private bool CanUseDelegateConstruction(IMethod targetMethod, ILInstruction thisArg, IMethod invokeMethod) + { + if (targetMethod.IsStatic) { + // If the invoke method is known, we can compare the parameter counts to figure out whether the + // delegate is static or binds the first argument + if (invokeMethod != null) { + if (invokeMethod.Parameters.Count == targetMethod.Parameters.Count) { + return thisArg.MatchLdNull(); + } else if (targetMethod.IsExtensionMethod && invokeMethod.Parameters.Count == targetMethod.Parameters.Count - 1) { + return true; + } else { + return false; + } + } else { + // delegate type unknown: + return thisArg.MatchLdNull() || targetMethod.IsExtensionMethod; + } + } else { + // targetMethod is instance method + if (invokeMethod != null && invokeMethod.Parameters.Count != targetMethod.Parameters.Count) + return false; + return true; + } } internal TranslatedExpression Build(LdVirtDelegate inst) From c32361d46440112815d18cb1a672652550ff0d4f Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 27 Sep 2019 23:37:32 +0200 Subject: [PATCH 24/62] #1691: Avoid replacing string.Concat() with operator+ when the evaluation order depends on the compiler version. --- .../CorrectnessTestRunner.cs | 6 ++ .../ICSharpCode.Decompiler.Tests.csproj | 1 + .../TestCases/Correctness/StringConcat.cs | 57 ++++++++++++++ .../ReplaceMethodCallsWithOperators.cs | 78 ++++++++++++++++++- 4 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Correctness/StringConcat.cs diff --git a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs index 6d75516da..4ad298c7c 100644 --- a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs @@ -280,6 +280,12 @@ namespace ICSharpCode.Decompiler.Tests RunCS(options: options); } + [Test] + public void StringConcat([ValueSource("defaultOptions")] CompilerOptions options) + { + RunCS(options: options); + } + [Test] public void MiniJSON([ValueSource("defaultOptions")] CompilerOptions options) { diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 1b7aedd7e..dad38279b 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -79,6 +79,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/StringConcat.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/StringConcat.cs new file mode 100644 index 000000000..229c9ef6a --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/StringConcat.cs @@ -0,0 +1,57 @@ +using System; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness +{ + class StringConcat + { + private class C + { + readonly int i; + + public C(int i) + { + Console.WriteLine(" new C(" + i + ")"); + this.i = i; + } + + public override string ToString() + { + Console.WriteLine(" C(" + i + ").ToString()"); + return i.ToString(); + } + } + + static string Space() + { + Console.WriteLine(" Space()"); + return " "; + } + + static void Main() + { + Console.WriteLine("string + C:"); + Console.WriteLine(Space() + new C(1)); + + Console.WriteLine("C + string:"); + Console.WriteLine(new C(2) + Space()); + + Console.WriteLine("C + string + C:"); + Console.WriteLine(new C(3) + Space() + new C(4)); + + Console.WriteLine("string + C + C:"); + Console.WriteLine(Space() + new C(5) + new C(6)); + + Console.WriteLine("string.Concat(C, string, C):"); + Console.WriteLine(string.Concat(new C(10), Space(), new C(11))); + + Console.WriteLine("string.Concat(string.Concat(C, string), C):"); + Console.WriteLine(string.Concat(string.Concat(new C(15), Space()), new C(16))); + + Console.WriteLine("string.Concat(C, string.Concat(string, C)):"); + Console.WriteLine(string.Concat(new C(20), string.Concat(Space(), new C(21)))); + + Console.WriteLine("string.Concat(C, string) + C:"); + Console.WriteLine(string.Concat(new C(30), Space()) + new C(31)); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs index 9d9838d71..d7a081e99 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs @@ -23,7 +23,9 @@ using System.Reflection; using System.Text; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; +using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp.Transforms { @@ -56,7 +58,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms var arguments = invocationExpression.Arguments.ToArray(); // Reduce "String.Concat(a, b)" to "a + b" - if (method.Name == "Concat" && method.DeclaringType.FullName == "System.String" && CheckArgumentsForStringConcat(arguments)) { + if (IsStringConcat(method) && CheckArgumentsForStringConcat(arguments)) { invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression Expression expr = arguments[0]; for (int i = 1; i < arguments.Length; i++) { @@ -174,9 +176,77 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (arguments.Length < 2) return false; - return !arguments.Any(arg => arg is NamedArgumentExpression) && - (arguments[0].GetResolveResult().Type.IsKnownType(KnownTypeCode.String) || - arguments[1].GetResolveResult().Type.IsKnownType(KnownTypeCode.String)); + if (arguments.Any(arg => arg is NamedArgumentExpression)) + return false; + + // The evaluation order when the object.ToString() calls happen is a mess: + // The C# spec says the evaluation for order for each individual string + should be: + // * evaluate left argument + // * evaluate right argument + // * call ToString() on object argument + // What actually happens pre-VS2019.3: + // * evaluate all arguments in chain of + operators from left to right + // * call ToString() on all object arguments from left to right + // What happens in VS2019.3: + // * for each argument in chain of + operators fom left to right: + // * evaluate argument + // * call ToString() on object argument + // See https://github.com/dotnet/roslyn/issues/38641 for details. + // To ensure the decompiled code's behavior matches the original IL behavior, + // no matter which compiler is used to recompile it, we require that all + // implicit ToString() calls except for the last are free of side effects. + foreach (var arg in arguments.SkipLast(1)) { + if (!ToStringIsKnownEffectFree(arg.GetResolveResult().Type)) { + return false; + } + } + foreach (var arg in arguments) { + if (arg.GetResolveResult() is InvocationResolveResult rr && IsStringConcat(rr.Member)) { + // Roslyn + mcs also flatten nested string.Concat() invocations within a operator+ use, + // which causes it to use the incorrect evaluation order despite the code using an + // explicit string.Concat() call. + // This problem is avoided if the outer call remains string.Concat() as well. + return false; + } + } + + // One of the first two arguments must be string, otherwise the + operator + // won't resolve to a string concatenation. + return arguments[0].GetResolveResult().Type.IsKnownType(KnownTypeCode.String) + || arguments[1].GetResolveResult().Type.IsKnownType(KnownTypeCode.String); + } + + private bool IsStringConcat(IParameterizedMember member) + { + return member is IMethod method + && method.Name == "Concat" + && method.DeclaringType.IsKnownType(KnownTypeCode.String); + } + + static bool ToStringIsKnownEffectFree(IType type) + { + type = NullableType.GetUnderlyingType(type); + switch (type.GetDefinition()?.KnownTypeCode) { + case KnownTypeCode.Boolean: + case KnownTypeCode.Char: + case KnownTypeCode.SByte: + case KnownTypeCode.Byte: + case KnownTypeCode.Int16: + case KnownTypeCode.UInt16: + case KnownTypeCode.Int32: + case KnownTypeCode.UInt32: + case KnownTypeCode.Int64: + case KnownTypeCode.UInt64: + case KnownTypeCode.Single: + case KnownTypeCode.Double: + case KnownTypeCode.Decimal: + case KnownTypeCode.IntPtr: + case KnownTypeCode.UIntPtr: + case KnownTypeCode.String: + return true; + default: + return false; + } } static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name) From 74f3b2a0dd161234647592fe5b38d8218bb28469 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 28 Sep 2019 09:33:25 +0200 Subject: [PATCH 25/62] Fix #1712: Invalid SequencePointsBlob when generating PDB: Filter duplicate IL range assignments. --- ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs | 2 +- ICSharpCode.Decompiler/DebugInfo/SequencePoint.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs b/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs index 849e0eee9..b652e6ebe 100644 --- a/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs @@ -300,7 +300,7 @@ namespace ICSharpCode.Decompiler.CSharp if (sequencePoint.Offset < pos) { // overlapping sequence point? // delete previous sequence points that are after sequencePoint.Offset - while (newList.Count > 0 && newList.Last().EndOffset > pos) { + while (newList.Count > 0 && newList.Last().EndOffset > sequencePoint.Offset) { var last = newList.Last(); if (last.Offset >= sequencePoint.Offset) { newList.RemoveAt(newList.Count - 1); diff --git a/ICSharpCode.Decompiler/DebugInfo/SequencePoint.cs b/ICSharpCode.Decompiler/DebugInfo/SequencePoint.cs index 441731f72..cb0392150 100644 --- a/ICSharpCode.Decompiler/DebugInfo/SequencePoint.cs +++ b/ICSharpCode.Decompiler/DebugInfo/SequencePoint.cs @@ -17,12 +17,14 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Diagnostics; namespace ICSharpCode.Decompiler.DebugInfo { /// /// A sequence point read from a PDB file or produced by the decompiler. /// + [DebuggerDisplay("SequencePoint IL_{Offset,h}-IL_{EndOffset,h}, {StartLine}:{StartColumn}-{EndLine}:{EndColumn}, IsHidden={IsHidden}")] public struct SequencePoint { /// From 851ee29261de27ee9d326ef7e6d62142fb30dbe5 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Tue, 3 Sep 2019 11:26:35 +0200 Subject: [PATCH 26/62] work on avalondock --- ILSpy/ILSpy.csproj | 2 + ILSpy/MainWindow.xaml | 188 +++++++++++++++++++-------------------- ILSpy/MainWindow.xaml.cs | 12 +-- 3 files changed, 101 insertions(+), 101 deletions(-) diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 1dd22579f..3ddf7448a 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -49,6 +49,8 @@ + + diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index c8846031e..5bd6722b1 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -4,7 +4,7 @@ x:ClassModifier="public" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:tv="clr-namespace:ICSharpCode.TreeView;assembly=ICSharpCode.TreeView" xmlns:local="clr-namespace:ICSharpCode.ILSpy" - xmlns:textView="clr-namespace:ICSharpCode.ILSpy.TextView" + xmlns:avalondock="http://schemas.xceed.com/wpf/xaml/avalondock" xmlns:controls="clr-namespace:ICSharpCode.ILSpy.Controls" xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties" Title="ILSpy" @@ -51,7 +51,8 @@ - + + @@ -107,99 +108,94 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - \ No newline at end of file From 97009de7940e6cbc9e67fc6ece58a839e3ba5c5f Mon Sep 17 00:00:00 2001 From: jkuehner Date: Tue, 3 Sep 2019 15:10:01 +0200 Subject: [PATCH 28/62] support multiple decompilations --- ILSpy/Commands/DecompileInNewViewCommand.cs | 48 +++++++++++++++++++++ ILSpy/Docking/PanePosition.cs | 3 +- ILSpy/ILSpy.csproj | 1 + ILSpy/MainWindow.xaml | 36 ++++++---------- ILSpy/MainWindow.xaml.cs | 46 ++++---------------- ILSpy/Properties/Resources.Designer.cs | 9 ++++ ILSpy/Properties/Resources.resx | 3 ++ 7 files changed, 83 insertions(+), 63 deletions(-) create mode 100644 ILSpy/Commands/DecompileInNewViewCommand.cs diff --git a/ILSpy/Commands/DecompileInNewViewCommand.cs b/ILSpy/Commands/DecompileInNewViewCommand.cs new file mode 100644 index 000000000..b088ae1ad --- /dev/null +++ b/ILSpy/Commands/DecompileInNewViewCommand.cs @@ -0,0 +1,48 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System.Linq; +using ICSharpCode.ILSpy.Properties; +using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.TreeNodes; + +namespace ICSharpCode.ILSpy.Analyzers +{ + [ExportContextMenuEntry(Header = nameof(Resources.DecompileToNewPanel), Icon = "images/Search.png", Category = nameof(Resources.Analyze), Order = 90)] + internal sealed class DecompileInNewViewCommand : IContextMenuEntry + { + public bool IsVisible(TextViewContext context) + { + return true; + } + + public bool IsEnabled(TextViewContext context) + { + return true; + } + + public void Execute(TextViewContext context) + { + var dtv = new DecompilerTextView(); + var nodes = context.SelectedTreeNodes.Cast().ToArray(); + var title = string.Join(", ", nodes.Select(x => x.ToString())); + MainWindow.Instance.ShowInNewPane(title, dtv, PanePosition.Document); + dtv.Decompile(MainWindow.Instance.CurrentLanguage, nodes, new DecompilationOptions()); + } + } +} diff --git a/ILSpy/Docking/PanePosition.cs b/ILSpy/Docking/PanePosition.cs index d0ff28370..d162d5d7b 100644 --- a/ILSpy/Docking/PanePosition.cs +++ b/ILSpy/Docking/PanePosition.cs @@ -3,6 +3,7 @@ public enum PanePosition { Top, - Bottom + Bottom, + Document } } diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 6c58619d0..79a5c8023 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -101,6 +101,7 @@ + diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index 75ef145a4..c99cbd82b 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -97,6 +97,15 @@ ItemsSource="{Binding SelectedItem.LanguageVersions, ElementName=languageComboBox, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding FilterSettings.LanguageVersion, UpdateSourceTrigger=PropertyChanged}"/> + + + + + + - - -