Skip to content

Commit a37b1b5

Browse files
WIP
1 parent 00baf1c commit a37b1b5

File tree

18 files changed

+316
-29
lines changed

18 files changed

+316
-29
lines changed

BuildTools/pre-commit

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55

66
set -eu
77

8-
DOTNET_FORMAT_VERSION=9.0.520307
8+
DOTNET_FORMAT_VERSION=10.0.100-preview.6.25358.103
99
DOTNET_PATH="$LOCALAPPDATA/ICSharpCode/ILSpy/dotnet-format-$DOTNET_FORMAT_VERSION"
1010
if [ ! -d "$DOTNET_PATH" ]; then
1111
echo "Downloading dotnet-format $DOTNET_FORMAT_VERSION..."
12-
dotnet tool install --tool-path "$DOTNET_PATH" dotnet-format --version "$DOTNET_FORMAT_VERSION" --add-source "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet9/nuget/v3/index.json"
12+
dotnet tool install --tool-path "$DOTNET_PATH" dotnet-format --version "$DOTNET_FORMAT_VERSION" --add-source "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet10-transport/nuget/v3/index.json"
1313
fi
1414

1515
"$DOTNET_PATH/dotnet-format.exe" --version

ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExtensionProperties.cs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,38 @@
11
using System.Collections.Generic;
2+
using System.Linq;
23

34
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
45
{
5-
static class ExtensionProperties
6+
internal static class ExtensionProperties
67
{
7-
extension<T>(ICollection<T> collection)
8+
extension<T>(ICollection<T> collection) where T : notnull
89
{
910
public bool IsEmpty => collection.Count == 0;
1011

11-
public void AddIfNotNull(T item) { }
12+
public int Test {
13+
get {
14+
return 42;
15+
}
16+
set {
17+
}
18+
}
1219

13-
public static void StaticExtension() { }
20+
public void AddIfNotNull(T item)
21+
{
22+
if (item != null)
23+
{
24+
collection.Add(item);
25+
}
26+
}
27+
28+
public T2 Cast<T2>(int index) where T2 : T
29+
{
30+
return (T2)(object)collection.ElementAt(index);
31+
}
32+
33+
public static void StaticExtension()
34+
{
35+
}
36+
}
1437
}
1538
}
16-
}

ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1993,5 +1993,23 @@ public void HasSpecialName()
19931993
Assert.That(@class.HasAttribute(KnownAttribute.SpecialName));
19941994
Assert.That(@struct.HasAttribute(KnownAttribute.SpecialName));
19951995
}
1996+
1997+
[Test]
1998+
public void ExtensionEverything()
1999+
{
2000+
var extensionEverything = GetTypeDefinition(typeof(ExtensionEverything));
2001+
Assert.That(extensionEverything.IsStatic, Is.True, "ExtensionEverything should be static");
2002+
Assert.That(extensionEverything.HasExtensions, Is.True, "ExtensionEverything should have extensions");
2003+
var info = extensionEverything.ExtensionInfo;
2004+
Assert.That(info, Is.Not.Null, "ExtensionEverything should have ExtensionInfo");
2005+
foreach (var method in extensionEverything.Methods)
2006+
{
2007+
Assert.That(method.IsStatic, Is.True, "Method should be static: " + method.Name);
2008+
ExtensionMemberInfo? infoOfImpl = info.InfoOfImplementationMember(method);
2009+
Assert.That(infoOfImpl, Is.Not.Null, "Method should have implementation info: " + method.Name);
2010+
ExtensionMemberInfo? infoOfExtension = info.InfoOfExtensionMember(infoOfImpl.Value.ExtensionMember);
2011+
Assert.That(infoOfExtension, Is.EqualTo(infoOfImpl), "Info of extension member should be equal to info of implementation member: " + method.Name);
2012+
}
2013+
}
19962014
}
19972015
}

ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemTestCase.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,4 +753,24 @@ public interface IMarshalAsTests
753753
[DispId(11)]
754754
void StopRouter();
755755
}
756+
757+
public static class ExtensionEverything
758+
{
759+
extension(int input)
760+
{
761+
public void Method() { }
762+
public void Method(char c) { }
763+
public string AsString => input.ToString();
764+
public string Test {
765+
get => "Test";
766+
set { }
767+
}
768+
public static void StaticMethod() { }
769+
public static void StaticMethod(double x) { }
770+
public static string StaticProperty => "StaticProperty";
771+
public static void GenericMethod<T>(T value)
772+
{
773+
}
774+
}
775+
}
756776
}

ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

Lines changed: 78 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,9 @@ public static bool MemberIsHidden(MetadataFile module, EntityHandle member, Deco
318318
return true;
319319
if (settings.FixedBuffers && name.StartsWith("<", StringComparison.Ordinal) && name.Contains("__FixedBuffer"))
320320
return true;
321-
if (settings.InlineArrays && name.StartsWith("<>y__InlineArray", StringComparison.Ordinal) && name.EndsWith("`1"))
321+
if (settings.InlineArrays && name.StartsWith("<>y__InlineArray", StringComparison.Ordinal) && name.EndsWith("`1", StringComparison.Ordinal))
322+
return true;
323+
if (settings.ExtensionMembers && name.StartsWith("<>E__", StringComparison.Ordinal))
322324
return true;
323325
}
324326
else if (type.IsCompilerGenerated(metadata))
@@ -1037,7 +1039,7 @@ public SyntaxTree Decompile(IEnumerable<EntityHandle> definitions)
10371039
break;
10381040
case HandleKind.MethodDefinition:
10391041
IMethod method = module.GetDefinition((MethodDefinitionHandle)entity);
1040-
syntaxTree.Members.Add(DoDecompile(method, decompileRun, new SimpleTypeResolveContext(method)));
1042+
syntaxTree.Members.Add(DoDecompile(method, decompileRun, new SimpleTypeResolveContext(method), null));
10411043
if (first)
10421044
{
10431045
parentTypeDef = method.DeclaringTypeDefinition;
@@ -1054,7 +1056,7 @@ public SyntaxTree Decompile(IEnumerable<EntityHandle> definitions)
10541056
break;
10551057
case HandleKind.PropertyDefinition:
10561058
IProperty property = module.GetDefinition((PropertyDefinitionHandle)entity);
1057-
syntaxTree.Members.Add(DoDecompile(property, decompileRun, new SimpleTypeResolveContext(property)));
1059+
syntaxTree.Members.Add(DoDecompile(property, decompileRun, new SimpleTypeResolveContext(property), null));
10581060
if (first)
10591061
{
10601062
parentTypeDef = property.DeclaringTypeDefinition;
@@ -1361,14 +1363,45 @@ EntityDeclaration DoDecompile(ITypeDefinition typeDef, DecompileRun decompileRun
13611363
partialTypeInfo = null;
13621364
}
13631365

1366+
if (settings.ExtensionMembers)
1367+
{
1368+
foreach (var group in typeDef.ExtensionInfo?.GetGroups() ?? [])
1369+
{
1370+
var ext = new ExtensionDeclaration();
1371+
ext.TypeParameters.AddRange(group.Key.DeclaringTypeDefinition.TypeParameters.Select(tp => typeSystemAstBuilder.ConvertTypeParameter(tp)));
1372+
ext.ReceiverParameters.Add(typeSystemAstBuilder.ConvertParameter(group.Key.Parameters.Single()));
1373+
AddAnnotationsToDeclaration(method, ext, function, parameterOffset);
1374+
ext.Constraints.AddRange(group.Key.DeclaringTypeDefinition.TypeParameters.Select(c => typeSystemAstBuilder.ConvertTypeParameterConstraint(c)));
1375+
1376+
foreach (var member in group)
1377+
{
1378+
IMember extMember = member.ExtensionMember;
1379+
if (member.ExtensionMember.IsAccessor)
1380+
{
1381+
extMember = member.ExtensionMember.AccessorOwner;
1382+
}
1383+
if (entityMap.Contains(extMember) || extMember.MetadataToken.IsNil)
1384+
{
1385+
// Member is already decompiled.
1386+
continue;
1387+
}
1388+
EntityDeclaration extMemberDecl = DoDecompileExtensionMember(extMember, typeDef.ExtensionInfo, decompileRun, decompilationContext);
1389+
ext.Members.Add(extMemberDecl);
1390+
entityMap.Add(extMember, extMemberDecl);
1391+
}
1392+
1393+
typeDecl.Members.Add(ext);
1394+
}
1395+
}
1396+
13641397
// Decompile members that are not compiler-generated.
13651398
foreach (var entity in allOrderedEntities)
13661399
{
13671400
if (entity.MetadataToken.IsNil || MemberIsHidden(module.MetadataFile, entity.MetadataToken, settings))
13681401
{
13691402
continue;
13701403
}
1371-
DoDecompileMember(entity, recordDecompiler, partialTypeInfo);
1404+
DoDecompileMember(entity, recordDecompiler, partialTypeInfo, typeDef.ExtensionInfo);
13721405
}
13731406

13741407
// Decompile compiler-generated members that are still needed.
@@ -1380,7 +1413,7 @@ EntityDeclaration DoDecompile(ITypeDefinition typeDef, DecompileRun decompileRun
13801413
// Member is already decompiled.
13811414
continue;
13821415
}
1383-
DoDecompileMember(entity, recordDecompiler, partialTypeInfo);
1416+
DoDecompileMember(entity, recordDecompiler, partialTypeInfo, typeDef.ExtensionInfo);
13841417
}
13851418

13861419
// Add all decompiled members to syntax tree in the correct order.
@@ -1470,13 +1503,18 @@ EntityDeclaration DoDecompile(ITypeDefinition typeDef, DecompileRun decompileRun
14701503
Instrumentation.DecompilerEventSource.Log.DoDecompileTypeDefinition(typeDef.FullName, watch.ElapsedMilliseconds);
14711504
}
14721505

1473-
void DoDecompileMember(IEntity entity, RecordDecompiler recordDecompiler, PartialTypeInfo partialType)
1506+
void DoDecompileMember(IEntity entity, RecordDecompiler recordDecompiler, PartialTypeInfo partialType, ExtensionInfo extensionInfo)
14741507
{
14751508
if (partialType != null && partialType.IsDeclaredMember(entity.MetadataToken))
14761509
{
14771510
return;
14781511
}
14791512

1513+
if (settings.ExtensionMembers && extensionInfo != null && entity is IMethod m && extensionInfo.InfoOfImplementationMember(m).HasValue)
1514+
{
1515+
return;
1516+
}
1517+
14801518
EntityDeclaration entityDecl;
14811519
switch (entity)
14821520
{
@@ -1497,15 +1535,15 @@ void DoDecompileMember(IEntity entity, RecordDecompiler recordDecompiler, Partia
14971535
{
14981536
return;
14991537
}
1500-
entityDecl = DoDecompile(property, decompileRun, decompilationContext.WithCurrentMember(property));
1538+
entityDecl = DoDecompile(property, decompileRun, decompilationContext.WithCurrentMember(property), null);
15011539
entityMap.Add(property, entityDecl);
15021540
break;
15031541
case IMethod method:
15041542
if (recordDecompiler?.MethodIsGenerated(method) == true)
15051543
{
15061544
return;
15071545
}
1508-
entityDecl = DoDecompile(method, decompileRun, decompilationContext.WithCurrentMember(method));
1546+
entityDecl = DoDecompile(method, decompileRun, decompilationContext.WithCurrentMember(method), null);
15091547
entityMap.Add(method, entityDecl);
15101548
foreach (var helper in AddInterfaceImplHelpers(entityDecl, method, typeSystemAstBuilder))
15111549
{
@@ -1543,6 +1581,19 @@ void DoDecompileMember(IEntity entity, RecordDecompiler recordDecompiler, Partia
15431581
}
15441582
}
15451583

1584+
private EntityDeclaration DoDecompileExtensionMember(IMember extMember, ExtensionInfo info, DecompileRun decompileRun, ITypeResolveContext decompilationContext)
1585+
{
1586+
switch (extMember)
1587+
{
1588+
case IProperty p:
1589+
return DoDecompile(p, decompileRun, decompilationContext.WithCurrentMember(p), info);
1590+
case IMethod m:
1591+
return DoDecompile(m, decompileRun, decompilationContext.WithCurrentMember(m), info);
1592+
}
1593+
1594+
throw new NotSupportedException($"Extension member {extMember} is not supported for decompilation.");
1595+
}
1596+
15461597
EnumValueDisplayMode DetectBestEnumValueDisplayMode(ITypeDefinition typeDef, MetadataFile module)
15471598
{
15481599
if (typeDef.HasAttribute(KnownAttribute.Flags))
@@ -1604,7 +1655,7 @@ EnumValueDisplayMode DetectBestEnumValueDisplayMode(ITypeDefinition typeDef, Met
16041655
return firstValue == 0 ? EnumValueDisplayMode.None : EnumValueDisplayMode.FirstOnly;
16051656
}
16061657

1607-
EntityDeclaration DoDecompile(IMethod method, DecompileRun decompileRun, ITypeResolveContext decompilationContext)
1658+
EntityDeclaration DoDecompile(IMethod method, DecompileRun decompileRun, ITypeResolveContext decompilationContext, ExtensionInfo extensionInfo)
16081659
{
16091660
Debug.Assert(decompilationContext.CurrentMember == method);
16101661
var watch = System.Diagnostics.Stopwatch.StartNew();
@@ -1630,7 +1681,7 @@ EntityDeclaration DoDecompile(IMethod method, DecompileRun decompileRun, ITypeRe
16301681
}
16311682
if (methodDefinition.HasBody())
16321683
{
1633-
DecompileBody(method, methodDecl, decompileRun, decompilationContext);
1684+
DecompileBody(method, methodDecl, decompileRun, decompilationContext, extensionInfo);
16341685
}
16351686
else if (!method.IsAbstract && method.DeclaringType.Kind != TypeKind.Interface)
16361687
{
@@ -1699,7 +1750,7 @@ internal static bool IsWindowsFormsInitializeComponentMethod(IMethod method)
16991750
return method.ReturnType.Kind == TypeKind.Void && method.Name == "InitializeComponent" && method.DeclaringTypeDefinition.GetNonInterfaceBaseTypes().Any(t => t.FullName == "System.Windows.Forms.Control");
17001751
}
17011752

1702-
void DecompileBody(IMethod method, EntityDeclaration entityDecl, DecompileRun decompileRun, ITypeResolveContext decompilationContext)
1753+
void DecompileBody(IMethod method, EntityDeclaration entityDecl, DecompileRun decompileRun, ITypeResolveContext decompilationContext, ExtensionInfo extensionInfo)
17031754
{
17041755
try
17051756
{
@@ -1708,6 +1759,14 @@ void DecompileBody(IMethod method, EntityDeclaration entityDecl, DecompileRun de
17081759
UseRefLocalsForAccurateOrderOfEvaluation = settings.UseRefLocalsForAccurateOrderOfEvaluation,
17091760
DebugInfo = DebugInfoProvider
17101761
};
1762+
int parameterOffset = 0;
1763+
if (extensionInfo != null)
1764+
{
1765+
if (!method.IsStatic)
1766+
parameterOffset = 1; // implementation method has an additional receiver parameter
1767+
method = extensionInfo.InfoOfExtensionMember(method).Value.ImplementationMethod;
1768+
}
1769+
17111770
var methodDef = metadata.GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken);
17121771
var body = BlockStatement.Null;
17131772
MethodBodyBlock methodBody;
@@ -1727,7 +1786,7 @@ void DecompileBody(IMethod method, EntityDeclaration entityDecl, DecompileRun de
17271786
var function = ilReader.ReadIL((MethodDefinitionHandle)method.MetadataToken, methodBody, cancellationToken: CancellationToken);
17281787
function.CheckInvariant(ILPhase.Normal);
17291788

1730-
AddAnnotationsToDeclaration(method, entityDecl, function);
1789+
AddAnnotationsToDeclaration(method, entityDecl, function, parameterOffset);
17311790

17321791
var localSettings = settings.Clone();
17331792
if (IsWindowsFormsInitializeComponentMethod(method))
@@ -1786,9 +1845,9 @@ void DecompileBody(IMethod method, EntityDeclaration entityDecl, DecompileRun de
17861845
}
17871846
}
17881847

1789-
internal static void AddAnnotationsToDeclaration(IMethod method, EntityDeclaration entityDecl, ILFunction function)
1848+
internal static void AddAnnotationsToDeclaration(IMethod method, EntityDeclaration entityDecl, ILFunction function, int parameterOffset = 0)
17901849
{
1791-
int i = 0;
1850+
int i = parameterOffset;
17921851
var parameters = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index);
17931852
foreach (var parameter in entityDecl.GetChildrenByRole(Roles.Parameter))
17941853
{
@@ -2023,7 +2082,7 @@ internal static bool IsFixedField(IField field, out IType type, out int elementC
20232082
return false;
20242083
}
20252084

2026-
EntityDeclaration DoDecompile(IProperty property, DecompileRun decompileRun, ITypeResolveContext decompilationContext)
2085+
EntityDeclaration DoDecompile(IProperty property, DecompileRun decompileRun, ITypeResolveContext decompilationContext, ExtensionInfo extensionInfo)
20272086
{
20282087
Debug.Assert(decompilationContext.CurrentMember == property);
20292088
var watch = System.Diagnostics.Stopwatch.StartNew();
@@ -2053,11 +2112,11 @@ EntityDeclaration DoDecompile(IProperty property, DecompileRun decompileRun, ITy
20532112
bool setterHasBody = property.CanSet && property.Setter.HasBody;
20542113
if (getterHasBody)
20552114
{
2056-
DecompileBody(property.Getter, getter, decompileRun, decompilationContext);
2115+
DecompileBody(property.Getter, getter, decompileRun, decompilationContext, extensionInfo);
20572116
}
20582117
if (setterHasBody)
20592118
{
2060-
DecompileBody(property.Setter, setter, decompileRun, decompilationContext);
2119+
DecompileBody(property.Setter, setter, decompileRun, decompilationContext, extensionInfo);
20612120
}
20622121
if (!getterHasBody && !setterHasBody && !property.IsAbstract && property.DeclaringType.Kind != TypeKind.Interface)
20632122
{
@@ -2113,11 +2172,11 @@ EntityDeclaration DoDecompile(IEvent ev, DecompileRun decompileRun, ITypeResolve
21132172
}
21142173
if (adderHasBody)
21152174
{
2116-
DecompileBody(ev.AddAccessor, ((CustomEventDeclaration)eventDecl).AddAccessor, decompileRun, decompilationContext);
2175+
DecompileBody(ev.AddAccessor, ((CustomEventDeclaration)eventDecl).AddAccessor, decompileRun, decompilationContext, null);
21172176
}
21182177
if (removerHasBody)
21192178
{
2120-
DecompileBody(ev.RemoveAccessor, ((CustomEventDeclaration)eventDecl).RemoveAccessor, decompileRun, decompilationContext);
2179+
DecompileBody(ev.RemoveAccessor, ((CustomEventDeclaration)eventDecl).RemoveAccessor, decompileRun, decompilationContext, null);
21212180
}
21222181
if (!adderHasBody && !removerHasBody && !ev.IsAbstract && ev.DeclaringType.Kind != TypeKind.Interface)
21232182
{

ICSharpCode.Decompiler/CSharp/CSharpLanguageVersion.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ public enum LanguageVersion
3636
CSharp11_0 = 1100,
3737
CSharp12_0 = 1200,
3838
CSharp13_0 = 1300,
39-
Preview = 1300,
39+
CSharp14_0 = 1400,
40+
Preview = 1400,
4041
Latest = 0x7FFFFFFF
4142
}
4243
}

ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2417,6 +2417,36 @@ public virtual void VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberD
24172417
EndNode(enumMemberDeclaration);
24182418
}
24192419

2420+
public virtual void VisitExtensionDeclaration(ExtensionDeclaration extensionDeclaration)
2421+
{
2422+
StartNode(extensionDeclaration);
2423+
WriteAttributes(extensionDeclaration.Attributes);
2424+
WriteModifiers(extensionDeclaration.ModifierTokens);
2425+
WriteKeyword(ExtensionDeclaration.ExtensionKeywordRole);
2426+
WriteTypeParameters(extensionDeclaration.TypeParameters);
2427+
Space(policy.SpaceBeforeMethodDeclarationParentheses);
2428+
WriteCommaSeparatedListInParenthesis(extensionDeclaration.ReceiverParameters, policy.SpaceWithinMethodDeclarationParentheses);
2429+
foreach (Constraint constraint in extensionDeclaration.Constraints)
2430+
{
2431+
constraint.AcceptVisitor(this);
2432+
}
2433+
OpenBrace(policy.ClassBraceStyle);
2434+
bool first = true;
2435+
foreach (var member in extensionDeclaration.Members)
2436+
{
2437+
if (!first)
2438+
{
2439+
for (int i = 0; i < policy.MinimumBlankLinesBetweenMembers; i++)
2440+
NewLine();
2441+
}
2442+
first = false;
2443+
member.AcceptVisitor(this);
2444+
}
2445+
CloseBrace(policy.ClassBraceStyle);
2446+
NewLine();
2447+
EndNode(extensionDeclaration);
2448+
}
2449+
24202450
public virtual void VisitEventDeclaration(EventDeclaration eventDeclaration)
24212451
{
24222452
StartNode(eventDeclaration);

0 commit comments

Comments
 (0)