Browse Source

Merge branch 'master' of https://github.com/icsharpcode/ILSpy

pull/2066/head
MysticBoy 6 years ago
parent
commit
d96b546db8
  1. 2
      DecompilerNuGetDemos.workbook
  2. 8
      ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj
  3. 2
      ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj
  4. 5
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  5. 2
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  6. 18
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs
  7. 13
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs
  8. 22
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  9. 12
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  10. 6
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  11. 3
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs
  12. 18
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  13. 3
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs
  14. 22
      ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs
  15. 29
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  16. 67
      ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs
  17. 3
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/Accessor.cs
  18. 1
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs
  19. 61
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  20. 20
      ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs
  21. 20
      ICSharpCode.Decompiler/DecompilerSettings.cs
  22. 6
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs
  23. 25
      ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs
  24. 2
      ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs
  25. 2
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  26. 8
      ICSharpCode.Decompiler/IL/ILReader.cs
  27. 14
      ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs
  28. 1
      ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs
  29. 9
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  30. 9
      ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
  31. 8
      ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs
  32. 24
      ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
  33. 2
      ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs
  34. 4
      ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
  35. 2
      ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs
  36. 2
      ICSharpCode.Decompiler/IL/Transforms/FixLoneIsInst.cs
  37. 36
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  38. 19
      ICSharpCode.Decompiler/IL/Transforms/IndexRangeTransform.cs
  39. 2
      ICSharpCode.Decompiler/IL/Transforms/IntroduceDynamicTypeOnLocals.cs
  40. 2
      ICSharpCode.Decompiler/IL/Transforms/IntroduceRefReadOnlyModifierOnLocals.cs
  41. 24
      ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
  42. 2
      ICSharpCode.Decompiler/IL/Transforms/LockTransform.cs
  43. 4
      ICSharpCode.Decompiler/IL/Transforms/NamedArgumentTransform.cs
  44. 2
      ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs
  45. 2
      ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs
  46. 2
      ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs
  47. 2
      ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs
  48. 2
      ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs
  49. 2
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
  50. 2
      ICSharpCode.Decompiler/IL/Transforms/UserDefinedLogicTransform.cs
  51. 2
      ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs
  52. 55
      ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs
  53. 33
      ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs
  54. 4
      ICSharpCode.Decompiler/Metadata/PEFile.cs
  55. 16
      ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs
  56. 25
      ICSharpCode.Decompiler/Output/TextTokenWriter.cs
  57. 6
      ICSharpCode.Decompiler/TypeSystem/IMethod.cs
  58. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs
  59. 41
      ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs
  60. 20
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs
  61. 3
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs
  62. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs
  63. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/SyntheticRangeIndexer.cs
  64. 1
      ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs
  65. 4
      ICSharpCode.Decompiler/Util/CollectionExtensions.cs
  66. 23
      ILSpy.AddIn/AssemblyFileFinder.cs
  67. 14
      ILSpy.AddIn/Commands/OpenCodeItemCommand.cs
  68. 5
      ILSpy.AddIn/Commands/OpenILSpyCommand.cs
  69. 39
      ILSpy.AddIn/ILSpy.AddIn.csproj
  70. 6
      ILSpy.AddIn/ILSpyInstance.cs
  71. 8
      ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj
  72. 13
      ILSpy/App.xaml.cs
  73. 6
      ILSpy/Commands/DecompileAllCommand.cs
  74. 7
      ILSpy/Commands/DisassembleAllCommand.cs
  75. 4
      ILSpy/ILSpy.csproj
  76. 2
      ILSpy/Languages/CSharpHighlightingTokenWriter.cs
  77. 3
      ILSpy/Languages/CSharpLanguage.cs
  78. 38
      ILSpy/LoadedAssembly.cs
  79. 9
      ILSpy/MainWindow.xaml.cs
  80. 1
      ILSpy/Metadata/Helpers.cs
  81. 2
      ILSpy/Properties/AssemblyInfo.template.cs
  82. 7
      ILSpy/SolutionWriter.cs

2
DecompilerNuGetDemos.workbook

@ -6,7 +6,7 @@ platforms:
- DotNetCore - DotNetCore
packages: packages:
- id: ICSharpCode.Decompiler - id: ICSharpCode.Decompiler
version: 6.0.0.5836 version: 6.1.0.5902
--- ---
Setup: load the references required to work with the decompiler Setup: load the references required to work with the decompiler

8
ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj

@ -8,7 +8,7 @@
<PackAsTool>true</PackAsTool> <PackAsTool>true</PackAsTool>
<AssemblyName>ilspycmd</AssemblyName> <AssemblyName>ilspycmd</AssemblyName>
<ToolCommandName>ilspycmd</ToolCommandName> <ToolCommandName>ilspycmd</ToolCommandName>
<Version>6.0.0.5836</Version> <Version>6.1.0.5902</Version>
<Description>Command-line decompiler using the ILSpy decompilation engine</Description> <Description>Command-line decompiler using the ILSpy decompilation engine</Description>
<Copyright>Copyright 2011-2020 AlphaSierraPapa</Copyright> <Copyright>Copyright 2011-2020 AlphaSierraPapa</Copyright>
<PackageProjectUrl>https://github.com/icsharpcode/ILSpy/</PackageProjectUrl> <PackageProjectUrl>https://github.com/icsharpcode/ILSpy/</PackageProjectUrl>
@ -16,8 +16,8 @@
<PackageIcon>ILSpyCmdNuGetPackageIcon.png</PackageIcon> <PackageIcon>ILSpyCmdNuGetPackageIcon.png</PackageIcon>
<RepositoryUrl>https://github.com/icsharpcode/ILSpy/</RepositoryUrl> <RepositoryUrl>https://github.com/icsharpcode/ILSpy/</RepositoryUrl>
<Company /> <Company />
<AssemblyVersion>6.0.0.0</AssemblyVersion> <AssemblyVersion>6.1.0.0</AssemblyVersion>
<FileVersion>6.0.0.0</FileVersion> <FileVersion>6.1.0.0</FileVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>ILSpy Team</Authors> <Authors>ILSpy Team</Authors>
</PropertyGroup> </PropertyGroup>
@ -42,7 +42,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release'"> <ItemGroup Condition="'$(Configuration)' == 'Release'">
<PackageReference Include="ICSharpCode.Decompiler" Version="6.0.0.5836" /> <PackageReference Include="ICSharpCode.Decompiler" Version="6.1.0.5902" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

2
ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj

@ -17,7 +17,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release'"> <ItemGroup Condition="'$(Configuration)' == 'Release'">
<PackageReference Include="ICSharpCode.Decompiler" Version="6.0.0.5836" /> <PackageReference Include="ICSharpCode.Decompiler" Version="6.1.0.5902" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

5
ICSharpCode.Decompiler.Tests/Helpers/Tester.cs

@ -185,7 +185,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
return Regex.Replace(il, @"'<PrivateImplementationDetails>\{[0-9A-F-]+\}'", "'<PrivateImplementationDetails>'"); return Regex.Replace(il, @"'<PrivateImplementationDetails>\{[0-9A-F-]+\}'", "'<PrivateImplementationDetails>'");
} }
static readonly string coreRefAsmPath = new DotNetCorePathFinder(new Version(3, 1)).GetReferenceAssemblyPath(".NETCoreApp, Version = v3.1"); static readonly string coreRefAsmPath = new DotNetCorePathFinder(TargetFrameworkIdentifier.NETCoreApp, new Version(3, 1)).GetReferenceAssemblyPath(".NETCoreApp, Version = v3.1");
static readonly string refAsmPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), static readonly string refAsmPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
@"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2"); @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2");
@ -257,6 +257,9 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
preprocessorSymbols.Add("LEGACY_CSC"); preprocessorSymbols.Add("LEGACY_CSC");
preprocessorSymbols.Add("LEGACY_VBC"); preprocessorSymbols.Add("LEGACY_VBC");
} }
if (flags.HasFlag(CompilerOptions.Preview)) {
preprocessorSymbols.Add("CS90");
}
return preprocessorSymbols; return preprocessorSymbols;
} }

2
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -199,7 +199,7 @@ namespace ICSharpCode.Decompiler.Tests
[Test] [Test]
public void LocalFunctions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) public void LocalFunctions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{ {
RunForLibrary(cscOptions: cscOptions); RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview);
} }
[Test] [Test]

18
ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs

@ -23,6 +23,12 @@ namespace LocalFunctions
{ {
internal class LocalFunctions internal class LocalFunctions
{ {
[AttributeUsage(AttributeTargets.All)]
internal class MyAttribute : Attribute
{
}
public class Generic<T1> where T1 : struct, ICloneable, IConvertible public class Generic<T1> where T1 : struct, ICloneable, IConvertible
{ {
public int MixedLocalFunction<T2>() where T2 : ICloneable, IConvertible public int MixedLocalFunction<T2>() where T2 : ICloneable, IConvertible
@ -31,15 +37,21 @@ namespace LocalFunctions
object z = this; object z = this;
for (int j = 0; j < 10; j++) { for (int j = 0; j < 10; j++) {
int i = 0; int i = 0;
i += NonStaticMethod6<object>(); i += NonStaticMethod6<object>(0);
int NonStaticMethod6<T3>() #if CS90
[My]
[return: My]
int NonStaticMethod6<[My] T3>([My] int unused)
#else
int NonStaticMethod6<T3>(int unused)
#endif
{ {
t2 = default(T2); t2 = default(T2);
int l = 0; int l = 0;
return NonStaticMethod6_1<T1>() + NonStaticMethod6_1<T2>() + z.GetHashCode(); return NonStaticMethod6_1<T1>() + NonStaticMethod6_1<T2>() + z.GetHashCode();
int NonStaticMethod6_1<T4>() int NonStaticMethod6_1<T4>()
{ {
return i + l + NonStaticMethod6<T4>() + StaticMethod1<decimal>(); return i + l + NonStaticMethod6<T4>(0) + StaticMethod1<decimal>();
} }
} }
} }

13
ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs

@ -356,5 +356,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
Console.WriteLine("normal exit"); Console.WriteLine("normal exit");
} }
internal IEnumerable<int> ForLoopWithYieldReturn(int end, int evil)
{
// This loop needs to pick the implicit "yield break;" as exit point
// in order to produce pretty code; not the "throw" which would
// be a less-pretty option.
for (int i = 0; i < end; i++) {
if (i == evil) {
throw new InvalidOperationException("Found evil number");
}
yield return i;
}
}
} }
} }

22
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -120,18 +120,12 @@ namespace ICSharpCode.Decompiler.CSharp
new DetectExitPoints(canIntroduceExitForReturn: true), new DetectExitPoints(canIntroduceExitForReturn: true),
new BlockILTransform { // per-block transforms new BlockILTransform { // per-block transforms
PostOrderTransforms = { PostOrderTransforms = {
//new UseExitPoints(),
new ConditionDetection(), new ConditionDetection(),
new LockTransform(), new LockTransform(),
new UsingTransform(), new UsingTransform(),
// CachedDelegateInitialization must run after ConditionDetection and before/in LoopingBlockTransform // CachedDelegateInitialization must run after ConditionDetection and before/in LoopingBlockTransform
// and must run before NullCoalescingTransform // and must run before NullCoalescingTransform
new CachedDelegateInitialization(), new CachedDelegateInitialization(),
// Run the assignment transform both before and after copy propagation.
// Before is necessary because inline assignments of constants are otherwise
// copy-propated (turned into two separate assignments of the constant).
// After is necessary because the assigned value might involve null coalescing/etc.
new StatementTransform(new ILInlining(), new TransformAssignment()),
new StatementTransform( new StatementTransform(
// per-block transforms that depend on each other, and thus need to // per-block transforms that depend on each other, and thus need to
// run interleaved (statement by statement). // run interleaved (statement by statement).
@ -411,6 +405,7 @@ namespace ICSharpCode.Decompiler.CSharp
typeSystemAstBuilder.AlwaysUseShortTypeNames = true; typeSystemAstBuilder.AlwaysUseShortTypeNames = true;
typeSystemAstBuilder.AddResolveResultAnnotations = true; typeSystemAstBuilder.AddResolveResultAnnotations = true;
typeSystemAstBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables; typeSystemAstBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables;
typeSystemAstBuilder.SupportInitAccessors = settings.InitAccessors;
return typeSystemAstBuilder; return typeSystemAstBuilder;
} }
@ -1351,8 +1346,16 @@ namespace ICSharpCode.Decompiler.CSharp
} }
entityDecl.AddAnnotation(function); entityDecl.AddAnnotation(function);
CleanUpMethodDeclaration(entityDecl, body, function, localSettings.DecompileMemberBodies);
} catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) {
throw new DecompilerException(module, method, innerException);
}
}
internal static void CleanUpMethodDeclaration(EntityDeclaration entityDecl, BlockStatement body, ILFunction function, bool decompileBody = true)
{
if (function.IsIterator) { if (function.IsIterator) {
if (localSettings.DecompileMemberBodies && !body.Descendants.Any(d => d is YieldReturnStatement || d is YieldBreakStatement)) { if (decompileBody && !body.Descendants.Any(d => d is YieldReturnStatement || d is YieldBreakStatement)) {
body.Add(new YieldBreakStatement()); body.Add(new YieldBreakStatement());
} }
if (function.IsAsync) { if (function.IsAsync) {
@ -1369,12 +1372,9 @@ namespace ICSharpCode.Decompiler.CSharp
RemoveAttribute(entityDecl, KnownAttribute.AsyncStateMachine); RemoveAttribute(entityDecl, KnownAttribute.AsyncStateMachine);
RemoveAttribute(entityDecl, KnownAttribute.DebuggerStepThrough); RemoveAttribute(entityDecl, KnownAttribute.DebuggerStepThrough);
} }
} catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) {
throw new DecompilerException(module, method, innerException);
}
} }
bool RemoveAttribute(EntityDeclaration entityDecl, KnownAttribute attributeType) internal static bool RemoveAttribute(EntityDeclaration entityDecl, KnownAttribute attributeType)
{ {
bool found = false; bool found = false;
foreach (var section in entityDecl.Attributes) { foreach (var section in entityDecl.Attributes) {

12
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -197,8 +197,7 @@ namespace ICSharpCode.Decompiler.CSharp
} else if (localFunction != null) { } else if (localFunction != null) {
var ide = new IdentifierExpression(localFunction.Name); var ide = new IdentifierExpression(localFunction.Name);
if (method.TypeArguments.Count > 0) { if (method.TypeArguments.Count > 0) {
int skipCount = localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters; ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
ide.TypeArguments.AddRange(method.TypeArguments.Skip(skipCount).Select(expressionBuilder.ConvertType));
} }
ide.AddAnnotation(localFunction); ide.AddAnnotation(localFunction);
target = ide.WithoutILInstruction() target = ide.WithoutILInstruction()
@ -1327,7 +1326,6 @@ namespace ICSharpCode.Decompiler.CSharp
target = default; target = default;
targetType = default; targetType = default;
methodName = localFunction.Name; methodName = localFunction.Name;
// TODO : think about how to handle generic local functions
} else if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) { } else if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) {
step = 5; step = 5;
targetType = method.Parameters[0].Type; targetType = method.Parameters[0].Type;
@ -1406,11 +1404,7 @@ namespace ICSharpCode.Decompiler.CSharp
} else { } else {
var ide = new IdentifierExpression(methodName); var ide = new IdentifierExpression(methodName);
if ((step & 2) != 0) { if ((step & 2) != 0) {
int skipCount = 0; ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
if (localFunction != null && method.TypeArguments.Count > 0) {
skipCount = localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters;
}
ide.TypeArguments.AddRange(method.TypeArguments.Skip(skipCount).Select(expressionBuilder.ConvertType));
} }
targetExpression = ide.WithRR(result); targetExpression = ide.WithRR(result);
} }
@ -1469,7 +1463,7 @@ namespace ICSharpCode.Decompiler.CSharp
method.DeclaringType, method.DeclaringType,
new IParameterizedMember[] { method } new IParameterizedMember[] { method }
) )
}, method.TypeArguments.Skip(localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters).ToArray() }, method.TypeArguments
); );
} }

6
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -92,6 +92,7 @@ namespace ICSharpCode.Decompiler.CSharp
this.astBuilder = new TypeSystemAstBuilder(resolver); this.astBuilder = new TypeSystemAstBuilder(resolver);
this.astBuilder.AlwaysUseShortTypeNames = true; this.astBuilder.AlwaysUseShortTypeNames = true;
this.astBuilder.AddResolveResultAnnotations = true; this.astBuilder.AddResolveResultAnnotations = true;
this.astBuilder.ShowAttributes = true;
this.astBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables; this.astBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables;
this.typeInference = new TypeInference(compilation) { Algorithm = TypeInferenceAlgorithm.Improved }; this.typeInference = new TypeInference(compilation) { Algorithm = TypeInferenceAlgorithm.Improved };
} }
@ -1983,7 +1984,7 @@ namespace ICSharpCode.Decompiler.CSharp
return SpecialType.UnknownType; return SpecialType.UnknownType;
} }
internal IEnumerable<ParameterDeclaration> MakeParameters(IReadOnlyList<IParameter> parameters, ILFunction function) IEnumerable<ParameterDeclaration> MakeParameters(IReadOnlyList<IParameter> parameters, ILFunction function)
{ {
var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index); var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index);
int i = 0; int i = 0;
@ -1992,9 +1993,6 @@ namespace ICSharpCode.Decompiler.CSharp
if (string.IsNullOrEmpty(pd.Name) && !pd.Type.IsArgList()) { if (string.IsNullOrEmpty(pd.Name) && !pd.Type.IsArgList()) {
// needs to be consistent with logic in ILReader.CreateILVarable(ParameterDefinition) // needs to be consistent with logic in ILReader.CreateILVarable(ParameterDefinition)
pd.Name = "P_" + i; pd.Name = "P_" + i;
// if this is a local function, we have to skip the parameters for closure references
if (settings.LocalFunctions && function.Kind == ILFunctionKind.LocalFunction && IL.Transforms.LocalFunctionDecompiler.IsClosureParameter(parameter, decompilationContext))
break;
} }
if (settings.AnonymousTypes && parameter.Type.ContainsAnonymousType()) if (settings.AnonymousTypes && parameter.Type.ContainsAnonymousType())
pd.Type = null; pd.Type = null;

3
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs

@ -24,6 +24,7 @@ using System.Linq;
using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
namespace ICSharpCode.Decompiler.CSharp.OutputVisitor namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{ {
@ -222,7 +223,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{ {
TypeSystemAstBuilder astBuilder = CreateAstBuilder(); TypeSystemAstBuilder astBuilder = CreateAstBuilder();
EntityDeclaration node = astBuilder.ConvertEntity(member); EntityDeclaration node = astBuilder.ConvertEntity(member);
if ((ConversionFlags & ConversionFlags.ShowDeclaringType) == ConversionFlags.ShowDeclaringType && member.DeclaringType != null) { if ((ConversionFlags & ConversionFlags.ShowDeclaringType) == ConversionFlags.ShowDeclaringType && member.DeclaringType != null && !(member is LocalFunctionMethod)) {
ConvertType(member.DeclaringType, writer, formattingPolicy); ConvertType(member.DeclaringType, writer, formattingPolicy);
writer.WriteToken(Roles.Dot, "."); writer.WriteToken(Roles.Dot, ".");
} }

18
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -1876,19 +1876,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement) public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement)
{ {
StartNode(localFunctionDeclarationStatement); StartNode(localFunctionDeclarationStatement);
localFunctionDeclarationStatement.Declaration.AcceptVisitor(this);
WriteModifiers(localFunctionDeclarationStatement.ModifierTokens);
localFunctionDeclarationStatement.ReturnType.AcceptVisitor(this);
Space();
WriteIdentifier(localFunctionDeclarationStatement.NameToken);
WriteTypeParameters(localFunctionDeclarationStatement.TypeParameters);
Space(policy.SpaceBeforeMethodDeclarationParentheses);
WriteCommaSeparatedListInParenthesis(localFunctionDeclarationStatement.Parameters, policy.SpaceWithinMethodDeclarationParentheses);
foreach (Constraint constraint in localFunctionDeclarationStatement.Constraints) {
constraint.AcceptVisitor(this);
}
WriteMethodBody(localFunctionDeclarationStatement.Body, policy.MethodBraceStyle);
EndNode(localFunctionDeclarationStatement); EndNode(localFunctionDeclarationStatement);
} }
@ -1939,7 +1927,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
WriteKeyword("get", PropertyDeclaration.GetKeywordRole); WriteKeyword("get", PropertyDeclaration.GetKeywordRole);
style = policy.PropertyGetBraceStyle; style = policy.PropertyGetBraceStyle;
} else if (accessor.Role == PropertyDeclaration.SetterRole) { } else if (accessor.Role == PropertyDeclaration.SetterRole) {
if (accessor.Keyword.Role == PropertyDeclaration.InitKeywordRole) {
WriteKeyword("init", PropertyDeclaration.InitKeywordRole);
} else {
WriteKeyword("set", PropertyDeclaration.SetKeywordRole); WriteKeyword("set", PropertyDeclaration.SetKeywordRole);
}
style = policy.PropertySetBraceStyle; style = policy.PropertySetBraceStyle;
} else if (accessor.Role == CustomEventDeclaration.AddAccessorRole) { } else if (accessor.Role == CustomEventDeclaration.AddAccessorRole) {
WriteKeyword("add", CustomEventDeclaration.AddKeywordRole); WriteKeyword("add", CustomEventDeclaration.AddKeywordRole);

3
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs

@ -33,6 +33,7 @@ using static ICSharpCode.Decompiler.Metadata.MetadataExtensions;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.Solution;
using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.DebugInfo;
using System.Collections.Concurrent;
namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
{ {
@ -205,7 +206,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
var progress = ProgressIndicator; var progress = ProgressIndicator;
DecompilerTypeSystem ts = new DecompilerTypeSystem(module, AssemblyResolver, Settings); DecompilerTypeSystem ts = new DecompilerTypeSystem(module, AssemblyResolver, Settings);
Parallel.ForEach( Parallel.ForEach(
files, Partitioner.Create(files, loadBalance: true),
new ParallelOptions { new ParallelOptions {
MaxDegreeOfParallelism = this.MaxDegreeOfParallelism, MaxDegreeOfParallelism = this.MaxDegreeOfParallelism,
CancellationToken = cancellationToken CancellationToken = cancellationToken

22
ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs

@ -17,6 +17,7 @@ namespace ICSharpCode.Decompiler.CSharp
static readonly Decompiler.TypeSystem.GenericContext genericContext = default; static readonly Decompiler.TypeSystem.GenericContext genericContext = default;
readonly HashSet<string> namespaces; readonly HashSet<string> namespaces;
readonly HashSet<IType> visitedTypes = new HashSet<IType>();
public RequiredNamespaceCollector(HashSet<string> namespaces) public RequiredNamespaceCollector(HashSet<string> namespaces)
{ {
@ -92,17 +93,18 @@ namespace ICSharpCode.Decompiler.CSharp
CollectNamespacesForTypeReference(field.ReturnType); CollectNamespacesForTypeReference(field.ReturnType);
break; break;
case IMethod method: case IMethod method:
HandleAttributes(method.GetAttributes());
HandleAttributes(method.GetReturnTypeAttributes());
CollectNamespacesForTypeReference(method.ReturnType);
foreach (var param in method.Parameters) {
HandleAttributes(param.GetAttributes());
CollectNamespacesForTypeReference(param.Type);
}
HandleTypeParameters(method.TypeParameters);
var reader = module.PEFile.Reader; var reader = module.PEFile.Reader;
var parts = mappingInfo.GetMethodParts((MethodDefinitionHandle)method.MetadataToken).ToList(); var parts = mappingInfo.GetMethodParts((MethodDefinitionHandle)method.MetadataToken).ToList();
foreach (var part in parts) { foreach (var part in parts) {
var partMethod = module.ResolveMethod(part, genericContext);
HandleAttributes(partMethod.GetAttributes());
HandleAttributes(partMethod.GetReturnTypeAttributes());
CollectNamespacesForTypeReference(partMethod.ReturnType);
foreach (var param in partMethod.Parameters) {
HandleAttributes(param.GetAttributes());
CollectNamespacesForTypeReference(param.Type);
}
HandleTypeParameters(partMethod.TypeParameters);
HandleOverrides(part.GetMethodImplementations(module.metadata), module); HandleOverrides(part.GetMethodImplementations(module.metadata), module);
var methodDef = module.metadata.GetMethodDefinition(part); var methodDef = module.metadata.GetMethodDefinition(part);
if (method.HasBody) { if (method.HasBody) {
@ -141,13 +143,15 @@ namespace ICSharpCode.Decompiler.CSharp
void CollectNamespacesForTypeReference(IType type) void CollectNamespacesForTypeReference(IType type)
{ {
if (!visitedTypes.Add(type))
return;
switch (type) { switch (type) {
case ParameterizedType parameterizedType: case ParameterizedType parameterizedType:
namespaces.Add(parameterizedType.Namespace); namespaces.Add(parameterizedType.Namespace);
CollectNamespacesForTypeReference(parameterizedType.GenericType); CollectNamespacesForTypeReference(parameterizedType.GenericType);
foreach (var arg in parameterizedType.TypeArguments) foreach (var arg in parameterizedType.TypeArguments)
CollectNamespacesForTypeReference(arg); CollectNamespacesForTypeReference(arg);
break; return; // no need to collect base types again
case TypeWithElementType typeWithElementType: case TypeWithElementType typeWithElementType:
CollectNamespacesForTypeReference(typeWithElementType.ElementType); CollectNamespacesForTypeReference(typeWithElementType.ElementType);
break; break;

29
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -1002,34 +1002,19 @@ namespace ICSharpCode.Decompiler.CSharp
LocalFunctionDeclarationStatement TranslateFunction(ILFunction function) LocalFunctionDeclarationStatement TranslateFunction(ILFunction function)
{ {
var stmt = new LocalFunctionDeclarationStatement();
var nestedBuilder = new StatementBuilder(typeSystem, exprBuilder.decompilationContext, function, settings, cancellationToken); var nestedBuilder = new StatementBuilder(typeSystem, exprBuilder.decompilationContext, function, settings, cancellationToken);
stmt.Name = function.Name; var astBuilder = exprBuilder.astBuilder;
stmt.Parameters.AddRange(exprBuilder.MakeParameters(function.Parameters, function)); var method = (MethodDeclaration)astBuilder.ConvertEntity(function.ReducedMethod);
stmt.ReturnType = exprBuilder.ConvertType(function.Method.ReturnType); method.Body = nestedBuilder.ConvertAsBlock(function.Body);
stmt.Body = nestedBuilder.ConvertAsBlock(function.Body);
Comment prev = null; Comment prev = null;
foreach (string warning in function.Warnings) { foreach (string warning in function.Warnings) {
stmt.Body.InsertChildAfter(prev, prev = new Comment(warning), Roles.Comment); method.Body.InsertChildAfter(prev, prev = new Comment(warning), Roles.Comment);
} }
if (function.Method.TypeParameters.Count > 0) { CSharpDecompiler.CleanUpMethodDeclaration(method, method.Body, function);
var astBuilder = exprBuilder.astBuilder; CSharpDecompiler.RemoveAttribute(method, KnownAttribute.CompilerGenerated);
if (astBuilder.ShowTypeParameters) { var stmt = new LocalFunctionDeclarationStatement(method);
int skipCount = function.ReducedMethod.NumberOfCompilerGeneratedTypeParameters;
stmt.TypeParameters.AddRange(function.Method.TypeParameters.Skip(skipCount).Select(t => astBuilder.ConvertTypeParameter(t)));
if (astBuilder.ShowTypeParameterConstraints) {
stmt.Constraints.AddRange(function.Method.TypeParameters.Skip(skipCount).Select(t => astBuilder.ConvertTypeParameterConstraint(t)).Where(c => c != null));
}
}
}
if (function.IsAsync) {
stmt.Modifiers |= Modifiers.Async;
}
if (settings.StaticLocalFunctions && function.ReducedMethod.IsStaticLocalFunction) {
stmt.Modifiers |= Modifiers.Static;
}
stmt.AddAnnotation(new MemberResolveResult(null, function.ReducedMethod)); stmt.AddAnnotation(new MemberResolveResult(null, function.ReducedMethod));
stmt.WithILInstruction(function); stmt.WithILInstruction(function);
return stmt; return stmt;

67
ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs

@ -16,69 +16,22 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System.Collections.Generic;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
namespace ICSharpCode.Decompiler.CSharp.Syntax namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
public class LocalFunctionDeclarationStatement : Statement public class LocalFunctionDeclarationStatement : Statement
{ {
public AstNodeCollection<TypeParameterDeclaration> TypeParameters { public static readonly Role<MethodDeclaration> MethodDeclarationRole = new Role<MethodDeclaration>("Method");
get { return GetChildrenByRole(Roles.TypeParameter); }
}
public CSharpTokenNode LParToken {
get { return GetChildByRole(Roles.LPar); }
}
public AstNodeCollection<ParameterDeclaration> Parameters {
get { return GetChildrenByRole(Roles.Parameter); }
}
public CSharpTokenNode RParToken {
get { return GetChildByRole(Roles.RPar); }
}
public AstNodeCollection<Constraint> Constraints {
get { return GetChildrenByRole(Roles.Constraint); }
}
public BlockStatement Body {
get { return GetChildByRole(Roles.Body); }
set { SetChildByRole(Roles.Body, value); }
}
public Modifiers Modifiers { public MethodDeclaration Declaration {
get { return EntityDeclaration.GetModifiers(this); } get { return GetChildByRole(MethodDeclarationRole); }
set { EntityDeclaration.SetModifiers(this, value); } set { SetChildByRole(MethodDeclarationRole, value); }
} }
public bool HasModifier(Modifiers mod) public LocalFunctionDeclarationStatement(MethodDeclaration methodDeclaration)
{ {
return (Modifiers & mod) == mod; AddChild(methodDeclaration, MethodDeclarationRole);
}
public IEnumerable<CSharpModifierToken> ModifierTokens {
get { return GetChildrenByRole(EntityDeclaration.ModifierRole); }
}
public virtual string Name {
get {
return GetChildByRole(Roles.Identifier).Name;
}
set {
SetChildByRole(Roles.Identifier, Identifier.Create(value, TextLocation.Empty));
}
}
public virtual Identifier NameToken {
get { return GetChildByRole(Roles.Identifier); }
set { SetChildByRole(Roles.Identifier, value); }
}
public virtual AstType ReturnType {
get { return GetChildByRole(Roles.Type); }
set { SetChildByRole(Roles.Type, value); }
} }
public override void AcceptVisitor(IAstVisitor visitor) public override void AcceptVisitor(IAstVisitor visitor)
@ -98,13 +51,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
protected internal override bool DoMatch(AstNode other, Match match) protected internal override bool DoMatch(AstNode other, Match match)
{ {
LocalFunctionDeclarationStatement o = other as LocalFunctionDeclarationStatement; return other is LocalFunctionDeclarationStatement o && Declaration.DoMatch(o.Declaration, match);
return o != null && MatchString(this.Name, o.Name)
&& (this.Modifiers == Modifiers.Any || this.Modifiers == o.Modifiers)
&& this.ReturnType.DoMatch(o.ReturnType, match)
&& this.TypeParameters.DoMatch(o.TypeParameters, match)
&& this.Parameters.DoMatch(o.Parameters, match) && this.Constraints.DoMatch(o.Constraints, match)
&& this.Body.DoMatch(o.Body, match);
} }
} }
} }

3
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/Accessor.cs

@ -72,12 +72,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
/// <summary> /// <summary>
/// Gets the 'get'/'set'/'add'/'remove' keyword /// Gets the 'get'/'set'/'init'/'add'/'remove' keyword
/// </summary> /// </summary>
public CSharpTokenNode Keyword { public CSharpTokenNode Keyword {
get { get {
for (AstNode child = this.FirstChild; child != null; child = child.NextSibling) { for (AstNode child = this.FirstChild; child != null; child = child.NextSibling) {
if (child.Role == PropertyDeclaration.GetKeywordRole || child.Role == PropertyDeclaration.SetKeywordRole if (child.Role == PropertyDeclaration.GetKeywordRole || child.Role == PropertyDeclaration.SetKeywordRole
|| child.Role == PropertyDeclaration.InitKeywordRole
|| child.Role == CustomEventDeclaration.AddKeywordRole || child.Role == CustomEventDeclaration.RemoveKeywordRole) || child.Role == CustomEventDeclaration.AddKeywordRole || child.Role == CustomEventDeclaration.RemoveKeywordRole)
{ {
return (CSharpTokenNode)child; return (CSharpTokenNode)child;

1
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs

@ -32,6 +32,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
public static readonly TokenRole GetKeywordRole = new TokenRole ("get"); public static readonly TokenRole GetKeywordRole = new TokenRole ("get");
public static readonly TokenRole SetKeywordRole = new TokenRole ("set"); public static readonly TokenRole SetKeywordRole = new TokenRole ("set");
public static readonly TokenRole InitKeywordRole = new TokenRole ("init");
public static readonly Role<Accessor> GetterRole = new Role<Accessor>("Getter", Accessor.Null); public static readonly Role<Accessor> GetterRole = new Role<Accessor>("Getter", Accessor.Null);
public static readonly Role<Accessor> SetterRole = new Role<Accessor>("Setter", Accessor.Null); public static readonly Role<Accessor> SetterRole = new Role<Accessor>("Setter", Accessor.Null);
public static readonly Role<Expression> ExpressionBodyRole = new Role<Expression>("ExpressionBody", Expression.Null); public static readonly Role<Expression> ExpressionBodyRole = new Role<Expression>("ExpressionBody", Expression.Null);

61
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -21,6 +21,7 @@ using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reflection;
using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.TypeSystem; using ICSharpCode.Decompiler.CSharp.TypeSystem;
using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.Semantics;
@ -205,6 +206,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// The default value is <see langword="false" />. /// The default value is <see langword="false" />.
/// </summary> /// </summary>
public bool PrintIntegralValuesAsHex { get; set; } public bool PrintIntegralValuesAsHex { get; set; }
/// <summary>
/// Controls whether C# 9 "init;" accessors are supported.
/// If disabled, emits "set /*init*/;" instead.
/// </summary>
public bool SupportInitAccessors { get; set; }
#endregion #endregion
#region Convert Type #region Convert Type
@ -1363,7 +1370,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return ConvertDestructor((IMethod)entity); return ConvertDestructor((IMethod)entity);
case SymbolKind.Accessor: case SymbolKind.Accessor:
IMethod accessor = (IMethod)entity; IMethod accessor = (IMethod)entity;
return ConvertAccessor(accessor, accessor.AccessorOwner != null ? accessor.AccessorOwner.Accessibility : Accessibility.None, false); Accessibility ownerAccessibility = accessor.AccessorOwner?.Accessibility ?? Accessibility.None;
return ConvertAccessor(accessor, accessor.AccessorKind, ownerAccessibility, false);
default: default:
throw new ArgumentException("Invalid value for SymbolKind: " + entity.SymbolKind); throw new ArgumentException("Invalid value for SymbolKind: " + entity.SymbolKind);
} }
@ -1554,15 +1562,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
} }
Accessor ConvertAccessor(IMethod accessor, Accessibility ownerAccessibility, bool addParameterAttribute) Accessor ConvertAccessor(IMethod accessor, MethodSemanticsAttributes kind, Accessibility ownerAccessibility, bool addParameterAttribute)
{ {
if (accessor == null) if (accessor == null)
return Accessor.Null; return Accessor.Null;
Accessor decl = new Accessor(); Accessor decl = new Accessor();
if (this.ShowAccessibility && accessor.Accessibility != ownerAccessibility)
decl.Modifiers = ModifierFromAccessibility(accessor.Accessibility);
if (accessor.HasReadonlyModifier())
decl.Modifiers |= Modifiers.Readonly;
if (ShowAttributes) { if (ShowAttributes) {
decl.Attributes.AddRange(ConvertAttributes(accessor.GetAttributes())); decl.Attributes.AddRange(ConvertAttributes(accessor.GetAttributes()));
decl.Attributes.AddRange(ConvertAttributes(accessor.GetReturnTypeAttributes(), "return")); decl.Attributes.AddRange(ConvertAttributes(accessor.GetReturnTypeAttributes(), "return"));
@ -1570,10 +1574,35 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
decl.Attributes.AddRange(ConvertAttributes(accessor.Parameters.Last().GetAttributes(), "param")); decl.Attributes.AddRange(ConvertAttributes(accessor.Parameters.Last().GetAttributes(), "param"));
} }
} }
if (this.ShowAccessibility && accessor.Accessibility != ownerAccessibility)
decl.Modifiers = ModifierFromAccessibility(accessor.Accessibility);
if (accessor.HasReadonlyModifier())
decl.Modifiers |= Modifiers.Readonly;
TokenRole keywordRole = kind switch
{
MethodSemanticsAttributes.Getter => PropertyDeclaration.GetKeywordRole,
MethodSemanticsAttributes.Setter => PropertyDeclaration.SetKeywordRole,
MethodSemanticsAttributes.Adder => CustomEventDeclaration.AddKeywordRole,
MethodSemanticsAttributes.Remover => CustomEventDeclaration.RemoveKeywordRole,
_ => null
};
if (kind == MethodSemanticsAttributes.Setter && SupportInitAccessors && accessor.IsInitOnly) {
keywordRole = PropertyDeclaration.InitKeywordRole;
}
if (keywordRole != null) {
decl.AddChild(new CSharpTokenNode(TextLocation.Empty, keywordRole), keywordRole);
}
if (accessor.IsInitOnly && keywordRole != PropertyDeclaration.InitKeywordRole) {
decl.AddChild(new Comment("init", CommentType.MultiLine), Roles.Comment);
}
if (AddResolveResultAnnotations) { if (AddResolveResultAnnotations) {
decl.AddAnnotation(new MemberResolveResult(null, accessor)); decl.AddAnnotation(new MemberResolveResult(null, accessor));
} }
if (GenerateBody) {
decl.Body = GenerateBodyBlock(); decl.Body = GenerateBodyBlock();
} else {
decl.AddChild(new CSharpTokenNode(TextLocation.Empty, Roles.Semicolon), Roles.Semicolon);
}
return decl; return decl;
} }
@ -1592,8 +1621,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
ct.HasReadOnlySpecifier = true; ct.HasReadOnlySpecifier = true;
} }
decl.Name = property.Name; decl.Name = property.Name;
decl.Getter = ConvertAccessor(property.Getter, property.Accessibility, false); decl.Getter = ConvertAccessor(property.Getter, MethodSemanticsAttributes.Getter, property.Accessibility, false);
decl.Setter = ConvertAccessor(property.Setter, property.Accessibility, true); decl.Setter = ConvertAccessor(property.Setter, MethodSemanticsAttributes.Setter, property.Accessibility, true);
decl.PrivateImplementationType = GetExplicitInterfaceType (property); decl.PrivateImplementationType = GetExplicitInterfaceType (property);
MergeReadOnlyModifiers(decl, decl.Getter, decl.Setter); MergeReadOnlyModifiers(decl, decl.Getter, decl.Setter);
return decl; return decl;
@ -1624,8 +1653,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
foreach (IParameter p in indexer.Parameters) { foreach (IParameter p in indexer.Parameters) {
decl.Parameters.Add(ConvertParameter(p)); decl.Parameters.Add(ConvertParameter(p));
} }
decl.Getter = ConvertAccessor(indexer.Getter, indexer.Accessibility, false); decl.Getter = ConvertAccessor(indexer.Getter, MethodSemanticsAttributes.Getter, indexer.Accessibility, false);
decl.Setter = ConvertAccessor(indexer.Setter, indexer.Accessibility, true); decl.Setter = ConvertAccessor(indexer.Setter, MethodSemanticsAttributes.Setter, indexer.Accessibility, true);
decl.PrivateImplementationType = GetExplicitInterfaceType (indexer); decl.PrivateImplementationType = GetExplicitInterfaceType (indexer);
MergeReadOnlyModifiers(decl, decl.Getter, decl.Setter); MergeReadOnlyModifiers(decl, decl.Getter, decl.Setter);
return decl; return decl;
@ -1644,8 +1673,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
decl.ReturnType = ConvertType(ev.ReturnType); decl.ReturnType = ConvertType(ev.ReturnType);
decl.Name = ev.Name; decl.Name = ev.Name;
decl.AddAccessor = ConvertAccessor(ev.AddAccessor, ev.Accessibility, true); decl.AddAccessor = ConvertAccessor(ev.AddAccessor, MethodSemanticsAttributes.Adder, ev.Accessibility, true);
decl.RemoveAccessor = ConvertAccessor(ev.RemoveAccessor, ev.Accessibility, true); decl.RemoveAccessor = ConvertAccessor(ev.RemoveAccessor, MethodSemanticsAttributes.Remover, ev.Accessibility, true);
decl.PrivateImplementationType = GetExplicitInterfaceType (ev); decl.PrivateImplementationType = GetExplicitInterfaceType (ev);
MergeReadOnlyModifiers(decl, decl.AddAccessor, decl.RemoveAccessor); MergeReadOnlyModifiers(decl, decl.AddAccessor, decl.RemoveAccessor);
return decl; return decl;
@ -1799,6 +1828,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return !member.IsStatic; return !member.IsStatic;
case SymbolKind.Destructor: case SymbolKind.Destructor:
return false; return false;
case SymbolKind.Method:
return !((IMethod)member).IsLocalFunction;
default: default:
return true; return true;
} }
@ -1811,7 +1842,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
m |= ModifierFromAccessibility (member.Accessibility); m |= ModifierFromAccessibility (member.Accessibility);
} }
if (this.ShowModifiers) { if (this.ShowModifiers) {
if (member.IsStatic) { if (member is LocalFunctionMethod localFunction) {
if (localFunction.IsStaticLocalFunction) {
m |= Modifiers.Static;
}
} else if (member.IsStatic) {
m |= Modifiers.Static; m |= Modifiers.Static;
} else { } else {
var declaringType = member.DeclaringType; var declaringType = member.DeclaringType;

20
ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs

@ -55,56 +55,56 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
public override TResult VisitMethodDeclaration(MethodDeclaration methodDeclaration) public override TResult VisitMethodDeclaration(MethodDeclaration methodDeclaration)
{ {
Debug.Assert(currentMethod == null); var oldMethod = currentMethod;
try { try {
currentMethod = methodDeclaration.GetSymbol() as IMethod; currentMethod = methodDeclaration.GetSymbol() as IMethod;
return base.VisitMethodDeclaration(methodDeclaration); return base.VisitMethodDeclaration(methodDeclaration);
} finally { } finally {
currentMethod = null; currentMethod = oldMethod;
} }
} }
public override TResult VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) public override TResult VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration)
{ {
Debug.Assert(currentMethod == null); var oldMethod = currentMethod;
try { try {
currentMethod = constructorDeclaration.GetSymbol() as IMethod; currentMethod = constructorDeclaration.GetSymbol() as IMethod;
return base.VisitConstructorDeclaration(constructorDeclaration); return base.VisitConstructorDeclaration(constructorDeclaration);
} finally { } finally {
currentMethod = null; currentMethod = oldMethod;
} }
} }
public override TResult VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) public override TResult VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration)
{ {
Debug.Assert(currentMethod == null); var oldMethod = currentMethod;
try { try {
currentMethod = destructorDeclaration.GetSymbol() as IMethod; currentMethod = destructorDeclaration.GetSymbol() as IMethod;
return base.VisitDestructorDeclaration(destructorDeclaration); return base.VisitDestructorDeclaration(destructorDeclaration);
} finally { } finally {
currentMethod = null; currentMethod = oldMethod;
} }
} }
public override TResult VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) public override TResult VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration)
{ {
Debug.Assert(currentMethod == null); var oldMethod = currentMethod;
try { try {
currentMethod = operatorDeclaration.GetSymbol() as IMethod; currentMethod = operatorDeclaration.GetSymbol() as IMethod;
return base.VisitOperatorDeclaration(operatorDeclaration); return base.VisitOperatorDeclaration(operatorDeclaration);
} finally { } finally {
currentMethod = null; currentMethod = oldMethod;
} }
} }
public override TResult VisitAccessor(Accessor accessor) public override TResult VisitAccessor(Accessor accessor)
{ {
Debug.Assert(currentMethod == null); var oldMethod = currentMethod;
try { try {
currentMethod = accessor.GetSymbol() as IMethod; currentMethod = accessor.GetSymbol() as IMethod;
return base.VisitAccessor(accessor); return base.VisitAccessor(accessor);
} finally { } finally {
currentMethod = null; currentMethod = oldMethod;
} }
} }
} }

20
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -116,12 +116,13 @@ namespace ICSharpCode.Decompiler
} }
if (languageVersion < CSharp.LanguageVersion.Preview) { if (languageVersion < CSharp.LanguageVersion.Preview) {
nativeIntegers = false; nativeIntegers = false;
initAccessors = false;
} }
} }
public CSharp.LanguageVersion GetMinimumRequiredVersion() public CSharp.LanguageVersion GetMinimumRequiredVersion()
{ {
if (nativeIntegers) if (nativeIntegers || initAccessors)
return CSharp.LanguageVersion.Preview; return CSharp.LanguageVersion.Preview;
if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement || staticLocalFunctions || ranges) if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement || staticLocalFunctions || ranges)
return CSharp.LanguageVersion.CSharp8_0; return CSharp.LanguageVersion.CSharp8_0;
@ -163,6 +164,23 @@ namespace ICSharpCode.Decompiler
} }
} }
bool initAccessors = true;
/// <summary>
/// Use C# 9 <c>init;</c> property accessors.
/// </summary>
[Category("C# 9.0 (experimental)")]
[Description("DecompilerSettings.InitAccessors")]
public bool InitAccessors {
get { return initAccessors; }
set {
if (initAccessors != value) {
initAccessors = value;
OnPropertyChanged();
}
}
}
bool anonymousMethods = true; bool anonymousMethods = true;
/// <summary> /// <summary>

6
ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs

@ -33,9 +33,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// <summary> /// <summary>
/// Decompiler step for C# 5 async/await. /// Decompiler step for C# 5 async/await.
/// </summary> /// </summary>
class AsyncAwaitDecompiler : IILTransform public class AsyncAwaitDecompiler : IILTransform
{ {
public static bool IsCompilerGeneratedStateMachine(TypeDefinitionHandle type, MetadataReader metadata) internal static bool IsCompilerGeneratedStateMachine(TypeDefinitionHandle type, MetadataReader metadata)
{ {
TypeDefinition td; TypeDefinition td;
if (type.IsNil || (td = metadata.GetTypeDefinition(type)).GetDeclaringType().IsNil) if (type.IsNil || (td = metadata.GetTypeDefinition(type)).GetDeclaringType().IsNil)
@ -50,7 +50,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return false; return false;
} }
public static bool IsCompilerGeneratedMainMethod(Metadata.PEFile module, MethodDefinitionHandle method) internal static bool IsCompilerGeneratedMainMethod(Metadata.PEFile module, MethodDefinitionHandle method)
{ {
var metadata = module.Metadata; var metadata = module.Metadata;
var definition = metadata.GetMethodDefinition(method); var definition = metadata.GetMethodDefinition(method);

25
ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs

@ -200,11 +200,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// * use only a single exit point if at all possible /// * use only a single exit point if at all possible
/// * minimize the amount of code in the in-loop partition /// * minimize the amount of code in the in-loop partition
/// (thus: maximize the amount of code in the out-of-loop partition) /// (thus: maximize the amount of code in the out-of-loop partition)
/// "amount of code" could be measured as:
/// * number of basic blocks
/// * number of instructions directly in those basic blocks (~= number of statements)
/// * number of instructions in those basic blocks (~= number of expressions)
/// (we currently use the number of statements)
/// ///
/// Observations: /// Observations:
/// * If a node is in-loop, so are all its ancestors in the dominator tree (up to the loop entry point) /// * If a node is in-loop, so are all its ancestors in the dominator tree (up to the loop entry point)
@ -315,6 +310,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
} }
ControlFlowNode exitPoint = null; ControlFlowNode exitPoint = null;
int exitPointILOffset = -1; int exitPointILOffset = -1;
ConsiderReturnAsExitPoint((Block)loopHead.UserData, ref exitPoint, ref exitPointILOffset);
foreach (var node in loopHead.DominatorTreeChildren) { foreach (var node in loopHead.DominatorTreeChildren) {
PickExitPoint(node, ref exitPoint, ref exitPointILOffset); PickExitPoint(node, ref exitPoint, ref exitPointILOffset);
} }
@ -459,11 +455,30 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return; // don't visit children, they are likely to have even later IL offsets and we'd end up return; // don't visit children, they are likely to have even later IL offsets and we'd end up
// moving almost all of the code into the loop. // moving almost all of the code into the loop.
} }
ConsiderReturnAsExitPoint(block, ref exitPoint, ref exitPointILOffset);
foreach (var child in node.DominatorTreeChildren) { foreach (var child in node.DominatorTreeChildren) {
PickExitPoint(child, ref exitPoint, ref exitPointILOffset); PickExitPoint(child, ref exitPoint, ref exitPointILOffset);
} }
} }
private static void ConsiderReturnAsExitPoint(Block block, ref ControlFlowNode exitPoint, ref int exitPointILOffset)
{
// It's possible that the real exit point of the loop is a "return;" that has been combined (by ControlFlowSimplification)
// with the condition block.
if (!block.MatchIfAtEndOfBlock(out _, out var trueInst, out var falseInst))
return;
if (trueInst.StartILOffset > exitPointILOffset && trueInst is Leave { IsLeavingFunction: true, Value: Nop _ }) {
// By using NoExitPoint, everything (including the "return;") becomes part of the loop body
// Then DetectExitPoint will move the "return;" out of the loop body.
exitPoint = NoExitPoint;
exitPointILOffset = trueInst.StartILOffset;
}
if (falseInst.StartILOffset > exitPointILOffset && falseInst is Leave { IsLeavingFunction: true, Value: Nop _ }) {
exitPoint = NoExitPoint;
exitPointILOffset = falseInst.StartILOffset;
}
}
/// <summary> /// <summary>
/// Constructs a new control flow graph. /// Constructs a new control flow graph.
/// Each node cfg[i] has a corresponding node rev[i]. /// Each node cfg[i] has a corresponding node rev[i].

2
ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs

@ -35,7 +35,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// Detect sequences of conditional branches that all test a single integer value, /// Detect sequences of conditional branches that all test a single integer value,
/// and simplify them into a ILAst switch instruction (which like C# does not require contiguous values). /// and simplify them into a ILAst switch instruction (which like C# does not require contiguous values).
/// </summary> /// </summary>
class SwitchDetection : IILTransform public class SwitchDetection : IILTransform
{ {
private readonly SwitchAnalysis analysis = new SwitchAnalysis(); private readonly SwitchAnalysis analysis = new SwitchAnalysis();

2
ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

@ -28,7 +28,7 @@ using System.Reflection.Metadata;
namespace ICSharpCode.Decompiler.IL.ControlFlow namespace ICSharpCode.Decompiler.IL.ControlFlow
{ {
class YieldReturnDecompiler : IILTransform public class YieldReturnDecompiler : IILTransform
{ {
// For a description on the code generated by the C# compiler for yield return: // For a description on the code generated by the C# compiler for yield return:
// http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx // http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx

8
ICSharpCode.Decompiler/IL/ILReader.cs

@ -515,10 +515,10 @@ namespace ICSharpCode.Decompiler.IL
var blockBuilder = new BlockBuilder(body, variableByExceptionHandler); var blockBuilder = new BlockBuilder(body, variableByExceptionHandler);
blockBuilder.CreateBlocks(mainContainer, instructionBuilder, isBranchTarget, cancellationToken); blockBuilder.CreateBlocks(mainContainer, instructionBuilder, isBranchTarget, cancellationToken);
var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer, kind); var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer, kind);
CollectionExtensions.AddRange(function.Variables, parameterVariables); function.Variables.AddRange(parameterVariables);
CollectionExtensions.AddRange(function.Variables, localVariables); function.Variables.AddRange(localVariables);
CollectionExtensions.AddRange(function.Variables, stackVariables); function.Variables.AddRange(stackVariables);
CollectionExtensions.AddRange(function.Variables, variableByExceptionHandler.Values); function.Variables.AddRange(variableByExceptionHandler.Values);
function.AddRef(); // mark the root node function.AddRef(); // mark the root node
var removedBlocks = new List<Block>(); var removedBlocks = new List<Block>();
foreach (var c in function.Descendants.OfType<BlockContainer>()) { foreach (var c in function.Descendants.OfType<BlockContainer>()) {

14
ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs

@ -20,6 +20,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.Util; using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
@ -239,6 +240,19 @@ namespace ICSharpCode.Decompiler.IL
} }
} }
internal override bool CanInlineIntoSlot(int childIndex, ILInstruction expressionBeingMoved)
{
// Inlining into the entry-point is allowed as long as we're not moving code into a loop.
// This is used to inline into the switch expression.
return childIndex == 0 && this.EntryPoint.IncomingEdgeCount == 1;
}
internal override bool PrepareExtract(int childIndex, ExtractionContext ctx)
{
// Un-inlining from the entry-point is allowed as long as we're not moving code out of a loop
return childIndex == 0 && this.EntryPoint.IncomingEdgeCount == 1;
}
/// <summary> /// <summary>
/// Topologically sort the blocks. /// Topologically sort the blocks.
/// The new order is returned without modifying the BlockContainer. /// The new order is returned without modifying the BlockContainer.

1
ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs

@ -18,6 +18,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using ICSharpCode.Decompiler.IL.Patterns; using ICSharpCode.Decompiler.IL.Patterns;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;

9
ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs

@ -359,6 +359,15 @@ namespace ICSharpCode.Decompiler.IL
} }
} }
internal override bool CanInlineIntoSlot(int childIndex, ILInstruction expressionBeingMoved)
{
// With expression trees, we occasionally need to inline constants into an existing expression tree.
// Only allow this for completely pure constants; a MayReadLocals effect would already be problematic
// because we're essentially delaying evaluation of the expression until the ILFunction is called.
Debug.Assert(childIndex == 0);
return kind == ILFunctionKind.ExpressionTree && expressionBeingMoved.Flags == InstructionFlags.None;
}
/// <summary> /// <summary>
/// Apply a list of transforms to this function. /// Apply a list of transforms to this function.
/// </summary> /// </summary>

9
ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs

@ -802,6 +802,15 @@ namespace ICSharpCode.Decompiler.IL
} }
return true; return true;
} }
/// <summary>
/// Gets whether the specified instruction may be inlined into the specified slot.
/// Note: this does not check whether reordering with the previous slots is valid; only wheter the target slot supports inlining at all!
/// </summary>
internal virtual bool CanInlineIntoSlot(int childIndex, ILInstruction expressionBeingMoved)
{
return GetChildSlot(childIndex).CanInlineInto;
}
} }
public interface IInstructionWithTypeOperand public interface IInstructionWithTypeOperand

8
ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs

@ -132,5 +132,13 @@ namespace ICSharpCode.Decompiler.IL
return base.PrepareExtract(childIndex, ctx) return base.PrepareExtract(childIndex, ctx)
&& (ctx.FlagsBeingMoved & InstructionFlags.MayUnwrapNull) == 0; && (ctx.FlagsBeingMoved & InstructionFlags.MayUnwrapNull) == 0;
} }
internal override bool CanInlineIntoSlot(int childIndex, ILInstruction expressionBeingMoved)
{
// Inlining into nullable.rewrap is OK unless the expression being inlined
// contains a nullable.wrap that isn't being re-wrapped within the expression being inlined.
return base.CanInlineIntoSlot(childIndex, expressionBeingMoved)
&& !expressionBeingMoved.HasFlag(InstructionFlags.MayUnwrapNull);
}
} }
} }

24
ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs

@ -20,6 +20,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Reflection.Metadata;
using Humanizer; using Humanizer;
using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.CSharp.OutputVisitor;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
@ -51,6 +53,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ILTransformContext context; ILTransformContext context;
string[] currentFieldNames; string[] currentFieldNames;
Dictionary<string, int> reservedVariableNames; Dictionary<string, int> reservedVariableNames;
Dictionary<MethodDefinitionHandle, string> localFunctionMapping;
HashSet<ILVariable> loopCounters; HashSet<ILVariable> loopCounters;
const char maxLoopVariableName = 'n'; const char maxLoopVariableName = 'n';
@ -59,6 +62,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
this.context = context; this.context = context;
currentFieldNames = function.Method.DeclaringTypeDefinition.Fields.Select(f => f.Name).ToArray(); currentFieldNames = function.Method.DeclaringTypeDefinition.Fields.Select(f => f.Name).ToArray();
reservedVariableNames = new Dictionary<string, int>(); reservedVariableNames = new Dictionary<string, int>();
localFunctionMapping = new Dictionary<MethodDefinitionHandle, string>();
loopCounters = CollectLoopCounters(function); loopCounters = CollectLoopCounters(function);
foreach (var f in function.Descendants.OfType<ILFunction>()) { foreach (var f in function.Descendants.OfType<ILFunction>()) {
if (f.Method != null) { if (f.Method != null) {
@ -180,6 +184,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!LocalFunctionDecompiler.ParseLocalFunctionName(localFunction.Name, out _, out var newName) || !IsValidName(newName)) if (!LocalFunctionDecompiler.ParseLocalFunctionName(localFunction.Name, out _, out var newName) || !IsValidName(newName))
newName = null; newName = null;
localFunction.Name = newName; localFunction.Name = newName;
localFunction.ReducedMethod.Name = newName;
} }
// Now generate names: // Now generate names:
var mapping = new Dictionary<ILVariable, string>(ILVariableEqualityComparer.Instance); var mapping = new Dictionary<ILVariable, string>(ILVariableEqualityComparer.Instance);
@ -199,6 +204,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms
newName = GetAlternativeName("f"); newName = GetAlternativeName("f");
} }
localFunction.Name = newName; localFunction.Name = newName;
localFunction.ReducedMethod.Name = newName;
localFunctionMapping[(MethodDefinitionHandle)localFunction.ReducedMethod.MetadataToken] = newName;
}
foreach (var inst in function.Descendants) {
LocalFunctionMethod localFunction;
switch (inst) {
case Call call:
localFunction = call.Method as LocalFunctionMethod;
break;
case LdFtn ldftn:
localFunction = ldftn.Method as LocalFunctionMethod;
break;
default:
localFunction = null;
break;
}
if (localFunction == null || !localFunctionMapping.TryGetValue((MethodDefinitionHandle)localFunction.MetadataToken, out var name))
continue;
localFunction.Name = name;
} }
} }

2
ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs

@ -138,7 +138,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}; };
block.Instructions.Insert(i++, new StLoc(uninlinedArgs[j], arg)); block.Instructions.Insert(i++, new StLoc(uninlinedArgs[j], arg));
} }
CollectionExtensions.AddRange(v.Function.Variables, uninlinedArgs); v.Function.Variables.AddRange(uninlinedArgs);
// perform copy propagation: // perform copy propagation:
foreach (var expr in v.LoadInstructions.ToArray()) { foreach (var expr in v.LoadInstructions.ToArray()) {
var clone = copiedExpr.Clone(); var clone = copiedExpr.Clone();

4
ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs

@ -25,9 +25,9 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms namespace ICSharpCode.Decompiler.IL.Transforms
{ {
/// <summary> /// <summary>
/// /// Transforms anonymous methods and lambdas by creating nested ILFunctions.
/// </summary> /// </summary>
class DelegateConstruction : IILTransform public class DelegateConstruction : IILTransform
{ {
ILTransformContext context; ILTransformContext context;
ITypeResolveContext decompilationContext; ITypeResolveContext decompilationContext;

2
ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs

@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms namespace ICSharpCode.Decompiler.IL.Transforms
{ {
class EarlyExpressionTransforms : ILVisitor, IILTransform public class EarlyExpressionTransforms : ILVisitor, IILTransform
{ {
ILTransformContext context; ILTransformContext context;

2
ICSharpCode.Decompiler/IL/Transforms/FixLoneIsInst.cs

@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// This transform un-inlines the argument of `isinst` instructions that can't be directly translated to C#, /// This transform un-inlines the argument of `isinst` instructions that can't be directly translated to C#,
/// thus allowing the emulation via "expr is T ? (T)expr : null". /// thus allowing the emulation via "expr is T ? (T)expr : null".
/// </summary> /// </summary>
class FixLoneIsInst : IILTransform public class FixLoneIsInst : IILTransform
{ {
void IILTransform.Run(ILFunction function, ILTransformContext context) void IILTransform.Run(ILFunction function, ILTransformContext context)
{ {

36
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -589,6 +589,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} else if (expr is Block block) { } else if (expr is Block block) {
// Inlining into inline-blocks? // Inlining into inline-blocks?
switch (block.Kind) { switch (block.Kind) {
case BlockKind.ControlFlow when block.Parent is BlockContainer:
case BlockKind.ArrayInitializer: case BlockKind.ArrayInitializer:
case BlockKind.CollectionInitializer: case BlockKind.CollectionInitializer:
case BlockKind.ObjectInitializer: case BlockKind.ObjectInitializer:
@ -604,19 +605,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
default: default:
return FindResult.Stop; return FindResult.Stop;
} }
} else if (expr is BlockContainer container && container.EntryPoint.IncomingEdgeCount == 1) {
// Possibly a switch-container, allow inlining into the switch instruction:
return NoContinue(FindLoadInNext(container.EntryPoint.Instructions[0], v, expressionBeingMoved, options));
// If FindLoadInNext() returns null, we still can't continue searching
// because we can't inline over the remainder of the blockcontainer.
} else if (expr is NullableRewrap) {
// Inlining into nullable.rewrap is OK unless the expression being inlined
// contains a nullable.wrap that isn't being re-wrapped within the expression being inlined.
if (expressionBeingMoved.HasFlag(InstructionFlags.MayUnwrapNull))
return FindResult.Stop;
} }
foreach (var child in expr.Children) { foreach (var child in expr.Children) {
if (!child.SlotInfo.CanInlineInto) if (!expr.CanInlineIntoSlot(child.ChildIndex, expressionBeingMoved))
return FindResult.Stop; return FindResult.Stop;
// Recursively try to find the load instruction // Recursively try to find the load instruction
@ -666,24 +657,33 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
/// <summary> /// <summary>
/// Gets whether arg can be un-inlined out of stmt. /// Gets whether 'expressionBeingMoved' can be moved from somewhere before 'stmt' to become the replacement of 'targetLoad'.
/// </summary> /// </summary>
/// <seealso cref="ILInstruction.Extract"/> public static bool CanMoveInto(ILInstruction expressionBeingMoved, ILInstruction stmt, ILInstruction targetLoad)
internal static bool CanUninline(ILInstruction arg, ILInstruction stmt)
{ {
Debug.Assert(arg.IsDescendantOf(stmt)); Debug.Assert(targetLoad.IsDescendantOf(stmt));
for (ILInstruction inst = arg; inst != stmt; inst = inst.Parent) { for (ILInstruction inst = targetLoad; inst != stmt; inst = inst.Parent) {
if (!inst.SlotInfo.CanInlineInto) if (!inst.Parent.CanInlineIntoSlot(inst.ChildIndex, expressionBeingMoved))
return false; return false;
// Check whether re-ordering with predecessors is valid: // Check whether re-ordering with predecessors is valid:
int childIndex = inst.ChildIndex; int childIndex = inst.ChildIndex;
for (int i = 0; i < childIndex; ++i) { for (int i = 0; i < childIndex; ++i) {
ILInstruction predecessor = inst.Parent.Children[i]; ILInstruction predecessor = inst.Parent.Children[i];
if (!SemanticHelper.MayReorder(arg, predecessor)) if (!IsSafeForInlineOver(predecessor, expressionBeingMoved))
return false; return false;
} }
} }
return true; return true;
} }
/// <summary>
/// Gets whether arg can be un-inlined out of stmt.
/// </summary>
/// <seealso cref="ILInstruction.Extract"/>
internal static bool CanUninline(ILInstruction arg, ILInstruction stmt)
{
// moving into and moving out-of are equivalent
return CanMoveInto(arg, stmt, arg);
}
} }
} }

19
ICSharpCode.Decompiler/IL/Transforms/IndexRangeTransform.cs

@ -205,6 +205,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!CheckContainerLengthVariableUseCount(containerLengthVar, startIndexKind)) { if (!CheckContainerLengthVariableUseCount(containerLengthVar, startIndexKind)) {
return; return;
} }
if (!call.IsDescendantOf(block.Instructions[pos]))
return;
// startOffsetVar might be used deep inside a complex statement, ensure we can inline up to that point: // startOffsetVar might be used deep inside a complex statement, ensure we can inline up to that point:
for (int i = startPos; i < pos; i++) { for (int i = startPos; i < pos; i++) {
if (!ILInlining.CanInlineInto(block.Instructions[pos], startOffsetVar, block.Instructions[i])) if (!ILInlining.CanInlineInto(block.Instructions[pos], startOffsetVar, block.Instructions[i]))
@ -271,13 +273,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return; return;
} }
if (rangeVar != null) { if (rangeVar != null) {
if (!MatchIndexFromRange(startIndexKind, startIndexLoad, rangeVar, "get_Start")) return; // this should only ever happen in the second step (ExtendSlicing)
return;
if (!MatchIndexFromRange(endIndexKind, endIndexLoad, rangeVar, "get_End"))
return;
} }
if (!(sliceLengthVar.LoadInstructions.Single().Parent is CallInstruction call)) if (!(sliceLengthVar.LoadInstructions.Single().Parent is CallInstruction call))
return; return;
if (!call.IsDescendantOf(block.Instructions[pos]))
return;
if (!IsSlicingMethod(call.Method)) if (!IsSlicingMethod(call.Method))
return; return;
if (call.Arguments.Count != 3) if (call.Arguments.Count != 3)
@ -291,9 +292,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} else { } else {
if (!call.Arguments[1].MatchLdLoc(startOffsetVar)) if (!call.Arguments[1].MatchLdLoc(startOffsetVar))
return; return;
if (!ILInlining.CanMoveInto(startOffsetVarInit, block.Instructions[pos], call.Arguments[1]))
return;
} }
if (!call.Arguments[2].MatchLdLoc(sliceLengthVar)) if (!call.Arguments[2].MatchLdLoc(sliceLengthVar))
return; return;
if (!ILInlining.CanMoveInto(sliceLengthVarInit, block.Instructions[pos], call.Arguments[2]))
return;
if (!CSharpWillGenerateIndexer(call.Method.DeclaringType, slicing: true)) if (!CSharpWillGenerateIndexer(call.Method.DeclaringType, slicing: true))
return; return;
var specialMethods = new IndexMethods(context.TypeSystem); var specialMethods = new IndexMethods(context.TypeSystem);
@ -385,13 +390,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// holds because we've used containerLengthVar at least once // holds because we've used containerLengthVar at least once
Debug.Assert(startIndexKind != IndexKind.FromStart || endIndexKind != IndexKind.FromStart); Debug.Assert(startIndexKind != IndexKind.FromStart || endIndexKind != IndexKind.FromStart);
if (rangeVar != null) { if (rangeVar != null) {
if (!ILInlining.CanMoveInto(rangeVarInit, block.Instructions[pos], startIndexLoad))
return;
if (!MatchIndexFromRange(startIndexKind, startIndexLoad, rangeVar, "get_Start")) if (!MatchIndexFromRange(startIndexKind, startIndexLoad, rangeVar, "get_Start"))
return; return;
if (!MatchIndexFromRange(endIndexKind, endIndexLoad, rangeVar, "get_End")) if (!MatchIndexFromRange(endIndexKind, endIndexLoad, rangeVar, "get_End"))
return; return;
} }
context.Step("Merge containerLengthVar into slicing", slicingCall);
var specialMethods = new IndexMethods(context.TypeSystem); var specialMethods = new IndexMethods(context.TypeSystem);
if (!specialMethods.IsValid)
return;
context.Step("Merge containerLengthVar into slicing", slicingCall);
rangeCtorCall.ReplaceWith(MakeRange(startIndexKind, startIndexLoad, endIndexKind, endIndexLoad, specialMethods)); rangeCtorCall.ReplaceWith(MakeRange(startIndexKind, startIndexLoad, endIndexKind, endIndexLoad, specialMethods));
for (int i = startPos; i < pos; i++) { for (int i = startPos; i < pos; i++) {
slicingCall.AddILRange(block.Instructions[i]); slicingCall.AddILRange(block.Instructions[i]);

2
ICSharpCode.Decompiler/IL/Transforms/IntroduceDynamicTypeOnLocals.cs

@ -24,7 +24,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
{ {
class IntroduceDynamicTypeOnLocals : IILTransform public class IntroduceDynamicTypeOnLocals : IILTransform
{ {
public void Run(ILFunction function, ILTransformContext context) public void Run(ILFunction function, ILTransformContext context)
{ {

2
ICSharpCode.Decompiler/IL/Transforms/IntroduceRefReadOnlyModifierOnLocals.cs

@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
{ {
class IntroduceRefReadOnlyModifierOnLocals : IILTransform public class IntroduceRefReadOnlyModifierOnLocals : IILTransform
{ {
public void Run(ILFunction function, ILTransformContext context) public void Run(ILFunction function, ILTransformContext context)
{ {

24
ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs

@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary> /// <summary>
/// Decompiler step for C# 7.0 local functions /// Decompiler step for C# 7.0 local functions
/// </summary> /// </summary>
class LocalFunctionDecompiler : IILTransform public class LocalFunctionDecompiler : IILTransform
{ {
ILTransformContext context; ILTransformContext context;
ITypeResolveContext resolveContext; ITypeResolveContext resolveContext;
@ -484,7 +484,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms
break; break;
parametersToRemove++; parametersToRemove++;
} }
return new LocalFunctionMethod(method, parametersToRemove, typeParametersToRemove); return new LocalFunctionMethod(method, method.Name, CanBeStaticLocalFunction(), parametersToRemove, typeParametersToRemove);
bool CanBeStaticLocalFunction()
{
if (!context.Settings.StaticLocalFunctions)
return false;
// Cannot be static because there are closure parameters that will be removed
if (parametersToRemove > 0)
return false;
// no closure parameters, but static:
// we can safely assume, this local function can be declared static
if (method.IsStatic)
return true;
// the local function is used in conjunction with a lambda, which means,
// it is defined inside the display-class type
var declaringType = method.DeclaringTypeDefinition;
if (!declaringType.IsCompilerGenerated())
return false;
// if there are no instance fields, we can make it a static local function
return !declaringType.GetFields(f => !f.IsStatic).Any();
}
} }
static void TransformToLocalFunctionReference(ILFunction function, CallInstruction useSite) static void TransformToLocalFunctionReference(ILFunction function, CallInstruction useSite)

2
ICSharpCode.Decompiler/IL/Transforms/LockTransform.cs

@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms namespace ICSharpCode.Decompiler.IL.Transforms
{ {
class LockTransform : IBlockTransform public class LockTransform : IBlockTransform
{ {
BlockTransformContext context; BlockTransformContext context;

4
ICSharpCode.Decompiler/IL/Transforms/NamedArgumentTransform.cs

@ -10,9 +10,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
using FindResult = ILInlining.FindResult; using FindResult = ILInlining.FindResult;
using FindResultType = ILInlining.FindResultType; using FindResultType = ILInlining.FindResultType;
class NamedArgumentTransform : IStatementTransform public class NamedArgumentTransform : IStatementTransform
{ {
public static FindResult CanIntroduceNamedArgument(CallInstruction call, ILInstruction child, ILVariable v, ILInstruction expressionBeingMoved) internal static FindResult CanIntroduceNamedArgument(CallInstruction call, ILInstruction child, ILVariable v, ILInstruction expressionBeingMoved)
{ {
Debug.Assert(child.Parent == call); Debug.Assert(child.Parent == call);
if (call.IsInstanceCall && child.ChildIndex == 0) if (call.IsInstanceCall && child.ChildIndex == 0)

2
ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs

@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// ///
/// The ?? operator for nullable value types is handled by NullableLiftingTransform. /// The ?? operator for nullable value types is handled by NullableLiftingTransform.
/// </summary> /// </summary>
class NullCoalescingTransform : IStatementTransform public class NullCoalescingTransform : IStatementTransform
{ {
public void Run(Block block, int pos, StatementTransformContext context) public void Run(Block block, int pos, StatementTransformContext context)
{ {

2
ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs

@ -489,7 +489,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
} }
class NullPropagationStatementTransform : IStatementTransform public class NullPropagationStatementTransform : IStatementTransform
{ {
public void Run(Block block, int pos, StatementTransformContext context) public void Run(Block block, int pos, StatementTransformContext context)
{ {

2
ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs

@ -908,7 +908,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
#endregion #endregion
} }
class NullableLiftingStatementTransform : IStatementTransform public class NullableLiftingStatementTransform : IStatementTransform
{ {
public void Run(Block block, int pos, StatementTransformContext context) public void Run(Block block, int pos, StatementTransformContext context)
{ {

2
ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs

@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary> /// <summary>
/// Detects switch-on-nullable patterns employed by the C# compiler and transforms them to an ILAst-switch-instruction. /// Detects switch-on-nullable patterns employed by the C# compiler and transforms them to an ILAst-switch-instruction.
/// </summary> /// </summary>
class SwitchOnNullableTransform : IILTransform public class SwitchOnNullableTransform : IILTransform
{ {
public void Run(ILFunction function, ILTransformContext context) public void Run(ILFunction function, ILTransformContext context)
{ {

2
ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs

@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary> /// <summary>
/// Detects switch-on-string patterns employed by the C# compiler and transforms them to an ILAst-switch-instruction. /// Detects switch-on-string patterns employed by the C# compiler and transforms them to an ILAst-switch-instruction.
/// </summary> /// </summary>
class SwitchOnStringTransform : IILTransform public class SwitchOnStringTransform : IILTransform
{ {
ILTransformContext context; ILTransformContext context;

2
ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs

@ -43,7 +43,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Note that 2) and 3) apply because declarations and uses of lambdas and local functions /// Note that 2) and 3) apply because declarations and uses of lambdas and local functions
/// are already transformed by the time this transform is applied. /// are already transformed by the time this transform is applied.
/// </summary> /// </summary>
class TransformDisplayClassUsage : ILVisitor, IILTransform public class TransformDisplayClassUsage : ILVisitor, IILTransform
{ {
class VariableToDeclare class VariableToDeclare
{ {

2
ICSharpCode.Decompiler/IL/Transforms/UserDefinedLogicTransform.cs

@ -26,7 +26,7 @@ using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL.Transforms namespace ICSharpCode.Decompiler.IL.Transforms
{ {
class UserDefinedLogicTransform : IStatementTransform public class UserDefinedLogicTransform : IStatementTransform
{ {
void IStatementTransform.Run(Block block, int pos, StatementTransformContext context) void IStatementTransform.Run(Block block, int pos, StatementTransformContext context)
{ {

2
ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs

@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms namespace ICSharpCode.Decompiler.IL.Transforms
{ {
class UsingTransform : IBlockTransform public class UsingTransform : IBlockTransform
{ {
BlockTransformContext context; BlockTransformContext context;

55
ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs

@ -61,29 +61,30 @@ namespace ICSharpCode.Decompiler.Metadata
}; };
readonly DotNetCorePackageInfo[] packages; readonly DotNetCorePackageInfo[] packages;
ISet<string> packageBasePaths = new HashSet<string>(StringComparer.Ordinal); readonly List<string> searchPaths = new List<string>();
readonly Version version; readonly List<string> packageBasePaths = new List<string>();
readonly Version targetFrameworkVersion;
readonly string dotnetBasePath = FindDotNetExeDirectory(); readonly string dotnetBasePath = FindDotNetExeDirectory();
public DotNetCorePathFinder(Version version) public DotNetCorePathFinder(TargetFrameworkIdentifier targetFramework, Version targetFrameworkVersion)
{ {
this.version = version; this.targetFrameworkVersion = targetFrameworkVersion;
}
public DotNetCorePathFinder(string parentAssemblyFileName, string targetFrameworkIdString, TargetFrameworkIdentifier targetFramework, Version version, ReferenceLoadInfo loadInfo = null)
{
string assemblyName = Path.GetFileNameWithoutExtension(parentAssemblyFileName);
string basePath = Path.GetDirectoryName(parentAssemblyFileName);
this.version = version;
if (targetFramework == TargetFrameworkIdentifier.NETStandard) { if (targetFramework == TargetFrameworkIdentifier.NETStandard) {
// .NET Standard 2.1 is implemented by .NET Core 3.0 or higher // .NET Standard 2.1 is implemented by .NET Core 3.0 or higher
if (version.Major == 2 && version.Minor == 1) { if (targetFrameworkVersion.Major == 2 && targetFrameworkVersion.Minor == 1) {
this.version = new Version(3, 0, 0); this.targetFrameworkVersion = new Version(3, 0, 0);
} }
} }
}
public DotNetCorePathFinder(string parentAssemblyFileName, string targetFrameworkIdString, TargetFrameworkIdentifier targetFramework, Version targetFrameworkVersion, ReferenceLoadInfo loadInfo = null)
: this(targetFramework, targetFrameworkVersion)
{
string assemblyName = Path.GetFileNameWithoutExtension(parentAssemblyFileName);
string basePath = Path.GetDirectoryName(parentAssemblyFileName);
packageBasePaths.Add(basePath); searchPaths.Add(basePath);
var depsJsonFileName = Path.Combine(basePath, $"{assemblyName}.deps.json"); var depsJsonFileName = Path.Combine(basePath, $"{assemblyName}.deps.json");
if (File.Exists(depsJsonFileName)) { if (File.Exists(depsJsonFileName)) {
@ -106,17 +107,17 @@ namespace ICSharpCode.Decompiler.Metadata
public void AddSearchDirectory(string path) public void AddSearchDirectory(string path)
{ {
this.packageBasePaths.Add(path); this.searchPaths.Add(path);
} }
public void RemoveSearchDirectory(string path) public void RemoveSearchDirectory(string path)
{ {
this.packageBasePaths.Remove(path); this.searchPaths.Remove(path);
} }
public string TryResolveDotNetCore(IAssemblyReference name) public string TryResolveDotNetCore(IAssemblyReference name)
{ {
foreach (var basePath in packageBasePaths) { foreach (var basePath in searchPaths.Concat(packageBasePaths)) {
if (File.Exists(Path.Combine(basePath, name.Name + ".dll"))) { if (File.Exists(Path.Combine(basePath, name.Name + ".dll"))) {
return Path.Combine(basePath, name.Name + ".dll"); return Path.Combine(basePath, name.Name + ".dll");
} else if (File.Exists(Path.Combine(basePath, name.Name + ".exe"))) { } else if (File.Exists(Path.Combine(basePath, name.Name + ".exe"))) {
@ -124,7 +125,7 @@ namespace ICSharpCode.Decompiler.Metadata
} }
} }
return FallbackToDotNetSharedDirectory(name, version); return FallbackToDotNetSharedDirectory(name);
} }
internal string GetReferenceAssemblyPath(string targetFramework) internal string GetReferenceAssemblyPath(string targetFramework)
@ -169,7 +170,7 @@ namespace ICSharpCode.Decompiler.Metadata
} }
} }
string FallbackToDotNetSharedDirectory(IAssemblyReference name, Version version) string FallbackToDotNetSharedDirectory(IAssemblyReference name)
{ {
if (dotnetBasePath == null) if (dotnetBasePath == null)
return null; return null;
@ -177,7 +178,7 @@ namespace ICSharpCode.Decompiler.Metadata
foreach (var basePath in basePaths) { foreach (var basePath in basePaths) {
if (!Directory.Exists(basePath)) if (!Directory.Exists(basePath))
continue; continue;
var closestVersion = GetClosestVersionFolder(basePath, version); var closestVersion = GetClosestVersionFolder(basePath, targetFrameworkVersion);
if (File.Exists(Path.Combine(basePath, closestVersion, name.Name + ".dll"))) { if (File.Exists(Path.Combine(basePath, closestVersion, name.Name + ".dll"))) {
return Path.Combine(basePath, closestVersion, name.Name + ".dll"); return Path.Combine(basePath, closestVersion, name.Name + ".dll");
} else if (File.Exists(Path.Combine(basePath, closestVersion, name.Name + ".exe"))) { } else if (File.Exists(Path.Combine(basePath, closestVersion, name.Name + ".exe"))) {
@ -189,15 +190,17 @@ namespace ICSharpCode.Decompiler.Metadata
static string GetClosestVersionFolder(string basePath, Version version) static string GetClosestVersionFolder(string basePath, Version version)
{ {
string result = null; var foundVersions = new DirectoryInfo(basePath).GetDirectories()
foreach (var folder in new DirectoryInfo(basePath).GetDirectories().Select(d => ConvertToVersion(d.Name)).Where(v => v.Item1 != null).OrderByDescending(v => v.Item1)) { .Select(d => ConvertToVersion(d.Name))
if (folder.Item1 >= version) .Where(v => v.version != null);
result = folder.Item2; foreach (var folder in foundVersions.OrderBy(v => v.Item1)) {
if (folder.version >= version)
return folder.directoryName;
} }
return result ?? version.ToString(); return version.ToString();
} }
internal static (Version, string) ConvertToVersion(string name) internal static (Version version, string directoryName) ConvertToVersion(string name)
{ {
string RemoveTrailingVersionInfo() string RemoveTrailingVersionInfo()
{ {

33
ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs

@ -20,20 +20,26 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using System.Reflection.PortableExecutable; using System.Reflection.PortableExecutable;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.Metadata namespace ICSharpCode.Decompiler.Metadata
{ {
public static class DotNetCorePathFinderExtensions public static class DotNetCorePathFinderExtensions
{ {
static readonly string RefPathPattern = static readonly string PathPattern =
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<type>.NETFramework)[/\\]v(?<version>[^/\\]+)[/\\])" + @"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<type>.NETFramework)[/\\]v(?<version>[^/\\]+)[/\\])" +
@"|((?<type>Microsoft\.NET)[/\\]assembly[/\\]GAC_(MSIL|32|64)[/\\])" + @"|((?<type>Microsoft\.NET)[/\\]assembly[/\\]GAC_(MSIL|32|64)[/\\])" +
@"|((?<type>Microsoft\.NET)[/\\]Framework(64)?[/\\](?<version>[^/\\]+)[/\\])" + @"|((?<type>Microsoft\.NET)[/\\]Framework(64)?[/\\](?<version>[^/\\]+)[/\\])" +
@"|(NuGetFallbackFolder[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\]ref[/\\])" + @"|(NuGetFallbackFolder[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\]ref[/\\])" +
@"|(shared[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\])"; @"|(shared[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\])" +
@"|(packs[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)\\ref([/\\].*)?[/\\])";
static readonly string RefPathPattern =
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<type>.NETFramework)[/\\]v(?<version>[^/\\]+)[/\\])" +
@"|(NuGetFallbackFolder[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\]ref[/\\])" +
@"|(packs[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)\\ref([/\\].*)?[/\\])";
public static string DetectTargetFrameworkId(this PEFile assembly) public static string DetectTargetFrameworkId(this PEFile assembly)
{ {
@ -99,7 +105,7 @@ namespace ICSharpCode.Decompiler.Metadata
* - .NETCore -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Console.dll * - .NETCore -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Console.dll
* - .NETStandard -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll * - .NETStandard -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll
*/ */
var pathMatch = Regex.Match(assemblyPath, RefPathPattern, var pathMatch = Regex.Match(assemblyPath, PathPattern,
RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture); RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture);
if (pathMatch.Success) { if (pathMatch.Success) {
var type = pathMatch.Groups["type"].Value; var type = pathMatch.Groups["type"].Value;
@ -121,6 +127,25 @@ namespace ICSharpCode.Decompiler.Metadata
return string.Empty; return string.Empty;
} }
public static bool IsReferenceAssembly(this PEFile assembly)
{
return IsReferenceAssembly(assembly.Reader, assembly.FileName);
}
public static bool IsReferenceAssembly(this PEReader assembly, string assemblyPath)
{
if (assembly == null)
throw new ArgumentNullException(nameof(assembly));
var metadata = assembly.GetMetadataReader();
if (metadata.GetCustomAttributes(Handle.AssemblyDefinition).HasKnownAttribute(metadata, KnownAttribute.ReferenceAssembly))
return true;
// Try to detect reference assembly through specific path pattern
var refPathMatch = Regex.Match(assemblyPath, RefPathPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
return refPathMatch.Success;
}
} }
public class ReferenceLoadInfo public class ReferenceLoadInfo

4
ICSharpCode.Decompiler/Metadata/PEFile.cs

@ -75,8 +75,12 @@ namespace ICSharpCode.Decompiler.Metadata
public TargetRuntime GetRuntime() public TargetRuntime GetRuntime()
{ {
string version = Metadata.MetadataVersion; string version = Metadata.MetadataVersion;
if (version == null || version.Length <= 1)
return TargetRuntime.Unknown;
switch (version[1]) { switch (version[1]) {
case '1': case '1':
if (version.Length <= 3)
return TargetRuntime.Unknown;
if (version[3] == 1) if (version[3] == 1)
return TargetRuntime.Net_1_0; return TargetRuntime.Net_1_0;
else else

16
ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs

@ -92,17 +92,20 @@ namespace ICSharpCode.Decompiler.Metadata
public UniversalAssemblyResolver(string mainAssemblyFileName, bool throwOnError, string targetFramework, public UniversalAssemblyResolver(string mainAssemblyFileName, bool throwOnError, string targetFramework,
PEStreamOptions streamOptions = PEStreamOptions.Default, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default) PEStreamOptions streamOptions = PEStreamOptions.Default, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default)
{ {
this.mainAssemblyFileName = mainAssemblyFileName;
this.throwOnError = throwOnError;
this.streamOptions = streamOptions; this.streamOptions = streamOptions;
this.metadataOptions = metadataOptions; this.metadataOptions = metadataOptions;
this.targetFramework = targetFramework ?? string.Empty; this.targetFramework = targetFramework ?? string.Empty;
(targetFrameworkIdentifier, targetFrameworkVersion) = ParseTargetFramework(this.targetFramework); (targetFrameworkIdentifier, targetFrameworkVersion) = ParseTargetFramework(this.targetFramework);
this.mainAssemblyFileName = mainAssemblyFileName;
this.baseDirectory = Path.GetDirectoryName(mainAssemblyFileName); if (mainAssemblyFileName != null) {
this.throwOnError = throwOnError; string baseDirectory = Path.GetDirectoryName(mainAssemblyFileName);
if (string.IsNullOrWhiteSpace(this.baseDirectory)) if (string.IsNullOrWhiteSpace(this.baseDirectory))
this.baseDirectory = Environment.CurrentDirectory; this.baseDirectory = Environment.CurrentDirectory;
AddSearchDirectory(baseDirectory); AddSearchDirectory(baseDirectory);
} }
}
internal static (TargetFrameworkIdentifier, Version) ParseTargetFramework(string targetFramework) internal static (TargetFrameworkIdentifier, Version) ParseTargetFramework(string targetFramework)
{ {
@ -191,6 +194,9 @@ namespace ICSharpCode.Decompiler.Metadata
if (IsZeroOrAllOnes(targetFrameworkVersion)) if (IsZeroOrAllOnes(targetFrameworkVersion))
goto default; goto default;
if (dotNetCorePathFinder == null) { if (dotNetCorePathFinder == null) {
if (mainAssemblyFileName == null)
dotNetCorePathFinder = new DotNetCorePathFinder(targetFrameworkIdentifier, targetFrameworkVersion);
else
dotNetCorePathFinder = new DotNetCorePathFinder(mainAssemblyFileName, targetFramework, targetFrameworkIdentifier, targetFrameworkVersion); dotNetCorePathFinder = new DotNetCorePathFinder(mainAssemblyFileName, targetFramework, targetFrameworkIdentifier, targetFrameworkVersion);
foreach (var directory in directories) { foreach (var directory in directories) {
dotNetCorePathFinder.AddSearchDirectory(directory); dotNetCorePathFinder.AddSearchDirectory(directory);
@ -327,9 +333,13 @@ namespace ICSharpCode.Decompiler.Metadata
if (assembly != null) if (assembly != null)
return assembly; return assembly;
// when decompiling assemblies that target frameworks prior to 4.0, we can fall back to the 4.0 assemblies in case the target framework is not installed.
// but when looking for Microsoft.Build.Framework, Version=15.0.0.0 we should not use the version 4.0 assembly here so that the LoadedAssembly logic can instead fall back to version 15.1.0.0
if (name.Version <= new Version(4, 0, 0, 0)) {
assembly = SearchDirectory(name, framework_dirs); assembly = SearchDirectory(name, framework_dirs);
if (assembly != null) if (assembly != null)
return assembly; return assembly;
}
if (throwOnError) if (throwOnError)
throw new AssemblyResolutionException(name); throw new AssemblyResolutionException(name);

25
ICSharpCode.Decompiler/Output/TextTokenWriter.cs

@ -19,6 +19,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.CSharp.OutputVisitor;
using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Resolver;
@ -26,6 +27,7 @@ using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.Semantics;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
namespace ICSharpCode.Decompiler namespace ICSharpCode.Decompiler
{ {
@ -110,6 +112,7 @@ namespace ICSharpCode.Decompiler
if (symbol != null && node.Role == Roles.Type && node.Parent is ObjectCreateExpression) { if (symbol != null && node.Role == Roles.Type && node.Parent is ObjectCreateExpression) {
symbol = node.Parent.GetSymbol(); symbol = node.Parent.GetSymbol();
} }
if (node is IdentifierExpression && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression && symbol is IMember member) { if (node is IdentifierExpression && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression && symbol is IMember member) {
var declaringType = member.DeclaringType; var declaringType = member.DeclaringType;
if (declaringType != null && declaringType.Kind == TypeKind.Delegate) if (declaringType != null && declaringType.Kind == TypeKind.Delegate)
@ -123,10 +126,8 @@ namespace ICSharpCode.Decompiler
if (symbol == null) if (symbol == null)
return null; return null;
//if (settings.AutomaticEvents && member is FieldDefinition) { if (symbol is LocalFunctionMethod)
// var field = (FieldDefinition)member; return null;
// return field.DeclaringType.Events.FirstOrDefault(ev => ev.Name == field.Name) ?? member;
//}
return symbol; return symbol;
} }
@ -142,14 +143,18 @@ namespace ICSharpCode.Decompiler
if (letClauseVariable != null) if (letClauseVariable != null)
return letClauseVariable; return letClauseVariable;
var gotoStatement = node as GotoStatement; if (node is GotoStatement gotoStatement) {
if (gotoStatement != null)
{
var method = nodeStack.Select(nd => nd.GetSymbol() as IMethod).FirstOrDefault(mr => mr != null); var method = nodeStack.Select(nd => nd.GetSymbol() as IMethod).FirstOrDefault(mr => mr != null);
if (method != null) if (method != null)
return method + gotoStatement.Label; return method + gotoStatement.Label;
} }
if (node.Role == Roles.TargetExpression && node.Parent is InvocationExpression) {
var symbol = node.Parent.GetSymbol();
if (symbol is LocalFunctionMethod)
return symbol;
}
return null; return null;
} }
@ -177,8 +182,8 @@ namespace ICSharpCode.Decompiler
return method + label.Label; return method + label.Label;
} }
if (node is LocalFunctionDeclarationStatement) { if (node is MethodDeclaration && node.Parent is LocalFunctionDeclarationStatement) {
var localFunction = node.GetResolveResult() as MemberResolveResult; var localFunction = node.Parent.GetResolveResult() as MemberResolveResult;
if (localFunction != null) if (localFunction != null)
return localFunction.Member; return localFunction.Member;
} }
@ -404,7 +409,7 @@ namespace ICSharpCode.Decompiler
public static bool IsDefinition(ref AstNode node) public static bool IsDefinition(ref AstNode node)
{ {
if (node is EntityDeclaration) if (node is EntityDeclaration && !(node.Parent is LocalFunctionDeclarationStatement))
return true; return true;
if (node is VariableInitializer && node.Parent is FieldDeclaration) { if (node is VariableInitializer && node.Parent is FieldDeclaration) {
node = node.Parent; node = node.Parent;

6
ICSharpCode.Decompiler/TypeSystem/IMethod.cs

@ -40,6 +40,12 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary> /// </summary>
bool ReturnTypeIsRefReadOnly { get; } bool ReturnTypeIsRefReadOnly { get; }
/// <summary>
/// Gets whether this method may only be called on fresh instances.
/// Used with C# 9 `init;` property setters.
/// </summary>
bool IsInitOnly { get; }
/// <summary> /// <summary>
/// Gets whether the method accepts the 'this' reference as ref readonly. /// Gets whether the method accepts the 'this' reference as ref readonly.
/// This can be either because the method is C# 8.0 'readonly', or because it is within a C# 7.2 'readonly struct' /// This can be either because the method is C# 8.0 'readonly', or because it is within a C# 7.2 'readonly struct'

1
ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs

@ -134,6 +134,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => EmptyList<IAttribute>.Instance; IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => EmptyList<IAttribute>.Instance;
bool IMethod.ReturnTypeIsRefReadOnly => false; bool IMethod.ReturnTypeIsRefReadOnly => false;
bool IMethod.ThisIsRefReadOnly => false; bool IMethod.ThisIsRefReadOnly => false;
bool IMethod.IsInitOnly => false;
public IReadOnlyList<ITypeParameter> TypeParameters { get; set; } = EmptyList<ITypeParameter>.Instance; public IReadOnlyList<ITypeParameter> TypeParameters { get; set; } = EmptyList<ITypeParameter>.Instance;

41
ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs

@ -31,11 +31,13 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
{ {
readonly IMethod baseMethod; readonly IMethod baseMethod;
public LocalFunctionMethod(IMethod baseMethod, int numberOfCompilerGeneratedParameters, int numberOfCompilerGeneratedTypeParameters) public LocalFunctionMethod(IMethod baseMethod, string name, bool isStaticLocalFunction, int numberOfCompilerGeneratedParameters, int numberOfCompilerGeneratedTypeParameters)
{ {
if (baseMethod == null) if (baseMethod == null)
throw new ArgumentNullException(nameof(baseMethod)); throw new ArgumentNullException(nameof(baseMethod));
this.baseMethod = baseMethod; this.baseMethod = baseMethod;
this.Name = name;
this.IsStaticLocalFunction = isStaticLocalFunction;
this.NumberOfCompilerGeneratedParameters = numberOfCompilerGeneratedParameters; this.NumberOfCompilerGeneratedParameters = numberOfCompilerGeneratedParameters;
this.NumberOfCompilerGeneratedTypeParameters = numberOfCompilerGeneratedTypeParameters; this.NumberOfCompilerGeneratedTypeParameters = numberOfCompilerGeneratedTypeParameters;
} }
@ -46,7 +48,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return false; return false;
return baseMethod.Equals(other.baseMethod, typeNormalization) return baseMethod.Equals(other.baseMethod, typeNormalization)
&& NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters && NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters
&& NumberOfCompilerGeneratedTypeParameters == other.NumberOfCompilerGeneratedTypeParameters; && NumberOfCompilerGeneratedTypeParameters == other.NumberOfCompilerGeneratedTypeParameters
&& IsStaticLocalFunction == other.IsStaticLocalFunction;
} }
public override bool Equals(object obj) public override bool Equals(object obj)
@ -55,7 +58,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return false; return false;
return baseMethod.Equals(other.baseMethod) return baseMethod.Equals(other.baseMethod)
&& NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters && NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters
&& NumberOfCompilerGeneratedTypeParameters == other.NumberOfCompilerGeneratedTypeParameters; && NumberOfCompilerGeneratedTypeParameters == other.NumberOfCompilerGeneratedTypeParameters
&& IsStaticLocalFunction == other.IsStaticLocalFunction;
} }
public override int GetHashCode() public override int GetHashCode()
@ -65,14 +69,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public override string ToString() public override string ToString()
{ {
return string.Format("[LocalFunctionMethod: ReducedFrom={0}, NumberOfGeneratedParameters={1}, NumberOfCompilerGeneratedTypeParameters={2}]", ReducedFrom, NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters); return string.Format("[LocalFunctionMethod: ReducedFrom={0}, Name={1}, NumberOfGeneratedParameters={2}, NumberOfCompilerGeneratedTypeParameters={3}, IsStaticLocalFunction={4}]", ReducedFrom, Name, NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters, IsStaticLocalFunction);
} }
internal int NumberOfCompilerGeneratedParameters { get; } internal int NumberOfCompilerGeneratedParameters { get; }
internal int NumberOfCompilerGeneratedTypeParameters { get; } internal int NumberOfCompilerGeneratedTypeParameters { get; }
internal bool IsStaticLocalFunction => NumberOfCompilerGeneratedParameters == 0 && (baseMethod.IsStatic || (baseMethod.DeclaringTypeDefinition.IsCompilerGenerated() && !baseMethod.DeclaringType.GetFields(f => !f.IsStatic).Any())); internal bool IsStaticLocalFunction { get; }
public IMember MemberDefinition => this; public IMember MemberDefinition => this;
@ -88,7 +92,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
{ {
return new LocalFunctionMethod( return new LocalFunctionMethod(
baseMethod.Specialize(substitution), baseMethod.Specialize(substitution),
NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters); Name, IsStaticLocalFunction, NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters);
} }
IMember IMember.Specialize(TypeParameterSubstitution substitution) IMember IMember.Specialize(TypeParameterSubstitution substitution)
@ -96,7 +100,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return Specialize(substitution); return Specialize(substitution);
} }
public IReadOnlyList<ITypeParameter> TypeParameters => baseMethod.TypeParameters;
public bool IsExtensionMethod => baseMethod.IsExtensionMethod; public bool IsExtensionMethod => baseMethod.IsExtensionMethod;
public bool IsLocalFunction => true; public bool IsLocalFunction => true;
public bool IsConstructor => baseMethod.IsConstructor; public bool IsConstructor => baseMethod.IsConstructor;
@ -107,7 +110,24 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public IMember AccessorOwner => baseMethod.AccessorOwner; public IMember AccessorOwner => baseMethod.AccessorOwner;
public MethodSemanticsAttributes AccessorKind => baseMethod.AccessorKind; public MethodSemanticsAttributes AccessorKind => baseMethod.AccessorKind;
public IMethod ReducedFrom => baseMethod; public IMethod ReducedFrom => baseMethod;
public IReadOnlyList<IType> TypeArguments => baseMethod.TypeArguments;
List<ITypeParameter> typeParameters;
public IReadOnlyList<ITypeParameter> TypeParameters {
get {
if (typeParameters == null)
typeParameters = new List<ITypeParameter>(baseMethod.TypeParameters.Skip(NumberOfCompilerGeneratedTypeParameters));
return typeParameters;
}
}
List<IType> typeArguments;
public IReadOnlyList<IType> TypeArguments {
get {
if (typeArguments == null)
typeArguments = new List<IType>(baseMethod.TypeArguments.Skip(NumberOfCompilerGeneratedTypeParameters));
return typeArguments;
}
}
List<IParameter> parameters; List<IParameter> parameters;
public IReadOnlyList<IParameter> Parameters { public IReadOnlyList<IParameter> Parameters {
@ -127,6 +147,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes(); IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes();
bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly; bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly;
bool IMethod.ThisIsRefReadOnly => baseMethod.ThisIsRefReadOnly; bool IMethod.ThisIsRefReadOnly => baseMethod.ThisIsRefReadOnly;
bool IMethod.IsInitOnly => baseMethod.IsInitOnly;
/// <summary> /// <summary>
/// We consider local functions as always static, because they do not have a "this parameter". /// We consider local functions as always static, because they do not have a "this parameter".
/// Even local functions in instance methods capture this. /// Even local functions in instance methods capture this.
@ -137,8 +158,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public Accessibility Accessibility => baseMethod.Accessibility; public Accessibility Accessibility => baseMethod.Accessibility;
public string FullName => baseMethod.FullName; public string FullName => Name;
public string Name => baseMethod.Name; public string Name { get; set; }
public string ReflectionName => baseMethod.ReflectionName; public string ReflectionName => baseMethod.ReflectionName;
public string Namespace => baseMethod.Namespace; public string Namespace => baseMethod.Namespace;

20
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs

@ -50,6 +50,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IType returnType; IType returnType;
byte returnTypeIsRefReadonly = ThreeState.Unknown; byte returnTypeIsRefReadonly = ThreeState.Unknown;
byte thisIsRefReadonly = ThreeState.Unknown; byte thisIsRefReadonly = ThreeState.Unknown;
bool isInitOnly;
internal MetadataMethod(MetadataModule module, MethodDefinitionHandle handle) internal MetadataMethod(MetadataModule module, MethodDefinitionHandle handle)
{ {
@ -150,6 +151,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
} }
} }
public bool IsInitOnly {
get {
var returnType = LazyInit.VolatileRead(ref this.returnType);
if (returnType == null)
DecodeSignature();
return this.isInitOnly;
}
}
internal Nullability NullableContext { internal Nullability NullableContext {
get { get {
var methodDef = module.metadata.GetMethodDefinition(handle); var methodDef = module.metadata.GetMethodDefinition(handle);
@ -163,19 +173,23 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
var genericContext = new GenericContext(DeclaringType.TypeParameters, this.TypeParameters); var genericContext = new GenericContext(DeclaringType.TypeParameters, this.TypeParameters);
IType returnType; IType returnType;
IParameter[] parameters; IParameter[] parameters;
ModifiedType mod;
try { try {
var nullableContext = methodDef.GetCustomAttributes().GetNullableContext(module.metadata) ?? DeclaringTypeDefinition.NullableContext; var nullableContext = methodDef.GetCustomAttributes().GetNullableContext(module.metadata) ?? DeclaringTypeDefinition.NullableContext;
var signature = methodDef.DecodeSignature(module.TypeProvider, genericContext); var signature = methodDef.DecodeSignature(module.TypeProvider, genericContext);
(returnType, parameters) = DecodeSignature(module, this, signature, methodDef.GetParameters(), nullableContext, module.OptionsForEntity(this)); (returnType, parameters, mod) = DecodeSignature(module, this, signature, methodDef.GetParameters(), nullableContext, module.OptionsForEntity(this));
} catch (BadImageFormatException) { } catch (BadImageFormatException) {
returnType = SpecialType.UnknownType; returnType = SpecialType.UnknownType;
parameters = Empty<IParameter>.Array; parameters = Empty<IParameter>.Array;
mod = null;
} }
this.isInitOnly = mod is { Modifier: { Name: "IsExternalInit", Namespace: "System.Runtime.CompilerServices" } };
LazyInit.GetOrSet(ref this.returnType, returnType); LazyInit.GetOrSet(ref this.returnType, returnType);
LazyInit.GetOrSet(ref this.parameters, parameters); LazyInit.GetOrSet(ref this.parameters, parameters);
} }
internal static (IType, IParameter[]) DecodeSignature(MetadataModule module, IParameterizedMember owner, internal static (IType returnType, IParameter[] parameters, ModifiedType returnTypeModifier) DecodeSignature(
MetadataModule module, IParameterizedMember owner,
MethodSignature<IType> signature, ParameterHandleCollection? parameterHandles, MethodSignature<IType> signature, ParameterHandleCollection? parameterHandles,
Nullability nullableContext, TypeSystemOptions typeSystemOptions, Nullability nullableContext, TypeSystemOptions typeSystemOptions,
CustomAttributeHandleCollection? returnTypeAttributes = null) CustomAttributeHandleCollection? returnTypeAttributes = null)
@ -231,7 +245,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
Debug.Assert(i == parameters.Length); Debug.Assert(i == parameters.Length);
var returnType = ApplyAttributeTypeVisitor.ApplyAttributesToType(signature.ReturnType, var returnType = ApplyAttributeTypeVisitor.ApplyAttributesToType(signature.ReturnType,
module.Compilation, returnTypeAttributes, metadata, typeSystemOptions, nullableContext); module.Compilation, returnTypeAttributes, metadata, typeSystemOptions, nullableContext);
return (returnType, parameters); return (returnType, parameters, signature.ReturnType as ModifiedType);
} }
#endregion #endregion

3
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs

@ -153,7 +153,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
// Roslyn uses the same workaround (see the NullableTypeDecoder.TransformType // Roslyn uses the same workaround (see the NullableTypeDecoder.TransformType
// call in PEPropertySymbol). // call in PEPropertySymbol).
var typeOptions = module.OptionsForEntity(declTypeDef); var typeOptions = module.OptionsForEntity(declTypeDef);
(returnType, parameters) = MetadataMethod.DecodeSignature(module, this, signature, (returnType, parameters, _) = MetadataMethod.DecodeSignature(
module, this, signature,
parameterHandles, nullableContext, typeOptions, parameterHandles, nullableContext, typeOptions,
returnTypeAttributes: propertyDef.GetCustomAttributes()); returnTypeAttributes: propertyDef.GetCustomAttributes());
} catch (BadImageFormatException) { } catch (BadImageFormatException) {

1
ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs

@ -98,6 +98,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public bool ReturnTypeIsRefReadOnly => methodDefinition.ReturnTypeIsRefReadOnly; public bool ReturnTypeIsRefReadOnly => methodDefinition.ReturnTypeIsRefReadOnly;
bool IMethod.ThisIsRefReadOnly => methodDefinition.ThisIsRefReadOnly; bool IMethod.ThisIsRefReadOnly => methodDefinition.ThisIsRefReadOnly;
bool IMethod.IsInitOnly => methodDefinition.IsInitOnly;
public IReadOnlyList<ITypeParameter> TypeParameters { public IReadOnlyList<ITypeParameter> TypeParameters {
get { get {

1
ICSharpCode.Decompiler/TypeSystem/Implementation/SyntheticRangeIndexer.cs

@ -62,6 +62,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
bool IMethod.ReturnTypeIsRefReadOnly => underlyingMethod.ReturnTypeIsRefReadOnly; bool IMethod.ReturnTypeIsRefReadOnly => underlyingMethod.ReturnTypeIsRefReadOnly;
bool IMethod.ThisIsRefReadOnly => underlyingMethod.ThisIsRefReadOnly; bool IMethod.ThisIsRefReadOnly => underlyingMethod.ThisIsRefReadOnly;
bool IMethod.IsInitOnly => underlyingMethod.IsInitOnly;
IReadOnlyList<ITypeParameter> IMethod.TypeParameters => EmptyList<ITypeParameter>.Instance; IReadOnlyList<ITypeParameter> IMethod.TypeParameters => EmptyList<ITypeParameter>.Instance;
IReadOnlyList<IType> IMethod.TypeArguments => EmptyList<IType>.Instance; IReadOnlyList<IType> IMethod.TypeArguments => EmptyList<IType>.Instance;

1
ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs

@ -115,6 +115,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes(); IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes();
bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly; bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly;
bool IMethod.ThisIsRefReadOnly => baseMethod.ThisIsRefReadOnly; bool IMethod.ThisIsRefReadOnly => baseMethod.ThisIsRefReadOnly;
bool IMethod.IsInitOnly => baseMethod.IsInitOnly;
public IReadOnlyList<ITypeParameter> TypeParameters { public IReadOnlyList<ITypeParameter> TypeParameters {
get { return baseMethod.TypeParameters; } get { return baseMethod.TypeParameters; }

4
ICSharpCode.Decompiler/Util/CollectionExtensions.cs

@ -12,10 +12,12 @@ namespace ICSharpCode.Decompiler.Util
value = pair.Value; value = pair.Value;
} }
#if !NETCORE
public static IEnumerable<(A, B)> Zip<A, B>(this IEnumerable<A> input1, IEnumerable<B> input2) public static IEnumerable<(A, B)> Zip<A, B>(this IEnumerable<A> input1, IEnumerable<B> input2)
{ {
return input1.Zip(input2, (a, b) => (a, b)); return input1.Zip(input2, (a, b) => (a, b));
} }
#endif
public static IEnumerable<(A, B)> ZipLongest<A, B>(this IEnumerable<A> input1, IEnumerable<B> input2) public static IEnumerable<(A, B)> ZipLongest<A, B>(this IEnumerable<A> input1, IEnumerable<B> input2)
{ {
@ -51,10 +53,12 @@ namespace ICSharpCode.Decompiler.Util
} }
} }
#if !NETCORE
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> input) public static HashSet<T> ToHashSet<T>(this IEnumerable<T> input)
{ {
return new HashSet<T>(input); return new HashSet<T>(input);
} }
#endif
public static IEnumerable<T> SkipLast<T>(this IReadOnlyCollection<T> input, int count) public static IEnumerable<T> SkipLast<T>(this IReadOnlyCollection<T> input, int count)
{ {

23
ILSpy.AddIn/AssemblyFileFinder.cs

@ -1,32 +1,29 @@
using System; using System;
using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Util; using ICSharpCode.Decompiler.Util;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.AddIn namespace ICSharpCode.ILSpy.AddIn
{ {
public class AssemblyFileFinder public class AssemblyFileFinder
{ {
public static string FindAssemblyFile(AssemblyDefinition assemblyDefinition, string assemblyFile) public static string FindAssemblyFile(Mono.Cecil.AssemblyDefinition assemblyDefinition, string assemblyFile)
{ {
var assemblyResolver = new UniversalAssemblyResolver(assemblyFile, false, string tfi = DetectTargetFrameworkId(assemblyDefinition, assemblyFile);
DetectTargetFrameworkId(assemblyDefinition, assemblyFile)); UniversalAssemblyResolver assemblyResolver;
if (IsReferenceAssembly(assemblyDefinition, assemblyFile)) { if (IsReferenceAssembly(assemblyDefinition, assemblyFile)) {
assemblyResolver.RemoveSearchDirectory(Path.GetDirectoryName(assemblyFile)); assemblyResolver = new UniversalAssemblyResolver(null, throwOnError: false, tfi);
} else {
assemblyResolver = new UniversalAssemblyResolver(assemblyFile, throwOnError: false, tfi);
} }
return assemblyResolver.FindAssemblyFile(
ICSharpCode.Decompiler.Metadata.AssemblyNameReference.Parse(assemblyDefinition.Name.FullName)); return assemblyResolver.FindAssemblyFile(AssemblyNameReference.Parse(assemblyDefinition.Name.FullName));
} }
static readonly string RefPathPattern = @"NuGetFallbackFolder[/\\][^/\\]+[/\\][^/\\]+[/\\]ref[/\\]"; static readonly string RefPathPattern = @"NuGetFallbackFolder[/\\][^/\\]+[/\\][^/\\]+[/\\]ref[/\\]";
public static bool IsReferenceAssembly(AssemblyDefinition assemblyDef, string assemblyFile) public static bool IsReferenceAssembly(Mono.Cecil.AssemblyDefinition assemblyDef, string assemblyFile)
{ {
if (assemblyDef.CustomAttributes.Any(ca => ca.AttributeType.FullName == "System.Runtime.CompilerServices.ReferenceAssemblyAttribute")) if (assemblyDef.CustomAttributes.Any(ca => ca.AttributeType.FullName == "System.Runtime.CompilerServices.ReferenceAssemblyAttribute"))
return true; return true;
@ -40,7 +37,7 @@ namespace ICSharpCode.ILSpy.AddIn
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<1>.NETFramework)[/\\]v(?<2>[^/\\]+)[/\\])" + @"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<1>.NETFramework)[/\\]v(?<2>[^/\\]+)[/\\])" +
@"|((NuGetFallbackFolder|packs)[/\\](?<1>[^/\\]+)\\(?<2>[^/\\]+)([/\\].*)?[/\\]ref[/\\])"; @"|((NuGetFallbackFolder|packs)[/\\](?<1>[^/\\]+)\\(?<2>[^/\\]+)([/\\].*)?[/\\]ref[/\\])";
public static string DetectTargetFrameworkId(AssemblyDefinition assembly, string assemblyPath = null) public static string DetectTargetFrameworkId(Mono.Cecil.AssemblyDefinition assembly, string assemblyPath = null)
{ {
if (assembly == null) if (assembly == null)
throw new ArgumentNullException(nameof(assembly)); throw new ArgumentNullException(nameof(assembly));

14
ILSpy.AddIn/Commands/OpenCodeItemCommand.cs

@ -38,10 +38,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
var document = owner.DTE.ActiveDocument; var document = owner.DTE.ActiveDocument;
menuItem.Visible = menuItem.Visible =
(document != null) && (document?.ProjectItem?.ContainingProject?.ConfigurationManager != null) &&
(document.ProjectItem != null) &&
(document.ProjectItem.ContainingProject != null) &&
(document.ProjectItem.ContainingProject.ConfigurationManager != null) &&
!string.IsNullOrEmpty(document.ProjectItem.ContainingProject.FileName); !string.IsNullOrEmpty(document.ProjectItem.ContainingProject.FileName);
} }
} }
@ -51,7 +48,6 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
ThreadHelper.ThrowIfNotOnUIThread(); ThreadHelper.ThrowIfNotOnUIThread();
var document = owner.DTE.ActiveDocument; var document = owner.DTE.ActiveDocument;
var selection = (EnvDTE.TextPoint)((EnvDTE.TextSelection)document.Selection).ActivePoint;
var id = owner.Workspace.CurrentSolution.GetDocumentIdsWithFilePath(document.FullName).FirstOrDefault(); var id = owner.Workspace.CurrentSolution.GetDocumentIdsWithFilePath(document.FullName).FirstOrDefault();
if (id == null) if (id == null)
return null; return null;
@ -59,14 +55,6 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
return owner.Workspace.CurrentSolution.GetDocument(id); return owner.Workspace.CurrentSolution.GetDocument(id);
} }
EnvDTE.TextPoint GetEditorSelection()
{
ThreadHelper.ThrowIfNotOnUIThread();
var document = owner.DTE.ActiveDocument;
return ((EnvDTE.TextSelection)document.Selection).ActivePoint;
}
protected override async void OnExecute(object sender, EventArgs e) protected override async void OnExecute(object sender, EventArgs e)
{ {
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

5
ILSpy.AddIn/Commands/OpenILSpyCommand.cs

@ -50,7 +50,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
protected string GetILSpyPath() protected string GetILSpyPath()
{ {
var basePath = Path.GetDirectoryName(typeof(ILSpyAddInPackage).Assembly.Location); var basePath = Path.GetDirectoryName(typeof(ILSpyAddInPackage).Assembly.Location);
return Path.Combine(basePath, "ILSpy.exe"); return Path.Combine(basePath, "ILSpy", "ILSpy.exe");
} }
protected void OpenAssembliesInILSpy(ILSpyParameters parameters) protected void OpenAssembliesInILSpy(ILSpyParameters parameters)
@ -80,8 +80,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
using (var assemblyDef = AssemblyDefinition.ReadAssembly(reference.Display)) { using (var assemblyDef = AssemblyDefinition.ReadAssembly(reference.Display)) {
string assemblyName = assemblyDef.Name.Name; string assemblyName = assemblyDef.Name.Name;
string resolvedAssemblyFile = AssemblyFileFinder.FindAssemblyFile(assemblyDef, reference.Display); string resolvedAssemblyFile = AssemblyFileFinder.FindAssemblyFile(assemblyDef, reference.Display);
dict.Add(assemblyName, dict.Add(assemblyName, new DetectedReference(assemblyName, resolvedAssemblyFile, false));
new DetectedReference(assemblyName, resolvedAssemblyFile, false));
} }
} }
foreach (var projectReference in parentProject.ProjectReferences) { foreach (var projectReference in parentProject.ProjectReferences) {

39
ILSpy.AddIn/ILSpy.AddIn.csproj

@ -51,10 +51,15 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ILSpy\ILSpy.csproj" /> <ProjectReference Include="..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj" />
<ProjectReference Include="..\ILSpy\ILSpy.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<Private>false</Private>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\ILSpy\NativeMethods.cs" Link="NativeMethods.cs" />
<Compile Include="Commands\AssemblyReferenceForILSpy.cs" /> <Compile Include="Commands\AssemblyReferenceForILSpy.cs" />
<Compile Include="Commands\NuGetReferenceForILSpy.cs" /> <Compile Include="Commands\NuGetReferenceForILSpy.cs" />
<Compile Include="Commands\ProjectItemForILSpy.cs" /> <Compile Include="Commands\ProjectItemForILSpy.cs" />
@ -102,37 +107,19 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Collections.Immutable.dll" /> <Content Include="$(OutputPath)Mono.Cecil.dll">
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Composition.AttributedModel.dll" /> <IncludeInVSIX>true</IncludeInVSIX>
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Composition.Convention.dll" /> <VSIXSubPath>\</VSIXSubPath>
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Composition.Hosting.dll" /> </Content>
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Composition.Runtime.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Composition.TypedParts.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Threading.Tasks.Dataflow.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)ICSharpCode.AvalonEdit.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Humanizer.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Reflection.Metadata.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Microsoft.VisualStudio.Composition.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Microsoft.VisualStudio.Validation.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Mono.Cecil.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Mono.Cecil.Pdb.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)ILSpy.BamlDecompiler.Plugin.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Microsoft.DiaSymReader*.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)OSVersionHelper.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Xceed.Wpf.AvalonDock.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)DataGridExtensions.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Iced.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)ILCompiler.Reflection.ReadyToRun.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)ILSpy.ReadyToRun.Plugin.dll" />
</ItemGroup> </ItemGroup>
<!-- END WORKAROUND -->
<ItemGroup> <ItemGroup>
<Content Include="@(AdditionalDependencies)"> <Content Include="$(ILSpyBuildPath)*.dll;$(ILSpyBuildPath)ILSpy.exe;$(ILSpyBuildPath)ILSpy.exe.config">
<IncludeInVSIX>true</IncludeInVSIX> <IncludeInVSIX>true</IncludeInVSIX>
<VSIXSubPath>\</VSIXSubPath> <VSIXSubPath>\ILSpy</VSIXSubPath>
</Content> </Content>
</ItemGroup> </ItemGroup>
<!-- END WORKAROUND -->
<ItemGroup> <ItemGroup>
<None Include="ILSpyAddIn.vsct" /> <None Include="ILSpyAddIn.vsct" />

6
ILSpy.AddIn/ILSpyInstance.cs

@ -12,11 +12,11 @@ namespace ICSharpCode.ILSpy.AddIn
{ {
public ILSpyParameters(IEnumerable<string> assemblyFileNames, params string[] arguments) public ILSpyParameters(IEnumerable<string> assemblyFileNames, params string[] arguments)
{ {
this.AssemblyFileNames = assemblyFileNames; this.AssemblyFileNames = assemblyFileNames.ToArray();
this.Arguments = arguments; this.Arguments = arguments;
} }
public IEnumerable<string> AssemblyFileNames { get; private set; } public string[] AssemblyFileNames { get; private set; }
public string[] Arguments { get; private set; } public string[] Arguments { get; private set; }
} }
@ -32,7 +32,7 @@ namespace ICSharpCode.ILSpy.AddIn
static string GetILSpyPath() static string GetILSpyPath()
{ {
var basePath = Path.GetDirectoryName(typeof(ILSpyAddInPackage).Assembly.Location); var basePath = Path.GetDirectoryName(typeof(ILSpyAddInPackage).Assembly.Location);
return Path.Combine(basePath, "ILSpy.exe"); return Path.Combine(basePath, "ILSpy", "ILSpy.exe");
} }
public void Start() public void Start()

8
ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project> <Project Sdk="MSBuild.Sdk.Extras">
<Import Sdk="Microsoft.NET.Sdk" Project="Sdk.props" />
<PropertyGroup> <PropertyGroup>
<TargetFramework>net472</TargetFramework> <TargetFramework>net472</TargetFramework>
@ -10,6 +9,9 @@
<GenerateAssemblyInfo>False</GenerateAssemblyInfo> <GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<EnableDefaultItems>false</EnableDefaultItems> <EnableDefaultItems>false</EnableDefaultItems>
<UseWpf>true</UseWpf>
<ExtrasEnableDefaultPageItems>false</ExtrasEnableDefaultPageItems>
<ExtrasEnableDefaultResourceItems>false</ExtrasEnableDefaultResourceItems>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'"> <PropertyGroup Condition="'$(Configuration)' == 'Debug'">
@ -75,8 +77,6 @@
<PackageReference Include="ILCompiler.Reflection.ReadyToRun" Version="1.0.8-alpha" /> <PackageReference Include="ILCompiler.Reflection.ReadyToRun" Version="1.0.8-alpha" />
</ItemGroup> </ItemGroup>
<Import Sdk="Microsoft.NET.Sdk" Project="Sdk.targets" />
<Target Name="RemoveTransitiveProjectReferences" AfterTargets="IncludeTransitiveProjectReferences"> <Target Name="RemoveTransitiveProjectReferences" AfterTargets="IncludeTransitiveProjectReferences">
<ItemGroup> <ItemGroup>
<ProjectReference Remove="@(_TransitiveProjectReferences)" /> <ProjectReference Remove="@(_TransitiveProjectReferences)" />

13
ILSpy/App.xaml.cs

@ -165,6 +165,9 @@ namespace ICSharpCode.ILSpy
} }
} }
[ThreadStatic]
static bool showingError;
static void UnhandledException(Exception exception) static void UnhandledException(Exception exception)
{ {
Debug.WriteLine(exception.ToString()); Debug.WriteLine(exception.ToString());
@ -176,7 +179,17 @@ namespace ICSharpCode.ILSpy
break; break;
} }
} }
if (showingError) {
// Ignore re-entrant calls
// We run the risk of opening an infinite number of exception dialogs.
return;
}
showingError = true;
try {
MessageBox.Show(exception.ToString(), "Sorry, we crashed"); MessageBox.Show(exception.ToString(), "Sorry, we crashed");
} finally {
showingError = false;
}
} }
#endregion #endregion

6
ILSpy/Commands/DecompileAllCommand.cs

@ -19,6 +19,7 @@
#if DEBUG #if DEBUG
using System; using System;
using System.Collections.Concurrent;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -40,7 +41,10 @@ namespace ICSharpCode.ILSpy
{ {
Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => { Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new AvalonEditTextOutput(); AvalonEditTextOutput output = new AvalonEditTextOutput();
Parallel.ForEach(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct }, delegate(LoadedAssembly asm) { Parallel.ForEach(
Partitioner.Create( MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), loadBalance: true),
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct },
delegate(LoadedAssembly asm) {
if (!asm.HasLoadError) { if (!asm.HasLoadError) {
Stopwatch w = Stopwatch.StartNew(); Stopwatch w = Stopwatch.StartNew();
Exception exception = null; Exception exception = null;

7
ILSpy/Commands/DisassembleAllCommand.cs

@ -23,6 +23,8 @@ using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.Properties;
using System.Collections.Concurrent;
namespace ICSharpCode.ILSpy namespace ICSharpCode.ILSpy
{ {
[ExportMainMenuCommand(Menu = nameof(Resources._File), Header = nameof(Resources.DEBUGDisassemble), MenuCategory = nameof(Resources.Open), MenuOrder = 2.5)] [ExportMainMenuCommand(Menu = nameof(Resources._File), Header = nameof(Resources.DEBUGDisassemble), MenuCategory = nameof(Resources.Open), MenuOrder = 2.5)]
@ -37,7 +39,10 @@ namespace ICSharpCode.ILSpy
{ {
Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => { Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new AvalonEditTextOutput(); AvalonEditTextOutput output = new AvalonEditTextOutput();
Parallel.ForEach(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct }, delegate(LoadedAssembly asm) { Parallel.ForEach(
Partitioner.Create(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), loadBalance: true),
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct },
delegate (LoadedAssembly asm) {
if (!asm.HasLoadError) { if (!asm.HasLoadError) {
Stopwatch w = Stopwatch.StartNew(); Stopwatch w = Stopwatch.StartNew();
Exception exception = null; Exception exception = null;

4
ILSpy/ILSpy.csproj

@ -128,9 +128,7 @@
<Compile Include="Commands\ShowDebugSteps.cs" /> <Compile Include="Commands\ShowDebugSteps.cs" />
<Compile Include="Commands\SortAssemblyListCommand.cs" /> <Compile Include="Commands\SortAssemblyListCommand.cs" />
<Compile Include="Controls\BoolToVisibilityConverter.cs" /> <Compile Include="Controls\BoolToVisibilityConverter.cs" />
<Compile Include="Controls\CustomDialog.cs"> <Compile Include="Controls\CustomDialog.cs" />
<SubType>Form</SubType>
</Compile>
<Compile Include="Controls\GridViewColumnAutoSize.cs" /> <Compile Include="Controls\GridViewColumnAutoSize.cs" />
<Compile Include="Controls\MarkupExtensions.cs" /> <Compile Include="Controls\MarkupExtensions.cs" />
<Compile Include="Controls\ResourceObjectTable.xaml.cs"> <Compile Include="Controls\ResourceObjectTable.xaml.cs">

2
ILSpy/Languages/CSharpHighlightingTokenWriter.cs

@ -184,8 +184,10 @@ namespace ICSharpCode.ILSpy
case "set": case "set":
case "add": case "add":
case "remove": case "remove":
case "init":
if (role == PropertyDeclaration.GetKeywordRole || if (role == PropertyDeclaration.GetKeywordRole ||
role == PropertyDeclaration.SetKeywordRole || role == PropertyDeclaration.SetKeywordRole ||
role == PropertyDeclaration.InitKeywordRole ||
role == CustomEventDeclaration.AddKeywordRole || role == CustomEventDeclaration.AddKeywordRole ||
role == CustomEventDeclaration.RemoveKeywordRole) role == CustomEventDeclaration.RemoveKeywordRole)
color = accessorKeywordsColor; color = accessorKeywordsColor;

3
ILSpy/Languages/CSharpLanguage.cs

@ -648,6 +648,9 @@ namespace ICSharpCode.ILSpy
if (!settings.LiftNullables) { if (!settings.LiftNullables) {
flags &= ~ConversionFlags.UseNullableSpecifierForValueTypes; flags &= ~ConversionFlags.UseNullableSpecifierForValueTypes;
} }
if (entity is IMethod m && m.IsLocalFunction) {
writer.WriteIdentifier(Identifier.Create("(local)"));
}
new CSharpAmbience() { new CSharpAmbience() {
ConversionFlags = flags, ConversionFlags = flags,
}.ConvertSymbol(entity, writer, settings.CSharpFormattingOptions); }.ConvertSymbol(entity, writer, settings.CSharpFormattingOptions);

38
ILSpy/LoadedAssembly.cs

@ -20,17 +20,21 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using System.Reflection.PortableExecutable; using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Threading;
using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.PdbProvider; using ICSharpCode.Decompiler.PdbProvider;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.ILSpy.Options; using ICSharpCode.ILSpy.Options;
namespace ICSharpCode.ILSpy namespace ICSharpCode.ILSpy
@ -318,6 +322,17 @@ namespace ICSharpCode.ILSpy
static readonly Dictionary<string, LoadedAssembly> loadingAssemblies = new Dictionary<string, LoadedAssembly>(); static readonly Dictionary<string, LoadedAssembly> loadingAssemblies = new Dictionary<string, LoadedAssembly>();
MyUniversalResolver universalResolver; MyUniversalResolver universalResolver;
/// <summary>
/// 1) try to find exact match by tfm + full asm name in loaded assemblies
/// 2) try to find match in search paths
/// 3) if a.deps.json is found: search %USERPROFILE%/.nuget/packages/* as well
/// 4) look in /dotnet/shared/{runtime-pack}/{closest-version}
/// 5) if the version is retargetable or all zeros or ones, search C:\Windows\Microsoft.NET\Framework64\v4.0.30319
/// 6) For "mscorlib.dll" we use the exact same assembly with which ILSpy runs
/// 7) Search the GAC
/// 8) search C:\Windows\Microsoft.NET\Framework64\v4.0.30319
/// 9) try to find match by asm name (no tfm/version) in loaded assemblies
/// </summary>
LoadedAssembly LookupReferencedAssemblyInternal(IAssemblyReference fullName, bool isWinRT, string tfm) LoadedAssembly LookupReferencedAssemblyInternal(IAssemblyReference fullName, bool isWinRT, string tfm)
{ {
string key = tfm + ";" + (isWinRT ? fullName.Name : fullName.FullName); string key = tfm + ";" + (isWinRT ? fullName.Name : fullName.FullName);
@ -359,9 +374,30 @@ namespace ICSharpCode.ILSpy
LoadedAssemblyReferencesInfo.AddMessage(fullName.ToString(), MessageKind.Info, "Success - Loading from: " + file); LoadedAssemblyReferencesInfo.AddMessage(fullName.ToString(), MessageKind.Info, "Success - Loading from: " + file);
asm = new LoadedAssembly(assemblyList, file) { IsAutoLoaded = true }; asm = new LoadedAssembly(assemblyList, file) { IsAutoLoaded = true };
} else { } else {
var candidates = new List<(LoadedAssembly assembly, Version version)>();
foreach (LoadedAssembly loaded in assemblyList.GetAssemblies()) {
var module = loaded.GetPEFileOrNull();
var reader = module?.Metadata;
if (reader == null || !reader.IsAssembly) continue;
var asmDef = reader.GetAssemblyDefinition();
var asmDefName = reader.GetString(asmDef.Name);
if (fullName.Name.Equals(asmDefName, StringComparison.OrdinalIgnoreCase)) {
candidates.Add((loaded, asmDef.Version));
}
}
if (candidates.Count == 0) {
LoadedAssemblyReferencesInfo.AddMessageOnce(fullName.ToString(), MessageKind.Error, "Could not find reference: " + fullName); LoadedAssemblyReferencesInfo.AddMessageOnce(fullName.ToString(), MessageKind.Error, "Could not find reference: " + fullName);
return null; return null;
} }
candidates.SortBy(c => c.version);
var bestCandidate = candidates.FirstOrDefault(c => c.version >= fullName.Version).assembly ?? candidates.Last().assembly;
LoadedAssemblyReferencesInfo.AddMessageOnce(fullName.ToString(), MessageKind.Info, "Success - Found in Assembly List with different TFM or version: " + bestCandidate.fileName);
return bestCandidate;
}
loadingAssemblies.Add(file, asm); loadingAssemblies.Add(file, asm);
} }
App.Current.Dispatcher.BeginInvoke((Action)delegate () { App.Current.Dispatcher.BeginInvoke((Action)delegate () {
@ -371,7 +407,7 @@ namespace ICSharpCode.ILSpy
lock (loadingAssemblies) { lock (loadingAssemblies) {
loadingAssemblies.Remove(file); loadingAssemblies.Remove(file);
} }
}); }, DispatcherPriority.Normal);
return asm; return asm;
} }

9
ILSpy/MainWindow.xaml.cs

@ -408,6 +408,9 @@ namespace ICSharpCode.ILSpy
found = true; found = true;
} else { } else {
IEntity mr = await Task.Run(() => FindEntityInRelevantAssemblies(navigateTo, relevantAssemblies)); IEntity mr = await Task.Run(() => FindEntityInRelevantAssemblies(navigateTo, relevantAssemblies));
// Make sure we wait for assemblies being loaded...
// BeginInvoke in LoadedAssembly.LookupReferencedAssemblyInternal
await Dispatcher.InvokeAsync(delegate { }, DispatcherPriority.Normal);
if (mr != null && mr.ParentModule.PEFile != null) { if (mr != null && mr.ParentModule.PEFile != null) {
found = true; found = true;
if (AssemblyTreeView.SelectedItem == initialSelection) { if (AssemblyTreeView.SelectedItem == initialSelection) {
@ -483,6 +486,12 @@ namespace ICSharpCode.ILSpy
return false; return false;
} }
// We intentionally ignore reference assemblies, so that the loop continues looking for another assembly that might have a usable definition.
if (module.IsReferenceAssembly()) {
typeHandle = default;
return false;
}
switch (typeRef) { switch (typeRef) {
case GetPotentiallyNestedClassTypeReference topLevelType: case GetPotentiallyNestedClassTypeReference topLevelType:
typeHandle = topLevelType.ResolveInPEFile(module); typeHandle = topLevelType.ResolveInPEFile(module);

1
ILSpy/Metadata/Helpers.cs

@ -113,6 +113,7 @@ namespace ICSharpCode.ILSpy.Metadata
}; };
switch (e.PropertyName) { switch (e.PropertyName) {
case "RID": case "RID":
case "Meaning":
e.Column.SetTemplate((ControlTemplate)MetadataTableViews.Instance["DefaultFilter"]); e.Column.SetTemplate((ControlTemplate)MetadataTableViews.Instance["DefaultFilter"]);
((DataGridCustomTextColumn)e.Column).ToolTipBinding = null; ((DataGridCustomTextColumn)e.Column).ToolTipBinding = null;
break; break;

2
ILSpy/Properties/AssemblyInfo.template.cs

@ -37,7 +37,7 @@ using System.Diagnostics.CodeAnalysis;
internal static class RevisionClass internal static class RevisionClass
{ {
public const string Major = "6"; public const string Major = "6";
public const string Minor = "0"; public const string Minor = "1";
public const string Build = "0"; public const string Build = "0";
public const string Revision = "$INSERTREVISION$"; public const string Revision = "$INSERTREVISION$";
public const string VersionName = null; public const string VersionName = null;

7
ILSpy/SolutionWriter.cs

@ -99,7 +99,12 @@ namespace ICSharpCode.ILSpy
Stopwatch stopwatch = Stopwatch.StartNew(); Stopwatch stopwatch = Stopwatch.StartNew();
try { try {
await Task.Run(() => Parallel.ForEach(assemblies, n => WriteProject(n, language, solutionDirectory, ct))) // Explicitly create an enumerable partitioner here to avoid Parallel.ForEach's special cases for lists,
// as those seem to use static partitioning which is inefficient if assemblies take differently
// long to decompile.
await Task.Run(() => Parallel.ForEach(Partitioner.Create(assemblies),
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct },
n => WriteProject(n, language, solutionDirectory, ct)))
.ConfigureAwait(false); .ConfigureAwait(false);
await Task.Run(() => SolutionCreator.WriteSolutionFile(solutionFilePath, projects)) await Task.Run(() => SolutionCreator.WriteSolutionFile(solutionFilePath, projects))

Loading…
Cancel
Save