diff --git a/BuildTools/update-assemblyinfo.ps1 b/BuildTools/update-assemblyinfo.ps1 index a716d15a7..7e14e23da 100644 --- a/BuildTools/update-assemblyinfo.ps1 +++ b/BuildTools/update-assemblyinfo.ps1 @@ -53,7 +53,7 @@ function gitCommitHash() { } function gitBranch() { - if (-not ((Test-Dir ".git") -and (Find-Git))) { + if (-not ((Test-Dir ".git" -or Test-File ".git") -and (Find-Git))) { return "no-branch"; } diff --git a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs index 4ad298c7c..799f91517 100644 --- a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs @@ -83,8 +83,14 @@ namespace ICSharpCode.Decompiler.Tests } [Test] - public void FloatingPointArithmetic([ValueSource("defaultOptions")] CompilerOptions options) - { + public void FloatingPointArithmetic([ValueSource("noMonoOptions")] CompilerOptions options, [Values(32, 64)] int bits) + { + // The behavior of the #1794 incorrect `(float)(double)val` cast only causes test failures + // for some runtime+compiler combinations. + if (bits == 32) + options |= CompilerOptions.Force32Bit; + // Mono is excluded because we never use it for the second pass, so the test ends up failing + // due to some Mono vs. Roslyn compiler differences. RunCS(options: options); } diff --git a/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs b/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs index 214dda115..f75ca7a41 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs @@ -13,7 +13,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers var section = (AttributeSection)attribute.Parent; SimpleType type = attribute.Type as SimpleType; if (section.AttributeTarget == "assembly" && - (type.Identifier == "CompilationRelaxations" || type.Identifier == "RuntimeCompatibility" || type.Identifier == "SecurityPermission" || type.Identifier == "PermissionSet" || type.Identifier == "AssemblyVersion" || type.Identifier == "Debuggable")) + (type.Identifier == "CompilationRelaxations" || type.Identifier == "RuntimeCompatibility" || type.Identifier == "SecurityPermission" || type.Identifier == "PermissionSet" || type.Identifier == "AssemblyVersion" || type.Identifier == "Debuggable" || type.Identifier == "TargetFramework")) { attribute.Remove(); if (section.Attributes.Count == 0) diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs index f17a64a52..8776bac1f 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs @@ -55,6 +55,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers UseRoslyn = 0x10, UseMcs = 0x20, ReferenceVisualBasic = 0x40, + ReferenceCore = 0x80, } [Flags] @@ -89,12 +90,12 @@ namespace ICSharpCode.Decompiler.Tests.Helpers outputFile += ".exe"; otherOptions += "/exe "; } - - + + if (options.HasFlag(AssemblerOptions.UseDebug)) { otherOptions += "/debug "; } - + ProcessStartInfo info = new ProcessStartInfo(ilasmPath); info.Arguments = $"/nologo {otherOptions}/output=\"{outputFile}\" \"{sourceFileName}\""; info.RedirectStandardError = true; @@ -115,7 +116,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers return outputFile; } - + public static string Disassemble(string sourceFileName, string outputFile, AssemblerOptions asmOptions) { if (asmOptions.HasFlag(AssemblerOptions.UseOwnDisassembler)) { @@ -141,7 +142,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers } string ildasmPath = SdkUtility.GetSdkPath("ildasm.exe"); - + ProcessStartInfo info = new ProcessStartInfo(ildasmPath); info.Arguments = $"/nobar /utf8 /out=\"{outputFile}\" \"{sourceFileName}\""; info.RedirectStandardError = true; @@ -182,8 +183,10 @@ namespace ICSharpCode.Decompiler.Tests.Helpers return Regex.Replace(il, @"'\{[0-9A-F-]+\}'", "''"); } + static readonly string coreRefAsmPath = new DotNetCorePathFinder(new Version(3, 0)).GetReferenceAssemblyPath(".NETCoreApp, Version = v3.0"); + 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"); static readonly string thisAsmPath = Path.GetDirectoryName(typeof(Tester).Assembly.Location); static readonly Lazy> defaultReferences = new Lazy>(delegate { @@ -202,6 +205,21 @@ namespace ICSharpCode.Decompiler.Tests.Helpers }; }); + static readonly Lazy> coreDefaultReferences = new Lazy>(GetDefaultReferences); + + const string targetFrameworkAttributeSnippet = @" + +[assembly: System.Runtime.Versioning.TargetFramework("".NETCoreApp, Version = v3.0"", FrameworkDisplayName = """")] + +"; + + static IEnumerable GetDefaultReferences() + { + foreach (var reference in Directory.EnumerateFiles(coreRefAsmPath, "*.dll")) { + yield return MetadataReference.CreateFromFile(reference); + } + } + static readonly Lazy> visualBasic = new Lazy>(delegate { return new[] { MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "Microsoft.VisualBasic.dll")) @@ -217,6 +235,9 @@ namespace ICSharpCode.Decompiler.Tests.Helpers if (flags.HasFlag(CompilerOptions.Optimize)) { preprocessorSymbols.Add("OPT"); } + if (flags.HasFlag(CompilerOptions.ReferenceCore)) { + preprocessorSymbols.Add("NETCORE"); + } if (flags.HasFlag(CompilerOptions.UseRoslyn)) { preprocessorSymbols.Add("ROSLYN"); preprocessorSymbols.Add("CS60"); @@ -252,7 +273,15 @@ namespace ICSharpCode.Decompiler.Tests.Helpers languageVersion: Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8 ); var syntaxTrees = sourceFileNames.Select(f => SyntaxFactory.ParseSyntaxTree(File.ReadAllText(f), parseOptions, path: f)); - var references = defaultReferences.Value; + if (flags.HasFlag(CompilerOptions.ReferenceCore)) { + syntaxTrees = syntaxTrees.Concat(new[] { SyntaxFactory.ParseSyntaxTree(targetFrameworkAttributeSnippet) }); + } + IEnumerable references; + if (flags.HasFlag(CompilerOptions.ReferenceCore)) { + references = coreDefaultReferences.Value; + } else { + references = defaultReferences.Value; + } if (flags.HasFlag(CompilerOptions.ReferenceVisualBasic)) { references = references.Concat(visualBasic.Value); } @@ -466,16 +495,16 @@ namespace ICSharpCode.Decompiler.Tests.Helpers return fileName; } } - + public static void RunAndCompareOutput(string testFileName, string outputFile, string decompiledOutputFile, string decompiledCodeFile = null) { string output1, output2, error1, error2; int result1 = Tester.Run(outputFile, out output1, out error1); int result2 = Tester.Run(decompiledOutputFile, out output2, out error2); - + Assert.AreEqual(0, result1, "Exit code != 0; did the test case crash?" + Environment.NewLine + error1); Assert.AreEqual(0, result2, "Exit code != 0; did the decompiled code crash?" + Environment.NewLine + error2); - + if (output1 != output2 || error1 != error2) { StringBuilder b = new StringBuilder(); b.AppendLine($"Test {testFileName} failed: output does not match."); diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index a591c7338..a3070ae32 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -42,8 +42,8 @@ - - + + @@ -60,6 +60,7 @@ + @@ -80,7 +81,10 @@ + + + diff --git a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs index a709794f8..916084055 100644 --- a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs @@ -198,6 +198,12 @@ namespace ICSharpCode.Decompiler.Tests Run(settings: new DecompilerSettings { RemoveDeadStores = true }); } + [Test] + public void WeirdEnums() + { + Run(); + } + void Run([CallerMemberName] string testName = null, DecompilerSettings settings = null) { var ilFile = Path.Combine(TestCasePath, testName + ".il"); diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 4a34baaf9..8af4d4a13 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -59,6 +59,12 @@ namespace ICSharpCode.Decompiler.Tests CompilerOptions.Optimize | CompilerOptions.UseRoslyn }; + static readonly CompilerOptions[] dotnetCoreOnlyOptions = + { + CompilerOptions.UseRoslyn | CompilerOptions.ReferenceCore, + CompilerOptions.Optimize | CompilerOptions.UseRoslyn | CompilerOptions.ReferenceCore + }; + static readonly CompilerOptions[] defaultOptions = { CompilerOptions.None, @@ -301,6 +307,18 @@ namespace ICSharpCode.Decompiler.Tests Run(cscOptions: cscOptions); } + [Test] + public void AsyncStreams([ValueSource(nameof(dotnetCoreOnlyOptions))] CompilerOptions cscOptions) + { + RunForLibrary(cscOptions: cscOptions); + } + + [Test] + public void AsyncUsing([ValueSource(nameof(dotnetCoreOnlyOptions))] CompilerOptions cscOptions) + { + RunForLibrary(cscOptions: cscOptions); + } + [Test] public void CustomTaskType([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) { @@ -436,7 +454,7 @@ namespace ICSharpCode.Decompiler.Tests [Test] public void InterfaceTests([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { - RunForLibrary(cscOptions: cscOptions); + RunForLibrary(cscOptions: cscOptions | CompilerOptions.ReferenceCore); } [Test] diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/FloatingPointArithmetic.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/FloatingPointArithmetic.cs index 72c0bbc72..e5a8fa9f3 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/FloatingPointArithmetic.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/FloatingPointArithmetic.cs @@ -8,12 +8,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { Issue999(); Issue1656(); + Issue1794(); return 0; } static void Issue999() { - for (float i = -10f; i <= 10f; i += 0.01f) + for (float i = -10f; i <= 10f; i += 0.01f) Console.WriteLine("{1:R}: {0:R}", M(i), i); } @@ -31,6 +32,63 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness CxAssert((primary--) == 'C'); } + static void Issue1794() + { + Console.WriteLine("CastUnsignedToFloat:"); + Console.WriteLine(CastUnsignedToFloat(9007199791611905).ToString("r")); + Console.WriteLine("CastUnsignedToDouble:"); + Console.WriteLine(CastUnsignedToDouble(9007199791611905).ToString("r")); + Console.WriteLine("CastUnsignedToFloatViaDouble:"); + Console.WriteLine(CastUnsignedToFloatViaDouble(9007199791611905).ToString("r")); + + Console.WriteLine("CastSignedToFloat:"); + Console.WriteLine(CastSignedToFloat(9007199791611905).ToString("r")); + Console.WriteLine("ImplicitCastSignedToFloat:"); + Console.WriteLine(ImplicitCastSignedToFloat(9007199791611905).ToString("r")); + Console.WriteLine("CastSignedToDouble:"); + Console.WriteLine(CastSignedToDouble(9007199791611905).ToString("r")); + Console.WriteLine("CastSignedToFloatViaDouble:"); + Console.WriteLine(CastSignedToFloatViaDouble(9007199791611905).ToString("r")); + } + + static float CastUnsignedToFloat(ulong val) + { + return (float)val; + } + + static double CastUnsignedToDouble(ulong val) + { + return (double)val; + } + + static float CastUnsignedToFloatViaDouble(ulong val) + { + // The double-rounding can increase the rounding error + return (float)(double)val; + } + + static float CastSignedToFloat(long val) + { + return (float)val; + } + + + static double CastSignedToDouble(long val) + { + return (double)val; + } + + static float CastSignedToFloatViaDouble(long val) + { + // The double-rounding can increase the rounding error + return (float)(double)val; + } + + static float ImplicitCastSignedToFloat(long val) + { + return val; + } + static void CxAssert(bool v) { if (!v) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/TrickyTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/TrickyTypes.cs index 437c37e54..151b8ac95 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/TrickyTypes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/TrickyTypes.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Linq; namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { @@ -27,6 +28,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness InterestingConstants(); TruncatedComp(); StringConcat(); + LinqNullableMin(); + LinqNullableMin(1, 2, 3); } static void Print(T val) @@ -101,5 +104,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness Print(string.Concat(1, 2)); Print(string.Concat(1, 2, "str")); } + + static void LinqNullableMin(params int[] arr) + { + Print(string.Format("LinqNullableMin {0}:", arr.Length)); + Print(arr.Min(v => (int?)v)); + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Debug.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Debug.cs index f199ea1fd..62620a8b9 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Debug.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Debug.cs @@ -14,14 +14,12 @@ using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.Versioning; [assembly: FSharpInterfaceDataVersion(2, 0, 0)] [assembly: AssemblyTitle("ConsoleApplication1")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ConsoleApplication1")] [assembly: AssemblyFileVersion("1.0.0.0")] -[assembly: TargetFramework(".NETFramework,Version=v4.6.1", FrameworkDisplayName = ".NET Framework 4.6.1")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCopyright("Copyright © 2017")] diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Release.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Release.cs index bfadda404..b465dafc3 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Release.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Release.cs @@ -15,14 +15,12 @@ using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.Versioning; [assembly: FSharpInterfaceDataVersion(2, 0, 0)] [assembly: AssemblyTitle("ConsoleApplication1")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ConsoleApplication1")] [assembly: AssemblyFileVersion("1.0.0.0")] -[assembly: TargetFramework(".NETFramework,Version=v4.6.1", FrameworkDisplayName = ".NET Framework 4.6.1")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCopyright("Copyright © 2017")] diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1325.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1325.cs index daa5be110..a2b728758 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1325.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1325.cs @@ -6,10 +6,8 @@ using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; -using System.Runtime.Versioning; [assembly: Embedded] [assembly: AssemblyInformationalVersion("1.0.0")] -[assembly: TargetFramework(".NETCoreApp,Version=v2.1", FrameworkDisplayName = "")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/WeirdEnums.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/WeirdEnums.cs new file mode 100644 index 000000000..f285a6a9c --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/WeirdEnums.cs @@ -0,0 +1,19 @@ +using System; + +namespace TestEnum +{ + public enum BooleanEnum : bool + { + Min = false, + Zero = false, + One = true, + Max = byte.MaxValue + } + + public enum NativeIntEnum : IntPtr + { + Zero = 0L, + One = 1L, + FortyTwo = 42L + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/WeirdEnums.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/WeirdEnums.il new file mode 100644 index 000000000..29356ef8a --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/WeirdEnums.il @@ -0,0 +1,36 @@ +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly BoolEnum +{ + .ver 1:0:0:0 +} +.module BoolEnum.exe +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00020003 // ILONLY 32BITPREFERRED +// Image base: 0x000001C4B6C90000 + +.class public auto ansi sealed TestEnum.BooleanEnum + extends [mscorlib]System.Enum +{ + .field public specialname rtspecialname bool value__ + .field public static literal valuetype TestEnum.BooleanEnum Min = bool(false) + .field public static literal valuetype TestEnum.BooleanEnum Zero = bool(false) + .field public static literal valuetype TestEnum.BooleanEnum One = bool(true) + .field public static literal valuetype TestEnum.BooleanEnum Max = uint8(255) +} + +.class public auto ansi sealed TestEnum.NativeIntEnum + extends [mscorlib]System.Enum +{ + .field public specialname rtspecialname native int value__ + .field public static literal valuetype TestEnum.NativeIntEnum Zero = int64(0) + .field public static literal valuetype TestEnum.NativeIntEnum One = int64(1) + .field public static literal valuetype TestEnum.NativeIntEnum FortyTwo = int64(42) +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs index 7d51bc732..746925b6e 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs @@ -199,6 +199,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty #endif } + public struct AsyncInStruct + { + private int i; + + public async Task Test(AsyncInStruct xx) + { + xx.i++; + i++; + await Task.Yield(); + return i + xx.i; + } + } + public struct HopToThreadPoolAwaitable : INotifyCompletion { public bool IsCompleted { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/AsyncStreams.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/AsyncStreams.cs new file mode 100644 index 000000000..268f4b13a --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/AsyncStreams.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + public class AsyncStreams + { + public static async IAsyncEnumerable CountTo(int until) + { + for (int i = 0; i < until; i++) { + yield return i; + await Task.Delay(10); + } + } + + public static async IAsyncEnumerable AlwaysThrow() + { + throw null; + yield break; + } + + public static async IAsyncEnumerator InfiniteLoop() + { + while (true) { + } + yield break; + } + + public static async IAsyncEnumerable InfiniteLoopWithAwait() + { + while (true) { + await Task.Delay(10); + } + yield break; + } + + public async IAsyncEnumerable AwaitInFinally() + { + try { + Console.WriteLine("try"); + yield return 1; + Console.WriteLine("end try"); + } finally { + Console.WriteLine("finally"); + await Task.Yield(); + Console.WriteLine("end finally"); + } + } + } + + public struct TestStruct + { + private int i; + + public async IAsyncEnumerable AwaitInStruct(TestStruct xx) + { + xx.i++; + i++; + await Task.Yield(); + yield return i; + yield return xx.i; + } + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/AsyncUsing.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/AsyncUsing.cs new file mode 100644 index 000000000..98b3ce07b --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/AsyncUsing.cs @@ -0,0 +1,60 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + internal class AsyncUsing + { + internal class AsyncDisposableClass : IAsyncDisposable + { + public ValueTask DisposeAsync() + { + throw new NotImplementedException(); + } + } + + [StructLayout(LayoutKind.Sequential, Size = 1)] + internal struct AsyncDisposableStruct : IAsyncDisposable + { + public ValueTask DisposeAsync() + { + throw new NotImplementedException(); + } + } + + public static async void TestAsyncUsing(IAsyncDisposable disposable) + { + await using (disposable) { + Console.WriteLine("Hello"); + } + } + + public static async void TestAsyncUsingClass() + { + await using (AsyncDisposableClass test = new AsyncDisposableClass()) { + Use(test); + } + } + + public static async void TestAsyncUsingStruct() + { + await using (AsyncDisposableStruct asyncDisposableStruct = default(AsyncDisposableStruct)) { + Use(asyncDisposableStruct); + } + } + + public static async void TestAsyncUsingNullableStruct() + { + await using (AsyncDisposableStruct? asyncDisposableStruct = new AsyncDisposableStruct?(default(AsyncDisposableStruct))) { + Use(asyncDisposableStruct); + } + } + + private static void Use(IAsyncDisposable test) + { + throw new NotImplementedException(); + } + + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs index 0ea7e675b..eebe0155c 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs @@ -420,8 +420,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty set; } + private static CustomStruct2 GetStruct() + { + throw new NotImplementedException(); + } #if CS70 - private static ref CustomStruct2 GetStruct() + private static ref CustomStruct2 GetRefStruct() { throw new NotImplementedException(); } @@ -910,8 +914,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ByteField += 5; GetClass().ByteProp += 5; #if CS70 - GetStruct().ByteField += 5; - GetStruct().ByteProp += 5; + GetRefStruct().ByteField += 5; + GetRefStruct().ByteProp += 5; GetRefByte() += 5; #endif } @@ -936,8 +940,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ByteField -= 5; GetClass().ByteProp -= 5; #if CS70 - GetStruct().ByteField -= 5; - GetStruct().ByteProp -= 5; + GetRefStruct().ByteField -= 5; + GetRefStruct().ByteProp -= 5; GetRefByte() -= 5; #endif } @@ -962,8 +966,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ByteField *= 5; GetClass().ByteProp *= 5; #if CS70 - GetStruct().ByteField *= 5; - GetStruct().ByteProp *= 5; + GetRefStruct().ByteField *= 5; + GetRefStruct().ByteProp *= 5; GetRefByte() *= 5; #endif } @@ -988,8 +992,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ByteField /= 5; GetClass().ByteProp /= 5; #if CS70 - GetStruct().ByteField /= 5; - GetStruct().ByteProp /= 5; + GetRefStruct().ByteField /= 5; + GetRefStruct().ByteProp /= 5; GetRefByte() /= 5; #endif } @@ -1014,8 +1018,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ByteField %= 5; GetClass().ByteProp %= 5; #if CS70 - GetStruct().ByteField %= 5; - GetStruct().ByteProp %= 5; + GetRefStruct().ByteField %= 5; + GetRefStruct().ByteProp %= 5; GetRefByte() %= 5; #endif } @@ -1040,8 +1044,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ByteField <<= 5; GetClass().ByteProp <<= 5; #if CS70 - GetStruct().ByteField <<= 5; - GetStruct().ByteProp <<= 5; + GetRefStruct().ByteField <<= 5; + GetRefStruct().ByteProp <<= 5; GetRefByte() <<= 5; #endif } @@ -1066,8 +1070,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ByteField >>= 5; GetClass().ByteProp >>= 5; #if CS70 - GetStruct().ByteField >>= 5; - GetStruct().ByteProp >>= 5; + GetRefStruct().ByteField >>= 5; + GetRefStruct().ByteProp >>= 5; GetRefByte() >>= 5; #endif } @@ -1092,8 +1096,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ByteField &= 5; GetClass().ByteProp &= 5; #if CS70 - GetStruct().ByteField &= 5; - GetStruct().ByteProp &= 5; + GetRefStruct().ByteField &= 5; + GetRefStruct().ByteProp &= 5; GetRefByte() &= 5; #endif } @@ -1118,8 +1122,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ByteField |= 5; GetClass().ByteProp |= 5; #if CS70 - GetStruct().ByteField |= 5; - GetStruct().ByteProp |= 5; + GetRefStruct().ByteField |= 5; + GetRefStruct().ByteProp |= 5; GetRefByte() |= 5; #endif } @@ -1144,8 +1148,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ByteField ^= 5; GetClass().ByteProp ^= 5; #if CS70 - GetStruct().ByteField ^= 5; - GetStruct().ByteProp ^= 5; + GetRefStruct().ByteField ^= 5; + GetRefStruct().ByteProp ^= 5; GetRefByte() ^= 5; #endif } @@ -1170,8 +1174,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().ByteField++); X(GetClass().ByteProp++); #if CS70 - X(GetStruct().ByteField++); - X(GetStruct().ByteProp++); + X(GetRefStruct().ByteField++); + X(GetRefStruct().ByteProp++); X(GetRefByte()++); #endif } @@ -1196,8 +1200,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(++GetClass().ByteField); X(++GetClass().ByteProp); #if CS70 - X(++GetStruct().ByteField); - X(++GetStruct().ByteProp); + X(++GetRefStruct().ByteField); + X(++GetRefStruct().ByteProp); X(++GetRefByte()); #endif } @@ -1221,8 +1225,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().ByteField--); X(GetClass().ByteProp--); #if CS70 - X(GetStruct().ByteField--); - X(GetStruct().ByteProp--); + X(GetRefStruct().ByteField--); + X(GetRefStruct().ByteProp--); X(GetRefByte()--); #endif } @@ -1247,8 +1251,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(--GetClass().ByteField); X(--GetClass().ByteProp); #if CS70 - X(--GetStruct().ByteField); - X(--GetStruct().ByteProp); + X(--GetRefStruct().ByteField); + X(--GetRefStruct().ByteProp); X(--GetRefByte()); #endif } @@ -1272,8 +1276,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().SbyteField += 5; GetClass().SbyteProp += 5; #if CS70 - GetStruct().SbyteField += 5; - GetStruct().SbyteProp += 5; + GetRefStruct().SbyteField += 5; + GetRefStruct().SbyteProp += 5; GetRefSbyte() += 5; #endif } @@ -1298,8 +1302,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().SbyteField -= 5; GetClass().SbyteProp -= 5; #if CS70 - GetStruct().SbyteField -= 5; - GetStruct().SbyteProp -= 5; + GetRefStruct().SbyteField -= 5; + GetRefStruct().SbyteProp -= 5; GetRefSbyte() -= 5; #endif } @@ -1324,8 +1328,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().SbyteField *= 5; GetClass().SbyteProp *= 5; #if CS70 - GetStruct().SbyteField *= 5; - GetStruct().SbyteProp *= 5; + GetRefStruct().SbyteField *= 5; + GetRefStruct().SbyteProp *= 5; GetRefSbyte() *= 5; #endif } @@ -1350,8 +1354,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().SbyteField /= 5; GetClass().SbyteProp /= 5; #if CS70 - GetStruct().SbyteField /= 5; - GetStruct().SbyteProp /= 5; + GetRefStruct().SbyteField /= 5; + GetRefStruct().SbyteProp /= 5; GetRefSbyte() /= 5; #endif } @@ -1376,8 +1380,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().SbyteField %= 5; GetClass().SbyteProp %= 5; #if CS70 - GetStruct().SbyteField %= 5; - GetStruct().SbyteProp %= 5; + GetRefStruct().SbyteField %= 5; + GetRefStruct().SbyteProp %= 5; GetRefSbyte() %= 5; #endif } @@ -1402,8 +1406,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().SbyteField <<= 5; GetClass().SbyteProp <<= 5; #if CS70 - GetStruct().SbyteField <<= 5; - GetStruct().SbyteProp <<= 5; + GetRefStruct().SbyteField <<= 5; + GetRefStruct().SbyteProp <<= 5; GetRefSbyte() <<= 5; #endif } @@ -1428,8 +1432,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().SbyteField >>= 5; GetClass().SbyteProp >>= 5; #if CS70 - GetStruct().SbyteField >>= 5; - GetStruct().SbyteProp >>= 5; + GetRefStruct().SbyteField >>= 5; + GetRefStruct().SbyteProp >>= 5; GetRefSbyte() >>= 5; #endif } @@ -1454,8 +1458,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().SbyteField &= 5; GetClass().SbyteProp &= 5; #if CS70 - GetStruct().SbyteField &= 5; - GetStruct().SbyteProp &= 5; + GetRefStruct().SbyteField &= 5; + GetRefStruct().SbyteProp &= 5; GetRefSbyte() &= 5; #endif } @@ -1480,8 +1484,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().SbyteField |= 5; GetClass().SbyteProp |= 5; #if CS70 - GetStruct().SbyteField |= 5; - GetStruct().SbyteProp |= 5; + GetRefStruct().SbyteField |= 5; + GetRefStruct().SbyteProp |= 5; GetRefSbyte() |= 5; #endif } @@ -1506,8 +1510,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().SbyteField ^= 5; GetClass().SbyteProp ^= 5; #if CS70 - GetStruct().SbyteField ^= 5; - GetStruct().SbyteProp ^= 5; + GetRefStruct().SbyteField ^= 5; + GetRefStruct().SbyteProp ^= 5; GetRefSbyte() ^= 5; #endif } @@ -1532,8 +1536,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().SbyteField++); X(GetClass().SbyteProp++); #if CS70 - X(GetStruct().SbyteField++); - X(GetStruct().SbyteProp++); + X(GetRefStruct().SbyteField++); + X(GetRefStruct().SbyteProp++); X(GetRefSbyte()++); #endif } @@ -1558,8 +1562,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(++GetClass().SbyteField); X(++GetClass().SbyteProp); #if CS70 - X(++GetStruct().SbyteField); - X(++GetStruct().SbyteProp); + X(++GetRefStruct().SbyteField); + X(++GetRefStruct().SbyteProp); X(++GetRefSbyte()); #endif } @@ -1583,8 +1587,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().SbyteField--); X(GetClass().SbyteProp--); #if CS70 - X(GetStruct().SbyteField--); - X(GetStruct().SbyteProp--); + X(GetRefStruct().SbyteField--); + X(GetRefStruct().SbyteProp--); X(GetRefSbyte()--); #endif } @@ -1609,8 +1613,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(--GetClass().SbyteField); X(--GetClass().SbyteProp); #if CS70 - X(--GetStruct().SbyteField); - X(--GetStruct().SbyteProp); + X(--GetRefStruct().SbyteField); + X(--GetRefStruct().SbyteProp); X(--GetRefSbyte()); #endif } @@ -1634,8 +1638,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ShortField += 5; GetClass().ShortProp += 5; #if CS70 - GetStruct().ShortField += 5; - GetStruct().ShortProp += 5; + GetRefStruct().ShortField += 5; + GetRefStruct().ShortProp += 5; GetRefShort() += 5; #endif } @@ -1660,8 +1664,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ShortField -= 5; GetClass().ShortProp -= 5; #if CS70 - GetStruct().ShortField -= 5; - GetStruct().ShortProp -= 5; + GetRefStruct().ShortField -= 5; + GetRefStruct().ShortProp -= 5; GetRefShort() -= 5; #endif } @@ -1686,8 +1690,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ShortField *= 5; GetClass().ShortProp *= 5; #if CS70 - GetStruct().ShortField *= 5; - GetStruct().ShortProp *= 5; + GetRefStruct().ShortField *= 5; + GetRefStruct().ShortProp *= 5; GetRefShort() *= 5; #endif } @@ -1712,8 +1716,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ShortField /= 5; GetClass().ShortProp /= 5; #if CS70 - GetStruct().ShortField /= 5; - GetStruct().ShortProp /= 5; + GetRefStruct().ShortField /= 5; + GetRefStruct().ShortProp /= 5; GetRefShort() /= 5; #endif } @@ -1738,8 +1742,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ShortField %= 5; GetClass().ShortProp %= 5; #if CS70 - GetStruct().ShortField %= 5; - GetStruct().ShortProp %= 5; + GetRefStruct().ShortField %= 5; + GetRefStruct().ShortProp %= 5; GetRefShort() %= 5; #endif } @@ -1764,8 +1768,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ShortField <<= 5; GetClass().ShortProp <<= 5; #if CS70 - GetStruct().ShortField <<= 5; - GetStruct().ShortProp <<= 5; + GetRefStruct().ShortField <<= 5; + GetRefStruct().ShortProp <<= 5; GetRefShort() <<= 5; #endif } @@ -1790,8 +1794,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ShortField >>= 5; GetClass().ShortProp >>= 5; #if CS70 - GetStruct().ShortField >>= 5; - GetStruct().ShortProp >>= 5; + GetRefStruct().ShortField >>= 5; + GetRefStruct().ShortProp >>= 5; GetRefShort() >>= 5; #endif } @@ -1816,8 +1820,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ShortField &= 5; GetClass().ShortProp &= 5; #if CS70 - GetStruct().ShortField &= 5; - GetStruct().ShortProp &= 5; + GetRefStruct().ShortField &= 5; + GetRefStruct().ShortProp &= 5; GetRefShort() &= 5; #endif } @@ -1842,8 +1846,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ShortField |= 5; GetClass().ShortProp |= 5; #if CS70 - GetStruct().ShortField |= 5; - GetStruct().ShortProp |= 5; + GetRefStruct().ShortField |= 5; + GetRefStruct().ShortProp |= 5; GetRefShort() |= 5; #endif } @@ -1868,8 +1872,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().ShortField ^= 5; GetClass().ShortProp ^= 5; #if CS70 - GetStruct().ShortField ^= 5; - GetStruct().ShortProp ^= 5; + GetRefStruct().ShortField ^= 5; + GetRefStruct().ShortProp ^= 5; GetRefShort() ^= 5; #endif } @@ -1894,8 +1898,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().ShortField++); X(GetClass().ShortProp++); #if CS70 - X(GetStruct().ShortField++); - X(GetStruct().ShortProp++); + X(GetRefStruct().ShortField++); + X(GetRefStruct().ShortProp++); X(GetRefShort()++); #endif } @@ -1920,8 +1924,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(++GetClass().ShortField); X(++GetClass().ShortProp); #if CS70 - X(++GetStruct().ShortField); - X(++GetStruct().ShortProp); + X(++GetRefStruct().ShortField); + X(++GetRefStruct().ShortProp); X(++GetRefShort()); #endif } @@ -1945,8 +1949,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().ShortField--); X(GetClass().ShortProp--); #if CS70 - X(GetStruct().ShortField--); - X(GetStruct().ShortProp--); + X(GetRefStruct().ShortField--); + X(GetRefStruct().ShortProp--); X(GetRefShort()--); #endif } @@ -1971,8 +1975,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(--GetClass().ShortField); X(--GetClass().ShortProp); #if CS70 - X(--GetStruct().ShortField); - X(--GetStruct().ShortProp); + X(--GetRefStruct().ShortField); + X(--GetRefStruct().ShortProp); X(--GetRefShort()); #endif } @@ -1996,8 +2000,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UshortField += 5; GetClass().UshortProp += 5; #if CS70 - GetStruct().UshortField += 5; - GetStruct().UshortProp += 5; + GetRefStruct().UshortField += 5; + GetRefStruct().UshortProp += 5; GetRefUshort() += 5; #endif } @@ -2022,8 +2026,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UshortField -= 5; GetClass().UshortProp -= 5; #if CS70 - GetStruct().UshortField -= 5; - GetStruct().UshortProp -= 5; + GetRefStruct().UshortField -= 5; + GetRefStruct().UshortProp -= 5; GetRefUshort() -= 5; #endif } @@ -2048,8 +2052,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UshortField *= 5; GetClass().UshortProp *= 5; #if CS70 - GetStruct().UshortField *= 5; - GetStruct().UshortProp *= 5; + GetRefStruct().UshortField *= 5; + GetRefStruct().UshortProp *= 5; GetRefUshort() *= 5; #endif } @@ -2074,8 +2078,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UshortField /= 5; GetClass().UshortProp /= 5; #if CS70 - GetStruct().UshortField /= 5; - GetStruct().UshortProp /= 5; + GetRefStruct().UshortField /= 5; + GetRefStruct().UshortProp /= 5; GetRefUshort() /= 5; #endif } @@ -2100,8 +2104,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UshortField %= 5; GetClass().UshortProp %= 5; #if CS70 - GetStruct().UshortField %= 5; - GetStruct().UshortProp %= 5; + GetRefStruct().UshortField %= 5; + GetRefStruct().UshortProp %= 5; GetRefUshort() %= 5; #endif } @@ -2126,8 +2130,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UshortField <<= 5; GetClass().UshortProp <<= 5; #if CS70 - GetStruct().UshortField <<= 5; - GetStruct().UshortProp <<= 5; + GetRefStruct().UshortField <<= 5; + GetRefStruct().UshortProp <<= 5; GetRefUshort() <<= 5; #endif } @@ -2152,8 +2156,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UshortField >>= 5; GetClass().UshortProp >>= 5; #if CS70 - GetStruct().UshortField >>= 5; - GetStruct().UshortProp >>= 5; + GetRefStruct().UshortField >>= 5; + GetRefStruct().UshortProp >>= 5; GetRefUshort() >>= 5; #endif } @@ -2178,8 +2182,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UshortField &= 5; GetClass().UshortProp &= 5; #if CS70 - GetStruct().UshortField &= 5; - GetStruct().UshortProp &= 5; + GetRefStruct().UshortField &= 5; + GetRefStruct().UshortProp &= 5; GetRefUshort() &= 5; #endif } @@ -2204,8 +2208,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UshortField |= 5; GetClass().UshortProp |= 5; #if CS70 - GetStruct().UshortField |= 5; - GetStruct().UshortProp |= 5; + GetRefStruct().UshortField |= 5; + GetRefStruct().UshortProp |= 5; GetRefUshort() |= 5; #endif } @@ -2230,8 +2234,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UshortField ^= 5; GetClass().UshortProp ^= 5; #if CS70 - GetStruct().UshortField ^= 5; - GetStruct().UshortProp ^= 5; + GetRefStruct().UshortField ^= 5; + GetRefStruct().UshortProp ^= 5; GetRefUshort() ^= 5; #endif } @@ -2256,8 +2260,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().UshortField++); X(GetClass().UshortProp++); #if CS70 - X(GetStruct().UshortField++); - X(GetStruct().UshortProp++); + X(GetRefStruct().UshortField++); + X(GetRefStruct().UshortProp++); X(GetRefUshort()++); #endif } @@ -2282,8 +2286,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(++GetClass().UshortField); X(++GetClass().UshortProp); #if CS70 - X(++GetStruct().UshortField); - X(++GetStruct().UshortProp); + X(++GetRefStruct().UshortField); + X(++GetRefStruct().UshortProp); X(++GetRefUshort()); #endif } @@ -2307,8 +2311,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().UshortField--); X(GetClass().UshortProp--); #if CS70 - X(GetStruct().UshortField--); - X(GetStruct().UshortProp--); + X(GetRefStruct().UshortField--); + X(GetRefStruct().UshortProp--); X(GetRefUshort()--); #endif } @@ -2333,8 +2337,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(--GetClass().UshortField); X(--GetClass().UshortProp); #if CS70 - X(--GetStruct().UshortField); - X(--GetStruct().UshortProp); + X(--GetRefStruct().UshortField); + X(--GetRefStruct().UshortProp); X(--GetRefUshort()); #endif } @@ -2358,8 +2362,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().IntField += 5; GetClass().IntProp += 5; #if CS70 - GetStruct().IntField += 5; - GetStruct().IntProp += 5; + GetRefStruct().IntField += 5; + GetRefStruct().IntProp += 5; GetRefInt() += 5; #endif } @@ -2384,8 +2388,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().IntField -= 5; GetClass().IntProp -= 5; #if CS70 - GetStruct().IntField -= 5; - GetStruct().IntProp -= 5; + GetRefStruct().IntField -= 5; + GetRefStruct().IntProp -= 5; GetRefInt() -= 5; #endif } @@ -2410,8 +2414,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().IntField *= 5; GetClass().IntProp *= 5; #if CS70 - GetStruct().IntField *= 5; - GetStruct().IntProp *= 5; + GetRefStruct().IntField *= 5; + GetRefStruct().IntProp *= 5; GetRefInt() *= 5; #endif } @@ -2436,8 +2440,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().IntField /= 5; GetClass().IntProp /= 5; #if CS70 - GetStruct().IntField /= 5; - GetStruct().IntProp /= 5; + GetRefStruct().IntField /= 5; + GetRefStruct().IntProp /= 5; GetRefInt() /= 5; #endif } @@ -2462,8 +2466,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().IntField %= 5; GetClass().IntProp %= 5; #if CS70 - GetStruct().IntField %= 5; - GetStruct().IntProp %= 5; + GetRefStruct().IntField %= 5; + GetRefStruct().IntProp %= 5; GetRefInt() %= 5; #endif } @@ -2488,8 +2492,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().IntField <<= 5; GetClass().IntProp <<= 5; #if CS70 - GetStruct().IntField <<= 5; - GetStruct().IntProp <<= 5; + GetRefStruct().IntField <<= 5; + GetRefStruct().IntProp <<= 5; GetRefInt() <<= 5; #endif } @@ -2514,8 +2518,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().IntField >>= 5; GetClass().IntProp >>= 5; #if CS70 - GetStruct().IntField >>= 5; - GetStruct().IntProp >>= 5; + GetRefStruct().IntField >>= 5; + GetRefStruct().IntProp >>= 5; GetRefInt() >>= 5; #endif } @@ -2540,8 +2544,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().IntField &= 5; GetClass().IntProp &= 5; #if CS70 - GetStruct().IntField &= 5; - GetStruct().IntProp &= 5; + GetRefStruct().IntField &= 5; + GetRefStruct().IntProp &= 5; GetRefInt() &= 5; #endif } @@ -2566,8 +2570,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().IntField |= 5; GetClass().IntProp |= 5; #if CS70 - GetStruct().IntField |= 5; - GetStruct().IntProp |= 5; + GetRefStruct().IntField |= 5; + GetRefStruct().IntProp |= 5; GetRefInt() |= 5; #endif } @@ -2592,8 +2596,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().IntField ^= 5; GetClass().IntProp ^= 5; #if CS70 - GetStruct().IntField ^= 5; - GetStruct().IntProp ^= 5; + GetRefStruct().IntField ^= 5; + GetRefStruct().IntProp ^= 5; GetRefInt() ^= 5; #endif } @@ -2618,8 +2622,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().IntField++); X(GetClass().IntProp++); #if CS70 - X(GetStruct().IntField++); - X(GetStruct().IntProp++); + X(GetRefStruct().IntField++); + X(GetRefStruct().IntProp++); X(GetRefInt()++); #endif } @@ -2644,8 +2648,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(++GetClass().IntField); X(++GetClass().IntProp); #if CS70 - X(++GetStruct().IntField); - X(++GetStruct().IntProp); + X(++GetRefStruct().IntField); + X(++GetRefStruct().IntProp); X(++GetRefInt()); #endif } @@ -2669,8 +2673,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().IntField--); X(GetClass().IntProp--); #if CS70 - X(GetStruct().IntField--); - X(GetStruct().IntProp--); + X(GetRefStruct().IntField--); + X(GetRefStruct().IntProp--); X(GetRefInt()--); #endif } @@ -2695,8 +2699,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(--GetClass().IntField); X(--GetClass().IntProp); #if CS70 - X(--GetStruct().IntField); - X(--GetStruct().IntProp); + X(--GetRefStruct().IntField); + X(--GetRefStruct().IntProp); X(--GetRefInt()); #endif } @@ -2720,8 +2724,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UintField += 5u; GetClass().UintProp += 5u; #if CS70 - GetStruct().UintField += 5u; - GetStruct().UintProp += 5u; + GetRefStruct().UintField += 5u; + GetRefStruct().UintProp += 5u; GetRefUint() += 5u; #endif } @@ -2746,8 +2750,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UintField -= 5u; GetClass().UintProp -= 5u; #if CS70 - GetStruct().UintField -= 5u; - GetStruct().UintProp -= 5u; + GetRefStruct().UintField -= 5u; + GetRefStruct().UintProp -= 5u; GetRefUint() -= 5u; #endif } @@ -2772,8 +2776,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UintField *= 5u; GetClass().UintProp *= 5u; #if CS70 - GetStruct().UintField *= 5u; - GetStruct().UintProp *= 5u; + GetRefStruct().UintField *= 5u; + GetRefStruct().UintProp *= 5u; GetRefUint() *= 5u; #endif } @@ -2798,8 +2802,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UintField /= 5u; GetClass().UintProp /= 5u; #if CS70 - GetStruct().UintField /= 5u; - GetStruct().UintProp /= 5u; + GetRefStruct().UintField /= 5u; + GetRefStruct().UintProp /= 5u; GetRefUint() /= 5u; #endif } @@ -2824,8 +2828,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UintField %= 5u; GetClass().UintProp %= 5u; #if CS70 - GetStruct().UintField %= 5u; - GetStruct().UintProp %= 5u; + GetRefStruct().UintField %= 5u; + GetRefStruct().UintProp %= 5u; GetRefUint() %= 5u; #endif } @@ -2850,8 +2854,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UintField <<= 5; GetClass().UintProp <<= 5; #if CS70 - GetStruct().UintField <<= 5; - GetStruct().UintProp <<= 5; + GetRefStruct().UintField <<= 5; + GetRefStruct().UintProp <<= 5; GetRefUint() <<= 5; #endif } @@ -2876,8 +2880,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UintField >>= 5; GetClass().UintProp >>= 5; #if CS70 - GetStruct().UintField >>= 5; - GetStruct().UintProp >>= 5; + GetRefStruct().UintField >>= 5; + GetRefStruct().UintProp >>= 5; GetRefUint() >>= 5; #endif } @@ -2902,8 +2906,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UintField &= 5u; GetClass().UintProp &= 5u; #if CS70 - GetStruct().UintField &= 5u; - GetStruct().UintProp &= 5u; + GetRefStruct().UintField &= 5u; + GetRefStruct().UintProp &= 5u; GetRefUint() &= 5u; #endif } @@ -2928,8 +2932,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UintField |= 5u; GetClass().UintProp |= 5u; #if CS70 - GetStruct().UintField |= 5u; - GetStruct().UintProp |= 5u; + GetRefStruct().UintField |= 5u; + GetRefStruct().UintProp |= 5u; GetRefUint() |= 5u; #endif } @@ -2954,8 +2958,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UintField ^= 5u; GetClass().UintProp ^= 5u; #if CS70 - GetStruct().UintField ^= 5u; - GetStruct().UintProp ^= 5u; + GetRefStruct().UintField ^= 5u; + GetRefStruct().UintProp ^= 5u; GetRefUint() ^= 5u; #endif } @@ -2980,8 +2984,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().UintField++); X(GetClass().UintProp++); #if CS70 - X(GetStruct().UintField++); - X(GetStruct().UintProp++); + X(GetRefStruct().UintField++); + X(GetRefStruct().UintProp++); X(GetRefUint()++); #endif } @@ -3006,8 +3010,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(++GetClass().UintField); X(++GetClass().UintProp); #if CS70 - X(++GetStruct().UintField); - X(++GetStruct().UintProp); + X(++GetRefStruct().UintField); + X(++GetRefStruct().UintProp); X(++GetRefUint()); #endif } @@ -3031,8 +3035,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().UintField--); X(GetClass().UintProp--); #if CS70 - X(GetStruct().UintField--); - X(GetStruct().UintProp--); + X(GetRefStruct().UintField--); + X(GetRefStruct().UintProp--); X(GetRefUint()--); #endif } @@ -3057,8 +3061,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(--GetClass().UintField); X(--GetClass().UintProp); #if CS70 - X(--GetStruct().UintField); - X(--GetStruct().UintProp); + X(--GetRefStruct().UintField); + X(--GetRefStruct().UintProp); X(--GetRefUint()); #endif } @@ -3082,8 +3086,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().LongField += 5L; GetClass().LongProp += 5L; #if CS70 - GetStruct().LongField += 5L; - GetStruct().LongProp += 5L; + GetRefStruct().LongField += 5L; + GetRefStruct().LongProp += 5L; GetRefLong() += 5L; #endif } @@ -3108,8 +3112,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().LongField -= 5L; GetClass().LongProp -= 5L; #if CS70 - GetStruct().LongField -= 5L; - GetStruct().LongProp -= 5L; + GetRefStruct().LongField -= 5L; + GetRefStruct().LongProp -= 5L; GetRefLong() -= 5L; #endif } @@ -3134,8 +3138,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().LongField *= 5L; GetClass().LongProp *= 5L; #if CS70 - GetStruct().LongField *= 5L; - GetStruct().LongProp *= 5L; + GetRefStruct().LongField *= 5L; + GetRefStruct().LongProp *= 5L; GetRefLong() *= 5L; #endif } @@ -3160,8 +3164,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().LongField /= 5L; GetClass().LongProp /= 5L; #if CS70 - GetStruct().LongField /= 5L; - GetStruct().LongProp /= 5L; + GetRefStruct().LongField /= 5L; + GetRefStruct().LongProp /= 5L; GetRefLong() /= 5L; #endif } @@ -3186,8 +3190,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().LongField %= 5L; GetClass().LongProp %= 5L; #if CS70 - GetStruct().LongField %= 5L; - GetStruct().LongProp %= 5L; + GetRefStruct().LongField %= 5L; + GetRefStruct().LongProp %= 5L; GetRefLong() %= 5L; #endif } @@ -3212,8 +3216,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().LongField <<= 5; GetClass().LongProp <<= 5; #if CS70 - GetStruct().LongField <<= 5; - GetStruct().LongProp <<= 5; + GetRefStruct().LongField <<= 5; + GetRefStruct().LongProp <<= 5; GetRefLong() <<= 5; #endif } @@ -3238,8 +3242,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().LongField >>= 5; GetClass().LongProp >>= 5; #if CS70 - GetStruct().LongField >>= 5; - GetStruct().LongProp >>= 5; + GetRefStruct().LongField >>= 5; + GetRefStruct().LongProp >>= 5; GetRefLong() >>= 5; #endif } @@ -3264,8 +3268,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().LongField &= 5L; GetClass().LongProp &= 5L; #if CS70 - GetStruct().LongField &= 5L; - GetStruct().LongProp &= 5L; + GetRefStruct().LongField &= 5L; + GetRefStruct().LongProp &= 5L; GetRefLong() &= 5L; #endif } @@ -3290,8 +3294,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().LongField |= 5L; GetClass().LongProp |= 5L; #if CS70 - GetStruct().LongField |= 5L; - GetStruct().LongProp |= 5L; + GetRefStruct().LongField |= 5L; + GetRefStruct().LongProp |= 5L; GetRefLong() |= 5L; #endif } @@ -3316,8 +3320,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().LongField ^= 5L; GetClass().LongProp ^= 5L; #if CS70 - GetStruct().LongField ^= 5L; - GetStruct().LongProp ^= 5L; + GetRefStruct().LongField ^= 5L; + GetRefStruct().LongProp ^= 5L; GetRefLong() ^= 5L; #endif } @@ -3342,8 +3346,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().LongField++); X(GetClass().LongProp++); #if CS70 - X(GetStruct().LongField++); - X(GetStruct().LongProp++); + X(GetRefStruct().LongField++); + X(GetRefStruct().LongProp++); X(GetRefLong()++); #endif } @@ -3368,8 +3372,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(++GetClass().LongField); X(++GetClass().LongProp); #if CS70 - X(++GetStruct().LongField); - X(++GetStruct().LongProp); + X(++GetRefStruct().LongField); + X(++GetRefStruct().LongProp); X(++GetRefLong()); #endif } @@ -3393,8 +3397,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().LongField--); X(GetClass().LongProp--); #if CS70 - X(GetStruct().LongField--); - X(GetStruct().LongProp--); + X(GetRefStruct().LongField--); + X(GetRefStruct().LongProp--); X(GetRefLong()--); #endif } @@ -3419,8 +3423,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(--GetClass().LongField); X(--GetClass().LongProp); #if CS70 - X(--GetStruct().LongField); - X(--GetStruct().LongProp); + X(--GetRefStruct().LongField); + X(--GetRefStruct().LongProp); X(--GetRefLong()); #endif } @@ -3444,8 +3448,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UlongField += 5uL; GetClass().UlongProp += 5uL; #if CS70 - GetStruct().UlongField += 5uL; - GetStruct().UlongProp += 5uL; + GetRefStruct().UlongField += 5uL; + GetRefStruct().UlongProp += 5uL; GetRefUlong() += 5uL; #endif } @@ -3470,8 +3474,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UlongField -= 5uL; GetClass().UlongProp -= 5uL; #if CS70 - GetStruct().UlongField -= 5uL; - GetStruct().UlongProp -= 5uL; + GetRefStruct().UlongField -= 5uL; + GetRefStruct().UlongProp -= 5uL; GetRefUlong() -= 5uL; #endif } @@ -3496,8 +3500,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UlongField *= 5uL; GetClass().UlongProp *= 5uL; #if CS70 - GetStruct().UlongField *= 5uL; - GetStruct().UlongProp *= 5uL; + GetRefStruct().UlongField *= 5uL; + GetRefStruct().UlongProp *= 5uL; GetRefUlong() *= 5uL; #endif } @@ -3522,8 +3526,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UlongField /= 5uL; GetClass().UlongProp /= 5uL; #if CS70 - GetStruct().UlongField /= 5uL; - GetStruct().UlongProp /= 5uL; + GetRefStruct().UlongField /= 5uL; + GetRefStruct().UlongProp /= 5uL; GetRefUlong() /= 5uL; #endif } @@ -3548,8 +3552,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UlongField %= 5uL; GetClass().UlongProp %= 5uL; #if CS70 - GetStruct().UlongField %= 5uL; - GetStruct().UlongProp %= 5uL; + GetRefStruct().UlongField %= 5uL; + GetRefStruct().UlongProp %= 5uL; GetRefUlong() %= 5uL; #endif } @@ -3574,8 +3578,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UlongField <<= 5; GetClass().UlongProp <<= 5; #if CS70 - GetStruct().UlongField <<= 5; - GetStruct().UlongProp <<= 5; + GetRefStruct().UlongField <<= 5; + GetRefStruct().UlongProp <<= 5; GetRefUlong() <<= 5; #endif } @@ -3600,8 +3604,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UlongField >>= 5; GetClass().UlongProp >>= 5; #if CS70 - GetStruct().UlongField >>= 5; - GetStruct().UlongProp >>= 5; + GetRefStruct().UlongField >>= 5; + GetRefStruct().UlongProp >>= 5; GetRefUlong() >>= 5; #endif } @@ -3626,8 +3630,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UlongField &= 5uL; GetClass().UlongProp &= 5uL; #if CS70 - GetStruct().UlongField &= 5uL; - GetStruct().UlongProp &= 5uL; + GetRefStruct().UlongField &= 5uL; + GetRefStruct().UlongProp &= 5uL; GetRefUlong() &= 5uL; #endif } @@ -3652,8 +3656,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UlongField |= 5uL; GetClass().UlongProp |= 5uL; #if CS70 - GetStruct().UlongField |= 5uL; - GetStruct().UlongProp |= 5uL; + GetRefStruct().UlongField |= 5uL; + GetRefStruct().UlongProp |= 5uL; GetRefUlong() |= 5uL; #endif } @@ -3678,8 +3682,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().UlongField ^= 5uL; GetClass().UlongProp ^= 5uL; #if CS70 - GetStruct().UlongField ^= 5uL; - GetStruct().UlongProp ^= 5uL; + GetRefStruct().UlongField ^= 5uL; + GetRefStruct().UlongProp ^= 5uL; GetRefUlong() ^= 5uL; #endif } @@ -3704,8 +3708,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().UlongField++); X(GetClass().UlongProp++); #if CS70 - X(GetStruct().UlongField++); - X(GetStruct().UlongProp++); + X(GetRefStruct().UlongField++); + X(GetRefStruct().UlongProp++); X(GetRefUlong()++); #endif } @@ -3730,8 +3734,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(++GetClass().UlongField); X(++GetClass().UlongProp); #if CS70 - X(++GetStruct().UlongField); - X(++GetStruct().UlongProp); + X(++GetRefStruct().UlongField); + X(++GetRefStruct().UlongProp); X(++GetRefUlong()); #endif } @@ -3755,8 +3759,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().UlongField--); X(GetClass().UlongProp--); #if CS70 - X(GetStruct().UlongField--); - X(GetStruct().UlongProp--); + X(GetRefStruct().UlongField--); + X(GetRefStruct().UlongProp--); X(GetRefUlong()--); #endif } @@ -3781,8 +3785,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(--GetClass().UlongField); X(--GetClass().UlongProp); #if CS70 - X(--GetStruct().UlongField); - X(--GetStruct().UlongProp); + X(--GetRefStruct().UlongField); + X(--GetRefStruct().UlongProp); X(--GetRefUlong()); #endif } @@ -3806,8 +3810,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomClassField += (CustomClass)null; GetClass().CustomClassProp += (CustomClass)null; #if CS70 - GetStruct().CustomClassField += (CustomClass)null; - GetStruct().CustomClassProp += (CustomClass)null; + GetRefStruct().CustomClassField += (CustomClass)null; + GetRefStruct().CustomClassProp += (CustomClass)null; GetRefCustomClass() += (CustomClass)null; #endif } @@ -3832,8 +3836,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomClassField -= (CustomClass)null; GetClass().CustomClassProp -= (CustomClass)null; #if CS70 - GetStruct().CustomClassField -= (CustomClass)null; - GetStruct().CustomClassProp -= (CustomClass)null; + GetRefStruct().CustomClassField -= (CustomClass)null; + GetRefStruct().CustomClassProp -= (CustomClass)null; GetRefCustomClass() -= (CustomClass)null; #endif } @@ -3858,8 +3862,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomClassField *= (CustomClass)null; GetClass().CustomClassProp *= (CustomClass)null; #if CS70 - GetStruct().CustomClassField *= (CustomClass)null; - GetStruct().CustomClassProp *= (CustomClass)null; + GetRefStruct().CustomClassField *= (CustomClass)null; + GetRefStruct().CustomClassProp *= (CustomClass)null; GetRefCustomClass() *= (CustomClass)null; #endif } @@ -3884,8 +3888,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomClassField /= (CustomClass)null; GetClass().CustomClassProp /= (CustomClass)null; #if CS70 - GetStruct().CustomClassField /= (CustomClass)null; - GetStruct().CustomClassProp /= (CustomClass)null; + GetRefStruct().CustomClassField /= (CustomClass)null; + GetRefStruct().CustomClassProp /= (CustomClass)null; GetRefCustomClass() /= (CustomClass)null; #endif } @@ -3910,8 +3914,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomClassField %= (CustomClass)null; GetClass().CustomClassProp %= (CustomClass)null; #if CS70 - GetStruct().CustomClassField %= (CustomClass)null; - GetStruct().CustomClassProp %= (CustomClass)null; + GetRefStruct().CustomClassField %= (CustomClass)null; + GetRefStruct().CustomClassProp %= (CustomClass)null; GetRefCustomClass() %= (CustomClass)null; #endif } @@ -3936,8 +3940,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomClassField <<= 5; GetClass().CustomClassProp <<= 5; #if CS70 - GetStruct().CustomClassField <<= 5; - GetStruct().CustomClassProp <<= 5; + GetRefStruct().CustomClassField <<= 5; + GetRefStruct().CustomClassProp <<= 5; GetRefCustomClass() <<= 5; #endif } @@ -3962,8 +3966,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomClassField >>= 5; GetClass().CustomClassProp >>= 5; #if CS70 - GetStruct().CustomClassField >>= 5; - GetStruct().CustomClassProp >>= 5; + GetRefStruct().CustomClassField >>= 5; + GetRefStruct().CustomClassProp >>= 5; GetRefCustomClass() >>= 5; #endif } @@ -3988,8 +3992,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomClassField &= (CustomClass)null; GetClass().CustomClassProp &= (CustomClass)null; #if CS70 - GetStruct().CustomClassField &= (CustomClass)null; - GetStruct().CustomClassProp &= (CustomClass)null; + GetRefStruct().CustomClassField &= (CustomClass)null; + GetRefStruct().CustomClassProp &= (CustomClass)null; GetRefCustomClass() &= (CustomClass)null; #endif } @@ -4014,8 +4018,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomClassField |= (CustomClass)null; GetClass().CustomClassProp |= (CustomClass)null; #if CS70 - GetStruct().CustomClassField |= (CustomClass)null; - GetStruct().CustomClassProp |= (CustomClass)null; + GetRefStruct().CustomClassField |= (CustomClass)null; + GetRefStruct().CustomClassProp |= (CustomClass)null; GetRefCustomClass() |= (CustomClass)null; #endif } @@ -4040,8 +4044,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomClassField ^= (CustomClass)null; GetClass().CustomClassProp ^= (CustomClass)null; #if CS70 - GetStruct().CustomClassField ^= (CustomClass)null; - GetStruct().CustomClassProp ^= (CustomClass)null; + GetRefStruct().CustomClassField ^= (CustomClass)null; + GetRefStruct().CustomClassProp ^= (CustomClass)null; GetRefCustomClass() ^= (CustomClass)null; #endif } @@ -4066,8 +4070,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().CustomClassField++); X(GetClass().CustomClassProp++); #if CS70 - X(GetStruct().CustomClassField++); - X(GetStruct().CustomClassProp++); + X(GetRefStruct().CustomClassField++); + X(GetRefStruct().CustomClassProp++); X(GetRefCustomClass()++); #endif } @@ -4092,8 +4096,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(++GetClass().CustomClassField); X(++GetClass().CustomClassProp); #if CS70 - X(++GetStruct().CustomClassField); - X(++GetStruct().CustomClassProp); + X(++GetRefStruct().CustomClassField); + X(++GetRefStruct().CustomClassProp); X(++GetRefCustomClass()); #endif } @@ -4117,8 +4121,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().CustomClassField--); X(GetClass().CustomClassProp--); #if CS70 - X(GetStruct().CustomClassField--); - X(GetStruct().CustomClassProp--); + X(GetRefStruct().CustomClassField--); + X(GetRefStruct().CustomClassProp--); X(GetRefCustomClass()--); #endif } @@ -4143,8 +4147,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(--GetClass().CustomClassField); X(--GetClass().CustomClassProp); #if CS70 - X(--GetStruct().CustomClassField); - X(--GetStruct().CustomClassProp); + X(--GetRefStruct().CustomClassField); + X(--GetRefStruct().CustomClassProp); X(--GetRefCustomClass()); #endif } @@ -4168,8 +4172,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomStructField += default(CustomStruct); GetClass().CustomStructProp += default(CustomStruct); #if CS70 - GetStruct().CustomStructField += default(CustomStruct); - GetStruct().CustomStructProp += default(CustomStruct); + GetRefStruct().CustomStructField += default(CustomStruct); + GetRefStruct().CustomStructProp += default(CustomStruct); GetRefCustomStruct() += default(CustomStruct); #endif } @@ -4194,8 +4198,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomStructField -= default(CustomStruct); GetClass().CustomStructProp -= default(CustomStruct); #if CS70 - GetStruct().CustomStructField -= default(CustomStruct); - GetStruct().CustomStructProp -= default(CustomStruct); + GetRefStruct().CustomStructField -= default(CustomStruct); + GetRefStruct().CustomStructProp -= default(CustomStruct); GetRefCustomStruct() -= default(CustomStruct); #endif } @@ -4220,8 +4224,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomStructField *= default(CustomStruct); GetClass().CustomStructProp *= default(CustomStruct); #if CS70 - GetStruct().CustomStructField *= default(CustomStruct); - GetStruct().CustomStructProp *= default(CustomStruct); + GetRefStruct().CustomStructField *= default(CustomStruct); + GetRefStruct().CustomStructProp *= default(CustomStruct); GetRefCustomStruct() *= default(CustomStruct); #endif } @@ -4246,8 +4250,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomStructField /= default(CustomStruct); GetClass().CustomStructProp /= default(CustomStruct); #if CS70 - GetStruct().CustomStructField /= default(CustomStruct); - GetStruct().CustomStructProp /= default(CustomStruct); + GetRefStruct().CustomStructField /= default(CustomStruct); + GetRefStruct().CustomStructProp /= default(CustomStruct); GetRefCustomStruct() /= default(CustomStruct); #endif } @@ -4272,8 +4276,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomStructField %= default(CustomStruct); GetClass().CustomStructProp %= default(CustomStruct); #if CS70 - GetStruct().CustomStructField %= default(CustomStruct); - GetStruct().CustomStructProp %= default(CustomStruct); + GetRefStruct().CustomStructField %= default(CustomStruct); + GetRefStruct().CustomStructProp %= default(CustomStruct); GetRefCustomStruct() %= default(CustomStruct); #endif } @@ -4298,8 +4302,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomStructField <<= 5; GetClass().CustomStructProp <<= 5; #if CS70 - GetStruct().CustomStructField <<= 5; - GetStruct().CustomStructProp <<= 5; + GetRefStruct().CustomStructField <<= 5; + GetRefStruct().CustomStructProp <<= 5; GetRefCustomStruct() <<= 5; #endif } @@ -4324,8 +4328,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomStructField >>= 5; GetClass().CustomStructProp >>= 5; #if CS70 - GetStruct().CustomStructField >>= 5; - GetStruct().CustomStructProp >>= 5; + GetRefStruct().CustomStructField >>= 5; + GetRefStruct().CustomStructProp >>= 5; GetRefCustomStruct() >>= 5; #endif } @@ -4350,8 +4354,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomStructField &= default(CustomStruct); GetClass().CustomStructProp &= default(CustomStruct); #if CS70 - GetStruct().CustomStructField &= default(CustomStruct); - GetStruct().CustomStructProp &= default(CustomStruct); + GetRefStruct().CustomStructField &= default(CustomStruct); + GetRefStruct().CustomStructProp &= default(CustomStruct); GetRefCustomStruct() &= default(CustomStruct); #endif } @@ -4376,8 +4380,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomStructField |= default(CustomStruct); GetClass().CustomStructProp |= default(CustomStruct); #if CS70 - GetStruct().CustomStructField |= default(CustomStruct); - GetStruct().CustomStructProp |= default(CustomStruct); + GetRefStruct().CustomStructField |= default(CustomStruct); + GetRefStruct().CustomStructProp |= default(CustomStruct); GetRefCustomStruct() |= default(CustomStruct); #endif } @@ -4402,8 +4406,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty GetClass().CustomStructField ^= default(CustomStruct); GetClass().CustomStructProp ^= default(CustomStruct); #if CS70 - GetStruct().CustomStructField ^= default(CustomStruct); - GetStruct().CustomStructProp ^= default(CustomStruct); + GetRefStruct().CustomStructField ^= default(CustomStruct); + GetRefStruct().CustomStructProp ^= default(CustomStruct); GetRefCustomStruct() ^= default(CustomStruct); #endif } @@ -4428,8 +4432,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().CustomStructField++); X(GetClass().CustomStructProp++); #if CS70 - X(GetStruct().CustomStructField++); - X(GetStruct().CustomStructProp++); + X(GetRefStruct().CustomStructField++); + X(GetRefStruct().CustomStructProp++); X(GetRefCustomStruct()++); #endif } @@ -4454,8 +4458,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(++GetClass().CustomStructField); X(++GetClass().CustomStructProp); #if CS70 - X(++GetStruct().CustomStructField); - X(++GetStruct().CustomStructProp); + X(++GetRefStruct().CustomStructField); + X(++GetRefStruct().CustomStructProp); X(++GetRefCustomStruct()); #endif } @@ -4479,8 +4483,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(GetClass().CustomStructField--); X(GetClass().CustomStructProp--); #if CS70 - X(GetStruct().CustomStructField--); - X(GetStruct().CustomStructProp--); + X(GetRefStruct().CustomStructField--); + X(GetRefStruct().CustomStructProp--); X(GetRefCustomStruct()--); #endif } @@ -4505,8 +4509,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty X(--GetClass().CustomStructField); X(--GetClass().CustomStructProp); #if CS70 - X(--GetStruct().CustomStructField); - X(--GetStruct().CustomStructProp); + X(--GetRefStruct().CustomStructField); + X(--GetRefStruct().CustomStructProp); X(--GetRefCustomStruct()); #endif } @@ -4775,5 +4779,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty ++customStruct; } #endif + + public void Issue1779(int value) + { + CustomStruct2 @struct = GetStruct(); + @struct.IntProp += value; + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs index 80164a385..966b26cb5 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs @@ -2,11 +2,12 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Threading.Tasks; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { - public class CustomTaskType + public class ValueTaskType { private int memberField; @@ -125,3 +126,76 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } } + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue1788 +{ + [AsyncMethodBuilder(typeof(builder))] + internal class async + { + public awaiter GetAwaiter() + { + throw null; + } + } + internal class await + { + public awaiter GetAwaiter() + { + throw null; + } + } + + internal class awaiter : INotifyCompletion + { + public bool IsCompleted => true; + public void GetResult() + { + } + public void OnCompleted(Action continuation) + { + } + } + + internal class builder + { + public async Task { + get { + throw null; + } + } + public static builder Create() + { + throw null; + } + public void SetResult() + { + } + public void SetException(Exception e) + { + } + public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine + { + throw null; + } + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine + { + throw null; + } + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine + { + throw null; + } + public void SetStateMachine(IAsyncStateMachine stateMachine) + { + throw null; + } + } + + public class C + { + internal async async @await(@await async) + { + await async; + } + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs index 2409c65a9..86ec9d1cc 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs @@ -385,5 +385,44 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return captured.FirstOrDefault(); }; } + + public static Func Issue1773(short data) + { + int integerData = data; + return () => integerData; + } + +#if !MCS + // does not compile with mcs... + public static Func Issue1773b(object data) + { +#if ROSLYN + dynamic dynamicData = data; + return () => dynamicData.DynamicCall(); +#else + // This is a bug in the old csc: captured dynamic local variables did not have the [DynamicAttribute] + // on the display-class field. + return () => ((dynamic)data).DynamicCall(); +#endif + } + + public static Func Issue1773c(object data) + { +#if ROSLYN + dynamic dynamicData = data; + return () => dynamicData; +#else + return () => (dynamic)data; +#endif + } +#endif + +#if ROSLYN + public static Func Issue1773d((int Integer, string String) data) + { + (int Integer, string RenamedString) valueTuple = data; + return () => valueTuple.RenamedString; + } +#endif } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InterfaceTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InterfaceTests.cs index d26c1fb68..7a7f8799d 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InterfaceTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InterfaceTests.cs @@ -16,9 +16,6 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -// We can't test this because "error CS8701: Target runtime doesn't support default interface implementation." -#undef CS80 - using System; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty diff --git a/ICSharpCode.Decompiler/CSharp/Annotations.cs b/ICSharpCode.Decompiler/CSharp/Annotations.cs index dcd3b23eb..effcfb4cf 100644 --- a/ICSharpCode.Decompiler/CSharp/Annotations.cs +++ b/ICSharpCode.Decompiler/CSharp/Annotations.cs @@ -41,8 +41,8 @@ namespace ICSharpCode.Decompiler.CSharp /// /// Currently unused; we'll probably use the LdToken ILInstruction as annotation instead when LdToken support gets reimplemented. /// - public class LdTokenAnnotation {} - + public class LdTokenAnnotation { } + public static class AnnotationExtensions { internal static ExpressionWithILInstruction WithILInstruction(this Expression expression, ILInstruction instruction) @@ -98,7 +98,7 @@ namespace ICSharpCode.Decompiler.CSharp expression.Expression.AddAnnotation(resolveResult); return new TranslatedExpression(expression, resolveResult); } - + /// /// Retrieves the associated with this AstNode, or null if no symbol is associated with the node. /// @@ -208,7 +208,7 @@ namespace ICSharpCode.Decompiler.CSharp return node; } } - + /// /// Represents a reference to a local variable. /// @@ -223,7 +223,7 @@ namespace ICSharpCode.Decompiler.CSharp public ILVariableResolveResult(ILVariable v, IType type) : base(type) { - this.Variable = v ?? throw new ArgumentNullException("v"); + this.Variable = v ?? throw new ArgumentNullException(nameof(v)); } } diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index bb304f77c..525f3bea8 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -305,6 +305,8 @@ namespace ICSharpCode.Decompiler.CSharp return true; if (settings.AsyncAwait && AsyncAwaitDecompiler.IsCompilerGeneratedStateMachine(typeHandle, metadata)) return true; + if (settings.AsyncEnumerator && AsyncAwaitDecompiler.IsCompilerGeneratorAsyncEnumerator(typeHandle, metadata)) + return true; if (settings.FixedBuffers && name.StartsWith("<", StringComparison.Ordinal) && name.Contains("__FixedBuffer")) return true; } else if (type.IsCompilerGenerated(metadata)) { @@ -1343,7 +1345,11 @@ namespace ICSharpCode.Decompiler.CSharp if (localSettings.DecompileMemberBodies && !body.Descendants.Any(d => d is YieldReturnStatement || d is YieldBreakStatement)) { body.Add(new YieldBreakStatement()); } - RemoveAttribute(entityDecl, KnownAttribute.IteratorStateMachine); + if (function.IsAsync) { + RemoveAttribute(entityDecl, KnownAttribute.AsyncIteratorStateMachine); + } else { + RemoveAttribute(entityDecl, KnownAttribute.IteratorStateMachine); + } if (function.StateMachineCompiledWithMono) { RemoveAttribute(entityDecl, KnownAttribute.DebuggerHidden); } diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 96ec8949e..3a79c3504 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -989,6 +989,10 @@ namespace ICSharpCode.Decompiler.CSharp foundMember = null; bestCandidateIsExpandedForm = false; var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule); + + Log.WriteLine("IsUnambiguousCall: Performing overload resolution for " + method); + Log.WriteCollection(" Arguments: ", arguments.Select(a => a.ResolveResult)); + var or = new OverloadResolution(resolver.Compilation, firstOptionalArgumentIndex < 0 ? arguments.SelectArray(a => a.ResolveResult) : arguments.Take(firstOptionalArgumentIndex).Select(a => a.ResolveResult).ToArray(), argumentNames: firstOptionalArgumentIndex < 0 || argumentNames == null ? argumentNames : argumentNames.Take(firstOptionalArgumentIndex).ToArray(), @@ -1043,6 +1047,9 @@ namespace ICSharpCode.Decompiler.CSharp bool IsUnambiguousAccess(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method, IList arguments, string[] argumentNames, out IMember foundMember) { + Log.WriteLine("IsUnambiguousAccess: Performing overload resolution for " + method); + Log.WriteCollection(" Arguments: ", arguments.Select(a => a.ResolveResult)); + foundMember = null; if (target == null) { var result = resolver.ResolveSimpleName(method.AccessorOwner.Name, @@ -1404,6 +1411,8 @@ namespace ICSharpCode.Decompiler.CSharp bool IsUnambiguousMethodReference(ExpectedTargetDetails expectedTargetDetails, IMethod method, ResolveResult target, IReadOnlyList typeArguments, out ResolveResult result) { + Log.WriteLine("IsUnambiguousMethodReference: Performing overload resolution for " + method); + var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule); var or = new OverloadResolution(resolver.Compilation, arguments: method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type)), // there are no arguments, use parameter types diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index c9cd50eab..0c9c6f28f 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -67,6 +67,7 @@ namespace ICSharpCode.Decompiler.CSharp /// sealed class ExpressionBuilder : ILVisitor { + readonly StatementBuilder statementBuilder; readonly IDecompilerTypeSystem typeSystem; internal readonly ITypeResolveContext decompilationContext; internal readonly ILFunction currentFunction; @@ -77,9 +78,10 @@ namespace ICSharpCode.Decompiler.CSharp internal readonly DecompilerSettings settings; readonly CancellationToken cancellationToken; - public ExpressionBuilder(IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext, ILFunction currentFunction, DecompilerSettings settings, CancellationToken cancellationToken) + public ExpressionBuilder(StatementBuilder statementBuilder, IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext, ILFunction currentFunction, DecompilerSettings settings, CancellationToken cancellationToken) { Debug.Assert(decompilationContext != null); + this.statementBuilder = statementBuilder; this.typeSystem = typeSystem; this.decompilationContext = decompilationContext; this.currentFunction = currentFunction; @@ -369,7 +371,7 @@ namespace ICSharpCode.Decompiler.CSharp } expr.Arguments.AddRange(args.Select(arg => arg.Expression)); return expr.WithILInstruction(inst) - .WithRR(new ArrayCreateResolveResult(new ArrayType(compilation, inst.Type, dimensions), args.Select(a => a.ResolveResult).ToList(), new ResolveResult[0])); + .WithRR(new ArrayCreateResolveResult(new ArrayType(compilation, inst.Type, dimensions), args.Select(a => a.ResolveResult).ToList(), Empty.Array)); } protected internal override TranslatedExpression VisitLocAlloc(LocAlloc inst, TranslationContext context) @@ -1940,6 +1942,31 @@ namespace ICSharpCode.Decompiler.CSharp } } + protected internal override TranslatedExpression VisitBlockContainer(BlockContainer container, TranslationContext context) + { + var oldReturnContainer = statementBuilder.currentReturnContainer; + var oldResultType = statementBuilder.currentResultType; + var oldIsIterator = statementBuilder.currentIsIterator; + + statementBuilder.currentReturnContainer = container; + statementBuilder.currentResultType = context.TypeHint; + statementBuilder.currentIsIterator = false; + try { + var body = statementBuilder.ConvertAsBlock(container); + body.InsertChildAfter(null, new Comment(" Could not convert BlockContainer to single expression"), Roles.Comment); + var ame = new AnonymousMethodExpression { Body = body }; + var delegateType = new ParameterizedType(compilation.FindType(typeof(Func<>)), InferReturnType(body)); + var invocationTarget = new CastExpression(ConvertType(delegateType), ame); + return new InvocationExpression(new MemberReferenceExpression(invocationTarget, "Invoke")) + .WithILInstruction(container) + .WithRR(new CSharpInvocationResolveResult(new ResolveResult(delegateType), delegateType.GetDelegateInvokeMethod(), EmptyList.Instance)); + } finally { + statementBuilder.currentReturnContainer = oldReturnContainer; + statementBuilder.currentResultType = oldResultType; + statementBuilder.currentIsIterator = oldIsIterator; + } + } + internal TranslatedExpression TranslateTarget(ILInstruction target, bool nonVirtualInvocation, bool memberStatic, IType memberDeclaringType) { diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs index e99aa7683..2e46c429b 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs @@ -33,34 +33,34 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor public class CSharpAmbience : IAmbience { public ConversionFlags ConversionFlags { get; set; } - + #region ConvertSymbol public string ConvertSymbol(ISymbol symbol) { if (symbol == null) - throw new ArgumentNullException("symbol"); - + throw new ArgumentNullException(nameof(symbol)); + StringWriter writer = new StringWriter(); ConvertSymbol(symbol, new TextWriterTokenWriter(writer), FormattingOptionsFactory.CreateEmpty()); return writer.ToString(); } - + public void ConvertSymbol(ISymbol symbol, TokenWriter writer, CSharpFormattingOptions formattingPolicy) { if (symbol == null) - throw new ArgumentNullException("symbol"); + throw new ArgumentNullException(nameof(symbol)); if (writer == null) - throw new ArgumentNullException("writer"); + throw new ArgumentNullException(nameof(writer)); if (formattingPolicy == null) - throw new ArgumentNullException("formattingPolicy"); - + throw new ArgumentNullException(nameof(formattingPolicy)); + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); AstNode node = astBuilder.ConvertSymbol(symbol); writer.StartNode(node); EntityDeclaration entityDecl = node as EntityDeclaration; if (entityDecl != null) PrintModifiers(entityDecl.Modifiers, writer); - + if ((ConversionFlags & ConversionFlags.ShowDefinitionKeyword) == ConversionFlags.ShowDefinitionKeyword) { if (node is TypeDeclaration) { switch (((TypeDeclaration)node).ClassType) { @@ -91,24 +91,23 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor writer.Space(); } } - + if ((ConversionFlags & ConversionFlags.PlaceReturnTypeAfterParameterList) != ConversionFlags.PlaceReturnTypeAfterParameterList - && (ConversionFlags & ConversionFlags.ShowReturnType) == ConversionFlags.ShowReturnType) - { + && (ConversionFlags & ConversionFlags.ShowReturnType) == ConversionFlags.ShowReturnType) { var rt = node.GetChildByRole(Roles.Type); if (!rt.IsNull) { rt.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); writer.Space(); } } - + if (symbol is ITypeDefinition) WriteTypeDeclarationName((ITypeDefinition)symbol, writer, formattingPolicy); else if (symbol is IMember) WriteMemberDeclarationName((IMember)symbol, writer, formattingPolicy); else writer.WriteIdentifier(Identifier.Create(symbol.Name)); - + if ((ConversionFlags & ConversionFlags.ShowParameterList) == ConversionFlags.ShowParameterList && HasParameters(symbol)) { writer.WriteToken(symbol.SymbolKind == SymbolKind.Indexer ? Roles.LBracket : Roles.LPar, symbol.SymbolKind == SymbolKind.Indexer ? "[" : "("); bool first = true; @@ -131,8 +130,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } if ((ConversionFlags & ConversionFlags.PlaceReturnTypeAfterParameterList) == ConversionFlags.PlaceReturnTypeAfterParameterList - && (ConversionFlags & ConversionFlags.ShowReturnType) == ConversionFlags.ShowReturnType) - { + && (ConversionFlags & ConversionFlags.ShowReturnType) == ConversionFlags.ShowReturnType) { var rt = node.GetChildByRole(Roles.Type); if (!rt.IsNull) { writer.Space(); @@ -171,7 +169,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor writer.EndNode(node); } } - + static bool HasParameters(ISymbol e) { switch (e.SymbolKind) { @@ -187,7 +185,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor return false; } } - + TypeSystemAstBuilder CreateAstBuilder() { TypeSystemAstBuilder astBuilder = new TypeSystemAstBuilder(); @@ -199,7 +197,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor astBuilder.ShowParameterNames = (ConversionFlags & ConversionFlags.ShowParameterNames) == ConversionFlags.ShowParameterNames; return astBuilder; } - + void WriteTypeDeclarationName(ITypeDefinition typeDef, TokenWriter writer, CSharpFormattingOptions formattingPolicy) { TypeSystemAstBuilder astBuilder = CreateAstBuilder(); @@ -299,7 +297,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } } } - + void WriteQualifiedName(string name, TokenWriter writer, CSharpFormattingOptions formattingPolicy) { var node = AstType.Create(name); @@ -307,25 +305,25 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor node.AcceptVisitor(outputVisitor); } #endregion - + public string ConvertVariable(IVariable v) { TypeSystemAstBuilder astBuilder = CreateAstBuilder(); AstNode astNode = astBuilder.ConvertVariable(v); return astNode.ToString().TrimEnd(';', '\r', '\n', (char)8232); } - + public string ConvertType(IType type) { if (type == null) - throw new ArgumentNullException("type"); - + throw new ArgumentNullException(nameof(type)); + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); astBuilder.AlwaysUseShortTypeNames = (ConversionFlags & ConversionFlags.UseFullyQualifiedEntityNames) != ConversionFlags.UseFullyQualifiedEntityNames; AstType astType = astBuilder.ConvertType(type); return astType.ToString(); } - + public void ConvertType(IType type, TokenWriter writer, CSharpFormattingOptions formattingPolicy) { TypeSystemAstBuilder astBuilder = CreateAstBuilder(); @@ -333,12 +331,12 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor AstType astType = astBuilder.ConvertType(type); astType.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); } - + public string ConvertConstantValue(object constantValue) { return TextWriterTokenWriter.PrintPrimitiveValue(constantValue); } - + public string WrapComment(string comment) { return "// " + comment; diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index 618f0d5af..5e155fb6e 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -36,32 +36,32 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { readonly protected TokenWriter writer; readonly protected CSharpFormattingOptions policy; - readonly protected Stack containerStack = new Stack (); - - public CSharpOutputVisitor (TextWriter textWriter, CSharpFormattingOptions formattingPolicy) + readonly protected Stack containerStack = new Stack(); + + public CSharpOutputVisitor(TextWriter textWriter, CSharpFormattingOptions formattingPolicy) { if (textWriter == null) { - throw new ArgumentNullException ("textWriter"); + throw new ArgumentNullException(nameof(textWriter)); } if (formattingPolicy == null) { - throw new ArgumentNullException ("formattingPolicy"); + throw new ArgumentNullException(nameof(formattingPolicy)); } this.writer = TokenWriter.Create(textWriter, formattingPolicy.IndentationString); this.policy = formattingPolicy; } - - public CSharpOutputVisitor (TokenWriter writer, CSharpFormattingOptions formattingPolicy) + + public CSharpOutputVisitor(TokenWriter writer, CSharpFormattingOptions formattingPolicy) { if (writer == null) { - throw new ArgumentNullException ("writer"); + throw new ArgumentNullException(nameof(writer)); } if (formattingPolicy == null) { - throw new ArgumentNullException ("formattingPolicy"); + throw new ArgumentNullException(nameof(formattingPolicy)); } this.writer = new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(writer)); this.policy = formattingPolicy; } - + #region StartNode/EndNode protected virtual void StartNode(AstNode node) { @@ -71,7 +71,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor containerStack.Push(node); writer.StartNode(node); } - + protected virtual void EndNode(AstNode node) { Debug.Assert(node == containerStack.Peek()); @@ -79,7 +79,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor writer.EndNode(node); } #endregion - + #region Comma /// /// Writes a comma. @@ -95,7 +95,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Space(!noSpaceAfterComma && policy.SpaceAfterBracketComma); // TODO: Comma policy has changed. } - + /// /// Writes an optional comma, e.g. at the end of an enum declaration or in an array initializer /// @@ -109,7 +109,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Comma(null, noSpaceAfterComma: true); } } - + /// /// Writes an optional semicolon, e.g. at the end of a type or namespace declaration. /// @@ -123,7 +123,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Semicolon(); } } - + protected virtual void WriteCommaSeparatedList(IEnumerable list) { bool isFirst = true; @@ -136,7 +136,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor node.AcceptVisitor(this); } } - + protected virtual void WriteCommaSeparatedListInParenthesis(IEnumerable list, bool spaceWithin) { LPar(); @@ -147,8 +147,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } RPar(); } - - #if DOTNET35 + +#if DOTNET35 void WriteCommaSeparatedList(IEnumerable list) { WriteCommaSeparatedList(list.SafeCast()); @@ -169,7 +169,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteCommaSeparatedListInParenthesis(list.SafeCast(), spaceWithin); } - #endif +#endif protected virtual void WriteCommaSeparatedListInBrackets(IEnumerable list, bool spaceWithin) { @@ -193,11 +193,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteToken(Roles.RBracket); } #endregion - + #region Write tokens protected bool isAtStartOfLine = true; protected bool isAfterSpace; - + /// /// Writes a keyword, and all specials up to /// @@ -205,50 +205,50 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { WriteKeyword(tokenRole.Token, tokenRole); } - + protected virtual void WriteKeyword(string token, Role tokenRole = null) { writer.WriteKeyword(tokenRole, token); isAtStartOfLine = false; isAfterSpace = false; } - + protected virtual void WriteIdentifier(Identifier identifier) { writer.WriteIdentifier(identifier); isAtStartOfLine = false; isAfterSpace = false; } - + protected virtual void WriteIdentifier(string identifier) { AstType.Create(identifier).AcceptVisitor(this); isAtStartOfLine = false; isAfterSpace = false; } - + protected virtual void WriteToken(TokenRole tokenRole) { WriteToken(tokenRole.Token, tokenRole); } - + protected virtual void WriteToken(string token, Role tokenRole) { writer.WriteToken(tokenRole, token); isAtStartOfLine = false; isAfterSpace = false; } - + protected virtual void LPar() { WriteToken(Roles.LPar); } - + protected virtual void RPar() { WriteToken(Roles.RPar); } - + /// /// Marks the end of a statement /// @@ -261,7 +261,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor NewLine(); } } - + /// /// Writes a space depending on policy. /// @@ -272,7 +272,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor isAfterSpace = true; } } - + protected virtual void NewLine() { writer.NewLine(); @@ -301,12 +301,12 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor if (callChainLength == 3) writer.Indent(); writer.NewLine(); - + isAtStartOfLine = true; isAfterSpace = false; return true; } - + protected virtual void OpenBrace(BraceStyle style) { switch (style) { @@ -337,12 +337,12 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor writer.WriteToken(Roles.LBrace, "{"); break; default: - throw new ArgumentOutOfRangeException (); + throw new ArgumentOutOfRangeException(); } writer.Indent(); NewLine(); } - + protected virtual void CloseBrace(BraceStyle style) { switch (style) { @@ -372,7 +372,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } #endregion - + #region IsKeyword Test static readonly HashSet unconditionalKeywords = new HashSet { "abstract", "as", "base", "bool", "break", "byte", "case", "catch", @@ -424,7 +424,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor return false; } #endregion - + #region Write constructs protected virtual void WriteTypeArguments(IEnumerable typeArguments) { @@ -434,7 +434,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteToken(Roles.RChevron); } } - + public virtual void WriteTypeParameters(IEnumerable typeParameters) { if (typeParameters.Any()) { @@ -443,7 +443,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteToken(Roles.RChevron); } } - + protected virtual void WriteModifiers(IEnumerable modifierTokens) { foreach (CSharpModifierToken modifier in modifierTokens) { @@ -451,7 +451,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Space(); } } - + protected virtual void WriteQualifiedIdentifier(IEnumerable identifiers) { bool first = true; @@ -496,7 +496,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor writer.Unindent(); } } - + protected virtual void WriteMethodBody(BlockStatement body, BraceStyle style) { if (body.IsNull) { @@ -506,14 +506,14 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor NewLine(); } } - + protected virtual void WriteAttributes(IEnumerable attributes) { foreach (AttributeSection attr in attributes) { attr.AcceptVisitor(this); } } - + protected virtual void WritePrivateImplementationType(AstType privateImplementationType) { if (!privateImplementationType.IsNull) { @@ -523,7 +523,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } #endregion - + #region Expressions public virtual void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) { @@ -540,7 +540,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteBlock(anonymousMethodExpression.Body, policy.AnonymousMethodBraceStyle); EndNode(anonymousMethodExpression); } - + public virtual void VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression) { StartNode(undocumentedExpression); @@ -565,7 +565,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } EndNode(undocumentedExpression); } - + public virtual void VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression) { StartNode(arrayCreateExpression); @@ -580,7 +580,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor arrayCreateExpression.Initializer.AcceptVisitor(this); EndNode(arrayCreateExpression); } - + public virtual void VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression) { StartNode(arrayInitializerExpression); @@ -599,7 +599,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } EndNode(arrayInitializerExpression); } - + protected bool CanBeConfusedWithObjectInitializer(Expression expr) { // "int a; new List { a = 1 };" is an object initalizers and invalid, but @@ -607,7 +607,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor AssignmentExpression ae = expr as AssignmentExpression; return ae != null && ae.Operator == AssignmentOperatorType.Assign; } - + protected bool IsObjectOrCollectionInitializer(AstNode node) { if (!(node is ArrayInitializerExpression)) { @@ -621,7 +621,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } return false; } - + protected virtual void PrintInitializerElements(AstNodeCollection elements) { BraceStyle style; @@ -648,7 +648,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor NewLine(); CloseBrace(style); } - + public virtual void VisitAsExpression(AsExpression asExpression) { StartNode(asExpression); @@ -659,7 +659,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor asExpression.Type.AcceptVisitor(this); EndNode(asExpression); } - + public virtual void VisitAssignmentExpression(AssignmentExpression assignmentExpression) { StartNode(assignmentExpression); @@ -670,14 +670,14 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor assignmentExpression.Right.AcceptVisitor(this); EndNode(assignmentExpression); } - + public virtual void VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression) { StartNode(baseReferenceExpression); WriteKeyword("base", baseReferenceExpression.Role); EndNode(baseReferenceExpression); } - + public virtual void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) { StartNode(binaryOperatorExpression); @@ -720,7 +720,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor spacePolicy = true; break; default: - throw new NotSupportedException ("Invalid value for BinaryOperatorType"); + throw new NotSupportedException("Invalid value for BinaryOperatorType"); } Space(spacePolicy); WriteToken(BinaryOperatorExpression.GetOperatorRole(binaryOperatorExpression.Operator)); @@ -728,7 +728,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor binaryOperatorExpression.Right.AcceptVisitor(this); EndNode(binaryOperatorExpression); } - + public virtual void VisitCastExpression(CastExpression castExpression) { StartNode(castExpression); @@ -741,7 +741,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor castExpression.Expression.AcceptVisitor(this); EndNode(castExpression); } - + public virtual void VisitCheckedExpression(CheckedExpression checkedExpression) { StartNode(checkedExpression); @@ -753,45 +753,45 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor RPar(); EndNode(checkedExpression); } - + public virtual void VisitConditionalExpression(ConditionalExpression conditionalExpression) { StartNode(conditionalExpression); conditionalExpression.Condition.AcceptVisitor(this); - + Space(policy.SpaceBeforeConditionalOperatorCondition); WriteToken(ConditionalExpression.QuestionMarkRole); Space(policy.SpaceAfterConditionalOperatorCondition); - + conditionalExpression.TrueExpression.AcceptVisitor(this); - + Space(policy.SpaceBeforeConditionalOperatorSeparator); WriteToken(ConditionalExpression.ColonRole); Space(policy.SpaceAfterConditionalOperatorSeparator); - + conditionalExpression.FalseExpression.AcceptVisitor(this); - + EndNode(conditionalExpression); } - + public virtual void VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression) { StartNode(defaultValueExpression); - + WriteKeyword(DefaultValueExpression.DefaultKeywordRole); LPar(); Space(policy.SpacesWithinTypeOfParentheses); defaultValueExpression.Type.AcceptVisitor(this); Space(policy.SpacesWithinTypeOfParentheses); RPar(); - + EndNode(defaultValueExpression); } - + public virtual void VisitDirectionExpression(DirectionExpression directionExpression) { StartNode(directionExpression); - + switch (directionExpression.FieldDirection) { case FieldDirection.Out: WriteKeyword(DirectionExpression.OutKeywordRole); @@ -803,11 +803,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteKeyword(DirectionExpression.InKeywordRole); break; default: - throw new NotSupportedException ("Invalid value for FieldDirection"); + throw new NotSupportedException("Invalid value for FieldDirection"); } Space(); directionExpression.Expression.AcceptVisitor(this); - + EndNode(directionExpression); } @@ -823,7 +823,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor EndNode(outVarDeclarationExpression); } - + public virtual void VisitIdentifierExpression(IdentifierExpression identifierExpression) { StartNode(identifierExpression); @@ -831,7 +831,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteTypeArguments(identifierExpression.TypeArguments); EndNode(identifierExpression); } - + public virtual void VisitIndexerExpression(IndexerExpression indexerExpression) { StartNode(indexerExpression); @@ -840,7 +840,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteCommaSeparatedListInBrackets(indexerExpression.Arguments); EndNode(indexerExpression); } - + public virtual void VisitInvocationExpression(InvocationExpression invocationExpression) { StartNode(invocationExpression); @@ -855,7 +855,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } EndNode(invocationExpression); } - + public virtual void VisitIsExpression(IsExpression isExpression) { StartNode(isExpression); @@ -865,7 +865,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor isExpression.Type.AcceptVisitor(this); EndNode(isExpression); } - + public virtual void VisitLambdaExpression(LambdaExpression lambdaExpression) { StartNode(lambdaExpression); @@ -888,7 +888,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } EndNode(lambdaExpression); } - + protected bool LambdaNeedsParenthesis(LambdaExpression lambdaExpression) { if (lambdaExpression.Parameters.Count != 1) { @@ -897,7 +897,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor var p = lambdaExpression.Parameters.Single(); return !(p.Type.IsNull && p.ParameterModifier == ParameterModifier.None); } - + public virtual void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) { StartNode(memberReferenceExpression); @@ -911,7 +911,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } EndNode(memberReferenceExpression); } - + public virtual void VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression) { StartNode(namedArgumentExpression); @@ -921,7 +921,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor namedArgumentExpression.Expression.AcceptVisitor(this); EndNode(namedArgumentExpression); } - + public virtual void VisitNamedExpression(NamedExpression namedExpression) { StartNode(namedExpression); @@ -932,7 +932,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor namedExpression.Expression.AcceptVisitor(this); EndNode(namedExpression); } - + public virtual void VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression) { StartNode(nullReferenceExpression); @@ -940,7 +940,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor isAfterSpace = false; EndNode(nullReferenceExpression); } - + public virtual void VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) { StartNode(objectCreateExpression); @@ -958,7 +958,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor objectCreateExpression.Initializer.AcceptVisitor(this); EndNode(objectCreateExpression); } - + public virtual void VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) { StartNode(anonymousTypeCreateExpression); @@ -977,7 +977,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor RPar(); EndNode(parenthesizedExpression); } - + public virtual void VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) { StartNode(pointerReferenceExpression); @@ -987,7 +987,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteTypeArguments(pointerReferenceExpression.TypeArguments); EndNode(pointerReferenceExpression); } - + #region VisitPrimitiveExpression public virtual void VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) { @@ -1037,17 +1037,17 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor public virtual void VisitSizeOfExpression(SizeOfExpression sizeOfExpression) { StartNode(sizeOfExpression); - + WriteKeyword(SizeOfExpression.SizeofKeywordRole); LPar(); Space(policy.SpacesWithinSizeOfParentheses); sizeOfExpression.Type.AcceptVisitor(this); Space(policy.SpacesWithinSizeOfParentheses); RPar(); - + EndNode(sizeOfExpression); } - + public virtual void VisitStackAllocExpression(StackAllocExpression stackAllocExpression) { StartNode(stackAllocExpression); @@ -1057,7 +1057,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor stackAllocExpression.Initializer.AcceptVisitor(this); EndNode(stackAllocExpression); } - + public virtual void VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) { StartNode(thisReferenceExpression); @@ -1087,24 +1087,24 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor public virtual void VisitTypeOfExpression(TypeOfExpression typeOfExpression) { StartNode(typeOfExpression); - + WriteKeyword(TypeOfExpression.TypeofKeywordRole); LPar(); Space(policy.SpacesWithinTypeOfParentheses); typeOfExpression.Type.AcceptVisitor(this); Space(policy.SpacesWithinTypeOfParentheses); RPar(); - + EndNode(typeOfExpression); } - + public virtual void VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) { StartNode(typeReferenceExpression); typeReferenceExpression.Type.AcceptVisitor(this); EndNode(typeReferenceExpression); } - + public virtual void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) { StartNode(unaryOperatorExpression); @@ -1112,6 +1112,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor var opSymbol = UnaryOperatorExpression.GetOperatorRole(opType); if (opType == UnaryOperatorType.Await) { WriteKeyword(opSymbol); + Space(); } else if (!IsPostfixOperator(opType) && opSymbol != null) { WriteToken(opSymbol); } @@ -1121,7 +1122,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } EndNode(unaryOperatorExpression); } - + static bool IsPostfixOperator(UnaryOperatorType op) { return op == UnaryOperatorType.PostIncrement @@ -1143,7 +1144,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } #endregion - + #region Query Expressions public virtual void VisitQueryExpression(QueryExpression queryExpression) { @@ -1165,7 +1166,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor writer.Unindent(); EndNode(queryExpression); } - + public virtual void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) { StartNode(queryContinuationClause); @@ -1176,7 +1177,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteIdentifier(queryContinuationClause.IdentifierToken); EndNode(queryContinuationClause); } - + public virtual void VisitQueryFromClause(QueryFromClause queryFromClause) { StartNode(queryFromClause); @@ -1190,7 +1191,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor queryFromClause.Expression.AcceptVisitor(this); EndNode(queryFromClause); } - + public virtual void VisitQueryLetClause(QueryLetClause queryLetClause) { StartNode(queryLetClause); @@ -1203,7 +1204,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor queryLetClause.Expression.AcceptVisitor(this); EndNode(queryLetClause); } - + public virtual void VisitQueryWhereClause(QueryWhereClause queryWhereClause) { StartNode(queryWhereClause); @@ -1212,7 +1213,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor queryWhereClause.Condition.AcceptVisitor(this); EndNode(queryWhereClause); } - + public virtual void VisitQueryJoinClause(QueryJoinClause queryJoinClause) { StartNode(queryJoinClause); @@ -1239,7 +1240,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } EndNode(queryJoinClause); } - + public virtual void VisitQueryOrderClause(QueryOrderClause queryOrderClause) { StartNode(queryOrderClause); @@ -1248,7 +1249,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteCommaSeparatedList(queryOrderClause.Orderings); EndNode(queryOrderClause); } - + public virtual void VisitQueryOrdering(QueryOrdering queryOrdering) { StartNode(queryOrdering); @@ -1265,7 +1266,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } EndNode(queryOrdering); } - + public virtual void VisitQuerySelectClause(QuerySelectClause querySelectClause) { StartNode(querySelectClause); @@ -1274,7 +1275,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor querySelectClause.Expression.AcceptVisitor(this); EndNode(querySelectClause); } - + public virtual void VisitQueryGroupClause(QueryGroupClause queryGroupClause) { StartNode(queryGroupClause); @@ -1289,7 +1290,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } #endregion - + #region GeneralScope public virtual void VisitAttribute(Attribute attribute) { @@ -1301,7 +1302,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } EndNode(attribute); } - + public virtual void VisitAttributeSection(AttributeSection attributeSection) { StartNode(attributeSection); @@ -1325,7 +1326,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } EndNode(attributeSection); } - + public virtual void VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) { StartNode(delegateDeclaration); @@ -1344,12 +1345,12 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Semicolon(); EndNode(delegateDeclaration); } - + public virtual void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) { StartNode(namespaceDeclaration); WriteKeyword(Roles.NamespaceKeyword); - namespaceDeclaration.NamespaceName.AcceptVisitor (this); + namespaceDeclaration.NamespaceName.AcceptVisitor(this); OpenBrace(policy.NamespaceBraceStyle); foreach (var member in namespaceDeclaration.Members) { member.AcceptVisitor(this); @@ -1360,7 +1361,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor NewLine(); EndNode(namespaceDeclaration); } - + public virtual void VisitTypeDeclaration(TypeDeclaration typeDeclaration) { StartNode(typeDeclaration); @@ -1429,7 +1430,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor NewLine(); EndNode(typeDeclaration); } - + public virtual void VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration) { StartNode(usingAliasDeclaration); @@ -1442,7 +1443,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Semicolon(); EndNode(usingAliasDeclaration); } - + public virtual void VisitUsingDeclaration(UsingDeclaration usingDeclaration) { StartNode(usingDeclaration); @@ -1451,7 +1452,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Semicolon(); EndNode(usingDeclaration); } - + public virtual void VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) { StartNode(externAliasDeclaration); @@ -1489,7 +1490,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor CloseBrace(style); EndNode(blockStatement); } - + public virtual void VisitBreakStatement(BreakStatement breakStatement) { StartNode(breakStatement); @@ -1497,7 +1498,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Semicolon(); EndNode(breakStatement); } - + public virtual void VisitCheckedStatement(CheckedStatement checkedStatement) { StartNode(checkedStatement); @@ -1505,7 +1506,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor checkedStatement.Body.AcceptVisitor(this); EndNode(checkedStatement); } - + public virtual void VisitContinueStatement(ContinueStatement continueStatement) { StartNode(continueStatement); @@ -1513,7 +1514,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Semicolon(); EndNode(continueStatement); } - + public virtual void VisitDoWhileStatement(DoWhileStatement doWhileStatement) { StartNode(doWhileStatement); @@ -1529,14 +1530,14 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Semicolon(); EndNode(doWhileStatement); } - + public virtual void VisitEmptyStatement(EmptyStatement emptyStatement) { StartNode(emptyStatement); Semicolon(); EndNode(emptyStatement); } - + public virtual void VisitExpressionStatement(ExpressionStatement expressionStatement) { StartNode(expressionStatement); @@ -1544,7 +1545,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Semicolon(); EndNode(expressionStatement); } - + public virtual void VisitFixedStatement(FixedStatement fixedStatement) { StartNode(fixedStatement); @@ -1560,7 +1561,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteEmbeddedStatement(fixedStatement.EmbeddedStatement); EndNode(fixedStatement); } - + public virtual void VisitForeachStatement(ForeachStatement foreachStatement) { StartNode(foreachStatement); @@ -1579,7 +1580,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteEmbeddedStatement(foreachStatement.EmbeddedStatement); EndNode(foreachStatement); } - + public virtual void VisitForStatement(ForStatement forStatement) { StartNode(forStatement); @@ -1587,12 +1588,12 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Space(policy.SpaceBeforeForParentheses); LPar(); Space(policy.SpacesWithinForParentheses); - + WriteCommaSeparatedList(forStatement.Initializers); Space(policy.SpaceBeforeForSemicolon); WriteToken(Roles.Semicolon); Space(policy.SpaceAfterForSemicolon); - + forStatement.Condition.AcceptVisitor(this); Space(policy.SpaceBeforeForSemicolon); WriteToken(Roles.Semicolon); @@ -1600,13 +1601,13 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Space(policy.SpaceAfterForSemicolon); WriteCommaSeparatedList(forStatement.Iterators); } - + Space(policy.SpacesWithinForParentheses); RPar(); WriteEmbeddedStatement(forStatement.EmbeddedStatement); EndNode(forStatement); } - + public virtual void VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement) { StartNode(gotoCaseStatement); @@ -1617,7 +1618,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Semicolon(); EndNode(gotoCaseStatement); } - + public virtual void VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement) { StartNode(gotoDefaultStatement); @@ -1626,7 +1627,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Semicolon(); EndNode(gotoDefaultStatement); } - + public virtual void VisitGotoStatement(GotoStatement gotoStatement) { StartNode(gotoStatement); @@ -1635,7 +1636,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Semicolon(); EndNode(gotoStatement); } - + public virtual void VisitIfElseStatement(IfElseStatement ifElseStatement) { StartNode(ifElseStatement); @@ -1646,7 +1647,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor ifElseStatement.Condition.AcceptVisitor(this); Space(policy.SpacesWithinIfParentheses); RPar(); - + if (ifElseStatement.FalseStatement.IsNull) { WriteEmbeddedStatement(ifElseStatement.TrueStatement); } else { @@ -1661,7 +1662,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } EndNode(ifElseStatement); } - + public virtual void VisitLabelStatement(LabelStatement labelStatement) { StartNode(labelStatement); @@ -1680,7 +1681,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor NewLine(); EndNode(labelStatement); } - + public virtual void VisitLockStatement(LockStatement lockStatement) { StartNode(lockStatement); @@ -1694,7 +1695,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteEmbeddedStatement(lockStatement.EmbeddedStatement); EndNode(lockStatement); } - + public virtual void VisitReturnStatement(ReturnStatement returnStatement) { StartNode(returnStatement); @@ -1706,7 +1707,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Semicolon(); EndNode(returnStatement); } - + public virtual void VisitSwitchStatement(SwitchStatement switchStatement) { StartNode(switchStatement); @@ -1721,11 +1722,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor if (!policy.IndentSwitchBody) { writer.Unindent(); } - + foreach (var section in switchStatement.SwitchSections) { section.AcceptVisitor(this); } - + if (!policy.IndentSwitchBody) { writer.Indent(); } @@ -1733,7 +1734,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor NewLine(); EndNode(switchStatement); } - + public virtual void VisitSwitchSection(SwitchSection switchSection) { StartNode(switchSection); @@ -1749,21 +1750,21 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor if (policy.IndentCaseBody && !isBlock) { writer.Indent(); } - + if (!isBlock) NewLine(); - + foreach (var statement in switchSection.Statements) { statement.AcceptVisitor(this); } - + if (policy.IndentCaseBody && !isBlock) { writer.Unindent(); } - + EndNode(switchSection); } - + public virtual void VisitCaseLabel(CaseLabel caseLabel) { StartNode(caseLabel); @@ -1777,7 +1778,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteToken(Roles.Colon); EndNode(caseLabel); } - + public virtual void VisitThrowStatement(ThrowStatement throwStatement) { StartNode(throwStatement); @@ -1789,7 +1790,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Semicolon(); EndNode(throwStatement); } - + public virtual void VisitTryCatchStatement(TryCatchStatement tryCatchStatement) { StartNode(tryCatchStatement); @@ -1813,7 +1814,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor NewLine(); EndNode(tryCatchStatement); } - + public virtual void VisitCatchClause(CatchClause catchClause) { StartNode(catchClause); @@ -1843,7 +1844,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteBlock(catchClause.Body, policy.StatementBraceStyle); EndNode(catchClause); } - + public virtual void VisitUncheckedStatement(UncheckedStatement uncheckedStatement) { StartNode(uncheckedStatement); @@ -1851,7 +1852,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor uncheckedStatement.Body.AcceptVisitor(this); EndNode(uncheckedStatement); } - + public virtual void VisitUnsafeStatement(UnsafeStatement unsafeStatement) { StartNode(unsafeStatement); @@ -1859,25 +1860,28 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor unsafeStatement.Body.AcceptVisitor(this); EndNode(unsafeStatement); } - + public virtual void VisitUsingStatement(UsingStatement usingStatement) { StartNode(usingStatement); + if (usingStatement.IsAsync) { + WriteKeyword(UsingStatement.AwaitRole); + } WriteKeyword(UsingStatement.UsingKeywordRole); Space(policy.SpaceBeforeUsingParentheses); LPar(); Space(policy.SpacesWithinUsingParentheses); - + usingStatement.ResourceAcquisition.AcceptVisitor(this); - + Space(policy.SpacesWithinUsingParentheses); RPar(); - + WriteEmbeddedStatement(usingStatement.EmbeddedStatement); - + EndNode(usingStatement); } - + public virtual void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) { StartNode(variableDeclarationStatement); @@ -1907,7 +1911,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor EndNode(localFunctionDeclarationStatement); } - + public virtual void VisitWhileStatement(WhileStatement whileStatement) { StartNode(whileStatement); @@ -1921,7 +1925,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteEmbeddedStatement(whileStatement.EmbeddedStatement); EndNode(whileStatement); } - + public virtual void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) { StartNode(yieldBreakStatement); @@ -1930,7 +1934,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Semicolon(); EndNode(yieldBreakStatement); } - + public virtual void VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement) { StartNode(yieldReturnStatement); @@ -1943,7 +1947,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } #endregion - + #region TypeMembers public virtual void VisitAccessor(Accessor accessor) { @@ -1967,7 +1971,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteMethodBody(accessor.Body, style); EndNode(accessor); } - + public virtual void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) { StartNode(constructorDeclaration); @@ -1989,7 +1993,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteMethodBody(constructorDeclaration.Body, policy.ConstructorBraceStyle); EndNode(constructorDeclaration); } - + public virtual void VisitConstructorInitializer(ConstructorInitializer constructorInitializer) { StartNode(constructorInitializer); @@ -2004,7 +2008,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteCommaSeparatedListInParenthesis(constructorInitializer.Arguments, policy.SpaceWithinMethodCallParentheses); EndNode(constructorInitializer); } - + public virtual void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) { StartNode(destructorDeclaration); @@ -2025,7 +2029,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteMethodBody(destructorDeclaration.Body, policy.DestructorBraceStyle); EndNode(destructorDeclaration); } - + public virtual void VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) { StartNode(enumMemberDeclaration); @@ -2040,7 +2044,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } EndNode(enumMemberDeclaration); } - + public virtual void VisitEventDeclaration(EventDeclaration eventDeclaration) { StartNode(eventDeclaration); @@ -2053,7 +2057,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Semicolon(); EndNode(eventDeclaration); } - + public virtual void VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration) { StartNode(customEventDeclaration); @@ -2075,7 +2079,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor NewLine(); EndNode(customEventDeclaration); } - + public virtual void VisitFieldDeclaration(FieldDeclaration fieldDeclaration) { StartNode(fieldDeclaration); @@ -2087,7 +2091,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Semicolon(); EndNode(fieldDeclaration); } - + public virtual void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) { StartNode(fixedFieldDeclaration); @@ -2101,7 +2105,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Semicolon(); EndNode(fixedFieldDeclaration); } - + public virtual void VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer) { StartNode(fixedVariableInitializer); @@ -2115,7 +2119,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } EndNode(fixedVariableInitializer); } - + public virtual void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) { StartNode(indexerDeclaration); @@ -2147,7 +2151,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } EndNode(indexerDeclaration); } - + public virtual void VisitMethodDeclaration(MethodDeclaration methodDeclaration) { StartNode(methodDeclaration); @@ -2166,7 +2170,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteMethodBody(methodDeclaration.Body, policy.MethodBraceStyle); EndNode(methodDeclaration); } - + public virtual void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) { StartNode(operatorDeclaration); @@ -2182,7 +2186,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteKeyword(OperatorDeclaration.OperatorKeywordRole); Space(); if (operatorDeclaration.OperatorType == OperatorType.Explicit - || operatorDeclaration.OperatorType == OperatorType.Implicit) { + || operatorDeclaration.OperatorType == OperatorType.Implicit) { operatorDeclaration.ReturnType.AcceptVisitor(this); } else { WriteToken(OperatorDeclaration.GetToken(operatorDeclaration.OperatorType), OperatorDeclaration.GetRole(operatorDeclaration.OperatorType)); @@ -2192,7 +2196,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteMethodBody(operatorDeclaration.Body, policy.MethodBraceStyle); EndNode(operatorDeclaration); } - + public virtual void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) { StartNode(parameterDeclaration); @@ -2234,7 +2238,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } EndNode(parameterDeclaration); } - + public virtual void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) { StartNode(propertyDeclaration); @@ -2272,7 +2276,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } #endregion - + #region Other nodes public virtual void VisitVariableInitializer(VariableInitializer variableInitializer) { @@ -2298,7 +2302,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor NewLine(); } } - + public virtual void VisitSyntaxTree(SyntaxTree syntaxTree) { // don't do node tracking as we visit all children directly @@ -2307,7 +2311,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor MaybeNewLinesAfterUsings(node); } } - + public virtual void VisitSimpleType(SimpleType simpleType) { StartNode(simpleType); @@ -2315,7 +2319,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteTypeArguments(simpleType.TypeArguments); EndNode(simpleType); } - + public virtual void VisitMemberType(MemberType memberType) { StartNode(memberType); @@ -2377,7 +2381,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } EndNode(composedType); } - + public virtual void VisitArraySpecifier(ArraySpecifier arraySpecifier) { StartNode(arraySpecifier); @@ -2388,14 +2392,14 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteToken(Roles.RBracket); EndNode(arraySpecifier); } - + public virtual void VisitPrimitiveType(PrimitiveType primitiveType) { StartNode(primitiveType); writer.WritePrimitiveType(primitiveType.Keyword); EndNode(primitiveType); } - + public virtual void VisitComment(Comment comment) { writer.StartNode(comment); @@ -2405,9 +2409,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor public virtual void VisitNewLine(NewLineNode newLineNode) { -// formatter.StartNode(newLineNode); -// formatter.NewLine(); -// formatter.EndNode(newLineNode); + // formatter.StartNode(newLineNode); + // formatter.NewLine(); + // formatter.EndNode(newLineNode); } public virtual void VisitWhitespace(WhitespaceNode whitespaceNode) @@ -2426,7 +2430,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor writer.WritePreProcessorDirective(preProcessorDirective.Type, preProcessorDirective.Argument); writer.EndNode(preProcessorDirective); } - + public virtual void VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration) { StartNode(typeParameterDeclaration); @@ -2441,12 +2445,12 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteKeyword(TypeParameterDeclaration.InVarianceKeywordRole); break; default: - throw new NotSupportedException ("Invalid value for VarianceModifier"); + throw new NotSupportedException("Invalid value for VarianceModifier"); } WriteIdentifier(typeParameterDeclaration.NameToken); EndNode(typeParameterDeclaration); } - + public virtual void VisitConstraint(Constraint constraint) { StartNode(constraint); @@ -2459,7 +2463,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteCommaSeparatedList(constraint.BaseTypes); EndNode(constraint); } - + public virtual void VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode) { CSharpModifierToken mod = cSharpTokenNode as CSharpModifierToken; @@ -2468,10 +2472,10 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor // StartNode(parentNode)-EndNode(parentNode)-pair is a child of parentNode. WriteKeyword(CSharpModifierToken.GetModifierName(mod.Modifier), cSharpTokenNode.Role); } else { - throw new NotSupportedException ("Should never visit individual tokens"); + throw new NotSupportedException("Should never visit individual tokens"); } } - + public virtual void VisitIdentifier(Identifier identifier) { // Do not call StartNode and EndNode for Identifier, because they are handled by the ITokenWriter. @@ -2498,7 +2502,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor VisitNodeInPattern(pattern); EndNode(placeholder); } - + void VisitAnyNode(AnyNode anyNode) { if (!string.IsNullOrEmpty(anyNode.GroupName)) { @@ -2506,7 +2510,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteToken(Roles.Colon); } } - + void VisitBackreference(Backreference backreference) { WriteKeyword("backreference"); @@ -2514,7 +2518,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteIdentifier(backreference.ReferencedGroupName); RPar(); } - + void VisitIdentifierExpressionBackreference(IdentifierExpressionBackreference identifierExpressionBackreference) { WriteKeyword("identifierBackreference"); @@ -2522,7 +2526,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteIdentifier(identifierExpressionBackreference.ReferencedGroupName); RPar(); } - + void VisitChoice(Choice choice) { WriteKeyword("choice"); @@ -2540,7 +2544,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor writer.Unindent(); RPar(); } - + void VisitNamedNode(NamedNode namedNode) { if (!string.IsNullOrEmpty(namedNode.GroupName)) { @@ -2549,7 +2553,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } VisitNodeInPattern(namedNode.ChildNode); } - + void VisitRepeat(Repeat repeat) { WriteKeyword("repeat"); @@ -2563,7 +2567,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor VisitNodeInPattern(repeat.ChildNode); RPar(); } - + void VisitOptionalNode(OptionalNode optionalNode) { WriteKeyword("optional"); @@ -2571,7 +2575,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor VisitNodeInPattern(optionalNode.ChildNode); RPar(); } - + void VisitNodeInPattern(INode childNode) { if (childNode is AstNode) { @@ -2595,7 +2599,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } } #endregion - + #region Documentation Reference public virtual void VisitDocumentationReference(DocumentationReference documentationReference) { @@ -2644,7 +2648,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor EndNode(documentationReference); } #endregion - + /// /// Converts special characters to escape sequences within the given string. /// diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/ITokenWriter.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/ITokenWriter.cs index a063871a5..e97e1288c 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/ITokenWriter.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/ITokenWriter.cs @@ -26,42 +26,42 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { public abstract void StartNode(AstNode node); public abstract void EndNode(AstNode node); - + /// /// Writes an identifier. /// public abstract void WriteIdentifier(Identifier identifier); - + /// /// Writes a keyword to the output. /// public abstract void WriteKeyword(Role role, string keyword); - + /// /// Writes a token to the output. /// public abstract void WriteToken(Role role, string token); - + /// /// Writes a primitive/literal value /// public abstract void WritePrimitiveValue(object value, string literalValue = null); - + public abstract void WritePrimitiveType(string type); - + public abstract void Space(); public abstract void Indent(); public abstract void Unindent(); public abstract void NewLine(); - + public abstract void WriteComment(CommentType commentType, string content); public abstract void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument); - + public static TokenWriter Create(TextWriter writer, string indentation = "\t") { return new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(new TextWriterTokenWriter(writer) { IndentationString = indentation })); } - + public static TokenWriter CreateWriterThatSetsLocationsInAST(TextWriter writer, string indentation = "\t") { var target = new TextWriterTokenWriter(writer) { IndentationString = indentation }; @@ -72,7 +72,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { return new InsertRequiredSpacesDecorator(writer); } - + public static TokenWriter WrapInWriterThatSetsLocationsInAST(TokenWriter writer) { if (!(writer is ILocatable)) @@ -80,84 +80,84 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor return new InsertMissingTokensDecorator(writer, (ILocatable)writer); } } - + public interface ILocatable { TextLocation Location { get; } int Length { get; } } - + public abstract class DecoratingTokenWriter : TokenWriter { TokenWriter decoratedWriter; - + protected DecoratingTokenWriter(TokenWriter decoratedWriter) { if (decoratedWriter == null) - throw new ArgumentNullException("decoratedWriter"); + throw new ArgumentNullException(nameof(decoratedWriter)); this.decoratedWriter = decoratedWriter; } - + public override void StartNode(AstNode node) { decoratedWriter.StartNode(node); } - + public override void EndNode(AstNode node) { decoratedWriter.EndNode(node); } - + public override void WriteIdentifier(Identifier identifier) { decoratedWriter.WriteIdentifier(identifier); } - + public override void WriteKeyword(Role role, string keyword) { decoratedWriter.WriteKeyword(role, keyword); } - + public override void WriteToken(Role role, string token) { decoratedWriter.WriteToken(role, token); } - + public override void WritePrimitiveValue(object value, string literalValue = null) { decoratedWriter.WritePrimitiveValue(value, literalValue); } - + public override void WritePrimitiveType(string type) { decoratedWriter.WritePrimitiveType(type); } - + public override void Space() { decoratedWriter.Space(); } - + public override void Indent() { decoratedWriter.Indent(); } - + public override void Unindent() { decoratedWriter.Unindent(); } - + public override void NewLine() { decoratedWriter.NewLine(); } - + public override void WriteComment(CommentType commentType, string content) { decoratedWriter.WriteComment(commentType, content); } - + public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument) { decoratedWriter.WritePreProcessorDirective(type, argument); diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs index d09f0f18f..1c7d3ea6d 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs @@ -47,7 +47,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor public TextWriterTokenWriter(TextWriter textWriter) { if (textWriter == null) - throw new ArgumentNullException("textWriter"); + throw new ArgumentNullException(nameof(textWriter)); this.textWriter = textWriter; this.IndentationString = "\t"; this.line = 1; @@ -298,15 +298,13 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } return; } - if (f == 0 && 1 / f == float.NegativeInfinity) { + var str = f.ToString("R", NumberFormatInfo.InvariantInfo) + "f"; + if (f == 0 && 1 / f == float.NegativeInfinity && str[0] != '-') { // negative zero is a special case // (again, not a primitive expression, but it's better to handle // the special case here than to do it in all code generators) - textWriter.Write("-"); - column++; - Length++; + str = '-' + str; } - var str = f.ToString("R", NumberFormatInfo.InvariantInfo) + "f"; column += str.Length; Length += str.Length; textWriter.Write(str); @@ -334,14 +332,13 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } return; } - if (f == 0 && 1 / f == double.NegativeInfinity) { + string number = f.ToString("R", NumberFormatInfo.InvariantInfo); + if (f == 0 && 1 / f == double.NegativeInfinity && number[0] != '-') { // negative zero is a special case // (again, not a primitive expression, but it's better to handle // the special case here than to do it in all code generators) - textWriter.Write("-"); - Length++; + number = '-' + number; } - string number = f.ToString("R", NumberFormatInfo.InvariantInfo); if (number.IndexOf('.') < 0 && number.IndexOf('E') < 0) { number += ".0"; } diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/AwaitResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/AwaitResolveResult.cs index 64264ec75..62d4e1d70 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/AwaitResolveResult.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/AwaitResolveResult.cs @@ -48,7 +48,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver /// This can also refer to an UnsafeOnCompleted method, if the awaiter type implements System.Runtime.CompilerServices.ICriticalNotifyCompletion. /// public readonly IMethod OnCompletedMethod; - + /// /// Method representing the GetResult method on the awaiter type. Can be null if the awaiter type or the method was not found, or when awaiting a dynamic expression. /// @@ -58,21 +58,22 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver : base(resultType) { if (awaiterType == null) - throw new ArgumentNullException("awaiterType"); + throw new ArgumentNullException(nameof(awaiterType)); if (getAwaiterInvocation == null) - throw new ArgumentNullException("getAwaiterInvocation"); + throw new ArgumentNullException(nameof(getAwaiterInvocation)); this.GetAwaiterInvocation = getAwaiterInvocation; this.AwaiterType = awaiterType; this.IsCompletedProperty = isCompletedProperty; this.OnCompletedMethod = onCompletedMethod; this.GetResultMethod = getResultMethod; } - + public override bool IsError { get { return this.GetAwaiterInvocation.IsError || (AwaiterType.Kind != TypeKind.Dynamic && (this.IsCompletedProperty == null || this.OnCompletedMethod == null || this.GetResultMethod == null)); } } - public override IEnumerable GetChildResults() { + public override IEnumerable GetChildResults() + { return new[] { GetAwaiterInvocation }; } } diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs index cb52b8385..69b880470 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs @@ -38,14 +38,14 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver { readonly ConcurrentDictionary implicitConversionCache = new ConcurrentDictionary(); readonly ICompilation compilation; - + public CSharpConversions(ICompilation compilation) { if (compilation == null) - throw new ArgumentNullException("compilation"); + throw new ArgumentNullException(nameof(compilation)); this.compilation = compilation; } - + /// /// Gets the Conversions instance for the specified . /// This will make use of the context's cache manager to reuse the Conversions instance. @@ -53,7 +53,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public static CSharpConversions Get(ICompilation compilation) { if (compilation == null) - throw new ArgumentNullException("compilation"); + throw new ArgumentNullException(nameof(compilation)); CacheManager cache = compilation.CacheManager; CSharpConversions operators = (CSharpConversions)cache.GetShared(typeof(CSharpConversions)); if (operators == null) { @@ -61,30 +61,30 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } return operators; } - + #region TypePair (for caching) struct TypePair : IEquatable { public readonly IType FromType; public readonly IType ToType; - + public TypePair(IType fromType, IType toType) { Debug.Assert(fromType != null && toType != null); this.FromType = fromType; this.ToType = toType; } - + public override bool Equals(object obj) { return (obj is TypePair) && Equals((TypePair)obj); } - + public bool Equals(TypePair other) { return object.Equals(this.FromType, other.FromType) && object.Equals(this.ToType, other.ToType); } - + public override int GetHashCode() { unchecked { @@ -93,7 +93,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } } #endregion - + #region ImplicitConversion private Conversion ImplicitConversion(ResolveResult resolveResult, IType toType, bool allowUserDefined, bool allowTuple) { @@ -137,7 +137,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver c = MethodGroupConversion(resolveResult, toType); return c; } - + private Conversion ImplicitConversion(IType fromType, IType toType, bool allowUserDefined, bool allowTuple) { // C# 4.0 spec: §6.1 @@ -151,17 +151,17 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public Conversion ImplicitConversion(ResolveResult resolveResult, IType toType) { if (resolveResult == null) - throw new ArgumentNullException("resolveResult"); + throw new ArgumentNullException(nameof(resolveResult)); return ImplicitConversion(resolveResult, toType, allowUserDefined: true, allowTuple: true); } public Conversion ImplicitConversion(IType fromType, IType toType) { if (fromType == null) - throw new ArgumentNullException("fromType"); + throw new ArgumentNullException(nameof(fromType)); if (toType == null) - throw new ArgumentNullException("toType"); - + throw new ArgumentNullException(nameof(toType)); + TypePair pair = new TypePair(fromType, toType); Conversion c; if (implicitConversionCache.TryGetValue(pair, out c)) @@ -176,9 +176,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public Conversion StandardImplicitConversion(IType fromType, IType toType) { if (fromType == null) - throw new ArgumentNullException("fromType"); + throw new ArgumentNullException(nameof(fromType)); if (toType == null) - throw new ArgumentNullException("toType"); + throw new ArgumentNullException(nameof(toType)); return StandardImplicitConversion(fromType, toType, allowTupleConversion: true); } @@ -220,10 +220,10 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public bool IsConstraintConvertible(IType fromType, IType toType) { if (fromType == null) - throw new ArgumentNullException("fromType"); + throw new ArgumentNullException(nameof(fromType)); if (toType == null) - throw new ArgumentNullException("toType"); - + throw new ArgumentNullException(nameof(toType)); + if (IdentityConversion(fromType, toType)) return true; if (ImplicitReferenceConversion(fromType, toType, 0)) @@ -242,15 +242,15 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return false; } #endregion - + #region ExplicitConversion public Conversion ExplicitConversion(ResolveResult resolveResult, IType toType) { if (resolveResult == null) - throw new ArgumentNullException("resolveResult"); + throw new ArgumentNullException(nameof(resolveResult)); if (toType == null) - throw new ArgumentNullException("toType"); - + throw new ArgumentNullException(nameof(toType)); + if (resolveResult.Type.Kind == TypeKind.Dynamic) return Conversion.ExplicitDynamicConversion; Conversion c = ImplicitConversion(resolveResult, toType, allowUserDefined: false, allowTuple: false); @@ -266,14 +266,14 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return c; return UserDefinedExplicitConversion(resolveResult, resolveResult.Type, toType); } - + public Conversion ExplicitConversion(IType fromType, IType toType) { if (fromType == null) - throw new ArgumentNullException("fromType"); + throw new ArgumentNullException(nameof(fromType)); if (toType == null) - throw new ArgumentNullException("toType"); - + throw new ArgumentNullException(nameof(toType)); + Conversion c = ImplicitConversion(fromType, toType, allowUserDefined: false, allowTuple: false); if (c != Conversion.None) return c; @@ -282,7 +282,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return c; return UserDefinedExplicitConversion(null, fromType, toType); } - + Conversion ExplicitConversionImpl(IType fromType, IType toType) { // This method is called after we already checked for implicit conversions, @@ -319,7 +319,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return fromType.Equals(toType); } #endregion - + #region Numeric Conversions static readonly bool[,] implicitNumericConversionLookup = { // to: short ushort int uint long ulong @@ -332,11 +332,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver /* int */ { false, false, false, false, true , false }, /* uint */ { false, false, false, false, true , true }, }; - + bool ImplicitNumericConversion(IType fromType, IType toType) { // C# 4.0 spec: §6.1.2 - + TypeCode from = ReflectionHelper.GetTypeCode(fromType); TypeCode to = ReflectionHelper.GetTypeCode(toType); if (to >= TypeCode.Single && to <= TypeCode.Decimal) { @@ -351,20 +351,20 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver && implicitNumericConversionLookup[from - TypeCode.Char, to - TypeCode.Int16]; } } - + bool IsNumericType(IType type) { TypeCode c = ReflectionHelper.GetTypeCode(type); return c >= TypeCode.Char && c <= TypeCode.Decimal; } - + bool AnyNumericConversion(IType fromType, IType toType) { // C# 4.0 spec: §6.1.2 + §6.2.1 return IsNumericType(fromType) && IsNumericType(toType); } #endregion - + #region Enumeration Conversions Conversion ImplicitEnumerationConversion(ResolveResult rr, IType toType) { @@ -378,7 +378,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } return Conversion.None; } - + bool ExplicitEnumerationConversion(IType fromType, IType toType) { // C# 4.0 spec: §6.2.2 @@ -390,7 +390,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return false; } #endregion - + #region Nullable Conversions Conversion ImplicitNullableConversion(IType fromType, IType toType) { @@ -405,7 +405,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } return Conversion.None; } - + Conversion ExplicitNullableConversion(IType fromType, IType toType) { // C# 4.0 spec: §6.1.4 @@ -422,7 +422,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return Conversion.None; } #endregion - + #region Null Literal Conversion bool NullLiteralConversion(IType fromType, IType toType) { @@ -434,24 +434,24 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } } #endregion - + #region Implicit Reference Conversion public bool IsImplicitReferenceConversion(IType fromType, IType toType) { return ImplicitReferenceConversion(fromType, toType, 0); } - + bool ImplicitReferenceConversion(IType fromType, IType toType, int subtypeCheckNestingDepth) { // C# 4.0 spec: §6.1.6 - + // reference conversions are possible: // - if both types are known to be reference types // - if both types are type parameters and fromType has a class constraint // (ImplicitTypeParameterConversionWithClassConstraintOnlyOnT) if (!(fromType.IsReferenceType == true && toType.IsReferenceType != false)) return false; - + ArrayType fromArray = fromType as ArrayType; if (fromArray != null) { ArrayType toArray = toType as ArrayType; @@ -471,11 +471,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver IType systemArray = compilation.FindType(KnownTypeCode.Array); return ImplicitReferenceConversion(systemArray, toType, subtypeCheckNestingDepth); } - + // now comes the hard part: traverse the inheritance chain and figure out generics+variance return IsSubtypeOf(fromType, toType, subtypeCheckNestingDepth); } - + /// /// For IList{T}, ICollection{T}, IEnumerable{T} and IReadOnlyList{T}, returns T. /// Otherwise, returns null. @@ -494,10 +494,10 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } return null; } - + // Determines whether s is a subtype of t. // Helper method used for ImplicitReferenceConversion, BoxingConversion and ImplicitTypeParameterConversion - + bool IsSubtypeOf(IType s, IType t, int subtypeCheckNestingDepth) { // conversion to dynamic + object are always possible @@ -507,7 +507,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver // Subtyping in C# is undecidable // (see "On Decidability of Nominal Subtyping with Variance" by Andrew J. Kennedy and Benjamin C. Pierce), // so we'll prevent infinite recursions by putting a limit on the nesting depth of variance conversions. - + // No real C# code should use generics nested more than 10 levels deep, and even if they do, most of // those nestings should not involve variance. return false; @@ -519,7 +519,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } return false; } - + bool IdentityOrVarianceConversion(IType s, IType t, int subtypeCheckNestingDepth) { ITypeDefinition def = s.GetDefinition(); @@ -559,12 +559,12 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } } #endregion - + #region Explicit Reference Conversion bool ExplicitReferenceConversion(IType fromType, IType toType) { // C# 4.0 spec: §6.2.4 - + // test that the types are reference types: if (toType.IsReferenceType != true) return false; @@ -576,7 +576,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return IsSubtypeOf(toType, fromType, 0); return false; } - + if (toType.Kind == TypeKind.Array) { ArrayType toArray = (ArrayType)toType; if (fromType.Kind == TypeKind.Array) { @@ -645,7 +645,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver || IsImplicitReferenceConversion(fromType, toType); } } - + bool IsSealedReferenceType(IType type) { TypeKind kind = type.Kind; @@ -686,7 +686,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return false; } #endregion - + #region Implicit Constant-Expression Conversion bool ImplicitConstantExpressionConversion(ResolveResult rr, IType toType) { @@ -721,7 +721,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return false; } #endregion - + #region Conversions involving type parameters /// /// Implicit conversions involving type parameters. @@ -734,7 +734,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return false; // already handled by ImplicitReferenceConversion return IsSubtypeOf(fromType, toType, 0); } - + Conversion ExplicitTypeParameterConversion(IType fromType, IType toType) { if (toType.Kind == TypeKind.TypeParameter) { @@ -749,7 +749,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return Conversion.None; } #endregion - + #region Pointer Conversions bool ImplicitPointerConversion(IType fromType, IType toType) { @@ -760,7 +760,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return true; return false; } - + bool ExplicitPointerConversion(IType fromType, IType toType) { // C# 4.0 spec: §18.4 Pointer conversions @@ -770,14 +770,14 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return toType.Kind == TypeKind.Pointer && IsIntegerType(fromType); } } - + bool IsIntegerType(IType type) { TypeCode c = ReflectionHelper.GetTypeCode(type); return c >= TypeCode.SByte && c <= TypeCode.UInt64; } #endregion - + #region User-Defined Conversions /// /// Gets whether type A is encompassed by type B. @@ -786,7 +786,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver { return a.Kind != TypeKind.Interface && b.Kind != TypeKind.Interface && StandardImplicitConversion(a, b).IsValid; } - + bool IsEncompassingOrEncompassedBy(IType a, IType b) { return a.Kind != TypeKind.Interface && b.Kind != TypeKind.Interface @@ -800,7 +800,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver if (best == null || IsEncompassedBy(current, best)) best = current; else if (!IsEncompassedBy(best, current)) - return null; // Ambiguous + return null; // Ambiguous } return best; } @@ -812,7 +812,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver if (best == null || IsEncompassedBy(best, current)) best = current; else if (!IsEncompassedBy(current, best)) - return null; // Ambiguous + return null; // Ambiguous } return best; } @@ -831,7 +831,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver var op = selected.First(s => !s.IsLifted); return Conversion.UserDefinedConversion(op.Method, isLifted: op.IsLifted, isImplicit: isImplicit, conversionBeforeUserDefinedOperator: ExplicitConversion(source, mostSpecificSource), conversionAfterUserDefinedOperator: ExplicitConversion(mostSpecificTarget, target)); } - + return Conversion.UserDefinedConversion(selected[0].Method, isLifted: selected[0].IsLifted, isImplicit: isImplicit, isAmbiguous: true, conversionBeforeUserDefinedOperator: ExplicitConversion(source, mostSpecificSource), conversionAfterUserDefinedOperator: ExplicitConversion(mostSpecificTarget, target)); } @@ -861,17 +861,15 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return other; } return selected; - } - else if (NullableType.IsNullable(toType)) + } else if (NullableType.IsNullable(toType)) return UserDefinedImplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType)); else return Conversion.None; - } - else { + } else { return Conversion.None; } } - + Conversion UserDefinedExplicitConversion(ResolveResult fromResult, IType fromType, IType toType) { // C# 4.0 spec §6.4.5 User-defined explicit conversions @@ -913,26 +911,24 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return other; } return selected; - } - else if (NullableType.IsNullable(toType)) + } else if (NullableType.IsNullable(toType)) return UserDefinedExplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType)); else if (NullableType.IsNullable(fromType)) - return UserDefinedExplicitConversion(null, NullableType.GetUnderlyingType(fromType), toType); // A? -> A -> B + return UserDefinedExplicitConversion(null, NullableType.GetUnderlyingType(fromType), toType); // A? -> A -> B else return Conversion.None; - } - else { + } else { return Conversion.None; } } - + class OperatorInfo { public readonly IMethod Method; public readonly IType SourceType; public readonly IType TargetType; public readonly bool IsLifted; - + public OperatorInfo(IMethod method, IType sourceType, IType targetType, bool isLifted) { this.Method = method; @@ -941,7 +937,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver this.IsLifted = isLifted; } } - + List GetApplicableConversionOperators(ResolveResult fromResult, IType fromType, IType toType, bool isExplicit) { // Find the candidate operators: @@ -950,7 +946,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver opFilter = m => m.IsStatic && m.IsOperator && (m.Name == "op_Explicit" || m.Name == "op_Implicit") && m.Parameters.Count == 1; else opFilter = m => m.IsStatic && m.IsOperator && m.Name == "op_Implicit" && m.Parameters.Count == 1; - + var operators = NullableType.GetUnderlyingType(fromType).GetMethods(opFilter) .Concat(NullableType.GetUnderlyingType(toType).GetMethods(opFilter)).Distinct(); // Determine whether one of them is applicable: @@ -990,7 +986,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return result; } #endregion - + #region AnonymousFunctionConversion Conversion AnonymousFunctionConversion(ResolveResult resolveResult, IType toType) { @@ -1006,18 +1002,18 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver IMethod d = toType.GetDelegateInvokeMethod(); if (d == null) return Conversion.None; - + IType[] dParamTypes = new IType[d.Parameters.Count]; for (int i = 0; i < dParamTypes.Length; i++) { dParamTypes[i] = d.Parameters[i].Type; } IType dReturnType = d.ReturnType; - + if (f.HasParameterList) { // If F contains an anonymous-function-signature, then D and F have the same number of parameters. if (d.Parameters.Count != f.Parameters.Count) return Conversion.None; - + if (f.IsImplicitlyTyped) { // If F has an implicitly typed parameter list, D has no ref or out parameters. foreach (IParameter p in d.Parameters) { @@ -1044,7 +1040,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return Conversion.None; } } - + return f.IsValid(dParamTypes, dReturnType, this); } @@ -1058,7 +1054,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } } #endregion - + #region MethodGroupConversion Conversion MethodGroupConversion(ResolveResult resolveResult, IType toType) { @@ -1069,7 +1065,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver IMethod invoke = toType.GetDelegateInvokeMethod(); if (invoke == null) return Conversion.None; - + ResolveResult[] args = new ResolveResult[invoke.Parameters.Count]; for (int i = 0; i < args.Length; i++) { IParameter param = invoke.Parameters[i]; @@ -1096,7 +1092,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return Conversion.None; } } - + /// /// Gets whether a is compatible with a delegate type. /// §15.2 Delegate compatibility @@ -1106,15 +1102,15 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public bool IsDelegateCompatible(IMethod method, IType delegateType) { if (method == null) - throw new ArgumentNullException("method"); + throw new ArgumentNullException(nameof(method)); if (delegateType == null) - throw new ArgumentNullException("delegateType"); + throw new ArgumentNullException(nameof(delegateType)); IMethod invoke = delegateType.GetDelegateInvokeMethod(); if (invoke == null) return false; return IsDelegateCompatible(method, invoke, false); } - + /// /// Gets whether a method is compatible with a delegate type. /// §15.2 Delegate compatibility @@ -1126,9 +1122,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver bool IsDelegateCompatible(IMethod m, IMethod invoke, bool isExtensionMethodInvocation) { if (m == null) - throw new ArgumentNullException("m"); + throw new ArgumentNullException(nameof(m)); if (invoke == null) - throw new ArgumentNullException("invoke"); + throw new ArgumentNullException(nameof(invoke)); int firstParameterInM = isExtensionMethodInvocation ? 1 : 0; if (m.Parameters.Count - firstParameterInM != invoke.Parameters.Count) return false; @@ -1227,14 +1223,14 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } if (lambda.HasParameterList && parameterTypes.Length != lambda.Parameters.Count) return 0; - + IType ret1 = m1.ReturnType; IType ret2 = m2.ReturnType; if (ret1.Kind == TypeKind.Void && ret2.Kind != TypeKind.Void) return 2; if (ret1.Kind != TypeKind.Void && ret2.Kind == TypeKind.Void) return 1; - + IType inferredRet = lambda.GetInferredReturnType(parameterTypes); int r = BetterConversion(inferredRet, ret1, ret2); if (r == 0 && lambda.IsAsync) { @@ -1249,7 +1245,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return BetterConversion(resolveResult.Type, t1, t2); } } - + /// /// Unpacks the generic Task[T]. Returns null if the input is not Task[T]. /// @@ -1261,7 +1257,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } return null; } - + /// /// Gets the better conversion (C# 4.0 spec, §7.5.3.4) /// @@ -1276,7 +1272,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return 2; return BetterConversionTarget(t1, t2); } - + /// /// Gets the better conversion target (C# 4.0 spec, §7.5.3.5) /// @@ -1297,7 +1293,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return 2; return 0; } - + bool IsBetterIntegralType(TypeCode t1, TypeCode t2) { // signed types are better than unsigned types diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs index 0ef09c1bb..1f8d8840c 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs @@ -49,7 +49,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public CSharpResolver(ICompilation compilation) { if (compilation == null) - throw new ArgumentNullException("compilation"); + throw new ArgumentNullException(nameof(compilation)); this.compilation = compilation; this.conversions = CSharpConversions.Get(compilation); this.context = new CSharpTypeResolveContext(compilation.MainModule); @@ -58,7 +58,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public CSharpResolver(CSharpTypeResolveContext context) { if (context == null) - throw new ArgumentNullException("context"); + throw new ArgumentNullException(nameof(context)); this.compilation = context.Compilation; this.conversions = CSharpConversions.Get(compilation); this.context = context; @@ -262,7 +262,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public CSharpResolver AddVariable(IVariable variable) { if (variable == null) - throw new ArgumentNullException("variable"); + throw new ArgumentNullException(nameof(variable)); return WithLocalVariableStack(localVariableStack.Push(variable)); } @@ -313,7 +313,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public CSharpResolver PushObjectInitializer(ResolveResult initializedObject) { if (initializedObject == null) - throw new ArgumentNullException("initializedObject"); + throw new ArgumentNullException(nameof(initializedObject)); return WithObjectInitializerStack(new ObjectInitializerContext(initializedObject, objectInitializerStack)); } @@ -379,14 +379,14 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return UnaryOperatorResolveResult(new PointerType(expression.Type), op, expression); case UnaryOperatorType.Await: { ResolveResult getAwaiterMethodGroup = ResolveMemberAccess(expression, "GetAwaiter", EmptyList.Instance, NameLookupMode.InvocationTarget); - ResolveResult getAwaiterInvocation = ResolveInvocation(getAwaiterMethodGroup, new ResolveResult[0], argumentNames: null, allowOptionalParameters: false); + ResolveResult getAwaiterInvocation = ResolveInvocation(getAwaiterMethodGroup, Empty.Array, argumentNames: null, allowOptionalParameters: false); var lookup = CreateMemberLookup(); IMethod getResultMethod; IType awaitResultType; var getResultMethodGroup = lookup.Lookup(getAwaiterInvocation, "GetResult", EmptyList.Instance, true) as MethodGroupResolveResult; if (getResultMethodGroup != null) { - var getResultOR = getResultMethodGroup.PerformOverloadResolution(compilation, new ResolveResult[0], allowExtensionMethods: false, conversions: conversions); + var getResultOR = getResultMethodGroup.PerformOverloadResolution(compilation, Empty.Array, allowExtensionMethods: false, conversions: conversions); getResultMethod = getResultOR.FoundApplicableCandidate ? getResultOR.GetBestCandidateWithSubstitutedTypeArguments() as IMethod : null; awaitResultType = getResultMethod != null ? getResultMethod.ReturnType : SpecialType.UnknownType; } @@ -1313,9 +1313,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver // C# 4.0 spec: §3.8 Namespace and type names; §7.6.2 Simple Names if (identifier == null) - throw new ArgumentNullException("identifier"); + throw new ArgumentNullException(nameof(identifier)); if (typeArguments == null) - throw new ArgumentNullException("typeArguments"); + throw new ArgumentNullException(nameof(typeArguments)); int k = typeArguments.Count; @@ -1698,12 +1698,12 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } getEnumeratorInvocation = ResolveCast(collectionType, expression); getEnumeratorInvocation = ResolveMemberAccess(getEnumeratorInvocation, "GetEnumerator", EmptyList.Instance, NameLookupMode.InvocationTarget); - getEnumeratorInvocation = ResolveInvocation(getEnumeratorInvocation, new ResolveResult[0]); + getEnumeratorInvocation = ResolveInvocation(getEnumeratorInvocation, Empty.Array); } else { var getEnumeratorMethodGroup = memberLookup.Lookup(expression, "GetEnumerator", EmptyList.Instance, true) as MethodGroupResolveResult; if (getEnumeratorMethodGroup != null) { var or = getEnumeratorMethodGroup.PerformOverloadResolution( - compilation, new ResolveResult[0], + compilation, Empty.Array, allowExtensionMethods: false, allowExpandingParams: false, allowOptionalParameters: false); if (or.FoundApplicableCandidate && !or.IsAmbiguous && !or.BestCandidate.IsStatic && or.BestCandidate.Accessibility == Accessibility.Public) { collectionType = expression.Type; @@ -1722,7 +1722,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver var moveNextMethodGroup = memberLookup.Lookup(new ResolveResult(enumeratorType), "MoveNext", EmptyList.Instance, false) as MethodGroupResolveResult; if (moveNextMethodGroup != null) { var or = moveNextMethodGroup.PerformOverloadResolution( - compilation, new ResolveResult[0], + compilation, Empty.Array, allowExtensionMethods: false, allowExpandingParams: false, allowOptionalParameters: false); moveNextMethod = or.GetBestCandidateWithSubstitutedTypeArguments() as IMethod; } @@ -1763,7 +1763,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } getEnumeratorInvocation = ResolveCast(collectionType, expression); getEnumeratorInvocation = ResolveMemberAccess(getEnumeratorInvocation, "GetEnumerator", EmptyList.Instance, NameLookupMode.InvocationTarget); - getEnumeratorInvocation = ResolveInvocation(getEnumeratorInvocation, new ResolveResult[0]); + getEnumeratorInvocation = ResolveInvocation(getEnumeratorInvocation, Empty.Array); } #endregion @@ -1866,9 +1866,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public static bool IsEligibleExtensionMethod(IType targetType, IMethod method, bool useTypeInference, out IType[] outInferredTypes) { if (targetType == null) - throw new ArgumentNullException("targetType"); + throw new ArgumentNullException(nameof(targetType)); if (method == null) - throw new ArgumentNullException("method"); + throw new ArgumentNullException(nameof(method)); var compilation = method.Compilation; return IsEligibleExtensionMethod(compilation, CSharpConversions.Get(compilation), targetType, method, useTypeInference, out outInferredTypes); } @@ -2348,7 +2348,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public ResolveResult ResolveCondition(ResolveResult input) { if (input == null) - throw new ArgumentNullException("input"); + throw new ArgumentNullException(nameof(input)); IType boolean = compilation.FindType(KnownTypeCode.Boolean); Conversion c = conversions.ImplicitConversion(input, boolean); if (!c.IsValid) { @@ -2368,7 +2368,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public ResolveResult ResolveConditionFalse(ResolveResult input) { if (input == null) - throw new ArgumentNullException("input"); + throw new ArgumentNullException(nameof(input)); IType boolean = compilation.FindType(KnownTypeCode.Boolean); Conversion c = conversions.ImplicitConversion(input, boolean); if (!c.IsValid) { diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/LambdaResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/LambdaResolveResult.cs index 4dde321dd..740af770f 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/LambdaResolveResult.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/LambdaResolveResult.cs @@ -152,7 +152,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver if (this.Parameters.Count != parameterTypes.Length) return Conversion.None; for (int i = 0; i < parameterTypes.Length; ++i) { - if (!parameterTypes[i].Equals(this.Parameters[i].Type)) { + if (!conversions.IdentityConversion(parameterTypes[i], this.Parameters[i].Type)) { if (IsImplicitlyTyped) { // it's possible that different parameter types also lead to a valid conversion return LambdaConversion.Instance; @@ -162,7 +162,8 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } } } - if (returnType.Equals(this.ReturnType)) { + if (conversions.IdentityConversion(this.ReturnType, returnType) + || conversions.ImplicitConversion(this.InferredReturnType, returnType).IsValid) { return LambdaConversion.Instance; } else { return Conversion.None; diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/MemberLookup.cs b/ICSharpCode.Decompiler/CSharp/Resolver/MemberLookup.cs index 2dca76dca..95c05ff6d 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/MemberLookup.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/MemberLookup.cs @@ -37,7 +37,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public static bool IsInvocable(IMember member) { if (member == null) - throw new ArgumentNullException("member"); + throw new ArgumentNullException(nameof(member)); // C# 4.0 spec, §7.4 member lookup if (member is IEvent || member is IMethod) return true; @@ -99,7 +99,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public bool IsAccessible(IEntity entity, bool allowProtectedAccess) { if (entity == null) - throw new ArgumentNullException("entity"); + throw new ArgumentNullException(nameof(entity)); // C# 4.0 spec, §3.5.2 Accessiblity domains switch (entity.Accessibility) { case Accessibility.None: @@ -159,7 +159,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public IEnumerable GetAccessibleMembers(ResolveResult targetResolveResult) { if (targetResolveResult == null) - throw new ArgumentNullException("targetResolveResult"); + throw new ArgumentNullException(nameof(targetResolveResult)); bool targetIsTypeParameter = targetResolveResult.Type.Kind == TypeKind.TypeParameter; bool allowProtectedAccess = IsProtectedAccessAllowed(targetResolveResult); @@ -276,11 +276,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public ResolveResult LookupType(IType declaringType, string name, IReadOnlyList typeArguments, bool parameterizeResultType = true) { if (declaringType == null) - throw new ArgumentNullException("declaringType"); + throw new ArgumentNullException(nameof(declaringType)); if (name == null) - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); if (typeArguments == null) - throw new ArgumentNullException("typeArguments"); + throw new ArgumentNullException(nameof(typeArguments)); int typeArgumentCount = typeArguments.Count; Predicate filter = delegate (ITypeDefinition d) { @@ -335,11 +335,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public ResolveResult Lookup(ResolveResult targetResolveResult, string name, IReadOnlyList typeArguments, bool isInvocation) { if (targetResolveResult == null) - throw new ArgumentNullException("targetResolveResult"); + throw new ArgumentNullException(nameof(targetResolveResult)); if (name == null) - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); if (typeArguments == null) - throw new ArgumentNullException("typeArguments"); + throw new ArgumentNullException(nameof(typeArguments)); bool targetIsTypeParameter = targetResolveResult.Type.Kind == TypeKind.TypeParameter; @@ -413,7 +413,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public IReadOnlyList LookupIndexers(ResolveResult targetResolveResult) { if (targetResolveResult == null) - throw new ArgumentNullException("targetResolveResult"); + throw new ArgumentNullException(nameof(targetResolveResult)); IType targetType = targetResolveResult.Type; bool allowProtectedAccess = IsProtectedAccessAllowed(targetResolveResult); diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs index 4f6022208..1b394555c 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs @@ -86,7 +86,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver : base(SpecialType.NoType) { if (methods == null) - throw new ArgumentNullException("methods"); + throw new ArgumentNullException(nameof(methods)); this.targetResult = targetResult; this.methodName = methodName; this.methodLists = methods; diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs b/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs index ef1a5e3d4..d645e36b2 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs @@ -35,53 +35,53 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver sealed class Candidate { public readonly IParameterizedMember Member; - + /// /// Returns the normal form candidate, if this is an expanded candidate. /// public readonly bool IsExpandedForm; - + /// /// Gets the parameter types. In the first step, these are the types without any substition. /// After type inference, substitutions will be performed. /// public readonly IType[] ParameterTypes; - + /// /// argument index -> parameter index; -1 for arguments that could not be mapped /// public int[] ArgumentToParameterMap; - + public OverloadResolutionErrors Errors; public int ErrorCount; - + public bool HasUnmappedOptionalParameters; - + public IType[] InferredTypes; - + /// /// Gets the original member parameters (before any substitution!) /// public readonly IReadOnlyList Parameters; - + /// /// Gets the original method type parameters (before any substitution!) /// public readonly IReadOnlyList TypeParameters; - + /// /// Conversions applied to the arguments. /// This field is set by the CheckApplicability step. /// public Conversion[] ArgumentConversions; - + public bool IsGenericMethod { get { IMethod method = Member as IMethod; return method != null && method.TypeParameters.Count > 0; } } - + public int ArgumentsPassedToParamsArray { get { int count = 0; @@ -95,7 +95,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return count; } } - + public Candidate(IParameterizedMember member, bool isExpanded) { this.Member = member; @@ -111,7 +111,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } this.ParameterTypes = new IType[this.Parameters.Count]; } - + public void AddError(OverloadResolutionErrors newError) { this.Errors |= newError; @@ -119,7 +119,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver this.ErrorCount++; } } - + readonly ICompilation compilation; readonly ResolveResult[] arguments; readonly string[] argumentNames; @@ -130,14 +130,14 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver IType[] explicitlyGivenTypeArguments; bool bestCandidateWasValidated; OverloadResolutionErrors bestCandidateValidationResult; - + #region Constructor public OverloadResolution(ICompilation compilation, ResolveResult[] arguments, string[] argumentNames = null, IType[] typeArguments = null, CSharpConversions conversions = null) { if (compilation == null) - throw new ArgumentNullException("compilation"); + throw new ArgumentNullException(nameof(compilation)); if (arguments == null) - throw new ArgumentNullException("arguments"); + throw new ArgumentNullException(nameof(arguments)); if (argumentNames == null) argumentNames = new string[arguments.Length]; else if (argumentNames.Length != arguments.Length) @@ -145,17 +145,17 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver this.compilation = compilation; this.arguments = arguments; this.argumentNames = argumentNames; - + // keep explicitlyGivenTypeArguments==null when no type arguments were specified if (typeArguments != null && typeArguments.Length > 0) this.explicitlyGivenTypeArguments = typeArguments; - + this.conversions = conversions ?? CSharpConversions.Get(compilation); this.AllowExpandingParams = true; this.AllowOptionalParameters = true; } #endregion - + #region Input Properties /// /// Gets/Sets whether the methods are extension methods that are being called using extension method syntax. @@ -165,27 +165,27 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver /// implicit identity, reference, or boxing conversions. /// public bool IsExtensionMethodInvocation { get; set; } - + /// /// Gets/Sets whether expanding 'params' into individual elements is allowed. /// The default value is true. /// public bool AllowExpandingParams { get; set; } - + /// /// Gets/Sets whether optional parameters may be left at their default value. /// The default value is true. /// If this property is set to false, optional parameters will be treated like regular parameters. /// public bool AllowOptionalParameters { get; set; } - + /// /// Gets/Sets whether ConversionResolveResults created by this OverloadResolution /// instance apply overflow checking. /// The default value is false. /// public bool CheckForOverflow { get; set; } - + /// /// Gets the arguments for which this OverloadResolution instance was created. /// @@ -193,7 +193,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver get { return arguments; } } #endregion - + #region AddCandidate /// /// Adds a candidate to overload resolution. @@ -205,7 +205,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver { return AddCandidate(member, OverloadResolutionErrors.None); } - + /// /// Adds a candidate to overload resolution. /// @@ -218,30 +218,30 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public OverloadResolutionErrors AddCandidate(IParameterizedMember member, OverloadResolutionErrors additionalErrors) { if (member == null) - throw new ArgumentNullException("member"); - + throw new ArgumentNullException(nameof(member)); + Candidate c = new Candidate(member, false); c.AddError(additionalErrors); if (CalculateCandidate(c)) { //candidates.Add(c); } - + if (this.AllowExpandingParams && member.Parameters.Count > 0 - && member.Parameters[member.Parameters.Count - 1].IsParams) + && member.Parameters[member.Parameters.Count - 1].IsParams) { Candidate expandedCandidate = new Candidate(member, true); expandedCandidate.AddError(additionalErrors); // consider expanded form only if it isn't obviously wrong if (CalculateCandidate(expandedCandidate)) { //candidates.Add(expandedCandidate); - + if (expandedCandidate.ErrorCount < c.ErrorCount) return expandedCandidate.Errors; } } return c.Errors; } - + /// /// Calculates applicability etc. for the candidate. /// @@ -256,7 +256,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver ConsiderIfNewCandidateIsBest(candidate); return true; } - + bool ResolveParameterTypes(Candidate candidate, bool useSpecializedParameters) { for (int i = 0; i < candidate.Parameters.Count; i++) { @@ -281,7 +281,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return true; } #endregion - + #region AddMethodLists /// /// Adds all candidates from the method lists. @@ -293,7 +293,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public void AddMethodLists(IReadOnlyList methodLists) { if (methodLists == null) - throw new ArgumentNullException("methodLists"); + throw new ArgumentNullException(nameof(methodLists)); // Base types come first, so go through the list backwards (derived types first) bool[] isHiddenByDerivedType; if (methodLists.Count > 1) @@ -305,20 +305,20 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver Log.WriteLine(" Skipping methods in {0} because they are hidden by an applicable method in a derived type", methodLists[i].DeclaringType); continue; } - + MethodListWithDeclaringType methodList = methodLists[i]; bool foundApplicableCandidateInCurrentList = false; - + for (int j = 0; j < methodList.Count; j++) { IParameterizedMember method = methodList[j]; Log.Indent(); OverloadResolutionErrors errors = AddCandidate(method); Log.Unindent(); LogCandidateAddingResult(" Candidate", method, errors); - + foundApplicableCandidateInCurrentList |= IsApplicable(errors); } - + if (foundApplicableCandidateInCurrentList && i > 0) { foreach (IType baseType in methodList.DeclaringType.GetAllBaseTypes()) { for (int j = 0; j < i; j++) { @@ -329,21 +329,21 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } } } - + [Conditional("DEBUG")] internal void LogCandidateAddingResult(string text, IParameterizedMember method, OverloadResolutionErrors errors) { #if DEBUG Log.WriteLine(string.Format("{0} {1} = {2}{3}", - text, method, - errors == OverloadResolutionErrors.None ? "Success" : errors.ToString(), - this.BestCandidate == method ? " (best candidate so far)" : - this.BestCandidateAmbiguousWith == method ? " (ambiguous)" : "" - )); + text, method, + errors == OverloadResolutionErrors.None ? "Success" : errors.ToString(), + this.BestCandidate == method ? " (best candidate so far)" : + this.BestCandidateAmbiguousWith == method ? " (ambiguous)" : "" + )); #endif } #endregion - + #region MapCorrespondingParameters void MapCorrespondingParameters(Candidate candidate) { @@ -386,7 +386,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } } #endregion - + #region RunTypeInference void RunTypeInference(Candidate candidate) { @@ -436,18 +436,18 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver if (!substitution.ConstraintsValid) candidate.AddError(OverloadResolutionErrors.ConstructedTypeDoesNotSatisfyConstraint); } - + sealed class ConstraintValidatingSubstitution : TypeParameterSubstitution { readonly CSharpConversions conversions; public bool ConstraintsValid = true; - + public ConstraintValidatingSubstitution(IReadOnlyList classTypeArguments, IReadOnlyList methodTypeArguments, OverloadResolution overloadResolution) : base(classTypeArguments, methodTypeArguments) { this.conversions = overloadResolution.conversions; } - + public override IType VisitParameterizedType(ParameterizedType type) { IType newType = base.VisitParameterizedType(type); @@ -470,14 +470,14 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } } #endregion - + #region Validate Constraints OverloadResolutionErrors ValidateMethodConstraints(Candidate candidate) { // If type inference already failed, we won't check the constraints: if ((candidate.Errors & OverloadResolutionErrors.TypeInferenceFailed) != 0) return OverloadResolutionErrors.None; - + if (candidate.TypeParameters == null || candidate.TypeParameters.Count == 0) return OverloadResolutionErrors.None; // the method isn't generic var substitution = GetSubstitution(candidate); @@ -487,7 +487,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } return OverloadResolutionErrors.None; } - + /// /// Validates whether the given type argument satisfies the constraints for the given type parameter. /// @@ -500,12 +500,12 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public static bool ValidateConstraints(ITypeParameter typeParameter, IType typeArgument, TypeVisitor substitution = null) { if (typeParameter == null) - throw new ArgumentNullException("typeParameter"); + throw new ArgumentNullException(nameof(typeParameter)); if (typeArgument == null) - throw new ArgumentNullException("typeArgument"); + throw new ArgumentNullException(nameof(typeArgument)); return ValidateConstraints(typeParameter, typeArgument, substitution, CSharpConversions.Get(typeParameter.Owner.Compilation)); } - + internal static bool ValidateConstraints(ITypeParameter typeParameter, IType typeArgument, TypeVisitor substitution, CSharpConversions conversions) { switch (typeArgument.Kind) { // void, null, and pointers cannot be used as type arguments @@ -543,7 +543,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return true; } #endregion - + #region CheckApplicability /// /// Returns whether a candidate with the given errors is still considered to be applicable. @@ -554,11 +554,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver OverloadResolutionErrors.AmbiguousMatch | OverloadResolutionErrors.MethodConstraintsNotSatisfied; return (errors & ~errorsThatDoNotMatterForApplicability) == OverloadResolutionErrors.None; } - + void CheckApplicability(Candidate candidate) { // C# 4.0 spec: §7.5.3.1 Applicable function member - + // Test whether parameters were mapped the correct number of arguments: int[] argumentCountPerParameter = new int[candidate.ParameterTypes.Length]; foreach (int parameterIndex in candidate.ArgumentToParameterMap) { @@ -577,7 +577,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver candidate.AddError(OverloadResolutionErrors.MultipleArgumentsForSingleParameter); } } - + candidate.ArgumentConversions = new Conversion[arguments.Length]; // Test whether argument passing mode matches the parameter passing mode for (int i = 0; i < arguments.Length; i++) { @@ -586,7 +586,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver candidate.ArgumentConversions[i] = Conversion.None; continue; } - + ByReferenceResolveResult brrr = arguments[i] as ByReferenceResolveResult; if (brrr != null) { if (brrr.ReferenceKind != candidate.Parameters[parameterIndex].ReferenceKind) @@ -609,7 +609,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } } #endregion - + #region BetterFunctionMember /// /// Returns 1 if c1 is better than c2; 2 if c2 is better than c1; or 0 if neither is better. @@ -621,7 +621,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return 1; if (c1.ErrorCount > 0 && c2.ErrorCount == 0) return 2; - + // C# 4.0 spec: §7.5.3.2 Better function member bool c1IsBetter = false; bool c2IsBetter = false; @@ -650,42 +650,42 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return 1; if (!c1IsBetter && c2IsBetter) return 2; - + // prefer members with less errors (part of heuristic that produces a best candidate even if none is applicable) if (c1.ErrorCount < c2.ErrorCount) return 1; if (c1.ErrorCount > c2.ErrorCount) return 2; - + if (!c1IsBetter && !c2IsBetter && parameterTypesEqual) { // we need the tie-breaking rules - + // non-generic methods are better if (!c1.IsGenericMethod && c2.IsGenericMethod) return 1; else if (c1.IsGenericMethod && !c2.IsGenericMethod) return 2; - + // non-expanded members are better if (!c1.IsExpandedForm && c2.IsExpandedForm) return 1; else if (c1.IsExpandedForm && !c2.IsExpandedForm) return 2; - + // prefer the member with less arguments mapped to the params-array int r = c1.ArgumentsPassedToParamsArray.CompareTo(c2.ArgumentsPassedToParamsArray); if (r < 0) return 1; else if (r > 0) return 2; - + // prefer the member where no default values need to be substituted if (!c1.HasUnmappedOptionalParameters && c2.HasUnmappedOptionalParameters) return 1; else if (c1.HasUnmappedOptionalParameters && !c2.HasUnmappedOptionalParameters) return 2; - + // compare the formal parameters r = MoreSpecificFormalParameters(c1, c2); if (r != 0) return r; - + // prefer non-lifted operators ILiftedOperator lift1 = c1.Member as ILiftedOperator; ILiftedOperator lift2 = c2.Member as ILiftedOperator; @@ -696,22 +696,22 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } return 0; } - + int MoreSpecificFormalParameters(Candidate c1, Candidate c2) { // prefer the member with more formal parmeters (in case both have different number of optional parameters) int r = c1.Parameters.Count.CompareTo(c2.Parameters.Count); if (r > 0) return 1; else if (r < 0) return 2; - + return MoreSpecificFormalParameters(c1.Parameters.Select(p => p.Type), c2.Parameters.Select(p => p.Type)); } - + static int MoreSpecificFormalParameters(IEnumerable t1, IEnumerable t2) { bool c1IsBetter = false; bool c2IsBetter = false; - foreach (var pair in t1.Zip(t2, (a,b) => new { Item1 = a, Item2 = b })) { + foreach (var pair in t1.Zip(t2, (a, b) => new { Item1 = a, Item2 = b })) { switch (MoreSpecificFormalParameter(pair.Item1, pair.Item2)) { case 1: c1IsBetter = true; @@ -727,14 +727,14 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return 2; return 0; } - + static int MoreSpecificFormalParameter(IType t1, IType t2) { if ((t1 is ITypeParameter) && !(t2 is ITypeParameter)) return 2; if ((t2 is ITypeParameter) && !(t1 is ITypeParameter)) return 1; - + ParameterizedType p1 = t1 as ParameterizedType; ParameterizedType p2 = t2 as ParameterizedType; if (p1 != null && p2 != null && p1.TypeParameterCount == p2.TypeParameterCount) { @@ -750,7 +750,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return 0; } #endregion - + #region ConsiderIfNewCandidateIsBest void ConsiderIfNewCandidateIsBest(Candidate candidate) { @@ -775,12 +775,12 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } } #endregion - + #region Output Properties public IParameterizedMember BestCandidate { get { return bestCandidate != null ? bestCandidate.Member : null; } } - + /// /// Returns the errors that apply to the best candidate. /// This includes additional errors that do not affect applicability (e.g. AmbiguousMatch, MethodConstraintsNotSatisfied) @@ -799,23 +799,23 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return err; } } - + public bool FoundApplicableCandidate { get { return bestCandidate != null && IsApplicable(bestCandidate.Errors); } } - + public IParameterizedMember BestCandidateAmbiguousWith { get { return bestCandidateAmbiguousWith != null ? bestCandidateAmbiguousWith.Member : null; } } - + public bool BestCandidateIsExpandedForm { get { return bestCandidate != null ? bestCandidate.IsExpandedForm : false; } } - + public bool IsAmbiguous { get { return bestCandidateAmbiguousWith != null; } } - + public IReadOnlyList InferredTypeArguments { get { if (bestCandidate != null && bestCandidate.InferredTypes != null) @@ -824,7 +824,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return EmptyList.Instance; } } - + /// /// Gets the implicit conversions that are being applied to the arguments. /// @@ -836,7 +836,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return Enumerable.Repeat(Conversion.None, arguments.Length).ToList(); } } - + /// /// Gets an array that maps argument indices to parameter indices. /// For arguments that could not be mapped to any parameter, the value will be -1. @@ -850,7 +850,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver else return null; } - + /// /// Returns the arguments for the method call in the order they were provided (not in the order of the parameters). /// Arguments are wrapped in a if an implicit conversion is being applied @@ -863,7 +863,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver else return GetArgumentsWithConversions(null, null); } - + /// /// Returns the arguments for the method call in the order they were provided (not in the order of the parameters). /// Arguments are wrapped in a if an implicit conversion is being applied @@ -878,7 +878,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver else return GetArgumentsWithConversions(null, GetBestCandidateWithSubstitutedTypeArguments()); } - + IList GetArgumentsWithConversions(ResolveResult targetResolveResult, IParameterizedMember bestCandidateForNamedArguments) { var conversions = this.ArgumentConversions; @@ -911,7 +911,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } return args; } - + public IParameterizedMember GetBestCandidateWithSubstitutedTypeArguments() { if (bestCandidate == null) @@ -923,14 +923,14 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return bestCandidate.Member; } } - + TypeParameterSubstitution GetSubstitution(Candidate candidate) { // Do not compose the substitutions, but merge them. // This is required for InvocationTests.SubstituteClassAndMethodTypeParametersAtOnce return new TypeParameterSubstitution(candidate.Member.Substitution.ClassTypeArguments, candidate.InferredTypes); } - + /// /// Creates a ResolveResult representing the result of overload resolution. /// diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs b/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs index 40a804025..1f912464f 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs @@ -61,7 +61,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public TypeInference(ICompilation compilation) { if (compilation == null) - throw new ArgumentNullException("compilation"); + throw new ArgumentNullException(nameof(compilation)); this.compilation = compilation; this.conversions = CSharpConversions.Get(compilation); } @@ -115,11 +115,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public IType[] InferTypeArguments(IReadOnlyList typeParameters, IReadOnlyList arguments, IReadOnlyList parameterTypes, out bool success, IReadOnlyList classTypeArguments = null) { if (typeParameters == null) - throw new ArgumentNullException("typeParameters"); + throw new ArgumentNullException(nameof(typeParameters)); if (arguments == null) - throw new ArgumentNullException("arguments"); + throw new ArgumentNullException(nameof(arguments)); if (parameterTypes == null) - throw new ArgumentNullException("parameterTypes"); + throw new ArgumentNullException(nameof(parameterTypes)); try { this.typeParameters = new TP[typeParameters.Count]; for (int i = 0; i < this.typeParameters.Length; i++) { @@ -173,13 +173,13 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public IType[] InferTypeArgumentsFromBounds(IReadOnlyList typeParameters, IType targetType, IEnumerable lowerBounds, IEnumerable upperBounds, out bool success) { if (typeParameters == null) - throw new ArgumentNullException("typeParameters"); + throw new ArgumentNullException(nameof(typeParameters)); if (targetType == null) - throw new ArgumentNullException("targetType"); + throw new ArgumentNullException(nameof(targetType)); if (lowerBounds == null) - throw new ArgumentNullException("lowerBounds"); + throw new ArgumentNullException(nameof(lowerBounds)); if (upperBounds == null) - throw new ArgumentNullException("upperBounds"); + throw new ArgumentNullException(nameof(upperBounds)); this.typeParameters = new TP[typeParameters.Count]; for (int i = 0; i < this.typeParameters.Length; i++) { if (i != typeParameters[i].Index) @@ -223,7 +223,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public TP(ITypeParameter typeParameter) { if (typeParameter == null) - throw new ArgumentNullException("typeParameter"); + throw new ArgumentNullException(nameof(typeParameter)); this.TypeParameter = typeParameter; } @@ -361,8 +361,6 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver #endregion #region Input Types / Output Types (§7.5.2.3 + §7.5.2.4) - static readonly IType[] emptyTypeArray = new IType[0]; - IType[] InputTypes(ResolveResult e, IType t) { // C# 4.0 spec: §7.5.2.3 Input types @@ -377,7 +375,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return inputTypes; } } - return emptyTypeArray; + return Empty.Array; } IType[] OutputTypes(ResolveResult e, IType t) @@ -390,16 +388,15 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return new[] { m.ReturnType }; } } - return emptyTypeArray; + return Empty.Array; } static IMethod GetDelegateOrExpressionTreeSignature(IType t) { - ParameterizedType pt = t as ParameterizedType; - if (pt != null && pt.TypeParameterCount == 1 && pt.Name == "Expression" - && pt.Namespace == "System.Linq.Expressions") + if (t.TypeParameterCount == 1 && t.Name == "Expression" + && t.Namespace == "System.Linq.Expressions") { - t = pt.GetTypeArgument(0); + t = t.TypeArguments[0]; } return t.GetDelegateInvokeMethod(); } @@ -571,8 +568,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver { Log.WriteLine("MakeExactInference from " + U + " to " + V); - if (V is NullabilityAnnotatedTypeParameter nullableTP) { - V = nullableTP.OriginalTypeParameter; + if (U.Nullability == V.Nullability) { + U = U.WithoutNullability(); + V = V.WithoutNullability(); } // If V is one of the unfixed Xi then U is added to the set of bounds for Xi. @@ -613,8 +611,10 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver TP GetTPForType(IType v) { - ITypeParameter p = v as ITypeParameter; - if (p != null) { + if (v is NullabilityAnnotatedTypeParameter natp) { + v = natp.OriginalTypeParameter; + } + if (v is ITypeParameter p) { int index = p.Index; if (index < typeParameters.Length && typeParameters[index].TypeParameter == p) return typeParameters[index]; @@ -631,7 +631,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver void MakeLowerBoundInference(IType U, IType V) { Log.WriteLine(" MakeLowerBoundInference from " + U + " to " + V); - + if (U.Nullability == V.Nullability) { + U = U.WithoutNullability(); + V = V.WithoutNullability(); + } + // If V is one of the unfixed Xi then U is added to the set of bounds for Xi. TP tp = GetTPForType(V); if (tp != null && tp.IsFixed == false) { @@ -728,7 +732,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver void MakeUpperBoundInference(IType U, IType V) { Log.WriteLine(" MakeUpperBoundInference from " + U + " to " + V); - + if (U.Nullability == V.Nullability) { + U = U.WithoutNullability(); + V = V.WithoutNullability(); + } + // If V is one of the unfixed Xi then U is added to the set of bounds for Xi. TP tp = GetTPForType(V); if (tp != null && tp.IsFixed == false) { @@ -826,7 +834,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public IType GetBestCommonType(IList expressions, out bool success) { if (expressions == null) - throw new ArgumentNullException("expressions"); + throw new ArgumentNullException(nameof(expressions)); if (expressions.Count == 1) { success = IsValidType(expressions[0].Type); return expressions[0].Type; @@ -853,9 +861,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public IType FindTypeInBounds(IReadOnlyList lowerBounds, IReadOnlyList upperBounds) { if (lowerBounds == null) - throw new ArgumentNullException("lowerBounds"); + throw new ArgumentNullException(nameof(lowerBounds)); if (upperBounds == null) - throw new ArgumentNullException("upperBounds"); + throw new ArgumentNullException(nameof(upperBounds)); var result = FindTypesInBounds(lowerBounds, upperBounds); diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 739cb1188..432f706ad 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -28,7 +28,6 @@ using System; using System.Threading; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; -using ICSharpCode.Decompiler.CSharp.Resolver; namespace ICSharpCode.Decompiler.CSharp { @@ -40,11 +39,18 @@ namespace ICSharpCode.Decompiler.CSharp readonly DecompilerSettings settings; readonly CancellationToken cancellationToken; + internal BlockContainer currentReturnContainer; + internal IType currentResultType; + internal bool currentIsIterator; + public StatementBuilder(IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext, ILFunction currentFunction, DecompilerSettings settings, CancellationToken cancellationToken) { Debug.Assert(typeSystem != null && decompilationContext != null); - this.exprBuilder = new ExpressionBuilder(typeSystem, decompilationContext, currentFunction, settings, cancellationToken); + this.exprBuilder = new ExpressionBuilder(this, typeSystem, decompilationContext, currentFunction, settings, cancellationToken); this.currentFunction = currentFunction; + this.currentReturnContainer = (BlockContainer)currentFunction.Body; + this.currentIsIterator = currentFunction.IsIterator; + this.currentResultType = currentFunction.IsAsync ? currentFunction.AsyncReturnType : currentFunction.ReturnType; this.typeSystem = typeSystem; this.settings = settings; this.cancellationToken = cancellationToken; @@ -300,19 +306,17 @@ namespace ICSharpCode.Decompiler.CSharp { if (inst.TargetContainer == breakTarget) return new BreakStatement(); - if (inst.IsLeavingFunction) { - if (currentFunction.IsIterator) + if (inst.TargetContainer == currentReturnContainer) { + if (currentIsIterator) return new YieldBreakStatement(); else if (!inst.Value.MatchNop()) { - IType targetType = currentFunction.IsAsync ? currentFunction.AsyncReturnType : currentFunction.ReturnType; - var expr = exprBuilder.Translate(inst.Value, typeHint: targetType) - .ConvertTo(targetType, exprBuilder, allowImplicitConversion: true); + var expr = exprBuilder.Translate(inst.Value, typeHint: currentResultType) + .ConvertTo(currentResultType, exprBuilder, allowImplicitConversion: true); return new ReturnStatement(expr); } else return new ReturnStatement(); } - string label; - if (!endContainerLabels.TryGetValue(inst.TargetContainer, out label)) { + if (!endContainerLabels.TryGetValue(inst.TargetContainer, out string label)) { label = "end_" + inst.TargetLabel; endContainerLabels.Add(inst.TargetContainer, label); } @@ -412,14 +416,29 @@ namespace ICSharpCode.Decompiler.CSharp return transformed; AstNode usingInit = resource; var var = inst.Variable; - if (!inst.ResourceExpression.MatchLdNull() && !NullableType.GetUnderlyingType(var.Type).GetAllBaseTypes().Any(b => b.IsKnownType(KnownTypeCode.IDisposable))) { + KnownTypeCode knownTypeCode; + IType disposeType; + string disposeTypeMethodName; + if (inst.IsAsync) { + knownTypeCode = KnownTypeCode.IAsyncDisposable; + disposeType = exprBuilder.compilation.FindType(KnownTypeCode.IAsyncDisposable); + disposeTypeMethodName = "DisposeAsync"; + } else { + knownTypeCode = KnownTypeCode.IDisposable; + disposeType = exprBuilder.compilation.FindType(KnownTypeCode.IDisposable); + disposeTypeMethodName = "Dispose"; + } + if (!inst.ResourceExpression.MatchLdNull() && !NullableType.GetUnderlyingType(var.Type).GetAllBaseTypes().Any(b => b.IsKnownType(knownTypeCode))) { Debug.Assert(var.Kind == VariableKind.UsingLocal); var.Kind = VariableKind.Local; - var disposeType = exprBuilder.compilation.FindType(KnownTypeCode.IDisposable); var disposeVariable = currentFunction.RegisterVariable( VariableKind.Local, disposeType, AssignVariableNames.GenerateVariableName(currentFunction, disposeType) ); + Expression disposeInvocation = new InvocationExpression(new MemberReferenceExpression(exprBuilder.ConvertVariable(disposeVariable).Expression, disposeTypeMethodName)); + if (inst.IsAsync) { + disposeInvocation = new UnaryOperatorExpression { Expression = disposeInvocation, Operator = UnaryOperatorType.Await }; + } return new BlockStatement { new ExpressionStatement(new AssignmentExpression(exprBuilder.ConvertVariable(var).Expression, resource.Detach())), new TryCatchStatement { @@ -428,7 +447,7 @@ namespace ICSharpCode.Decompiler.CSharp new ExpressionStatement(new AssignmentExpression(exprBuilder.ConvertVariable(disposeVariable).Expression, new AsExpression(exprBuilder.ConvertVariable(var).Expression, exprBuilder.ConvertType(disposeType)))), new IfElseStatement { Condition = new BinaryOperatorExpression(exprBuilder.ConvertVariable(disposeVariable), BinaryOperatorType.InEquality, new NullReferenceExpression()), - TrueStatement = new ExpressionStatement(new InvocationExpression(new MemberReferenceExpression(exprBuilder.ConvertVariable(disposeVariable).Expression, "Dispose"))) + TrueStatement = new ExpressionStatement(disposeInvocation) } } }, @@ -442,6 +461,7 @@ namespace ICSharpCode.Decompiler.CSharp } return new UsingStatement { ResourceAcquisition = usingInit, + IsAsync = inst.IsAsync, EmbeddedStatement = ConvertAsBlock(inst.Body) }; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs b/ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs index b12334470..1d0e9e685 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs @@ -38,11 +38,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public abstract class AstNode : AbstractAnnotatable, IFreezable, INode, ICloneable { // the Root role must be available when creating the null nodes, so we can't put it in the Roles class - internal static readonly Role RootRole = new Role ("Root"); - + internal static readonly Role RootRole = new Role("Root"); + #region Null - public static readonly AstNode Null = new NullAstNode (); - + public static readonly AstNode Null = new NullAstNode(); + sealed class NullAstNode : AstNode { public override NodeType NodeType { @@ -50,108 +50,108 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return NodeType.Unknown; } } - + public override bool IsNull { get { return true; } } - - public override void AcceptVisitor (IAstVisitor visitor) + + public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitNullNode(this); } - - public override T AcceptVisitor (IAstVisitor visitor) + + public override T AcceptVisitor(IAstVisitor visitor) { return visitor.VisitNullNode(this); } - - public override S AcceptVisitor (IAstVisitor visitor, T data) + + public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitNullNode(this, data); } - - protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { return other == null || other.IsNull; } } #endregion - + #region PatternPlaceholder - public static implicit operator AstNode (PatternMatching.Pattern pattern) + public static implicit operator AstNode(PatternMatching.Pattern pattern) { - return pattern != null ? new PatternPlaceholder (pattern) : null; + return pattern != null ? new PatternPlaceholder(pattern) : null; } - + sealed class PatternPlaceholder : AstNode, INode { readonly PatternMatching.Pattern child; - - public PatternPlaceholder (PatternMatching.Pattern child) + + public PatternPlaceholder(PatternMatching.Pattern child) { this.child = child; } - + public override NodeType NodeType { get { return NodeType.Pattern; } } - - public override void AcceptVisitor (IAstVisitor visitor) + + public override void AcceptVisitor(IAstVisitor visitor) { - visitor.VisitPatternPlaceholder (this, child); + visitor.VisitPatternPlaceholder(this, child); } - - public override T AcceptVisitor (IAstVisitor visitor) + + public override T AcceptVisitor(IAstVisitor visitor) { - return visitor.VisitPatternPlaceholder (this, child); + return visitor.VisitPatternPlaceholder(this, child); } - public override S AcceptVisitor (IAstVisitor visitor, T data) + public override S AcceptVisitor(IAstVisitor visitor, T data) { - return visitor.VisitPatternPlaceholder (this, child, data); + return visitor.VisitPatternPlaceholder(this, child, data); } - - protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { - return child.DoMatch (other, match); + return child.DoMatch(other, match); } - - bool PatternMatching.INode.DoMatchCollection (Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) { - return child.DoMatchCollection (role, pos, match, backtrackingInfo); + return child.DoMatchCollection(role, pos, match, backtrackingInfo); } } #endregion - + AstNode parent; AstNode prevSibling; AstNode nextSibling; AstNode firstChild; AstNode lastChild; - + // Flags, from least significant to most significant bits: // - Role.RoleIndexBits: role index // - 1 bit: IsFrozen protected uint flags = RootRole.Index; // Derived classes may also use a few bits, // for example Identifier uses 1 bit for IsVerbatim - + const uint roleIndexMask = (1u << Role.RoleIndexBits) - 1; const uint frozenBit = 1u << Role.RoleIndexBits; protected const int AstNodeFlagsUsedBits = Role.RoleIndexBits + 1; - + protected AstNode() { if (IsNull) Freeze(); } - + public bool IsFrozen { get { return (flags & frozenBit) != 0; } } - + public void Freeze() { if (!IsFrozen) { @@ -160,23 +160,23 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax flags |= frozenBit; } } - + protected void ThrowIfFrozen() { if (IsFrozen) throw new InvalidOperationException("Cannot mutate frozen " + GetType().Name); } - + public abstract NodeType NodeType { get; } - + public virtual bool IsNull { get { return false; } } - + public virtual TextLocation StartLocation { get { var child = firstChild; @@ -185,7 +185,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return child.StartLocation; } } - + public virtual TextLocation EndLocation { get { var child = lastChild; @@ -194,61 +194,61 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return child.EndLocation; } } - + public AstNode Parent { get { return parent; } } - + public Role Role { get { return Role.GetByIndex(flags & roleIndexMask); } set { if (value == null) - throw new ArgumentNullException("value"); + throw new ArgumentNullException(nameof(value)); if (!value.IsValid(this)) throw new ArgumentException("This node is not valid in the new role."); ThrowIfFrozen(); SetRole(value); } } - + internal uint RoleIndex { get { return flags & roleIndexMask; } } - + void SetRole(Role role) { flags = (flags & ~roleIndexMask) | role.Index; } - + public AstNode NextSibling { get { return nextSibling; } } - + public AstNode PrevSibling { get { return prevSibling; } } - + public AstNode FirstChild { get { return firstChild; } } - + public AstNode LastChild { get { return lastChild; } } - + public bool HasChildren { get { return firstChild != null; } } - + public IEnumerable Children { get { AstNode next; for (AstNode cur = firstChild; cur != null; cur = next) { - Debug.Assert (cur.parent == this); + Debug.Assert(cur.parent == this); // Remember next before yielding cur. // This allows removing/replacing nodes while iterating through the list. next = cur.nextSibling; @@ -256,7 +256,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } } } - + /// /// Gets the ancestors of this node (excluding this node itself) /// @@ -267,7 +267,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } } } - + /// /// Gets the ancestors of this node (including this node itself) /// @@ -278,31 +278,31 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } } } - + /// /// Gets all descendants of this node (excluding this node itself) in pre-order. /// public IEnumerable Descendants { get { return GetDescendantsImpl(false); } } - + /// /// Gets all descendants of this node (including this node itself) in pre-order. /// public IEnumerable DescendantsAndSelf { get { return GetDescendantsImpl(true); } } - - public IEnumerable DescendantNodes (Func descendIntoChildren = null) + + public IEnumerable DescendantNodes(Func descendIntoChildren = null) { return GetDescendantsImpl(false, descendIntoChildren); } - - public IEnumerable DescendantNodesAndSelf (Func descendIntoChildren = null) + + public IEnumerable DescendantNodesAndSelf(Func descendIntoChildren = null) { return GetDescendantsImpl(true, descendIntoChildren); } - + IEnumerable GetDescendantsImpl(bool includeSelf, Func descendIntoChildren = null) { @@ -327,7 +327,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax pos = nextStack.Pop(); } } - + /// /// Gets the first child with the specified role. /// Returns the role's null object if the child is not found. @@ -335,7 +335,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public T GetChildByRole(Role role) where T : AstNode { if (role == null) - throw new ArgumentNullException ("role"); + throw new ArgumentNullException(nameof(role)); uint roleIndex = role.Index; for (var cur = firstChild; cur != null; cur = cur.nextSibling) { if ((cur.flags & roleIndexMask) == roleIndex) @@ -343,7 +343,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } return role.NullObject; } - + public T GetParent() where T : AstNode { return Ancestors.OfType().FirstOrDefault(); @@ -354,54 +354,54 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return Ancestors.FirstOrDefault(pred); } - public AstNodeCollection GetChildrenByRole (Role role) where T : AstNode + public AstNodeCollection GetChildrenByRole(Role role) where T : AstNode { - return new AstNodeCollection (this, role); + return new AstNodeCollection(this, role); } - - protected void SetChildByRole (Role role, T newChild) where T : AstNode + + protected void SetChildByRole(Role role, T newChild) where T : AstNode { - AstNode oldChild = GetChildByRole (role); + AstNode oldChild = GetChildByRole(role); if (oldChild.IsNull) - AddChild (newChild, role); + AddChild(newChild, role); else - oldChild.ReplaceWith (newChild); + oldChild.ReplaceWith(newChild); } - - public void AddChild (T child, Role role) where T : AstNode + + public void AddChild(T child, Role role) where T : AstNode { if (role == null) - throw new ArgumentNullException ("role"); + throw new ArgumentNullException(nameof(role)); if (child == null || child.IsNull) return; ThrowIfFrozen(); if (child == this) - throw new ArgumentException ("Cannot add a node to itself as a child.", "child"); + throw new ArgumentException("Cannot add a node to itself as a child.", nameof(child)); if (child.parent != null) - throw new ArgumentException ("Node is already used in another tree.", "child"); + throw new ArgumentException("Node is already used in another tree.", nameof(child)); if (child.IsFrozen) - throw new ArgumentException ("Cannot add a frozen node.", "child"); - AddChildUnsafe (child, role); + throw new ArgumentException("Cannot add a frozen node.", nameof(child)); + AddChildUnsafe(child, role); } - - public void AddChildWithExistingRole (AstNode child) + + public void AddChildWithExistingRole(AstNode child) { if (child == null || child.IsNull) return; ThrowIfFrozen(); if (child == this) - throw new ArgumentException ("Cannot add a node to itself as a child.", "child"); + throw new ArgumentException("Cannot add a node to itself as a child.", nameof(child)); if (child.parent != null) - throw new ArgumentException ("Node is already used in another tree.", "child"); + throw new ArgumentException("Node is already used in another tree.", nameof(child)); if (child.IsFrozen) - throw new ArgumentException ("Cannot add a frozen node.", "child"); - AddChildUnsafe (child, child.Role); + throw new ArgumentException("Cannot add a frozen node.", nameof(child)); + AddChildUnsafe(child, child.Role); } - + /// /// Adds a child without performing any safety checks. /// - internal void AddChildUnsafe (AstNode child, Role role) + internal void AddChildUnsafe(AstNode child, Role role) { child.parent = this; child.SetRole(role); @@ -414,70 +414,70 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } } - public void InsertChildBefore (AstNode nextSibling, T child, Role role) where T : AstNode + public void InsertChildBefore(AstNode nextSibling, T child, Role role) where T : AstNode { if (role == null) - throw new ArgumentNullException ("role"); + throw new ArgumentNullException(nameof(role)); if (nextSibling == null || nextSibling.IsNull) { - AddChild (child, role); + AddChild(child, role); return; } - + if (child == null || child.IsNull) return; ThrowIfFrozen(); if (child.parent != null) - throw new ArgumentException ("Node is already used in another tree.", "child"); + throw new ArgumentException("Node is already used in another tree.", nameof(child)); if (child.IsFrozen) - throw new ArgumentException ("Cannot add a frozen node.", "child"); + throw new ArgumentException("Cannot add a frozen node.", nameof(child)); if (nextSibling.parent != this) - throw new ArgumentException ("NextSibling is not a child of this node.", "nextSibling"); + throw new ArgumentException("NextSibling is not a child of this node.", nameof(nextSibling)); // No need to test for "Cannot add children to null nodes", // as there isn't any valid nextSibling in null nodes. - InsertChildBeforeUnsafe (nextSibling, child, role); + InsertChildBeforeUnsafe(nextSibling, child, role); } - - internal void InsertChildBeforeUnsafe (AstNode nextSibling, AstNode child, Role role) + + internal void InsertChildBeforeUnsafe(AstNode nextSibling, AstNode child, Role role) { child.parent = this; child.SetRole(role); child.nextSibling = nextSibling; child.prevSibling = nextSibling.prevSibling; - + if (nextSibling.prevSibling != null) { - Debug.Assert (nextSibling.prevSibling.nextSibling == nextSibling); + Debug.Assert(nextSibling.prevSibling.nextSibling == nextSibling); nextSibling.prevSibling.nextSibling = child; } else { - Debug.Assert (firstChild == nextSibling); + Debug.Assert(firstChild == nextSibling); firstChild = child; } nextSibling.prevSibling = child; } - - public void InsertChildAfter (AstNode prevSibling, T child, Role role) where T : AstNode + + public void InsertChildAfter(AstNode prevSibling, T child, Role role) where T : AstNode { - InsertChildBefore ((prevSibling == null || prevSibling.IsNull) ? firstChild : prevSibling.nextSibling, child, role); + InsertChildBefore((prevSibling == null || prevSibling.IsNull) ? firstChild : prevSibling.nextSibling, child, role); } - + /// /// Removes this node from its parent. /// - public void Remove () + public void Remove() { if (parent != null) { ThrowIfFrozen(); if (prevSibling != null) { - Debug.Assert (prevSibling.nextSibling == this); + Debug.Assert(prevSibling.nextSibling == this); prevSibling.nextSibling = nextSibling; } else { - Debug.Assert (parent.firstChild == this); + Debug.Assert(parent.firstChild == this); parent.firstChild = nextSibling; } if (nextSibling != null) { - Debug.Assert (nextSibling.prevSibling == this); + Debug.Assert(nextSibling.prevSibling == this); nextSibling.prevSibling = prevSibling; } else { - Debug.Assert (parent.lastChild == this); + Debug.Assert(parent.lastChild == this); parent.lastChild = prevSibling; } parent = null; @@ -485,100 +485,100 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax nextSibling = null; } } - + /// /// Replaces this node with the new node. /// - public void ReplaceWith (AstNode newNode) + public void ReplaceWith(AstNode newNode) { if (newNode == null || newNode.IsNull) { - Remove (); + Remove(); return; } if (newNode == this) return; // nothing to do... if (parent == null) { - throw new InvalidOperationException (this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node"); + throw new InvalidOperationException(this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node"); } ThrowIfFrozen(); // Because this method doesn't statically check the new node's type with the role, // we perform a runtime test: - if (!this.Role.IsValid (newNode)) { - throw new ArgumentException (string.Format ("The new node '{0}' is not valid in the role {1}", newNode.GetType ().Name, this.Role.ToString ()), "newNode"); + if (!this.Role.IsValid(newNode)) { + throw new ArgumentException(string.Format("The new node '{0}' is not valid in the role {1}", newNode.GetType().Name, this.Role.ToString()), nameof(newNode)); } if (newNode.parent != null) { // newNode is used within this tree? - if (newNode.Ancestors.Contains (this)) { + if (newNode.Ancestors.Contains(this)) { // e.g. "parenthesizedExpr.ReplaceWith(parenthesizedExpr.Expression);" // enable automatic removal - newNode.Remove (); + newNode.Remove(); } else { - throw new ArgumentException ("Node is already used in another tree.", "newNode"); + throw new ArgumentException("Node is already used in another tree.", nameof(newNode)); } } if (newNode.IsFrozen) - throw new ArgumentException ("Cannot add a frozen node.", "newNode"); - + throw new ArgumentException("Cannot add a frozen node.", nameof(newNode)); + newNode.parent = parent; newNode.SetRole(this.Role); newNode.prevSibling = prevSibling; newNode.nextSibling = nextSibling; if (prevSibling != null) { - Debug.Assert (prevSibling.nextSibling == this); + Debug.Assert(prevSibling.nextSibling == this); prevSibling.nextSibling = newNode; } else { - Debug.Assert (parent.firstChild == this); + Debug.Assert(parent.firstChild == this); parent.firstChild = newNode; } if (nextSibling != null) { - Debug.Assert (nextSibling.prevSibling == this); + Debug.Assert(nextSibling.prevSibling == this); nextSibling.prevSibling = newNode; } else { - Debug.Assert (parent.lastChild == this); + Debug.Assert(parent.lastChild == this); parent.lastChild = newNode; } parent = null; prevSibling = null; nextSibling = null; } - - public AstNode ReplaceWith (Func replaceFunction) + + public AstNode ReplaceWith(Func replaceFunction) { if (replaceFunction == null) - throw new ArgumentNullException ("replaceFunction"); + throw new ArgumentNullException(nameof(replaceFunction)); if (parent == null) { - throw new InvalidOperationException (this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node"); + throw new InvalidOperationException(this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node"); } AstNode oldParent = parent; AstNode oldSuccessor = nextSibling; Role oldRole = this.Role; - Remove (); - AstNode replacement = replaceFunction (this); + Remove(); + AstNode replacement = replaceFunction(this); if (oldSuccessor != null && oldSuccessor.parent != oldParent) - throw new InvalidOperationException ("replace function changed nextSibling of node being replaced?"); + throw new InvalidOperationException("replace function changed nextSibling of node being replaced?"); if (!(replacement == null || replacement.IsNull)) { if (replacement.parent != null) - throw new InvalidOperationException ("replace function must return the root of a tree"); - if (!oldRole.IsValid (replacement)) { - throw new InvalidOperationException (string.Format ("The new node '{0}' is not valid in the role {1}", replacement.GetType ().Name, oldRole.ToString ())); + throw new InvalidOperationException("replace function must return the root of a tree"); + if (!oldRole.IsValid(replacement)) { + throw new InvalidOperationException(string.Format("The new node '{0}' is not valid in the role {1}", replacement.GetType().Name, oldRole.ToString())); } - + if (oldSuccessor != null) - oldParent.InsertChildBeforeUnsafe (oldSuccessor, replacement, oldRole); + oldParent.InsertChildBeforeUnsafe(oldSuccessor, replacement, oldRole); else - oldParent.AddChildUnsafe (replacement, oldRole); + oldParent.AddChildUnsafe(replacement, oldRole); } return replacement; } - + /// /// Clones the whole subtree starting at this AST node. /// /// Annotations are copied over to the new nodes; and any annotations implementing ICloneable will be cloned. - public AstNode Clone () + public AstNode Clone() { - AstNode copy = (AstNode)MemberwiseClone (); + AstNode copy = (AstNode)MemberwiseClone(); // First, reset the shallow pointer copies copy.parent = null; copy.firstChild = null; @@ -586,66 +586,66 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax copy.prevSibling = null; copy.nextSibling = null; copy.flags &= ~frozenBit; // unfreeze the copy - + // Then perform a deep copy: for (AstNode cur = firstChild; cur != null; cur = cur.nextSibling) { - copy.AddChildUnsafe (cur.Clone (), cur.Role); + copy.AddChildUnsafe(cur.Clone(), cur.Role); } - + // Finally, clone the annotation, if necessary copy.CloneAnnotations(); - + return copy; } - + object ICloneable.Clone() { return Clone(); } - - public abstract void AcceptVisitor (IAstVisitor visitor); - - public abstract T AcceptVisitor (IAstVisitor visitor); - - public abstract S AcceptVisitor (IAstVisitor visitor, T data); - + + public abstract void AcceptVisitor(IAstVisitor visitor); + + public abstract T AcceptVisitor(IAstVisitor visitor); + + public abstract S AcceptVisitor(IAstVisitor visitor, T data); + #region Pattern Matching - protected static bool MatchString (string pattern, string text) + protected static bool MatchString(string pattern, string text) { return PatternMatching.Pattern.MatchString(pattern, text); } - - protected internal abstract bool DoMatch (AstNode other, PatternMatching.Match match); - - bool PatternMatching.INode.DoMatch (PatternMatching.INode other, PatternMatching.Match match) + + protected internal abstract bool DoMatch(AstNode other, PatternMatching.Match match); + + bool PatternMatching.INode.DoMatch(PatternMatching.INode other, PatternMatching.Match match) { AstNode o = other as AstNode; // try matching if other is null, or if other is an AstNode - return (other == null || o != null) && DoMatch (o, match); + return (other == null || o != null) && DoMatch(o, match); } - - bool PatternMatching.INode.DoMatchCollection (Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) { AstNode o = pos as AstNode; - return (pos == null || o != null) && DoMatch (o, match); + return (pos == null || o != null) && DoMatch(o, match); } - + PatternMatching.INode PatternMatching.INode.NextSibling { get { return nextSibling; } } - + PatternMatching.INode PatternMatching.INode.FirstChild { get { return firstChild; } } #endregion - - public AstNode GetNextNode () + + public AstNode GetNextNode() { if (NextSibling != null) return NextSibling; if (Parent != null) - return Parent.GetNextNode (); + return Parent.GetNextNode(); return null; } @@ -654,20 +654,20 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// /// The next node. /// The predicate. - public AstNode GetNextNode (Func pred) + public AstNode GetNextNode(Func pred) { var next = GetNextNode(); - while (next != null && !pred (next)) + while (next != null && !pred(next)) next = next.GetNextNode(); return next; } - public AstNode GetPrevNode () + public AstNode GetPrevNode() { if (PrevSibling != null) return PrevSibling; if (Parent != null) - return Parent.GetPrevNode (); + return Parent.GetPrevNode(); return null; } @@ -676,21 +676,21 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// /// The next node. /// The predicate. - public AstNode GetPrevNode (Func pred) + public AstNode GetPrevNode(Func pred) { var prev = GetPrevNode(); - while (prev != null && !pred (prev)) + while (prev != null && !pred(prev)) prev = prev.GetPrevNode(); return prev; } // filters all non c# nodes (comments, white spaces or pre processor directives) - public AstNode GetCSharpNodeBefore (AstNode node) + public AstNode GetCSharpNodeBefore(AstNode node) { var n = node.PrevSibling; while (n != null) { if (n.Role != Roles.Comment) return n; - n = n.GetPrevNode (); + n = n.GetPrevNode(); } return null; } @@ -700,10 +700,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// /// The next node. /// The predicate. - public AstNode GetNextSibling (Func pred) + public AstNode GetNextSibling(Func pred) { var next = NextSibling; - while (next != null && !pred (next)) + while (next != null && !pred(next)) next = next.NextSibling; return next; } @@ -713,31 +713,31 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// /// The next node. /// The predicate. - public AstNode GetPrevSibling (Func pred) + public AstNode GetPrevSibling(Func pred) { var prev = PrevSibling; - while (prev != null && !pred (prev)) + while (prev != null && !pred(prev)) prev = prev.PrevSibling; return prev; } - + #region GetNodeAt /// /// Gets the node specified by T at the location line, column. This is useful for getting a specific node from the tree. For example searching /// the current method declaration. /// (End exclusive) /// - public AstNode GetNodeAt (int line, int column, Predicate pred = null) + public AstNode GetNodeAt(int line, int column, Predicate pred = null) { - return GetNodeAt (new TextLocation (line, column), pred); + return GetNodeAt(new TextLocation(line, column), pred); } - + /// /// Gets the node specified by pred at location. This is useful for getting a specific node from the tree. For example searching /// the current method declaration. /// (End exclusive) /// - public AstNode GetNodeAt (TextLocation location, Predicate pred = null) + public AstNode GetNodeAt(TextLocation location, Predicate pred = null) { AstNode result = null; AstNode node = this; @@ -746,7 +746,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax while (child != null && child.StartLocation > location) child = child.prevSibling; if (child != null && location < child.EndLocation) { - if (pred == null || pred (child)) + if (pred == null || pred(child)) result = child; node = child; } else { @@ -756,23 +756,23 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } return result; } - + /// /// Gets the node specified by T at the location line, column. This is useful for getting a specific node from the tree. For example searching /// the current method declaration. /// (End exclusive) /// - public T GetNodeAt (int line, int column) where T : AstNode + public T GetNodeAt(int line, int column) where T : AstNode { - return GetNodeAt (new TextLocation (line, column)); + return GetNodeAt(new TextLocation(line, column)); } - + /// /// Gets the node specified by T at location. This is useful for getting a specific node from the tree. For example searching /// the current method declaration. /// (End exclusive) /// - public T GetNodeAt (TextLocation location) where T : AstNode + public T GetNodeAt(TextLocation location) where T : AstNode { T result = null; AstNode node = this; @@ -802,15 +802,15 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// public AstNode GetAdjacentNodeAt(int line, int column, Predicate pred = null) { - return GetAdjacentNodeAt (new TextLocation (line, column), pred); + return GetAdjacentNodeAt(new TextLocation(line, column), pred); } - + /// /// Gets the node specified by pred at location. This is useful for getting a specific node from the tree. For example searching /// the current method declaration. /// (End inclusive) /// - public AstNode GetAdjacentNodeAt (TextLocation location, Predicate pred = null) + public AstNode GetAdjacentNodeAt(TextLocation location, Predicate pred = null) { AstNode result = null; AstNode node = this; @@ -819,7 +819,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax while (child != null && child.StartLocation > location) child = child.prevSibling; if (child != null && location <= child.EndLocation) { - if (pred == null || pred (child)) + if (pred == null || pred(child)) result = child; node = child; } else { @@ -829,7 +829,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } return result; } - + /// /// Gets the node specified by T at the location line, column. This is useful for getting a specific node from the tree. For example searching /// the current method declaration. @@ -837,15 +837,15 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// public T GetAdjacentNodeAt(int line, int column) where T : AstNode { - return GetAdjacentNodeAt (new TextLocation (line, column)); + return GetAdjacentNodeAt(new TextLocation(line, column)); } - + /// /// Gets the node specified by T at location. This is useful for getting a specific node from the tree. For example searching /// the current method declaration. /// (End inclusive) /// - public T GetAdjacentNodeAt (TextLocation location) where T : AstNode + public T GetAdjacentNodeAt(TextLocation location) where T : AstNode { T result = null; AstNode node = this; @@ -878,19 +878,19 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } return this; } - + /// /// Returns the root nodes of all subtrees that are fully contained in the specified region. /// - public IEnumerable GetNodesBetween (int startLine, int startColumn, int endLine, int endColumn) + public IEnumerable GetNodesBetween(int startLine, int startColumn, int endLine, int endColumn) { - return GetNodesBetween (new TextLocation (startLine, startColumn), new TextLocation (endLine, endColumn)); + return GetNodesBetween(new TextLocation(startLine, startColumn), new TextLocation(endLine, endColumn)); } - + /// /// Returns the root nodes of all subtrees that are fully contained between and (inclusive). /// - public IEnumerable GetNodesBetween (TextLocation start, TextLocation end) + public IEnumerable GetNodesBetween(TextLocation start, TextLocation end) { AstNode node = this; while (node != null) { @@ -907,7 +907,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax next = node.FirstChild; } } - + if (next != null && next.StartLocation > end) yield break; node = next; @@ -920,13 +920,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// /// Formatting options. /// - public virtual string ToString (CSharpFormattingOptions formattingOptions) + public virtual string ToString(CSharpFormattingOptions formattingOptions) { if (IsNull) return ""; - var w = new StringWriter (); - AcceptVisitor (new CSharpOutputVisitor (w, formattingOptions ?? FormattingOptionsFactory.CreateMono ())); - return w.ToString (); + var w = new StringWriter(); + AcceptVisitor(new CSharpOutputVisitor(w, formattingOptions ?? FormattingOptionsFactory.CreateMono())); + return w.ToString(); } public sealed override string ToString() @@ -940,51 +940,51 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// /// True, if the given coordinates are between StartLocation and EndLocation (exclusive); otherwise, false. /// - public bool Contains (int line, int column) + public bool Contains(int line, int column) { - return Contains (new TextLocation (line, column)); + return Contains(new TextLocation(line, column)); } - + /// /// Returns true, if the given coordinates are in the node. /// /// /// True, if location is between StartLocation and EndLocation (exclusive); otherwise, false. /// - public bool Contains (TextLocation location) + public bool Contains(TextLocation location) { return this.StartLocation <= location && location < this.EndLocation; } - + /// /// Returns true, if the given coordinates (line, column) are in the node. /// /// /// True, if the given coordinates are between StartLocation and EndLocation (inclusive); otherwise, false. /// - public bool IsInside (int line, int column) + public bool IsInside(int line, int column) { - return IsInside (new TextLocation (line, column)); + return IsInside(new TextLocation(line, column)); } - + /// /// Returns true, if the given coordinates are in the node. /// /// /// True, if location is between StartLocation and EndLocation (inclusive); otherwise, false. /// - public bool IsInside (TextLocation location) + public bool IsInside(TextLocation location) { return this.StartLocation <= location && location <= this.EndLocation; } - - public override void AddAnnotation (object annotation) + + public override void AddAnnotation(object annotation) { if (this.IsNull) - throw new InvalidOperationException ("Cannot add annotations to the null node"); - base.AddAnnotation (annotation); + throw new InvalidOperationException("Cannot add annotations to the null node"); + base.AddAnnotation(annotation); } - + internal string DebugToString() { if (IsNull) diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs b/ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs index 4355c4cbb..1a72042ee 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs @@ -37,9 +37,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public AstNodeCollection(AstNode node, Role role) { if (node == null) - throw new ArgumentNullException("node"); + throw new ArgumentNullException(nameof(node)); if (role == null) - throw new ArgumentNullException("role"); + throw new ArgumentNullException(nameof(role)); this.node = node; this.role = role; } @@ -96,7 +96,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public void MoveTo(ICollection targetCollection) { if (targetCollection == null) - throw new ArgumentNullException("targetCollection"); + throw new ArgumentNullException(nameof(targetCollection)); foreach (T node in this) { node.Remove(); targetCollection.Add(node); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/Expression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/Expression.cs index 388757459..4089fd7f8 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/Expression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/Expression.cs @@ -123,7 +123,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public Expression ReplaceWith(Func replaceFunction) { if (replaceFunction == null) - throw new ArgumentNullException("replaceFunction"); + throw new ArgumentNullException(nameof(replaceFunction)); return (Expression)base.ReplaceWith(node => replaceFunction((Expression)node)); } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs index 1ba5df668..db676a149 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs @@ -64,7 +64,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax static AstType ConstructType(string[] arr, int i) { if (i < 0 || i >= arr.Length) - throw new ArgumentOutOfRangeException("i"); + throw new ArgumentOutOfRangeException(nameof(i)); if (i == 0) return new SimpleType(arr[i]); return new MemberType(ConstructType(arr, i - 1), arr[i]); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/IAnnotatable.cs b/ICSharpCode.Decompiler/CSharp/Syntax/IAnnotatable.cs index 0eea3a140..e1a6b908c 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/IAnnotatable.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/IAnnotatable.cs @@ -34,7 +34,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax IEnumerable Annotations { get; } - + /// /// Gets the first annotation of the specified type. /// Returns null if no matching annotation exists. @@ -42,8 +42,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// /// The type of the annotation. /// - T Annotation () where T: class; - + T Annotation() where T : class; + /// /// Gets the first annotation of the specified type. /// Returns null if no matching annotation exists. @@ -51,24 +51,24 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// /// The type of the annotation. /// - object Annotation (Type type); - + object Annotation(Type type); + /// /// Adds an annotation to this instance. /// /// /// The annotation to add. /// - void AddAnnotation (object annotation); - + void AddAnnotation(object annotation); + /// /// Removes all annotations of the specified type. /// /// /// The type of the annotations to remove. /// - void RemoveAnnotations () where T : class; - + void RemoveAnnotations() where T : class; + /// /// Removes all annotations of the specified type. /// @@ -77,7 +77,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// void RemoveAnnotations(Type type); } - + /// /// Base class used to implement the IAnnotatable interface. /// This implementation is thread-safe. @@ -88,9 +88,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax // Annotations: points either null (no annotations), to the single annotation, // or to an AnnotationList. // Once it is pointed at an AnnotationList, it will never change (this allows thread-safety support by locking the list) - + object annotations; - + /// /// Clones all annotations. /// This method is intended to be called by Clone() implementations in derived classes. @@ -111,86 +111,86 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax // There are two uses for this custom list type: // 1) it's private, and thus (unlike List) cannot be confused with real annotations // 2) It allows us to simplify the cloning logic by making the list behave the same as a clonable annotation. - public AnnotationList (int initialCapacity) : base(initialCapacity) + public AnnotationList(int initialCapacity) : base(initialCapacity) { } - - public object Clone () + + public object Clone() { lock (this) { - AnnotationList copy = new AnnotationList (this.Count); + AnnotationList copy = new AnnotationList(this.Count); for (int i = 0; i < this.Count; i++) { - object obj = this [i]; + object obj = this[i]; ICloneable c = obj as ICloneable; - copy.Add (c != null ? c.Clone () : obj); + copy.Add(c != null ? c.Clone() : obj); } return copy; } } } - - public virtual void AddAnnotation (object annotation) + + public virtual void AddAnnotation(object annotation) { if (annotation == null) - throw new ArgumentNullException ("annotation"); + throw new ArgumentNullException(nameof(annotation)); retry: // Retry until successful - object oldAnnotation = Interlocked.CompareExchange (ref this.annotations, annotation, null); + object oldAnnotation = Interlocked.CompareExchange(ref this.annotations, annotation, null); if (oldAnnotation == null) { return; // we successfully added a single annotation } AnnotationList list = oldAnnotation as AnnotationList; if (list == null) { // we need to transform the old annotation into a list - list = new AnnotationList (4); - list.Add (oldAnnotation); - list.Add (annotation); - if (Interlocked.CompareExchange (ref this.annotations, list, oldAnnotation) != oldAnnotation) { + list = new AnnotationList(4); + list.Add(oldAnnotation); + list.Add(annotation); + if (Interlocked.CompareExchange(ref this.annotations, list, oldAnnotation) != oldAnnotation) { // the transformation failed (some other thread wrote to this.annotations first) goto retry; } } else { // once there's a list, use simple locking lock (list) { - list.Add (annotation); + list.Add(annotation); } } } - - public virtual void RemoveAnnotations () where T : class + + public virtual void RemoveAnnotations() where T : class { - retry: // Retry until successful + retry: // Retry until successful object oldAnnotations = this.annotations; AnnotationList list = oldAnnotations as AnnotationList; if (list != null) { lock (list) - list.RemoveAll (obj => obj is T); + list.RemoveAll(obj => obj is T); } else if (oldAnnotations is T) { - if (Interlocked.CompareExchange (ref this.annotations, null, oldAnnotations) != oldAnnotations) { + if (Interlocked.CompareExchange(ref this.annotations, null, oldAnnotations) != oldAnnotations) { // Operation failed (some other thread wrote to this.annotations first) goto retry; } } } - - public virtual void RemoveAnnotations (Type type) + + public virtual void RemoveAnnotations(Type type) { if (type == null) - throw new ArgumentNullException ("type"); - retry: // Retry until successful + throw new ArgumentNullException(nameof(type)); + retry: // Retry until successful object oldAnnotations = this.annotations; AnnotationList list = oldAnnotations as AnnotationList; if (list != null) { lock (list) list.RemoveAll(type.IsInstanceOfType); - } else if (type.IsInstanceOfType (oldAnnotations)) { - if (Interlocked.CompareExchange (ref this.annotations, null, oldAnnotations) != oldAnnotations) { + } else if (type.IsInstanceOfType(oldAnnotations)) { + if (Interlocked.CompareExchange(ref this.annotations, null, oldAnnotations) != oldAnnotations) { // Operation failed (some other thread wrote to this.annotations first) goto retry; } } } - - public T Annotation () where T: class + + public T Annotation() where T : class { object annotations = this.annotations; AnnotationList list = annotations as AnnotationList; @@ -207,27 +207,27 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return annotations as T; } } - - public object Annotation (Type type) + + public object Annotation(Type type) { if (type == null) - throw new ArgumentNullException ("type"); + throw new ArgumentNullException(nameof(type)); object annotations = this.annotations; AnnotationList list = annotations as AnnotationList; if (list != null) { lock (list) { foreach (object obj in list) { - if (type.IsInstanceOfType (obj)) + if (type.IsInstanceOfType(obj)) return obj; } } } else { - if (type.IsInstanceOfType (annotations)) + if (type.IsInstanceOfType(annotations)) return annotations; } return null; } - + /// /// Gets all annotations stored on this AstNode. /// @@ -237,13 +237,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax AnnotationList list = annotations as AnnotationList; if (list != null) { lock (list) { - return list.ToArray (); + return list.ToArray(); } } else { if (annotations != null) return new object[] { annotations }; else - return Enumerable.Empty (); + return Enumerable.Empty(); } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Identifier.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Identifier.cs index 256c22201..bbadecc74 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Identifier.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Identifier.cs @@ -71,7 +71,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax get { return this.name; } set { if (value == null) - throw new ArgumentNullException("value"); + throw new ArgumentNullException(nameof(value)); ThrowIfFrozen(); this.name = value; } @@ -119,7 +119,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax protected Identifier (string name, TextLocation location) { if (name == null) - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); this.Name = name; this.startLocation = location; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/IdentifierExpressionBackreference.cs b/ICSharpCode.Decompiler/CSharp/Syntax/IdentifierExpressionBackreference.cs index 856d39dc0..219fda00f 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/IdentifierExpressionBackreference.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/IdentifierExpressionBackreference.cs @@ -35,7 +35,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public IdentifierExpressionBackreference(string referencedGroupName) { if (referencedGroupName == null) - throw new ArgumentNullException("referencedGroupName"); + throw new ArgumentNullException(nameof(referencedGroupName)); this.referencedGroupName = referencedGroupName; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Backreference.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Backreference.cs index d3d8c3e93..76ff33aa4 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Backreference.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Backreference.cs @@ -35,7 +35,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching public Backreference(string referencedGroupName) { if (referencedGroupName == null) - throw new ArgumentNullException("referencedGroupName"); + throw new ArgumentNullException(nameof(referencedGroupName)); this.referencedGroupName = referencedGroupName; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Choice.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Choice.cs index 95d0c69ca..af322cfd3 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Choice.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Choice.cs @@ -32,14 +32,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching public void Add(string name, INode alternative) { if (alternative == null) - throw new ArgumentNullException("alternative"); + throw new ArgumentNullException(nameof(alternative)); alternatives.Add(new NamedNode(name, alternative)); } public void Add(INode alternative) { if (alternative == null) - throw new ArgumentNullException("alternative"); + throw new ArgumentNullException(nameof(alternative)); alternatives.Add(alternative); } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/INode.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/INode.cs index 3eab26de3..dc93e371d 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/INode.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/INode.cs @@ -51,7 +51,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching public static Match Match(this INode pattern, INode other) { if (pattern == null) - throw new ArgumentNullException("pattern"); + throw new ArgumentNullException(nameof(pattern)); Match match = PatternMatching.Match.CreateNew(); if (pattern.DoMatch(other, match)) return match; @@ -62,7 +62,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching public static bool IsMatch(this INode pattern, INode other) { if (pattern == null) - throw new ArgumentNullException("pattern"); + throw new ArgumentNullException(nameof(pattern)); return pattern.DoMatch(other, PatternMatching.Match.CreateNew()); } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/NamedNode.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/NamedNode.cs index aab6cf4e4..95e4f48a4 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/NamedNode.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/NamedNode.cs @@ -39,7 +39,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching public NamedNode(string groupName, INode childNode) { if (childNode == null) - throw new ArgumentNullException("childNode"); + throw new ArgumentNullException(nameof(childNode)); this.groupName = groupName; this.childNode = childNode; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/OptionalNode.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/OptionalNode.cs index 871ad489c..f4f3af44f 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/OptionalNode.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/OptionalNode.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching public OptionalNode(INode childNode) { if (childNode == null) - throw new ArgumentNullException("childNode"); + throw new ArgumentNullException(nameof(childNode)); this.childNode = childNode; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Repeat.cs b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Repeat.cs index fdb9258b7..2feaa9966 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Repeat.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/PatternMatching/Repeat.cs @@ -38,7 +38,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching public Repeat(INode childNode) { if (childNode == null) - throw new ArgumentNullException("childNode"); + throw new ArgumentNullException(nameof(childNode)); this.childNode = childNode; this.MinCount = 0; this.MaxCount = int.MaxValue; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Role.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Role.cs index 241198bd8..930537b4b 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Role.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Role.cs @@ -89,16 +89,16 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public Role(string name) { if (name == null) - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); this.name = name; } public Role(string name, T nullObject) { if (name == null) - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); if (nullObject == null) - throw new ArgumentNullException ("nullObject"); + throw new ArgumentNullException (nameof(nullObject)); this.nullObject = nullObject; this.name = name; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/Statement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/Statement.cs index 58b004f96..5ffab0f7f 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/Statement.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/Statement.cs @@ -116,7 +116,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public Statement ReplaceWith(Func replaceFunction) { if (replaceFunction == null) - throw new ArgumentNullException("replaceFunction"); + throw new ArgumentNullException(nameof(replaceFunction)); return (Statement)base.ReplaceWith(node => replaceFunction((Statement)node)); } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/UsingStatement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/UsingStatement.cs index 814819e8d..b8ad576a8 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/UsingStatement.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/UsingStatement.cs @@ -28,57 +28,67 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { /// - /// using (ResourceAcquisition) EmbeddedStatement + /// [ await ] using (ResourceAcquisition) EmbeddedStatement /// public class UsingStatement : Statement { - public static readonly TokenRole UsingKeywordRole = new TokenRole ("using"); + public static readonly TokenRole UsingKeywordRole = new TokenRole("using"); + public static readonly TokenRole AwaitRole = UnaryOperatorExpression.AwaitRole; public static readonly Role ResourceAcquisitionRole = new Role("ResourceAcquisition", AstNode.Null); - + public CSharpTokenNode UsingToken { - get { return GetChildByRole (UsingKeywordRole); } + get { return GetChildByRole(UsingKeywordRole); } + } + + public CSharpTokenNode AwaitToken { + get { return GetChildByRole(AwaitRole); } } - + + public bool IsAsync { + get { return !GetChildByRole(AwaitRole).IsNull; } + set { SetChildByRole(AwaitRole, value ? new CSharpTokenNode(TextLocation.Empty, null) : null); } + } + public CSharpTokenNode LParToken { - get { return GetChildByRole (Roles.LPar); } + get { return GetChildByRole(Roles.LPar); } } - + /// /// Either a VariableDeclarationStatement, or an Expression. /// public AstNode ResourceAcquisition { - get { return GetChildByRole (ResourceAcquisitionRole); } - set { SetChildByRole (ResourceAcquisitionRole, value); } + get { return GetChildByRole(ResourceAcquisitionRole); } + set { SetChildByRole(ResourceAcquisitionRole, value); } } - + public CSharpTokenNode RParToken { - get { return GetChildByRole (Roles.RPar); } + get { return GetChildByRole(Roles.RPar); } } - + public Statement EmbeddedStatement { - get { return GetChildByRole (Roles.EmbeddedStatement); } - set { SetChildByRole (Roles.EmbeddedStatement, value); } + get { return GetChildByRole(Roles.EmbeddedStatement); } + set { SetChildByRole(Roles.EmbeddedStatement, value); } } - - public override void AcceptVisitor (IAstVisitor visitor) + + public override void AcceptVisitor(IAstVisitor visitor) { - visitor.VisitUsingStatement (this); + visitor.VisitUsingStatement(this); } - - public override T AcceptVisitor (IAstVisitor visitor) + + public override T AcceptVisitor(IAstVisitor visitor) { - return visitor.VisitUsingStatement (this); + return visitor.VisitUsingStatement(this); } - - public override S AcceptVisitor (IAstVisitor visitor, T data) + + public override S AcceptVisitor(IAstVisitor visitor, T data) { - return visitor.VisitUsingStatement (this, data); + return visitor.VisitUsingStatement(this, data); } - + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { UsingStatement o = other as UsingStatement; - return o != null && this.ResourceAcquisition.DoMatch(o.ResourceAcquisition, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + return o != null && this.IsAsync == o.IsAsync && this.ResourceAcquisition.DoMatch(o.ResourceAcquisition, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index d99b4b4b1..a4a77358d 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -47,7 +47,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public TypeSystemAstBuilder(CSharpResolver resolver) { if (resolver == null) - throw new ArgumentNullException("resolver"); + throw new ArgumentNullException(nameof(resolver)); this.resolver = resolver; InitProperties(); } @@ -204,7 +204,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public AstType ConvertType(IType type) { if (type == null) - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); AstType astType = ConvertTypeHelper(type); AddTypeAnnotation(astType, type); return astType; @@ -579,7 +579,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public AstType ConvertAttributeType(IType type) { if (type == null) - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); AstType astType = ConvertTypeHelper(type); string shortName = null; @@ -663,7 +663,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public Expression ConvertConstantValue(ResolveResult rr) { if (rr == null) - throw new ArgumentNullException("rr"); + throw new ArgumentNullException(nameof(rr)); bool isBoxing = false; if (rr is ConversionResolveResult crr) { // unpack ConversionResolveResult if necessary @@ -734,7 +734,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public Expression ConvertConstantValue(IType expectedType, IType type, object constantValue) { if (type == null) - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); if (constantValue == null) { if (type.IsReferenceType == true || type.IsKnownType(KnownTypeCode.NullableOfT)) { var expr = new NullReferenceExpression(); @@ -1248,7 +1248,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public ParameterDeclaration ConvertParameter(IParameter parameter) { if (parameter == null) - throw new ArgumentNullException("parameter"); + throw new ArgumentNullException(nameof(parameter)); ParameterDeclaration decl = new ParameterDeclaration(); if (parameter.IsRef) { decl.ParameterModifier = ParameterModifier.Ref; @@ -1286,7 +1286,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public AstNode ConvertSymbol(ISymbol symbol) { if (symbol == null) - throw new ArgumentNullException("symbol"); + throw new ArgumentNullException(nameof(symbol)); switch (symbol.SymbolKind) { case SymbolKind.Namespace: return ConvertNamespaceDeclaration((INamespace)symbol); @@ -1307,7 +1307,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public EntityDeclaration ConvertEntity(IEntity entity) { if (entity == null) - throw new ArgumentNullException("entity"); + throw new ArgumentNullException(nameof(entity)); switch (entity.SymbolKind) { case SymbolKind.TypeDefinition: return ConvertTypeDefinition((ITypeDefinition)entity); @@ -1772,7 +1772,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } else { var declaringType = member.DeclaringType; if (declaringType.Kind == TypeKind.Interface) { - if (!member.IsVirtual && !member.IsAbstract && !member.IsOverride && member.Accessibility != Accessibility.Private) + if (!member.IsVirtual && !member.IsAbstract && !member.IsOverride && member.Accessibility != Accessibility.Private && member is IMethod method2 && method2.HasBody) m |= Modifiers.Sealed; } else { if (member.IsAbstract) diff --git a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs index 778bde274..d16146236 100644 --- a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs @@ -487,7 +487,7 @@ namespace ICSharpCode.Decompiler.CSharp return newTargetType.IsKnownType(KnownTypeCode.FormattableString) || newTargetType.IsKnownType(KnownTypeCode.IFormattable); } - return oldTargetType.Equals(newTargetType); + return conversions.IdentityConversion(oldTargetType, newTargetType); } TranslatedExpression LdcI4(ICompilation compilation, int val) diff --git a/ICSharpCode.Decompiler/CSharp/TypeSystem/AliasNamespaceReference.cs b/ICSharpCode.Decompiler/CSharp/TypeSystem/AliasNamespaceReference.cs index 7a7303a7e..c8fd57043 100644 --- a/ICSharpCode.Decompiler/CSharp/TypeSystem/AliasNamespaceReference.cs +++ b/ICSharpCode.Decompiler/CSharp/TypeSystem/AliasNamespaceReference.cs @@ -38,7 +38,7 @@ namespace ICSharpCode.Decompiler.CSharp.TypeSystem public AliasNamespaceReference(string identifier) { if (identifier == null) - throw new ArgumentNullException("identifier"); + throw new ArgumentNullException(nameof(identifier)); this.identifier = identifier; } diff --git a/ICSharpCode.Decompiler/CSharp/TypeSystem/MemberTypeOrNamespaceReference.cs b/ICSharpCode.Decompiler/CSharp/TypeSystem/MemberTypeOrNamespaceReference.cs index 26ca16387..024b64e2d 100644 --- a/ICSharpCode.Decompiler/CSharp/TypeSystem/MemberTypeOrNamespaceReference.cs +++ b/ICSharpCode.Decompiler/CSharp/TypeSystem/MemberTypeOrNamespaceReference.cs @@ -40,9 +40,9 @@ namespace ICSharpCode.Decompiler.CSharp.TypeSystem public MemberTypeOrNamespaceReference(TypeOrNamespaceReference target, string identifier, IList typeArguments, NameLookupMode lookupMode = NameLookupMode.Type) { if (target == null) - throw new ArgumentNullException("target"); + throw new ArgumentNullException(nameof(target)); if (identifier == null) - throw new ArgumentNullException("identifier"); + throw new ArgumentNullException(nameof(identifier)); this.target = target; this.identifier = identifier; this.typeArguments = typeArguments ?? EmptyList.Instance; diff --git a/ICSharpCode.Decompiler/CSharp/TypeSystem/ResolvedUsingScope.cs b/ICSharpCode.Decompiler/CSharp/TypeSystem/ResolvedUsingScope.cs index 8ffa45c0f..2fffd1491 100644 --- a/ICSharpCode.Decompiler/CSharp/TypeSystem/ResolvedUsingScope.cs +++ b/ICSharpCode.Decompiler/CSharp/TypeSystem/ResolvedUsingScope.cs @@ -44,9 +44,9 @@ namespace ICSharpCode.Decompiler.CSharp.TypeSystem public ResolvedUsingScope(CSharpTypeResolveContext context, UsingScope usingScope) { if (context == null) - throw new ArgumentNullException("context"); + throw new ArgumentNullException(nameof(context)); if (usingScope == null) - throw new ArgumentNullException("usingScope"); + throw new ArgumentNullException(nameof(usingScope)); this.parentContext = context; this.usingScope = usingScope; if (usingScope.Parent != null) { diff --git a/ICSharpCode.Decompiler/CSharp/TypeSystem/SimpleTypeOrNamespaceReference.cs b/ICSharpCode.Decompiler/CSharp/TypeSystem/SimpleTypeOrNamespaceReference.cs index f3d950bdb..8a3bc901f 100644 --- a/ICSharpCode.Decompiler/CSharp/TypeSystem/SimpleTypeOrNamespaceReference.cs +++ b/ICSharpCode.Decompiler/CSharp/TypeSystem/SimpleTypeOrNamespaceReference.cs @@ -39,7 +39,7 @@ namespace ICSharpCode.Decompiler.CSharp.TypeSystem public SimpleTypeOrNamespaceReference(string identifier, IList typeArguments, NameLookupMode lookupMode = NameLookupMode.Type) { if (identifier == null) - throw new ArgumentNullException("identifier"); + throw new ArgumentNullException(nameof(identifier)); this.identifier = identifier; this.typeArguments = typeArguments ?? EmptyList.Instance; this.lookupMode = lookupMode; diff --git a/ICSharpCode.Decompiler/CSharp/TypeSystem/UsingScope.cs b/ICSharpCode.Decompiler/CSharp/TypeSystem/UsingScope.cs index 88b80d4ce..6d8af6849 100644 --- a/ICSharpCode.Decompiler/CSharp/TypeSystem/UsingScope.cs +++ b/ICSharpCode.Decompiler/CSharp/TypeSystem/UsingScope.cs @@ -67,9 +67,9 @@ namespace ICSharpCode.Decompiler.CSharp.TypeSystem public UsingScope(UsingScope parent, string shortName) { if (parent == null) - throw new ArgumentNullException("parent"); + throw new ArgumentNullException(nameof(parent)); if (shortName == null) - throw new ArgumentNullException("shortName"); + throw new ArgumentNullException(nameof(shortName)); this.parent = parent; this.shortName = shortName; } diff --git a/ICSharpCode.Decompiler/DebugInfo/DebugInfoGenerator.cs b/ICSharpCode.Decompiler/DebugInfo/DebugInfoGenerator.cs index a5f7864bb..5a65476bd 100644 --- a/ICSharpCode.Decompiler/DebugInfo/DebugInfoGenerator.cs +++ b/ICSharpCode.Decompiler/DebugInfo/DebugInfoGenerator.cs @@ -60,7 +60,7 @@ namespace ICSharpCode.Decompiler.DebugInfo public DebugInfoGenerator(IDecompilerTypeSystem typeSystem) { - this.typeSystem = typeSystem ?? throw new ArgumentNullException("typeSystem"); + this.typeSystem = typeSystem ?? throw new ArgumentNullException(nameof(typeSystem)); this.currentImportScope = globalImportScope; } diff --git a/ICSharpCode.Decompiler/DecompilerException.cs b/ICSharpCode.Decompiler/DecompilerException.cs index 004da99ff..c7e4394a0 100644 --- a/ICSharpCode.Decompiler/DecompilerException.cs +++ b/ICSharpCode.Decompiler/DecompilerException.cs @@ -77,7 +77,7 @@ namespace ICSharpCode.Decompiler string ToString(Exception exception) { if (exception == null) - throw new ArgumentNullException("exception"); + throw new ArgumentNullException(nameof(exception)); string exceptionType = GetTypeName(exception); string stacktrace = GetStackTrace(exception); while (exception.InnerException != null) { diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index 40aa62e4a..fed45dec5 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -108,12 +108,14 @@ namespace ICSharpCode.Decompiler if (languageVersion < CSharp.LanguageVersion.CSharp8_0) { nullableReferenceTypes = false; readOnlyMethods = false; + asyncUsingAndForEachStatement = false; + asyncEnumerator = false; } } public CSharp.LanguageVersion GetMinimumRequiredVersion() { - if (nullableReferenceTypes || readOnlyMethods) + if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement) return CSharp.LanguageVersion.CSharp8_0; if (introduceUnmanagedConstraint || tupleComparisons || stackAllocInitializers) return CSharp.LanguageVersion.CSharp7_3; @@ -273,6 +275,24 @@ namespace ICSharpCode.Decompiler } } + bool asyncEnumerator = true; + + /// + /// Decompile IAsyncEnumerator/IAsyncEnumerable. + /// Only has an effect if is enabled. + /// + [Category("C# 8.0 / VS 2019")] + [Description("DecompilerSettings.AsyncEnumerator")] + public bool AsyncEnumerator { + get { return asyncEnumerator; } + set { + if (asyncEnumerator != value) { + asyncEnumerator = value; + OnPropertyChanged(); + } + } + } + bool decimalConstants = true; /// @@ -846,7 +866,7 @@ namespace ICSharpCode.Decompiler bool readOnlyMethods = true; [Category("C# 8.0 / VS 2019")] - [Description("DecompilerSettings.IsReadOnlyAttributeShouldBeReplacedWithReadonlyInModifiersOnStructsParameters")] + [Description("DecompilerSettings.ReadOnlyMethods")] public bool ReadOnlyMethods { get { return readOnlyMethods; } set { @@ -857,6 +877,20 @@ namespace ICSharpCode.Decompiler } } + bool asyncUsingAndForEachStatement = true; + + [Category("C# 8.0 / VS 2019")] + [Description("DecompilerSettings.DetectAsyncUsingAndForeachStatements")] + public bool AsyncUsingAndForEachStatement { + get { return asyncUsingAndForEachStatement; } + set { + if (asyncUsingAndForEachStatement != value) { + asyncUsingAndForEachStatement = value; + OnPropertyChanged(); + } + } + } + bool introduceUnmanagedConstraint = true; /// diff --git a/ICSharpCode.Decompiler/Documentation/IdStringProvider.cs b/ICSharpCode.Decompiler/Documentation/IdStringProvider.cs index 43488e36c..5c8b0fb8f 100644 --- a/ICSharpCode.Decompiler/Documentation/IdStringProvider.cs +++ b/ICSharpCode.Decompiler/Documentation/IdStringProvider.cs @@ -94,7 +94,7 @@ namespace ICSharpCode.Decompiler.Documentation public static string GetTypeName(IType type) { if (type == null) - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); StringBuilder b = new StringBuilder(); AppendTypeName(b, type, false); return b.ToString(); @@ -194,7 +194,7 @@ namespace ICSharpCode.Decompiler.Documentation public static IMemberReference ParseMemberIdString(string memberIdString) { if (memberIdString == null) - throw new ArgumentNullException("memberIdString"); + throw new ArgumentNullException(nameof(memberIdString)); if (memberIdString.Length < 2 || memberIdString[1] != ':') throw new ReflectionNameParseException(0, "Missing type tag"); char typeChar = memberIdString[0]; @@ -242,7 +242,7 @@ namespace ICSharpCode.Decompiler.Documentation public static ITypeReference ParseTypeName(string typeName) { if (typeName == null) - throw new ArgumentNullException("typeName"); + throw new ArgumentNullException(nameof(typeName)); int pos = 0; if (typeName.StartsWith("T:", StringComparison.Ordinal)) pos = 2; @@ -378,9 +378,9 @@ namespace ICSharpCode.Decompiler.Documentation public static IEntity FindEntity(string idString, ITypeResolveContext context) { if (idString == null) - throw new ArgumentNullException("idString"); + throw new ArgumentNullException(nameof(idString)); if (context == null) - throw new ArgumentNullException("context"); + throw new ArgumentNullException(nameof(context)); if (idString.StartsWith("T:", StringComparison.Ordinal)) { return ParseTypeName(idString.Substring(2)).Resolve(context).GetDefinition(); } else { diff --git a/ICSharpCode.Decompiler/Documentation/XmlDocumentationElement.cs b/ICSharpCode.Decompiler/Documentation/XmlDocumentationElement.cs index dce0c7405..7f360b9ed 100644 --- a/ICSharpCode.Decompiler/Documentation/XmlDocumentationElement.cs +++ b/ICSharpCode.Decompiler/Documentation/XmlDocumentationElement.cs @@ -55,7 +55,7 @@ namespace ICSharpCode.Decompiler.Documentation public XmlDocumentationElement(XElement element, IEntity declaringEntity, Func crefResolver) { if (element == null) - throw new ArgumentNullException("element"); + throw new ArgumentNullException(nameof(element)); this.element = element; this.declaringEntity = declaringEntity; this.crefResolver = crefResolver; @@ -67,7 +67,7 @@ namespace ICSharpCode.Decompiler.Documentation public XmlDocumentationElement(string text, IEntity declaringEntity) { if (text == null) - throw new ArgumentNullException("text"); + throw new ArgumentNullException(nameof(text)); this.declaringEntity = declaringEntity; this.textContent = text; } diff --git a/ICSharpCode.Decompiler/Documentation/XmlDocumentationProvider.cs b/ICSharpCode.Decompiler/Documentation/XmlDocumentationProvider.cs index b92613ce7..3a35ee4c1 100644 --- a/ICSharpCode.Decompiler/Documentation/XmlDocumentationProvider.cs +++ b/ICSharpCode.Decompiler/Documentation/XmlDocumentationProvider.cs @@ -24,6 +24,7 @@ using System.Runtime.Serialization; using System.Text; using System.Xml; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.Documentation { @@ -61,7 +62,7 @@ namespace ICSharpCode.Decompiler.Documentation public XmlDocumentationCache(int size = 50) { if (size <= 0) - throw new ArgumentOutOfRangeException("size", size, "Value must be positive"); + throw new ArgumentOutOfRangeException(nameof(size), size, "Value must be positive"); this.entries = new KeyValuePair[size]; } @@ -128,7 +129,7 @@ namespace ICSharpCode.Decompiler.Documentation public XmlDocumentationProvider(string fileName) { if (fileName == null) - throw new ArgumentNullException("fileName"); + throw new ArgumentNullException(nameof(fileName)); using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)) { using (XmlTextReader xmlReader = new XmlTextReader(fs)) { @@ -327,7 +328,7 @@ namespace ICSharpCode.Decompiler.Documentation public string GetDocumentation(string key) { if (key == null) - throw new ArgumentNullException("key"); + throw new ArgumentNullException(nameof(key)); return GetDocumentation(key, true); } @@ -392,11 +393,11 @@ namespace ICSharpCode.Decompiler.Documentation } } catch (IOException) { // Ignore errors on reload; IEntity.Documentation callers aren't prepared to handle exceptions - this.index = new IndexEntry[0]; // clear index to avoid future load attempts + this.index = Empty.Array; // clear index to avoid future load attempts return null; } catch (XmlException) { - this.index = new IndexEntry[0]; // clear index to avoid future load attempts - return null; + this.index = Empty.Array; // clear index to avoid future load attempts + return null; } return GetDocumentation(key, allowReload: false); // prevent infinite reload loops } diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index cefdc8541..588f877bf 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -64,6 +64,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs index 63610c57e..d41409719 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs @@ -62,14 +62,16 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow { Void, Task, - TaskOfT + TaskOfT, + AsyncEnumerator, + AsyncEnumerable } ILTransformContext context; - // These fields are set by MatchTaskCreationPattern() - IType taskType; // return type of the async method - IType underlyingReturnType; // return type of the method (only the "T" for Task{T}) + // These fields are set by MatchTaskCreationPattern() or MatchEnumeratorCreationNewObj() + IType taskType; // return type of the async method; or IAsyncEnumerable{T}/IAsyncEnumerator{T} + IType underlyingReturnType; // return type of the method (only the "T" for Task{T}), for async enumerators this is the type being yielded AsyncMethodType methodType; ITypeDefinition stateMachineType; ITypeDefinition builderType; @@ -78,15 +80,18 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow int initialState; Dictionary fieldToParameterMap = new Dictionary(); Dictionary cachedFieldToParameterMap = new Dictionary(); + IField disposeModeField; // 'disposeMode' field (IAsyncEnumerable/IAsyncEnumerator only) // These fields are set by AnalyzeMoveNext(): ILFunction moveNextFunction; ILVariable cachedStateVar; // variable in MoveNext that caches the stateField. TryCatch mainTryCatch; Block setResultAndExitBlock; // block that is jumped to for return statements + // Note: for async enumerators, a jump to setResultAndExitBlock is a 'yield break;' int finalState; // final state after the setResultAndExitBlock bool finalStateKnown; ILVariable resultVar; // the variable that gets returned by the setResultAndExitBlock + Block setResultYieldBlock; // block that is jumped to for 'yield return' statements ILVariable doFinallyBodies; // These fields are set by AnalyzeStateMachine(): @@ -110,11 +115,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow awaitBlocks.Clear(); awaitDebugInfos.Clear(); moveNextLeaves.Clear(); - if (!MatchTaskCreationPattern(function)) + if (!MatchTaskCreationPattern(function) && !MatchAsyncEnumeratorCreationPattern(function)) return; try { AnalyzeMoveNext(); ValidateCatchBlock(); + AnalyzeDisposeAsync(); } catch (SymbolicAnalysisFailedException) { return; } @@ -133,7 +139,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow TranslateCachedFieldsToLocals(); FinalizeInlineMoveNext(function); - ((BlockContainer)function.Body).ExpectedResultType = underlyingReturnType.GetStackType(); + if (methodType == AsyncMethodType.AsyncEnumerable || methodType == AsyncMethodType.AsyncEnumerator) { + ((BlockContainer)function.Body).ExpectedResultType = StackType.Void; + } else { + ((BlockContainer)function.Body).ExpectedResultType = underlyingReturnType.GetStackType(); + } // Re-run control flow simplification over the newly constructed set of gotos, // and inlining because TranslateFieldsToLocalAccess() might have opened up new inlining opportunities. @@ -164,9 +174,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow CopyPropagation.Propagate(stloc, context); } new RemoveDeadVariableInit().Run(function, context); - // Run inlining, but don't remove dead variables (they might get revived by TranslateFieldsToLocalAccess) foreach (var block in function.Descendants.OfType()) { + // Run inlining, but don't remove dead variables (they might get revived by TranslateFieldsToLocalAccess) ILInlining.InlineAllInBlock(function, block, context); + if (IsAsyncEnumerator) { + // Remove lone 'ldc.i4', those are sometimes left over after C# compiler + // optimizes out stores to the state variable. + block.Instructions.RemoveAll(inst => inst.OpCode == OpCode.LdcI4); + } } context.StepEndGroup(); } @@ -179,22 +194,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow if (blockContainer.Blocks.Count != 1) return false; var body = blockContainer.EntryPoint.Instructions; - if (body.Count < 5) + if (body.Count < 4) return false; /* Example: - V_0 is an instance of the compiler-generated struct/class, + V_0 is an instance of the compiler-generated struct/class, V_1 is an instance of the builder struct/class Block IL_0000 (incoming: 1) { - stobj System.Runtime.CompilerServices.AsyncVoidMethodBuilder(ldflda [Field ICSharpCode.Decompiler.Tests.TestCases.Pretty.Async+d__3.<>t__builder](ldloca V_0), call Create()) - stobj System.Int32(ldflda [Field ICSharpCode.Decompiler.Tests.TestCases.Pretty.Async+d__3.<>1__state](ldloca V_0), ldc.i4 -1) - stloc V_1(ldobj System.Runtime.CompilerServices.AsyncVoidMethodBuilder(ldflda [Field ICSharpCode.Decompiler.Tests.TestCases.Pretty.Async+d__3.<>t__builder](ldloc V_0))) + ... + stobj AsyncVoidMethodBuilder(ldflda [Field Async+d__3.<>t__builder](ldloca V_0), call Create()) + stobj System.Int32(ldflda [Field Async+d__3.<>1__state](ldloca V_0), ldc.i4 -1) + stloc V_1(ldobj System.Runtime.CompilerServices.AsyncVoidMethodBuilder(ldflda [Field Async+d__3.<>t__builder](ldloc V_0))) call Start(ldloca V_1, ldloca V_0) leave IL_0000 (or ret for non-void async methods) } + With custom task types, it's possible that the builder is a reference type. + In that case, the variable V_1 may be inlined. */ // Check the second-to-last instruction (the start call) first, as we can get the most information from that - if (!(body[body.Count - 2] is Call startCall)) + int pos = body.Count - 2; + if (!(body[pos] is CallInstruction startCall)) return false; if (startCall.Method.Name != "Start") return false; @@ -223,18 +242,21 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } if (startCall.Arguments.Count != 2) return false; - if (!startCall.Arguments[0].MatchLdLocRef(out ILVariable builderVar)) - return false; + ILInstruction loadBuilderExpr = startCall.Arguments[0]; if (!startCall.Arguments[1].MatchLdLoca(out ILVariable stateMachineVar)) return false; stateMachineType = stateMachineVar.Type.GetDefinition(); if (stateMachineType == null) return false; + pos--; - // Check third-to-last instruction (copy of builder) - // stloc builder(ldfld StateMachine::<>t__builder(ldloc stateMachine)) - if (!body[body.Count - 3].MatchStLoc(builderVar, out var loadBuilderExpr)) - return false; + if (loadBuilderExpr.MatchLdLocRef(out ILVariable builderVar)) { + // Check third-to-last instruction (copy of builder) + // stloc builder(ldfld StateMachine::<>t__builder(ldloc stateMachine)) + if (!body[pos].MatchStLoc(builderVar, out loadBuilderExpr)) + return false; + pos--; + } if (!loadBuilderExpr.MatchLdFld(out var loadStateMachineForBuilderExpr, out builderField)) return false; builderField = (IField)builderField.MemberDefinition; @@ -262,22 +284,25 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } if (builderField2.MemberDefinition != builderField) return false; - if (!target.MatchLdLocRef(stateMachineVar)) + if (!(target.MatchLdLoc(stateMachineVar) || target.MatchLdLoca(stateMachineVar))) return false; } // Check the last field assignment - this should be the state field // stfld <>1__state(ldloca stateField, ldc.i4 -1) - if (!MatchStFld(body[body.Count - 4], stateMachineVar, out stateField, out var initialStateExpr)) + if (!MatchStFld(body[pos], stateMachineVar, out stateField, out var initialStateExpr)) return false; if (!initialStateExpr.MatchLdcI4(out initialState)) return false; if (initialState != -1) return false; + pos--; // Check the second-to-last field assignment - this should be the builder field // stfld StateMachine.builder(ldloca stateMachine, call Create()) - if (!MatchStFld(body[body.Count - 5], stateMachineVar, out var builderField3, out var builderInitialization)) + if (pos < 0) + return false; + if (!MatchStFld(body[pos], stateMachineVar, out var builderField3, out var builderInitialization)) return false; if (builderField3 != builderField) return false; @@ -286,7 +311,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow if (createCall.Method.Name != "Create" || createCall.Arguments.Count != 0) return false; - int pos = 0; + int stopPos = pos; + pos = 0; if (stateMachineType.Kind == TypeKind.Class) { // If state machine is a class, the first instruction creates an instance: // stloc stateMachine(newobj StateMachine.ctor()) @@ -296,15 +322,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow return false; pos++; } - for (; pos < body.Count - 5; pos++) { + for (; pos < stopPos; pos++) { // stfld StateMachine.field(ldloca stateMachine, ldvar(param)) if (!MatchStFld(body[pos], stateMachineVar, out var field, out var fieldInit)) return false; - if (!fieldInit.MatchLdLoc(out var v)) - return false; - if (v.Kind != VariableKind.Parameter) + if (fieldInit.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter) { + // OK, copies parameter into state machine + fieldToParameterMap[field] = v; + } else if (fieldInit is LdObj ldobj && ldobj.Target.MatchLdThis()) { + // stfld <>4__this(ldloc stateMachine, ldobj AsyncInStruct(ldloc this)) + fieldToParameterMap[field] = ((LdLoc)ldobj.Target).Variable; + } else { return false; - fieldToParameterMap[field] = v; + } } return true; @@ -337,6 +367,164 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } #endregion + #region MatchAsyncEnumeratorCreationPattern + private bool MatchAsyncEnumeratorCreationPattern(ILFunction function) + { + if (!context.Settings.AsyncEnumerator) + return false; + taskType = function.ReturnType; + if (taskType.IsKnownType(KnownTypeCode.IAsyncEnumeratorOfT)) { + methodType = AsyncMethodType.AsyncEnumerator; + } else if (taskType.IsKnownType(KnownTypeCode.IAsyncEnumerableOfT)) { + methodType = AsyncMethodType.AsyncEnumerable; + } else { + return false; + } + underlyingReturnType = taskType.TypeArguments.Single(); + if (!(function.Body is BlockContainer blockContainer)) + return false; + if (blockContainer.Blocks.Count != 1) + return false; + var body = blockContainer.EntryPoint; + if (body.Instructions.Count == 1) { + // No parameters passed to enumerator (not even 'this'): + // ret(newobj(...)) + if (!body.Instructions[0].MatchReturn(out var newObj)) + return false; + if (MatchEnumeratorCreationNewObj(newObj, context, out initialState, out stateMachineType)) { + // HACK: the normal async/await logic expects 'initialState' to be the 'in progress' state + initialState = -1; + try { + AnalyzeEnumeratorCtor(((NewObj)newObj).Method, context, out builderField, out builderType, out stateField); + } catch (SymbolicAnalysisFailedException) { + return false; + } + return true; + } else { + return false; + } + } else { + + // stloc v(newobj d__0..ctor(ldc.i4 - 2)) + // stfld <>4__this(ldloc v, ldloc this) + // stfld <>3__otherParam(ldloc v, ldloc otherParam) + // leave IL_0000(ldloc v) + int pos = 0; + if (!body.Instructions[pos].MatchStLoc(out var v, out var newObj)) + return false; + if (!MatchEnumeratorCreationNewObj(newObj, context, out initialState, out stateMachineType)) + return false; + pos++; + + while (MatchStFld(body.Instructions[pos], v, out var field, out var value)) { + if (value.MatchLdLoc(out var p) && p.Kind == VariableKind.Parameter) { + fieldToParameterMap[field] = p; + } else if (value is LdObj ldobj && ldobj.Target.MatchLdThis()) { + fieldToParameterMap[field] = ((LdLoc)ldobj.Target).Variable; + } else { + return false; + } + pos++; + } + if (!body.Instructions[pos].MatchReturn(out var returnValue)) + return false; + if (!returnValue.MatchLdLoc(v)) + return false; + + // HACK: the normal async/await logic expects 'initialState' to be the 'in progress' state + initialState = -1; + try { + AnalyzeEnumeratorCtor(((NewObj)newObj).Method, context, out builderField, out builderType, out stateField); + if (methodType == AsyncMethodType.AsyncEnumerable) { + ResolveIEnumerableIEnumeratorFieldMapping(); + } + } catch (SymbolicAnalysisFailedException) { + return false; + } + return true; + } + } + + static bool MatchEnumeratorCreationNewObj(ILInstruction inst, ILTransformContext context, + out int initialState, out ITypeDefinition stateMachineType) + { + initialState = default; + stateMachineType = default; + // newobj(CurrentType/...::.ctor, ldc.i4(-2)) + if (!(inst is NewObj newObj)) + return false; + if (newObj.Arguments.Count != 1) + return false; + if (!newObj.Arguments[0].MatchLdcI4(out initialState)) + return false; + stateMachineType = newObj.Method.DeclaringTypeDefinition; + if (stateMachineType == null) + return false; + if (stateMachineType.DeclaringTypeDefinition != context.Function.Method.DeclaringTypeDefinition) + return false; + return IsCompilerGeneratorAsyncEnumerator( + (TypeDefinitionHandle)stateMachineType.MetadataToken, + context.TypeSystem.MainModule.metadata); + } + + public static bool IsCompilerGeneratorAsyncEnumerator(TypeDefinitionHandle type, MetadataReader metadata) + { + TypeDefinition td; + if (type.IsNil || !type.IsCompilerGeneratedOrIsInCompilerGeneratedClass(metadata) || (td = metadata.GetTypeDefinition(type)).GetDeclaringType().IsNil) + return false; + foreach (var i in td.GetInterfaceImplementations()) { + var tr = metadata.GetInterfaceImplementation(i).Interface.GetFullTypeName(metadata); + if (!tr.IsNested && tr.TopLevelTypeName.Namespace == "System.Collections.Generic" && tr.TopLevelTypeName.Name == "IAsyncEnumerator" && tr.TopLevelTypeName.TypeParameterCount == 1) + return true; + } + return false; + } + + static void AnalyzeEnumeratorCtor(IMethod ctor, ILTransformContext context, out IField builderField, out ITypeDefinition builderType, out IField stateField) + { + builderField = null; + stateField = null; + + var ctorHandle = (MethodDefinitionHandle)ctor.MetadataToken; + Block body = YieldReturnDecompiler.SingleBlock(YieldReturnDecompiler.CreateILAst(ctorHandle, context).Body); + if (body == null) + throw new SymbolicAnalysisFailedException("Missing enumeratorCtor.Body"); + // Block IL_0000 (incoming: 1) { + // call Object..ctor(ldloc this) + // stfld <>1__state(ldloc this, ldloc <>1__state) + // stfld <>t__builder(ldloc this, call Create()) + // leave IL_0000 (nop) + // } + foreach (var inst in body.Instructions) { + if (inst.MatchStFld(out var target, out var field, out var value) + && target.MatchLdThis() + && value.MatchLdLoc(out var arg) + && arg.Kind == VariableKind.Parameter && arg.Index == 0) { + stateField = (IField)field.MemberDefinition; + } + if (inst.MatchStFld(out target, out field, out value) + && target.MatchLdThis() + && value is Call call && call.Method.Name == "Create") { + builderField = (IField)field.MemberDefinition; + } + } + if (stateField == null || builderField == null) + throw new SymbolicAnalysisFailedException(); + + builderType = builderField.Type.GetDefinition(); + if (builderType == null) + throw new SymbolicAnalysisFailedException(); + } + + private void ResolveIEnumerableIEnumeratorFieldMapping() + { + var getAsyncEnumerator = stateMachineType.Methods.FirstOrDefault(m => m.Name.EndsWith(".GetAsyncEnumerator", StringComparison.Ordinal)); + if (getAsyncEnumerator == null) + throw new SymbolicAnalysisFailedException(); + YieldReturnDecompiler.ResolveIEnumerableIEnumeratorFieldMapping((MethodDefinitionHandle)getAsyncEnumerator.MetadataToken, context, fieldToParameterMap); + } + #endregion + #region AnalyzeMoveNext /// /// First peek into MoveNext(); analyzes everything outside the big try-catch. @@ -353,8 +541,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow moveNextFunction = YieldReturnDecompiler.CreateILAst(moveNextMethod, context); if (!(moveNextFunction.Body is BlockContainer blockContainer)) throw new SymbolicAnalysisFailedException(); - if (blockContainer.Blocks.Count != 2 && blockContainer.Blocks.Count != 1) - throw new SymbolicAnalysisFailedException(); if (blockContainer.EntryPoint.IncomingEdgeCount != 1) throw new SymbolicAnalysisFailedException(); cachedStateVar = null; @@ -385,12 +571,51 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow if (((BlockContainer)mainTryCatch.TryBlock).EntryPoint.Instructions[0] is StLoc initDoFinallyBodies && initDoFinallyBodies.Variable.Kind == VariableKind.Local && initDoFinallyBodies.Variable.Type.IsKnownType(KnownTypeCode.Boolean) - && initDoFinallyBodies.Value.MatchLdcI4(1)) - { + && initDoFinallyBodies.Value.MatchLdcI4(1)) { doFinallyBodies = initDoFinallyBodies.Variable; } - setResultAndExitBlock = blockContainer.Blocks.ElementAtOrDefault(1); + Debug.Assert(blockContainer.Blocks[0] == blockContainer.EntryPoint); // already checked this block + pos = 1; + if (MatchYieldBlock(blockContainer, pos)) { + setResultYieldBlock = blockContainer.Blocks[pos]; + pos++; + } else { + setResultYieldBlock = null; + } + + setResultAndExitBlock = blockContainer.Blocks.ElementAtOrDefault(pos); + CheckSetResultAndExitBlock(blockContainer); + + if (pos + 1 < blockContainer.Blocks.Count) + throw new SymbolicAnalysisFailedException("too many blocks"); + } + + private bool IsAsyncEnumerator => methodType == AsyncMethodType.AsyncEnumerable || methodType == AsyncMethodType.AsyncEnumerator; + + bool MatchYieldBlock(BlockContainer blockContainer, int pos) + { + if (!IsAsyncEnumerator) + return false; + var block = blockContainer.Blocks.ElementAtOrDefault(pos); + if (block == null) + return false; + // call SetResult(ldflda <>v__promiseOfValueOrEnd(ldloc this), ldc.i4 1) + // leave IL_0000(nop) + if (block.Instructions.Count != 2) + return false; + if (!MatchCall(block.Instructions[0], "SetResult", out var args)) + return false; + if (args.Count != 2) + return false; + if (!IsBuilderOrPromiseFieldOnThis(args[0])) + return false; + if (!args[1].MatchLdcI4(1)) + return false; + return block.Instructions[1].MatchLeave(blockContainer); + } + void CheckSetResultAndExitBlock(BlockContainer blockContainer) + { if (setResultAndExitBlock == null) { // This block can be absent if the function never exits normally, // but always throws an exception/loops infinitely. @@ -400,27 +625,44 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } // stobj System.Int32(ldflda [Field ICSharpCode.Decompiler.Tests.TestCases.Pretty.Async+d__7.<>1__state](ldloc this), ldc.i4 -2) // call SetResult(ldflda [Field ICSharpCode.Decompiler.Tests.TestCases.Pretty.Async+d__7.<>t__builder](ldloc this), ldloc result) + // [optional] call Complete(ldflda <>t__builder(ldloc this)) // leave IL_0000 - if (setResultAndExitBlock.Instructions.Count != 3) - throw new SymbolicAnalysisFailedException(); if (!MatchStateAssignment(setResultAndExitBlock.Instructions[0], out finalState)) throw new SymbolicAnalysisFailedException(); finalStateKnown = true; if (!MatchCall(setResultAndExitBlock.Instructions[1], "SetResult", out var args)) throw new SymbolicAnalysisFailedException(); - if (!IsBuilderFieldOnThis(args[0])) + if (!IsBuilderOrPromiseFieldOnThis(args[0])) throw new SymbolicAnalysisFailedException(); - if (methodType == AsyncMethodType.TaskOfT) { - if (args.Count != 2) - throw new SymbolicAnalysisFailedException(); - if (!args[1].MatchLdLoc(out resultVar)) - throw new SymbolicAnalysisFailedException(); - } else { - resultVar = null; - if (args.Count != 1) + switch (methodType) { + case AsyncMethodType.TaskOfT: + if (args.Count != 2) + throw new SymbolicAnalysisFailedException(); + if (!args[1].MatchLdLoc(out resultVar)) + throw new SymbolicAnalysisFailedException(); + break; + case AsyncMethodType.Task: + case AsyncMethodType.Void: + resultVar = null; + if (args.Count != 1) + throw new SymbolicAnalysisFailedException(); + break; + case AsyncMethodType.AsyncEnumerable: + case AsyncMethodType.AsyncEnumerator: + resultVar = null; + if (args.Count != 2) + throw new SymbolicAnalysisFailedException(); + if (!args[1].MatchLdcI4(0)) + throw new SymbolicAnalysisFailedException(); + break; + } + int pos = 2; + if (MatchCall(setResultAndExitBlock.Instructions[pos], "Complete", out args)) { + if (!(args.Count == 1 && IsBuilderFieldOnThis(args[0]))) throw new SymbolicAnalysisFailedException(); + pos++; } - if (!setResultAndExitBlock.Instructions[2].MatchLeave(blockContainer)) + if (!setResultAndExitBlock.Instructions[pos].MatchLeave(blockContainer)) throw new SymbolicAnalysisFailedException(); } @@ -431,6 +673,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // stloc exception(ldloc E_143) // stfld <>1__state(ldloc this, ldc.i4 -2) // call SetException(ldfld <>t__builder(ldloc this), ldloc exception) + // [optional] call Complete(ldfld <>t__builder(ldloc this)) // leave IL_0000 // } // } @@ -442,9 +685,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow if (!handler.Filter.MatchLdcI4(1)) throw new SymbolicAnalysisFailedException(); var catchBlock = YieldReturnDecompiler.SingleBlock(handler.Body); - catchHandlerOffset = catchBlock.StartILOffset; - if (catchBlock?.Instructions.Count != 4) + if (catchBlock == null) throw new SymbolicAnalysisFailedException(); + catchHandlerOffset = catchBlock.StartILOffset; // stloc exception(ldloc E_143) if (!(catchBlock.Instructions[0] is StLoc stloc)) throw new SymbolicAnalysisFailedException(); @@ -465,12 +708,21 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow throw new SymbolicAnalysisFailedException(); if (args.Count != 2) throw new SymbolicAnalysisFailedException(); - if (!IsBuilderFieldOnThis(args[0])) + if (!IsBuilderOrPromiseFieldOnThis(args[0])) throw new SymbolicAnalysisFailedException(); if (!args[1].MatchLdLoc(stloc.Variable)) throw new SymbolicAnalysisFailedException(); + + int pos = 3; + // [optional] call Complete(ldfld <>t__builder(ldloc this)) + if (MatchCall(catchBlock.Instructions[pos], "Complete", out args)) { + if (!(args.Count == 1 && IsBuilderFieldOnThis(args[0]))) + throw new SymbolicAnalysisFailedException(); + pos++; + } + // leave IL_0000 - if (!catchBlock.Instructions[3].MatchLeave((BlockContainer)moveNextFunction.Body)) + if (!catchBlock.Instructions[pos].MatchLeave((BlockContainer)moveNextFunction.Body)) throw new SymbolicAnalysisFailedException(); } @@ -490,19 +742,55 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow return target.MatchLdThis() && field.MemberDefinition == builderField; } + bool IsBuilderOrPromiseFieldOnThis(ILInstruction inst) + { + if (methodType == AsyncMethodType.AsyncEnumerable || methodType == AsyncMethodType.AsyncEnumerator) { + return true; // TODO: check against uses of promise fields in other methods? + } else { + return IsBuilderFieldOnThis(inst); + } + } + bool MatchStateAssignment(ILInstruction inst, out int newState) { // stfld(StateMachine::<>1__state, ldloc(this), ldc.i4(stateId)) if (inst.MatchStFld(out var target, out var field, out var value) - && target.MatchLdThis() + && StackSlotValue(target).MatchLdThis() && field.MemberDefinition == stateField - && value.MatchLdcI4(out newState)) + && StackSlotValue(value).MatchLdcI4(out newState)) { return true; } newState = 0; return false; } + + /// + /// Analyse the DisposeAsync() method in order to find the disposeModeField. + /// + private void AnalyzeDisposeAsync() + { + disposeModeField = null; + if (!IsAsyncEnumerator) { + return; + } + var disposeAsync = stateMachineType.Methods.FirstOrDefault(m => m.Name.EndsWith(".DisposeAsync", StringComparison.Ordinal)); + if (disposeAsync == null) + throw new SymbolicAnalysisFailedException("Could not find DisposeAsync()"); + var disposeAsyncHandle = (MethodDefinitionHandle)disposeAsync.MetadataToken; + var function = YieldReturnDecompiler.CreateILAst(disposeAsyncHandle, context); + foreach (var store in function.Descendants) { + if (!store.MatchStFld(out var target, out var field, out var value)) + continue; + if (!target.MatchLdThis()) + continue; + if (!value.MatchLdcI4(1)) + throw new SymbolicAnalysisFailedException(); + if (disposeModeField != null) + throw new SymbolicAnalysisFailedException("Multiple stores to disposeMode in DisposeAsync()"); + disposeModeField = (IField)field.MemberDefinition; + } + } #endregion #region InlineBodyOfMoveNext @@ -513,6 +801,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow function.AsyncReturnType = underlyingReturnType; function.MoveNextMethod = moveNextFunction.Method; function.CodeSize = moveNextFunction.CodeSize; + function.IsIterator = IsAsyncEnumerator; moveNextFunction.Variables.Clear(); moveNextFunction.ReleaseRef(); foreach (var branch in function.Descendants.OfType()) { @@ -520,6 +809,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow branch.ReplaceWith(new Leave((BlockContainer)function.Body, resultVar == null ? null : new LdLoc(resultVar)).WithILRange(branch)); } } + if (setResultYieldBlock != null) { + // We still might have branches to this block; and we can't quite yet get rid of it. + ((BlockContainer)function.Body).Blocks.Add(setResultYieldBlock); + } foreach (var leave in function.Descendants.OfType()) { if (leave.TargetContainer == moveNextFunction.Body) { leave.TargetContainer = (BlockContainer)function.Body; @@ -563,7 +856,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// void AnalyzeStateMachine(ILFunction function) { - context.Step("AnalyzeStateMachine()", function); + context.StepStartGroup("AnalyzeStateMachine()", function); smallestAwaiterVarIndex = int.MaxValue; foreach (var container in function.Descendants.OfType()) { // Use a separate state range analysis per container. @@ -577,6 +870,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow context.CancellationToken.ThrowIfCancellationRequested(); if (block.Instructions.Last() is Leave leave && moveNextLeaves.Contains(leave)) { // This is likely an 'await' block + context.Step($"AnalyzeAwaitBlock({block.StartILOffset:x4})", block); if (AnalyzeAwaitBlock(block, out var awaiterVar, out var awaiterField, out int state, out int yieldOffset)) { block.Instructions.Add(new Await(new LdLoca(awaiterVar))); Block targetBlock = stateToBlockMap.GetOrDefault(state); @@ -591,7 +885,25 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow smallestAwaiterVarIndex = awaiterVar.Index.Value; } } + } else if (block.Instructions.Last().MatchBranch(setResultYieldBlock)) { + // This is a 'yield return' in an async enumerator. + context.Step($"AnalyzeYieldReturn({block.StartILOffset:x4})", block); + if (AnalyzeYieldReturn(block, out var yieldValue, out int state)) { + block.Instructions.Add(new YieldReturn(yieldValue)); + Block targetBlock = stateToBlockMap.GetOrDefault(state); + if (targetBlock != null) { + block.Instructions.Add(new Branch(targetBlock)); + } else { + block.Instructions.Add(new InvalidBranch("Could not find block for state " + state)); + } + } else { + block.Instructions.Add(new InvalidBranch("Could not detect 'yield return'")); + } } + TransformYieldBreak(block); + } + foreach (var block in container.Blocks) { + SimplifyIfDisposeMode(block); } // Skip the state dispatcher and directly jump to the initial state var entryPoint = stateToBlockMap.GetOrDefault(initialState); @@ -604,6 +916,99 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } container.SortBlocks(deleteUnreachableBlocks: true); } + context.StepEndGroup(); + } + + private bool TransformYieldBreak(Block block) + { + // stfld disposeMode(ldloc this, ldc.i4 1) + // br nextBlock + if (block.Instructions.Count < 2) + return false; + if (!(block.Instructions.Last() is Branch branch)) + return false; + if (!block.Instructions[block.Instructions.Count - 2].MatchStFld(out var target, out var field, out var value)) + return false; + if (!target.MatchLdThis()) + return false; + if (field.MemberDefinition != disposeModeField) + return false; + if (!value.MatchLdcI4(1)) + return false; + + // Detected a 'yield break;' + context.Step($"TransformYieldBreak({block.StartILOffset:x4})", block); + var breakTarget = FindYieldBreakTarget(branch.TargetBlock); + if (breakTarget is Block targetBlock) { + branch.TargetBlock = targetBlock; + } else { + Debug.Assert(breakTarget is BlockContainer); + branch.ReplaceWith(new Leave((BlockContainer)breakTarget).WithILRange(branch)); + } + return true; + } + + ILInstruction FindYieldBreakTarget(Block block) + { + // We'll follow the branch and evaluate the following instructions + // under the assumption that disposeModeField==1, which lets us follow a series of jumps + // to determine the final target. + var visited = new HashSet(); + var evalContext = new SymbolicEvaluationContext(disposeModeField); + while (true) { + for (int i = 0; i < block.Instructions.Count; i++) { + ILInstruction inst = block.Instructions[i]; + while (inst.MatchIfInstruction(out var condition, out var trueInst, out var falseInst)) { + var condVal = evalContext.Eval(condition).AsBool(); + if (condVal.Type == SymbolicValueType.IntegerConstant) { + inst = condVal.Constant != 0 ? trueInst : falseInst; + } else if (condVal.Type == SymbolicValueType.StateInSet) { + inst = condVal.ValueSet.Contains(1) ? trueInst : falseInst; + } else { + return block; + } + } + if (inst.MatchBranch(out var targetBlock)) { + if (visited.Add(block)) { + block = targetBlock; + break; // continue with next block + } else { + return block; // infinite loop detected + } + } else if (inst is Leave leave && leave.Value.OpCode == OpCode.Nop) { + return leave.TargetContainer; + } else if (inst.OpCode == OpCode.Nop) { + continue; // continue with next instruction in this block + } else { + return block; + } + } + } + } + + private bool SimplifyIfDisposeMode(Block block) + { + // Occasionally Roslyn optimizes out an "if (disposeMode)", but keeps the + // disposeMode field access. Get rid of those field accesses: + block.Instructions.RemoveAll(MatchLdDisposeMode); + + // if (logic.not(ldfld disposeMode(ldloc this))) br falseInst + // br trueInst + if (!block.MatchIfAtEndOfBlock(out var condition, out _, out var falseInst)) + return false; + if (!MatchLdDisposeMode(condition)) + return false; + context.Step($"SimplifyIfDisposeMode({block.StartILOffset:x4})", block); + block.Instructions[block.Instructions.Count - 2] = falseInst; + block.Instructions.RemoveAt(block.Instructions.Count - 1); + return true; + + bool MatchLdDisposeMode(ILInstruction inst) + { + if (!inst.MatchLdFld(out var target, out var field)) + return false; + return target.MatchLdThis() && field.MemberDefinition == disposeModeField; + } } bool AnalyzeAwaitBlock(Block block, out ILVariable awaiter, out IField awaiterField, out int state, out int yieldOffset) @@ -705,6 +1110,50 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } return inst; } + + private bool AnalyzeYieldReturn(Block block, out ILInstruction yieldValue, out int newState) + { + yieldValue = default; + newState = default; + Debug.Assert(block.Instructions.Last().MatchBranch(setResultYieldBlock)); + // stfld current(ldloc this, ldstr "yieldValue") + // stloc S_45(ldloc this) + // stloc S_46(ldc.i4 -5) + // stloc V_0(ldloc S_46) + // stfld stateField(ldloc S_45, ldloc S_46) + // br setResultYieldBlock + + int pos = block.Instructions.Count - 2; + // Immediately before the 'yield return', there should be a state assignment: + if (pos < 0 || !MatchStateAssignment(block.Instructions[pos], out newState)) + return false; + pos--; + + if (pos >= 0 && block.Instructions[pos].MatchStLoc(cachedStateVar, out var cachedStateNewValue)) { + if (StackSlotValue(cachedStateNewValue).MatchLdcI4(newState)) { + pos--; // OK, ignore V_0 store + } else { + return false; + } + } + + while (pos >= 0 && block.Instructions[pos] is StLoc stloc) { + if (stloc.Variable.Kind != VariableKind.StackSlot) + return false; + if (!SemanticHelper.IsPure(stloc.Value.Flags)) + return false; + pos--; + } + + if (pos < 0 || !block.Instructions[pos].MatchStFld(out var target, out var field, out yieldValue)) + return false; + if (!StackSlotValue(target).MatchLdThis()) + return false; + // TODO: check that we are accessing the current field (compare with get_Current) + + block.Instructions.RemoveRange(pos, block.Instructions.Count - pos); + return true; + } #endregion #region DetectAwaitPattern @@ -761,7 +1210,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // continue matching call get_IsCompleted(ldloca awaiterVar) if (!MatchCall(condition, "get_IsCompleted", out var isCompletedArgs) || isCompletedArgs.Count != 1) return; - if (!isCompletedArgs[0].MatchLdLocRef(awaiterVar)) + if (!UnwrapConvUnknown(isCompletedArgs[0]).MatchLdLocRef(awaiterVar)) return; // Check awaitBlock and resumeBlock: if (!awaitBlocks.TryGetValue(awaitBlock, out var awaitBlockData)) @@ -779,14 +1228,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow return; if (!MatchCall(getResultCall, "GetResult", out var getResultArgs) || getResultArgs.Count != 1) return; - if (!getResultArgs[0].MatchLdLocRef(awaiterVar)) + if (!UnwrapConvUnknown(getResultArgs[0]).MatchLdLocRef(awaiterVar)) return; // All checks successful, let's transform. context.Step("Transform await pattern", block); block.Instructions.RemoveAt(block.Instructions.Count - 3); // remove getAwaiter call block.Instructions.RemoveAt(block.Instructions.Count - 2); // remove if (isCompleted) ((Branch)block.Instructions.Last()).TargetBlock = completedBlock; // instead, directly jump to completed block - Await awaitInst = new Await(getAwaiterCall.Arguments.Single()); + Await awaitInst = new Await(UnwrapConvUnknown(getAwaiterCall.Arguments.Single())); awaitInst.GetResultMethod = getResultCall.Method; awaitInst.GetAwaiterMethod = getAwaiterCall.Method; getResultCall.ReplaceWith(awaitInst); @@ -798,7 +1247,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } } } - + + static ILInstruction UnwrapConvUnknown(ILInstruction inst) + { + if (inst is Conv conv && conv.TargetType == PrimitiveType.Unknown) { + return conv.Argument; + } + return inst; + } + bool CheckAwaitBlock(Block block, out Block resumeBlock, out IField stackField) { // awaitBlock: @@ -833,6 +1290,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // stloc awaiterVar(ldfld awaiterField(ldloc this)) if (!block.Instructions[pos].MatchStLoc(awaiterVar, out var value)) return false; + if (value is CastClass cast && cast.Type.Equals(awaiterVar.Type)) { + // If the awaiter is a reference type, it might get stored in a field of type `object` + // and cast back to the awaiter type in the resume block + value = cast.Argument; + } if (!value.MatchLdFld(out var target, out var field)) return false; if (!target.MatchLdThis()) @@ -845,7 +1307,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow if (block.Instructions[pos].MatchStFld(out target, out field, out value) && target.MatchLdThis() && field.Equals(awaiterField) - && value.OpCode == OpCode.DefaultValue) + && (value.OpCode == OpCode.DefaultValue || value.OpCode == OpCode.LdNull)) { pos++; } else { diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/AwaitInCatchTransform.cs b/ICSharpCode.Decompiler/IL/ControlFlow/AwaitInCatchTransform.cs index bd1c76287..fa7a4049c 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/AwaitInCatchTransform.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/AwaitInCatchTransform.cs @@ -18,12 +18,8 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; -using System.Text; -using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.Decompiler.IL.Transforms; -using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.ControlFlow { @@ -192,218 +188,4 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow return false; } } - - class AwaitInFinallyTransform - { - public static void Run(ILFunction function, ILTransformContext context) - { - HashSet changedContainers = new HashSet(); - - // analyze all try-catch statements in the function - foreach (var tryCatch in function.Descendants.OfType().ToArray()) { - if (!(tryCatch.Parent?.Parent is BlockContainer container)) - continue; - // await in finally uses a single catch block with catch-type object - if (tryCatch.Handlers.Count != 1 || !(tryCatch.Handlers[0].Body is BlockContainer catchBlockContainer) || !tryCatch.Handlers[0].Variable.Type.IsKnownType(KnownTypeCode.Object)) - continue; - // and consists of an assignment to a temporary that is used outside the catch block - // and a jump to the finally block - var block = catchBlockContainer.EntryPoint; - if (block.Instructions.Count < 2 || !block.Instructions[0].MatchStLoc(out var globalCopyVar, out var value) || !value.MatchLdLoc(tryCatch.Handlers[0].Variable)) - continue; - if (block.Instructions.Count == 3) { - if (!block.Instructions[1].MatchStLoc(out var globalCopyVarTemp, out value) || !value.MatchLdLoc(globalCopyVar)) - continue; - globalCopyVar = globalCopyVarTemp; - } - if (!block.Instructions[block.Instructions.Count - 1].MatchBranch(out var entryPointOfFinally)) - continue; - // globalCopyVar should only be used once, at the end of the finally-block - if (globalCopyVar.LoadCount != 1 || globalCopyVar.StoreCount > 2) - continue; - var tempStore = globalCopyVar.LoadInstructions[0].Parent as StLoc; - if (tempStore == null || !MatchExceptionCaptureBlock(tempStore, out var exitOfFinally, out var afterFinally, out var blocksToRemove)) - continue; - if (!MatchAfterFinallyBlock(ref afterFinally, blocksToRemove, out bool removeFirstInstructionInAfterFinally)) - continue; - var cfg = new ControlFlowGraph(container, context.CancellationToken); - var exitOfFinallyNode = cfg.GetNode(exitOfFinally); - var entryPointOfFinallyNode = cfg.GetNode(entryPointOfFinally); - - var additionalBlocksInFinally = new HashSet(); - var invalidExits = new List(); - - TraverseDominatorTree(entryPointOfFinallyNode); - - void TraverseDominatorTree(ControlFlowNode node) - { - if (entryPointOfFinallyNode != node) { - if (entryPointOfFinallyNode.Dominates(node)) - additionalBlocksInFinally.Add((Block)node.UserData); - else - invalidExits.Add(node); - } - - if (node == exitOfFinallyNode) - return; - - foreach (var child in node.DominatorTreeChildren) { - TraverseDominatorTree(child); - } - } - - if (invalidExits.Any()) - continue; - - context.Step("Inline finally block with await", tryCatch.Handlers[0]); - - foreach (var blockToRemove in blocksToRemove) { - blockToRemove.Remove(); - } - var finallyContainer = new BlockContainer(); - entryPointOfFinally.Remove(); - if (removeFirstInstructionInAfterFinally) - afterFinally.Instructions.RemoveAt(0); - changedContainers.Add(container); - var outer = BlockContainer.FindClosestContainer(container.Parent); - if (outer != null) changedContainers.Add(outer); - finallyContainer.Blocks.Add(entryPointOfFinally); - finallyContainer.AddILRange(entryPointOfFinally); - exitOfFinally.Instructions.RemoveRange(tempStore.ChildIndex, 3); - exitOfFinally.Instructions.Add(new Leave(finallyContainer)); - foreach (var branchToFinally in container.Descendants.OfType()) { - if (branchToFinally.TargetBlock == entryPointOfFinally) - branchToFinally.ReplaceWith(new Branch(afterFinally)); - } - foreach (var newBlock in additionalBlocksInFinally) { - newBlock.Remove(); - finallyContainer.Blocks.Add(newBlock); - finallyContainer.AddILRange(newBlock); - } - tryCatch.ReplaceWith(new TryFinally(tryCatch.TryBlock, finallyContainer).WithILRange(tryCatch.TryBlock)); - } - - // clean up all modified containers - foreach (var container in changedContainers) - container.SortBlocks(deleteUnreachableBlocks: true); - } - - /// - /// Block finallyHead (incoming: 2) { - /// [body of finally] - /// stloc V_4(ldloc V_1) - /// if (comp(ldloc V_4 == ldnull)) br afterFinally - /// br typeCheckBlock - /// } - /// - /// Block typeCheckBlock (incoming: 1) { - /// stloc S_110(isinst System.Exception(ldloc V_4)) - /// if (comp(ldloc S_110 != ldnull)) br captureBlock - /// br throwBlock - /// } - /// - /// Block throwBlock (incoming: 1) { - /// throw(ldloc V_4) - /// } - /// - /// Block captureBlock (incoming: 1) { - /// callvirt Throw(call Capture(ldloc S_110)) - /// br afterFinally - /// } - /// - /// Block afterFinally (incoming: 2) { - /// stloc V_1(ldnull) - /// [after finally] - /// } - /// - static bool MatchExceptionCaptureBlock(StLoc tempStore, out Block endOfFinally, out Block afterFinally, out List blocksToRemove) - { - afterFinally = null; - endOfFinally = (Block)tempStore.Parent; - blocksToRemove = new List(); - int count = endOfFinally.Instructions.Count; - if (tempStore.ChildIndex != count - 3) - return false; - if (!(endOfFinally.Instructions[count - 2] is IfInstruction ifInst)) - return false; - if (!endOfFinally.Instructions.Last().MatchBranch(out var typeCheckBlock)) - return false; - if (!ifInst.TrueInst.MatchBranch(out afterFinally)) - return false; - // match typeCheckBlock - if (typeCheckBlock.Instructions.Count != 3) - return false; - if (!typeCheckBlock.Instructions[0].MatchStLoc(out var castStore, out var cast) - || !cast.MatchIsInst(out var arg, out var type) || !type.IsKnownType(KnownTypeCode.Exception) || !arg.MatchLdLoc(tempStore.Variable)) - return false; - if (!typeCheckBlock.Instructions[1].MatchIfInstruction(out var cond, out var jumpToCaptureBlock)) - return false; - if (!cond.MatchCompNotEqualsNull(out arg) || !arg.MatchLdLoc(castStore)) - return false; - if (!typeCheckBlock.Instructions[2].MatchBranch(out var throwBlock)) - return false; - if (!jumpToCaptureBlock.MatchBranch(out var captureBlock)) - return false; - // match throwBlock - if (throwBlock.Instructions.Count != 1 || !throwBlock.Instructions[0].MatchThrow(out arg) || !arg.MatchLdLoc(tempStore.Variable)) - return false; - // match captureBlock - if (captureBlock.Instructions.Count != 2) - return false; - if (!captureBlock.Instructions[1].MatchBranch(afterFinally)) - return false; - if (!(captureBlock.Instructions[0] is CallVirt callVirt) || callVirt.Method.FullName != "System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw" || callVirt.Arguments.Count != 1) - return false; - if (!(callVirt.Arguments[0] is Call call) || call.Method.FullName != "System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture" || call.Arguments.Count != 1) - return false; - if (!call.Arguments[0].MatchLdLoc(castStore)) - return false; - blocksToRemove.Add(typeCheckBlock); - blocksToRemove.Add(throwBlock); - blocksToRemove.Add(captureBlock); - return true; - } - - static bool MatchAfterFinallyBlock(ref Block afterFinally, List blocksToRemove, out bool removeFirstInstructionInAfterFinally) - { - removeFirstInstructionInAfterFinally = false; - if (afterFinally.Instructions.Count < 2) - return false; - ILVariable globalCopyVarSplitted; - switch (afterFinally.Instructions[0]) { - case IfInstruction ifInst: - if (ifInst.Condition.MatchCompEquals(out var load, out var ldone) && ldone.MatchLdcI4(1) && load.MatchLdLoc(out var variable)) { - if (!ifInst.TrueInst.MatchBranch(out var targetBlock)) - return false; - blocksToRemove.Add(afterFinally); - afterFinally = targetBlock; - return true; - } else if (ifInst.Condition.MatchCompNotEquals(out load, out ldone) && ldone.MatchLdcI4(1) && load.MatchLdLoc(out variable)) { - if (!afterFinally.Instructions[1].MatchBranch(out var targetBlock)) - return false; - blocksToRemove.Add(afterFinally); - afterFinally = targetBlock; - return true; - } - return false; - case LdLoc ldLoc: - if (ldLoc.Variable.LoadCount != 1 || ldLoc.Variable.StoreCount != 1) - return false; - if (!afterFinally.Instructions[1].MatchStLoc(out globalCopyVarSplitted, out var ldnull) || !ldnull.MatchLdNull()) - return false; - removeFirstInstructionInAfterFinally = true; - break; - case StLoc stloc: - globalCopyVarSplitted = stloc.Variable; - if (!stloc.Value.MatchLdNull()) - return false; - break; - default: - return false; - } - if (globalCopyVarSplitted.StoreCount != 1 || globalCopyVarSplitted.LoadCount != 0) - return false; - return true; - } - } } diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/AwaitInFinallyTransform.cs b/ICSharpCode.Decompiler/IL/ControlFlow/AwaitInFinallyTransform.cs new file mode 100644 index 000000000..c0e616765 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/ControlFlow/AwaitInFinallyTransform.cs @@ -0,0 +1,242 @@ +// Copyright (c) 2018 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.Decompiler.FlowAnalysis; +using ICSharpCode.Decompiler.IL.Transforms; +using ICSharpCode.Decompiler.TypeSystem; + +namespace ICSharpCode.Decompiler.IL.ControlFlow +{ + class AwaitInFinallyTransform + { + public static void Run(ILFunction function, ILTransformContext context) + { + if (!context.Settings.AwaitInCatchFinally) + return; + HashSet changedContainers = new HashSet(); + + // analyze all try-catch statements in the function + foreach (var tryCatch in function.Descendants.OfType().ToArray()) { + if (!(tryCatch.Parent?.Parent is BlockContainer container)) + continue; + // await in finally uses a single catch block with catch-type object + if (tryCatch.Handlers.Count != 1 || !(tryCatch.Handlers[0].Body is BlockContainer catchBlockContainer) || !tryCatch.Handlers[0].Variable.Type.IsKnownType(KnownTypeCode.Object)) + continue; + // and consists of an assignment to a temporary that is used outside the catch block + // and a jump to the finally block + var block = catchBlockContainer.EntryPoint; + if (block.Instructions.Count < 2 || !block.Instructions[0].MatchStLoc(out var globalCopyVar, out var value) || !value.MatchLdLoc(tryCatch.Handlers[0].Variable)) + continue; + if (block.Instructions.Count == 3) { + if (!block.Instructions[1].MatchStLoc(out var globalCopyVarTemp, out value) || !value.MatchLdLoc(globalCopyVar)) + continue; + globalCopyVar = globalCopyVarTemp; + } + if (!block.Instructions[block.Instructions.Count - 1].MatchBranch(out var entryPointOfFinally)) + continue; + // globalCopyVar should only be used once, at the end of the finally-block + if (globalCopyVar.LoadCount != 1 || globalCopyVar.StoreCount > 2) + continue; + var tempStore = globalCopyVar.LoadInstructions[0].Parent as StLoc; + if (tempStore == null || !MatchExceptionCaptureBlock(tempStore, out var exitOfFinally, out var afterFinally, out var blocksToRemove)) + continue; + if (!MatchAfterFinallyBlock(ref afterFinally, blocksToRemove, out bool removeFirstInstructionInAfterFinally)) + continue; + var cfg = new ControlFlowGraph(container, context.CancellationToken); + var exitOfFinallyNode = cfg.GetNode(exitOfFinally); + var entryPointOfFinallyNode = cfg.GetNode(entryPointOfFinally); + + var additionalBlocksInFinally = new HashSet(); + var invalidExits = new List(); + + TraverseDominatorTree(entryPointOfFinallyNode); + + void TraverseDominatorTree(ControlFlowNode node) + { + if (entryPointOfFinallyNode != node) { + if (entryPointOfFinallyNode.Dominates(node)) + additionalBlocksInFinally.Add((Block)node.UserData); + else + invalidExits.Add(node); + } + + if (node == exitOfFinallyNode) + return; + + foreach (var child in node.DominatorTreeChildren) { + TraverseDominatorTree(child); + } + } + + if (invalidExits.Any()) + continue; + + context.Step("Inline finally block with await", tryCatch.Handlers[0]); + + foreach (var blockToRemove in blocksToRemove) { + blockToRemove.Remove(); + } + var finallyContainer = new BlockContainer(); + entryPointOfFinally.Remove(); + if (removeFirstInstructionInAfterFinally) + afterFinally.Instructions.RemoveAt(0); + changedContainers.Add(container); + var outer = BlockContainer.FindClosestContainer(container.Parent); + if (outer != null) changedContainers.Add(outer); + finallyContainer.Blocks.Add(entryPointOfFinally); + finallyContainer.AddILRange(entryPointOfFinally); + exitOfFinally.Instructions.RemoveRange(tempStore.ChildIndex, 3); + exitOfFinally.Instructions.Add(new Leave(finallyContainer)); + foreach (var branchToFinally in container.Descendants.OfType()) { + if (branchToFinally.TargetBlock == entryPointOfFinally) + branchToFinally.ReplaceWith(new Branch(afterFinally)); + } + foreach (var newBlock in additionalBlocksInFinally) { + newBlock.Remove(); + finallyContainer.Blocks.Add(newBlock); + finallyContainer.AddILRange(newBlock); + } + tryCatch.ReplaceWith(new TryFinally(tryCatch.TryBlock, finallyContainer).WithILRange(tryCatch.TryBlock)); + } + + // clean up all modified containers + foreach (var container in changedContainers) + container.SortBlocks(deleteUnreachableBlocks: true); + } + + /// + /// Block finallyHead (incoming: 2) { + /// [body of finally] + /// stloc V_4(ldloc V_1) + /// if (comp(ldloc V_4 == ldnull)) br afterFinally + /// br typeCheckBlock + /// } + /// + /// Block typeCheckBlock (incoming: 1) { + /// stloc S_110(isinst System.Exception(ldloc V_4)) + /// if (comp(ldloc S_110 != ldnull)) br captureBlock + /// br throwBlock + /// } + /// + /// Block throwBlock (incoming: 1) { + /// throw(ldloc V_4) + /// } + /// + /// Block captureBlock (incoming: 1) { + /// callvirt Throw(call Capture(ldloc S_110)) + /// br afterFinally + /// } + /// + /// Block afterFinally (incoming: 2) { + /// stloc V_1(ldnull) + /// [after finally] + /// } + /// + static bool MatchExceptionCaptureBlock(StLoc tempStore, out Block endOfFinally, out Block afterFinally, out List blocksToRemove) + { + afterFinally = null; + endOfFinally = (Block)tempStore.Parent; + blocksToRemove = new List(); + int count = endOfFinally.Instructions.Count; + if (tempStore.ChildIndex != count - 3) + return false; + if (!(endOfFinally.Instructions[count - 2] is IfInstruction ifInst)) + return false; + if (!endOfFinally.Instructions.Last().MatchBranch(out var typeCheckBlock)) + return false; + if (!ifInst.TrueInst.MatchBranch(out afterFinally)) + return false; + // match typeCheckBlock + if (typeCheckBlock.Instructions.Count != 3) + return false; + if (!typeCheckBlock.Instructions[0].MatchStLoc(out var castStore, out var cast) + || !cast.MatchIsInst(out var arg, out var type) || !type.IsKnownType(KnownTypeCode.Exception) || !arg.MatchLdLoc(tempStore.Variable)) + return false; + if (!typeCheckBlock.Instructions[1].MatchIfInstruction(out var cond, out var jumpToCaptureBlock)) + return false; + if (!cond.MatchCompNotEqualsNull(out arg) || !arg.MatchLdLoc(castStore)) + return false; + if (!typeCheckBlock.Instructions[2].MatchBranch(out var throwBlock)) + return false; + if (!jumpToCaptureBlock.MatchBranch(out var captureBlock)) + return false; + // match throwBlock + if (throwBlock.Instructions.Count != 1 || !throwBlock.Instructions[0].MatchThrow(out arg) || !arg.MatchLdLoc(tempStore.Variable)) + return false; + // match captureBlock + if (captureBlock.Instructions.Count != 2) + return false; + if (!captureBlock.Instructions[1].MatchBranch(afterFinally)) + return false; + if (!(captureBlock.Instructions[0] is CallVirt callVirt) || callVirt.Method.FullName != "System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw" || callVirt.Arguments.Count != 1) + return false; + if (!(callVirt.Arguments[0] is Call call) || call.Method.FullName != "System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture" || call.Arguments.Count != 1) + return false; + if (!call.Arguments[0].MatchLdLoc(castStore)) + return false; + blocksToRemove.Add(typeCheckBlock); + blocksToRemove.Add(throwBlock); + blocksToRemove.Add(captureBlock); + return true; + } + + static bool MatchAfterFinallyBlock(ref Block afterFinally, List blocksToRemove, out bool removeFirstInstructionInAfterFinally) + { + removeFirstInstructionInAfterFinally = false; + if (afterFinally.Instructions.Count < 2) + return false; + ILVariable globalCopyVarSplitted; + switch (afterFinally.Instructions[0]) { + case IfInstruction ifInst: + if (ifInst.Condition.MatchCompEquals(out var load, out var ldone) && ldone.MatchLdcI4(1) && load.MatchLdLoc(out var variable)) { + if (!ifInst.TrueInst.MatchBranch(out var targetBlock)) + return false; + blocksToRemove.Add(afterFinally); + afterFinally = targetBlock; + return true; + } else if (ifInst.Condition.MatchCompNotEquals(out load, out ldone) && ldone.MatchLdcI4(1) && load.MatchLdLoc(out variable)) { + if (!afterFinally.Instructions[1].MatchBranch(out var targetBlock)) + return false; + blocksToRemove.Add(afterFinally); + afterFinally = targetBlock; + return true; + } + return false; + case LdLoc ldLoc: + if (ldLoc.Variable.LoadCount != 1 || ldLoc.Variable.StoreCount != 1) + return false; + if (!afterFinally.Instructions[1].MatchStLoc(out globalCopyVarSplitted, out var ldnull) || !ldnull.MatchLdNull()) + return false; + removeFirstInstructionInAfterFinally = true; + break; + case StLoc stloc: + globalCopyVarSplitted = stloc.Variable; + if (!stloc.Value.MatchLdNull()) + return false; + break; + default: + return false; + } + if (globalCopyVarSplitted.StoreCount != 1 || globalCopyVarSplitted.LoadCount != 0) + return false; + return true; + } + } +} diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs index 9fd519461..1f8233b6b 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs @@ -46,7 +46,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow context.CancellationToken.ThrowIfCancellationRequested(); RemoveNopInstructions(block); - RemoveDeadStackStores(block, aggressive: context.Settings.RemoveDeadStores); + RemoveDeadStackStores(block, context); InlineVariableInReturnBlock(block, context); // 1st pass SimplifySwitchInstruction before SimplifyBranchChains() @@ -70,13 +70,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow block.Instructions.RemoveAll(inst => inst.OpCode == OpCode.Nop); } - private void RemoveDeadStackStores(Block block, bool aggressive) + private static void RemoveDeadStackStores(Block block, ILTransformContext context) { + bool aggressive = context.Settings.RemoveDeadStores; // Previously copy propagation did this; // ideally the ILReader would already do this, // for now do this here (even though it's not control-flow related). for (int i = block.Instructions.Count - 1; i >= 0; i--) { if (block.Instructions[i] is StLoc stloc && stloc.Variable.IsSingleDefinition && stloc.Variable.LoadCount == 0 && stloc.Variable.Kind == VariableKind.StackSlot) { + context.Step($"Remove dead stack store {stloc.Variable.Name}", stloc); if (aggressive ? SemanticHelper.IsPure(stloc.Value.Flags) : IsSimple(stloc.Value)) { Debug.Assert(SemanticHelper.IsPure(stloc.Value.Flags)); block.Instructions.RemoveAt(i++); diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs index bcd9e047b..5156a9fef 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs @@ -305,6 +305,16 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// bool MatchEnumeratorCreationNewObj(ILInstruction inst) { + return MatchEnumeratorCreationNewObj(inst, metadata, currentType, + out enumeratorCtor, out enumeratorType); + } + + internal static bool MatchEnumeratorCreationNewObj(ILInstruction inst, + MetadataReader metadata, TypeDefinitionHandle currentType, + out MethodDefinitionHandle enumeratorCtor, out TypeDefinitionHandle enumeratorType) + { + enumeratorCtor = default; + enumeratorType = default; // newobj(CurrentType/...::.ctor, ldc.i4(-2)) if (!(inst is NewObj newObj)) return false; @@ -443,6 +453,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow MethodDefinitionHandle getEnumeratorMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault( m => metadata.GetString(metadata.GetMethodDefinition(m).Name).StartsWith("System.Collections.Generic.IEnumerable", StringComparison.Ordinal) && metadata.GetString(metadata.GetMethodDefinition(m).Name).EndsWith(".GetEnumerator", StringComparison.Ordinal)); + ResolveIEnumerableIEnumeratorFieldMapping(getEnumeratorMethod, context, fieldToParameterMap); + } + + internal static void ResolveIEnumerableIEnumeratorFieldMapping(MethodDefinitionHandle getEnumeratorMethod, ILTransformContext context, + Dictionary fieldToParameterMap) + { if (getEnumeratorMethod.IsNil) return; // no mappings (maybe it's just an IEnumerator implementation?) var function = CreateILAst(getEnumeratorMethod, context); diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 8cbb8a1ff..2bea3eb93 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -666,7 +666,7 @@ namespace ICSharpCode.Decompiler.IL case ILOpCode.Conv_u: return Push(new Conv(Pop(), PrimitiveType.U, false, Sign.None)); case ILOpCode.Conv_r_un: - return Push(new Conv(Pop(), PrimitiveType.R8, false, Sign.Unsigned)); + return Push(new Conv(Pop(), PrimitiveType.R, false, Sign.Unsigned)); case ILOpCode.Conv_ovf_i1: return Push(new Conv(Pop(), PrimitiveType.I1, true, Sign.Signed)); case ILOpCode.Conv_ovf_i2: diff --git a/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs b/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs index 1303102e4..32c0230a3 100644 --- a/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs +++ b/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs @@ -38,17 +38,14 @@ namespace ICSharpCode.Decompiler.IL return StackType.I8; case PrimitiveType.I: case PrimitiveType.U: - case (PrimitiveType)0x0f: // Ptr - case (PrimitiveType)0x1b: // FnPtr return StackType.I; case PrimitiveType.R4: return StackType.F4; case PrimitiveType.R8: + case PrimitiveType.R: return StackType.F8; - case (PrimitiveType)0x10: // ByRef + case PrimitiveType.Ref: // ByRef return StackType.Ref; - case (PrimitiveType)0x01: // Void - return StackType.Void; case PrimitiveType.Unknown: return StackType.Unknown; default: @@ -65,6 +62,7 @@ namespace ICSharpCode.Decompiler.IL case PrimitiveType.I8: case PrimitiveType.R4: case PrimitiveType.R8: + case PrimitiveType.R: case PrimitiveType.I: return Sign.Signed; case PrimitiveType.U1: @@ -100,6 +98,7 @@ namespace ICSharpCode.Decompiler.IL case PrimitiveType.I8: case PrimitiveType.R8: case PrimitiveType.U8: + case PrimitiveType.R: return 8; case PrimitiveType.I: case PrimitiveType.U: @@ -126,6 +125,18 @@ namespace ICSharpCode.Decompiler.IL return primitiveType.GetStackType().IsIntegerType(); } + public static bool IsFloatType(this PrimitiveType type) + { + switch (type) { + case PrimitiveType.R4: + case PrimitiveType.R8: + case PrimitiveType.R: + return true; + default: + return false; + } + } + /// /// Infers the C# type for an IL instruction. /// diff --git a/ICSharpCode.Decompiler/IL/Instructions/Block.cs b/ICSharpCode.Decompiler/IL/Instructions/Block.cs index 20b9da814..5bb1a2dae 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Block.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Block.cs @@ -311,6 +311,27 @@ namespace ICSharpCode.Decompiler.IL return false; return this.FinalInstruction.MatchLdLoc(tmp); } + + public bool MatchIfAtEndOfBlock(out ILInstruction condition, out ILInstruction trueInst, out ILInstruction falseInst) + { + condition = null; + trueInst = null; + falseInst = null; + if (Instructions.Count < 2) + return false; + if (Instructions[Instructions.Count - 2].MatchIfInstruction(out condition, out trueInst)) { + // Swap trueInst<>falseInst for every logic.not in the condition. + falseInst = Instructions.Last(); + while (condition.MatchLogicNot(out var arg)) { + condition = arg; + ILInstruction tmp = trueInst; + trueInst = falseInst; + falseInst = tmp; + } + return true; + } + return false; + } } public enum BlockKind diff --git a/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs b/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs index e2b751218..20ac323a7 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs @@ -68,7 +68,7 @@ namespace ICSharpCode.Decompiler.IL this.IsInstance = isInstance; this.HasExplicitThis = hasExplicitThis; this.CallingConvention = callingConvention; - this.ReturnType = returnType ?? throw new ArgumentNullException("returnType"); + this.ReturnType = returnType ?? throw new ArgumentNullException(nameof(returnType)); this.ParameterTypes = parameterTypes.ToImmutableArray(); this.Arguments = new InstructionCollection(this, 0); this.Arguments.AddRange(arguments); diff --git a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs index ba8c61586..6e27ee85d 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs @@ -313,7 +313,7 @@ namespace ICSharpCode.Decompiler.IL : base(OpCode.DynamicCompoundAssign, CompoundEvalModeFromOperation(op), target, targetKind, value) { if (!IsExpressionTypeSupported(op)) - throw new ArgumentOutOfRangeException("op"); + throw new ArgumentOutOfRangeException(nameof(op)); this.BinderFlags = binderFlags; this.Operation = op; this.TargetArgumentInfo = targetArgumentInfo; diff --git a/ICSharpCode.Decompiler/IL/Instructions/Conv.cs b/ICSharpCode.Decompiler/IL/Instructions/Conv.cs index ad88befba..6b23db08d 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Conv.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Conv.cs @@ -48,7 +48,7 @@ namespace ICSharpCode.Decompiler.IL /// /// Converts from the current precision available on the evaluation stack to the precision specified by /// the TargetType. - /// Uses "round-to-nearest" mode is the precision is reduced. + /// Uses "round-to-nearest" mode if the precision is reduced. /// FloatPrecisionChange, /// @@ -156,7 +156,7 @@ namespace ICSharpCode.Decompiler.IL public Conv(ILInstruction argument, StackType inputType, Sign inputSign, PrimitiveType targetType, bool checkForOverflow, bool isLifted = false) : base(OpCode.Conv, argument) { - bool needsSign = checkForOverflow || (!inputType.IsFloatType() && (targetType == PrimitiveType.R4 || targetType == PrimitiveType.R8)); + bool needsSign = checkForOverflow || (!inputType.IsFloatType() && targetType.IsFloatType()); Debug.Assert(!(needsSign && inputSign == Sign.None)); this.InputSign = needsSign ? inputSign : Sign.None; this.InputType = inputType; @@ -264,6 +264,7 @@ namespace ICSharpCode.Decompiler.IL default: return ConversionKind.Invalid; } + case PrimitiveType.R: case PrimitiveType.R8: switch (inputType) { case StackType.I4: diff --git a/ICSharpCode.Decompiler/IL/Instructions/UsingInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/UsingInstruction.cs index 61913e619..f44d30459 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/UsingInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/UsingInstruction.cs @@ -36,10 +36,16 @@ namespace ICSharpCode.Decompiler.IL /// partial class UsingInstruction { + public bool IsAsync { get; set; } + public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); - output.Write("using ("); + output.Write("using"); + if (IsAsync) { + output.Write(".async"); + } + output.Write(" ("); Variable.WriteTo(output); output.Write(" = "); ResourceExpression.WriteTo(output, options); diff --git a/ICSharpCode.Decompiler/IL/PrimitiveType.cs b/ICSharpCode.Decompiler/IL/PrimitiveType.cs index d93d60a8e..9d45d541d 100644 --- a/ICSharpCode.Decompiler/IL/PrimitiveType.cs +++ b/ICSharpCode.Decompiler/IL/PrimitiveType.cs @@ -35,7 +35,18 @@ namespace ICSharpCode.Decompiler.IL U8 = PrimitiveTypeCode.UInt64, I = PrimitiveTypeCode.IntPtr, U = PrimitiveTypeCode.UIntPtr, + /// Managed reference Ref = 16, + /// Floating point type of unspecified size: + /// usually 80 bits on x86 (when the runtime uses x87 instructions); + /// but only 64-bit on x64. + /// This only occurs for "conv.r.un" instructions. The C# compiler usually follows those + /// with a "conv.r4" or "conv.r8" instruction to indicate the desired float type, so + /// we only use this as conversion target type and don't bother tracking it as its own stack type: + /// basically everything treats R identical to R8, except for the (conv.r.un + conv.r[48] => conv.r[48].un) + /// combining logic which should not combine (conv.r.un + conv.r8 + conv.r4) into a single conv.r4.un. + /// + R = 254, Unknown = 255 } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index b2b9b4c13..3a4dd4933 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -177,6 +177,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms context.Step("conv.i4(ldlen array) => ldlen.i4(array)", inst); inst.AddILRange(inst.Argument); inst.ReplaceWith(new LdLen(inst.TargetType.GetStackType(), array).WithILRange(inst)); + return; + } + if (inst.TargetType.IsFloatType() && inst.Argument is Conv conv + && conv.Kind == ConversionKind.IntToFloat && conv.TargetType == PrimitiveType.R) + { + // IL conv.r.un does not indicate whether to convert the target type to R4 or R8, + // so the C# compiler usually follows it with an explicit conv.r4 or conv.r8. + // To avoid emitting '(float)(double)val', we combine these two conversions: + context.Step("conv.rN(conv.r.un(...)) => conv.rN.un(...)", inst); + inst.ReplaceWith(new Conv(conv.Argument, conv.InputType, conv.InputSign, inst.TargetType, inst.CheckForOverflow, inst.IsLifted | conv.IsLifted)); + return; } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index aff3867a2..1983006f2 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -299,9 +299,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms case OpCode.Call: case OpCode.CallVirt: method = ((CallInstruction)inst.Parent).Method; - if (method.IsAccessor && method.AccessorKind != MethodSemanticsAttributes.Getter) { - // C# doesn't allow calling setters on temporary structs - return false; + if (method.IsAccessor) { + if (method.AccessorKind == MethodSemanticsAttributes.Getter) { + // C# doesn't allow property compound assignments on temporary structs + return !(inst.Parent.Parent is CompoundAssignmentInstruction cai + && cai.TargetKind == CompoundTargetKind.Property + && cai.Target == inst.Parent); + } else { + // C# doesn't allow calling setters on temporary structs + return false; + } } return !method.IsStatic; case OpCode.Await: diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs index b8da2c416..37f8ffcd3 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs @@ -262,7 +262,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return NullableLiftingTransform.MatchGetValueOrDefault(inst, out ILInstruction arg) && arg.MatchLdLoc(testedVar); default: - throw new ArgumentOutOfRangeException("mode"); + throw new ArgumentOutOfRangeException(nameof(mode)); } } @@ -306,7 +306,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms ).WithILRange(varLoad); break; default: - throw new ArgumentOutOfRangeException("mode"); + throw new ArgumentOutOfRangeException(nameof(mode)); } oldParentChildren[oldChildIndex] = replacement; } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index e7a86f8fe..c8384aac7 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -322,7 +322,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms parameter = null; if (!IsDisplayClassFieldAccess(inst.Target, out var displayClassVar, out displayClass, out field)) return false; - if (!(inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter && v.Function == currentFunction)) + if (!(inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter && v.Function == currentFunction && v.Type.Equals(field.Type))) return false; if (displayClass.Variables.ContainsKey((IField)field.MemberDefinition)) return false; diff --git a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs index 86e0cecf2..31c8c91d5 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs @@ -34,7 +34,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!context.Settings.UsingStatement) return; this.context = context; for (int i = block.Instructions.Count - 1; i >= 0; i--) { - if (!TransformUsing(block, i) && !TransformUsingVB(block, i)) + if (!TransformUsing(block, i) && !TransformUsingVB(block, i) && !TransformAsyncUsing(block, i)) continue; // This happens in some cases: // Use correct index after transformation. @@ -167,7 +167,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - bool MatchDisposeBlock(BlockContainer container, ILVariable objVar, bool usingNull) + bool MatchDisposeBlock(BlockContainer container, ILVariable objVar, bool usingNull, in string disposeMethodFullName = "System.IDisposable.Dispose", KnownTypeCode disposeTypeCode = KnownTypeCode.IDisposable) { var entryPoint = container.EntryPoint; if (entryPoint.Instructions.Count < 2 || entryPoint.Instructions.Count > 3 || entryPoint.IncomingEdgeCount != 1) @@ -180,17 +180,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (castIndex > -1) { if (!entryPoint.Instructions[castIndex].MatchStLoc(out var tempVar, out var isinst)) return false; - if (!isinst.MatchIsInst(out var load, out var disposableType) || !load.MatchLdLoc(objVar) || !disposableType.IsKnownType(KnownTypeCode.IDisposable)) + if (!isinst.MatchIsInst(out var load, out var disposableType) || !load.MatchLdLoc(objVar) || !disposableType.IsKnownType(disposeTypeCode)) return false; if (!tempVar.IsSingleDefinition) return false; isReference = true; - if (!MatchDisposeCheck(tempVar, checkInst, isReference, usingNull, out int numObjVarLoadsInCheck)) + if (!MatchDisposeCheck(tempVar, checkInst, isReference, usingNull, out int numObjVarLoadsInCheck, disposeMethodFullName, disposeTypeCode)) return false; if (tempVar.LoadCount != numObjVarLoadsInCheck) return false; } else { - if (!MatchDisposeCheck(objVar, checkInst, isReference, usingNull, out _)) + if (!MatchDisposeCheck(objVar, checkInst, isReference, usingNull, out _, disposeMethodFullName, disposeTypeCode)) return false; } if (!entryPoint.Instructions[leaveIndex].MatchLeave(container, out var returnValue) || !returnValue.MatchNop()) @@ -198,9 +198,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - bool MatchDisposeCheck(ILVariable objVar, ILInstruction checkInst, bool isReference, bool usingNull, out int numObjVarLoadsInCheck) + bool MatchDisposeCheck(ILVariable objVar, ILInstruction checkInst, bool isReference, bool usingNull, out int numObjVarLoadsInCheck, in string disposeMethodFullName, KnownTypeCode disposeTypeCode) { numObjVarLoadsInCheck = 2; + ILInstruction disposeInvocation; CallVirt callVirt; if (objVar.Type.IsKnownType(KnownTypeCode.NullableOfT)) { if (checkInst.MatchIfInstruction(out var condition, out var disposeInst)) { @@ -208,22 +209,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(disposeInst is Block disposeBlock) || disposeBlock.Instructions.Count != 1) return false; - callVirt = disposeBlock.Instructions[0] as CallVirt; + disposeInvocation = disposeBlock.Instructions[0]; } else if (checkInst.MatchNullableRewrap(out disposeInst)) { - callVirt = disposeInst as CallVirt; + disposeInvocation = disposeInst; } else { return false; } + if (disposeTypeCode == KnownTypeCode.IAsyncDisposable) { + if (!UnwrapAwait(ref disposeInvocation)) + return false; + } + callVirt = disposeInvocation as CallVirt; if (callVirt == null) return false; - if (callVirt.Method.FullName != "System.IDisposable.Dispose") + if (callVirt.Method.FullName != disposeMethodFullName) return false; if (callVirt.Method.Parameters.Count > 0) return false; if (callVirt.Arguments.Count != 1) return false; var firstArg = callVirt.Arguments.FirstOrDefault(); - if (!(firstArg.MatchUnboxAny(out var innerArg1, out var unboxType) && unboxType.IsKnownType(KnownTypeCode.IDisposable))) { + if (!(firstArg.MatchUnboxAny(out var innerArg1, out var unboxType) && unboxType.IsKnownType(disposeTypeCode))) { if (!firstArg.MatchAddressOf(out var innerArg2, out _)) return false; return NullableLiftingTransform.MatchGetValueOrDefault(innerArg2, objVar) @@ -255,7 +261,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(disposeInst is Block disposeBlock) || disposeBlock.Instructions.Count != 1) return false; - if (!(disposeBlock.Instructions[0] is CallVirt cv)) + disposeInvocation = disposeBlock.Instructions[0]; + if (disposeTypeCode == KnownTypeCode.IAsyncDisposable) { + if (!UnwrapAwait(ref disposeInvocation)) + return false; + } + if (!(disposeInvocation is CallVirt cv)) return false; target = cv.Arguments.FirstOrDefault(); if (target == null) @@ -264,6 +275,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms target = newTarget; callVirt = cv; } else { + if (disposeTypeCode == KnownTypeCode.IAsyncDisposable) { + if (!UnwrapAwait(ref checkInst)) + return false; + } if (!(checkInst is CallVirt cv)) return false; target = cv.Arguments.FirstOrDefault(); @@ -275,7 +290,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } callVirt = cv; } - if (callVirt.Method.FullName != "System.IDisposable.Dispose") + if (callVirt.Method.FullName != disposeMethodFullName) return false; if (callVirt.Method.Parameters.Count > 0) return false; @@ -286,8 +301,74 @@ namespace ICSharpCode.Decompiler.IL.Transforms || (usingNull && callVirt.Arguments[0].MatchLdNull()) || (isReference && checkInst is NullableRewrap && target.MatchIsInst(out var arg, out var type2) - && arg.MatchLdLoc(objVar) && type2.IsKnownType(KnownTypeCode.IDisposable)); + && arg.MatchLdLoc(objVar) && type2.IsKnownType(disposeTypeCode)); } } + + /// + /// stloc test(resourceExpression) + /// .try BlockContainer { + /// Block IL_002b (incoming: 1) { + /// call Use(ldloc test) + /// leave IL_002b (nop) + /// } + /// + /// } finally BlockContainer { + /// Block IL_0045 (incoming: 1) { + /// if (comp.o(ldloc test == ldnull)) leave IL_0045 (nop) + /// br IL_00ae + /// } + /// + /// Block IL_00ae (incoming: 1) { + /// await(addressof System.Threading.Tasks.ValueTask(callvirt DisposeAsync(ldloc test))) + /// leave IL_0045 (nop) + /// } + /// + /// } + /// + private bool TransformAsyncUsing(Block block, int i) + { + if (i < 1 || !context.Settings.AsyncUsingAndForEachStatement) return false; + if (!(block.Instructions[i] is TryFinally tryFinally) || !(block.Instructions[i - 1] is StLoc storeInst)) + return false; + if (!CheckAsyncResourceType(storeInst.Variable.Type)) + return false; + if (storeInst.Variable.Kind != VariableKind.Local) + return false; + if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(tryFinally))) + return false; + if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(tryFinally) || (la.IsDescendantOf(tryFinally.TryBlock) && !ILInlining.IsUsedAsThisPointerInCall(la)))) + return false; + if (storeInst.Variable.StoreInstructions.Count > 1) + return false; + if (!(tryFinally.FinallyBlock is BlockContainer container) || !MatchDisposeBlock(container, storeInst.Variable, usingNull: false, "System.IAsyncDisposable.DisposeAsync", KnownTypeCode.IAsyncDisposable)) + return false; + context.Step("AsyncUsingTransform", tryFinally); + storeInst.Variable.Kind = VariableKind.UsingLocal; + block.Instructions.RemoveAt(i); + block.Instructions[i - 1] = new UsingInstruction(storeInst.Variable, storeInst.Value, tryFinally.TryBlock) { IsAsync = true } + .WithILRange(storeInst); + return true; + } + + bool CheckAsyncResourceType(IType type) + { + if (NullableType.GetUnderlyingType(type).GetAllBaseTypes().Any(b => b.IsKnownType(KnownTypeCode.IAsyncDisposable))) + return true; + return false; + } + + bool UnwrapAwait(ref ILInstruction awaitInstruction) + { + if (awaitInstruction == null) + return false; + if (!awaitInstruction.MatchAwait(out var arg)) + return false; + if (!arg.MatchAddressOf(out awaitInstruction, out var type)) + return false; + if (!type.IsKnownType(KnownTypeCode.ValueTask)) + return false; + return true; + } } } diff --git a/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs b/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs index 2fd984596..4f1c8fd65 100644 --- a/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs +++ b/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs @@ -116,7 +116,7 @@ namespace ICSharpCode.Decompiler.Metadata public static AssemblyNameReference Parse(string fullName) { if (fullName == null) - throw new ArgumentNullException("fullName"); + throw new ArgumentNullException(nameof(fullName)); if (fullName.Length == 0) throw new ArgumentException("Name can not be empty"); diff --git a/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs b/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs index 42c32c4ef..441abe535 100644 --- a/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs +++ b/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs @@ -62,15 +62,18 @@ namespace ICSharpCode.Decompiler.Metadata readonly Dictionary packages; ISet packageBasePaths = new HashSet(StringComparer.Ordinal); - readonly string assemblyName; - readonly string basePath; readonly Version version; readonly string dotnetBasePath = FindDotNetExeDirectory(); + public DotNetCorePathFinder(Version version) + { + this.version = version; + } + public DotNetCorePathFinder(string parentAssemblyFileName, string targetFrameworkId, Version version, ReferenceLoadInfo loadInfo = null) { - this.assemblyName = Path.GetFileNameWithoutExtension(parentAssemblyFileName); - this.basePath = Path.GetDirectoryName(parentAssemblyFileName); + string assemblyName = Path.GetFileNameWithoutExtension(parentAssemblyFileName); + string basePath = Path.GetDirectoryName(parentAssemblyFileName); this.version = version; var depsJsonFileName = Path.Combine(basePath, $"{assemblyName}.deps.json"); @@ -106,6 +109,25 @@ namespace ICSharpCode.Decompiler.Metadata return FallbackToDotNetSharedDirectory(name, version); } + internal string GetReferenceAssemblyPath(string targetFramework) + { + var (tfi, version) = UniversalAssemblyResolver.ParseTargetFramework(targetFramework); + string identifier, identifierExt; + switch (tfi) { + case TargetFrameworkIdentifier.NETCoreApp: + identifier = "Microsoft.NETCore.App"; + identifierExt = "netcoreapp" + version.Major + "." + version.Minor; + break; + case TargetFrameworkIdentifier.NETStandard: + identifier = "NETStandard.Library"; + identifierExt = "netstandard" + version.Major + "." + version.Minor; + break; + default: + throw new NotSupportedException(); + } + return Path.Combine(dotnetBasePath, "packs", identifier + ".Ref", version.ToString(), "ref", identifierExt); + } + static IEnumerable LoadPackageInfos(string depsJsonFileName, string targetFramework) { var dependencies = JsonReader.Parse(File.ReadAllText(depsJsonFileName)); diff --git a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs index d7b5ae5aa..a25b383f8 100644 --- a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs +++ b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs @@ -26,6 +26,21 @@ using System.Text; namespace ICSharpCode.Decompiler.Metadata { + enum TargetFrameworkIdentifier + { + NETFramework, + NETCoreApp, + NETStandard, + Silverlight + } + + enum DecompilerRuntime + { + NETFramework, + NETCoreApp, + Mono + } + // This is inspired by Mono.Cecil's BaseAssemblyResolver/DefaultAssemblyResolver. public class UniversalAssemblyResolver : IAssemblyResolver { @@ -67,21 +82,6 @@ namespace ICSharpCode.Decompiler.Metadata return directories.ToArray(); } - enum TargetFrameworkIdentifier - { - NETFramework, - NETCoreApp, - NETStandard, - Silverlight - } - - enum DecompilerRuntime - { - NETFramework, - NETCoreApp, - Mono - } - string targetFramework; TargetFrameworkIdentifier targetFrameworkIdentifier; Version targetFrameworkVersion; @@ -101,7 +101,7 @@ namespace ICSharpCode.Decompiler.Metadata AddSearchDirectory(baseDirectory); } - (TargetFrameworkIdentifier, Version) ParseTargetFramework(string targetFramework) + internal static (TargetFrameworkIdentifier, Version) ParseTargetFramework(string targetFramework) { string[] tokens = targetFramework.Split(','); TargetFrameworkIdentifier identifier; @@ -131,7 +131,7 @@ namespace ICSharpCode.Decompiler.Metadata switch (pair[0].Trim().ToUpperInvariant()) { case "VERSION": - var versionString = pair[1].TrimStart('v'); + var versionString = pair[1].TrimStart('v', ' ', '\t'); if (identifier == TargetFrameworkIdentifier.NETCoreApp || identifier == TargetFrameworkIdentifier.NETStandard) { diff --git a/ICSharpCode.Decompiler/Semantics/ArrayAccessResolveResult.cs b/ICSharpCode.Decompiler/Semantics/ArrayAccessResolveResult.cs index f5b8be0a1..5cd5a9a7d 100644 --- a/ICSharpCode.Decompiler/Semantics/ArrayAccessResolveResult.cs +++ b/ICSharpCode.Decompiler/Semantics/ArrayAccessResolveResult.cs @@ -34,9 +34,9 @@ namespace ICSharpCode.Decompiler.Semantics public ArrayAccessResolveResult(IType elementType, ResolveResult array, IList indexes) : base(elementType) { if (array == null) - throw new ArgumentNullException("array"); + throw new ArgumentNullException(nameof(array)); if (indexes == null) - throw new ArgumentNullException("indexes"); + throw new ArgumentNullException(nameof(indexes)); this.Array = array; this.Indexes = indexes; } diff --git a/ICSharpCode.Decompiler/Semantics/ArrayCreateResolveResult.cs b/ICSharpCode.Decompiler/Semantics/ArrayCreateResolveResult.cs index d30d46524..9e4a2e1cb 100644 --- a/ICSharpCode.Decompiler/Semantics/ArrayCreateResolveResult.cs +++ b/ICSharpCode.Decompiler/Semantics/ArrayCreateResolveResult.cs @@ -43,7 +43,7 @@ namespace ICSharpCode.Decompiler.Semantics : base(arrayType) { if (sizeArguments == null) - throw new ArgumentNullException("sizeArguments"); + throw new ArgumentNullException(nameof(sizeArguments)); this.SizeArguments = sizeArguments; this.InitializerElements = initializerElements; } diff --git a/ICSharpCode.Decompiler/Semantics/Conversion.cs b/ICSharpCode.Decompiler/Semantics/Conversion.cs index b90107392..030f7bde9 100644 --- a/ICSharpCode.Decompiler/Semantics/Conversion.cs +++ b/ICSharpCode.Decompiler/Semantics/Conversion.cs @@ -89,21 +89,21 @@ namespace ICSharpCode.Decompiler.Semantics public static Conversion UserDefinedConversion(IMethod operatorMethod, bool isImplicit, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted = false, bool isAmbiguous = false) { if (operatorMethod == null) - throw new ArgumentNullException("operatorMethod"); + throw new ArgumentNullException(nameof(operatorMethod)); return new UserDefinedConv(isImplicit, operatorMethod, conversionBeforeUserDefinedOperator, conversionAfterUserDefinedOperator, isLifted, isAmbiguous); } public static Conversion MethodGroupConversion(IMethod chosenMethod, bool isVirtualMethodLookup, bool delegateCapturesFirstArgument) { if (chosenMethod == null) - throw new ArgumentNullException("chosenMethod"); + throw new ArgumentNullException(nameof(chosenMethod)); return new MethodGroupConv(chosenMethod, isVirtualMethodLookup, delegateCapturesFirstArgument, isValid: true); } public static Conversion InvalidMethodGroupConversion(IMethod chosenMethod, bool isVirtualMethodLookup, bool delegateCapturesFirstArgument) { if (chosenMethod == null) - throw new ArgumentNullException("chosenMethod"); + throw new ArgumentNullException(nameof(chosenMethod)); return new MethodGroupConv(chosenMethod, isVirtualMethodLookup, delegateCapturesFirstArgument, isValid: false); } diff --git a/ICSharpCode.Decompiler/Semantics/ConversionResolveResult.cs b/ICSharpCode.Decompiler/Semantics/ConversionResolveResult.cs index 615c0e72e..388119b53 100644 --- a/ICSharpCode.Decompiler/Semantics/ConversionResolveResult.cs +++ b/ICSharpCode.Decompiler/Semantics/ConversionResolveResult.cs @@ -42,9 +42,9 @@ namespace ICSharpCode.Decompiler.Semantics : base(targetType) { if (input == null) - throw new ArgumentNullException("input"); + throw new ArgumentNullException(nameof(input)); if (conversion == null) - throw new ArgumentNullException("conversion"); + throw new ArgumentNullException(nameof(conversion)); this.Input = input; this.Conversion = conversion; } diff --git a/ICSharpCode.Decompiler/Semantics/ForEachResolveResult.cs b/ICSharpCode.Decompiler/Semantics/ForEachResolveResult.cs index 579b0b0e9..5e8e30529 100644 --- a/ICSharpCode.Decompiler/Semantics/ForEachResolveResult.cs +++ b/ICSharpCode.Decompiler/Semantics/ForEachResolveResult.cs @@ -64,13 +64,13 @@ namespace ICSharpCode.Decompiler.Semantics : base(voidType) { if (getEnumeratorCall == null) - throw new ArgumentNullException("getEnumeratorCall"); + throw new ArgumentNullException(nameof(getEnumeratorCall)); if (collectionType == null) - throw new ArgumentNullException("collectionType"); + throw new ArgumentNullException(nameof(collectionType)); if (enumeratorType == null) - throw new ArgumentNullException("enumeratorType"); + throw new ArgumentNullException(nameof(enumeratorType)); if (elementType == null) - throw new ArgumentNullException("elementType"); + throw new ArgumentNullException(nameof(elementType)); this.GetEnumeratorCall = getEnumeratorCall; this.CollectionType = collectionType; this.EnumeratorType = enumeratorType; diff --git a/ICSharpCode.Decompiler/Semantics/LocalResolveResult.cs b/ICSharpCode.Decompiler/Semantics/LocalResolveResult.cs index e1ea775f3..092746fda 100644 --- a/ICSharpCode.Decompiler/Semantics/LocalResolveResult.cs +++ b/ICSharpCode.Decompiler/Semantics/LocalResolveResult.cs @@ -38,7 +38,7 @@ namespace ICSharpCode.Decompiler.Semantics static IType UnpackTypeIfByRefParameter(IVariable variable) { if (variable == null) - throw new ArgumentNullException("variable"); + throw new ArgumentNullException(nameof(variable)); IType type = variable.Type; if (type.Kind == TypeKind.ByReference) { IParameter p = variable as IParameter; diff --git a/ICSharpCode.Decompiler/Semantics/NamedArgumentResolveResult.cs b/ICSharpCode.Decompiler/Semantics/NamedArgumentResolveResult.cs index 34acc839c..b988c862a 100644 --- a/ICSharpCode.Decompiler/Semantics/NamedArgumentResolveResult.cs +++ b/ICSharpCode.Decompiler/Semantics/NamedArgumentResolveResult.cs @@ -53,9 +53,9 @@ namespace ICSharpCode.Decompiler.Semantics : base(argument.Type) { if (parameter == null) - throw new ArgumentNullException("parameter"); + throw new ArgumentNullException(nameof(parameter)); if (argument == null) - throw new ArgumentNullException("argument"); + throw new ArgumentNullException(nameof(argument)); this.Member = member; this.Parameter = parameter; this.ParameterName = parameter.Name; @@ -66,9 +66,9 @@ namespace ICSharpCode.Decompiler.Semantics : base(argument.Type) { if (parameterName == null) - throw new ArgumentNullException("parameterName"); + throw new ArgumentNullException(nameof(parameterName)); if (argument == null) - throw new ArgumentNullException("argument"); + throw new ArgumentNullException(nameof(argument)); this.ParameterName = parameterName; this.Argument = argument; } diff --git a/ICSharpCode.Decompiler/Semantics/OperatorResolveResult.cs b/ICSharpCode.Decompiler/Semantics/OperatorResolveResult.cs index eb7a0d6f8..af5318c18 100644 --- a/ICSharpCode.Decompiler/Semantics/OperatorResolveResult.cs +++ b/ICSharpCode.Decompiler/Semantics/OperatorResolveResult.cs @@ -37,7 +37,7 @@ namespace ICSharpCode.Decompiler.Semantics : base(resultType) { if (operands == null) - throw new ArgumentNullException("operands"); + throw new ArgumentNullException(nameof(operands)); this.operatorType = operatorType; this.operands = operands; } @@ -46,7 +46,7 @@ namespace ICSharpCode.Decompiler.Semantics : base(resultType) { if (operands == null) - throw new ArgumentNullException("operands"); + throw new ArgumentNullException(nameof(operands)); this.operatorType = operatorType; this.userDefinedOperatorMethod = userDefinedOperatorMethod; this.isLiftedOperator = isLiftedOperator; diff --git a/ICSharpCode.Decompiler/Semantics/ResolveResult.cs b/ICSharpCode.Decompiler/Semantics/ResolveResult.cs index ed29737a9..593c83465 100644 --- a/ICSharpCode.Decompiler/Semantics/ResolveResult.cs +++ b/ICSharpCode.Decompiler/Semantics/ResolveResult.cs @@ -33,7 +33,7 @@ namespace ICSharpCode.Decompiler.Semantics public ResolveResult(IType type) { if (type == null) - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); this.type = type; } diff --git a/ICSharpCode.Decompiler/Semantics/SizeOfResolveResult.cs b/ICSharpCode.Decompiler/Semantics/SizeOfResolveResult.cs index ca4abbed5..34306afd8 100644 --- a/ICSharpCode.Decompiler/Semantics/SizeOfResolveResult.cs +++ b/ICSharpCode.Decompiler/Semantics/SizeOfResolveResult.cs @@ -33,7 +33,7 @@ namespace ICSharpCode.Decompiler.Semantics : base(int32) { if (referencedType == null) - throw new ArgumentNullException("referencedType"); + throw new ArgumentNullException(nameof(referencedType)); this.referencedType = referencedType; this.constantValue = constantValue; } diff --git a/ICSharpCode.Decompiler/Semantics/TypeIsResolveResult.cs b/ICSharpCode.Decompiler/Semantics/TypeIsResolveResult.cs index bc2718b74..2d6b57e60 100644 --- a/ICSharpCode.Decompiler/Semantics/TypeIsResolveResult.cs +++ b/ICSharpCode.Decompiler/Semantics/TypeIsResolveResult.cs @@ -37,9 +37,9 @@ namespace ICSharpCode.Decompiler.Semantics : base(booleanType) { if (input == null) - throw new ArgumentNullException("input"); + throw new ArgumentNullException(nameof(input)); if (targetType == null) - throw new ArgumentNullException("targetType"); + throw new ArgumentNullException(nameof(targetType)); this.Input = input; this.TargetType = targetType; } diff --git a/ICSharpCode.Decompiler/Semantics/TypeOfResolveResult.cs b/ICSharpCode.Decompiler/Semantics/TypeOfResolveResult.cs index f1d3be7ad..b274558b5 100644 --- a/ICSharpCode.Decompiler/Semantics/TypeOfResolveResult.cs +++ b/ICSharpCode.Decompiler/Semantics/TypeOfResolveResult.cs @@ -32,7 +32,7 @@ namespace ICSharpCode.Decompiler.Semantics : base(systemType) { if (referencedType == null) - throw new ArgumentNullException("referencedType"); + throw new ArgumentNullException(nameof(referencedType)); this.referencedType = referencedType; } diff --git a/ICSharpCode.Decompiler/Semantics/UnknownMemberResolveResult.cs b/ICSharpCode.Decompiler/Semantics/UnknownMemberResolveResult.cs index 23327ddc7..0040caded 100644 --- a/ICSharpCode.Decompiler/Semantics/UnknownMemberResolveResult.cs +++ b/ICSharpCode.Decompiler/Semantics/UnknownMemberResolveResult.cs @@ -38,7 +38,7 @@ namespace ICSharpCode.Decompiler.Semantics : base(SpecialType.UnknownType) { if (targetType == null) - throw new ArgumentNullException("targetType"); + throw new ArgumentNullException(nameof(targetType)); this.targetType = targetType; this.memberName = memberName; this.typeArguments = new ReadOnlyCollection(typeArguments.ToArray()); diff --git a/ICSharpCode.Decompiler/TypeSystem/ArrayType.cs b/ICSharpCode.Decompiler/TypeSystem/ArrayType.cs index 55f794138..4f6fe6e5e 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ArrayType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ArrayType.cs @@ -35,9 +35,9 @@ namespace ICSharpCode.Decompiler.TypeSystem public ArrayType(ICompilation compilation, IType elementType, int dimensions = 1, Nullability nullability = Nullability.Oblivious) : base(elementType) { if (compilation == null) - throw new ArgumentNullException("compilation"); + throw new ArgumentNullException(nameof(compilation)); if (dimensions <= 0) - throw new ArgumentOutOfRangeException("dimensions", dimensions, "dimensions must be positive"); + throw new ArgumentOutOfRangeException(nameof(dimensions), dimensions, "dimensions must be positive"); this.compilation = compilation; this.dimensions = dimensions; this.nullability = nullability; @@ -181,9 +181,9 @@ namespace ICSharpCode.Decompiler.TypeSystem public ArrayTypeReference(ITypeReference elementType, int dimensions = 1) { if (elementType == null) - throw new ArgumentNullException("elementType"); + throw new ArgumentNullException(nameof(elementType)); if (dimensions <= 0) - throw new ArgumentOutOfRangeException("dimensions", dimensions, "dimensions must be positive"); + throw new ArgumentOutOfRangeException(nameof(dimensions), dimensions, "dimensions must be positive"); this.elementType = elementType; this.dimensions = dimensions; } diff --git a/ICSharpCode.Decompiler/TypeSystem/ByReferenceType.cs b/ICSharpCode.Decompiler/TypeSystem/ByReferenceType.cs index 592fe53c2..6f587b389 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ByReferenceType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ByReferenceType.cs @@ -77,7 +77,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public ByReferenceTypeReference(ITypeReference elementType) { if (elementType == null) - throw new ArgumentNullException("elementType"); + throw new ArgumentNullException(nameof(elementType)); this.elementType = elementType; } diff --git a/ICSharpCode.Decompiler/TypeSystem/FullTypeName.cs b/ICSharpCode.Decompiler/TypeSystem/FullTypeName.cs index 636d0b6b6..9eaf22a51 100644 --- a/ICSharpCode.Decompiler/TypeSystem/FullTypeName.cs +++ b/ICSharpCode.Decompiler/TypeSystem/FullTypeName.cs @@ -45,7 +45,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public NestedTypeName(string name, int additionalTypeParameterCount) { if (name == null) - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); this.Name = name; this.AdditionalTypeParameterCount = additionalTypeParameterCount; } @@ -217,7 +217,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public FullTypeName NestedType(string name, int additionalTypeParameterCount) { if (name == null) - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); var newNestedType = new NestedTypeName(name, additionalTypeParameterCount); if (nestedTypes == null) return new FullTypeName(topLevelType, new[] { newNestedType }); diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs index 373ea7433..c302529bc 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs @@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation protected AbstractTypeParameter(IEntity owner, int index, string name, VarianceModifier variance) { if (owner == null) - throw new ArgumentNullException("owner"); + throw new ArgumentNullException(nameof(owner)); this.owner = owner; this.compilation = owner.Compilation; this.ownerType = owner.SymbolKind; @@ -48,7 +48,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation protected AbstractTypeParameter(ICompilation compilation, SymbolKind ownerType, int index, string name, VarianceModifier variance) { if (compilation == null) - throw new ArgumentNullException("compilation"); + throw new ArgumentNullException(nameof(compilation)); this.compilation = compilation; this.ownerType = ownerType; this.index = index; @@ -370,7 +370,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public override string ToString() { - return this.ReflectionName + " (owner=" + owner + ")"; + return this.ReflectionName; } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultAttribute.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultAttribute.cs index ca3d4935b..8e03b96d1 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultAttribute.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultAttribute.cs @@ -42,7 +42,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation ImmutableArray> namedArguments) { if (attributeType == null) - throw new ArgumentNullException("attributeType"); + throw new ArgumentNullException(nameof(attributeType)); this.attributeType = attributeType; this.FixedArguments = fixedArguments; this.NamedArguments = namedArguments; @@ -53,7 +53,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation ImmutableArray> namedArguments) { if (constructor == null) - throw new ArgumentNullException("constructor"); + throw new ArgumentNullException(nameof(constructor)); this.constructor = constructor; this.attributeType = constructor.DeclaringType ?? SpecialType.UnknownType; this.FixedArguments = fixedArguments; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs index c9b2d2b15..961d2a81f 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs @@ -39,9 +39,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public DefaultParameter(IType type, string name) { if (type == null) - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); if (name == null) - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); this.type = type; this.name = name; this.attributes = EmptyList.Instance; @@ -51,9 +51,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation ReferenceKind referenceKind = ReferenceKind.None, bool isParams = false, bool isOptional = false, object defaultValue = null) { if (type == null) - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); if (name == null) - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); this.type = type; this.name = name; this.owner = owner; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultVariable.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultVariable.cs index 483c9546d..f08b37a4b 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultVariable.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultVariable.cs @@ -33,9 +33,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public DefaultVariable(IType type, string name) { if (type == null) - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); if (name == null) - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); this.type = type; this.name = name; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/GetClassTypeReference.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/GetClassTypeReference.cs index 4c0aa3b3a..107c2eb76 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/GetClassTypeReference.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/GetClassTypeReference.cs @@ -98,7 +98,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public IType Resolve(ITypeResolveContext context) { if (context == null) - throw new ArgumentNullException("context"); + throw new ArgumentNullException(nameof(context)); IType type = null; if (module == null) { diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs index 766cdc025..872f31af0 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs @@ -64,6 +64,7 @@ namespace ICSharpCode.Decompiler.TypeSystem IteratorStateMachine, AsyncStateMachine, AsyncMethodBuilder, + AsyncIteratorStateMachine, // Field attributes: FieldOffset, @@ -131,6 +132,7 @@ namespace ICSharpCode.Decompiler.TypeSystem new TopLevelTypeName("System.Runtime.CompilerServices", nameof(IteratorStateMachineAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(AsyncStateMachineAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", "AsyncMethodBuilderAttribute"), + new TopLevelTypeName("System.Runtime.CompilerServices", "AsyncIteratorStateMachineAttribute"), // Field attributes: new TopLevelTypeName("System.Runtime.InteropServices", nameof(FieldOffsetAttribute)), new TopLevelTypeName("System", nameof(NonSerializedAttribute)), diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MergedNamespace.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MergedNamespace.cs index f6b5e3508..34aec4a4c 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MergedNamespace.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MergedNamespace.cs @@ -44,9 +44,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public MergedNamespace(ICompilation compilation, INamespace[] namespaces, string externAlias = null) { if (compilation == null) - throw new ArgumentNullException("compilation"); + throw new ArgumentNullException(nameof(compilation)); if (namespaces == null) - throw new ArgumentNullException("namespaces"); + throw new ArgumentNullException(nameof(namespaces)); this.compilation = compilation; this.namespaces = namespaces; this.externAlias = externAlias; @@ -60,9 +60,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public MergedNamespace(INamespace parentNamespace, INamespace[] namespaces) { if (parentNamespace == null) - throw new ArgumentNullException("parentNamespace"); + throw new ArgumentNullException(nameof(parentNamespace)); if (namespaces == null) - throw new ArgumentNullException("namespaces"); + throw new ArgumentNullException(nameof(namespaces)); this.parentNamespace = parentNamespace; this.namespaces = namespaces; this.compilation = parentNamespace.Compilation; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs index 75597198c..73b72d147 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs @@ -230,7 +230,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public override string ToString() { - return $"{MetadataTokens.GetToken(handle):X8} Index={Index} Owner={Owner}"; + return $"{MetadataTokens.GetToken(handle):X8} {ReflectionName}"; } } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/NestedTypeReference.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/NestedTypeReference.cs index a1646e1fd..03096f53d 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/NestedTypeReference.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/NestedTypeReference.cs @@ -44,9 +44,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public NestedTypeReference(ITypeReference declaringTypeRef, string name, int additionalTypeParameterCount, bool? isReferenceType = null) { if (declaringTypeRef == null) - throw new ArgumentNullException("declaringTypeRef"); + throw new ArgumentNullException(nameof(declaringTypeRef)); if (name == null) - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); this.declaringTypeRef = declaringTypeRef; this.name = name; this.additionalTypeParameterCount = additionalTypeParameterCount; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs index 63e7b0885..3b93a66a7 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs @@ -14,7 +14,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation internal NullabilityAnnotatedType(IType type, Nullability nullability) : base(type) { - Debug.Assert(nullability != type.Nullability); + Debug.Assert(type.Nullability == Nullability.Oblivious); + Debug.Assert(nullability != Nullability.Oblivious); // Due to IType -> concrete type casts all over the type system, we can insert // the NullabilityAnnotatedType wrapper only in some limited places. Debug.Assert(type is ITypeDefinition diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SimpleCompilation.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SimpleCompilation.cs index eca2b96ed..7ca5cd549 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SimpleCompilation.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SimpleCompilation.cs @@ -52,9 +52,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation protected void Init(IModuleReference mainAssembly, IEnumerable assemblyReferences) { if (mainAssembly == null) - throw new ArgumentNullException("mainAssembly"); + throw new ArgumentNullException(nameof(mainAssembly)); if (assemblyReferences == null) - throw new ArgumentNullException("assemblyReferences"); + throw new ArgumentNullException(nameof(assemblyReferences)); var context = new SimpleTypeResolveContext(this); this.mainModule = mainAssembly.Resolve(context); List assemblies = new List(); diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMember.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMember.cs index 4498b5a98..9b88d0aef 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMember.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMember.cs @@ -40,7 +40,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation protected SpecializedMember(IMember memberDefinition) { if (memberDefinition == null) - throw new ArgumentNullException("memberDefinition"); + throw new ArgumentNullException(nameof(memberDefinition)); if (memberDefinition is SpecializedMember) throw new ArgumentException("Member definition cannot be specialized. Please use IMember.Specialize() instead of directly constructing SpecializedMember instances."); diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs index c25ba6013..58d1e2f56 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs @@ -54,7 +54,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation : base(methodDefinition) { if (substitution == null) - throw new ArgumentNullException("substitution"); + throw new ArgumentNullException(nameof(substitution)); this.methodDefinition = methodDefinition; this.isParameterized = substitution.MethodTypeArguments != null; if (methodDefinition.TypeParameters.Count > 0) { diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeWithElementType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeWithElementType.cs index 853d6acc7..768b086e6 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeWithElementType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeWithElementType.cs @@ -27,7 +27,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation protected TypeWithElementType(IType elementType) { if (elementType == null) - throw new ArgumentNullException("elementType"); + throw new ArgumentNullException(nameof(elementType)); this.elementType = elementType; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs index 83ae0801c..018276115 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs @@ -41,7 +41,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public UnknownType(string namespaceName, string name, int typeParameterCount = 0, bool? isReferenceType = null) { if (name == null) - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); this.namespaceKnown = namespaceName != null; this.fullTypeName = new TopLevelTypeName(namespaceName ?? string.Empty, name, typeParameterCount); this.isReferenceType = isReferenceType; @@ -71,7 +71,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation IType ITypeReference.Resolve(ITypeResolveContext context) { if (context == null) - throw new ArgumentNullException("context"); + throw new ArgumentNullException(nameof(context)); return this; } diff --git a/ICSharpCode.Decompiler/TypeSystem/InheritanceHelper.cs b/ICSharpCode.Decompiler/TypeSystem/InheritanceHelper.cs index 017f38724..2d78d65b7 100644 --- a/ICSharpCode.Decompiler/TypeSystem/InheritanceHelper.cs +++ b/ICSharpCode.Decompiler/TypeSystem/InheritanceHelper.cs @@ -48,7 +48,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public static IEnumerable GetBaseMembers(IMember member, bool includeImplementedInterfaces) { if (member == null) - throw new ArgumentNullException("member"); + throw new ArgumentNullException(nameof(member)); if (includeImplementedInterfaces) { if (member.IsExplicitInterfaceImplementation && member.ExplicitlyImplementedInterfaceMembers.Count() == 1) { @@ -100,9 +100,9 @@ namespace ICSharpCode.Decompiler.TypeSystem public static IMember GetDerivedMember(IMember baseMember, ITypeDefinition derivedType) { if (baseMember == null) - throw new ArgumentNullException("baseMember"); + throw new ArgumentNullException(nameof(baseMember)); if (derivedType == null) - throw new ArgumentNullException("derivedType"); + throw new ArgumentNullException(nameof(derivedType)); if (baseMember.Compilation != derivedType.Compilation) throw new ArgumentException("baseMember and derivedType must be from the same compilation"); diff --git a/ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs b/ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs index 29d14224c..805f95b14 100644 --- a/ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs +++ b/ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs @@ -115,10 +115,16 @@ namespace ICSharpCode.Decompiler.TypeSystem Task, /// System.Threading.Tasks.Task{T} TaskOfT, + /// System.Threading.Tasks.ValueTask + ValueTask, + /// System.Threading.Tasks.ValueTask{T} + ValueTaskOfT, /// System.Nullable{T} NullableOfT, /// System.IDisposable IDisposable, + /// System.IAsyncDisposable + IAsyncDisposable, /// System.Runtime.CompilerServices.INotifyCompletion INotifyCompletion, /// System.Runtime.CompilerServices.ICriticalNotifyCompletion @@ -137,15 +143,19 @@ namespace ICSharpCode.Decompiler.TypeSystem MemoryOfT, /// System.Runtime.CompilerServices.Unsafe Unsafe, + /// System.Collections.Generic.IAsyncEnumerable{T} + IAsyncEnumerableOfT, + /// System.Collections.Generic.IAsyncEnumerator{T} + IAsyncEnumeratorOfT, } - + /// /// Contains well-known type references. /// [Serializable] public sealed class KnownTypeReference : ITypeReference { - internal const int KnownTypeCodeCount = (int)KnownTypeCode.Unsafe + 1; + internal const int KnownTypeCodeCount = (int)KnownTypeCode.IAsyncEnumeratorOfT + 1; static readonly KnownTypeReference[] knownTypeReferences = new KnownTypeReference[KnownTypeCodeCount] { null, // None @@ -189,10 +199,13 @@ namespace ICSharpCode.Decompiler.TypeSystem new KnownTypeReference(KnownTypeCode.IReadOnlyCollectionOfT, TypeKind.Interface, "System.Collections.Generic", "IReadOnlyCollection", 1), new KnownTypeReference(KnownTypeCode.IReadOnlyListOfT, TypeKind.Interface, "System.Collections.Generic", "IReadOnlyList", 1), - new KnownTypeReference(KnownTypeCode.Task, TypeKind.Class, "System.Threading.Tasks", "Task"), - new KnownTypeReference(KnownTypeCode.TaskOfT, TypeKind.Class, "System.Threading.Tasks", "Task", 1, baseType: KnownTypeCode.Task), + new KnownTypeReference(KnownTypeCode.Task, TypeKind.Class, "System.Threading.Tasks", "Task"), + new KnownTypeReference(KnownTypeCode.TaskOfT, TypeKind.Class, "System.Threading.Tasks", "Task", 1, baseType: KnownTypeCode.Task), + new KnownTypeReference(KnownTypeCode.ValueTask, TypeKind.Struct, "System.Threading.Tasks", "ValueTask"), + new KnownTypeReference(KnownTypeCode.ValueTaskOfT, TypeKind.Struct, "System.Threading.Tasks", "ValueTask", 1), new KnownTypeReference(KnownTypeCode.NullableOfT, TypeKind.Struct, "System", "Nullable", 1), new KnownTypeReference(KnownTypeCode.IDisposable, TypeKind.Interface, "System", "IDisposable"), + new KnownTypeReference(KnownTypeCode.IAsyncDisposable, TypeKind.Interface, "System", "IAsyncDisposable"), new KnownTypeReference(KnownTypeCode.INotifyCompletion, TypeKind.Interface, "System.Runtime.CompilerServices", "INotifyCompletion"), new KnownTypeReference(KnownTypeCode.ICriticalNotifyCompletion, TypeKind.Interface, "System.Runtime.CompilerServices", "ICriticalNotifyCompletion"), @@ -203,6 +216,8 @@ namespace ICSharpCode.Decompiler.TypeSystem new KnownTypeReference(KnownTypeCode.ReadOnlySpanOfT, TypeKind.Struct, "System", "ReadOnlySpan", 1), new KnownTypeReference(KnownTypeCode.MemoryOfT, TypeKind.Struct, "System", "Memory", 1), new KnownTypeReference(KnownTypeCode.Unsafe, TypeKind.Class, "System.Runtime.CompilerServices", "Unsafe", 0), + new KnownTypeReference(KnownTypeCode.IAsyncEnumerableOfT, TypeKind.Interface, "System.Collections.Generic", "IAsyncEnumerable", 1), + new KnownTypeReference(KnownTypeCode.IAsyncEnumeratorOfT, TypeKind.Interface, "System.Collections.Generic", "IAsyncEnumerator", 1), }; /// diff --git a/ICSharpCode.Decompiler/TypeSystem/NullableType.cs b/ICSharpCode.Decompiler/TypeSystem/NullableType.cs index eae7190c1..42f72cf65 100644 --- a/ICSharpCode.Decompiler/TypeSystem/NullableType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/NullableType.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public static bool IsNullable(IType type) { if (type == null) - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); ParameterizedType pt = type.SkipModifiers() as ParameterizedType; return pt != null && pt.TypeParameterCount == 1 && pt.GenericType.IsKnownType(KnownTypeCode.NullableOfT); } @@ -48,7 +48,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public static IType GetUnderlyingType(IType type) { if (type == null) - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); ParameterizedType pt = type.SkipModifiers() as ParameterizedType; if (pt != null && pt.TypeParameterCount == 1 && pt.GenericType.IsKnownType(KnownTypeCode.NullableOfT)) return pt.GetTypeArgument(0); @@ -62,9 +62,9 @@ namespace ICSharpCode.Decompiler.TypeSystem public static IType Create(ICompilation compilation, IType elementType) { if (compilation == null) - throw new ArgumentNullException("compilation"); + throw new ArgumentNullException(nameof(compilation)); if (elementType == null) - throw new ArgumentNullException("elementType"); + throw new ArgumentNullException(nameof(elementType)); IType nullableType = compilation.FindType(KnownTypeCode.NullableOfT); ITypeDefinition nullableTypeDef = nullableType.GetDefinition(); @@ -80,7 +80,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public static ParameterizedTypeReference Create(ITypeReference elementType) { if (elementType == null) - throw new ArgumentNullException("elementType"); + throw new ArgumentNullException(nameof(elementType)); return new ParameterizedTypeReference(KnownTypeReference.Get(KnownTypeCode.NullableOfT), new [] { elementType }); } } diff --git a/ICSharpCode.Decompiler/TypeSystem/ParameterListComparer.cs b/ICSharpCode.Decompiler/TypeSystem/ParameterListComparer.cs index 20be982d6..6fafe982b 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ParameterListComparer.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ParameterListComparer.cs @@ -111,7 +111,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public SignatureComparer(StringComparer nameComparer) { if (nameComparer == null) - throw new ArgumentNullException("nameComparer"); + throw new ArgumentNullException(nameof(nameComparer)); this.nameComparer = nameComparer; } diff --git a/ICSharpCode.Decompiler/TypeSystem/ParameterizedType.cs b/ICSharpCode.Decompiler/TypeSystem/ParameterizedType.cs index 5dd907ace..3f1d500e3 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ParameterizedType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ParameterizedType.cs @@ -44,9 +44,9 @@ namespace ICSharpCode.Decompiler.TypeSystem public ParameterizedType(IType genericType, IEnumerable typeArguments) { if (genericType == null) - throw new ArgumentNullException("genericType"); + throw new ArgumentNullException(nameof(genericType)); if (typeArguments == null) - throw new ArgumentNullException("typeArguments"); + throw new ArgumentNullException(nameof(typeArguments)); this.genericType = genericType; this.typeArguments = typeArguments.ToArray(); // copy input array to ensure it isn't modified if (this.typeArguments.Length == 0) @@ -67,7 +67,7 @@ namespace ICSharpCode.Decompiler.TypeSystem /// Fast internal version of the constructor. (no safety checks) /// Keeps the array that was passed and assumes it won't be modified. /// - internal ParameterizedType(IType genericType, IType[] typeArguments) + internal ParameterizedType(IType genericType, params IType[] typeArguments) { Debug.Assert(genericType.TypeParameterCount == typeArguments.Length); this.genericType = genericType; @@ -358,9 +358,9 @@ namespace ICSharpCode.Decompiler.TypeSystem public ParameterizedTypeReference(ITypeReference genericType, IEnumerable typeArguments) { if (genericType == null) - throw new ArgumentNullException("genericType"); + throw new ArgumentNullException(nameof(genericType)); if (typeArguments == null) - throw new ArgumentNullException("typeArguments"); + throw new ArgumentNullException(nameof(typeArguments)); this.genericType = genericType; this.typeArguments = typeArguments.ToArray(); for (int i = 0; i < this.typeArguments.Length; i++) { diff --git a/ICSharpCode.Decompiler/TypeSystem/PointerType.cs b/ICSharpCode.Decompiler/TypeSystem/PointerType.cs index 048f63a02..654269fb0 100644 --- a/ICSharpCode.Decompiler/TypeSystem/PointerType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/PointerType.cs @@ -75,7 +75,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public PointerTypeReference(ITypeReference elementType) { if (elementType == null) - throw new ArgumentNullException("elementType"); + throw new ArgumentNullException(nameof(elementType)); this.elementType = elementType; } diff --git a/ICSharpCode.Decompiler/TypeSystem/ReflectionHelper.cs b/ICSharpCode.Decompiler/TypeSystem/ReflectionHelper.cs index 1a46c177a..4eb6677ce 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ReflectionHelper.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ReflectionHelper.cs @@ -210,7 +210,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public static ITypeReference ParseReflectionName(string reflectionTypeName) { if (reflectionTypeName == null) - throw new ArgumentNullException("reflectionTypeName"); + throw new ArgumentNullException(nameof(reflectionTypeName)); int pos = 0; ITypeReference r = ParseReflectionName(reflectionTypeName, ref pos); if (pos < reflectionTypeName.Length) diff --git a/ICSharpCode.Decompiler/TypeSystem/SimpleTypeResolveContext.cs b/ICSharpCode.Decompiler/TypeSystem/SimpleTypeResolveContext.cs index c9cf42375..10a042da6 100644 --- a/ICSharpCode.Decompiler/TypeSystem/SimpleTypeResolveContext.cs +++ b/ICSharpCode.Decompiler/TypeSystem/SimpleTypeResolveContext.cs @@ -33,7 +33,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public SimpleTypeResolveContext(ICompilation compilation) { if (compilation == null) - throw new ArgumentNullException("compilation"); + throw new ArgumentNullException(nameof(compilation)); this.compilation = compilation; } @@ -48,7 +48,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public SimpleTypeResolveContext(IEntity entity) { if (entity == null) - throw new ArgumentNullException("entity"); + throw new ArgumentNullException(nameof(entity)); this.compilation = entity.Compilation; this.currentModule = entity.ParentModule; this.currentTypeDefinition = (entity as ITypeDefinition) ?? entity.DeclaringTypeDefinition; diff --git a/ICSharpCode.Decompiler/TypeSystem/SpecialType.cs b/ICSharpCode.Decompiler/TypeSystem/SpecialType.cs index 612c56178..082376efa 100644 --- a/ICSharpCode.Decompiler/TypeSystem/SpecialType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/SpecialType.cs @@ -84,7 +84,7 @@ namespace ICSharpCode.Decompiler.TypeSystem IType ITypeReference.Resolve(ITypeResolveContext context) { if (context == null) - throw new ArgumentNullException("context"); + throw new ArgumentNullException(nameof(context)); return this; } diff --git a/ICSharpCode.Decompiler/TypeSystem/TaskType.cs b/ICSharpCode.Decompiler/TypeSystem/TaskType.cs index 63aa880d0..f59e82496 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TaskType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TaskType.cs @@ -121,9 +121,9 @@ namespace ICSharpCode.Decompiler.TypeSystem public static IType Create(ICompilation compilation, IType elementType) { if (compilation == null) - throw new ArgumentNullException("compilation"); + throw new ArgumentNullException(nameof(compilation)); if (elementType == null) - throw new ArgumentNullException("elementType"); + throw new ArgumentNullException(nameof(elementType)); if (elementType.Kind == TypeKind.Void) return compilation.FindType(KnownTypeCode.Task); diff --git a/ICSharpCode.Decompiler/TypeSystem/TopLevelTypeName.cs b/ICSharpCode.Decompiler/TypeSystem/TopLevelTypeName.cs index 1ece4144e..4e70f5ec6 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TopLevelTypeName.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TopLevelTypeName.cs @@ -36,9 +36,9 @@ namespace ICSharpCode.Decompiler.TypeSystem public TopLevelTypeName(string namespaceName, string name, int typeParameterCount = 0) { if (namespaceName == null) - throw new ArgumentNullException("namespaceName"); + throw new ArgumentNullException(nameof(namespaceName)); if (name == null) - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); this.namespaceName = namespaceName; this.name = name; this.typeParameterCount = typeParameterCount; diff --git a/ICSharpCode.Decompiler/TypeSystem/TupleType.cs b/ICSharpCode.Decompiler/TypeSystem/TupleType.cs index 30635438f..7833b84c5 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TupleType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TupleType.cs @@ -373,7 +373,8 @@ namespace ICSharpCode.Decompiler.TypeSystem { public static IType TupleUnderlyingTypeOrSelf(this IType type) { - return (type as TupleType)?.UnderlyingType ?? type; + var t = (type as TupleType)?.UnderlyingType ?? type; + return t.WithoutNullability(); } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeParameterSubstitution.cs b/ICSharpCode.Decompiler/TypeSystem/TypeParameterSubstitution.cs index 107fe0e9f..0e92e7326 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeParameterSubstitution.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeParameterSubstitution.cs @@ -18,6 +18,7 @@ using System.Collections.Generic; using System.Text; +using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.TypeSystem { @@ -186,7 +187,21 @@ namespace ICSharpCode.Decompiler.TypeSystem return base.VisitTypeParameter(type); } } - + + public override IType VisitNullabilityAnnotatedType(NullabilityAnnotatedType type) + { + if (type is NullabilityAnnotatedTypeParameter tp) { + if (tp.Nullability == Nullability.Nullable) { + return VisitTypeParameter(tp).ChangeNullability(Nullability.Nullable); + } else { + // T! substituted with T=oblivious string should result in oblivious string + return VisitTypeParameter(tp); + } + } else { + return base.VisitNullabilityAnnotatedType(type); + } + } + public override string ToString() { StringBuilder b = new StringBuilder(); diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs index 1b6e6cdbe..c6beca5c3 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs @@ -44,7 +44,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public static IEnumerable GetAllBaseTypes(this IType type) { if (type == null) - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); BaseTypeCollector collector = new BaseTypeCollector(); collector.CollectBaseTypes(type); return collector; @@ -61,7 +61,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public static IEnumerable GetNonInterfaceBaseTypes(this IType type) { if (type == null) - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); BaseTypeCollector collector = new BaseTypeCollector(); collector.SkipImplementedInterfaces = true; collector.CollectBaseTypes(type); @@ -80,7 +80,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public static IEnumerable GetAllBaseTypeDefinitions(this IType type) { if (type == null) - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); return type.GetAllBaseTypes().Select(t => t.GetDefinition()).Where(d => d != null).Distinct(); } @@ -91,7 +91,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public static bool IsDerivedFrom(this ITypeDefinition type, ITypeDefinition baseType) { if (type == null) - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); if (baseType == null) return false; if (type.Compilation != baseType.Compilation) { @@ -106,7 +106,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public static bool IsDerivedFrom(this ITypeDefinition type, KnownTypeCode baseType) { if (type == null) - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); if (baseType == KnownTypeCode.None) return false; return IsDerivedFrom(type, type.Compilation.FindType(baseType).GetDefinition()); @@ -178,7 +178,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public static bool IsOpen(this IType type) { if (type == null) - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); TypeClassificationVisitor v = new TypeClassificationVisitor(); type.AcceptVisitor(v); return v.isOpen; @@ -193,7 +193,7 @@ namespace ICSharpCode.Decompiler.TypeSystem static IEntity GetTypeParameterOwner(IType type) { if (type == null) - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); TypeClassificationVisitor v = new TypeClassificationVisitor(); type.AcceptVisitor(v); return v.typeParameterOwner; @@ -210,7 +210,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public static bool IsUnbound(this IType type) { if (type == null) - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); return type is ITypeDefinition && type.TypeParameterCount > 0; } @@ -265,7 +265,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public static IMethod GetDelegateInvokeMethod(this IType type) { if (type == null) - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); if (type.Kind == TypeKind.Delegate) return type.GetMethods(m => m.Name == "Invoke", GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); else @@ -310,7 +310,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public static IReadOnlyList Resolve(this IList typeReferences, ITypeResolveContext context) { if (typeReferences == null) - throw new ArgumentNullException("typeReferences"); + throw new ArgumentNullException(nameof(typeReferences)); if (typeReferences.Count == 0) return EmptyList.Instance; else @@ -336,7 +336,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public static IType FindType(this ICompilation compilation, FullTypeName fullTypeName) { if (compilation == null) - throw new ArgumentNullException("compilation"); + throw new ArgumentNullException(nameof(compilation)); foreach (IModule asm in compilation.Modules) { ITypeDefinition def = asm.GetTypeDefinition(fullTypeName); if (def != null) @@ -535,5 +535,10 @@ namespace ICSharpCode.Decompiler.TypeSystem { return KnownAttributes.IsKnownAttributeType(type); } + + public static IType WithoutNullability(this IType type) + { + return type.ChangeNullability(Nullability.Oblivious); + } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs b/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs index 8663a7926..4741fef7b 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs @@ -398,6 +398,7 @@ namespace ICSharpCode.Decompiler.TypeSystem case PrimitiveType.R4: return KnownTypeCode.Single; case PrimitiveType.R8: + case PrimitiveType.R: return KnownTypeCode.Double; case PrimitiveType.U1: return KnownTypeCode.Byte; diff --git a/ICSharpCode.Decompiler/Util/CSharpPrimitiveCast.cs b/ICSharpCode.Decompiler/Util/CSharpPrimitiveCast.cs index c78c51fc1..719465c03 100644 --- a/ICSharpCode.Decompiler/Util/CSharpPrimitiveCast.cs +++ b/ICSharpCode.Decompiler/Util/CSharpPrimitiveCast.cs @@ -64,6 +64,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.Single: return (char)(float)input; case TypeCode.Double: return (char)(double)input; case TypeCode.Decimal: return (char)(decimal)input; + case TypeCode.Boolean: return (char)((bool)input ? 1 : 0); } break; case TypeCode.SByte: @@ -79,6 +80,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.Single: return (sbyte)(float)input; case TypeCode.Double: return (sbyte)(double)input; case TypeCode.Decimal: return (sbyte)(decimal)input; + case TypeCode.Boolean: return (sbyte)((bool)input ? 1 : 0); } break; case TypeCode.Byte: @@ -94,6 +96,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.Single: return (byte)(float)input; case TypeCode.Double: return (byte)(double)input; case TypeCode.Decimal: return (byte)(decimal)input; + case TypeCode.Boolean: return (byte)((bool)input ? 1 : 0); } break; case TypeCode.Int16: @@ -109,6 +112,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.Single: return (short)(float)input; case TypeCode.Double: return (short)(double)input; case TypeCode.Decimal: return (short)(decimal)input; + case TypeCode.Boolean: return (short)((bool)input ? 1 : 0); } break; case TypeCode.UInt16: @@ -124,6 +128,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.Single: return (ushort)(float)input; case TypeCode.Double: return (ushort)(double)input; case TypeCode.Decimal: return (ushort)(decimal)input; + case TypeCode.Boolean: return (ushort)((bool)input ? 1 : 0); } break; case TypeCode.Int32: @@ -139,6 +144,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.Single: return (int)(float)input; case TypeCode.Double: return (int)(double)input; case TypeCode.Decimal: return (int)(decimal)input; + case TypeCode.Boolean: return (int)((bool)input ? 1 : 0); } break; case TypeCode.UInt32: @@ -154,6 +160,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.Single: return (uint)(float)input; case TypeCode.Double: return (uint)(double)input; case TypeCode.Decimal: return (uint)(decimal)input; + case TypeCode.Boolean: return (uint)((bool)input ? 1 : 0); } break; case TypeCode.Int64: @@ -169,6 +176,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.Single: return (long)(float)input; case TypeCode.Double: return (long)(double)input; case TypeCode.Decimal: return (long)(decimal)input; + case TypeCode.Boolean: return (long)((bool)input ? 1 : 0); } break; case TypeCode.UInt64: @@ -184,6 +192,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.Single: return (ulong)(float)input; case TypeCode.Double: return (ulong)(double)input; case TypeCode.Decimal: return (ulong)(decimal)input; + case TypeCode.Boolean: return (ulong)((bool)input ? 1 : 0); } break; case TypeCode.Single: @@ -199,6 +208,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.UInt64: return (float)(ulong)input; case TypeCode.Double: return (float)(double)input; case TypeCode.Decimal: return (float)(decimal)input; + case TypeCode.Boolean: return (float)((bool)input ? 1 : 0); } break; case TypeCode.Double: @@ -214,6 +224,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.UInt64: return (double)(ulong)input; case TypeCode.Single: return (double)(float)input; case TypeCode.Decimal: return (double)(decimal)input; + case TypeCode.Boolean: return (double)((bool)input ? 1 : 0); } break; case TypeCode.Decimal: @@ -229,6 +240,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.UInt64: return (decimal)(ulong)input; case TypeCode.Single: return (decimal)(float)input; case TypeCode.Double: return (decimal)(double)input; + case TypeCode.Boolean: return (decimal)((bool)input ? 1 : 0); } break; } @@ -256,6 +268,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.Single: return (char)(float)input; case TypeCode.Double: return (char)(double)input; case TypeCode.Decimal: return (char)(decimal)input; + case TypeCode.Boolean: return (char)((bool)input ? 1 : 0); } break; case TypeCode.SByte: @@ -271,6 +284,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.Single: return (sbyte)(float)input; case TypeCode.Double: return (sbyte)(double)input; case TypeCode.Decimal: return (sbyte)(decimal)input; + case TypeCode.Boolean: return (sbyte)((bool)input ? 1 : 0); } break; case TypeCode.Byte: @@ -286,6 +300,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.Single: return (byte)(float)input; case TypeCode.Double: return (byte)(double)input; case TypeCode.Decimal: return (byte)(decimal)input; + case TypeCode.Boolean: return (byte)((bool)input ? 1 : 0); } break; case TypeCode.Int16: @@ -301,6 +316,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.Single: return (short)(float)input; case TypeCode.Double: return (short)(double)input; case TypeCode.Decimal: return (short)(decimal)input; + case TypeCode.Boolean: return (short)((bool)input ? 1 : 0); } break; case TypeCode.UInt16: @@ -316,6 +332,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.Single: return (ushort)(float)input; case TypeCode.Double: return (ushort)(double)input; case TypeCode.Decimal: return (ushort)(decimal)input; + case TypeCode.Boolean: return (ushort)((bool)input ? 1 : 0); } break; case TypeCode.Int32: @@ -331,6 +348,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.Single: return (int)(float)input; case TypeCode.Double: return (int)(double)input; case TypeCode.Decimal: return (int)(decimal)input; + case TypeCode.Boolean: return (int)((bool)input ? 1 : 0); } break; case TypeCode.UInt32: @@ -346,6 +364,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.Single: return (uint)(float)input; case TypeCode.Double: return (uint)(double)input; case TypeCode.Decimal: return (uint)(decimal)input; + case TypeCode.Boolean: return (uint)((bool)input ? 1 : 0); } break; case TypeCode.Int64: @@ -361,6 +380,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.Single: return (long)(float)input; case TypeCode.Double: return (long)(double)input; case TypeCode.Decimal: return (long)(decimal)input; + case TypeCode.Boolean: return (long)((bool)input ? 1 : 0); } break; case TypeCode.UInt64: @@ -376,6 +396,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.Single: return (ulong)(float)input; case TypeCode.Double: return (ulong)(double)input; case TypeCode.Decimal: return (ulong)(decimal)input; + case TypeCode.Boolean: return (ulong)((bool)input ? 1 : 0); } break; case TypeCode.Single: @@ -391,6 +412,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.UInt64: return (float)(ulong)input; case TypeCode.Double: return (float)(double)input; case TypeCode.Decimal: return (float)(decimal)input; + case TypeCode.Boolean: return (float)((bool)input ? 1 : 0); } break; case TypeCode.Double: @@ -406,6 +428,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.UInt64: return (double)(ulong)input; case TypeCode.Single: return (double)(float)input; case TypeCode.Decimal: return (double)(decimal)input; + case TypeCode.Boolean: return (double)((bool)input ? 1 : 0); } break; case TypeCode.Decimal: @@ -421,6 +444,7 @@ namespace ICSharpCode.Decompiler.Util case TypeCode.UInt64: return (decimal)(ulong)input; case TypeCode.Single: return (decimal)(float)input; case TypeCode.Double: return (decimal)(double)input; + case TypeCode.Boolean: return (decimal)((bool)input ? 1 : 0); } break; } diff --git a/ICSharpCode.Decompiler/Util/CallbackOnDispose.cs b/ICSharpCode.Decompiler/Util/CallbackOnDispose.cs index f98601869..5fbdbb530 100644 --- a/ICSharpCode.Decompiler/Util/CallbackOnDispose.cs +++ b/ICSharpCode.Decompiler/Util/CallbackOnDispose.cs @@ -35,7 +35,7 @@ namespace ICSharpCode.Decompiler.Util public CallbackOnDispose(Action action) { if (action == null) - throw new ArgumentNullException("action"); + throw new ArgumentNullException(nameof(action)); this.action = action; } diff --git a/ICSharpCode.Decompiler/Util/EmptyList.cs b/ICSharpCode.Decompiler/Util/EmptyList.cs index 6fdef375b..d39ff4671 100644 --- a/ICSharpCode.Decompiler/Util/EmptyList.cs +++ b/ICSharpCode.Decompiler/Util/EmptyList.cs @@ -30,8 +30,8 @@ namespace ICSharpCode.Decompiler.Util private EmptyList() {} public T this[int index] { - get { throw new ArgumentOutOfRangeException("index"); } - set { throw new ArgumentOutOfRangeException("index"); } + get { throw new ArgumentOutOfRangeException(nameof(index)); } + set { throw new ArgumentOutOfRangeException(nameof(index)); } } public int Count { @@ -114,7 +114,7 @@ namespace ICSharpCode.Decompiler.Util public static class Empty { - public static readonly T[] Array = new T[0]; + public static readonly T[] Array = System.Array.Empty(); } public struct Unit { } diff --git a/ICSharpCode.Decompiler/Util/GraphVizGraph.cs b/ICSharpCode.Decompiler/Util/GraphVizGraph.cs index 3488a52f1..588733995 100644 --- a/ICSharpCode.Decompiler/Util/GraphVizGraph.cs +++ b/ICSharpCode.Decompiler/Util/GraphVizGraph.cs @@ -114,7 +114,7 @@ namespace ICSharpCode.Decompiler.Util public void Save(TextWriter writer) { if (writer == null) - throw new ArgumentNullException("writer"); + throw new ArgumentNullException(nameof(writer)); writer.WriteLine("digraph G {"); writer.WriteLine("node [fontsize = 16];"); WriteGraphAttribute(writer, "rankdir", rankdir); @@ -147,9 +147,9 @@ namespace ICSharpCode.Decompiler.Util public GraphVizEdge(string source, string target) { if (source == null) - throw new ArgumentNullException("source"); + throw new ArgumentNullException(nameof(source)); if (target == null) - throw new ArgumentNullException("target"); + throw new ArgumentNullException(nameof(target)); this.Source = source; this.Target = target; } @@ -195,7 +195,7 @@ namespace ICSharpCode.Decompiler.Util public GraphVizNode(string id) { if (id == null) - throw new ArgumentNullException("id"); + throw new ArgumentNullException(nameof(id)); this.ID = id; } diff --git a/ICSharpCode.Decompiler/Util/KeyComparer.cs b/ICSharpCode.Decompiler/Util/KeyComparer.cs index 43e26e9f9..5a604aa06 100644 --- a/ICSharpCode.Decompiler/Util/KeyComparer.cs +++ b/ICSharpCode.Decompiler/Util/KeyComparer.cs @@ -58,11 +58,11 @@ namespace ICSharpCode.Decompiler.Util public KeyComparer(Func keySelector, IComparer keyComparer, IEqualityComparer keyEqualityComparer) { if (keySelector == null) - throw new ArgumentNullException("keySelector"); + throw new ArgumentNullException(nameof(keySelector)); if (keyComparer == null) - throw new ArgumentNullException("keyComparer"); + throw new ArgumentNullException(nameof(keyComparer)); if (keyEqualityComparer == null) - throw new ArgumentNullException("keyEqualityComparer"); + throw new ArgumentNullException(nameof(keyEqualityComparer)); this.keySelector = keySelector; this.keyComparer = keyComparer; this.keyEqualityComparer = keyEqualityComparer; diff --git a/ICSharpCode.Decompiler/Util/ProjectedList.cs b/ICSharpCode.Decompiler/Util/ProjectedList.cs index 2291488f1..3a136cf36 100644 --- a/ICSharpCode.Decompiler/Util/ProjectedList.cs +++ b/ICSharpCode.Decompiler/Util/ProjectedList.cs @@ -30,9 +30,9 @@ namespace ICSharpCode.Decompiler.Util public ProjectedList(IList input, Func projection) { if (input == null) - throw new ArgumentNullException("input"); + throw new ArgumentNullException(nameof(input)); if (projection == null) - throw new ArgumentNullException("projection"); + throw new ArgumentNullException(nameof(projection)); this.input = input; this.projection = projection; this.items = new TOutput[input.Count]; @@ -75,9 +75,9 @@ namespace ICSharpCode.Decompiler.Util public ProjectedList(TContext context, IList input, Func projection) { if (input == null) - throw new ArgumentNullException("input"); + throw new ArgumentNullException(nameof(input)); if (projection == null) - throw new ArgumentNullException("projection"); + throw new ArgumentNullException(nameof(projection)); this.input = input; this.context = context; this.projection = projection; diff --git a/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs b/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs index a3e44ccad..a98d4bed9 100644 --- a/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs +++ b/ICSharpCode.Decompiler/Util/ResXResourceWriter.cs @@ -69,10 +69,10 @@ namespace ICSharpCode.Decompiler.Util public ResXResourceWriter(Stream stream) { if (stream == null) - throw new ArgumentNullException("stream"); + throw new ArgumentNullException(nameof(stream)); if (!stream.CanWrite) - throw new ArgumentException("stream is not writable.", "stream"); + throw new ArgumentException("stream is not writable.", nameof(stream)); this.stream = stream; } @@ -80,7 +80,7 @@ namespace ICSharpCode.Decompiler.Util public ResXResourceWriter(TextWriter textWriter) { if (textWriter == null) - throw new ArgumentNullException("textWriter"); + throw new ArgumentNullException(nameof(textWriter)); this.textwriter = textWriter; } @@ -88,7 +88,7 @@ namespace ICSharpCode.Decompiler.Util public ResXResourceWriter(string fileName) { if (fileName == null) - throw new ArgumentNullException("fileName"); + throw new ArgumentNullException(nameof(fileName)); this.filename = fileName; } @@ -217,10 +217,10 @@ namespace ICSharpCode.Decompiler.Util public void AddResource(string name, byte[] value) { if (name == null) - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); if (value == null) - throw new ArgumentNullException("value"); + throw new ArgumentNullException(nameof(value)); if (written) throw new InvalidOperationException("The resource is already generated."); @@ -244,7 +244,7 @@ namespace ICSharpCode.Decompiler.Util } if (name == null) - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); if (written) throw new InvalidOperationException("The resource is already generated."); @@ -307,10 +307,10 @@ namespace ICSharpCode.Decompiler.Util private void AddResource(string name, string value, string comment) { if (name == null) - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); if (value == null) - throw new ArgumentNullException("value"); + throw new ArgumentNullException(nameof(value)); if (written) throw new InvalidOperationException("The resource is already generated."); @@ -324,10 +324,10 @@ namespace ICSharpCode.Decompiler.Util public void AddMetadata(string name, string value) { if (name == null) - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); if (value == null) - throw new ArgumentNullException("value"); + throw new ArgumentNullException(nameof(value)); if (written) throw new InvalidOperationException("The resource is already generated."); @@ -347,10 +347,10 @@ namespace ICSharpCode.Decompiler.Util public void AddMetadata(string name, byte[] value) { if (name == null) - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); if (value == null) - throw new ArgumentNullException("value"); + throw new ArgumentNullException(nameof(value)); if (written) throw new InvalidOperationException("The resource is already generated."); @@ -383,10 +383,10 @@ namespace ICSharpCode.Decompiler.Util } if (name == null) - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); if (value == null) - throw new ArgumentNullException("value"); + throw new ArgumentNullException(nameof(value)); if (!value.GetType().IsSerializable) throw new InvalidOperationException(String.Format("The element '{0}' of type '{1}' is not serializable.", name, value.GetType().Name)); diff --git a/ILSpy.Tests/ILSpy.Tests.csproj b/ILSpy.Tests/ILSpy.Tests.csproj index 32c81dd25..901e19667 100644 --- a/ILSpy.Tests/ILSpy.Tests.csproj +++ b/ILSpy.Tests/ILSpy.Tests.csproj @@ -42,8 +42,8 @@ - - + + diff --git a/ILSpy/Analyzers/Builtin/AttributeAppliedToAnalyzer.cs b/ILSpy/Analyzers/Builtin/AttributeAppliedToAnalyzer.cs index 08f75db76..1f35d1867 100644 --- a/ILSpy/Analyzers/Builtin/AttributeAppliedToAnalyzer.cs +++ b/ILSpy/Analyzers/Builtin/AttributeAppliedToAnalyzer.cs @@ -24,6 +24,7 @@ using System.Reflection.Metadata; using System.Runtime.InteropServices; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.Util; namespace ICSharpCode.ILSpy.Analyzers.Builtin { @@ -33,7 +34,7 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) { if (!(analyzedSymbol is ITypeDefinition attributeType)) - return Array.Empty(); + return Empty.Array; var scope = context.GetScopeOf(attributeType); // TODO: DeclSecurity attributes are not supported. diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index bff8bd646..3e05b0f2a 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -436,6 +436,11 @@ namespace ICSharpCode.ILSpy static bool CanResolveTypeInPEFile(PEFile module, ITypeReference typeRef, out EntityHandle typeHandle) { + if (module == null) { + typeHandle = default; + return false; + } + switch (typeRef) { case GetPotentiallyNestedClassTypeReference topLevelType: typeHandle = topLevelType.ResolveInPEFile(module); diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 3283a7103..dd8e2a829 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -556,6 +556,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Decompile async IAsyncEnumerator methods. + /// + public static string DecompilerSettings_AsyncEnumerator { + get { + return ResourceManager.GetString("DecompilerSettings.AsyncEnumerator", resourceCulture); + } + } + /// /// Looks up a localized string similar to Decompile ?. and ?[] operators. /// @@ -664,6 +673,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Detect awaited using and foreach statements. + /// + public static string DecompilerSettings_DetectAsyncUsingAndForeachStatements { + get { + return ResourceManager.GetString("DecompilerSettings.DetectAsyncUsingAndForeachStatements", resourceCulture); + } + } + /// /// Looks up a localized string similar to Detect foreach statements. /// @@ -810,6 +828,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Read-only methods. + /// + public static string DecompilerSettings_ReadOnlyMethods { + get { + return ResourceManager.GetString("DecompilerSettings.ReadOnlyMethods", resourceCulture); + } + } + /// /// Looks up a localized string similar to Remove dead and side effect free code (use with caution!). /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index eb20f2e6e..7aa7db1df 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -447,9 +447,6 @@ Version - - Culture - Public Key Token @@ -769,4 +766,13 @@ Are you sure you want to continue? Decompile to new tab + + Decompile async IAsyncEnumerator methods + + + Read-only methods + + + Detect awaited using and foreach statements + \ No newline at end of file diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index f7771d9c6..52917540e 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -345,14 +345,20 @@ namespace ICSharpCode.ILSpy.TextView } return new FlowDocumentTooltip(renderer.CreateDocument()); } else if (segment.Reference is IEntity entity) { - return new FlowDocumentTooltip(CreateTooltipForEntity(entity)); + var document = CreateTooltipForEntity(entity); + if (document == null) + return null; + return new FlowDocumentTooltip(document); } else if (segment.Reference is ValueTuple unresolvedEntity) { var typeSystem = new DecompilerTypeSystem(unresolvedEntity.Item1, unresolvedEntity.Item1.GetAssemblyResolver(), TypeSystemOptions.Default | TypeSystemOptions.Uncached); try { IEntity resolved = typeSystem.MainModule.ResolveEntity(unresolvedEntity.Item2); if (resolved == null) return null; - return new FlowDocumentTooltip(CreateTooltipForEntity(resolved)); + var document = CreateTooltipForEntity(resolved); + if (document == null) + return null; + return new FlowDocumentTooltip(document); } catch (BadImageFormatException) { return null; }