Skip to content

Commit

Permalink
Fix #2716: Add an option to allow sorting custom attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
siegfriedpammer committed Feb 20, 2025
1 parent 853e9d4 commit 8b76879
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 8 deletions.
1 change: 1 addition & 0 deletions ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,7 @@ static TypeSystemAstBuilder CreateAstBuilder(DecompilerSettings settings)
{
var typeSystemAstBuilder = new TypeSystemAstBuilder();
typeSystemAstBuilder.ShowAttributes = true;
typeSystemAstBuilder.SortAttributes = settings.SortCustomAttributes;
typeSystemAstBuilder.AlwaysUseShortTypeNames = true;
typeSystemAstBuilder.AddResolveResultAnnotations = true;
typeSystemAstBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables;
Expand Down
78 changes: 70 additions & 8 deletions ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ void InitProperties()
/// </summary>
public bool ShowAttributes { get; set; }

/// <summary>
/// Controls whether to sort attributes, if set to <see langword="false" /> attributes are shown in metadata order.
/// The default value is <see langword="false" />.
/// </summary>
public bool SortAttributes { get; set; }

/// <summary>
/// Controls whether to use fully-qualified type names or short type names.
/// The default value is <see langword="false" />.
Expand Down Expand Up @@ -793,16 +799,72 @@ public Attribute ConvertAttribute(IAttribute attribute)
return attr;
}

private IEnumerable<AttributeSection> ConvertAttributes(IEnumerable<IAttribute> attributes)
{
return attributes.Select(a => new AttributeSection(ConvertAttribute(a)));
}

private IEnumerable<AttributeSection> ConvertAttributes(IEnumerable<IAttribute> attributes, string target)
private IEnumerable<AttributeSection> ConvertAttributes(IEnumerable<IAttribute> attributes, string target = null)
{
return attributes.Select(a => new AttributeSection(ConvertAttribute(a)) {
AttributeTarget = target
if (SortAttributes)
attributes = attributes.OrderBy(a => a, new DelegateComparer<IAttribute>(CompareAttribute));
return attributes.Select(a => {
var section = new AttributeSection(ConvertAttribute(a));
if (target != null)
section.AttributeTarget = target;
return section;
});

static int CompareAttribute(IAttribute a, IAttribute b)
{
int result = CompareType(a.AttributeType, b.AttributeType);
if (result != 0)
return result;
if (a.HasDecodeErrors && b.HasDecodeErrors)
return 0;
if (a.HasDecodeErrors)
return -1;
if (b.HasDecodeErrors)
return 1;
result = a.FixedArguments.Length - b.FixedArguments.Length;
if (result != 0)
return result;
for (int i = 0; i < a.FixedArguments.Length; i++)
{
var argA = a.FixedArguments[i];
var argB = b.FixedArguments[i];
result = CompareType(argA.Type, argB.Type);
if (result != 0)
return result;
if (argA.Value is IComparable compA && argB.Value is IComparable compB)
result = compA.CompareTo(compB);
else
result = 0;
if (result != 0)
return result;
}
result = a.NamedArguments.Length - b.NamedArguments.Length;
if (result != 0)
return result;
for (int i = 0; i < a.FixedArguments.Length; i++)
{
var argA = a.NamedArguments[i];
var argB = b.NamedArguments[i];
result = argA.Name.CompareTo(argB.Name);
if (result != 0)
return result;
result = CompareType(argA.Type, argB.Type);
if (result != 0)
return result;
if (argA.Value is IComparable compA && argB.Value is IComparable compB)
result = compA.CompareTo(compB);
else
result = 0;
if (result != 0)
return result;
}
return 0;
}

static int CompareType(IType a, IType b)
{
return a.FullName.CompareTo(b.FullName);
}
}
#endregion

Expand Down
18 changes: 18 additions & 0 deletions ICSharpCode.Decompiler/DecompilerSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2148,6 +2148,24 @@ public bool AlwaysUseGlobal {
}
}

bool sortCustomAttributes = false;

/// <summary>
/// Sort custom attributes.
/// </summary>
[Category("DecompilerSettings.Other")]
[Description("DecompilerSettings.SortCustomAttributes")]
public bool SortCustomAttributes {
get { return sortCustomAttributes; }
set {
if (sortCustomAttributes != value)
{
sortCustomAttributes = value;
OnPropertyChanged();
}
}
}

CSharpFormattingOptions csharpFormattingOptions;

[Browsable(false)]
Expand Down
1 change: 1 addition & 0 deletions ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@
<Compile Include="Properties\DecompilerVersionInfo.cs" />
<Compile Include="TypeSystem\ITypeDefinitionOrUnknown.cs" />
<Compile Include="Util\BitOperations.cs" />
<Compile Include="Util\DelegateComparer.cs" />
<Compile Include="Util\Index.cs" />
<Compile Include="Metadata\WebCilFile.cs" />
<None Include="Properties\DecompilerVersionInfo.template.cs" />
Expand Down
39 changes: 39 additions & 0 deletions ICSharpCode.Decompiler/Util/DelegateComparer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#nullable enable
// Copyright (c) 2010-2013 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;

namespace ICSharpCode.Decompiler.Util
{
public class DelegateComparer<T> : IComparer<T>
{
private readonly Func<T?, T?, int> func;

public DelegateComparer(Func<T?, T?, int> func)
{
this.func = func ?? throw new ArgumentNullException(nameof(func));
}

public int Compare(T? x, T? y)
{
return func(x, y);
}
}
}
9 changes: 9 additions & 0 deletions ILSpy/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions ILSpy/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,9 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.ShowInfoFromDebugSymbolsIfAvailable" xml:space="preserve">
<value>Show info from debug symbols, if available</value>
</data>
<data name="DecompilerSettings.SortCustomAttributes" xml:space="preserve">
<value>Sort custom attributes</value>
</data>
<data name="DecompilerSettings.SparseIntegerSwitch" xml:space="preserve">
<value>Detect switch on integer even if IL code does not use a jump table</value>
</data>
Expand Down
3 changes: 3 additions & 0 deletions ILSpy/Properties/Resources.zh-Hans.resx
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,9 @@
<data name="DecompilerSettings.ShowInfoFromDebugSymbolsIfAvailable" xml:space="preserve">
<value>显示调试符号中的信息(如果可用)</value>
</data>
<data name="DecompilerSettings.SortCustomAttributes" xml:space="preserve">
<value />
</data>
<data name="DecompilerSettings.SparseIntegerSwitch" xml:space="preserve">
<value>检测整型 switch 即使 IL 代码不使用跳转表</value>
</data>
Expand Down

0 comments on commit 8b76879

Please sign in to comment.