diff --git a/DecompilerNuGetDemos.workbook b/DecompilerNuGetDemos.workbook index b6349e200..e0629fbc7 100644 --- a/DecompilerNuGetDemos.workbook +++ b/DecompilerNuGetDemos.workbook @@ -6,7 +6,7 @@ platforms: - DotNetCore packages: - id: ICSharpCode.Decompiler - version: 5.0.0.4793-preview2 + version: 5.0.0.5066-preview4 --- Setup: load the references required to work with the decompiler diff --git a/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj b/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj index 1fd74a245..f207dfe1f 100644 --- a/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj +++ b/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj @@ -7,7 +7,7 @@ true ilspycmd ilspycmd - 5.0.0-preview2 + 5.0.0-preview4 Command-line decompiler using the ILSpy decompilation engine Copyright 2011-2019 AlphaSierraPapa https://github.com/icsharpcode/ILSpy/ @@ -28,7 +28,7 @@ - + diff --git a/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj b/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj index 4dd7f895e..3477b5693 100644 --- a/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj +++ b/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj @@ -8,7 +8,7 @@ - + diff --git a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs index e5f200486..6d75516da 100644 --- a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs @@ -196,12 +196,6 @@ namespace ICSharpCode.Decompiler.Tests RunCS(options: options); } - [Test] - public void RefLocalsAndReturns([ValueSource("roslynOnlyOptions")] CompilerOptions options) - { - RunCS(options: options); - } - [Test] public void BitNot([Values(false, true)] bool force32Bit) { @@ -295,12 +289,6 @@ namespace ICSharpCode.Decompiler.Tests RunCS(options: options); } - [Test] - public void LocalFunctions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions options) - { - RunCS(options: options); - } - void RunCS([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug) { string testFileName = testName + ".cs"; diff --git a/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs b/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs index 2deaaab97..214dda115 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs @@ -40,6 +40,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers "System.Runtime.CompilerServices.IsByRefLikeAttribute", "System.Runtime.CompilerServices.IsUnmanagedAttribute", "System.Runtime.CompilerServices.NullableAttribute", + "System.Runtime.CompilerServices.NullableContextAttribute", "Microsoft.CodeAnalysis.EmbeddedAttribute", }; diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.VB.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.VB.cs index e09cecda9..55f0f5a3e 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.VB.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.VB.cs @@ -56,10 +56,10 @@ namespace ICSharpCode.Decompiler.Tests.Helpers var provider = new VBCodeProvider(new Dictionary { { "CompilerVersion", "v4.0" } }); CompilerParameters options = new CompilerParameters(); options.GenerateExecutable = !flags.HasFlag(CompilerOptions.Library); - options.CompilerOptions = "/o" + (flags.HasFlag(CompilerOptions.Optimize) ? "+" : "-"); + options.CompilerOptions = "/optimize" + (flags.HasFlag(CompilerOptions.Optimize) ? "+" : "-"); options.CompilerOptions += (flags.HasFlag(CompilerOptions.UseDebug) ? " /debug" : ""); options.CompilerOptions += (flags.HasFlag(CompilerOptions.Force32Bit) ? " /platform:anycpu32bitpreferred" : ""); - options.CompilerOptions += "/optioninfer+ /optionexplicit+"; + options.CompilerOptions += " /optioninfer+ /optionexplicit+"; if (preprocessorSymbols.Count > 0) { options.CompilerOptions += " /d:" + string.Join(",", preprocessorSymbols.Select(p => $"{p.Key}={p.Value}")); } diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs index dfe0d7cb2..1ad55293d 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs @@ -534,5 +534,27 @@ namespace ICSharpCode.Decompiler.Tests.Helpers // If the last try still fails, don't catch the exception action(); } + + public static void SignAssembly(string assemblyPath, string keyFilePath) + { + string snPath = SdkUtility.GetSdkPath("sn.exe"); + + ProcessStartInfo info = new ProcessStartInfo(snPath); + info.Arguments = $"-R \"{assemblyPath}\" \"{keyFilePath}\""; + info.RedirectStandardError = true; + info.RedirectStandardOutput = true; + info.UseShellExecute = false; + + Process process = Process.Start(info); + + var outputTask = process.StandardOutput.ReadToEndAsync(); + var errorTask = process.StandardError.ReadToEndAsync(); + + Task.WaitAll(outputTask, errorTask); + process.WaitForExit(); + + Console.WriteLine("output: " + outputTask.Result); + Console.WriteLine("errors: " + errorTask.Result); + } } } diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index e3e43be69..fe066f25f 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -7,7 +7,7 @@ True - 1701;1702;1705,67,169,1058,728,1720,649,168,251 + 1701;1702;1705,67,169,1058,728,1720,649,168,251,660,661,675 False @@ -42,13 +42,13 @@ - - + + - - + + @@ -61,11 +61,17 @@ + + + + + + @@ -74,6 +80,15 @@ + + + + + + + + + @@ -83,12 +98,12 @@ - - + + @@ -230,6 +245,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs index bfdbf2156..72e16db69 100644 --- a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs @@ -58,6 +58,12 @@ namespace ICSharpCode.Decompiler.Tests Run(); } + [Test] + public void Issue684() + { + Run(); + } + [Test] public void Issue959() { @@ -91,13 +97,19 @@ namespace ICSharpCode.Decompiler.Tests [Test] public void FSharpUsing_Debug() { - Run(settings: new DecompilerSettings { RemoveDeadCode = true }); + Run(settings: new DecompilerSettings { RemoveDeadStores = true }); } [Test] public void FSharpUsing_Release() { - Run(settings: new DecompilerSettings { RemoveDeadCode = true }); + Run(settings: new DecompilerSettings { RemoveDeadStores = true }); + } + + [Test] + public void DirectCallToExplicitInterfaceImpl() + { + Run(); } [Test] @@ -170,14 +182,14 @@ namespace ICSharpCode.Decompiler.Tests public void FSharpLoops_Debug() { CopyFSharpCoreDll(); - Run(settings: new DecompilerSettings { RemoveDeadCode = true }); + Run(settings: new DecompilerSettings { RemoveDeadStores = true }); } [Test] public void FSharpLoops_Release() { CopyFSharpCoreDll(); - Run(settings: new DecompilerSettings { RemoveDeadCode = true }); + Run(settings: new DecompilerSettings { RemoveDeadStores = true }); } void Run([CallerMemberName] string testName = null, DecompilerSettings settings = null) diff --git a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs index c13ee99a0..78de11172 100644 --- a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs @@ -18,7 +18,7 @@ using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests { - [TestFixture] + [TestFixture, Parallelizable(ParallelScope.All)] public class PdbGenerationTestRunner { static readonly string TestCasePath = Tester.TestCasePath + "/PdbGen"; diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 1e15135f1..401c23221 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -114,7 +114,7 @@ namespace ICSharpCode.Decompiler.Tests RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings { NullPropagation = false, // legacy csc generates a dead store in debug builds - RemoveDeadCode = (cscOptions == CompilerOptions.None) + RemoveDeadStores = (cscOptions == CompilerOptions.None) }); } @@ -123,7 +123,7 @@ namespace ICSharpCode.Decompiler.Tests { RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings { // legacy csc generates a dead store in debug builds - RemoveDeadCode = (cscOptions == CompilerOptions.None) + RemoveDeadStores = (cscOptions == CompilerOptions.None) }); } @@ -134,7 +134,7 @@ namespace ICSharpCode.Decompiler.Tests } [Test] - public void DelegateConstruction([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) + public void DelegateConstruction([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions) { RunForLibrary(cscOptions: cscOptions); } @@ -180,10 +180,16 @@ namespace ICSharpCode.Decompiler.Tests { RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings { // legacy csc generates a dead store in debug builds - RemoveDeadCode = (cscOptions == CompilerOptions.None) + RemoveDeadStores = (cscOptions == CompilerOptions.None) }); } + [Test] + public void LocalFunctions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) + { + RunForLibrary(cscOptions: cscOptions); + } + [Test] public void PropertiesAndEvents([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { @@ -233,6 +239,12 @@ namespace ICSharpCode.Decompiler.Tests RunForLibrary(cscOptions: cscOptions, asmOptions: AssemblerOptions.UseOwnDisassembler); } + [Test] + public void OutVariables([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) + { + RunForLibrary(cscOptions: cscOptions); + } + [Test] public void InitializerTests([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { @@ -317,6 +329,12 @@ namespace ICSharpCode.Decompiler.Tests RunForLibrary(cscOptions: cscOptions); } + [Test] + public void ThrowExpressions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) + { + RunForLibrary(cscOptions: cscOptions); + } + [Test] public void WellKnownConstants([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { @@ -420,7 +438,13 @@ namespace ICSharpCode.Decompiler.Tests } [Test] - public void YieldReturn([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) + public void YieldReturn([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions) + { + RunForLibrary(cscOptions: cscOptions); + } + + [Test] + public void UserDefinedConversions([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { RunForLibrary(cscOptions: cscOptions); } diff --git a/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs b/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs index dea1e49f5..210a45d13 100644 --- a/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs +++ b/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs @@ -69,11 +69,7 @@ namespace ICSharpCode.Decompiler.Tests [Test] public void ICSharpCode_Decompiler() { - try { - RunWithTest("ICSharpCode.Decompiler", "ICSharpCode.Decompiler.dll", "ICSharpCode.Decompiler.Tests.exe"); - } catch (CompilationFailedException) { - Assert.Ignore("C# 7 local functions not yet supported."); - } + RunWithTest("ICSharpCode.Decompiler", "ICSharpCode.Decompiler.dll", "ICSharpCode.Decompiler.Tests.exe"); } [Test] @@ -106,11 +102,11 @@ namespace ICSharpCode.Decompiler.Tests RunWithOutput("Random Tests\\TestCases", "TestCase-1.exe"); } - void RunWithTest(string dir, string fileToRoundtrip, string fileToTest) + void RunWithTest(string dir, string fileToRoundtrip, string fileToTest, string keyFile = null) { - RunInternal(dir, fileToRoundtrip, outputDir => RunTest(outputDir, fileToTest)); + RunInternal(dir, fileToRoundtrip, outputDir => RunTest(outputDir, fileToTest), keyFile); } - + void RunWithOutput(string dir, string fileToRoundtrip) { string inputDir = Path.Combine(TestDir, dir); @@ -118,7 +114,7 @@ namespace ICSharpCode.Decompiler.Tests outputDir => Tester.RunAndCompareOutput(fileToRoundtrip, Path.Combine(inputDir, fileToRoundtrip), Path.Combine(outputDir, fileToRoundtrip))); } - void RunInternal(string dir, string fileToRoundtrip, Action testAction) + void RunInternal(string dir, string fileToRoundtrip, Action testAction, string snkFilePath = null) { if (!Directory.Exists(TestDir)) { Assert.Ignore($"Assembly-roundtrip test ignored: test directory '{TestDir}' needs to be checked out separately." + Environment.NewLine + @@ -156,6 +152,9 @@ namespace ICSharpCode.Decompiler.Tests decompiler.Settings = new DecompilerSettings(LanguageVersion.CSharp7_3); // use a fixed GUID so that we can diff the output between different ILSpy runs without spurious changes decompiler.ProjectGuid = Guid.Parse("{127C83E4-4587-4CF9-ADCA-799875F3DFE6}"); + if (snkFilePath != null) { + decompiler.StrongNameKeyFile = Path.Combine(inputDir, snkFilePath); + } decompiler.DecompileProject(module, decompiledDir); Console.WriteLine($"Decompiled {fileToRoundtrip} in {w.Elapsed.TotalSeconds:f2}"); projectFile = Path.Combine(decompiledDir, module.Name + ".csproj"); diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/LocalFunctions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/LocalFunctions.cs deleted file mode 100644 index f05d95f27..000000000 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/LocalFunctions.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LocalFunctions -{ - class LocalFunctions - { - int field; - - public static void Main(string[] args) - { - StaticContextNoCapture(10); - StaticContextSimpleCapture(10); - StaticContextCaptureInForLoop(10); - var inst = new LocalFunctions() { field = 10 }; - inst.ContextNoCapture(); - inst.ContextSimpleCapture(); - inst.ContextCaptureInForLoop(); - } - - public static void StaticContextNoCapture(int length) - { - for (int i = 0; i < length; i++) { - LocalWrite("Hello " + i); - } - - void LocalWrite(string s) => Console.WriteLine(s); - } - - public static void StaticContextSimpleCapture(int length) - { - for (int i = 0; i < length; i++) { - LocalWrite(); - } - - void LocalWrite() => Console.WriteLine("Hello " + length); - } - - public static void StaticContextCaptureInForLoop(int length) - { - for (int i = 0; i < length; i++) { - void LocalWrite() => Console.WriteLine("Hello " + i + "/" + length); - LocalWrite(); - } - } - - public void ContextNoCapture() - { - for (int i = 0; i < field; i++) { - LocalWrite("Hello " + i); - } - - void LocalWrite(string s) => Console.WriteLine(s); - } - - public void ContextSimpleCapture() - { - for (int i = 0; i < field; i++) { - LocalWrite(); - } - - void LocalWrite() => Console.WriteLine("Hello " + field); - } - - public void ContextCaptureInForLoop() - { - for (int i = 0; i < field; i++) { - void LocalWrite() => Console.WriteLine("Hello " + i + "/" + field); - LocalWrite(); - } - } - } -} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/RefLocalsAndReturns.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/RefLocalsAndReturns.cs deleted file mode 100644 index 59f846290..000000000 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/RefLocalsAndReturns.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness -{ - class RefLocalsAndReturns - { - static int[] numbers = { 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023 }; - static string[] strings = { "Hello", "World" }; - static string NullString = ""; - static int DefaultInt = 0; - - public delegate ref TReturn RefFunc(T1 param1); - - public static TReturn Invoker(RefFunc action, T1 value) - { - return action(value); - } - - public static ref int FindNumber(int target) - { - for (int ctr = 0; ctr < numbers.Length; ctr++) { - if (numbers[ctr] >= target) - return ref numbers[ctr]; - } - return ref numbers[0]; - } - - public static ref int LastNumber() - { - return ref numbers[numbers.Length - 1]; - } - - public static ref int ElementAtOrDefault(int index) - { - return ref index < 0 || index >= numbers.Length ? ref DefaultInt : ref numbers[index]; - } - - public static ref int LastOrDefault() - { - return ref numbers.Length > 0 ? ref numbers[numbers.Length - 1] : ref DefaultInt; - } - - public static void DoubleNumber(ref int num) - { - Console.WriteLine("old: " + num); - num *= 2; - Console.WriteLine("new: " + num); - } - - public static ref string GetOrSetString(int index) - { - if (index < 0 || index >= strings.Length) - return ref NullString; - return ref strings[index]; - } - - public static void Main(string[] args) - { - DoubleNumber(ref FindNumber(32)); - Console.WriteLine(string.Join(", ", numbers)); - DoubleNumber(ref LastNumber()); - Console.WriteLine(string.Join(", ", numbers)); - Console.WriteLine(GetOrSetString(0)); - GetOrSetString(0) = "Goodbye"; - Console.WriteLine(string.Join(" ", strings)); - GetOrSetString(5) = "Here I mutated the null value!?"; - Console.WriteLine(GetOrSetString(-5)); - - Console.WriteLine(Invoker(x => ref numbers[x], 0)); - Console.WriteLine(LastOrDefault()); - LastOrDefault() = 10000; - Console.WriteLine(ElementAtOrDefault(-5)); - } - } -} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/TrickyTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/TrickyTypes.cs index 83b609b2f..437c37e54 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/TrickyTypes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/TrickyTypes.cs @@ -26,6 +26,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { InterestingConstants(); TruncatedComp(); + StringConcat(); } static void Print(T val) @@ -92,5 +93,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness Print(val1 <= val2); Print((int)val1 <= val2); } + + static void StringConcat() + { + // Some string.Concat()-cases that cannot be replaced using operator+ + Print(string.Concat("String concat:")); + Print(string.Concat(1, 2)); + Print(string.Concat(1, 2, "str")); + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.cs new file mode 100644 index 000000000..7e8fc4ee3 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.cs @@ -0,0 +1,14 @@ +using System; + +public sealed class TestClass : IDisposable +{ + void IDisposable.Dispose() + { + } + + public void Test(TestClass other) + { + ((IDisposable)this).Dispose(); + ((IDisposable)other).Dispose(); + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il new file mode 100644 index 000000000..4a9a59309 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il @@ -0,0 +1,36 @@ +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly DirectCallToExplicitInterfaceImpl +{ + .ver 1:0:0:0 +} +.module DirectCallToExplicitInterfaceImpl.exe + +.class public auto ansi sealed TestClass + extends [mscorlib]System.Object + implements [mscorlib]System.IDisposable +{ + // Methods + + .method private final hidebysig newslot virtual + instance void System.IDisposable.Dispose () cil managed + { + .override method instance void [mscorlib]System.IDisposable::Dispose() + ret + } + + .method public hidebysig void Test (class TestClass other) cil managed + { + ldarg.0 + call instance void TestClass::System.IDisposable.Dispose() + + ldarg.1 + call instance void TestClass::System.IDisposable.Dispose() + + ret + } + +} // end of class TestClass diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1047.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1047.cs index f5d32e786..93f375a02 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1047.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1047.cs @@ -6,12 +6,8 @@ private void ProblemMethod() { - IL_0000: while (!dummy) { } - return; - IL_0014: - goto IL_0000; } } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.cs new file mode 100644 index 000000000..5ec516778 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.cs @@ -0,0 +1,39 @@ +using System; + +public static class Issue684 +{ + static int Main(string[] A_0) + { + int[] array = new int[1000]; + int num = int.Parse(Console.ReadLine()); + // Point of this test was to ensure the stack slot here uses an appropriate type, + // (bool instead of int). Unfortunately our type fixup runs too late to affect variable names. + bool num2 = num >= 1000; + if (!num2) { + num2 = (num < 2); + } + if (num2) { + Console.WriteLine(-1); + } else { + int i = 2; + for (int num3 = 2; num3 <= num; num3 = i) { + Console.WriteLine(num3); + for (; i <= num; i += num3) { + int num4 = array[i] = 1; + } + i = num3; + while (true) { + bool num5 = i <= num; + if (num5) { + num5 = (array[i] != 0); + } + if (!num5) { + break; + } + i++; + } + } + } + return 0; + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.il new file mode 100644 index 000000000..55417b20b --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.il @@ -0,0 +1,133 @@ +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly Issue684 +{ + .ver 1:0:0:0 +} +.module Issue684.exe + +.class public auto ansi abstract sealed Issue684 + extends [mscorlib]System.Object +{ + // Methods + + .method static privatescope + int32 Main$PST06000001 ( + string[] '' + ) cil managed + { + // Method begins at RVA 0x2050 + // Code size 196 (0xc4) + .maxstack 11 + .entrypoint + .locals init ( + [0] int32, + [1] int32, + [2] int32, + [3] int32[], + [4] int32 + ) + + IL_0000: ldc.i4 1000 + IL_0005: newarr [mscorlib]System.Int32 + IL_000a: stloc.3 + IL_000b: call string [mscorlib]System.Console::ReadLine() + IL_0010: call int32 [mscorlib]System.Int32::Parse(string) + IL_0015: stloc.2 + IL_0016: ldloc.2 + IL_0017: ldc.i4 1000 + IL_001c: clt + IL_001e: ldc.i4.0 + IL_001f: ceq + IL_0021: dup + IL_0022: brtrue IL_0030 + + IL_0027: pop + IL_0028: ldloc.2 + IL_0029: ldc.i4 2 + IL_002e: clt + + IL_0030: brfalse IL_0045 + + IL_0035: ldc.i4 1 + IL_003a: neg + IL_003b: call void [mscorlib]System.Console::WriteLine(int32) + IL_0040: br IL_00c2 + + IL_0045: ldc.i4 2 + IL_004a: stloc.0 + IL_004b: ldc.i4 2 + IL_0050: stloc.1 + // loop start (head: IL_0051) + IL_0051: ldloc.1 + IL_0052: ldloc.2 + IL_0053: cgt + IL_0055: ldc.i4.0 + IL_0056: ceq + IL_0058: brfalse IL_00c2 + + IL_005d: ldloc.1 + IL_005e: call void [mscorlib]System.Console::WriteLine(int32) + // loop start (head: IL_0063) + IL_0063: ldloc.0 + IL_0064: ldloc.2 + IL_0065: cgt + IL_0067: ldc.i4.0 + IL_0068: ceq + IL_006a: brfalse IL_0088 + + IL_006f: ldc.i4 1 + IL_0074: stloc.s 4 + IL_0076: ldloc.3 + IL_0077: ldloc.0 + IL_0078: ldloc.s 4 + IL_007a: stelem.any [mscorlib]System.Int32 + IL_007f: ldloc.0 + IL_0080: ldloc.1 + IL_0081: add + IL_0082: stloc.0 + IL_0083: br IL_0063 + // end loop + + IL_0088: ldloc.1 + IL_0089: stloc.0 + // loop start (head: IL_008a) + IL_008a: ldloc.0 + IL_008b: ldloc.2 + IL_008c: cgt + IL_008e: ldc.i4.0 + IL_008f: ceq + IL_0091: dup + IL_0092: brfalse IL_00a9 + + IL_0097: pop + IL_0098: ldloc.3 + IL_0099: ldloc.0 + IL_009a: ldelem.any [mscorlib]System.Int32 + IL_009f: ldc.i4 0 + IL_00a4: ceq + IL_00a6: ldc.i4.0 + IL_00a7: ceq + + IL_00a9: brfalse IL_00bb + + IL_00ae: ldloc.0 + IL_00af: ldc.i4 1 + IL_00b4: add + IL_00b5: stloc.0 + IL_00b6: br IL_008a + // end loop + + IL_00bb: ldloc.0 + IL_00bc: stloc.1 + IL_00bd: br IL_0051 + // end loop + + IL_00c2: ldc.i4.0 + IL_00c3: ret + } // end of method Program::Main + +} // end of class Issue684 diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs index 14e6a8858..9f6b5c58a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs @@ -18,6 +18,7 @@ #pragma warning disable 1998 using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading.Tasks; @@ -25,6 +26,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class Async { + private int memberField; + public async void SimpleVoidMethod() { Console.WriteLine("Before"); @@ -71,6 +74,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("No Await"); } + public async Task CapturingThis() + { + await Task.Delay(memberField); + } + + public async Task CapturingThisWithoutAwait() + { + Console.WriteLine(memberField); + } + public async Task SimpleBoolTaskMethod() { Console.WriteLine("Before"); @@ -126,6 +139,46 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } #endif + + public static async Task GetIntegerSumAsync(IEnumerable items) + { + await Task.Delay(100); + int num = 0; + foreach (int item in items) { + num += item; + } + return num; + } + + public static Func> AsyncLambda() + { + return async () => await GetIntegerSumAsync(new int[3] { + 1, + 2, + 3 + }); + } + + public static Func> AsyncDelegate() + { + return async delegate { + await Task.Delay(10); + return 2; + }; + } + +#if CS70 + public static async Task AsyncLocalFunctions() + { + return await Nested(1) + await Nested(2); + + async Task Nested(int i) + { + await Task.Delay(i); + return i; + } + } +#endif } public struct HopToThreadPoolAwaitable : INotifyCompletion diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs index 5244e21c0..109831418 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs @@ -4577,12 +4577,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty new CustomClass().StringProp += 1; } -#if false public uint PreIncrementIndexer(string name) { return ++M()[name]; } -#endif + public int PreIncrementByRef(ref int i) { return ++i; @@ -4593,6 +4592,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return ++(*GetPointer()); } + public unsafe int PreIncrementOfPointer(int* ptr) + { + return *(++ptr); + } + public int PreIncrement2DArray() { return ++Array()[1, 2]; @@ -4627,12 +4631,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return array[Environment.TickCount] *= 10; } -#if false + public uint CompoundAssignIndexer(string name) { - return M()[name] -= 2; + return M()[name] -= 2u; } -#endif + + public uint CompoundAssignIndexerComplexIndex(string name) + { + return M()[ToString()] -= 2u; + } + public int CompoundAssignIncrement2DArray() { return Array()[1, 2] %= 10; @@ -4643,6 +4652,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return i <<= 2; } + public unsafe int* CompoundAssignOfPointer(int* ptr) + { + return ptr += 10; + } + public unsafe double CompoundAssignByPointer(double* ptr) { return *ptr /= 1.5; @@ -4669,17 +4683,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return array[pos]--; } -#if false + public uint PostIncrementIndexer(string name) { return M()[name]++; } +#if false public unsafe int PostIncrementOfPointer(int* ptr) { return *(ptr++); } #endif + public int PostDecrementInstanceField() { return M().Field--; @@ -4704,5 +4720,50 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return (*GetPointer())++; } + + public void Issue1552Pre(CustomStruct a, CustomStruct b) + { + CustomStruct customStruct = a + b; + Console.WriteLine(++customStruct); + } + + public void Issue1552Stmt(CustomStruct a, CustomStruct b) + { + CustomStruct customStruct = a + b; + ++customStruct; + } + + public void Issue1552StmtUseLater(CustomStruct a, CustomStruct b) + { + CustomStruct lhs = a + b; + ++lhs; + Console.WriteLine(); + Console.WriteLine(lhs * b); + } + + public void Issue1552Decimal(decimal a) + { + // Legacy csc compiles this using op_Increment, + // ensure we don't misdetect this as an invalid pre-increment "++(a * 10m)" + Console.WriteLine(a * 10m + 1m); + } + +#if !(ROSLYN && OPT) + // Roslyn opt no longer has a detectable post-increment pattern + // due to optimizing out some of the stores. + // Our emitted code is valid but has some additional temporaries. + public void Issue1552Post(CustomStruct a, CustomStruct b) + { + CustomStruct customStruct = a + b; + Console.WriteLine(customStruct++); + } + + public void Issue1552StmtTwice(CustomStruct a, CustomStruct b) + { + CustomStruct customStruct = a + b; + ++customStruct; + ++customStruct; + } +#endif } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstantsTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstantsTests.cs index f2cb636dd..83d9eb8bf 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstantsTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstantsTests.cs @@ -35,5 +35,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty private void Test(bool expr) { } + + private void Test(decimal expr) + { + } + + public void Decimal() + { + // Roslyn and legacy csc both normalize the decimal constant references, + // but to a different representation (ctor call vs. field use) + Test(0m); + Test(1m); + Test(-1m); + Test(decimal.MinValue); + Test(decimal.MaxValue); + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs index 1a748024d..4f3ed04a4 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs @@ -146,6 +146,64 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } + + public interface IM3 + { + void M3(); + } + public class BaseClass : IM3 + { + protected virtual void M1() + { + } + protected virtual void M2() + { + } + public virtual void M3() + { + } + } + public class SubClass : BaseClass + { + protected override void M2() + { + } + public new void M3() + { + } + + public void Test() + { + Noop("M1.base", base.M1); + Noop("M1", M1); + Noop("M2.base", base.M2); + Noop("M2", M2); + Noop("M3.base", base.M3); + Noop("M3.base_virt", ((BaseClass)this).M3); + Noop("M3.base_interface", ((IM3)this).M3); +#if CS70 + Noop("M3", this.M3); + Noop("M3", M3); + + void M3() + { + + } +#else + Noop("M3", M3); +#endif + } + public void Test2() + { + Noop("M3.new", new BaseClass().M3); + Noop("M3.new", new SubClass().M3); + } + + private void Noop(string name, Action _) + { + } + } + public static Func test0 = (string a, string b) => string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b); public static Func test1 = (string a, string b) => string.IsNullOrEmpty(a) || !string.IsNullOrEmpty(b); public static Func test2 = (string a, string b) => !string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b); @@ -159,6 +217,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { } + public static Predicate And(this Predicate filter1, Predicate filter2) + { + if (filter1 == null) { + return filter2; + } + if (filter2 == null) { + return filter1; + } + return (T m) => filter1(m) && filter2(m); + } + public static Action ExtensionMethodUnbound() { return Test; @@ -174,6 +243,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return ((string)null).Test; } + public static Predicate NoExtensionMethodOnLambda() + { + return And((int x) => x >= 0, (int x) => x <= 100); + } + public static object StaticMethod() { return new Func(ExtensionMethodBound); diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs index f328fc634..3f6ab5112 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs @@ -64,6 +64,34 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { } + private static void CallWithOut(out dynamic d) + { + d = null; + } + +#if CS70 + private static void CallWithIn(in dynamic d) + { + } +#endif + + private static void CallWithRef(ref dynamic d) + { + } + + private static void RefCallSiteTests() + { +#if CS70 + CallWithOut(out dynamic d); + CallWithIn(in d); +#else + dynamic d; + CallWithOut(out d); +#endif + CallWithRef(ref d); + d.SomeCall(); + } + private static void InvokeConstructor() { DynamicTests dynamicTests = new DynamicTests(); @@ -382,6 +410,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } + private static bool ConstantTarget(dynamic a) + { + return true.Equals(a); + } + + private static IntPtr NewIntPtr(dynamic a) + { + return new IntPtr(a); + } + private static dynamic GetDynamic(int i) { return null; diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.cs index 06b071f85..1472eda8f 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.cs @@ -243,7 +243,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("0"); } - + Console.WriteLine("End Try"); } catch { @@ -259,7 +259,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("Catch2"); } return B(10) && B(11); - } catch { + } catch { Console.WriteLine("Catch"); } finally { Console.WriteLine("Finally"); @@ -281,5 +281,75 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } } + + public void ReassignExceptionVar() + { + try { + Console.WriteLine("ReassignExceptionVar"); + } catch (Exception innerException) { + if (innerException.InnerException != null) { + innerException = innerException.InnerException; + } + Console.WriteLine(innerException); + } + } + + public int UseExceptionVarOutsideCatch() + { + Exception ex2; + try { + return 1; + } catch (Exception ex) { + ex2 = ex; + } + Console.WriteLine(ex2 != null); + return 2; + } + + public void GenericException(int input) where TException : Exception + { + try { + Console.WriteLine(input); + } catch (TException val) { + Console.WriteLine(val.Message); + throw; + } + } + + public void GenericException2() where T : Exception + { + try { + Console.WriteLine("CatchT"); +#if ROSLYN + } catch (T val) { + Console.WriteLine("{0} {1}", val, val.ToString()); + } +#else + } catch (T arg) { + Console.WriteLine("{0} {1}", arg, arg.ToString()); + } +#endif + } + +#if CS60 + public void GenericExceptionWithCondition(int input) where TException : Exception + { + try { + Console.WriteLine(input); + } catch (TException val) when (val.Message.Contains("Test")) { + Console.WriteLine(val.Message); + throw; + } + } + + public void GenericException2WithCondition(int input) where TException : Exception + { + try { + Console.WriteLine(input); + } catch (TException val) when (val.Message.Contains("Test")) { + Console.WriteLine("{0} {1}", val, val.ToString()); + } + } +#endif } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs index 1e9429978..e05e5bd91 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs @@ -585,7 +585,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty 2004, 2008, 2012 - }).Any)); + }).Any)); } public void MethodGroupConstant() @@ -625,16 +625,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty //no params ToCode(X(), () => call(() => 42)); //one param - ToCode(X(), () => from x in new int[2] { + ToCode(X(), () => new int[2] { 37, 42 - } - select x * 2); + }.Select((int x) => x * 2)); //two params ToCode(X(), () => new int[2] { - 37, - 42 - }.Select((int x, int i) => x * 2)); + 37, + 42 + }.Select((int x, int i) => x * 2)); } public void CurriedLambda() @@ -731,13 +730,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public void QuotedWithAnonymous() { - ToCode(X(), () => (from o in new[] { + ToCode(X(), () => new[] { new { X = "a", Y = "b" } - } - select o.X + o.Y).Single()); + }.Select(o => o.X + o.Y).Single()); } public void StaticCall() diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs index 13416aaf1..0aa2ecae5 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs @@ -1468,9 +1468,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests DateTimeFormat = new DateTimeFormatInfo { ShortDatePattern = "ddmmyy" }, - NumberFormat = (from format in source - where format.CurrencySymbol == "$" - select format).First() + NumberFormat = source.Where((NumberFormatInfo format) => format.CurrencySymbol == "$").First() } }); } @@ -1678,13 +1676,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests }); } - private void NestedListWithIndexInitializer(MyEnum myEnum) + private List> NestedListWithIndexInitializer(MyEnum myEnum) { -#if !OPT - List> list = new List> { -#else - List> obj = new List> { -#endif + return new List> { [0] = { 1, 2, @@ -1717,13 +1711,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests }); } - public static void Issue1390(IEnumerable tokens, bool alwaysAllowAdministrators, char wireDelimiter) + public static List> Issue1390(IEnumerable tokens, bool alwaysAllowAdministrators, char wireDelimiter) { -#if OPT - List> obj = new List> { -#else - List> list = new List> { -#endif + return new List> { { "tokens", string.Join(wireDelimiter.ToString(), tokens), diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs index 9620accb5..8ca021f08 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs @@ -32,11 +32,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty get; set; } + public static int StaticProperty { get; set; } + public bool BoolProperty { + get; + set; + } + public void SimpleInlineWithLocals() { int index; @@ -136,5 +142,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return InstanceProperty = GetIndex(); } + + public bool BoolPropertyTest(object x) + { + return BoolProperty = (x != null); + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs new file mode 100644 index 000000000..581f6eac6 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs @@ -0,0 +1,325 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.Linq; + +namespace LocalFunctions +{ + internal class LocalFunctions + { + private int field; + + private Lazy nonCapturinglocalFunctionInLambda = new Lazy(delegate { + return CreateValue(); + + object CreateValue() + { + return null; + } + }); + + private Lazy capturinglocalFunctionInLambda = new Lazy(delegate { + int x = 42; + return Do(); + + object Do() + { + return CreateValue(); + + int CreateValue() + { + return x; + } + } + }); + + private static void Test(int x) + { + } + + private static int GetInt(string a) + { + return a.Length; + } + + private static string GetString(int a) + { + return a.ToString(); + } + + public static void StaticContextNoCapture(int length) + { + for (int i = 0; i < length; i++) { + LocalWrite("Hello " + i); + } + + void LocalWrite(string s) + { + Console.WriteLine(s); + } + } + + public static void StaticContextSimpleCapture(int length) + { + for (int i = 0; i < length; i++) { + LocalWrite(); + } + + void LocalWrite() + { + Console.WriteLine("Hello " + length); + } + } + + public static void StaticContextCaptureForLoopVariable(int length) + { + int i; + for (i = 0; i < length; i++) { + LocalWrite(); + } + void LocalWrite() + { + Console.WriteLine("Hello " + i + "/" + length); + } + } + + public void ContextNoCapture() + { + for (int i = 0; i < field; i++) { + LocalWrite("Hello " + i); + } + + void LocalWrite(string s) + { + Console.WriteLine(s); + } + } + + public void ContextSimpleCapture() + { + for (int i = 0; i < field; i++) { + LocalWrite(); + } + + void LocalWrite() + { + Console.WriteLine("Hello " + field); + } + } + + public void ContextCaptureForLoopVariable() + { + int i; + for (i = 0; i < field; i++) { + LocalWrite(); + } + void LocalWrite() + { + Console.WriteLine("Hello " + i + "/" + field); + } + } + + public void CapturedOutsideLoop() + { + int i = 0; + while (i < field) { + i = GetInt("asdf"); + LocalWrite(); + } + + void LocalWrite() + { + Console.WriteLine("Hello " + i + "/" + field); + } + } + + public void CapturedInForeachLoop(IEnumerable args) + { + foreach (string arg2 in args) { + string arg = arg2; + LocalWrite(); + void LocalWrite() + { + Console.WriteLine("Hello " + arg); + } + } + } + + public void Overloading() + { + Test(5); + LocalFunctions.Test(2); + + void Test(int x) + { + Console.WriteLine("x: {0}", x); + } + } + + private void Name() + { + + } + + private void LocalFunctionHidingMethod() + { + Action action = this.Name; + Name(); + action(); + + void Name() + { + + } + } + + public void NamedArgument() + { + Use(Get(1), Get(2), Get(3)); + Use(Get(1), c: Get(2), b: Get(3)); + + int Get(int i) + { + return i; + } + + void Use(int a, int b, int c) + { + Console.WriteLine(a + b + c); + } + } + + public static Func LambdaInLocalFunction() + { + int x = (int)Math.Pow(2.0, 10.0); + return Create(); + + Func Create() + { + return () => x; + } + } + + public static Func MethodRef() + { + int x = (int)Math.Pow(2.0, 10.0); + Enumerable.Range(1, 100).Select(LocalFunction); + return null; + + int LocalFunction(int y) + { + return x * y; + } + } + + public static int Fib(int i) + { + return FibHelper(i); + + int FibHelper(int n) + { + if (n <= 0) { + return 0; + } + + return FibHelper(n - 1) + FibHelper(n - 2); + } + } + public int MutuallyRecursiveLocalFunctions() + { + return B(4) + C(3); + + int A(int i) + { + if (i > 0) { + return A(i - 1) + 2 * B(i - 1) + 3 * C(i - 1); + } + return 1; + } + + int B(int i) + { + if (i > 0) { + return 3 * A(i - 1) + B(i - 1); + } + return 1; + } + + int C(int i) + { + if (i > 0) { + return 2 * A(i - 1) + C(i - 1); + } + return 1; + } + } + + public static int NestedLocalFunctions(int i) + { + return A(); + + int A() + { + double x = Math.Pow(10.0, 2.0); + return B(); + + int B() + { + return i + (int)x; + } + } + } + + public static int LocalFunctionInLambda(IEnumerable xs) + { + return xs.First(delegate(int x) { + return Do(); + + bool Do() + { + return x == 3; + } + }); + } + + public static IEnumerable YieldReturn(int n) + { + return GetNumbers(); + + IEnumerable GetNumbers() + { + for (int i = 0; i < n; i++) { + yield return i; + } + } + } + + //public static void LocalFunctionInUsing() + //{ + // using (MemoryStream memoryStream = new MemoryStream()) { + // Do(); + + // void Do() + // { + // memoryStream.WriteByte(42); + // } + // } + //} + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs index 9ce4066cf..98b5df626 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs @@ -25,6 +25,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty private class MyClass { public int IntVal; + public readonly int ReadonlyIntVal; + public MyStruct StructField; + public readonly MyStruct ReadonlyStructField; public string Text; public MyClass Field; public MyClass Property { @@ -45,6 +48,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty private struct MyStruct { public int IntVal; + public readonly int ReadonlyIntVal; public MyClass Field; public MyStruct? Property1 => null; public MyStruct Property2 => default(MyStruct); @@ -178,6 +182,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { Use(GetMyClass()?.Text ?? "Hello"); } + + public void CallOnValueTypeField() + { + Use(GetMyClass()?.IntVal.ToString()); + Use(GetMyStruct()?.IntVal.ToString()); + Use(GetMyClass()?.ReadonlyIntVal.ToString()); + Use(GetMyStruct()?.ReadonlyIntVal.ToString()); + GetMyClass()?.StructField.Done(); + GetMyClass()?.ReadonlyStructField.Done(); + } public void InvokeDelegate(EventHandler eh) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs index 0c2cd8417..baf5e6184 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs @@ -1,15 +1,20 @@ #nullable enable +using System; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { - public class NullableRefTypes + public class T01_NullableRefTypes { private string field_string; private string? field_nullable_string; private dynamic? field_nullable_dynamic; private Dictionary field_generic; + private Dictionary field_generic2; + private Dictionary field_generic3; + private KeyValuePair field_generic_value_type; + private KeyValuePair? field_generic_nullable_value_type; private (string, string?, string) field_tuple; private string[]?[] field_array; private Dictionary<(string, string?), (int, string[]?, string?[])> field_complex; @@ -28,5 +33,67 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return field_nullable_string?.Length + arr?.Length; } + + public void GenericNullable((T1?, T1, T2, T2?, T1, T1?) x) where T1 : class where T2 : struct + { + } + + public T ByRef(ref T t) + { + return t; + } + + public void CallByRef(ref string a, ref string? b) + { + ByRef(ref a).ToString(); + ByRef(ref b)!.ToString(); + } + + public void Constraints() where C : class where CN : class? where NN : notnull where S : struct where D : IDisposable where DN : IDisposable? where NND : notnull, IDisposable + { + } + } + + public class T02_EverythingIsNullableInHere + { + private string? field1; + private object? field2; + // value types are irrelevant for the nullability attributes: + private int field3; + private int? field4; + + public string? Property { + get; + set; + } + public event EventHandler? Event; + } + + public class T03_EverythingIsNotNullableInHere + { + private string field1; + private object field2; + // value types are irrelevant for the nullability attributes: + private int field3; + private int? field4; + + public string Property { + get; + set; + } + public event EventHandler Event; + } + + public class T04_Dictionary where TKey : notnull + { + private struct Entry + { + public TKey key; + public TValue value; + } + + private int[]? _buckets; + private Entry[]? _entries; + private IEqualityComparer? _comparer; } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs index 80631f96a..cf97f492f 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs @@ -97,6 +97,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty }; } + private static string GetStr(int unused) + { + return " "; + } + + public static string Issue1567(string str1, string str2) + { + return string.Concat(str1.Replace('"', '\''), str2: str2.Replace('"', '\''), str1: GetStr(42)); + } + private void CallerMemberName([CallerMemberName] string memberName = null) { diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/RenameCallbackArguments.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OutVariables.cs similarity index 60% rename from ICSharpCode.Decompiler/CSharp/Resolver/RenameCallbackArguments.cs rename to ICSharpCode.Decompiler.Tests/TestCases/Pretty/OutVariables.cs index 082291dd0..5a6916b8e 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/RenameCallbackArguments.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OutVariables.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2010-2014 AlphaSierraPapa for the SharpDevelop Team +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software @@ -17,26 +17,29 @@ // DEALINGS IN THE SOFTWARE. using System; -using ICSharpCode.Decompiler.CSharp.Syntax; +using System.Collections.Generic; -namespace ICSharpCode.Decompiler.CSharp.Resolver +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { - /// - /// Arguments for the callback of . - /// - public class RenameCallbackArguments + public class PatternMatching { - public AstNode NodeToReplace { get; private set; } - public AstNode NewNode { get; private set; } - - public RenameCallbackArguments(AstNode nodeToReplace, AstNode newNode) + public static void OutVarInShortCircuit(Dictionary d) { - if (nodeToReplace == null) - throw new ArgumentNullException("nodeToReplace"); - if (newNode == null) - throw new ArgumentNullException("newNode"); - this.NodeToReplace = nodeToReplace; - this.NewNode = newNode; + if (d.Count > 2 && d.TryGetValue(42, out string value)) { + Console.WriteLine(value); + } + } + + public static Action CapturedOutVarInShortCircuit(Dictionary d) + { + // Note: needs reasoning about "definitely assigned if true" + // to ensure that the value is initialized when the delegate is declared. + if (d.Count > 2 && d.TryGetValue(42, out string value)) { + return delegate { + Console.WriteLine(value); + }; + } + return null; } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs index 46cbd3930..3f3c5cf59 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs @@ -221,12 +221,21 @@ namespace ICSharpCode.Decompiler.Tests.Pretty { } +#if CS72 + public static void Do(this ref DateTime test) + { + + } +#endif - public static void Do2(this int test) + public static void Do2(this int test, DateTime date) { test.Do(); ((IEnumerable)null).Any(); ((object)null).Do(); +#if CS72 + date.Do(); +#endif } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs index 67bc28561..63712439a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs @@ -2,10 +2,52 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { + internal static class Ext + { + public static void ExtOnRef(this ref RefLocalsAndReturns.NormalStruct s) + { + } + public static void ExtOnIn(this in RefLocalsAndReturns.NormalStruct s) + { + } + public static void ExtOnRef(this ref RefLocalsAndReturns.ReadOnlyStruct s) + { + } + public static void ExtOnIn(this in RefLocalsAndReturns.ReadOnlyStruct s) + { + } + public static void ExtOnRef(this ref RefLocalsAndReturns.ReadOnlyRefStruct s) + { + } + public static void ExtOnIn(this in RefLocalsAndReturns.ReadOnlyRefStruct s) + { + } + } + internal class RefLocalsAndReturns { + public struct Issue1630 + { + private object data; + + private int next; + + public static void Test() + { + Issue1630[] array = new Issue1630[1]; + int num = 0; + while (num >= 0) { + ref Issue1630 reference = ref array[num]; + Console.WriteLine(reference.data); + num = reference.next; + } + } + } + + public delegate ref T RefFunc(); public delegate ref readonly T ReadOnlyRefFunc(); + public delegate ref TReturn RefFunc(T1 param1); public ref struct RefStruct { @@ -35,6 +77,28 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } + private static int[] numbers = new int[10] { + 1, + 3, + 7, + 15, + 31, + 63, + 127, + 255, + 511, + 1023 + }; + + private static string[] strings = new string[2] { + "Hello", + "World" + }; + + private static string NullString = ""; + + private static int DefaultInt = 0; + public static ref T GetRef() { throw new NotImplementedException(); @@ -80,5 +144,97 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty ReadOnlyStruct readOnlyStruct = rs; readOnlyStruct.Method(); } + + public static TReturn Invoker(RefFunc action, T1 value) + { + return action(value); + } + + public static ref int FindNumber(int target) + { + for (int i = 0; i < numbers.Length; i++) { + if (numbers[i] >= target) { + return ref numbers[i]; + } + } + return ref numbers[0]; + } + + public static ref int LastNumber() + { + return ref numbers[numbers.Length - 1]; + } + + public static ref int ElementAtOrDefault(int index) + { + if (index >= 0 && index < numbers.Length) { + return ref numbers[index]; + } + return ref DefaultInt; + } + + public static ref int LastOrDefault() + { + if (numbers.Length != 0) { + return ref numbers[numbers.Length - 1]; + } + return ref DefaultInt; + } + + public static void DoubleNumber(ref int num) + { + Console.WriteLine("old: " + num); + num *= 2; + Console.WriteLine("new: " + num); + } + + public static ref string GetOrSetString(int index) + { + if (index < 0 || index >= strings.Length) { + return ref NullString; + } + + return ref strings[index]; + } + + public void CallSiteTests(NormalStruct s, ReadOnlyStruct r, ReadOnlyRefStruct rr) + { + s.ExtOnIn(); + s.ExtOnRef(); + r.ExtOnIn(); + r.ExtOnRef(); + rr.ExtOnIn(); + rr.ExtOnRef(); + CallOnInParam(in s, in r); + } + + public void RefReassignment(ref NormalStruct s) + { + ref NormalStruct @ref = ref GetRef(); + RefReassignment(ref @ref); + if (s.GetHashCode() == 0) { + @ref = ref GetRef(); + } + RefReassignment(ref @ref.GetHashCode() == 4 ? ref @ref : ref s); + } + + public static void Main(string[] args) + { + DoubleNumber(ref args.Length == 1 ? ref numbers[0] : ref DefaultInt); + DoubleNumber(ref FindNumber(32)); + Console.WriteLine(string.Join(", ", numbers)); + DoubleNumber(ref LastNumber()); + Console.WriteLine(string.Join(", ", numbers)); + Console.WriteLine(GetOrSetString(0)); + GetOrSetString(0) = "Goodbye"; + Console.WriteLine(string.Join(" ", strings)); + GetOrSetString(5) = "Here I mutated the null value!?"; + Console.WriteLine(GetOrSetString(-5)); + + Console.WriteLine(Invoker((int x) => ref numbers[x], 0)); + Console.WriteLine(LastOrDefault()); + LastOrDefault() = 10000; + Console.WriteLine(ElementAtOrDefault(-5)); + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs index aa5d29df2..874769b9b 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -1145,5 +1145,52 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } Console.WriteLine(); } + + public static string Issue1621(int x) + { + if (x == 5) { + return "5"; + } + switch (x) { + case 1: + return "1"; + case 2: + case 6: + case 7: + return "2-6-7"; + case 3: + return "3"; + case 4: + return "4"; + case 5: + return "unreachable"; + default: + throw new Exception(); + } + } + + public static int Issue1602(string x) + { + switch (x) { + case null: + return 0; + case "": + return -1; + case "A": + return 65; + case "B": + return 66; + case "C": + return 67; + case "D": + return 68; + case "E": + return 69; + case "F": + return 70; + default: + throw new ArgumentOutOfRangeException(); + } + } } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ThrowExpressions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ThrowExpressions.cs new file mode 100644 index 000000000..df8e1f41c --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ThrowExpressions.cs @@ -0,0 +1,247 @@ +using System; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + internal class ThrowExpressions + { + private class ArgumentCheckingCtor + { + private int initializedFromCtor = CountSheep() ?? throw new Exception("No sheep?!"); + private object cacheObj = TryGetObj() ?? throw new Exception("What?"); + + private object simpleObj; + private int? nullableInt; + + public ArgumentCheckingCtor(object simpleObj, int? nullableInt) + { + this.simpleObj = (simpleObj ?? throw new ArgumentNullException("simpleObj")); + this.nullableInt = (nullableInt ?? throw new ArgumentNullException("nullableInt")); + } + + public ArgumentCheckingCtor(string input) + : this(input, GetIntOrNull(input ?? throw new ArgumentNullException("input"))) + { + + } + + public ArgumentCheckingCtor(DataObject obj) + : this(obj ?? throw new Exception(), GetIntOrNull(obj.NullableDataField?.NullableDataField.ToString() ?? throw new ArgumentNullException("input"))) + { + + } + + private static int? GetIntOrNull(string v) + { + if (int.TryParse(v, out int result)) { + return result; + } + + return null; + } + + private static int? CountSheep() + { + throw new NotImplementedException(); + } + + private static object TryGetObj() + { + return null; + } + + public override int GetHashCode() + { + return initializedFromCtor; + } + + public override bool Equals(object obj) + { + return true; + } + } + + public class DataObject + { + public int IntField; + public int? NullableIntField; + public Data DataField; + public Data? NullableDataField; + public int IntProperty { + get; + set; + } + public int? NullableIntProperty { + get; + set; + } + public Data DataProperty { + get; + } + public Data? NullableDataProperty { + get; + } + } + + public struct Data + { + public int IntField; + public int? NullableIntField; + public MoreData DataField; + public MoreData? NullableDataField; + public int IntProperty { + get; + set; + } + public int? NullableIntProperty { + get; + set; + } + public MoreData DataProperty { + get; + } + public MoreData? NullableDataProperty { + get; + } + } + + public struct MoreData + { + public int IntField; + public int? NullableIntField; + public int IntProperty { + get; + set; + } + public int? NullableIntProperty { + get; + set; + } + } + + public static int IntField; + public static int? NullableIntField; + public static object ObjectField; + public int InstIntField; + public int? InstNullableIntField; + public object InstObjectField; + public Data DataField; + public Data? NullableDataField; + public DataObject DataObjectField; + + public static int IntProperty { + get; + } + public static int? NullableIntProperty { + get; + } + public static object ObjProperty { + get; + } + public int InstIntProperty { + get; + } + public int? InstNullableIntProperty { + get; + } + public object InstObjProperty { + get; + } + public Data DataProperty { + get; + } + public Data? NullableDataProperty { + get; + } + public DataObject DataObjectProperty { + get; + } + + public static int ReturnIntField() + { + return NullableIntField ?? throw new Exception(); + } + public static int ReturnIntProperty() + { + return NullableIntProperty ?? throw new Exception(); + } + public static object ReturnObjField() + { + return ObjectField ?? throw new Exception(); + } + public static object ReturnObjProperty() + { + return ObjProperty ?? throw new Exception(); + } + public static int ReturnIntField(ThrowExpressions inst) + { + return inst.InstNullableIntField ?? throw new Exception(); + } + public static int ReturnIntProperty(ThrowExpressions inst) + { + return inst.InstNullableIntProperty ?? throw new Exception(); + } + public static object ReturnObjField(ThrowExpressions inst) + { + return inst.InstObjectField ?? throw new Exception(); + } + public static object ReturnObjProperty(ThrowExpressions inst) + { + return inst.InstObjProperty ?? throw new Exception(); + } + + public static void UseComplexNullableStruct(ThrowExpressions inst) + { + Use(inst.InstNullableIntField ?? throw new Exception()); + Use((inst.NullableDataField ?? throw new Exception()).IntField); + Use(inst.NullableDataField?.NullableIntField ?? throw new Exception()); + Use((inst.NullableDataProperty ?? throw new Exception()).IntField); + Use(inst.NullableDataProperty?.NullableIntField ?? throw new Exception()); + Use((inst.NullableDataField ?? throw new Exception()).DataField.IntField); + Use(inst.NullableDataField?.DataField.NullableIntField ?? throw new Exception()); + Use((inst.NullableDataProperty ?? throw new Exception()).DataField.IntField); + Use(inst.NullableDataProperty?.DataField.NullableIntField ?? throw new Exception()); + Use((inst.NullableDataField ?? throw new Exception()).DataProperty.IntField); + Use(inst.NullableDataField?.DataProperty.NullableIntField ?? throw new Exception()); + Use((inst.NullableDataProperty ?? throw new Exception()).DataProperty.IntField); + Use(inst.NullableDataProperty?.DataProperty.NullableIntField ?? throw new Exception()); + Use(inst.NullableDataField?.NullableDataField?.IntField ?? throw new Exception()); + Use(inst.NullableDataField?.NullableDataField?.NullableIntField ?? throw new Exception()); + Use(inst.NullableDataProperty?.NullableDataField?.IntField ?? throw new Exception()); + Use(inst.NullableDataProperty?.NullableDataField?.NullableIntField ?? throw new Exception()); + Use(inst.NullableDataField?.NullableDataProperty?.IntField ?? throw new Exception()); + Use(inst.NullableDataField?.NullableDataProperty?.NullableIntField ?? throw new Exception()); + Use(inst.NullableDataProperty?.NullableDataProperty?.IntField ?? throw new Exception()); + Use(inst.NullableDataProperty?.NullableDataProperty?.NullableIntField ?? throw new Exception()); + } + + public static void UseComplexNullableObject(DataObject inst) + { + Use(inst?.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataField?.IntField ?? throw new Exception()); + Use(inst?.NullableDataField?.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.IntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataField?.DataField.IntField ?? throw new Exception()); + Use(inst?.NullableDataField?.DataField.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.DataField.IntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.DataField.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataField?.DataProperty.IntField ?? throw new Exception()); + Use(inst?.NullableDataField?.DataProperty.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.DataProperty.IntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.DataProperty.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataField?.NullableDataField?.IntField ?? throw new Exception()); + Use(inst?.NullableDataField?.NullableDataField?.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.NullableDataField?.IntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.NullableDataField?.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataField?.NullableDataProperty?.IntField ?? throw new Exception()); + Use(inst?.NullableDataField?.NullableDataProperty?.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.NullableDataProperty?.IntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.NullableDataProperty?.NullableIntField ?? throw new Exception()); + } + + public static void Use(T usage) + { + + } + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs index 6fd605ccd..de17663e9 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs @@ -38,6 +38,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } + public struct GenericStruct + { + public T Field; + public T Property { + get; + set; + } + } + public ValueTuple VT0; public ValueTuple VT1; public ValueTuple VT7EmptyRest; @@ -88,6 +97,36 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public object NotTargetTyping => ((string)null, (object)1, (Action)delegate { }); + public void UnnamedTupleOut(out (int, string, Action, dynamic) tuple) + { + tuple = (42, "Hello", Console.WriteLine, null); + } + + public void UnnamedTupleIn(in (int, string, Action, dynamic) tuple) + { + + } + + public void UnnamedTupleRef(ref (int, string, Action, dynamic) tuple) + { + + } + + public void NamedTupleOut(out (int A, string B, Action C, dynamic D) tuple) + { + tuple = (42, "Hello", Console.WriteLine, null); + } + + public void NamedTupleIn(in (int A, string B, Action C, dynamic D) tuple) + { + + } + + public void NamedTupleRef(ref (int A, string B, Action C, dynamic D) tuple) + { + + } + public void UseDict() { if (TupleDict.Count > 10) { @@ -140,5 +179,26 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty (2, "b") }); } + + public void DynamicTuple((dynamic A, dynamic B) a) + { + a.A.DynamicCall(); + a.B.Dynamic = 42; + } + + public void GenericStructWithElementNames(GenericStruct<(int A, int B)> s) + { + Console.WriteLine(s.Field.A + s.Property.B); + } + + public void RefCallSites(out (int, string, Action, dynamic) tuple) + { + UnnamedTupleOut(out tuple); + UnnamedTupleIn(in tuple); + UnnamedTupleRef(ref tuple); + NamedTupleOut(out tuple); + NamedTupleIn(in tuple); + NamedTupleRef(ref tuple); + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UserDefinedConversions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UserDefinedConversions.cs new file mode 100644 index 000000000..a91ca7a82 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UserDefinedConversions.cs @@ -0,0 +1,134 @@ +// Copyright (c) 2019 Daniel Grunwald +// +// 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. + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + internal class T01Issue1574 + { + private struct A + { + private bool val; + + public static implicit operator bool(A a) + { + return a.val; + } + } + + private struct C + { + private int val; + + public static implicit operator C(bool b) + { + return default(C); + } + } + + private C ChainedConversion() + { + return (bool)default(A); + } + + public void Call_Overloaded() + { + Overloaded((bool)default(A)); + } + + private void Overloaded(A a) + { + } + + private void Overloaded(bool a) + { + } + } + + internal class T02BothDirectAndChainedConversionPossible + { + private struct A + { + private bool val; + + public static implicit operator bool(A a) + { + return a.val; + } + } + + private struct C + { + private int val; + + public static implicit operator C(bool b) + { + return default(C); + } + + public static implicit operator C(A a) + { + return default(C); + } + + public static bool operator ==(C a, C b) + { + return true; + } + public static bool operator !=(C a, C b) + { + return false; + } + } + + private C DirectConvert(A a) + { + return a; + } + + private C IndirectConvert(A a) + { + return (bool)a; + } + + private C? LiftedDirectConvert(A? a) + { + return a; + } + + private C? LiftedIndirectConvert(A? a) + { + return (bool?)a; + } + + private bool Compare(A a, C c) + { + return a == c; + } + + private void LiftedCompare(A? a, C? c) + { + UseBool(a == c); + UseBool(a == default(C)); + UseBool(c == default(A)); + } + + private void UseBool(bool b) + { + } + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs index e03c5468e..346c3b13e 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs @@ -66,6 +66,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty S s = this; s.SetField(); } + + public void UseField(int val) + { + UseField(Get().Field); + } } #if CS72 @@ -263,5 +268,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty r.Property = 2; #endif } + + public static void CallOnFieldOfTemporary() + { + Get().Field.ToString(); + } } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs index 3b2818941..0306343d3 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs @@ -335,5 +335,26 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty yield return val; } } + + public static IEnumerable MultipleYieldBreakInTryFinally(int i) + { + try { + if (i == 2) { + yield break; + } + + while (i < 40) { + if (i % 2 == 0) { + yield break; + } + i++; + + yield return i; + } + } finally { + Console.WriteLine("finally"); + } + Console.WriteLine("normal exit"); + } } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/.gitignore b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/.gitignore new file mode 100644 index 000000000..6a7461313 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/.gitignore @@ -0,0 +1 @@ +*.dll diff --git a/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.cs b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.cs new file mode 100644 index 000000000..8b8807ec4 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.cs @@ -0,0 +1,20 @@ +using Microsoft.VisualBasic; +using Microsoft.VisualBasic.CompilerServices; + +[StandardModule] +internal sealed class VBCompoundAssign +{ + public static double[] Sum3(int[] v) + { + double[] array = new double[4]; + int num = Information.UBound(v); + checked { + for (int i = 0; i <= num; i += 3) { + array[0] += v[i]; + array[1] += v[i + 1]; + array[2] += v[i + 2]; + } + return array; + } + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.vb b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.vb new file mode 100644 index 000000000..2dc0c7d96 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.vb @@ -0,0 +1,14 @@ +Imports System +Imports Microsoft.VisualBasic + +Module VBCompoundAssign + Function Sum3(v As Int32()) As Double() + Dim arr(3) As Double + For i = 0 To UBound(v) Step 3 + arr(0) += v(i) + arr(1) += v(i + 1) + arr(2) += v(i + 2) + Next + Return arr + End Function +End Module diff --git a/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs b/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs index dc7d6bad1..273cf2b38 100644 --- a/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs +++ b/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs @@ -707,8 +707,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem public void InOutParametersOnRefMethod() { IParameter p = GetTypeDefinition(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod").Parameters.Single(); - Assert.IsTrue(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.Ref, p.ReferenceKind); var attr = p.GetAttributes().ToList(); Assert.AreEqual(2, attr.Count); Assert.AreEqual("System.Runtime.InteropServices.InAttribute", attr[0].AttributeType.FullName); @@ -728,9 +727,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOutParameter").Parameters.Single(); Assert.IsFalse(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsTrue(p.IsOut); - Assert.IsFalse(p.IsIn); + Assert.AreEqual(ReferenceKind.Out, p.ReferenceKind); Assert.AreEqual(0, p.GetAttributes().Count()); Assert.IsTrue(p.Type.Kind == TypeKind.ByReference); } @@ -740,9 +737,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithRefParameter").Parameters.Single(); Assert.IsFalse(p.IsOptional); - Assert.IsTrue(p.IsRef); - Assert.IsFalse(p.IsOut); - Assert.IsFalse(p.IsIn); + Assert.AreEqual(ReferenceKind.Ref, p.ReferenceKind); Assert.AreEqual(0, p.GetAttributes().Count()); Assert.IsTrue(p.Type.Kind == TypeKind.ByReference); } @@ -752,9 +747,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithInParameter").Parameters.Single(); Assert.IsFalse(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); - Assert.IsTrue(p.IsIn); + Assert.AreEqual(ReferenceKind.In, p.ReferenceKind); Assert.AreEqual(0, p.GetAttributes().Count()); Assert.IsTrue(p.Type.Kind == TypeKind.ByReference); } @@ -764,8 +757,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithParamsArray").Parameters.Single(); Assert.IsFalse(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsTrue(p.IsParams); Assert.AreEqual(0, p.GetAttributes().Count()); Assert.IsTrue(p.Type.Kind == TypeKind.Array); @@ -776,8 +768,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalParameter").Parameters.Single(); Assert.IsTrue(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsFalse(p.IsParams); Assert.IsTrue(p.HasConstantValueInSignature); Assert.AreEqual(0, p.GetAttributes().Count()); @@ -789,8 +780,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithExplicitOptionalParameter").Parameters.Single(); Assert.IsTrue(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsFalse(p.IsParams); Assert.IsFalse(p.HasConstantValueInSignature); // explicit optional parameter appears in type system if it's read from C#, but not when read from IL @@ -802,8 +792,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithEnumOptionalParameter").Parameters.Single(); Assert.IsTrue(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsFalse(p.IsParams); Assert.IsTrue(p.HasConstantValueInSignature); Assert.AreEqual(0, p.GetAttributes().Count()); @@ -815,8 +804,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalNullableParameter").Parameters.Single(); Assert.IsTrue(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsFalse(p.IsParams); Assert.IsTrue(p.HasConstantValueInSignature); Assert.AreEqual(0, p.GetAttributes().Count()); @@ -828,8 +816,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalLongParameter").Parameters.Single(); Assert.IsTrue(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsFalse(p.IsParams); Assert.IsTrue(p.HasConstantValueInSignature); Assert.AreEqual(1L, p.GetConstantValue()); @@ -841,8 +828,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalNullableLongParameter").Parameters.Single(); Assert.IsTrue(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsFalse(p.IsParams); Assert.IsTrue(p.HasConstantValueInSignature); Assert.AreEqual(1L, p.GetConstantValue()); @@ -854,8 +840,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalDecimalParameter").Parameters.Single(); Assert.IsTrue(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsFalse(p.IsParams); Assert.IsTrue(p.HasConstantValueInSignature); Assert.AreEqual(1M, p.GetConstantValue()); @@ -867,8 +852,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "VarArgsMethod").Parameters.Single(); Assert.IsFalse(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsFalse(p.IsParams); Assert.AreEqual(TypeKind.ArgList, p.Type.Kind); Assert.AreEqual("", p.Name); @@ -879,8 +863,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(VarArgsCtor)).Methods.Single(m => m.IsConstructor).Parameters.Single(); Assert.IsFalse(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsFalse(p.IsParams); Assert.AreEqual(TypeKind.ArgList, p.Type.Kind); Assert.AreEqual("", p.Name); diff --git a/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs index e8d1d8071..8cf1c28c0 100644 --- a/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs @@ -61,17 +61,27 @@ namespace ICSharpCode.Decompiler.Tests }; [Test, Ignore("Implement VB async/await")] - public void Async([ValueSource("defaultOptions")] CompilerOptions options) + public void Async([ValueSource(nameof(defaultOptions))] CompilerOptions options) { Run(options: options); } + [Test] // TODO: legacy VB compound assign + public void VBCompoundAssign([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions options) + { + Run(options: options | CompilerOptions.Library); + } + void Run([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug, DecompilerSettings settings = null) { var vbFile = Path.Combine(TestCasePath, testName + ".vb"); var csFile = Path.Combine(TestCasePath, testName + ".cs"); + var exeFile = Path.Combine(TestCasePath, testName) + Tester.GetSuffix(options) + ".exe"; + if (options.HasFlag(CompilerOptions.Library)) { + exeFile = Path.ChangeExtension(exeFile, ".dll"); + } - var executable = Tester.CompileVB(vbFile, options); + var executable = Tester.CompileVB(vbFile, options | CompilerOptions.ReferenceVisualBasic, exeFile); var decompiled = Tester.DecompileCSharp(executable.PathToAssembly, settings); CodeAssert.FilesAreEqual(csFile, decompiled); diff --git a/ICSharpCode.Decompiler/CSharp/Annotations.cs b/ICSharpCode.Decompiler/CSharp/Annotations.cs index 064da15f2..dcd3b23eb 100644 --- a/ICSharpCode.Decompiler/CSharp/Annotations.cs +++ b/ICSharpCode.Decompiler/CSharp/Annotations.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Linq; +using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Semantics; @@ -104,7 +105,23 @@ namespace ICSharpCode.Decompiler.CSharp public static ISymbol GetSymbol(this AstNode node) { var rr = node.Annotation(); - return rr != null ? rr.GetSymbol() : null; + if (rr is MethodGroupResolveResult) { + // delegate construction? + var newObj = node.Annotation(); + if (newObj != null) { + var funcptr = newObj.Arguments.ElementAtOrDefault(1); + if (funcptr is LdFtn ldftn) { + return ldftn.Method; + } else if (funcptr is LdVirtFtn ldVirtFtn) { + return ldVirtFtn.Method; + } + } + var ldVirtDelegate = node.Annotation(); + if (ldVirtDelegate != null) { + return ldVirtDelegate.Method; + } + } + return rr?.GetSymbol(); } /// @@ -240,4 +257,18 @@ namespace ICSharpCode.Decompiler.CSharp this.Leave = leave; } } + + /// + /// Annotates an expression when an implicit user-defined conversion was omitted. + /// + public class ImplicitConversionAnnotation + { + public readonly ConversionResolveResult ConversionResolveResult; + public IType TargetType => ConversionResolveResult.Type; + + public ImplicitConversionAnnotation(ConversionResolveResult conversionResolveResult) + { + this.ConversionResolveResult = conversionResolveResult; + } + } } diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 5863da05e..994be0c13 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -131,7 +131,6 @@ namespace ICSharpCode.Decompiler.CSharp // copy-propated (turned into two separate assignments of the constant). // After is necessary because the assigned value might involve null coalescing/etc. new StatementTransform(new ILInlining(), new TransformAssignment()), - new CopyPropagation(), new StatementTransform( // per-block transforms that depend on each other, and thus need to // run interleaved (statement by statement). @@ -155,7 +154,11 @@ namespace ICSharpCode.Decompiler.CSharp } }, new ProxyCallReplacer(), + new FixRemainingIncrements(), + new CopyPropagation(), new DelegateConstruction(), + new LocalFunctionDecompiler(), + new TransformDisplayClassUsage(), new HighLevelLoopTransform(), new ReduceNestingTransform(), new IntroduceDynamicTypeOnLocals(), @@ -395,7 +398,7 @@ namespace ICSharpCode.Decompiler.CSharp return new DecompilerTypeSystem(file, resolver); } - TypeSystemAstBuilder CreateAstBuilder(ITypeResolveContext decompilationContext) + static TypeSystemAstBuilder CreateAstBuilder(ITypeResolveContext decompilationContext) { var typeSystemAstBuilder = new TypeSystemAstBuilder(); typeSystemAstBuilder.ShowAttributes = true; @@ -620,39 +623,7 @@ namespace ICSharpCode.Decompiler.CSharp var memberRef = module.Metadata.GetMemberReference((MemberReferenceHandle)token); if (memberRef.GetKind() != MemberReferenceKind.Field) continue; - switch (memberRef.Parent.Kind) { - case HandleKind.TypeReference: - // This should never happen in normal code, because we are looking at nested types - // If it's not a nested type, it can't be a reference to the state machine anyway, and - // those should be either TypeDef or TypeSpec. - continue; - case HandleKind.TypeDefinition: - fsmTypeDef = (TypeDefinitionHandle)memberRef.Parent; - break; - case HandleKind.TypeSpecification: - var ts = module.Metadata.GetTypeSpecification((TypeSpecificationHandle)memberRef.Parent); - if (ts.Signature.IsNil) - continue; - // Do a quick scan using BlobReader - var signature = module.Metadata.GetBlobReader(ts.Signature); - // When dealing with FSM implementations, we can safely assume that if it's a type spec, - // it must be a generic type instance. - if (signature.ReadByte() != (byte)SignatureTypeCode.GenericTypeInstance) - continue; - // Skip over the rawTypeKind: value type or class - var rawTypeKind = signature.ReadCompressedInteger(); - if (rawTypeKind < 17 || rawTypeKind > 18) - continue; - // Only read the generic type, ignore the type arguments - var genericType = signature.ReadTypeHandle(); - // Again, we assume this is a type def, because we are only looking at nested types - if (genericType.Kind != HandleKind.TypeDefinition) - continue; - fsmTypeDef = (TypeDefinitionHandle)genericType; - break; - default: - continue; - } + fsmTypeDef = ExtractDeclaringType(memberRef); break; default: continue; @@ -662,10 +633,10 @@ namespace ICSharpCode.Decompiler.CSharp // Must be a nested type of the containing type. if (fsmType.GetDeclaringType() != declaringType) break; - if (!processedNestedTypes.Add(fsmTypeDef)) - break; if (YieldReturnDecompiler.IsCompilerGeneratorEnumerator(fsmTypeDef, module.Metadata) || AsyncAwaitDecompiler.IsCompilerGeneratedStateMachine(fsmTypeDef, module.Metadata)) { + if (!processedNestedTypes.Add(fsmTypeDef)) + break; foreach (var h in fsmType.GetMethods()) { if (module.MethodSemanticsLookup.GetSemantics(h).Item2 != 0) continue; @@ -680,9 +651,66 @@ namespace ICSharpCode.Decompiler.CSharp case ILOpCode.Ldftn: // deal with ldftn instructions, i.e., lambdas token = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32()); - if (!token.IsNil && token.Kind == HandleKind.MethodDefinition) { - if (((MethodDefinitionHandle)token).IsCompilerGeneratedOrIsInCompilerGeneratedClass(module.Metadata)) - connectedMethods.Enqueue((MethodDefinitionHandle)token); + if (token.IsNil) + continue; + TypeDefinitionHandle closureTypeHandle; + switch (token.Kind) { + case HandleKind.MethodDefinition: + if (((MethodDefinitionHandle)token).IsCompilerGeneratedOrIsInCompilerGeneratedClass(module.Metadata)) { + connectedMethods.Enqueue((MethodDefinitionHandle)token); + } + continue; + case HandleKind.MemberReference: + var memberRef = module.Metadata.GetMemberReference((MemberReferenceHandle)token); + if (memberRef.GetKind() != MemberReferenceKind.Method) + continue; + closureTypeHandle = ExtractDeclaringType(memberRef); + if (!closureTypeHandle.IsNil) { + var closureType = module.Metadata.GetTypeDefinition(closureTypeHandle); + if (closureTypeHandle != declaringType) { + // Must be a nested type of the containing type. + if (closureType.GetDeclaringType() != declaringType) + break; + if (!processedNestedTypes.Add(closureTypeHandle)) + break; + foreach (var m in closureType.GetMethods()) { + connectedMethods.Enqueue(m); + } + } else { + // Delegate body is declared in the same type + foreach (var m in closureType.GetMethods()) { + var methodDef = module.Metadata.GetMethodDefinition(m); + if (methodDef.Name == memberRef.Name) + connectedMethods.Enqueue(m); + } + } + break; + } + break; + default: + continue; + } + break; + case ILOpCode.Call: + case ILOpCode.Callvirt: + // deal with call/callvirt instructions, i.e., local function invocations + token = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32()); + if (token.IsNil) + continue; + switch (token.Kind) { + case HandleKind.MethodDefinition: + break; + case HandleKind.MethodSpecification: + var methodSpec = module.Metadata.GetMethodSpecification((MethodSpecificationHandle)token); + if (methodSpec.Method.IsNil || methodSpec.Method.Kind != HandleKind.MethodDefinition) + continue; + token = methodSpec.Method; + break; + default: + continue; + } + if (LocalFunctionDecompiler.IsLocalFunctionMethod(module, (MethodDefinitionHandle)token)) { + connectedMethods.Enqueue((MethodDefinitionHandle)token); } break; default: @@ -692,6 +720,40 @@ namespace ICSharpCode.Decompiler.CSharp } info.AddMapping(parent, part); + + TypeDefinitionHandle ExtractDeclaringType(MemberReference memberRef) + { + switch (memberRef.Parent.Kind) { + case HandleKind.TypeReference: + // This should never happen in normal code, because we are looking at nested types + // If it's not a nested type, it can't be a reference to the state machine or lambda anyway, and + // those should be either TypeDef or TypeSpec. + return default; + case HandleKind.TypeDefinition: + return (TypeDefinitionHandle)memberRef.Parent; + case HandleKind.TypeSpecification: + var ts = module.Metadata.GetTypeSpecification((TypeSpecificationHandle)memberRef.Parent); + if (ts.Signature.IsNil) + return default; + // Do a quick scan using BlobReader + var signature = module.Metadata.GetBlobReader(ts.Signature); + // When dealing with FSM implementations, we can safely assume that if it's a type spec, + // it must be a generic type instance. + if (signature.ReadByte() != (byte)SignatureTypeCode.GenericTypeInstance) + return default; + // Skip over the rawTypeKind: value type or class + var rawTypeKind = signature.ReadCompressedInteger(); + if (rawTypeKind < 17 || rawTypeKind > 18) + return default; + // Only read the generic type, ignore the type arguments + var genericType = signature.ReadTypeHandle(); + // Again, we assume this is a type def, because we are only looking at nested types + if (genericType.Kind != HandleKind.TypeDefinition) + return default; + return (TypeDefinitionHandle)genericType; + } + return default; + } } /// diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 61494fceb..c765b12d4 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -54,11 +54,16 @@ namespace ICSharpCode.Decompiler.CSharp public bool IsExpandedForm; public int Length => Arguments.Length; + private int GetActualArgumentCount() + { + if (FirstOptionalArgumentIndex < 0) + return Arguments.Length; + return FirstOptionalArgumentIndex; + } + public IEnumerable GetArgumentResolveResults(int skipCount = 0) { - return FirstOptionalArgumentIndex < 0 - ? Arguments.Skip(skipCount).Select(a => a.ResolveResult) - : Arguments.Skip(skipCount).Take(FirstOptionalArgumentIndex).Select(a => a.ResolveResult); + return Arguments.Skip(skipCount).Take(GetActualArgumentCount()).Select(a => a.ResolveResult); } public IEnumerable GetArgumentExpressions(int skipCount = 0) @@ -77,22 +82,12 @@ namespace ICSharpCode.Decompiler.CSharp } } } + int argumentCount = GetActualArgumentCount(); if (ArgumentNames == null) { - if (FirstOptionalArgumentIndex < 0) - return Arguments.Skip(skipCount).Select(arg => arg.Expression); - return Arguments.Skip(skipCount).Take(FirstOptionalArgumentIndex).Select(arg => arg.Expression); + return Arguments.Skip(skipCount).Take(argumentCount).Select(arg => arg.Expression); } else { Debug.Assert(skipCount == 0); - if (FirstOptionalArgumentIndex < 0) { - return Arguments.Zip(ArgumentNames, - (arg, name) => { - if (name == null) - return arg.Expression; - else - return new NamedArgumentExpression(name, arg); - }); - } - return Arguments.Take(FirstOptionalArgumentIndex).Zip(ArgumentNames.Take(FirstOptionalArgumentIndex), + return Arguments.Take(argumentCount).Zip(ArgumentNames.Take(argumentCount), (arg, name) => { if (name == null) return arg.Expression; @@ -147,7 +142,7 @@ namespace ICSharpCode.Decompiler.CSharp public TranslatedExpression Build(CallInstruction inst) { - if (inst is NewObj newobj && IL.Transforms.DelegateConstruction.IsDelegateConstruction(newobj, true)) { + if (inst is NewObj newobj && IL.Transforms.DelegateConstruction.IsDelegateConstruction(newobj)) { return HandleDelegateConstruction(newobj); } if (settings.TupleTypes && TupleTransform.MatchTupleConstruction(inst as NewObj, out var tupleElements) && tupleElements.Length >= 2) { @@ -177,13 +172,32 @@ namespace ICSharpCode.Decompiler.CSharp IReadOnlyList argumentToParameterMap = null, IType constrainedTo = null) { + if (method.IsExplicitInterfaceImplementation && callOpCode == OpCode.Call) { + // Direct non-virtual call to explicit interface implementation. + // This can't really be represented in C#, but at least in the case where + // the class is sealed, we can equivalently call the interface member instead: + var interfaceMembers = method.ExplicitlyImplementedInterfaceMembers.ToList(); + if (method.DeclaringTypeDefinition?.Kind == TypeKind.Class && method.DeclaringTypeDefinition.IsSealed && interfaceMembers.Count == 1) { + method = (IMethod)interfaceMembers.Single(); + callOpCode = OpCode.CallVirt; + } + } // Used for Call, CallVirt and NewObj var expectedTargetDetails = new ExpectedTargetDetails { CallOpCode = callOpCode }; + ILFunction localFunction = null; + if (method.IsLocalFunction) { + localFunction = expressionBuilder.ResolveLocalFunction(method); + Debug.Assert(localFunction != null); + } TranslatedExpression target; if (callOpCode == OpCode.NewObj) { target = default(TranslatedExpression); // no target + } else if (method.IsLocalFunction && localFunction != null) { + target = new IdentifierExpression(localFunction.Name) + .WithoutILInstruction() + .WithRR(ToMethodGroup(method, localFunction)); } else { target = expressionBuilder.TranslateTarget( callArguments.FirstOrDefault(), @@ -210,6 +224,12 @@ namespace ICSharpCode.Decompiler.CSharp var argumentList = BuildArgumentList(expectedTargetDetails, target.ResolveResult, method, firstParamIndex, callArguments, argumentToParameterMap); + + if (method.IsLocalFunction) { + return new InvocationExpression(target, argumentList.GetArgumentExpressions()) + .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, + argumentList.GetArgumentResolveResults().ToList(), isExpandedForm: argumentList.IsExpandedForm)); + } if (method is VarArgInstanceMethod) { argumentList.FirstOptionalArgumentIndex = -1; @@ -334,7 +354,7 @@ namespace ICSharpCode.Decompiler.CSharp // settings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls == true is used in Windows Forms' InitializeComponent methods. if (method.IsExplicitInterfaceImplementation && (target.Expression is ThisReferenceExpression || settings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls)) { var interfaceMember = method.ExplicitlyImplementedInterfaceMembers.First(); - var castExpression = new CastExpression(expressionBuilder.ConvertType(interfaceMember.DeclaringType), target.Expression); + var castExpression = new CastExpression(expressionBuilder.ConvertType(interfaceMember.DeclaringType), target.Expression.Detach()); methodName = interfaceMember.Name; targetExpr = new MemberReferenceExpression(castExpression, methodName); typeArgumentList = ((MemberReferenceExpression)targetExpr).TypeArguments; @@ -386,7 +406,7 @@ namespace ICSharpCode.Decompiler.CSharp argumentList.ArgumentNames = null; argumentList.AddNamesToPrimitiveValues = false; var transform = GetRequiredTransformationsForCall(expectedTargetDetails, method, ref unused, - ref argumentList, CallTransformation.None, out IParameterizedMember foundMethod); + ref argumentList, CallTransformation.None, out _); Debug.Assert(transform == CallTransformation.None || transform == CallTransformation.NoOptionalArgumentAllowed); // Calls with only one argument do not need an array initializer expression to wrap them. @@ -427,6 +447,9 @@ namespace ICSharpCode.Decompiler.CSharp var assignment = HandleAccessorCall(expectedTargetDetails, method, unused, argumentList.Arguments.ToList(), argumentList.ArgumentNames); + if (((AssignmentExpression)assignment).Left is IndexerExpression indexer && !indexer.Target.IsNull) + indexer.Target.ReplaceWith(n => null); + if (value != null) return assignment; @@ -565,9 +588,8 @@ namespace ICSharpCode.Decompiler.CSharp } } - - private ArgumentList BuildArgumentList(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method, int firstParamIndex, - IReadOnlyList callArguments, IReadOnlyList argumentToParameterMap) + private ArgumentList BuildArgumentList(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method, + int firstParamIndex, IReadOnlyList callArguments, IReadOnlyList argumentToParameterMap) { ArgumentList list = new ArgumentList(); @@ -633,8 +655,8 @@ namespace ICSharpCode.Decompiler.CSharp arg = arg.ConvertTo(parameterType, expressionBuilder, allowImplicitConversion: arg.Type.Kind != TypeKind.Dynamic); - if (parameter.IsOut) { - arg = ExpressionBuilder.ChangeDirectionExpressionToOut(arg); + if (parameter.ReferenceKind != ReferenceKind.None) { + arg = ExpressionBuilder.ChangeDirectionExpressionTo(arg, parameter.ReferenceKind); } arguments.Add(arg); @@ -748,7 +770,9 @@ namespace ICSharpCode.Decompiler.CSharp if (expressionBuilder.HidesVariableWithName(method.Name)) { requireTarget = true; } else { - if (method.IsStatic) + if (method.IsLocalFunction) + requireTarget = false; + else if (method.IsStatic) requireTarget = !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) || method.Name == ".cctor"; else if (method.Name == ".ctor") requireTarget = true; // always use target for base/this-ctor-call, the constructor initializer pattern depends on this @@ -979,11 +1003,14 @@ namespace ICSharpCode.Decompiler.CSharp } else if (method.IsOperator) { IEnumerable operatorCandidates; if (arguments.Count == 1) { - operatorCandidates = resolver.GetUserDefinedOperatorCandidates(arguments[0].Type, method.Name); + IType argType = NullableType.GetUnderlyingType(arguments[0].Type); + operatorCandidates = resolver.GetUserDefinedOperatorCandidates(argType, method.Name); } else if (arguments.Count == 2) { + IType lhsType = NullableType.GetUnderlyingType(arguments[0].Type); + IType rhsType = NullableType.GetUnderlyingType(arguments[1].Type); var hashSet = new HashSet(); - hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(arguments[0].Type, method.Name)); - hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(arguments[1].Type, method.Name)); + hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(lhsType, method.Name)); + hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(rhsType, method.Name)); operatorCandidates = hashSet; } else { operatorCandidates = EmptyList.Instance; @@ -1107,8 +1134,7 @@ namespace ICSharpCode.Decompiler.CSharp } var op = AssignmentOperatorType.Assign; - var parentEvent = method.AccessorOwner as IEvent; - if (parentEvent != null) { + if (method.AccessorOwner is IEvent parentEvent) { if (method.Equals(parentEvent.AddAccessor)) { op = AssignmentOperatorType.Add; } @@ -1196,28 +1222,70 @@ namespace ICSharpCode.Decompiler.CSharp ILInstruction thisArg = inst.Arguments[0]; ILInstruction func = inst.Arguments[1]; IMethod method; + ExpectedTargetDetails expectedTargetDetails = default; switch (func.OpCode) { case OpCode.LdFtn: method = ((LdFtn)func).Method; + expectedTargetDetails.CallOpCode = OpCode.Call; break; case OpCode.LdVirtFtn: method = ((LdVirtFtn)func).Method; + expectedTargetDetails.CallOpCode = OpCode.CallVirt; break; default: throw new ArgumentException($"Unknown instruction type: {func.OpCode}"); } - var invokeMethod = inst.Method.DeclaringType.GetDelegateInvokeMethod(); + return HandleDelegateConstruction(inst.Method.DeclaringType, method, expectedTargetDetails, thisArg, inst); + } + + internal TranslatedExpression Build(LdVirtDelegate inst) + { + return HandleDelegateConstruction(inst.Type, inst.Method, new ExpectedTargetDetails { CallOpCode = OpCode.CallVirt }, inst.Argument, inst); + } + + TranslatedExpression HandleDelegateConstruction(IType delegateType, IMethod method, ExpectedTargetDetails expectedTargetDetails, ILInstruction thisArg, ILInstruction inst) + { + var invokeMethod = delegateType.GetDelegateInvokeMethod(); TranslatedExpression target; IType targetType; bool requireTarget; - if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) { + ResolveResult result = null; + string methodName = method.Name; + // There are three possible adjustments, we can make, to solve conflicts: + // 1. add target (represented as bit 0) + // 2. add type arguments (represented as bit 1) + // 3. cast target (represented as bit 2) + int step; + if (method.IsLocalFunction) { + step = 0; + requireTarget = false; + var localFunction = expressionBuilder.ResolveLocalFunction(method); + result = ToMethodGroup(method, localFunction); + target = default; + targetType = default; + methodName = localFunction.Name; + // TODO : think about how to handle generic local functions + } else if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) { + step = 5; targetType = method.Parameters[0].Type; if (targetType.Kind == TypeKind.ByReference && thisArg is Box thisArgBox) { targetType = ((ByReferenceType)targetType).ElementType; thisArg = thisArgBox.Argument; } target = expressionBuilder.Translate(thisArg, targetType); + // TODO : check if cast is necessary + target = target.ConvertTo(targetType, expressionBuilder); requireTarget = true; + result = new MethodGroupResolveResult( + target.ResolveResult, method.Name, + new MethodListWithDeclaringType[] { + new MethodListWithDeclaringType( + null, + new[] { method } + ) + }, + method.TypeArguments + ); } else { targetType = method.DeclaringType; if (targetType.IsReferenceType == false && thisArg is Box thisArgBox) { @@ -1230,63 +1298,105 @@ namespace ICSharpCode.Decompiler.CSharp } } target = expressionBuilder.TranslateTarget(thisArg, - nonVirtualInvocation: func.OpCode == OpCode.LdFtn, + nonVirtualInvocation: expectedTargetDetails.CallOpCode == OpCode.Call, memberStatic: method.IsStatic, memberDeclaringType: method.DeclaringType); requireTarget = expressionBuilder.HidesVariableWithName(method.Name) || (method.IsStatic ? !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) : !(target.Expression is ThisReferenceExpression)); - } - var expectedTargetDetails = new ExpectedTargetDetails { - CallOpCode = inst.OpCode - }; - bool needsCast = false; - ResolveResult result = null; - var or = new OverloadResolution(resolver.Compilation, method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type))); - if (!requireTarget) { - result = resolver.ResolveSimpleName(method.Name, method.TypeArguments, isInvocationTarget: false); - if (result is MethodGroupResolveResult mgrr) { - or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); - requireTarget = (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(expectedTargetDetails, method, or.BestCandidate)); - } else { - requireTarget = true; - } - } - MemberLookup lookup = null; - if (requireTarget) { - lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule); - var rr = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, false) ; - needsCast = true; - result = rr; - if (rr is MethodGroupResolveResult mgrr) { - or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); - needsCast = (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(expectedTargetDetails, method, or.BestCandidate)); + + var savedTarget = target; + for (step = requireTarget ? 1 : 0; step < 7; step++) { + ResolveResult targetResolveResult; + if (!method.IsLocalFunction && (step & 1) != 0) { + targetResolveResult = savedTarget.ResolveResult; + target = savedTarget; + } else { + targetResolveResult = null; + } + IReadOnlyList typeArguments; + if ((step & 2) != 0) { + typeArguments = method.TypeArguments; + } else { + typeArguments = EmptyList.Instance; + } + if (targetResolveResult != null && targetType != null && (step & 4) != 0) { + target = target.ConvertTo(targetType, expressionBuilder); + targetResolveResult = target.ResolveResult; + } + bool success = IsUnambiguousMethodReference(expectedTargetDetails, method, targetResolveResult, typeArguments, out var newResult); + if (newResult is MethodGroupResolveResult || result == null) + result = newResult; + if (success) + break; } } - if (needsCast) { - Debug.Assert(requireTarget); - target = target.ConvertTo(targetType, expressionBuilder); - result = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, false); - } + requireTarget = !method.IsLocalFunction && (step & 1) != 0; Expression targetExpression; + Debug.Assert(result != null); if (requireTarget) { - var mre = new MemberReferenceExpression(target, method.Name); - mre.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); + Debug.Assert(target.Expression != null); + var mre = new MemberReferenceExpression(target, methodName); + if ((step & 2) != 0) + mre.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); mre.WithRR(result); targetExpression = mre; } else { - var ide = new IdentifierExpression(method.Name) - .WithRR(result); + var ide = new IdentifierExpression(methodName); + if ((step & 2) != 0) + ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); + ide.WithRR(result); targetExpression = ide; } - var oce = new ObjectCreateExpression(expressionBuilder.ConvertType(inst.Method.DeclaringType), targetExpression) + var oce = new ObjectCreateExpression(expressionBuilder.ConvertType(delegateType), targetExpression) .WithILInstruction(inst) .WithRR(new ConversionResolveResult( - inst.Method.DeclaringType, - new MemberResolveResult(target.ResolveResult, method), - Conversion.MethodGroupConversion(method, func.OpCode == OpCode.LdVirtFtn, false))); + delegateType, + result, + Conversion.MethodGroupConversion(method, expectedTargetDetails.CallOpCode == OpCode.CallVirt, false))); return oce; } + bool IsUnambiguousMethodReference(ExpectedTargetDetails expectedTargetDetails, IMethod method, ResolveResult target, IReadOnlyList typeArguments, out ResolveResult result) + { + 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 + argumentNames: null, // argument names are not possible + typeArguments.ToArray(), + conversions: expressionBuilder.resolver.conversions + ); + if (target == null) { + result = resolver.ResolveSimpleName(method.Name, typeArguments, isInvocationTarget: false); + if (!(result is MethodGroupResolveResult mgrr)) + return false; + or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); + } else { + result = lookup.Lookup(target, method.Name, typeArguments, isInvocation: false); + if (!(result is MethodGroupResolveResult mgrr)) + return false; + or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); + } + + var foundMethod = or.GetBestCandidateWithSubstitutedTypeArguments(); + if (!IsAppropriateCallTarget(expectedTargetDetails, method, foundMethod)) + return false; + return result is MethodGroupResolveResult; + } + + static MethodGroupResolveResult ToMethodGroup(IMethod method, ILFunction localFunction) + { + return new MethodGroupResolveResult( + null, + localFunction.Name, + new[] { + new MethodListWithDeclaringType( + method.DeclaringType, + new IParameterizedMember[] { method } + ) + }, EmptyList.Instance + ); + } + internal TranslatedExpression CallWithNamedArgs(Block block) { Debug.Assert(block.Kind == BlockKind.CallWithNamedArgs); diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 8197c4e35..239d84976 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -65,10 +65,10 @@ namespace ICSharpCode.Decompiler.CSharp /// * Otherwise, the C# type of the resulting expression shall match the IL stack type, /// and the evaluated values shall be the same. /// - class ExpressionBuilder : ILVisitor + sealed class ExpressionBuilder : ILVisitor { readonly IDecompilerTypeSystem typeSystem; - readonly ITypeResolveContext decompilationContext; + internal readonly ITypeResolveContext decompilationContext; internal readonly ILFunction currentFunction; internal readonly ICompilation compilation; internal readonly CSharpResolver resolver; @@ -134,7 +134,7 @@ namespace ICSharpCode.Decompiler.CSharp }; var cexpr = inst.AcceptVisitor(this, context); #if DEBUG - if (inst.ResultType != StackType.Void && cexpr.Type.Kind != TypeKind.Unknown && inst.ResultType != StackType.Unknown) { + if (inst.ResultType != StackType.Void && cexpr.Type.Kind != TypeKind.Unknown && inst.ResultType != StackType.Unknown && cexpr.Type.Kind != TypeKind.None) { // Validate the Translate post-condition (documented at beginning of this file): if (inst.ResultType.IsIntegerType()) { Debug.Assert(cexpr.Type.GetStackType().IsIntegerType(), "IL instructions of integer type must convert into C# expressions of integer type"); @@ -181,7 +181,7 @@ namespace ICSharpCode.Decompiler.CSharp expr.WithRR(new ILVariableResolveResult(variable, elementType)); expr = new DirectionExpression(FieldDirection.Ref, expr); - return expr.WithRR(new ByReferenceResolveResult(elementType, isOut: false)); + return expr.WithRR(new ByReferenceResolveResult(elementType, ReferenceKind.Ref)); } else { return expr.WithRR(new ILVariableResolveResult(variable, variable.Type)); } @@ -189,7 +189,35 @@ namespace ICSharpCode.Decompiler.CSharp internal bool HidesVariableWithName(string name) { - return currentFunction.Ancestors.OfType().SelectMany(f => f.Variables).Any(v => v.Name == name); + return currentFunction.Ancestors.OfType().Any(HidesVariableOrNestedFunction); + + bool HidesVariableOrNestedFunction(ILFunction function) + { + foreach (var v in function.Variables) { + if (v.Name == name) + return true; + } + + foreach (var f in function.LocalFunctions.OfType()) { + if (f.Name == name) + return true; + } + + return false; + } + } + + internal ILFunction ResolveLocalFunction(IMethod method) + { + Debug.Assert(method.IsLocalFunction); + method = method.ReducedFrom; + foreach (var parent in currentFunction.Ancestors.OfType()) { + var definition = parent.LocalFunctions.FirstOrDefault(f => f.Method.Equals(method)); + if (definition != null) { + return definition; + } + } + return null; } bool RequiresQualifier(IMember member, TranslatedExpression target) @@ -220,19 +248,20 @@ namespace ICSharpCode.Decompiler.CSharp bool targetCasted = false; var targetResolveResult = requireTarget ? target.ResolveResult : null; - bool IsUnambiguousAccess() + bool IsUnambiguousAccess(out MemberResolveResult result) { if (targetResolveResult == null) { - var result = resolver.ResolveSimpleName(field.Name, EmptyList.Instance, isInvocationTarget: false) as MemberResolveResult; + result = resolver.ResolveSimpleName(field.Name, EmptyList.Instance, isInvocationTarget: false) as MemberResolveResult; return !(result == null || result.IsError || !result.Member.Equals(field, NormalizeTypeVisitor.TypeErasure)); } else { var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule); - var result = lookup.Lookup(target.ResolveResult, field.Name, EmptyList.Instance, false) as MemberResolveResult; + result = lookup.Lookup(target.ResolveResult, field.Name, EmptyList.Instance, false) as MemberResolveResult; return !(result == null || result.IsError || !result.Member.Equals(field, NormalizeTypeVisitor.TypeErasure)); } } - while (!IsUnambiguousAccess()) { + MemberResolveResult mrr; + while (!IsUnambiguousAccess(out mrr)) { if (!requireTarget) { requireTarget = true; targetResolveResult = target.ResolveResult; @@ -244,13 +273,16 @@ namespace ICSharpCode.Decompiler.CSharp break; } } + if (mrr == null) { + mrr = new MemberResolveResult(target.ResolveResult, field); + } if (requireTarget) { return new MemberReferenceExpression(target, field.Name) - .WithRR(new MemberResolveResult(target.ResolveResult, field)); + .WithRR(mrr); } else { return new IdentifierExpression(field.Name) - .WithRR(new MemberResolveResult(target.ResolveResult, field)); + .WithRR(mrr); } } @@ -315,7 +347,12 @@ namespace ICSharpCode.Decompiler.CSharp } return new CallBuilder(this, typeSystem, settings).Build(inst); } - + + protected internal override TranslatedExpression VisitLdVirtDelegate(LdVirtDelegate inst, TranslationContext context) + { + return new CallBuilder(this, typeSystem, settings).Build(inst); + } + protected internal override TranslatedExpression VisitNewArr(NewArr inst, TranslationContext context) { var dimensions = inst.Indices.Count; @@ -511,7 +548,9 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitLdTypeToken(LdTypeToken inst, TranslationContext context) { - return new MemberReferenceExpression(new TypeOfExpression(ConvertType(inst.Type)), "TypeHandle") + var typeofExpr = new TypeOfExpression(ConvertType(inst.Type)) + .WithRR(new TypeOfResolveResult(compilation.FindType(KnownTypeCode.Type), inst.Type)); + return new MemberReferenceExpression(typeofExpr, "TypeHandle") .WithILInstruction(inst) .WithRR(new TypeOfResolveResult(compilation.FindType(new TopLevelTypeName("System", "RuntimeTypeHandle")), inst.Type)); } @@ -522,7 +561,7 @@ namespace ICSharpCode.Decompiler.CSharp var argUType = NullableType.GetUnderlyingType(argument.Type); if (argUType.GetStackType().GetSize() < inst.UnderlyingResultType.GetSize() - || argUType.Kind == TypeKind.Enum && argUType.IsSmallIntegerType() + || argUType.Kind == TypeKind.Enum && argUType.IsSmallIntegerType() || argUType.GetStackType() == StackType.I || argUType.IsKnownType(KnownTypeCode.Boolean) || argUType.IsKnownType(KnownTypeCode.Char)) @@ -576,7 +615,7 @@ namespace ICSharpCode.Decompiler.CSharp // because the DirectionExpression might get removed by dereferencing instructions such as LdObj return new DirectionExpression(FieldDirection.Ref, expr.Expression) .WithoutILInstruction() - .WithRR(new ByReferenceResolveResult(expr.ResolveResult, isOut: false)); + .WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); } protected internal override TranslatedExpression VisitStLoc(StLoc inst, TranslationContext context) @@ -585,7 +624,7 @@ namespace ICSharpCode.Decompiler.CSharp if (inst.Variable.Kind == VariableKind.StackSlot && !loadedVariablesSet.Contains(inst.Variable)) { // Stack slots in the ILAst have inaccurate types (e.g. System.Object for StackType.O) // so we should replace them with more accurate types where possible: - if ((inst.Variable.IsSingleDefinition || IsOtherValueType(translatedValue.Type) || inst.Variable.StackType == StackType.Ref) + if (CanUseTypeForStackSlot(inst.Variable, translatedValue.Type) && inst.Variable.StackType == translatedValue.Type.GetStackType() && translatedValue.Type.Kind != TypeKind.Null) { inst.Variable.Type = translatedValue.Type; @@ -605,10 +644,31 @@ namespace ICSharpCode.Decompiler.CSharp return Assignment(lhs, translatedValue).WithILInstruction(inst); } + bool CanUseTypeForStackSlot(ILVariable v, IType type) + { + return v.IsSingleDefinition + || IsOtherValueType(type) + || v.StackType == StackType.Ref + || AllStoresUseConsistentType(v.StoreInstructions, type); + } + bool IsOtherValueType(IType type) { return type.IsReferenceType == false && type.GetStackType() == StackType.O; } + + bool AllStoresUseConsistentType(IReadOnlyList storeInstructions, IType expectedType) + { + expectedType = expectedType.AcceptVisitor(NormalizeTypeVisitor.TypeErasure); + foreach (var store in storeInstructions) { + if (!(store is StLoc stloc)) + return false; + IType type = stloc.Value.InferType(compilation).AcceptVisitor(NormalizeTypeVisitor.TypeErasure); + if (!type.Equals(expectedType)) + return false; + } + return true; + } } protected internal override TranslatedExpression VisitComp(Comp inst, TranslationContext context) @@ -889,6 +949,13 @@ namespace ICSharpCode.Decompiler.CSharp .WithILInstruction(inst); } + protected internal override TranslatedExpression VisitThrow(Throw inst, TranslationContext context) + { + return new ThrowExpression(Translate(inst.Argument)) + .WithILInstruction(inst) + .WithRR(new ThrowResolveResult()); + } + protected internal override TranslatedExpression VisitUserDefinedLogicOperator(UserDefinedLogicOperator inst, TranslationContext context) { var left = Translate(inst.Left, inst.Method.Parameters[0].Type).ConvertTo(inst.Method.Parameters[0].Type, this); @@ -1018,7 +1085,7 @@ namespace ICSharpCode.Decompiler.CSharp // ref - ref => i return CallUnsafeIntrinsic("ByteOffset", new[] { left.Expression, right.Expression }, compilation.FindType(KnownTypeCode.IntPtr), inst); } - if (inst.LeftInputType == StackType.Ref && inst.RightInputType == StackType.I + if (inst.LeftInputType == StackType.Ref && inst.RightInputType.IsIntegerType() && left.Type is ByReferenceType brt) { // ref [+-] int string name = (inst.Operator == BinaryNumericOperator.Sub ? "Subtract" : "Add"); @@ -1035,7 +1102,7 @@ namespace ICSharpCode.Decompiler.CSharp .WithILInstruction(inst) .WithRR(new ResolveResult(elementType)); return new DirectionExpression(FieldDirection.Ref, expr) - .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.Type, isOut: false)); + .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.Type, ReferenceKind.Ref)); } return CallUnsafeIntrinsic(name, new[] { left.Expression, Translate(offsetInst).Expression }, brt, inst); } else { @@ -1226,8 +1293,9 @@ namespace ICSharpCode.Decompiler.CSharp var resultExpr = new BinaryOperatorExpression(left.Expression, op, right.Expression) .WithILInstruction(inst) .WithRR(rr); - if (BinaryOperatorMightCheckForOverflow(op)) + if (BinaryOperatorMightCheckForOverflow(op) && !inst.UnderlyingResultType.IsFloatType()) { resultExpr.Expression.AddAnnotation(inst.CheckForOverflow ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); + } return resultExpr; } @@ -1326,7 +1394,13 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitUserDefinedCompoundAssign(UserDefinedCompoundAssign inst, TranslationContext context) { - var target = Translate(inst.Target); + IType loadType = inst.Method.Parameters[0].Type; + ExpressionWithResolveResult target; + if (inst.TargetKind == CompoundTargetKind.Address) { + target = LdObj(inst.Target, loadType); + } else { + target = Translate(inst.Target, loadType); + } if (UserDefinedCompoundAssign.IsStringConcat(inst.Method)) { Debug.Assert(inst.Method.Parameters.Count == 2); var value = Translate(inst.Value).ConvertTo(inst.Method.Parameters[1].Type, this, allowImplicitConversion: true); @@ -1342,7 +1416,7 @@ namespace ICSharpCode.Decompiler.CSharp .WithILInstruction(inst) .WithRR(new OperatorResolveResult(inst.Method.ReturnType, AssignmentExpression.GetLinqNodeType(op.Value, false), inst.Method, inst.IsLifted, new[] { target.ResolveResult, value.ResolveResult })); } else { - UnaryOperatorType? op = GetUnaryOperatorTypeFromMetadataName(inst.Method.Name, inst.CompoundAssignmentType == CompoundAssignmentType.EvaluatesToOldValue); + UnaryOperatorType? op = GetUnaryOperatorTypeFromMetadataName(inst.Method.Name, inst.EvalMode == CompoundEvalMode.EvaluatesToOldValue); Debug.Assert(op != null); return new UnaryOperatorExpression(op.Value, target) @@ -1421,10 +1495,15 @@ namespace ICSharpCode.Decompiler.CSharp TranslatedExpression HandleCompoundAssignment(NumericCompoundAssign inst, AssignmentOperatorType op) { - var target = Translate(inst.Target); + ExpressionWithResolveResult target; + if (inst.TargetKind == CompoundTargetKind.Address) { + target = LdObj(inst.Target, inst.Type); + } else { + target = Translate(inst.Target, inst.Type); + } TranslatedExpression resultExpr; - if (inst.CompoundAssignmentType == CompoundAssignmentType.EvaluatesToOldValue) { + if (inst.EvalMode == CompoundEvalMode.EvaluatesToOldValue) { Debug.Assert(op == AssignmentOperatorType.Add || op == AssignmentOperatorType.Subtract); Debug.Assert(inst.Value.MatchLdcI(1)); UnaryOperatorType unary; @@ -1449,7 +1528,7 @@ namespace ICSharpCode.Decompiler.CSharp var pao = GetPointerArithmeticOffset(inst.Value, value, ((PointerType)target.Type).ElementType, inst.CheckForOverflow); if (pao != null) { value = pao.Value; - } else { + } else { value.Expression.AddChild(new Comment("ILSpy Error: GetPointerArithmeticOffset() failed", CommentType.MultiLine), Roles.Comment); } } else { @@ -1478,15 +1557,21 @@ namespace ICSharpCode.Decompiler.CSharp .WithILInstruction(inst) .WithRR(new OperatorResolveResult(target.Type, AssignmentExpression.GetLinqNodeType(op, inst.CheckForOverflow), target.ResolveResult, value.ResolveResult)); } - if (AssignmentOperatorMightCheckForOverflow(op)) + if (AssignmentOperatorMightCheckForOverflow(op) && !inst.UnderlyingResultType.IsFloatType()) { resultExpr.Expression.AddAnnotation(inst.CheckForOverflow ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); + } return resultExpr; } TranslatedExpression HandleCompoundShift(NumericCompoundAssign inst, AssignmentOperatorType op) { - Debug.Assert(inst.CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue); - var target = Translate(inst.Target); + Debug.Assert(inst.EvalMode == CompoundEvalMode.EvaluatesToNewValue); + ExpressionWithResolveResult target; + if (inst.TargetKind == CompoundTargetKind.Address) { + target = LdObj(inst.Target, inst.Type); + } else { + target = Translate(inst.Target, inst.Type); + } var value = Translate(inst.Value); // Shift operators in C# always expect type 'int' on the right-hand-side @@ -1641,6 +1726,10 @@ namespace ICSharpCode.Decompiler.CSharp } else if (inst.TargetType == IL.PrimitiveType.Ref) { // converting to unknown ref-type targetType = new ByReferenceType(compilation.FindType(KnownTypeCode.Byte)); + } else if (inst.TargetType == IL.PrimitiveType.None) { + // convert to some object type + // (e.g. invalid I4->O conversion) + targetType = compilation.FindType(KnownTypeCode.Object); } else { targetType = GetType(inst.TargetType.ToKnownTypeCode()); } @@ -1691,7 +1780,7 @@ namespace ICSharpCode.Decompiler.CSharp if (type.Kind == TypeKind.ByReference) { return new DirectionExpression(FieldDirection.Ref, expr.Expression) .WithoutILInstruction() - .WithRR(new ByReferenceResolveResult(expr.ResolveResult, isOut: false)); + .WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); } return expr; } @@ -1823,22 +1912,28 @@ namespace ICSharpCode.Decompiler.CSharp return SpecialType.UnknownType; } - IEnumerable MakeParameters(IReadOnlyList parameters, ILFunction function) + internal IEnumerable MakeParameters(IReadOnlyList parameters, ILFunction function) { var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index); int i = 0; foreach (var parameter in parameters) { var pd = astBuilder.ConvertParameter(parameter); + if (string.IsNullOrEmpty(pd.Name) && !pd.Type.IsArgList()) { + // needs to be consistent with logic in ILReader.CreateILVarable(ParameterDefinition) + pd.Name = "P_" + i; + // if this is a local function, we have to skip the parameters for closure references + if (settings.LocalFunctions && function.Kind == ILFunctionKind.LocalFunction && IL.Transforms.LocalFunctionDecompiler.IsClosureParameter(parameter, decompilationContext)) + break; + } if (settings.AnonymousTypes && parameter.Type.ContainsAnonymousType()) pd.Type = null; - ILVariable v; - if (variables.TryGetValue(i, out v)) + if (variables.TryGetValue(i, out var v)) pd.AddAnnotation(new ILVariableResolveResult(v, parameters[i].Type)); yield return pd; i++; } } - + internal TranslatedExpression TranslateTarget(ILInstruction target, bool nonVirtualInvocation, bool memberStatic, IType memberDeclaringType) { @@ -1861,7 +1956,7 @@ namespace ICSharpCode.Decompiler.CSharp if (translatedTarget.Expression is DirectionExpression) { // (ref x).member => x.member translatedTarget = translatedTarget.UnwrapChild(((DirectionExpression)translatedTarget).Expression); - } else if (translatedTarget.Expression is UnaryOperatorExpression uoe + } else if (translatedTarget.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.NullConditional && uoe.Expression is DirectionExpression) { // (ref x)?.member => x?.member @@ -1897,46 +1992,46 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitLdObj(LdObj inst, TranslationContext context) { - var target = Translate(inst.Target); - if (TypeUtils.IsCompatiblePointerTypeForMemoryAccess(target.Type, inst.Type)) { - TranslatedExpression result; + var result = LdObj(inst.Target, inst.Type); + //if (target.Type.IsSmallIntegerType() && loadType.IsSmallIntegerType() && target.Type.GetSign() != loadType.GetSign()) + // return result.ConvertTo(loadType, this); + return result.WithILInstruction(inst); + } + + ExpressionWithResolveResult LdObj(ILInstruction address, IType loadType) + { + var target = Translate(address); + if (TypeUtils.IsCompatiblePointerTypeForMemoryAccess(target.Type, loadType)) { + ExpressionWithResolveResult result; if (target.Expression is DirectionExpression dirExpr) { // we can dereference the managed reference by stripping away the 'ref' result = target.UnwrapChild(dirExpr.Expression); - result.Expression.AddAnnotation(inst); // add LdObj in addition to the existing ILInstruction annotation } else if (target.Type is PointerType pointerType) { if (target.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.AddressOf) { // We can dereference the pointer by stripping away the '&' result = target.UnwrapChild(uoe.Expression); - result.Expression.AddAnnotation(inst); // add LdObj in addition to the existing ILInstruction annotation } else { // Dereference the existing pointer result = new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) - .WithILInstruction(inst) .WithRR(new ResolveResult(pointerType.ElementType)); } } else { // reference type behind non-DirectionExpression? // this case should be impossible, but we can use a pointer cast // just to make sure - target = target.ConvertTo(new PointerType(inst.Type), this); + target = target.ConvertTo(new PointerType(loadType), this); return new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) - .WithILInstruction(inst) - .WithRR(new ResolveResult(inst.Type)); + .WithRR(new ResolveResult(loadType)); } // we don't convert result to inst.Type, because the LdObj type // might be inaccurate (it's often System.Object for all reference types), // and our parent node should already insert casts where necessary - - if (target.Type.IsSmallIntegerType() && inst.Type.IsSmallIntegerType() && target.Type.GetSign() != inst.Type.GetSign()) - return result.ConvertTo(inst.Type, this); return result; } else { // We need to cast the pointer type: - target = target.ConvertTo(new PointerType(inst.Type), this); + target = target.ConvertTo(new PointerType(loadType), this); return new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) - .WithILInstruction(inst) - .WithRR(new ResolveResult(inst.Type)); + .WithRR(new ResolveResult(loadType)); } } @@ -1975,7 +2070,7 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitLdLen(LdLen inst, TranslationContext context) { - TranslatedExpression arrayExpr = Translate(inst.Array); + TranslatedExpression arrayExpr = Translate(inst.Array, typeHint: compilation.FindType(KnownTypeCode.Array)); if (arrayExpr.Type.Kind != TypeKind.Array) { arrayExpr = arrayExpr.ConvertTo(compilation.FindType(KnownTypeCode.Array), this); } @@ -2015,14 +2110,16 @@ namespace ICSharpCode.Decompiler.CSharp nonVirtualInvocation: true, memberStatic: false, memberDeclaringType: underlyingTupleType); - if (translatedTarget.Type is TupleType tupleType && tupleType.UnderlyingType.Equals(underlyingTupleType) && position <= tupleType.ElementNames.Length) { + if (translatedTarget.Type is TupleType tupleType && NormalizeTypeVisitor.TypeErasure.EquivalentTypes(tupleType, underlyingTupleType) && position <= tupleType.ElementNames.Length) { string elementName = tupleType.ElementNames[position - 1]; if (elementName == null) { elementName = "Item" + position; } + // tupleType.ElementTypes are more accurate w.r.t. nullability/dynamic than inst.Field.Type + var rr = new MemberResolveResult(translatedTarget.ResolveResult, inst.Field, + returnTypeOverride: tupleType.ElementTypes[position - 1]); expr = new MemberReferenceExpression(translatedTarget, elementName) - .WithRR(new MemberResolveResult(translatedTarget.ResolveResult, inst.Field)) - .WithILInstruction(inst); + .WithRR(rr).WithILInstruction(inst); } else { expr = ConvertField(inst.Field, inst.Target).WithILInstruction(inst); } @@ -2036,7 +2133,7 @@ namespace ICSharpCode.Decompiler.CSharp } else { // ldflda producing managed pointer return new DirectionExpression(FieldDirection.Ref, expr) - .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, isOut: false)); + .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); } } @@ -2044,7 +2141,7 @@ namespace ICSharpCode.Decompiler.CSharp { var expr = ConvertField(inst.Field).WithILInstruction(inst); return new DirectionExpression(FieldDirection.Ref, expr) - .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.Type, isOut: false)); + .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.Type, ReferenceKind.Ref)); } protected internal override TranslatedExpression VisitLdElema(LdElema inst, TranslationContext context) @@ -2052,14 +2149,14 @@ namespace ICSharpCode.Decompiler.CSharp TranslatedExpression arrayExpr = Translate(inst.Array); var arrayType = arrayExpr.Type as ArrayType; if (arrayType == null || !TypeUtils.IsCompatibleTypeForMemoryAccess(arrayType.ElementType, inst.Type)) { - arrayType = new ArrayType(compilation, inst.Type, inst.Indices.Count); + arrayType = new ArrayType(compilation, inst.Type, inst.Indices.Count); arrayExpr = arrayExpr.ConvertTo(arrayType, this); } TranslatedExpression expr = new IndexerExpression( arrayExpr, inst.Indices.Select(i => TranslateArrayIndex(i).Expression) ).WithILInstruction(inst).WithRR(new ResolveResult(arrayType.ElementType)); return new DirectionExpression(FieldDirection.Ref, expr) - .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.Type, isOut: false)); + .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.Type, ReferenceKind.Ref)); } TranslatedExpression TranslateArrayIndex(ILInstruction i) @@ -2099,8 +2196,7 @@ namespace ICSharpCode.Decompiler.CSharp // try via its effective base class. arg = arg.ConvertTo(((ITypeParameter)targetType).EffectiveBaseClass, this); } - } - else { + } else { // Before unboxing arg must be a object arg = arg.ConvertTo(compilation.FindType(KnownTypeCode.Object), this); } @@ -2117,7 +2213,7 @@ namespace ICSharpCode.Decompiler.CSharp .WithRR(new ConversionResolveResult(inst.Type, arg.ResolveResult, Conversion.UnboxingConversion)); return new DirectionExpression(FieldDirection.Ref, castExpression) .WithILInstruction(inst) - .WithRR(new ByReferenceResolveResult(castExpression.ResolveResult, isOut: false)); + .WithRR(new ByReferenceResolveResult(castExpression.ResolveResult, ReferenceKind.Ref)); } protected internal override TranslatedExpression VisitBox(Box inst, TranslationContext context) @@ -2177,7 +2273,7 @@ namespace ICSharpCode.Decompiler.CSharp Arguments = { Translate(inst.Argument).Expression, new TypeReferenceExpression(ConvertType(inst.Type)) } }.WithRR(new ResolveResult(inst.Type)); return new DirectionExpression(FieldDirection.Ref, expr.WithILInstruction(inst)).WithoutILInstruction() - .WithRR(new ByReferenceResolveResult(inst.Type, false)); + .WithRR(new ByReferenceResolveResult(inst.Type, ReferenceKind.Ref)); } protected internal override TranslatedExpression VisitBlock(Block block, TranslationContext context) @@ -2190,8 +2286,6 @@ namespace ICSharpCode.Decompiler.CSharp case BlockKind.CollectionInitializer: case BlockKind.ObjectInitializer: return TranslateObjectAndCollectionInitializer(block); - case BlockKind.PostfixOperator: - return TranslatePostfixOperator(block); case BlockKind.CallInlineAssign: return TranslateSetterCallAssignment(block); case BlockKind.CallWithNamedArgs: @@ -2341,7 +2435,7 @@ namespace ICSharpCode.Decompiler.CSharp } TranslatedExpression MakeInitializerAssignment(InitializedObjectResolveResult rr, IL.Transforms.AccessPathElement memberPath, - IL.Transforms.AccessPathElement valuePath, List values, + IL.Transforms.AccessPathElement valuePath, List values, Dictionary indexVariables) { TranslatedExpression value; @@ -2526,19 +2620,6 @@ namespace ICSharpCode.Decompiler.CSharp .WithRR(new ResolveResult(stloc.Variable.Type)); } - TranslatedExpression TranslatePostfixOperator(Block block) - { - var targetInst = (block.Instructions.ElementAtOrDefault(0) as StLoc)?.Value; - var inst = (block.Instructions.ElementAtOrDefault(1) as StLoc)?.Value as BinaryNumericInstruction; - if (targetInst == null || inst == null || (inst.Operator != BinaryNumericOperator.Add && inst.Operator != BinaryNumericOperator.Sub)) - throw new ArgumentException("given Block is invalid!"); - var op = inst.Operator == BinaryNumericOperator.Add ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement; - var target = Translate(targetInst); - return new UnaryOperatorExpression(op, target) - .WithILInstruction(block) - .WithRR(resolver.WithCheckForOverflow(inst.CheckForOverflow).ResolveUnaryOperator(op, target.ResolveResult)); - } - /// /// If expr is a constant integer expression, and its value fits into type, /// convert the expression into the target type. @@ -2587,7 +2668,9 @@ namespace ICSharpCode.Decompiler.CSharp var rr = resolver.ResolveBinaryOperator(BinaryOperatorType.NullCoalescing, value.ResolveResult, fallback.ResolveResult); if (rr.IsError) { IType targetType; - if (!value.Type.Equals(SpecialType.NullType) && !fallback.Type.Equals(SpecialType.NullType) && !value.Type.Equals(fallback.Type)) { + if (fallback.Expression is ThrowExpression && fallback.Type.Equals(SpecialType.NoType)) { + targetType = NullableType.GetUnderlyingType(value.Type); + } else if (!value.Type.Equals(SpecialType.NullType) && !fallback.Type.Equals(SpecialType.NullType) && !value.Type.Equals(fallback.Type)) { targetType = compilation.FindType(inst.UnderlyingResultType.ToKnownTypeCode()); } else { targetType = value.Type.Equals(SpecialType.NullType) ? fallback.Type : value.Type; @@ -2676,7 +2759,7 @@ namespace ICSharpCode.Decompiler.CSharp new ConditionalExpression(condition.Expression, trueBranch.Expression, falseBranch.Expression) .WithILInstruction(inst) .WithRR(conditionalResolveResult) - ).WithoutILInstruction().WithRR(new ByReferenceResolveResult(conditionalResolveResult, isOut: false)); + ).WithoutILInstruction().WithRR(new ByReferenceResolveResult(conditionalResolveResult, ReferenceKind.Ref)); } else { return new ConditionalExpression(condition.Expression, trueBranch.Expression, falseBranch.Expression) .WithILInstruction(inst) @@ -2696,7 +2779,7 @@ namespace ICSharpCode.Decompiler.CSharp var value = Translate(inst.Value, targetTypeHint); return new DirectionExpression(FieldDirection.Ref, value) .WithILInstruction(inst) - .WithRR(new ByReferenceResolveResult(value.ResolveResult, false)); + .WithRR(new ByReferenceResolveResult(value.ResolveResult, ReferenceKind.Ref)); } protected internal override TranslatedExpression VisitAwait(Await inst, TranslationContext context) @@ -2808,7 +2891,6 @@ namespace ICSharpCode.Decompiler.CSharp { Debug.Assert(!argumentInfo.HasFlag(CSharpArgumentInfoFlags.NamedArgument)); Debug.Assert(!argumentInfo.HasFlag(CSharpArgumentInfoFlags.IsOut)); - Debug.Assert(!argumentInfo.HasFlag(CSharpArgumentInfoFlags.Constant)); if (argumentInfo.HasFlag(CSharpArgumentInfoFlags.IsStaticType) && IL.Transforms.TransformExpressionTrees.MatchGetTypeFromHandle(inst, out var callTargetType)) { return new TypeReferenceExpression(ConvertType(callTargetType)) @@ -2851,7 +2933,7 @@ namespace ICSharpCode.Decompiler.CSharp translatedExpression = translatedExpression.ConvertTo(typeHint, this); } if (info.HasFlag(CSharpArgumentInfoFlags.IsOut)) { - translatedExpression = ChangeDirectionExpressionToOut(translatedExpression); + translatedExpression = ChangeDirectionExpressionTo(translatedExpression, ReferenceKind.Out); } if (info.HasFlag(CSharpArgumentInfoFlags.NamedArgument) && !string.IsNullOrWhiteSpace(info.Name)) { translatedExpression = new TranslatedExpression(new NamedArgumentExpression(info.Name, translatedExpression.Expression)); @@ -2860,16 +2942,16 @@ namespace ICSharpCode.Decompiler.CSharp return translatedExpression; } - internal static TranslatedExpression ChangeDirectionExpressionToOut(TranslatedExpression input) + internal static TranslatedExpression ChangeDirectionExpressionTo(TranslatedExpression input, ReferenceKind kind) { if (!(input.Expression is DirectionExpression dirExpr && input.ResolveResult is ByReferenceResolveResult brrr)) return input; - dirExpr.FieldDirection = FieldDirection.Out; + dirExpr.FieldDirection = (FieldDirection)kind; dirExpr.RemoveAnnotations(); if (brrr.ElementResult == null) - brrr = new ByReferenceResolveResult(brrr.ElementType, isOut: true); + brrr = new ByReferenceResolveResult(brrr.ElementType, kind); else - brrr = new ByReferenceResolveResult(brrr.ElementResult, isOut: true); + brrr = new ByReferenceResolveResult(brrr.ElementResult, kind); dirExpr.AddAnnotation(brrr); return new TranslatedExpression(dirExpr); } @@ -3035,7 +3117,12 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitDynamicCompoundAssign(DynamicCompoundAssign inst, TranslationContext context) { - var target = TranslateDynamicArgument(inst.Target, inst.TargetArgumentInfo); + ExpressionWithResolveResult target; + if (inst.TargetKind == CompoundTargetKind.Address) { + target = LdObj(inst.Target, SpecialType.Dynamic); + } else { + target = TranslateDynamicArgument(inst.Target, inst.TargetArgumentInfo); + } var value = TranslateDynamicArgument(inst.Value, inst.ValueArgumentInfo); var ae = new AssignmentExpression(target, AssignmentExpression.GetAssignmentOperatorTypeFromExpressionType(inst.Operation).Value, value); diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index d3bef7f73..618f0d5af 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -1313,10 +1313,15 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } WriteCommaSeparatedList(attributeSection.Attributes); WriteToken(Roles.RBracket); - if (attributeSection.Parent is ParameterDeclaration || attributeSection.Parent is TypeParameterDeclaration) { - Space(); - } else { - NewLine(); + switch (attributeSection.Parent) { + case ParameterDeclaration _: + case TypeParameterDeclaration _: + case ComposedType _: + Space(); + break; + default: + NewLine(); + break; } EndNode(attributeSection); } @@ -1883,6 +1888,25 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Semicolon(); EndNode(variableDeclarationStatement); } + + public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement) + { + StartNode(localFunctionDeclarationStatement); + + WriteModifiers(localFunctionDeclarationStatement.ModifierTokens); + localFunctionDeclarationStatement.ReturnType.AcceptVisitor(this); + Space(); + WriteIdentifier(localFunctionDeclarationStatement.NameToken); + WriteTypeParameters(localFunctionDeclarationStatement.TypeParameters); + Space(policy.SpaceBeforeMethodDeclarationParentheses); + WriteCommaSeparatedListInParenthesis(localFunctionDeclarationStatement.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + foreach (Constraint constraint in localFunctionDeclarationStatement.Constraints) { + constraint.AcceptVisitor(this); + } + WriteMethodBody(localFunctionDeclarationStatement.Body, policy.MethodBraceStyle); + + EndNode(localFunctionDeclarationStatement); + } public virtual void VisitWhileStatement(WhileStatement whileStatement) { @@ -2173,21 +2197,26 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { StartNode(parameterDeclaration); WriteAttributes(parameterDeclaration.Attributes); + if (parameterDeclaration.HasThisModifier) { + WriteKeyword(ParameterDeclaration.ThisModifierRole); + Space(); + } switch (parameterDeclaration.ParameterModifier) { case ParameterModifier.Ref: WriteKeyword(ParameterDeclaration.RefModifierRole); + Space(); break; case ParameterModifier.Out: WriteKeyword(ParameterDeclaration.OutModifierRole); + Space(); break; case ParameterModifier.Params: WriteKeyword(ParameterDeclaration.ParamsModifierRole); - break; - case ParameterModifier.This: - WriteKeyword(ParameterDeclaration.ThisModifierRole); + Space(); break; case ParameterModifier.In: WriteKeyword(ParameterDeclaration.InModifierRole); + Space(); break; } parameterDeclaration.Type.AcceptVisitor(this); @@ -2325,6 +2354,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor public virtual void VisitComposedType(ComposedType composedType) { StartNode(composedType); + if (composedType.Attributes.Any()) { + foreach (var attr in composedType.Attributes) { + attr.AcceptVisitor(this); + } + } if (composedType.HasRefSpecifier) { WriteKeyword(ComposedType.RefRole); } diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs index 8fbeb4bbd..6391cf62f 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs @@ -209,7 +209,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } /// - /// The K&R style, so named because it was used in Kernighan and Ritchie's book The C Programming Language, + /// The K&R style, so named because it was used in Kernighan and Ritchie's book The C Programming Language, /// is commonly used in C. It is less common for C++, C#, and others. /// public static CSharpFormattingOptions CreateKRStyle() diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs index 4966b06a1..3af55614f 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs @@ -362,7 +362,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { // assignment is right-associative ParenthesizeIfRequired(assignmentExpression.Left, Assignment + 1); - if (InsertParenthesesForReadability) { + if (InsertParenthesesForReadability && !(assignmentExpression.Right is DirectionExpression)) { ParenthesizeIfRequired(assignmentExpression.Right, RelationalAndTypeTesting + 1); } else { ParenthesizeIfRequired(assignmentExpression.Right, Assignment); diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs index 19e31d3bf..162f19fd2 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs @@ -382,6 +382,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor // ASCII characters we allow directly in the output even though we don't use // other Unicode characters of the same category. return null; + case '\ufffd': + return "\\u" + ((int)ch).ToString("x4"); default: switch (char.GetUnicodeCategory(ch)) { case UnicodeCategory.ModifierLetter: diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs index ff0ff5854..cb52b8385 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs @@ -115,6 +115,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver if (c != Conversion.None) return c; } + if (resolveResult is ThrowResolveResult) { + return Conversion.ThrowExpressionConversion; + } if (allowUserDefined && allowTuple) { // if allowUserDefined and allowTuple are true, we might as well use the cache c = ImplicitConversion(resolveResult.Type, toType); @@ -647,7 +650,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver { TypeKind kind = type.Kind; return kind == TypeKind.Class && type.GetDefinition().IsSealed - || kind == TypeKind.Delegate || kind == TypeKind.Anonymous; + || kind == TypeKind.Delegate; } #endregion @@ -1018,7 +1021,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver if (f.IsImplicitlyTyped) { // If F has an implicitly typed parameter list, D has no ref or out parameters. foreach (IParameter p in d.Parameters) { - if (p.IsOut || p.IsRef) + if (p.ReferenceKind != ReferenceKind.None) return Conversion.None; } } else { @@ -1027,7 +1030,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver for (int i = 0; i < f.Parameters.Count; i++) { IParameter pD = d.Parameters[i]; IParameter pF = f.Parameters[i]; - if (pD.IsRef != pF.IsRef || pD.IsOut != pF.IsOut) + if (pD.ReferenceKind != pF.ReferenceKind) return Conversion.None; if (!IdentityConversion(dParamTypes[i], pF.Type)) return Conversion.None; @@ -1071,9 +1074,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver for (int i = 0; i < args.Length; i++) { IParameter param = invoke.Parameters[i]; IType parameterType = param.Type; - if ((param.IsRef || param.IsOut) && parameterType.Kind == TypeKind.ByReference) { + if (param.ReferenceKind != ReferenceKind.None && parameterType.Kind == TypeKind.ByReference) { parameterType = ((ByReferenceType)parameterType).ElementType; - args[i] = new ByReferenceResolveResult(parameterType, param.IsOut); + args[i] = new ByReferenceResolveResult(parameterType, param.ReferenceKind); } else { args[i] = new ResolveResult(parameterType); } @@ -1132,11 +1135,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver for (int i = 0; i < invoke.Parameters.Count; i++) { var pm = m.Parameters[firstParameterInM + i]; var pd = invoke.Parameters[i]; - // ret/out must match - if (pm.IsRef != pd.IsRef || pm.IsOut != pd.IsOut) + // ret/out/in must match + if (pm.ReferenceKind != pd.ReferenceKind) return false; - if (pm.IsRef || pm.IsOut) { - // ref/out parameters must have same types + if (pm.ReferenceKind != ReferenceKind.None) { + // ref/out/in parameters must have same types if (!pm.Type.Equals(pd.Type)) return false; } else { diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs index ee66cb8d9..61e64a7c0 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs @@ -46,22 +46,8 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver /// Gets whether a params-Array is being used in its expanded form. /// public readonly bool IsExpandedForm; - - readonly IReadOnlyList argumentToParameterMap; - /// - /// If IsExtensionMethodInvocation is true this property holds the reduced method. - /// - IMethod reducedMethod; - public IMethod ReducedMethod { - get { - if (!IsExtensionMethodInvocation) - return null; - if (reducedMethod == null && Member is IMethod) - reducedMethod = new ReducedExtensionMethod ((IMethod)Member); - return reducedMethod; - } - } + readonly IReadOnlyList argumentToParameterMap; public CSharpInvocationResolveResult( ResolveResult targetResult, IParameterizedMember member, diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs index d26e8d857..0ef09c1bb 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs @@ -2084,7 +2084,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver // create the parameter: ByReferenceResolveResult brrr = arguments[i] as ByReferenceResolveResult; if (brrr != null) { - list.Add(new DefaultParameter(arguments[i].Type, argumentNames[i], isRef: brrr.IsRef, isOut: brrr.IsOut)); + list.Add(new DefaultParameter(arguments[i].Type, argumentNames[i], referenceKind: brrr.ReferenceKind)); } else { // argument might be a lambda or delegate type, so we have to try to guess the delegate type IType type = arguments[i].Type; diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs index 83207005d..4f6022208 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs @@ -174,12 +174,12 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } return extensionMethods ?? Enumerable.Empty>(); } - + /// /// Gets the eligible extension methods. /// /// - /// Specifies whether to produce a + /// Specifies whether to produce a SpecializedMethod /// when type arguments could be inferred from . /// This setting is only used for inferred types and has no effect if the type parameters are /// specified explicitly. diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs b/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs index 21643562e..ef1a5e3d4 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs @@ -589,10 +589,10 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver ByReferenceResolveResult brrr = arguments[i] as ByReferenceResolveResult; if (brrr != null) { - if ((brrr.IsOut && !candidate.Parameters[parameterIndex].IsOut) || (brrr.IsRef && !candidate.Parameters[parameterIndex].IsRef)) + if (brrr.ReferenceKind != candidate.Parameters[parameterIndex].ReferenceKind) candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch); } else { - if (candidate.Parameters[parameterIndex].IsOut || candidate.Parameters[parameterIndex].IsRef) + if (candidate.Parameters[parameterIndex].ReferenceKind != ReferenceKind.None) candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch); } IType parameterType = candidate.ParameterTypes[parameterIndex]; @@ -940,6 +940,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver /// /// Statements for Objects/Collections initializer. /// + /// /// /// If not null, use this instead of the ReturnType of the member as the type of the created resolve result. /// diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs b/ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs deleted file mode 100644 index 185e626c0..000000000 --- a/ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs +++ /dev/null @@ -1,306 +0,0 @@ -// -// ReducedExtensionMethod.cs -// -// Author: -// Mike Krüger -// -// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using ICSharpCode.Decompiler.TypeSystem; - -namespace ICSharpCode.Decompiler.CSharp.Resolver -{ - /// - /// An invocated extension method hides the extension parameter in its parameter list. - /// It's used to hide the internals of extension method invocation in certain situation to simulate the - /// syntactic way of writing extension methods on semantic level. - /// - public class ReducedExtensionMethod : IMethod - { - readonly IMethod baseMethod; - - public ReducedExtensionMethod(IMethod baseMethod) - { - this.baseMethod = baseMethod; - } - - public bool Equals(IMember obj, TypeVisitor typeNormalization) - { - var other = obj as ReducedExtensionMethod; - if (other == null) - return false; - return baseMethod.Equals(other.baseMethod, typeNormalization); - } - - public override bool Equals(object obj) - { - var other = obj as ReducedExtensionMethod; - if (other == null) - return false; - return baseMethod.Equals(other.baseMethod); - } - - public override int GetHashCode() - { - unchecked { - return baseMethod.GetHashCode() + 1; - } - } - - public override string ToString() - { - return string.Format("[ReducedExtensionMethod: ReducedFrom={0}]", ReducedFrom); - } - - #region IMember implementation - public IMember MemberDefinition { - get { - return baseMethod.MemberDefinition; - } - } - - public IType ReturnType { - get { - return baseMethod.ReturnType; - } - } - - public IEnumerable ExplicitlyImplementedInterfaceMembers { - get { - return baseMethod.ExplicitlyImplementedInterfaceMembers; - } - } - - public bool IsExplicitInterfaceImplementation { - get { - return baseMethod.IsExplicitInterfaceImplementation; - } - } - - public bool IsVirtual { - get { - return baseMethod.IsVirtual; - } - } - - public bool IsOverride { - get { - return baseMethod.IsOverride; - } - } - - public bool IsOverridable { - get { - return baseMethod.IsOverridable; - } - } - - public TypeParameterSubstitution Substitution { - get { - return baseMethod.Substitution; - } - } - - public IMethod Specialize(TypeParameterSubstitution substitution) - { - return new ReducedExtensionMethod((IMethod)baseMethod.Specialize(substitution)); - } - - IMember IMember.Specialize(TypeParameterSubstitution substitution) - { - return Specialize(substitution); - } - - #endregion - - #region IMethod implementation - - public IReadOnlyList TypeParameters { - get { - return baseMethod.TypeParameters; - } - } - - public bool IsExtensionMethod { - get { - return true; - } - } - - public bool IsConstructor { - get { - return baseMethod.IsConstructor; - } - } - - public bool IsDestructor { - get { - return baseMethod.IsDestructor; - } - } - - public bool IsOperator { - get { - return baseMethod.IsOperator; - } - } - - public bool HasBody { - get { - return baseMethod.HasBody; - } - } - - public bool IsAccessor => baseMethod.IsAccessor; - public IMember AccessorOwner => baseMethod.AccessorOwner; - public MethodSemanticsAttributes AccessorKind => baseMethod.AccessorKind; - - public IMethod ReducedFrom { - get { - return baseMethod; - } - } - - public IReadOnlyList TypeArguments { - get { - return baseMethod.TypeArguments; - } - } - #endregion - - #region IParameterizedMember implementation - List parameters; - public IReadOnlyList Parameters { - get { - if (parameters == null) - parameters = new List (baseMethod.Parameters.Skip (1)); - return parameters; - } - } - - #endregion - - #region IEntity implementation - - public System.Reflection.Metadata.EntityHandle MetadataToken => baseMethod.MetadataToken; - - public SymbolKind SymbolKind { - get { - return baseMethod.SymbolKind; - } - } - - public ITypeDefinition DeclaringTypeDefinition { - get { - return baseMethod.DeclaringTypeDefinition; - } - } - - public IType DeclaringType { - get { - return baseMethod.DeclaringType; - } - } - - public IModule ParentModule { - get { - return baseMethod.ParentModule; - } - } - - IEnumerable IEntity.GetAttributes() => baseMethod.GetAttributes(); - IEnumerable IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes(); - bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly; - - public bool IsStatic { - get { - return false; - } - } - - public bool IsAbstract { - get { - return baseMethod.IsAbstract; - } - } - - public bool IsSealed { - get { - return baseMethod.IsSealed; - } - } - - #endregion - - #region IHasAccessibility implementation - - public Accessibility Accessibility { - get { - return baseMethod.Accessibility; - } - } - - #endregion - - #region INamedElement implementation - - public string FullName { - get { - return baseMethod.FullName; - } - } - - public string Name { - get { - return baseMethod.Name; - } - } - - public string ReflectionName { - get { - return baseMethod.ReflectionName; - } - } - - public string Namespace { - get { - return baseMethod.Namespace; - } - } - - #endregion - - #region ICompilationProvider implementation - - public ICompilation Compilation { - get { - return baseMethod.Compilation; - } - } - - #endregion - } -} - diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs b/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs index 1dd855800..40a804025 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs @@ -513,9 +513,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver for (int i = 0; i < args.Length; i++) { IParameter param = m.Parameters[i]; IType parameterType = param.Type.AcceptVisitor(substitution); - if ((param.IsRef || param.IsOut) && parameterType.Kind == TypeKind.ByReference) { + if ((param.ReferenceKind != ReferenceKind.None) && parameterType.Kind == TypeKind.ByReference) { parameterType = ((ByReferenceType)parameterType).ElementType; - args[i] = new ByReferenceResolveResult(parameterType, param.IsOut); + args[i] = new ByReferenceResolveResult(parameterType, param.ReferenceKind); } else { args[i] = new ResolveResult(parameterType); } @@ -570,7 +570,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver void MakeExactInference(IType U, IType V) { Log.WriteLine("MakeExactInference from " + U + " to " + V); - + + if (V is NullabilityAnnotatedTypeParameter nullableTP) { + V = nullableTP.OriginalTypeParameter; + } + // 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) { @@ -640,7 +644,13 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver MakeLowerBoundInference(NullableType.GetUnderlyingType(U), NullableType.GetUnderlyingType(V)); return; } - + // Handle by reference types: + ByReferenceType brU = U as ByReferenceType; + ByReferenceType brV = V as ByReferenceType; + if (brU != null && brV != null) { + MakeExactInference(brU.ElementType, brV.ElementType); + return; + } // Handle array types: ArrayType arrU = U as ArrayType; ArrayType arrV = V as ArrayType; diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index a799c9cf4..06ea73131 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -28,10 +28,11 @@ 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 { - class StatementBuilder : ILVisitor + sealed class StatementBuilder : ILVisitor { internal readonly ExpressionBuilder exprBuilder; readonly ILFunction currentFunction; @@ -411,6 +412,7 @@ namespace ICSharpCode.Decompiler.CSharp AstNode usingInit = resource; var var = inst.Variable; if (!inst.ResourceExpression.MatchLdNull() && !NullableType.GetUnderlyingType(var.Type).GetAllBaseTypes().Any(b => b.IsKnownType(KnownTypeCode.IDisposable))) { + Debug.Assert(var.Kind == VariableKind.UsingLocal); var.Kind = VariableKind.Local; var disposeType = exprBuilder.compilation.FindType(KnownTypeCode.IDisposable); var disposeVariable = currentFunction.RegisterVariable( @@ -707,7 +709,7 @@ namespace ICSharpCode.Decompiler.CSharp while (inst.Parent is UnboxAny || inst.Parent is CastClass) inst = inst.Parent; // One variable was found. - if (inst.Parent is StLoc stloc) { + if (inst.Parent is StLoc stloc && (stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.StackSlot)) { // Must be a plain assignment expression and variable must only be used in 'body' + only assigned once. if (stloc.Parent == loopBody && VariableIsOnlyUsedInBlock(stloc, usingContainer)) { foreachVariable = stloc.Variable; @@ -725,18 +727,32 @@ namespace ICSharpCode.Decompiler.CSharp /// /// Determines whether storeInst.Variable is only assigned once and used only inside . - /// Loads by reference (ldloca) are only allowed in the context of this pointer in call instructions. + /// Loads by reference (ldloca) are only allowed in the context of this pointer in call instructions, + /// or as target of ldobj. /// (This only applies to value types.) /// bool VariableIsOnlyUsedInBlock(StLoc storeInst, BlockContainer usingContainer) { if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(usingContainer))) return false; - if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(usingContainer) || !ILInlining.IsUsedAsThisPointerInCall(la) || IsTargetOfSetterCall(la, la.Variable.Type))) + if (storeInst.Variable.AddressInstructions.Any(inst => !AddressUseAllowed(inst))) return false; if (storeInst.Variable.StoreInstructions.OfType().Any(st => st != storeInst)) return false; return true; + + bool AddressUseAllowed(LdLoca la) + { + if (!la.IsDescendantOf(usingContainer)) + return false; + if (ILInlining.IsUsedAsThisPointerInCall(la) && !IsTargetOfSetterCall(la, la.Variable.Type)) + return true; + var current = la.Parent; + while (current is LdFlda next) { + current = next.Parent; + } + return current is LdObj; + } } /// @@ -849,6 +865,7 @@ namespace ICSharpCode.Decompiler.CSharp if (blockStatement.LastOrDefault() is ContinueStatement continueStmt) continueStmt.Remove(); + DeclareLocalFunctions(currentFunction, container, blockStatement); return new WhileStatement(new PrimitiveExpression(true), blockStatement); case ContainerKind.While: continueTarget = container.EntryPoint; @@ -870,6 +887,7 @@ namespace ICSharpCode.Decompiler.CSharp if (blockStatement.LastOrDefault() is ContinueStatement continueStmt2) continueStmt2.Remove(); + DeclareLocalFunctions(currentFunction, container, blockStatement); return new WhileStatement(exprBuilder.TranslateCondition(condition), blockStatement); case ContainerKind.DoWhile: continueTarget = container.Blocks.Last(); @@ -888,6 +906,7 @@ namespace ICSharpCode.Decompiler.CSharp // to continue statements, we have to introduce an extra label. blockStatement.Add(new LabelStatement { Label = continueTarget.Label }); } + DeclareLocalFunctions(currentFunction, container, blockStatement); if (blockStatement.Statements.Count == 0) { return new WhileStatement { Condition = exprBuilder.TranslateCondition(condition), @@ -919,6 +938,7 @@ namespace ICSharpCode.Decompiler.CSharp } if (continueTarget.IncomingEdgeCount > continueCount) blockStatement.Add(new LabelStatement { Label = continueTarget.Label }); + DeclareLocalFunctions(currentFunction, container, blockStatement); return forStmt; default: throw new ArgumentOutOfRangeException(); @@ -927,7 +947,33 @@ namespace ICSharpCode.Decompiler.CSharp BlockStatement ConvertBlockContainer(BlockContainer container, bool isLoop) { - return ConvertBlockContainer(new BlockStatement(), container, container.Blocks, isLoop); + var blockStatement = ConvertBlockContainer(new BlockStatement(), container, container.Blocks, isLoop); + DeclareLocalFunctions(currentFunction, container, blockStatement); + return blockStatement; + } + + void DeclareLocalFunctions(ILFunction currentFunction, BlockContainer container, BlockStatement blockStatement) + { + foreach (var localFunction in currentFunction.LocalFunctions.OrderBy(f => f.Name)) { + if (localFunction.DeclarationScope != container) + continue; + blockStatement.Add(TranslateFunction(localFunction)); + } + + LocalFunctionDeclarationStatement TranslateFunction(ILFunction function) + { + var stmt = new LocalFunctionDeclarationStatement(); + var nestedBuilder = new StatementBuilder(typeSystem, exprBuilder.decompilationContext, function, settings, cancellationToken); + stmt.Name = function.Name; + stmt.Parameters.AddRange(exprBuilder.MakeParameters(function.Parameters, function)); + stmt.ReturnType = exprBuilder.ConvertType(function.Method.ReturnType); + stmt.Body = nestedBuilder.ConvertAsBlock(function.Body); + if (function.IsAsync) { + stmt.Modifiers |= Modifiers.Async; + } + stmt.AddAnnotation(new MemberResolveResult(null, function.ReducedMethod)); + return stmt; + } } BlockStatement ConvertBlockContainer(BlockStatement blockStatement, BlockContainer container, IEnumerable blocks, bool isLoop) diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs b/ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs index f4b768ad2..79363dc99 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs @@ -35,11 +35,15 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { public class ComposedType : AstType { + public static readonly Role AttributeRole = EntityDeclaration.AttributeRole; public static readonly TokenRole RefRole = new TokenRole("ref"); public static readonly TokenRole ReadonlyRole = new TokenRole("readonly"); public static readonly TokenRole NullableRole = new TokenRole("?"); public static readonly TokenRole PointerRole = new TokenRole("*"); public static readonly Role ArraySpecifierRole = new Role("ArraySpecifier"); + public AstNodeCollection Attributes { + get { return base.GetChildrenByRole(AttributeRole); } + } /// /// Gets/sets whether this type has a 'ref' specifier. @@ -83,6 +87,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } } + public bool HasOnlyNullableSpecifier { + get { + return HasNullableSpecifier && !HasRefSpecifier && !HasReadOnlySpecifier && PointerRank == 0 && ArraySpecifiers.Count == 0; + } + } + public CSharpTokenNode NullableSpecifierToken { get { return GetChildByRole(NullableRole); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs b/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs index 44add8be6..3b3a0b216 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs @@ -390,7 +390,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { VisitChildren (variableDeclarationStatement); } - + + public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement) + { + VisitChildren(localFunctionDeclarationStatement); + } + public virtual void VisitWhileStatement (WhileStatement whileStatement) { VisitChildren (whileStatement); @@ -1037,7 +1042,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { return VisitChildren (variableDeclarationStatement); } - + + public virtual T VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement) + { + return VisitChildren(localFunctionDeclarationStatement); + } + public virtual T VisitWhileStatement (WhileStatement whileStatement) { return VisitChildren (whileStatement); @@ -1684,7 +1694,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { return VisitChildren (variableDeclarationStatement, data); } - + + public virtual S VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement, T data) + { + return VisitChildren(localFunctionDeclarationStatement, data); + } + public virtual S VisitWhileStatement (WhileStatement whileStatement, T data) { return VisitChildren (whileStatement, data); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs index f9a9f2d95..da02b77e7 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs @@ -250,13 +250,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax Divide, /// left %= right Modulus, - - /// left <<= right + + /// left <<= right ShiftLeft, /// left >>= right ShiftRight, - /// left &= right + /// left &= right BitwiseAnd, /// left |= right BitwiseOr, diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs index 60213cc7d..31e9bd9f1 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs @@ -182,7 +182,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax PostDecrement, /// Dereferencing (*a) Dereference, - /// Get address (&a) + /// Get address (&a) AddressOf, /// C# 5.0 await Await, diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Constraint.cs b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Constraint.cs index 1bd6b59a2..ec1d707a1 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Constraint.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Constraint.cs @@ -36,9 +36,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public class Constraint : AstNode { public override NodeType NodeType { - get { - return NodeType.Unknown; - } + get { return NodeType.Unknown; } } public CSharpTokenNode WhereKeyword { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/ExternAliasDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/ExternAliasDeclaration.cs index e0b10c17f..b3c6419ba 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/ExternAliasDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/ExternAliasDeclaration.cs @@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { /// - /// extern alias ; + /// extern alias IDENTIFIER; /// public class ExternAliasDeclaration : AstNode { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs b/ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs index 2d5bd8760..9ed151865 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs @@ -110,6 +110,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax void VisitUnsafeStatement(UnsafeStatement unsafeStatement); void VisitUsingStatement(UsingStatement usingStatement); void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement); + void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement); void VisitWhileStatement(WhileStatement whileStatement); void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement); void VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement); @@ -251,6 +252,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax S VisitUnsafeStatement(UnsafeStatement unsafeStatement); S VisitUsingStatement(UsingStatement usingStatement); S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement); + S VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement); S VisitWhileStatement(WhileStatement whileStatement); S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement); S VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement); @@ -392,6 +394,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax S VisitUnsafeStatement(UnsafeStatement unsafeStatement, T data); S VisitUsingStatement(UsingStatement usingStatement, T data); S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, T data); + S VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement, T data); S VisitWhileStatement(WhileStatement whileStatement, T data); S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, T data); S VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement, T data); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs new file mode 100644 index 000000000..47ff1641c --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs @@ -0,0 +1,110 @@ +// Copyright (c) 2019 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 ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; + +namespace ICSharpCode.Decompiler.CSharp.Syntax +{ + public class LocalFunctionDeclarationStatement : Statement + { + public AstNodeCollection TypeParameters { + get { return GetChildrenByRole(Roles.TypeParameter); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole(Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole(Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole(Roles.RPar); } + } + + public AstNodeCollection Constraints { + get { return GetChildrenByRole(Roles.Constraint); } + } + + public BlockStatement Body { + get { return GetChildByRole(Roles.Body); } + set { SetChildByRole(Roles.Body, value); } + } + + public Modifiers Modifiers { + get { return EntityDeclaration.GetModifiers(this); } + set { EntityDeclaration.SetModifiers(this, value); } + } + + public bool HasModifier(Modifiers mod) + { + return (Modifiers & mod) == mod; + } + + public IEnumerable ModifierTokens { + get { return GetChildrenByRole(EntityDeclaration.ModifierRole); } + } + + public virtual string Name { + get { + return GetChildByRole(Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, Identifier.Create(value, TextLocation.Empty)); + } + } + + public virtual Identifier NameToken { + get { return GetChildByRole(Roles.Identifier); } + set { SetChildByRole(Roles.Identifier, value); } + } + + public virtual AstType ReturnType { + get { return GetChildByRole(Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public override void AcceptVisitor(IAstVisitor visitor) + { + visitor.VisitLocalFunctionDeclarationStatement(this); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + return visitor.VisitLocalFunctionDeclarationStatement(this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitLocalFunctionDeclarationStatement(this, data); + } + + protected internal override bool DoMatch(AstNode other, Match match) + { + LocalFunctionDeclarationStatement o = other as LocalFunctionDeclarationStatement; + return o != null && MatchString(this.Name, o.Name) + && (this.Modifiers == Modifiers.Any || this.Modifiers == o.Modifiers) + && this.ReturnType.DoMatch(o.ReturnType, match) + && this.TypeParameters.DoMatch(o.TypeParameters, match) + && this.Parameters.DoMatch(o.Parameters, match) && this.Constraints.DoMatch(o.Constraints, match) + && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxExtensions.cs b/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxExtensions.cs index b4ffe1f74..632e6bbbb 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxExtensions.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxExtensions.cs @@ -75,5 +75,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax node.Remove(); return node; } + + public static Expression UnwrapInDirectionExpression(this Expression expr) + { + if (!(expr is DirectionExpression dir && dir.FieldDirection == FieldDirection.In)) + return expr; + return dir.Expression.Detach(); + } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs index b49bbf7dd..8f9746d8d 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs @@ -26,10 +26,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// A line/column position. /// Text editor lines/columns are counted started from one. /// - /// - /// The document provides the methods and - /// to convert between offsets and TextLocations. - /// [Serializable] [TypeConverter(typeof(TextLocationConverter))] public struct TextLocation : IComparable, IEquatable diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs index 2bca3eb50..a8e254c1d 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs @@ -25,7 +25,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public abstract class EntityDeclaration : AstNode { public static readonly Role AttributeRole = new Role("Attribute"); - public static readonly Role UnattachedAttributeRole = new Role("UnattachedAttribute"); public static readonly Role ModifierRole = new Role("Modifier"); public static readonly Role PrivateImplementationTypeRole = new Role("PrivateImplementationType", AstType.Null); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/MethodDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/MethodDeclaration.cs index ee91ad37d..c7da3d4ea 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/MethodDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/MethodDeclaration.cs @@ -71,7 +71,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public bool IsExtensionMethod { get { ParameterDeclaration pd = (ParameterDeclaration)GetChildByRole (Roles.Parameter); - return pd != null && pd.ParameterModifier == ParameterModifier.This; + return pd != null && pd.HasThisModifier; } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs index 4e4211ff8..b100f12bd 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs @@ -27,15 +27,15 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { - public enum ParameterModifier { + public enum ParameterModifier + { None, Ref, Out, Params, - This, In } - + public class ParameterDeclaration : AstNode { public static readonly Role AttributeRole = EntityDeclaration.AttributeRole; @@ -96,11 +96,30 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return NodeType.Unknown; } } - + public AstNodeCollection Attributes { - get { return GetChildrenByRole (AttributeRole); } + get { return GetChildrenByRole(AttributeRole); } } - + + bool hasThisModifier; + + public CSharpTokenNode ThisKeyword { + get { + if (hasThisModifier) { + return GetChildByRole(ThisModifierRole); + } + return CSharpTokenNode.Null; + } + } + + public bool HasThisModifier { + get { return hasThisModifier; } + set { + ThrowIfFrozen(); + hasThisModifier = value; + } + } + ParameterModifier parameterModifier; public ParameterModifier ParameterModifier { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index ae680a94a..02416b7e0 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -527,12 +527,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { Attribute attr = new Attribute(); attr.Type = ConvertAttributeType(attribute.AttributeType); - SimpleType st = attr.Type as SimpleType; - MemberType mt = attr.Type as MemberType; - if (st != null && st.Identifier.EndsWith("Attribute", StringComparison.Ordinal)) { - st.Identifier = st.Identifier.Substring(0, st.Identifier.Length - 9); - } else if (mt != null && mt.MemberName.EndsWith("Attribute", StringComparison.Ordinal)) { - mt.MemberName = mt.MemberName.Substring(0, mt.MemberName.Length - 9); + switch (attr.Type) { + case SimpleType st: + if (st.Identifier.EndsWith("Attribute", StringComparison.Ordinal)) + st.Identifier = st.Identifier.Substring(0, st.Identifier.Length - 9); + break; + case MemberType mt: + if (mt.MemberName.EndsWith("Attribute", StringComparison.Ordinal)) + mt.MemberName = mt.MemberName.Substring(0, mt.MemberName.Length - 9); + break; + } + if (AddResolveResultAnnotations && attribute.Constructor != null) { + attr.AddAnnotation(new MemberResolveResult(null, attribute.Constructor)); } var parameters = attribute.Constructor?.Parameters ?? EmptyList.Instance; for (int i = 0; i < attribute.FixedArguments.Length; i++) { @@ -1633,8 +1639,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax foreach (IParameter p in method.Parameters) { decl.Parameters.Add(ConvertParameter(p)); } - if (method.IsExtensionMethod && method.ReducedFrom == null && decl.Parameters.Any() && decl.Parameters.First().ParameterModifier == ParameterModifier.None) - decl.Parameters.First().ParameterModifier = ParameterModifier.This; + if (method.IsExtensionMethod && method.ReducedFrom == null && decl.Parameters.Any()) + decl.Parameters.First().HasThisModifier = true; if (this.ShowTypeParameters && this.ShowTypeParameterConstraints && !method.IsOverride && !method.IsExplicitInterfaceImplementation) { foreach (ITypeParameter tp in method.TypeParameters) { @@ -1779,7 +1785,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax Constraint ConvertTypeParameterConstraint(ITypeParameter tp) { - if (!tp.HasDefaultConstructorConstraint && !tp.HasReferenceTypeConstraint && !tp.HasValueTypeConstraint && tp.DirectBaseTypes.All(IsObjectOrValueType)) { + if (!tp.HasDefaultConstructorConstraint && !tp.HasReferenceTypeConstraint && !tp.HasValueTypeConstraint && tp.NullabilityConstraint != Nullability.NotNullable && tp.DirectBaseTypes.All(IsObjectOrValueType)) { return null; } Constraint c = new Constraint(); @@ -1796,10 +1802,22 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } else { c.BaseTypes.Add(new PrimitiveType("struct")); } - } - foreach (IType t in tp.DirectBaseTypes) { - if (!IsObjectOrValueType(t)) - c.BaseTypes.Add(ConvertType(t)); + } else if (tp.NullabilityConstraint == Nullability.NotNullable) { + c.BaseTypes.Add(new PrimitiveType("notnull")); + } + foreach (TypeConstraint t in tp.TypeConstraints) { + if (!IsObjectOrValueType(t.Type) || t.Attributes.Count > 0) { + AstType astType = ConvertType(t.Type); + if (t.Attributes.Count > 0) { + var attrSection = new AttributeSection(); + attrSection.Attributes.AddRange(t.Attributes.Select(ConvertAttribute)); + astType = new ComposedType { + Attributes = { attrSection }, + BaseType = astType + }; + } + c.BaseTypes.Add(astType); + } } if (tp.HasDefaultConstructorConstraint && !tp.HasValueTypeConstraint) { c.BaseTypes.Add(new PrimitiveType("new")); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs b/ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs index 0ce8a0bde..16f067a96 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs @@ -296,9 +296,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms costUncheckedContextCheckedBlockOpen += stmtResult.CostInCheckedContext; nodesUncheckedContextCheckedBlockOpen += stmtResult.NodesToInsertInCheckedContext; - if (statement is LabelStatement) { + if (statement is LabelStatement || statement is LocalFunctionDeclarationStatement) { // We can't move labels into blocks because that might cause goto-statements - // to be unable to just to the labels. + // to be unable to jump to the labels. + // Also, we can't move local functions into blocks, because that might cause + // them to become out of scope from the call-sites. costCheckedContextUncheckedBlockOpen = Cost.Infinite; costUncheckedContextCheckedBlockOpen = Cost.Infinite; } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs index 4b2051cc9..c9310107a 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs @@ -260,7 +260,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } else { newPoint = new InsertionPoint { level = nodeLevel, nextNode = identExpr }; if (variable.HasInitialValue) { - // Uninitialized variables are logically initialized at the beginning of the functin + // Uninitialized variables are logically initialized at the beginning of the function // Because it's possible that the variable has a loop-carried dependency, // declare it outside of any loops. while (startIndex >= 0) { @@ -291,7 +291,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } } - bool VariableNeedsDeclaration(VariableKind kind) + internal static bool VariableNeedsDeclaration(VariableKind kind) { switch (kind) { case VariableKind.PinnedLocal: diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/FlattenSwitchBlocks.cs b/ICSharpCode.Decompiler/CSharp/Transforms/FlattenSwitchBlocks.cs index 68bb7c47f..5e9908aaf 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/FlattenSwitchBlocks.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/FlattenSwitchBlocks.cs @@ -16,12 +16,25 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms continue; var blockStatement = switchSection.Statements.First() as BlockStatement; - if (blockStatement == null || blockStatement.Statements.Any(st => st is VariableDeclarationStatement)) + if (blockStatement == null || blockStatement.Statements.Any(ContainsLocalDeclaration)) continue; blockStatement.Remove(); blockStatement.Statements.MoveTo(switchSection.Statements); } + + bool ContainsLocalDeclaration(AstNode node) + { + if (node is VariableDeclarationStatement || node is LocalFunctionDeclarationStatement || node is OutVarDeclarationExpression) + return true; + if (node is BlockStatement) + return false; + foreach (var child in node.Children) { + if (ContainsLocalDeclaration(child)) + return true; + } + return false; + } } } } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs index e15d0d765..0e23d86cc 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs @@ -137,7 +137,13 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } if (!CanTransformToExtensionMethodCall(resolver, method, typeArguments, target, args, argNames)) return; - if (firstArgument is NullReferenceExpression) { + if (firstArgument is DirectionExpression dirExpr) { + if (!context.Settings.RefExtensionMethods || dirExpr.FieldDirection == FieldDirection.Out) + return; + firstArgument = dirExpr.Expression; + target = firstArgument.GetResolveResult(); + dirExpr.Detach(); + } else if (firstArgument is NullReferenceExpression) { Debug.Assert(context.RequiredNamespacesSuperset.Contains(method.Parameters[0].Type.Namespace)); firstArgument = firstArgument.ReplaceWith(expr => new CastExpression(context.TypeSystemAstBuilder.ConvertType(method.Parameters[0].Type), expr.Detach())); } @@ -162,13 +168,16 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms public static bool CanTransformToExtensionMethodCall(CSharpResolver resolver, IMethod method, IReadOnlyList typeArguments, ResolveResult target, ResolveResult[] arguments, string[] argumentNames) { + if (target is LambdaResolveResult) + return false; var rr = resolver.ResolveMemberAccess(target, method.Name, typeArguments, NameLookupMode.InvocationTarget) as MethodGroupResolveResult; if (rr == null) return false; var or = rr.PerformOverloadResolution(resolver.CurrentTypeResolveContext.Compilation, arguments, argumentNames, allowExtensionMethods: true); if (or == null || or.IsAmbiguous) return false; - return method.Equals(or.GetBestCandidateWithSubstitutedTypeArguments()); + return method.Equals(or.GetBestCandidateWithSubstitutedTypeArguments()) + && CSharpResolver.IsEligibleExtensionMethod(target.Type, method, useTypeInference: false, out _); } public static bool CanTransformToExtensionMethodCall(IMethod method, CSharpTypeResolveContext resolveContext, bool ignoreTypeArguments = false, bool ignoreArgumentNames = true) diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs index f1309f429..02faac75e 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs @@ -90,10 +90,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (mre == null || IsNullConditional(mre.Target)) return null; switch (mre.MemberName) { - case "Select": - { + case "Select": { if (invocation.Arguments.Count != 1) return null; + if (!IsComplexQuery(mre)) + return null; ParameterDeclaration parameter; Expression body; if (MatchSimpleLambda(invocation.Arguments.Single(), out parameter, out body)) { @@ -158,6 +159,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { if (invocation.Arguments.Count != 1) return null; + if (!IsComplexQuery(mre)) + return null; ParameterDeclaration parameter; Expression body; if (MatchSimpleLambda(invocation.Arguments.Single(), out parameter, out body)) { @@ -175,6 +178,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { if (invocation.Arguments.Count != 1) return null; + if (!IsComplexQuery(mre)) + return null; ParameterDeclaration parameter; Expression orderExpression; if (MatchSimpleLambda(invocation.Arguments.Single(), out parameter, out orderExpression)) { @@ -250,6 +255,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } } + static bool IsComplexQuery(MemberReferenceExpression mre) + { + return ((mre.Target is InvocationExpression && mre.Parent is InvocationExpression) || mre.Parent?.Parent is QueryClause); + } + QueryFromClause MakeFromClause(ParameterDeclaration parameter, Expression body) { QueryFromClause fromClause = new QueryFromClause { diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index 7d23edfa8..faa6a3163 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -153,7 +153,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms Statements = { new Repeat(new AnyNode("statement")), new NamedNode( - "increment", + "iterator", new ExpressionStatement( new AssignmentExpression { Left = new Backreference("ident"), @@ -180,6 +180,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (variable != m3.Get("ident").Single().GetILVariable()) return null; WhileStatement loop = (WhileStatement)next; + // Cannot convert to for loop, if any variable that is used in the "iterator" part of the pattern, + // will be declared in the body of the while-loop. + var iteratorStatement = m3.Get("iterator").Single(); + if (IteratorVariablesDeclaredInsideLoopBody(iteratorStatement)) + return null; // Cannot convert to for loop, because that would change the semantics of the program. // continue in while jumps to the condition block. // Whereas continue in for jumps to the increment block. @@ -193,7 +198,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms forStatement.CopyAnnotationsFrom(loop); forStatement.Initializers.Add(node); forStatement.Condition = loop.Condition.Detach(); - forStatement.Iterators.Add(m3.Get("increment").Single().Detach()); + forStatement.Iterators.Add(iteratorStatement.Detach()); forStatement.EmbeddedStatement = newBody; loop.ReplaceWith(forStatement); return forStatement; @@ -216,6 +221,18 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return true; return false; } + + bool IteratorVariablesDeclaredInsideLoopBody(Statement iteratorStatement) + { + foreach (var id in iteratorStatement.DescendantsAndSelf.OfType()) { + var v = id.GetILVariable(); + if (v == null || !DeclareVariables.VariableNeedsDeclaration(v.Kind)) + continue; + if (declareVariables.GetDeclarationPoint(v).Parent == iteratorStatement.Parent) + return true; + } + return false; + } #endregion #region foreach @@ -467,6 +484,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms statementsToDelete.Add(stmt.GetNextStatement()); var itemVariable = foreachVariable.GetILVariable(); if (itemVariable == null || !itemVariable.IsSingleDefinition + || (itemVariable.Kind != IL.VariableKind.Local && itemVariable.Kind != IL.VariableKind.StackSlot) || !upperBounds.All(ub => ub.IsSingleDefinition && ub.LoadCount == 1) || !lowerBounds.All(lb => lb.StoreCount == 2 && lb.LoadCount == 3 && lb.AddressCount == 0)) return null; @@ -776,8 +794,16 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms default: return false; } - if (!ev.ReturnType.IsMatch(m.Get("type").Single())) - return false; // variable types must match event type + if (!ev.ReturnType.IsMatch(m.Get("type").Single())) { + // Variable types must match event type, + // except that the event type may have an additional nullability annotation + if (ev.ReturnType is ComposedType ct && ct.HasOnlyNullableSpecifier) { + if (!ct.BaseType.IsMatch(m.Get("type").Single())) + return false; + } else { + return false; + } + } var combineMethod = m.Get("delegateCombine").Single().Parent.GetSymbol() as IMethod; if (combineMethod == null || combineMethod.Name != (isAddAccessor ? "Combine" : "Remove")) return false; @@ -976,24 +1002,26 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms /// /// Use associativity of logic operators to avoid parentheses. /// - public override AstNode VisitBinaryOperatorExpression(BinaryOperatorExpression boe1) + public override AstNode VisitBinaryOperatorExpression(BinaryOperatorExpression expr) { - switch (boe1.Operator) { + switch (expr.Operator) { case BinaryOperatorType.ConditionalAnd: case BinaryOperatorType.ConditionalOr: // a && (b && c) ==> (a && b) && c - var boe2 = boe1.Right as BinaryOperatorExpression; - if (boe2 != null && boe2.Operator == boe1.Operator) { - // make boe2 the parent and boe1 the child - var b = boe2.Left.Detach(); - boe1.ReplaceWith(boe2.Detach()); - boe2.Left = boe1; - boe1.Right = b; - return base.VisitBinaryOperatorExpression(boe2); + var bAndC = expr.Right as BinaryOperatorExpression; + if (bAndC != null && bAndC.Operator == expr.Operator) { + // make bAndC the parent and expr the child + var b = bAndC.Left.Detach(); + var c = bAndC.Right.Detach(); + expr.ReplaceWith(bAndC.Detach()); + bAndC.Left = expr; + bAndC.Right = c; + expr.Right = b; + return base.VisitBinaryOperatorExpression(bAndC); } break; } - return base.VisitBinaryOperatorExpression(boe1); + return base.VisitBinaryOperatorExpression(expr); } #endregion } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs index 62b97c24f..9d9838d71 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs @@ -60,7 +60,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression Expression expr = arguments[0]; for (int i = 1; i < arguments.Length; i++) { - expr = new BinaryOperatorExpression(expr, BinaryOperatorType.Add, arguments[i]); + expr = new BinaryOperatorExpression(expr, BinaryOperatorType.Add, arguments[i].UnwrapInDirectionExpression()); } expr.CopyAnnotationsFrom(invocationExpression); invocationExpression.ReplaceWith(expr); @@ -116,28 +116,48 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (bop != null && arguments.Length == 2) { invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression invocationExpression.ReplaceWith( - new BinaryOperatorExpression(arguments[0], bop.Value, arguments[1]).CopyAnnotationsFrom(invocationExpression) + new BinaryOperatorExpression( + arguments[0].UnwrapInDirectionExpression(), + bop.Value, + arguments[1].UnwrapInDirectionExpression() + ).CopyAnnotationsFrom(invocationExpression) ); return; } UnaryOperatorType? uop = GetUnaryOperatorTypeFromMetadataName(method.Name); if (uop != null && arguments.Length == 1) { + if (uop == UnaryOperatorType.Increment || uop == UnaryOperatorType.Decrement) { + // `op_Increment(a)` is not equivalent to `++a`, + // because it doesn't assign the incremented value to a. + if (method.DeclaringType.IsKnownType(KnownTypeCode.Decimal)) { + // Legacy csc optimizes "d + 1m" to "op_Increment(d)", + // so reverse that optimization here: + invocationExpression.ReplaceWith( + new BinaryOperatorExpression( + arguments[0].UnwrapInDirectionExpression().Detach(), + (uop == UnaryOperatorType.Increment ? BinaryOperatorType.Add : BinaryOperatorType.Subtract), + new PrimitiveExpression(1m) + ).CopyAnnotationsFrom(invocationExpression) + ); + } + return; + } arguments[0].Remove(); // detach argument invocationExpression.ReplaceWith( - new UnaryOperatorExpression(uop.Value, arguments[0]).CopyAnnotationsFrom(invocationExpression) + new UnaryOperatorExpression(uop.Value, arguments[0].UnwrapInDirectionExpression()).CopyAnnotationsFrom(invocationExpression) ); return; } if (method.Name == "op_Explicit" && arguments.Length == 1) { arguments[0].Remove(); // detach argument invocationExpression.ReplaceWith( - new CastExpression(context.TypeSystemAstBuilder.ConvertType(method.ReturnType), arguments[0]) + new CastExpression(context.TypeSystemAstBuilder.ConvertType(method.ReturnType), arguments[0].UnwrapInDirectionExpression()) .CopyAnnotationsFrom(invocationExpression) ); return; } if (method.Name == "op_True" && arguments.Length == 1 && invocationExpression.Role == Roles.Condition) { - invocationExpression.ReplaceWith(arguments[0]); + invocationExpression.ReplaceWith(arguments[0].UnwrapInDirectionExpression()); return; } @@ -154,8 +174,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (arguments.Length < 2) return false; - return arguments[0].GetResolveResult().Type.IsKnownType(KnownTypeCode.String) || - arguments[1].GetResolveResult().Type.IsKnownType(KnownTypeCode.String); + return !arguments.Any(arg => arg is NamedArgumentExpression) && + (arguments[0].GetResolveResult().Type.IsKnownType(KnownTypeCode.String) || + arguments[1].GetResolveResult().Type.IsKnownType(KnownTypeCode.String)); } static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name) diff --git a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs index e0d3dbae6..35582ec1d 100644 --- a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs @@ -163,7 +163,7 @@ namespace ICSharpCode.Decompiler.CSharp } throw new ArgumentException("descendant must be a descendant of the current node"); } - + /// /// Adds casts (if necessary) to convert this expression to the specified target type. /// @@ -176,6 +176,17 @@ namespace ICSharpCode.Decompiler.CSharp /// /// From the caller's perspective, IntPtr/UIntPtr behave like normal C# integers except that they have native int size. /// All the special cases necessary to make IntPtr/UIntPtr behave sanely are handled internally in ConvertTo(). + /// + /// Post-condition: + /// The "expected evaluation result" is the value computed by this.Expression, + /// converted to targetType via an IL conv instruction. + /// + /// ConvertTo(targetType, allowImplicitConversion=false).Type must be equal to targetType (modulo identity conversions). + /// The value computed by the converted expression must match the "expected evaluation result". + /// + /// ConvertTo(targetType, allowImplicitConversion=true) must produce an expression that, + /// when evaluated in a context where it will be implicitly converted to targetType, + /// evaluates to the "expected evaluation result". /// public TranslatedExpression ConvertTo(IType targetType, ExpressionBuilder expressionBuilder, bool checkForOverflow = false, bool allowImplicitConversion = false) { @@ -191,7 +202,11 @@ namespace ICSharpCode.Decompiler.CSharp conversion.Input.Type, type, targetType )) { - return this.UnwrapChild(cast.Expression); + var result = this.UnwrapChild(cast.Expression); + if (conversion.Conversion.IsUserDefined) { + result.Expression.AddAnnotation(new ImplicitConversionAnnotation(conversion)); + } + return result; } else if (Expression is ObjectCreateExpression oce && conversion.Conversion.IsMethodGroupConversion && oce.Arguments.Count == 1 && expressionBuilder.settings.UseImplicitMethodGroupConversion) { return this.UnwrapChild(oce.Arguments.Single()); @@ -211,6 +226,21 @@ namespace ICSharpCode.Decompiler.CSharp if (targetType.Kind == TypeKind.Unknown || targetType.Kind == TypeKind.Void || targetType.Kind == TypeKind.None) { return this; // don't attempt to insert cast to '?' or 'void' as these are not valid. } + var convAnnotation = this.Expression.Annotation(); + if (convAnnotation != null) { + // If an implicit user-defined conversion was stripped from this expression; + // it needs to be re-introduced before we can apply other casts to this expression. + // This happens when the CallBuilder discovers that the conversion is necessary in + // order to choose the correct overload. + this.Expression.RemoveAnnotations(); + return new CastExpression(expressionBuilder.ConvertType(convAnnotation.TargetType), Expression) + .WithoutILInstruction() + .WithRR(convAnnotation.ConversionResolveResult) + .ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion); + } + if (Expression is ThrowExpression && allowImplicitConversion) { + return this; // Throw expressions have no type and are implicitly convertible to any type + } if (Expression is TupleExpression tupleExpr && targetType is TupleType targetTupleType && tupleExpr.Elements.Count == targetTupleType.ElementTypes.Length) { @@ -231,8 +261,9 @@ namespace ICSharpCode.Decompiler.CSharp } var compilation = expressionBuilder.compilation; var conversions = Resolver.CSharpConversions.Get(compilation); - if (ResolveResult is ConversionResolveResult conv && Expression is CastExpression cast2 && - CastCanBeMadeImplicit(conversions, conv.Conversion, conv.Input.Type, type, targetType)) + if (ResolveResult is ConversionResolveResult conv && Expression is CastExpression cast2 + && !conv.Conversion.IsUserDefined + && CastCanBeMadeImplicit(conversions, conv.Conversion, conv.Input.Type, type, targetType)) { var unwrapped = this.UnwrapChild(cast2.Expression); if (allowImplicitConversion) @@ -359,7 +390,7 @@ namespace ICSharpCode.Decompiler.CSharp var convertedTemp = this.UnwrapChild(thisDir.Expression).ConvertTo(elementType, expressionBuilder, checkForOverflow); return new DirectionExpression(FieldDirection.Ref, convertedTemp) .WithILInstruction(this.ILInstructions) - .WithRR(new ByReferenceResolveResult(convertedTemp.ResolveResult, false)); + .WithRR(new ByReferenceResolveResult(convertedTemp.ResolveResult, ReferenceKind.Ref)); } // Convert from integer/pointer to reference. // First, convert to the corresponding pointer type: @@ -379,7 +410,7 @@ namespace ICSharpCode.Decompiler.CSharp // And then take a reference: return new DirectionExpression(FieldDirection.Ref, expr) .WithoutILInstruction() - .WithRR(new ByReferenceResolveResult(elementRR, false)); + .WithRR(new ByReferenceResolveResult(elementRR, ReferenceKind.Ref)); } var rr = expressionBuilder.resolver.WithCheckForOverflow(checkForOverflow).ResolveCast(targetType, ResolveResult); if (rr.IsCompileTimeConstant && !rr.IsError) { @@ -396,8 +427,15 @@ namespace ICSharpCode.Decompiler.CSharp .WithILInstruction(this.ILInstructions) .WithRR(new ConstantResolveResult(targetType, null)); } - if (allowImplicitConversion && conversions.ImplicitConversion(ResolveResult, targetType).IsValid) { - return this; + if (allowImplicitConversion) { + if (conversions.ImplicitConversion(ResolveResult, targetType).IsValid) { + return this; + } + } else { + if (targetType.Kind != TypeKind.Dynamic && type.Kind != TypeKind.Dynamic && NormalizeTypeVisitor.TypeErasure.EquivalentTypes(type, targetType)) { + // avoid an explicit cast when types differ only in nullability of reference types + return this; + } } var castExpr = new CastExpression(expressionBuilder.ConvertType(targetType), Expression); bool needsCheckAnnotation = targetUType.GetStackType().IsIntegerType(); diff --git a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs index d467ba883..a757e2921 100644 --- a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs @@ -35,6 +35,7 @@ using System.Reflection.Metadata; using static ICSharpCode.Decompiler.Metadata.DotNetCorePathFinderExtensions; using static ICSharpCode.Decompiler.Metadata.MetadataExtensions; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.Solution; namespace ICSharpCode.Decompiler.CSharp { @@ -78,6 +79,12 @@ namespace ICSharpCode.Decompiler.CSharp /// public Guid? ProjectGuid { get; set; } + /// + /// Path to the snk file to use for signing. + /// null to not sign. + /// + public string StrongNameKeyFile { get; set; } + public int MaxDegreeOfParallelism { get; set; } = Environment.ProcessorCount; #endregion @@ -101,7 +108,7 @@ namespace ICSharpCode.Decompiler.CSharp } } - public void DecompileProject(PEFile moduleDefinition, string targetDirectory, TextWriter projectFileWriter, CancellationToken cancellationToken = default(CancellationToken)) + public ProjectId DecompileProject(PEFile moduleDefinition, string targetDirectory, TextWriter projectFileWriter, CancellationToken cancellationToken = default(CancellationToken)) { if (string.IsNullOrEmpty(targetDirectory)) { throw new InvalidOperationException("Must set TargetDirectory"); @@ -110,7 +117,10 @@ namespace ICSharpCode.Decompiler.CSharp directories.Clear(); var files = WriteCodeFilesInProject(moduleDefinition, cancellationToken).ToList(); files.AddRange(WriteResourceFilesInProject(moduleDefinition)); - WriteProjectFile(projectFileWriter, files, moduleDefinition); + if (StrongNameKeyFile != null) { + File.Copy(StrongNameKeyFile, Path.Combine(targetDirectory, Path.GetFileName(StrongNameKeyFile))); + } + return WriteProjectFile(projectFileWriter, files, moduleDefinition); } enum LanguageTargets @@ -120,11 +130,12 @@ namespace ICSharpCode.Decompiler.CSharp } #region WriteProjectFile - void WriteProjectFile(TextWriter writer, IEnumerable> files, Metadata.PEFile module) + ProjectId WriteProjectFile(TextWriter writer, IEnumerable> files, Metadata.PEFile module) { const string ns = "http://schemas.microsoft.com/developer/msbuild/2003"; string platformName = GetPlatformName(module); Guid guid = this.ProjectGuid ?? Guid.NewGuid(); + using (XmlTextWriter w = new XmlTextWriter(writer)) { w.Formatting = Formatting.Indented; w.WriteStartDocument(); @@ -213,6 +224,11 @@ namespace ICSharpCode.Decompiler.CSharp } w.WriteElementString("WarningLevel", "4"); w.WriteElementString("AllowUnsafeBlocks", "True"); + + if (StrongNameKeyFile != null) { + w.WriteElementString("SignAssembly", "True"); + w.WriteElementString("AssemblyOriginatorKeyFile", Path.GetFileName(StrongNameKeyFile)); + } w.WriteEndElement(); // @@ -281,6 +297,8 @@ namespace ICSharpCode.Decompiler.CSharp w.WriteEndDocument(); } + + return new ProjectId(platformName, guid); } protected virtual bool IsGacAssembly(Metadata.IAssemblyReference r, Metadata.PEFile asm) diff --git a/ICSharpCode.Decompiler/DecompilerException.cs b/ICSharpCode.Decompiler/DecompilerException.cs index 04ef64d50..004da99ff 100644 --- a/ICSharpCode.Decompiler/DecompilerException.cs +++ b/ICSharpCode.Decompiler/DecompilerException.cs @@ -21,6 +21,7 @@ using System.Diagnostics; using System.IO; using System.Reflection; using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Security; @@ -44,7 +45,7 @@ namespace ICSharpCode.Decompiler public PEFile File { get; } public DecompilerException(MetadataModule module, IEntity decompiledEntity, Exception innerException, string message = null) - : base(message ?? "Error decompiling " + decompiledEntity?.FullName, innerException) + : base(message ?? GetDefaultMessage(decompiledEntity), innerException) { this.File = module.PEFile; this.Module = module; @@ -57,6 +58,13 @@ namespace ICSharpCode.Decompiler this.File = file; } + static string GetDefaultMessage(IEntity entity) + { + if (entity == null) + return "Error decompiling"; + return $"Error decompiling @{MetadataTokens.GetToken(entity.MetadataToken):X8} {entity.FullName}"; + } + // This constructor is needed for serialization. protected DecompilerException(SerializationInfo info, StreamingContext context) : base(info, context) { diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index 163d58346..f62d22969 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -88,6 +88,7 @@ namespace ICSharpCode.Decompiler } if (languageVersion < CSharp.LanguageVersion.CSharp7) { outVariables = false; + throwExpressions = false; tupleTypes = false; tupleConversions = false; discards = false; @@ -97,6 +98,7 @@ namespace ICSharpCode.Decompiler introduceReadonlyAndInModifiers = false; introduceRefModifiersOnStructs = false; nonTrailingNamedArguments = false; + refExtensionMethods = false; } if (languageVersion < CSharp.LanguageVersion.CSharp7_3) { introduceUnmanagedConstraint = false; @@ -114,10 +116,10 @@ namespace ICSharpCode.Decompiler return CSharp.LanguageVersion.CSharp8_0; if (introduceUnmanagedConstraint || tupleComparisons || stackAllocInitializers) return CSharp.LanguageVersion.CSharp7_3; - if (introduceRefModifiersOnStructs || introduceReadonlyAndInModifiers || nonTrailingNamedArguments) + if (introduceRefModifiersOnStructs || introduceReadonlyAndInModifiers || nonTrailingNamedArguments || refExtensionMethods) return CSharp.LanguageVersion.CSharp7_2; // C# 7.1 missing - if (outVariables || tupleTypes || tupleConversions || discards || localFunctions) + if (outVariables || throwExpressions || tupleTypes || tupleConversions || discards || localFunctions) return CSharp.LanguageVersion.CSharp7; if (awaitInCatchFinally || useExpressionBodyForCalculatedGetterOnlyProperties || nullPropagation || stringInterpolation || dictionaryInitializers || extensionMethodsInCollectionInitializers) @@ -626,6 +628,23 @@ namespace ICSharpCode.Decompiler } } + bool refExtensionMethods = true; + + /// + /// Gets/Sets whether to use C# 7.2 'ref' extension methods. + /// + [Category("C# 7.2 / VS 2017.4")] + [Description("DecompilerSettings.AllowExtensionMethodSyntaxOnRef")] + public bool RefExtensionMethods { + get { return refExtensionMethods; } + set { + if (refExtensionMethods != value) { + refExtensionMethods = value; + OnPropertyChanged(); + } + } + } + bool stringInterpolation = true; /// @@ -856,6 +875,23 @@ namespace ICSharpCode.Decompiler } } + bool throwExpressions = true; + + /// + /// Gets/Sets whether throw expressions should be used. + /// + [Category("C# 7.0 / VS 2017")] + [Description("DecompilerSettings.UseThrowExpressions")] + public bool ThrowExpressions { + get { return throwExpressions; } + set { + if (throwExpressions != value) { + throwExpressions = value; + OnPropertyChanged(); + } + } + } + bool tupleConversions = true; /// @@ -942,22 +978,19 @@ namespace ICSharpCode.Decompiler } } - bool localFunctions = false; + bool localFunctions = true; /// - /// Gets/Sets whether C# 7.0 local functions should be used. - /// Note: this language feature is currently not implemented and this setting is always false. + /// Gets/Sets whether C# 7.0 local functions should be transformed. /// [Category("C# 7.0 / VS 2017")] - [Description("DecompilerSettings.IntroduceLocalFunctionsNOTIMPLEMENTED")] - [Browsable(false)] + [Description("DecompilerSettings.IntroduceLocalFunctions")] public bool LocalFunctions { get { return localFunctions; } set { if (localFunctions != value) { - throw new NotImplementedException("C# 7.0 local functions are not implemented!"); - //localFunctions = value; - //OnPropertyChanged(); + localFunctions = value; + OnPropertyChanged(); } } } @@ -1062,6 +1095,20 @@ namespace ICSharpCode.Decompiler } } } + + bool removeDeadStores = false; + + [Category("DecompilerSettings.FSpecificOptions")] + [Description("DecompilerSettings.RemoveDeadStores")] + public bool RemoveDeadStores { + get { return removeDeadStores; } + set { + if (removeDeadStores != value) { + removeDeadStores = value; + OnPropertyChanged(); + } + } + } #endregion #region Assembly Load and Resolve options diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index ba1f1f985..2f9712599 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -310,7 +310,7 @@ namespace ICSharpCode.Decompiler.Disassembler } foreach (var p in methodDefinition.GetGenericParameters()) { - WriteGenericParameterAttributes(module, p); + WriteGenericParameterAttributes(module, genericContext, p); } foreach (var p in methodDefinition.GetParameters()) { WriteParameterAttributes(module, p); @@ -1005,15 +1005,28 @@ namespace ICSharpCode.Decompiler.Disassembler output.WriteLine(); } - void WriteGenericParameterAttributes(PEFile module, GenericParameterHandle handle) + void WriteGenericParameterAttributes(PEFile module, GenericContext context, GenericParameterHandle handle) { var metadata = module.Metadata; var p = metadata.GetGenericParameter(handle); - if (p.GetCustomAttributes().Count == 0) - return; - output.Write(".param type {0}", metadata.GetString(p.Name)); - output.WriteLine(); - WriteAttributes(module, p.GetCustomAttributes()); + if (p.GetCustomAttributes().Count > 0) { + output.Write(".param type {0}", metadata.GetString(p.Name)); + output.WriteLine(); + output.Indent(); + WriteAttributes(module, p.GetCustomAttributes()); + output.Unindent(); + } + foreach (var constraintHandle in p.GetConstraints()) { + var constraint = metadata.GetGenericParameterConstraint(constraintHandle); + if (constraint.GetCustomAttributes().Count > 0) { + output.Write(".param constraint {0}, ", metadata.GetString(p.Name)); + constraint.Type.WriteTo(module, output, context, ILNameSyntax.TypeName); + output.WriteLine(); + output.Indent(); + WriteAttributes(module, constraint.GetCustomAttributes()); + output.Unindent(); + } + } } void WriteParameterAttributes(PEFile module, ParameterHandle handle) @@ -1028,7 +1041,9 @@ namespace ICSharpCode.Decompiler.Disassembler WriteConstant(metadata, metadata.GetConstant(p.GetDefaultValue())); } output.WriteLine(); + output.Indent(); WriteAttributes(module, p.GetCustomAttributes()); + output.Unindent(); } void WriteConstant(MetadataReader metadata, Constant constant) @@ -1378,6 +1393,9 @@ namespace ICSharpCode.Decompiler.Disassembler isInType = true; WriteAttributes(module, typeDefinition.GetCustomAttributes()); WriteSecurityDeclarations(module, typeDefinition.GetDeclarativeSecurityAttributes()); + foreach (var tp in typeDefinition.GetGenericParameters()) { + WriteGenericParameterAttributes(module, genericContext, tp); + } var layout = typeDefinition.GetLayout(); if (!layout.IsDefault) { output.WriteLine(".pack {0}", layout.PackingSize); diff --git a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs index ebef49e54..a53fcfb79 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs @@ -222,6 +222,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis this.bottomState = initialState.Clone(); this.bottomState.ReplaceWithBottom(); Debug.Assert(bottomState.IsBottom); + this.stateOnNullableRewrap = bottomState.Clone(); this.currentStateOnException = state.Clone(); } @@ -254,7 +255,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis #endif [Conditional("DEBUG")] - void DebugStartPoint(ILInstruction inst) + protected void DebugStartPoint(ILInstruction inst) { #if DEBUG DebugPoint(debugInputState, inst); @@ -262,7 +263,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis } [Conditional("DEBUG")] - void DebugEndPoint(ILInstruction inst) + protected void DebugEndPoint(ILInstruction inst) { #if DEBUG DebugPoint(debugOutputState, inst); @@ -286,7 +287,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis foreach (var child in inst.Children) { child.AcceptVisitor(this); Debug.Assert(state.IsBottom || !child.HasFlag(InstructionFlags.EndPointUnreachable), - "Unreachable code must be in the bottom state."); + "Unreachable code must be in the bottom state."); } DebugEndPoint(inst); @@ -602,16 +603,110 @@ namespace ICSharpCode.Decompiler.FlowAnalysis protected internal override void VisitIfInstruction(IfInstruction inst) { DebugStartPoint(inst); - inst.Condition.AcceptVisitor(this); - State branchState = state.Clone(); + var (beforeThen, beforeElse) = EvaluateCondition(inst.Condition); + state = beforeThen; inst.TrueInst.AcceptVisitor(this); State afterTrueState = state; - state = branchState; + state = beforeElse; inst.FalseInst.AcceptVisitor(this); state.JoinWith(afterTrueState); DebugEndPoint(inst); } - + + /// + /// Evaluates the condition of an if. + /// + /// + /// A pair of: + /// * The state after the condition evaluates to true + /// * The state after the condition evaluates to false + /// + /// + /// this.state is invalid after this function was called, and must be overwritten + /// with one of the return values. + /// + (State OnTrue, State OnFalse) EvaluateCondition(ILInstruction inst) + { + if (inst is IfInstruction ifInst) { + // 'if (a?b:c)' or similar. + // This also includes conditions that are logic.not, logic.and, logic.or. + DebugStartPoint(ifInst); + var (beforeThen, beforeElse) = EvaluateCondition(ifInst.Condition); + state = beforeThen; + var (afterThenTrue, afterThenFalse) = EvaluateCondition(ifInst.TrueInst); + state = beforeElse; + var (afterElseTrue, afterElseFalse) = EvaluateCondition(ifInst.FalseInst); + + var onTrue = afterThenTrue; + onTrue.JoinWith(afterElseTrue); + var onFalse = afterThenFalse; + onFalse.JoinWith(afterElseFalse); + + DebugEndPoint(ifInst); + return (onTrue, onFalse); + } else if (inst is LdcI4 constant) { + if (constant.Value == 0) { + return (bottomState.Clone(), state); + } else { + return (state, bottomState.Clone()); + } + } else { + // other kind of condition + inst.AcceptVisitor(this); + return (state, state.Clone()); + } + } + + protected internal override void VisitNullCoalescingInstruction(NullCoalescingInstruction inst) + { + HandleBinaryWithOptionalEvaluation(inst, inst.ValueInst, inst.FallbackInst); + } + + protected internal override void VisitDynamicLogicOperatorInstruction(DynamicLogicOperatorInstruction inst) + { + HandleBinaryWithOptionalEvaluation(inst, inst.Left, inst.Right); + } + + protected internal override void VisitUserDefinedLogicOperator(UserDefinedLogicOperator inst) + { + HandleBinaryWithOptionalEvaluation(inst, inst.Left, inst.Right); + } + + void HandleBinaryWithOptionalEvaluation(ILInstruction parent, ILInstruction left, ILInstruction right) + { + DebugStartPoint(parent); + left.AcceptVisitor(this); + State branchState = state.Clone(); + right.AcceptVisitor(this); + state.JoinWith(branchState); + DebugEndPoint(parent); + } + + State stateOnNullableRewrap; + + protected internal override void VisitNullableRewrap(NullableRewrap inst) + { + DebugStartPoint(inst); + var oldState = stateOnNullableRewrap.Clone(); + stateOnNullableRewrap.ReplaceWithBottom(); + + inst.Argument.AcceptVisitor(this); + // Join incoming control flow from the NullableUnwraps. + state.JoinWith(stateOnNullableRewrap); + + stateOnNullableRewrap = oldState; + DebugEndPoint(inst); + } + + protected internal override void VisitNullableUnwrap(NullableUnwrap inst) + { + DebugStartPoint(inst); + inst.Argument.AcceptVisitor(this); + // Conditional control flow edge to the surrounding NullableRewrap. + stateOnNullableRewrap.JoinWith(state); + DebugEndPoint(inst); + } + protected internal override void VisitSwitchInstruction(SwitchInstruction inst) { DebugStartPoint(inst); @@ -635,6 +730,22 @@ namespace ICSharpCode.Decompiler.FlowAnalysis DebugEndPoint(inst); } + protected internal override void VisitUsingInstruction(UsingInstruction inst) + { + DebugStartPoint(inst); + inst.ResourceExpression.AcceptVisitor(this); + inst.Body.AcceptVisitor(this); + DebugEndPoint(inst); + } + + protected internal override void VisitLockInstruction(LockInstruction inst) + { + DebugStartPoint(inst); + inst.OnExpression.AcceptVisitor(this); + inst.Body.AcceptVisitor(this); + DebugEndPoint(inst); + } + protected internal override void VisitILFunction(ILFunction function) { throw new NotImplementedException(); diff --git a/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs b/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs index e05fbaccc..5b465b735 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs @@ -20,6 +20,9 @@ using System.Diagnostics; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Util; using System.Threading; +using System; +using System.Collections.Generic; +using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.FlowAnalysis { @@ -117,6 +120,9 @@ namespace ICSharpCode.Decompiler.FlowAnalysis readonly CancellationToken cancellationToken; readonly ILFunction scope; readonly BitSet variablesWithUninitializedUsage; + + readonly Dictionary stateOfLocalFunctionUse = new Dictionary(); + readonly HashSet localFunctionsNeedingAnalysis = new HashSet(); public DefiniteAssignmentVisitor(ILFunction scope, CancellationToken cancellationToken) { @@ -203,8 +209,46 @@ namespace ICSharpCode.Decompiler.FlowAnalysis HandleCall(inst); } + protected internal override void VisitILFunction(ILFunction inst) + { + DebugStartPoint(inst); + State stateBeforeFunction = state.Clone(); + State stateOnExceptionBeforeFunction = currentStateOnException.Clone(); + // Note: lambdas are handled at their point of declaration. + // We immediately visit their body, because captured variables need to be definitely initialized at this point. + // We ignore the state after the lambda body (by resetting to the state before), because we don't know + // when the lambda will be invoked. + // This also makes this logic unsuitable for reaching definitions, as we wouldn't see the effect of stores in lambdas. + // Only the simpler case of definite assignment can support lambdas. + inst.Body.AcceptVisitor(this); + + // For local functions, the situation is similar to lambdas. + // However, we don't use the state of the declaration site when visiting local functions, + // but instead the state(s) of their point of use. + // Because we might discover additional points of use within the local functions, + // we use a fixed-point iteration. + bool changed; + do { + changed = false; + foreach (var nestedFunction in inst.LocalFunctions) { + if (!localFunctionsNeedingAnalysis.Contains(nestedFunction.ReducedMethod)) + continue; + localFunctionsNeedingAnalysis.Remove(nestedFunction.ReducedMethod); + State stateOnEntry = stateOfLocalFunctionUse[nestedFunction.ReducedMethod]; + this.state.ReplaceWith(stateOnEntry); + this.currentStateOnException.ReplaceWith(stateOnEntry); + nestedFunction.AcceptVisitor(this); + changed = true; + } + } while (changed); + currentStateOnException = stateOnExceptionBeforeFunction; + state = stateBeforeFunction; + DebugEndPoint(inst); + } + void HandleCall(CallInstruction call) { + DebugStartPoint(call); bool hasOutArgs = false; foreach (var arg in call.Arguments) { if (arg.MatchLdLoca(out var v) && call.GetParameter(arg.ChildIndex)?.IsOut == true) { @@ -223,6 +267,34 @@ namespace ICSharpCode.Decompiler.FlowAnalysis } } } + HandleLocalFunctionUse(call.Method); + DebugEndPoint(call); + } + + /// + /// For a use of a local function, remember the current state to use as stateOnEntry when + /// later processing the local function body. + /// + void HandleLocalFunctionUse(IMethod method) + { + if (method.IsLocalFunction) { + if (stateOfLocalFunctionUse.TryGetValue(method, out var stateOnEntry)) { + if (!state.LessThanOrEqual(stateOnEntry)) { + stateOnEntry.JoinWith(state); + localFunctionsNeedingAnalysis.Add(method); + } + } else { + stateOfLocalFunctionUse.Add(method, state.Clone()); + localFunctionsNeedingAnalysis.Add(method); + } + } + } + + protected internal override void VisitLdFtn(LdFtn inst) + { + DebugStartPoint(inst); + HandleLocalFunctionUse(inst.Method); + DebugEndPoint(inst); } } } diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 88450d089..ff0318a04 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -16,9 +16,10 @@ false 7.2 - + true True ICSharpCode.Decompiler.snk + 1701;1702;1591;1573 @@ -61,6 +62,9 @@ + + + @@ -161,6 +165,7 @@ + @@ -222,8 +227,9 @@ - - + + + @@ -272,6 +278,7 @@ + @@ -374,6 +381,7 @@ + @@ -433,7 +441,6 @@ - diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template index 7f01f6e4a..314edbd73 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template @@ -24,5 +24,6 @@ + \ No newline at end of file diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs index f7366b383..fd4e3d4dd 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs @@ -128,7 +128,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow CleanDoFinallyBodies(function); context.Step("Translate fields to local accesses", function); - MarkGeneratedVariables(function); YieldReturnDecompiler.TranslateFieldsToLocalAccess(function, function, fieldToParameterMap); TranslateCachedFieldsToLocals(); @@ -355,28 +354,24 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow throw new SymbolicAnalysisFailedException(); if (blockContainer.EntryPoint.IncomingEdgeCount != 1) throw new SymbolicAnalysisFailedException(); + cachedStateVar = null; int pos = 0; - if (blockContainer.EntryPoint.Instructions[0].MatchStLoc(out cachedStateVar, out var cachedStateInit)) { - // stloc(cachedState, ldfld(valuetype StateMachineStruct::<>1__state, ldloc(this))) - if (!cachedStateInit.MatchLdFld(out var target, out var loadedField)) - throw new SymbolicAnalysisFailedException(); - if (!target.MatchLdThis()) - throw new SymbolicAnalysisFailedException(); - if (loadedField.MemberDefinition != stateField) - throw new SymbolicAnalysisFailedException(); - ++pos; - } while (blockContainer.EntryPoint.Instructions[pos] is StLoc stloc) { // stloc V_1(ldfld <>4__this(ldloc this)) - if (!stloc.Variable.IsSingleDefinition) - throw new SymbolicAnalysisFailedException(); if (!stloc.Value.MatchLdFld(out var target, out var field)) throw new SymbolicAnalysisFailedException(); if (!target.MatchLdThis()) throw new SymbolicAnalysisFailedException(); - if (!fieldToParameterMap.TryGetValue((IField)field.MemberDefinition, out var param)) + if (field.MemberDefinition == stateField && cachedStateVar == null) { + // stloc(cachedState, ldfld(valuetype StateMachineStruct::<>1__state, ldloc(this))) + cachedStateVar = stloc.Variable; + } else if (fieldToParameterMap.TryGetValue((IField)field.MemberDefinition, out var param)) { + if (!stloc.Variable.IsSingleDefinition) + throw new SymbolicAnalysisFailedException(); + cachedFieldToParameterMap[stloc.Variable] = param; + } else { throw new SymbolicAnalysisFailedException(); - cachedFieldToParameterMap[stloc.Variable] = param; + } pos++; } mainTryCatch = blockContainer.EntryPoint.Instructions[pos] as TryCatch; @@ -735,15 +730,21 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // if (call get_IsCompleted(ldloca awaiterVar)) br completedBlock if (!block.Instructions[block.Instructions.Count - 2].MatchIfInstruction(out var condition, out var trueInst)) return; - if (!MatchCall(condition, "get_IsCompleted", out var isCompletedArgs) || isCompletedArgs.Count != 1) - return; - if (!isCompletedArgs[0].MatchLdLocRef(awaiterVar)) - return; if (!trueInst.MatchBranch(out var completedBlock)) return; // br awaitBlock if (!block.Instructions.Last().MatchBranch(out var awaitBlock)) return; + // condition might be inverted, swap branches: + if (condition.MatchLogicNot(out var negatedCondition)) { + condition = negatedCondition; + ExtensionMethods.Swap(ref completedBlock, ref awaitBlock); + } + // continue matching call get_IsCompleted(ldloca awaiterVar) + if (!MatchCall(condition, "get_IsCompleted", out var isCompletedArgs) || isCompletedArgs.Count != 1) + return; + if (!isCompletedArgs[0].MatchLdLocRef(awaiterVar)) + return; // Check awaitBlock and resumeBlock: if (!awaitBlocks.TryGetValue(awaitBlock, out var awaitBlockData)) return; @@ -902,17 +903,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } #endregion - void MarkGeneratedVariables(ILFunction function) - { - // Variables after the awaiters are usually compiler-generated; - // so mark them as stack slots. - foreach (var v in function.Variables) { - if (v.Kind == VariableKind.Local && v.Index >= smallestAwaiterVarIndex) { - v.Kind = VariableKind.StackSlot; - } - } - } - /// /// Eliminates usage of doFinallyBodies /// diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs index f37cb1fed..bb7a120b1 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs @@ -354,7 +354,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow //assert then block terminates var trueExitInst = GetExit(ifInst.TrueInst); var exitInst = GetExit(block); - context.Step("Negate if for desired branch "+trueExitInst, ifInst); + context.Step($"InvertIf at IL_{ifInst.StartILOffset:x4}", ifInst); //if the then block terminates, else blocks are redundant, and should not exist Debug.Assert(IsEmpty(ifInst.FalseInst)); @@ -403,7 +403,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// /// if (cond) { if (nestedCond) { nestedThen... } } /// -> - /// if (cond && nestedCond) { nestedThen... } + /// if (cond && nestedCond) { nestedThen... } /// private void IntroduceShortCircuit(IfInstruction ifInst) { diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs index d06e00012..9fd519461 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs @@ -15,6 +15,7 @@ // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -45,6 +46,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow context.CancellationToken.ThrowIfCancellationRequested(); RemoveNopInstructions(block); + RemoveDeadStackStores(block, aggressive: context.Settings.RemoveDeadStores); InlineVariableInReturnBlock(block, context); // 1st pass SimplifySwitchInstruction before SimplifyBranchChains() @@ -68,6 +70,35 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow block.Instructions.RemoveAll(inst => inst.OpCode == OpCode.Nop); } + private void RemoveDeadStackStores(Block block, bool aggressive) + { + // 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) { + if (aggressive ? SemanticHelper.IsPure(stloc.Value.Flags) : IsSimple(stloc.Value)) { + Debug.Assert(SemanticHelper.IsPure(stloc.Value.Flags)); + block.Instructions.RemoveAt(i++); + } else { + stloc.Value.AddILRange(stloc); + stloc.ReplaceWith(stloc.Value); + } + } + } + + bool IsSimple(ILInstruction inst) + { + switch (inst.OpCode) { + case OpCode.LdLoc: + case OpCode.LdStr: // C# 1.0 compiler sometimes emits redundant ldstr in switch-on-string pattern + return true; + default: + return false; + } + } + } + void InlineVariableInReturnBlock(Block block, ILTransformContext context) { // In debug mode, the C#-compiler generates 'return blocks' that @@ -158,9 +189,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } // Remove return blocks that are no longer reachable: container.Blocks.RemoveAll(b => b.IncomingEdgeCount == 0 && b.Instructions.Count == 0); - if (context.Settings.RemoveDeadCode) { - container.SortBlocks(deleteUnreachableBlocks: true); - } } } diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs index b2ff0ef2d..049ad5bdc 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs @@ -504,26 +504,23 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // stloc nativeVar(conv o->i (ldloc pinnedVar)) // if (comp(ldloc nativeVar == conv i4->i (ldc.i4 0))) br targetBlock // br adjustOffsetToStringData - if (!body.EntryPoint.Instructions[0].MatchStLoc(out ILVariable nativeVar, out ILInstruction initInst)) - return; ILVariable newVar; - if (body.EntryPoint.Instructions.Count != 3) { + if (!body.EntryPoint.Instructions[0].MatchStLoc(out ILVariable nativeVar, out ILInstruction initInst)) { // potentially a special case with legacy csc and an unused pinned variable: - if (nativeVar.IsSingleDefinition && nativeVar.LoadCount == 0 && initInst.MatchLdLoc(pinnedRegion.Variable) - && pinnedRegion.Variable.LoadCount == 1) - { - // initInst is dead store - body.EntryPoint.Instructions.RemoveAt(0); + if (pinnedRegion.Variable.AddressCount == 0 && pinnedRegion.Variable.LoadCount == 0) { var charPtr = new PointerType(context.TypeSystem.FindType(KnownTypeCode.Char)); newVar = new ILVariable(VariableKind.PinnedLocal, charPtr, pinnedRegion.Variable.Index); newVar.Name = pinnedRegion.Variable.Name; newVar.HasGeneratedName = pinnedRegion.Variable.HasGeneratedName; - nativeVar.Function.Variables.Add(newVar); + pinnedRegion.Variable.Function.Variables.Add(newVar); pinnedRegion.Variable = newVar; pinnedRegion.Init = new ArrayToPointer(pinnedRegion.Init); } return; } + if (body.EntryPoint.Instructions.Count != 3) { + return; + } if (nativeVar.Type.GetStackType() != StackType.I) return; diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs index 8b096630f..0ae42262b 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs @@ -107,7 +107,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow IncludeNestedContainers(loop); // Try to extend the loop to reduce the number of exit points: ExtendLoop(h, loop, out var exitPoint); - IncludeUnreachablePredecessors(loop); // Sort blocks in the loop in reverse post-order to make the output look a bit nicer. // (if the loop doesn't contain nested loops, this is a topological sort) @@ -115,7 +114,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow Debug.Assert(loop[0] == h); foreach (var node in loop) { node.Visited = false; // reset visited flag so that we can find outer loops - Debug.Assert(h.Dominates(node) || !node.IsReachable, "The loop body must be dominated by the loop head"); + Debug.Assert(h.Dominates(node), "The loop body must be dominated by the loop head"); } ConstructLoop(loop, exitPoint); } @@ -150,7 +149,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // (the entry-point itself doesn't have a CFG node, because it's newly created by this transform) for (int i = 1; i < nestedContainer.Blocks.Count; i++) { var node = context.ControlFlowGraph.GetNode(nestedContainer.Blocks[i]); - Debug.Assert(loop[0].Dominates(node) || !node.IsReachable); + Debug.Assert(loop[0].Dominates(node)); if (!node.Visited) { node.Visited = true; loop.Add(node); @@ -258,6 +257,22 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow loop.Add(node); } } + // The loop/switch can only be entered through the entry point. + if (isSwitch) { + // In the case of a switch, false positives in the "continue;" detection logic + // can lead to falsely excludes some blocks from the body. + // Fix that by including all predecessors of included blocks. + Debug.Assert(loop[0] == loopHead); + for (int i = 1; i < loop.Count; i++) { + foreach (var p in loop[i].Predecessors) { + if (!p.Visited) { + p.Visited = true; + loop.Add(p); + } + } + } + } + Debug.Assert(loop.All(n => n == loopHead || n.Predecessors.All(p => p.Visited))); } else { // We are in case 2, but could not find a suitable exit point. // Heuristically try to minimize the number of exit points @@ -458,7 +473,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// or that leave the block Container. /// /// Entry point of the loop. - /// Whether to ignore branches that map to C# 'continue' statements. /// out: The number of different CFG nodes. /// Possible values: /// 0 = no CFG nodes used as exit nodes (although edges leaving the block container might still be exits); @@ -603,30 +617,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } #endregion - /// - /// While our normal dominance logic ensures the loop has just a single reachable entry point, - /// it's possible that there are unreachable code blocks that have jumps into the loop. - /// We'll also include those into the loop. - /// - /// Requires and maintains the invariant that a node is marked as visited iff it is contained in the loop. - /// - private void IncludeUnreachablePredecessors(List loop) - { - for (int i = 1; i < loop.Count; i++) { - Debug.Assert(loop[i].Visited); - foreach (var pred in loop[i].Predecessors) { - if (!pred.Visited) { - if (pred.IsReachable) { - Debug.Fail("All jumps into the loop body should go through the entry point"); - } else { - pred.Visited = true; - loop.Add(pred); - } - } - } - } - } - /// /// Move the blocks associated with the loop into a new block container. /// @@ -709,7 +699,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } exitPoint = null; } - IncludeUnreachablePredecessors(nodesInSwitch); context.Step("Create BlockContainer for switch", switchInst); // Sort blocks in the loop in reverse post-order to make the output look a bit nicer. @@ -718,7 +707,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow Debug.Assert(nodesInSwitch[0] == h); foreach (var node in nodesInSwitch) { node.Visited = false; // reset visited flag so that we can find outer loops - Debug.Assert(h.Dominates(node) || !node.IsReachable, "The switch body must be dominated by the switch head"); + Debug.Assert(h.Dominates(node), "The switch body must be dominated by the switch head"); } BlockContainer switchContainer = new BlockContainer(ContainerKind.Switch); diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs index cb50ec78f..bc9b39b77 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs @@ -28,10 +28,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// /// The variable to be used as the argument of the switch instruction. /// - public ILVariable SwitchVariable - { - get { return switchVar; } - } + public ILVariable SwitchVariable => switchVar; /// /// Whether at least one the analyzed blocks contained an IL switch constructors. @@ -62,6 +59,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow public Block RootBlock { get; private set; } + /// + /// Gets/sets whether to allow unreachable cases in switch instructions. + /// + public bool AllowUnreachableCases { get; set; } + /// /// Analyze the last two statements in the block and see if they can be turned into a /// switch instruction. @@ -180,6 +182,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } foreach (var section in inst.Sections) { var matchValues = section.Labels.AddOffset(offset).IntersectWith(inputValues); + if (!AllowUnreachableCases && matchValues.IsEmpty) + return false; if (matchValues.Count() > 1 && section.Body.MatchBranch(out var targetBlock) && AnalyzeBlock(targetBlock, matchValues)) { InnerBlocks.Add(targetBlock); } else { diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs index 6a4429ec4..08a1f185f 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs @@ -132,6 +132,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow { this.context = context; + analysis.AllowUnreachableCases = context.Settings.RemoveDeadCode; + foreach (var container in function.Descendants.OfType()) { currentContainer = container; controlFlowGraph = null; @@ -158,13 +160,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow void ProcessBlock(Block block, ref bool blockContainerNeedsCleanup) { bool analysisSuccess = analysis.AnalyzeBlock(block); - KeyValuePair defaultSection; - if (analysisSuccess && UseCSharpSwitch(out defaultSection)) { + if (analysisSuccess && UseCSharpSwitch(out _)) { // complex multi-block switch that can be combined into a single SwitchInstruction ILInstruction switchValue = new LdLoc(analysis.SwitchVariable); - if (switchValue.ResultType == StackType.Unknown) { + Debug.Assert(switchValue.ResultType.IsIntegerType() || switchValue.ResultType == StackType.Unknown); + if (!(switchValue.ResultType == StackType.I4 || switchValue.ResultType == StackType.I8)) { // switchValue must have a result type of either I4 or I8 - switchValue = new Conv(switchValue, PrimitiveType.I8, false, TypeSystem.Sign.Signed); + switchValue = new Conv(switchValue, PrimitiveType.I8, false, Sign.Signed); } var sw = new SwitchInstruction(switchValue); foreach (var section in analysis.Sections) { @@ -441,7 +443,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// s c /// /// where: - /// p|n: if (a && b) goto c; goto s; + /// p|n: if (a && b) goto c; goto s; /// /// Note that if n has only 1 successor, but is still a flow node, then a short circuit expression /// has a target (c) with no corresponding block (leave) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs index fc6f6dcc0..bcd9e047b 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs @@ -70,7 +70,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// Set in AnalyzeCurrentProperty() IField currentField; - /// The disposing field of the compiler-generated enumerator class./summary> + /// The disposing field of the compiler-generated enumerator class. /// Set in ConstructExceptionTable() for assembly compiled with Mono IField disposingField; @@ -142,7 +142,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow function.Body = newBody; // register any locals used in newBody function.Variables.AddRange(newBody.Descendants.OfType().Select(inst => inst.Variable).Distinct()); - function.CheckInvariant(ILPhase.Normal); PrintFinallyMethodStateRanges(newBody); @@ -164,6 +163,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // Note: because this only deletes blocks outright, the 'stateChanges' entries remain valid // (though some may point to now-deleted blocks) newBody.SortBlocks(deleteUnreachableBlocks: true); + function.CheckInvariant(ILPhase.Normal); if (!isCompiledWithMono) { DecompileFinallyBlocks(); @@ -338,7 +338,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow public static bool IsCompilerGeneratorEnumerator(TypeDefinitionHandle type, MetadataReader metadata) { TypeDefinition td; - if (type.IsNil || !type.IsCompilerGenerated(metadata) || (td = metadata.GetTypeDefinition(type)).GetDeclaringType().IsNil) + 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); @@ -390,7 +390,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow methodTypeParameters: null); var body = context.TypeSystem.MainModule.PEFile.Reader.GetMethodBody(methodDef.RelativeVirtualAddress); var il = context.CreateILReader() - .ReadIL(method, body, genericContext, context.CancellationToken); + .ReadIL(method, body, genericContext, ILFunctionKind.TopLevelFunction, context.CancellationToken); il.RunTransforms(CSharpDecompiler.EarlyILTransforms(true), new ILTransformContext(il, context.TypeSystem, context.DebugInfo, context.Settings) { CancellationToken = context.CancellationToken, @@ -810,14 +810,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow break; case Leave leave: if (leave.MatchReturn(out var value)) { + bool validYieldBreak = value.MatchLdcI4(0); if (value.MatchLdLoc(out var v) && (v.Kind == VariableKind.Local || v.Kind == VariableKind.StackSlot) - && v.StoreInstructions.Count == 1 - && v.StoreInstructions[0] is StLoc stloc) { - returnStores.Add(stloc); - value = stloc.Value; + && v.StoreInstructions.All(store => store is StLoc stloc && stloc.Value.MatchLdcI4(0))) + { + validYieldBreak = true; + returnStores.AddRange(v.StoreInstructions.Cast()); } - if (value.MatchLdcI4(0)) { + if (validYieldBreak) { // yield break leave.ReplaceWith(new Leave(newBody).WithILRange(leave)); } else { diff --git a/ICSharpCode.Decompiler/IL/ILInstructionExtensions.cs b/ICSharpCode.Decompiler/IL/ILInstructionExtensions.cs index 222d82daa..32310150c 100644 --- a/ICSharpCode.Decompiler/IL/ILInstructionExtensions.cs +++ b/ICSharpCode.Decompiler/IL/ILInstructionExtensions.cs @@ -18,5 +18,14 @@ namespace ICSharpCode.Decompiler.IL target.AddILRange(range); return target; } + + public static ILInstruction GetNextSibling(this ILInstruction instruction) + { + if (instruction?.Parent == null) + return null; + if (instruction.ChildIndex + 1 >= instruction.Parent.Children.Count) + return null; + return instruction.Parent.Children[instruction.ChildIndex + 1]; + } } } diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 4d98af2fd..730198abf 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -124,9 +124,10 @@ namespace ICSharpCode.Decompiler.IL EntityHandle ReadAndDecodeMetadataToken() { int token = reader.ReadInt32(); - if (token < 0) { + if (token <= 0) { // SRM uses negative tokens as "virtual tokens" and can get confused // if we manually create them. + // Row-IDs < 1 are always invalid. throw new BadImageFormatException("Invalid metadata token"); } return MetadataTokens.EntityHandle(token); @@ -480,21 +481,31 @@ namespace ICSharpCode.Decompiler.IL /// /// Decodes the specified method body and returns an ILFunction. /// - public ILFunction ReadIL(MethodDefinitionHandle method, MethodBodyBlock body, GenericContext genericContext = default, CancellationToken cancellationToken = default) + public ILFunction ReadIL(MethodDefinitionHandle method, MethodBodyBlock body, GenericContext genericContext = default, ILFunctionKind kind = ILFunctionKind.TopLevelFunction, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); Init(method, body, genericContext); ReadInstructions(cancellationToken); var blockBuilder = new BlockBuilder(body, variableByExceptionHandler); blockBuilder.CreateBlocks(mainContainer, instructionBuilder, isBranchTarget, cancellationToken); - var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer); + var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer, kind); CollectionExtensions.AddRange(function.Variables, parameterVariables); CollectionExtensions.AddRange(function.Variables, localVariables); CollectionExtensions.AddRange(function.Variables, stackVariables); CollectionExtensions.AddRange(function.Variables, variableByExceptionHandler.Values); function.AddRef(); // mark the root node + var removedBlocks = new List(); foreach (var c in function.Descendants.OfType()) { - c.SortBlocks(); + var newOrder = c.TopologicalSort(deleteUnreachableBlocks: true); + if (newOrder.Count < c.Blocks.Count) { + removedBlocks.AddRange(c.Blocks.Except(newOrder)); + } + c.Blocks.ReplaceList(newOrder); + } + if (removedBlocks.Count > 0) { + removedBlocks.SortBy(b => b.StartILOffset); + function.Warnings.Add("Discarded unreachable code: " + + string.Join(", ", removedBlocks.Select(b => $"IL_{b.StartILOffset:x4}"))); } function.Warnings.AddRange(Warnings); return function; @@ -1187,14 +1198,14 @@ namespace ICSharpCode.Decompiler.IL return Pop(StackType.O); case false: // field of value type: ldfld can handle temporaries - if (PeekStackType() == StackType.O) + if (PeekStackType() == StackType.O || PeekStackType() == StackType.Unknown) return new AddressOf(Pop()); else return PopPointer(); default: // field in unresolved type - if (PeekStackType() == StackType.O) - return Pop(StackType.O); + if (PeekStackType() == StackType.O || PeekStackType() == StackType.Unknown) + return Pop(); else return PopPointer(); } @@ -1401,12 +1412,17 @@ namespace ICSharpCode.Decompiler.IL var signatureHandle = (StandaloneSignatureHandle)ReadAndDecodeMetadataToken(); var signature = module.DecodeMethodSignature(signatureHandle, genericContext); var functionPointer = Pop(StackType.I); - Debug.Assert(!signature.Header.IsInstance); - var arguments = new ILInstruction[signature.ParameterTypes.Length]; + int firstArgument = signature.Header.IsInstance ? 1 : 0; + var arguments = new ILInstruction[firstArgument + signature.ParameterTypes.Length]; for (int i = signature.ParameterTypes.Length - 1; i >= 0; i--) { - arguments[i] = Pop(signature.ParameterTypes[i].GetStackType()); + arguments[firstArgument + i] = Pop(signature.ParameterTypes[i].GetStackType()); + } + if (firstArgument == 1) { + arguments[0] = Pop(); } var call = new CallIndirect( + signature.Header.IsInstance, + signature.Header.HasExplicitThis, signature.Header.CallingConvention, signature.ReturnType, signature.ParameterTypes, diff --git a/ICSharpCode.Decompiler/IL/ILVariable.cs b/ICSharpCode.Decompiler/IL/ILVariable.cs index a81f355e0..4d3fca31d 100644 --- a/ICSharpCode.Decompiler/IL/ILVariable.cs +++ b/ICSharpCode.Decompiler/IL/ILVariable.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Generic; using ICSharpCode.Decompiler.TypeSystem; using System.Diagnostics; +using System.Linq; namespace ICSharpCode.Decompiler.IL { @@ -74,6 +75,11 @@ namespace ICSharpCode.Decompiler.IL static class VariableKindExtensions { + public static bool IsThis(this ILVariable v) + { + return v.Kind == VariableKind.Parameter && v.Index < 0; + } + public static bool IsLocal(this VariableKind kind) { switch (kind) { @@ -102,8 +108,12 @@ namespace ICSharpCode.Decompiler.IL internal set { if (kind == VariableKind.Parameter) throw new InvalidOperationException("Kind=Parameter cannot be changed!"); - if (Index != null && value.IsLocal()) - Debug.Assert(kind.IsLocal()); + if (Index != null && value.IsLocal() && !kind.IsLocal()) { + // For variables, Index has different meaning than for stack slots, + // so we need to reset it to null. + // StackSlot -> ForeachLocal can happen sometimes (e.g. PST.TransformForeachOnArray) + Index = null; + } kind = value; } } @@ -133,7 +143,7 @@ namespace ICSharpCode.Decompiler.IL /// For ExceptionStackSlot, the index is the IL offset of the exception handler. /// For other kinds, the index has no meaning, and is usually null. /// - public readonly int? Index; + public int? Index { get; private set; } [Conditional("DEBUG")] internal void CheckInvariant() @@ -207,7 +217,7 @@ namespace ICSharpCode.Decompiler.IL /// This list is automatically updated when adding/removing ldloc instructions from the ILAst. /// public IReadOnlyList LoadInstructions => loadInstructions; - + /// /// Number of store instructions referencing this variable, /// plus 1 if HasInitialValue. @@ -410,7 +420,8 @@ namespace ICSharpCode.Decompiler.IL output.Write(" init"); } if (CaptureScope != null) { - output.Write(" captured in " + CaptureScope.EntryPoint.Label); + output.Write(" captured in "); + output.WriteLocalReference(CaptureScope.EntryPoint.Label, CaptureScope); } if (StateMachineField != null) { output.Write(" from state-machine"); diff --git a/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs b/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs index 3e3134d71..b246ac5b9 100644 --- a/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs +++ b/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs @@ -69,8 +69,10 @@ namespace ICSharpCode.Decompiler.IL public static void WriteTo(this EntityHandle entity, PEFile module, ITextOutput output, Metadata.GenericContext genericContext, ILNameSyntax syntax = ILNameSyntax.Signature) { - if (entity.IsNil) - throw new ArgumentNullException(nameof(entity)); + if (entity.IsNil) { + output.Write(""); + return; + } if (module == null) throw new ArgumentNullException(nameof(module)); var metadata = module.Metadata; @@ -133,14 +135,7 @@ namespace ICSharpCode.Decompiler.IL case HandleKind.MethodDefinition: { var md = metadata.GetMethodDefinition((MethodDefinitionHandle)entity); methodSignature = md.DecodeSignature(new DisassemblerSignatureProvider(module, output), new Metadata.GenericContext((MethodDefinitionHandle)entity, module)); - if (methodSignature.Header.HasExplicitThis) { - output.Write("instance explicit "); - } else if (methodSignature.Header.IsInstance) { - output.Write("instance "); - } - if (methodSignature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { - output.Write("vararg "); - } + WriteSignatureHeader(output, methodSignature); methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write(' '); var declaringType = md.GetDeclaringType(); @@ -189,13 +184,7 @@ namespace ICSharpCode.Decompiler.IL } output.Write('>'); } - output.Write("("); - for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) { - if (i > 0) - output.Write(", "); - methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); - } - output.Write(")"); + WriteParameterList(output, methodSignature); break; } case HandleKind.MemberReference: @@ -204,28 +193,13 @@ namespace ICSharpCode.Decompiler.IL switch (mr.GetKind()) { case MemberReferenceKind.Method: methodSignature = mr.DecodeMethodSignature(new DisassemblerSignatureProvider(module, output), genericContext); - if (methodSignature.Header.HasExplicitThis) { - output.Write("instance explicit "); - } else if (methodSignature.Header.IsInstance) { - output.Write("instance "); - } - if (methodSignature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { - output.Write("vararg "); - } + WriteSignatureHeader(output, methodSignature); methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write(' '); WriteParent(output, module, metadata, mr.Parent, genericContext, syntax); output.Write("::"); output.WriteReference(module, entity, DisassemblerHelpers.Escape(memberName)); - output.Write("("); - for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) { - if (i > 0) - output.Write(", "); - if (i == methodSignature.RequiredParameterCount) - output.Write("..., "); - methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); - } - output.Write(")"); + WriteParameterList(output, methodSignature); break; case MemberReferenceKind.Field: var fieldSignature = mr.DecodeFieldSignature(new DisassemblerSignatureProvider(module, output), genericContext); @@ -245,14 +219,7 @@ namespace ICSharpCode.Decompiler.IL var methodDefinition = metadata.GetMethodDefinition((MethodDefinitionHandle)ms.Method); var methodName = metadata.GetString(methodDefinition.Name); methodSignature = methodDefinition.DecodeSignature(new DisassemblerSignatureProvider(module, output), genericContext); - if (methodSignature.Header.HasExplicitThis) { - output.Write("instance explicit "); - } else if (methodSignature.Header.IsInstance) { - output.Write("instance "); - } - if (methodSignature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { - output.Write("vararg "); - } + WriteSignatureHeader(output, methodSignature); methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write(' '); var declaringType = methodDefinition.GetDeclaringType(); @@ -266,52 +233,21 @@ namespace ICSharpCode.Decompiler.IL } else { output.Write(DisassemblerHelpers.Escape(methodName)); } - output.Write('<'); - for (int i = 0; i < substitution.Length; i++) { - if (i > 0) - output.Write(", "); - substitution[i](syntax); - } - output.Write('>'); - output.Write("("); - for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) { - if (i > 0) - output.Write(", "); - methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); - } - output.Write(")"); + WriteTypeParameterList(output, syntax, substitution); + WriteParameterList(output, methodSignature); break; case HandleKind.MemberReference: var memberReference = metadata.GetMemberReference((MemberReferenceHandle)ms.Method); memberName = metadata.GetString(memberReference.Name); methodSignature = memberReference.DecodeMethodSignature(new DisassemblerSignatureProvider(module, output), genericContext); - if (methodSignature.Header.HasExplicitThis) { - output.Write("instance explicit "); - } else if (methodSignature.Header.IsInstance) { - output.Write("instance "); - } - if (methodSignature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { - output.Write("vararg "); - } + WriteSignatureHeader(output, methodSignature); methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write(' '); WriteParent(output, module, metadata, memberReference.Parent, genericContext, syntax); output.Write("::"); output.Write(DisassemblerHelpers.Escape(memberName)); - output.Write('<'); - for (int i = 0; i < substitution.Length; i++) { - if (i > 0) - output.Write(", "); - substitution[i](syntax); - } - output.Write('>'); - output.Write("("); - for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) { - if (i > 0) - output.Write(", "); - methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); - } - output.Write(")"); + WriteTypeParameterList(output, syntax, substitution); + WriteParameterList(output, methodSignature); break; } break; @@ -319,15 +255,10 @@ namespace ICSharpCode.Decompiler.IL var standaloneSig = metadata.GetStandaloneSignature((StandaloneSignatureHandle)entity); switch (standaloneSig.GetKind()) { case StandaloneSignatureKind.Method: - var methodSig = standaloneSig.DecodeMethodSignature(new DisassemblerSignatureProvider(module, output), genericContext); - methodSig.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); - output.Write('('); - for (int i = 0; i < methodSig.ParameterTypes.Length; i++) { - if (i > 0) - output.Write(", "); - methodSig.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); - } - output.Write(')'); + methodSignature = standaloneSig.DecodeMethodSignature(new DisassemblerSignatureProvider(module, output), genericContext); + WriteSignatureHeader(output, methodSignature); + methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); + WriteParameterList(output, methodSignature); break; case StandaloneSignatureKind.LocalVariables: default: @@ -341,6 +272,56 @@ namespace ICSharpCode.Decompiler.IL } } + static void WriteTypeParameterList(ITextOutput output, ILNameSyntax syntax, System.Collections.Immutable.ImmutableArray> substitution) + { + output.Write('<'); + for (int i = 0; i < substitution.Length; i++) { + if (i > 0) + output.Write(", "); + substitution[i](syntax); + } + output.Write('>'); + } + + static void WriteParameterList(ITextOutput output, MethodSignature> methodSignature) + { + output.Write("("); + for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) { + if (i > 0) + output.Write(", "); + if (i == methodSignature.RequiredParameterCount) + output.Write("..., "); + methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); + } + output.Write(")"); + } + + static void WriteSignatureHeader(ITextOutput output, MethodSignature> methodSignature) + { + if (methodSignature.Header.HasExplicitThis) { + output.Write("instance explicit "); + } else if (methodSignature.Header.IsInstance) { + output.Write("instance "); + } + switch (methodSignature.Header.CallingConvention) { + case SignatureCallingConvention.CDecl: + output.Write("unmanaged cdecl "); + break; + case SignatureCallingConvention.StdCall: + output.Write("unmanaged stdcall "); + break; + case SignatureCallingConvention.ThisCall: + output.Write("unmanaged thiscall "); + break; + case SignatureCallingConvention.FastCall: + output.Write("unmanaged fastcall "); + break; + case SignatureCallingConvention.VarArgs: + output.Write("vararg "); + break; + } + } + static void WriteParent(ITextOutput output, PEFile module, MetadataReader metadata, EntityHandle parentHandle, Metadata.GenericContext genericContext, ILNameSyntax syntax) { switch (parentHandle.Kind) { diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index 27e0af93d..67d736150 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -115,7 +115,7 @@ namespace ICSharpCode.Decompiler.IL /// In case 3 (managed reference), the dereferenced value is the input being tested, and the nullable.unwrap instruction returns the managed reference unmodified (if the value is non-null). NullableUnwrap, /// Serves as jump target for the nullable.unwrap instruction. - /// If the input evaluates normally, evaluates to the input value (wrapped in Nullable if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null. + /// If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null. NullableRewrap, /// Loads a constant string. LdStr, @@ -135,6 +135,8 @@ namespace ICSharpCode.Decompiler.IL LdFtn, /// Load method pointer LdVirtFtn, + /// Virtual delegate construction + LdVirtDelegate, /// Loads runtime representation of metadata token LdTypeToken, /// Loads runtime representation of metadata token @@ -189,7 +191,7 @@ namespace ICSharpCode.Decompiler.IL StringToInt, /// ILAst representation of Expression.Convert. ExpressionTreeCast, - /// Use of user-defined && or || operator. + /// Use of user-defined && or || operator. UserDefinedLogicOperator, /// ILAst representation of a short-circuiting binary operator inside a dynamic expression. DynamicLogicOperatorInstruction, @@ -476,7 +478,7 @@ namespace ICSharpCode.Decompiler.IL { switch (index) { default: - this.Arguments[index - 0] = value; + this.Arguments[index - 0] = (ILInstruction)value; break; } } @@ -491,7 +493,7 @@ namespace ICSharpCode.Decompiler.IL { var clone = (CallInstruction)ShallowClone(); clone.Arguments = new InstructionCollection(clone, 0); - clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone())); + clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone())); return clone; } protected override InstructionFlags ComputeFlags() @@ -742,9 +744,11 @@ namespace ICSharpCode.Decompiler.IL SetChildInstruction(ref this.body, value, 0); } } + public static readonly SlotInfo LocalFunctionsSlot = new SlotInfo("LocalFunctions"); + public InstructionCollection LocalFunctions { get; private set; } protected sealed override int GetChildCount() { - return 1; + return 1 + LocalFunctions.Count; } protected sealed override ILInstruction GetChild(int index) { @@ -752,7 +756,7 @@ namespace ICSharpCode.Decompiler.IL case 0: return this.body; default: - throw new IndexOutOfRangeException(); + return this.LocalFunctions[index - 1]; } } protected sealed override void SetChild(int index, ILInstruction value) @@ -762,7 +766,8 @@ namespace ICSharpCode.Decompiler.IL this.Body = value; break; default: - throw new IndexOutOfRangeException(); + this.LocalFunctions[index - 1] = (ILFunction)value; + break; } } protected sealed override SlotInfo GetChildSlot(int index) @@ -771,17 +776,19 @@ namespace ICSharpCode.Decompiler.IL case 0: return BodySlot; default: - throw new IndexOutOfRangeException(); + return LocalFunctionsSlot; } } public sealed override ILInstruction Clone() { var clone = (ILFunction)ShallowClone(); clone.Body = this.body.Clone(); + clone.LocalFunctions = new InstructionCollection(clone, 1); + clone.LocalFunctions.AddRange(this.LocalFunctions.Select(arg => (ILFunction)arg.Clone())); clone.CloneVariables(); return clone; } - public override StackType ResultType { get { return StackType.O; } } + public override StackType ResultType { get { return DelegateType?.GetStackType() ?? StackType.O; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitILFunction(this); @@ -797,7 +804,7 @@ namespace ICSharpCode.Decompiler.IL protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) { var o = other as ILFunction; - return o != null && this.body.PerformMatch(o.body, ref match); + return o != null && this.body.PerformMatch(o.body, ref match) && Patterns.ListMatch.DoMatch(this.LocalFunctions, o.LocalFunctions, ref match); } } } @@ -1058,7 +1065,7 @@ namespace ICSharpCode.Decompiler.IL protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) { var o = other as NumericCompoundAssign; - return o != null && type.Equals(o.type) && CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); + return o != null && type.Equals(o.type) && CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && this.EvalMode == o.EvalMode && this.TargetKind == o.TargetKind && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); } } } @@ -1092,7 +1099,7 @@ namespace ICSharpCode.Decompiler.IL protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) { var o = other as UserDefinedCompoundAssign; - return o != null && this.Method.Equals(o.Method) && this.CompoundAssignmentType == o.CompoundAssignmentType && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); + return o != null && this.Method.Equals(o.Method) && this.EvalMode == o.EvalMode && this.TargetKind == o.TargetKind && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); } } } @@ -1126,7 +1133,7 @@ namespace ICSharpCode.Decompiler.IL protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) { var o = other as DynamicCompoundAssign; - return o != null && this.CompoundAssignmentType == o.CompoundAssignmentType && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); + return o != null && this.EvalMode == o.EvalMode && this.TargetKind == o.TargetKind && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); } } } @@ -2709,7 +2716,7 @@ namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL { /// Serves as jump target for the nullable.unwrap instruction. - /// If the input evaluates normally, evaluates to the input value (wrapped in Nullable if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null. + /// If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null. public sealed partial class NullableRewrap : UnaryInstruction { public NullableRewrap(ILInstruction argument) : base(OpCode.NullableRewrap, argument) @@ -3076,6 +3083,66 @@ namespace ICSharpCode.Decompiler.IL } } namespace ICSharpCode.Decompiler.IL +{ + /// Virtual delegate construction + public sealed partial class LdVirtDelegate : UnaryInstruction, IInstructionWithMethodOperand + { + public LdVirtDelegate(ILInstruction argument, IType type, IMethod method) : base(OpCode.LdVirtDelegate, argument) + { + this.type = type; + this.method = method; + } + IType type; + /// Returns the type operand. + public IType Type { + get { return type; } + set { type = value; InvalidateFlags(); } + } + readonly IMethod method; + /// Returns the method operand. + public IMethod Method { get { return method; } } + public override StackType ResultType { get { return StackType.O; } } + protected override InstructionFlags ComputeFlags() + { + return base.ComputeFlags() | InstructionFlags.MayThrow; + } + public override InstructionFlags DirectFlags { + get { + return base.DirectFlags | InstructionFlags.MayThrow; + } + } + public override void WriteTo(ITextOutput output, ILAstWritingOptions options) + { + WriteILRange(output, options); + output.Write(OpCode); + output.Write(' '); + type.WriteTo(output); + output.Write(' '); + method.WriteTo(output); + output.Write('('); + Argument.WriteTo(output, options); + output.Write(')'); + } + public override void AcceptVisitor(ILVisitor visitor) + { + visitor.VisitLdVirtDelegate(this); + } + public override T AcceptVisitor(ILVisitor visitor) + { + return visitor.VisitLdVirtDelegate(this); + } + public override T AcceptVisitor(ILVisitor visitor, C context) + { + return visitor.VisitLdVirtDelegate(this, context); + } + protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) + { + var o = other as LdVirtDelegate; + return o != null && this.Argument.PerformMatch(o.Argument, ref match) && type.Equals(o.type) && method.Equals(o.method); + } + } +} +namespace ICSharpCode.Decompiler.IL { /// Loads runtime representation of metadata token public sealed partial class LdTypeToken : SimpleInstruction @@ -4254,7 +4321,7 @@ namespace ICSharpCode.Decompiler.IL { switch (index) { default: - this.Indices[index - 0] = value; + this.Indices[index - 0] = (ILInstruction)value; break; } } @@ -4269,7 +4336,7 @@ namespace ICSharpCode.Decompiler.IL { var clone = (NewArr)ShallowClone(); clone.Indices = new InstructionCollection(clone, 0); - clone.Indices.AddRange(this.Indices.Select(arg => arg.Clone())); + clone.Indices.AddRange(this.Indices.Select(arg => (ILInstruction)arg.Clone())); return clone; } public override StackType ResultType { get { return StackType.O; } } @@ -4365,7 +4432,7 @@ namespace ICSharpCode.Decompiler.IL public Throw(ILInstruction argument) : base(OpCode.Throw, argument) { } - public override StackType ResultType { get { return StackType.Void; } } + public override StackType ResultType { get { return this.resultType; } } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable; @@ -4607,7 +4674,7 @@ namespace ICSharpCode.Decompiler.IL this.Array = value; break; default: - this.Indices[index - 1] = value; + this.Indices[index - 1] = (ILInstruction)value; break; } } @@ -4625,7 +4692,7 @@ namespace ICSharpCode.Decompiler.IL var clone = (LdElema)ShallowClone(); clone.Array = this.array.Clone(); clone.Indices = new InstructionCollection(clone, 1); - clone.Indices.AddRange(this.Indices.Select(arg => arg.Clone())); + clone.Indices.AddRange(this.Indices.Select(arg => (ILInstruction)arg.Clone())); return clone; } public bool DelayExceptions; // NullReferenceException/IndexOutOfBoundsException only occurs when the reference is dereferenced @@ -4905,7 +4972,7 @@ namespace ICSharpCode.Decompiler.IL } namespace ICSharpCode.Decompiler.IL { - /// Use of user-defined && or || operator. + /// Use of user-defined && or || operator. public sealed partial class UserDefinedLogicOperator : ILInstruction, IInstructionWithMethodOperand { public UserDefinedLogicOperator(IMethod method, ILInstruction left, ILInstruction right) : base(OpCode.UserDefinedLogicOperator) @@ -5578,7 +5645,7 @@ namespace ICSharpCode.Decompiler.IL { switch (index) { default: - this.Arguments[index - 0] = value; + this.Arguments[index - 0] = (ILInstruction)value; break; } } @@ -5593,7 +5660,7 @@ namespace ICSharpCode.Decompiler.IL { var clone = (DynamicGetIndexInstruction)ShallowClone(); clone.Arguments = new InstructionCollection(clone, 0); - clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone())); + clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone())); return clone; } protected override InstructionFlags ComputeFlags() @@ -5646,7 +5713,7 @@ namespace ICSharpCode.Decompiler.IL { switch (index) { default: - this.Arguments[index - 0] = value; + this.Arguments[index - 0] = (ILInstruction)value; break; } } @@ -5661,7 +5728,7 @@ namespace ICSharpCode.Decompiler.IL { var clone = (DynamicSetIndexInstruction)ShallowClone(); clone.Arguments = new InstructionCollection(clone, 0); - clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone())); + clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone())); return clone; } protected override InstructionFlags ComputeFlags() @@ -5714,7 +5781,7 @@ namespace ICSharpCode.Decompiler.IL { switch (index) { default: - this.Arguments[index - 0] = value; + this.Arguments[index - 0] = (ILInstruction)value; break; } } @@ -5729,7 +5796,7 @@ namespace ICSharpCode.Decompiler.IL { var clone = (DynamicInvokeMemberInstruction)ShallowClone(); clone.Arguments = new InstructionCollection(clone, 0); - clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone())); + clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone())); return clone; } protected override InstructionFlags ComputeFlags() @@ -5782,7 +5849,7 @@ namespace ICSharpCode.Decompiler.IL { switch (index) { default: - this.Arguments[index - 0] = value; + this.Arguments[index - 0] = (ILInstruction)value; break; } } @@ -5797,7 +5864,7 @@ namespace ICSharpCode.Decompiler.IL { var clone = (DynamicInvokeConstructorInstruction)ShallowClone(); clone.Arguments = new InstructionCollection(clone, 0); - clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone())); + clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone())); return clone; } protected override InstructionFlags ComputeFlags() @@ -5850,7 +5917,7 @@ namespace ICSharpCode.Decompiler.IL { switch (index) { default: - this.Arguments[index - 0] = value; + this.Arguments[index - 0] = (ILInstruction)value; break; } } @@ -5865,7 +5932,7 @@ namespace ICSharpCode.Decompiler.IL { var clone = (DynamicInvokeInstruction)ShallowClone(); clone.Arguments = new InstructionCollection(clone, 0); - clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone())); + clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone())); return clone; } protected override InstructionFlags ComputeFlags() @@ -6545,6 +6612,10 @@ namespace ICSharpCode.Decompiler.IL { Default(inst); } + protected internal virtual void VisitLdVirtDelegate(LdVirtDelegate inst) + { + Default(inst); + } protected internal virtual void VisitLdTypeToken(LdTypeToken inst) { Default(inst); @@ -6927,6 +6998,10 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst); } + protected internal virtual T VisitLdVirtDelegate(LdVirtDelegate inst) + { + return Default(inst); + } protected internal virtual T VisitLdTypeToken(LdTypeToken inst) { return Default(inst); @@ -7309,6 +7384,10 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst, context); } + protected internal virtual T VisitLdVirtDelegate(LdVirtDelegate inst, C context) + { + return Default(inst, context); + } protected internal virtual T VisitLdTypeToken(LdTypeToken inst, C context) { return Default(inst, context); @@ -7539,6 +7618,7 @@ namespace ICSharpCode.Decompiler.IL "ldnull", "ldftn", "ldvirtftn", + "ldvirtdelegate", "ldtypetoken", "ldmembertoken", "localloc", @@ -7859,6 +7939,20 @@ namespace ICSharpCode.Decompiler.IL method = default(IMethod); return false; } + public bool MatchLdVirtDelegate(out ILInstruction argument, out IType type, out IMethod method) + { + var inst = this as LdVirtDelegate; + if (inst != null) { + argument = inst.Argument; + type = inst.Type; + method = inst.Method; + return true; + } + argument = default(ILInstruction); + type = default(IType); + method = default(IMethod); + return false; + } public bool MatchLdTypeToken(out IType type) { var inst = this as LdTypeToken; diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index 4d63cc974..97add0105 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -49,8 +49,9 @@ VoidResult, NoArguments, CustomWriteTo), new OpCode("ILFunction", "A container of IL blocks.", CustomChildren(new [] { - new ChildInfo("body") - }), CustomConstructor, CustomWriteTo, CustomComputeFlags, CustomVariableName("function"), ResultType("O") + new ChildInfo("body"), + new ChildInfo("localFunctions") { IsCollection = true, Type = "ILFunction" } + }), CustomConstructor, CustomWriteTo, CustomComputeFlags, CustomVariableName("function"), ResultType("DelegateType?.GetStackType() ?? StackType.O") ), new OpCode("BlockContainer", "A container of IL blocks.", ResultType("this.ExpectedResultType"), CustomConstructor, CustomVariableName("container"), @@ -74,19 +75,23 @@ CustomClassName("NumericCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), CustomConstructor, CustomComputeFlags, MayThrow, HasTypeOperand, ResultType("type.GetStackType()"), CustomWriteTo, MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator"), + MatchCondition("this.EvalMode == o.EvalMode"), + MatchCondition("this.TargetKind == o.TargetKind"), MatchCondition("Target.PerformMatch(o.Target, ref match)"), MatchCondition("Value.PerformMatch(o.Value, ref match)")), new OpCode("user.compound", "Common instruction for user-defined compound assignments.", CustomClassName("UserDefinedCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), CustomConstructor, MayThrow, SideEffect, CustomWriteTo, MatchCondition("this.Method.Equals(o.Method)"), - MatchCondition("this.CompoundAssignmentType == o.CompoundAssignmentType"), + MatchCondition("this.EvalMode == o.EvalMode"), + MatchCondition("this.TargetKind == o.TargetKind"), MatchCondition("Target.PerformMatch(o.Target, ref match)"), MatchCondition("Value.PerformMatch(o.Value, ref match)")), new OpCode("dynamic.compound", "Common instruction for dynamic compound assignments.", CustomClassName("DynamicCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), MayThrow, SideEffect, CustomWriteTo, CustomConstructor, ResultType("O"), - MatchCondition("this.CompoundAssignmentType == o.CompoundAssignmentType"), + MatchCondition("this.EvalMode == o.EvalMode"), + MatchCondition("this.TargetKind == o.TargetKind"), MatchCondition("Target.PerformMatch(o.Target, ref match)"), MatchCondition("Value.PerformMatch(o.Value, ref match)")), new OpCode("bit.not", "Bitwise NOT", Unary, CustomConstructor, MatchCondition("IsLifted == o.IsLifted && UnderlyingResultType == o.UnderlyingResultType")), @@ -189,7 +194,7 @@ + "returns the managed reference unmodified (if the value is non-null).", Unary, CustomConstructor, CustomWriteTo, HasFlag("InstructionFlags.MayUnwrapNull")), new OpCode("nullable.rewrap", "Serves as jump target for the nullable.unwrap instruction." + Environment.NewLine - + "If the input evaluates normally, evaluates to the input value (wrapped in Nullable if the input is a non-nullable value type)." + + "If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type)." + "If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction," + "the nullable.rewrap instruction evaluates to null.", Unary, CustomComputeFlags), @@ -212,6 +217,9 @@ CustomClassName("LdFtn"), NoArguments, HasMethodOperand, ResultType("I")), new OpCode("ldvirtftn", "Load method pointer", CustomClassName("LdVirtFtn"), Unary, HasMethodOperand, MayThrow, ResultType("I")), + new OpCode("ldvirtdelegate", "Virtual delegate construction", + CustomClassName("LdVirtDelegate"), Unary, HasTypeOperand, HasMethodOperand, + MayThrow, ResultType("O")), new OpCode("ldtypetoken", "Loads runtime representation of metadata token", CustomClassName("LdTypeToken"), NoArguments, HasTypeOperand, ResultType("O")), new OpCode("ldmembertoken", "Loads runtime representation of metadata token", @@ -260,7 +268,7 @@ new OpCode("default.value", "Returns the default value for a type.", NoArguments, HasTypeOperand, ResultType("type.GetStackType()")), new OpCode("throw", "Throws an exception.", - Unary, MayThrow, UnconditionalBranch), + Unary, MayThrow, HasFlag("InstructionFlags.EndPointUnreachable"), ResultType("this.resultType")), new OpCode("rethrow", "Rethrows the current exception.", NoArguments, MayThrow, UnconditionalBranch), new OpCode("sizeof", "Gets the size of a type in bytes.", @@ -281,7 +289,7 @@ CustomClassName("ExpressionTreeCast"), Unary, HasTypeOperand, MayThrow, CustomConstructor, CustomWriteTo, ResultType("type.GetStackType()"), MatchCondition("this.IsChecked == o.IsChecked")), - new OpCode("user.logic.operator", "Use of user-defined && or || operator.", + new OpCode("user.logic.operator", "Use of user-defined && or || operator.", CustomClassName("UserDefinedLogicOperator"), HasMethodOperand, ResultType("O"), CustomChildren(new []{ @@ -769,6 +777,7 @@ namespace ICSharpCode.Decompiler.IL public readonly string SlotName; public bool IsCollection; + public string Type = "ILInstruction"; public bool CanInlineInto; public string[] ExpectedTypes; @@ -814,7 +823,7 @@ namespace ICSharpCode.Decompiler.IL childCount = children.Length - 1; opCode.Flags.Add(argProp + ".Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags)"); opCode.ConstructorParameters.Add("params ILInstruction[] " + arg); - opCode.ConstructorBody.Add("this." + argProp + " = new InstructionCollection(this, " + i + ");"); + opCode.ConstructorBody.Add("this." + argProp + " = new InstructionCollection<" + children[i].Type + ">(this, " + i + ");"); opCode.ConstructorBody.Add("this." + argProp + ".AddRange(" + arg + ");"); opCode.PerformMatchConditions.Add("Patterns.ListMatch.DoMatch(this." + argProp + ", o." + argProp + ", ref match)"); if (i == 0) @@ -827,7 +836,7 @@ namespace ICSharpCode.Decompiler.IL opCode.WriteArguments.Add("\t" + arg + ".WriteTo(output, options);"); opCode.WriteArguments.Add("}"); opCode.Members.Add("public static readonly SlotInfo " + children[i].SlotName + " = " + children[i].GetSlotInit() + ";"); - opCode.Members.Add("public InstructionCollection " + argProp + " { get; private set; }"); + opCode.Members.Add("public InstructionCollection<" + children[i].Type + "> " + argProp + " { get; private set; }"); } else { opCode.Flags.Add(arg + ".Flags"); opCode.ConstructorParameters.Add("ILInstruction " + arg); @@ -906,7 +915,7 @@ namespace ICSharpCode.Decompiler.IL if (collection == null) b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();"); else { - b.AppendLine("\t\t\tthis." + collection.PropertyName + "[index - " + childCount + "] = value;"); + b.AppendLine("\t\t\tthis." + collection.PropertyName + "[index - " + childCount + "] = (" + collection.Type + ")value;"); b.AppendLine("\t\t\tbreak;"); } b.AppendLine("\t}"); @@ -936,8 +945,8 @@ namespace ICSharpCode.Decompiler.IL b.AppendLine("\tvar clone = (" + opCode.Name + ")ShallowClone();"); for (int i = 0; i < children.Length; i++) { if (children[i].IsCollection) { - b.AppendLine("\tclone." + children[i].PropertyName + " = new InstructionCollection(clone, " + i + ");"); - b.AppendLine("\tclone." + children[i].PropertyName + ".AddRange(this." + children[i].PropertyName + ".Select(arg => arg.Clone()));"); + b.AppendLine("\tclone." + children[i].PropertyName + " = new InstructionCollection<" + children[i].Type + ">(clone, " + i + ");"); + b.AppendLine("\tclone." + children[i].PropertyName + ".AddRange(this." + children[i].PropertyName + ".Select(arg => (" + children[i].Type + ")arg.Clone()));"); } else { b.AppendLine("\tclone." + children[i].PropertyName + " = this." + children[i].Name + ".Clone();"); } diff --git a/ICSharpCode.Decompiler/IL/Instructions/Block.cs b/ICSharpCode.Decompiler/IL/Instructions/Block.cs index ab04f550e..20b9da814 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Block.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Block.cs @@ -43,11 +43,11 @@ namespace ICSharpCode.Decompiler.IL { public static readonly SlotInfo InstructionSlot = new SlotInfo("Instruction", isCollection: true); public static readonly SlotInfo FinalInstructionSlot = new SlotInfo("FinalInstruction"); - + public readonly BlockKind Kind; public readonly InstructionCollection Instructions; ILInstruction finalInstruction; - + /// /// For blocks in a block container, this field holds /// the number of incoming control flow edges to this block. @@ -77,21 +77,21 @@ namespace ICSharpCode.Decompiler.IL SetChildInstruction(ref finalInstruction, value, Instructions.Count); } } - + protected internal override void InstructionCollectionUpdateComplete() { base.InstructionCollectionUpdateComplete(); if (finalInstruction.Parent == this) finalInstruction.ChildIndex = Instructions.Count; } - + public Block(BlockKind kind = BlockKind.ControlFlow) : base(OpCode.Block) { this.Kind = kind; this.Instructions = new InstructionCollection(this, 0); this.FinalInstruction = new Nop(); } - + public override ILInstruction Clone() { Block clone = new Block(Kind); @@ -100,7 +100,7 @@ namespace ICSharpCode.Decompiler.IL clone.FinalInstruction = this.FinalInstruction.Clone(); return clone; } - + internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); @@ -133,18 +133,17 @@ namespace ICSharpCode.Decompiler.IL break; } } - + public override StackType ResultType { get { return finalInstruction.ResultType; } } - + /// /// Gets the name of this block. /// - public string Label - { + public string Label { get { return Disassembler.DisassemblerHelpers.OffsetToString(this.StartILOffset); } } @@ -179,19 +178,19 @@ namespace ICSharpCode.Decompiler.IL output.Write("}"); output.MarkFoldEnd(); } - + protected override int GetChildCount() { return Instructions.Count + 1; } - + protected override ILInstruction GetChild(int index) { if (index == Instructions.Count) return finalInstruction; return Instructions[index]; } - + protected override void SetChild(int index, ILInstruction value) { if (index == Instructions.Count) @@ -199,7 +198,7 @@ namespace ICSharpCode.Decompiler.IL else Instructions[index] = value; } - + protected override SlotInfo GetChildSlot(int index) { if (index == Instructions.Count) @@ -207,7 +206,7 @@ namespace ICSharpCode.Decompiler.IL else return InstructionSlot; } - + protected override InstructionFlags ComputeFlags() { var flags = InstructionFlags.None; @@ -217,7 +216,7 @@ namespace ICSharpCode.Decompiler.IL flags |= FinalInstruction.Flags; return flags; } - + public override InstructionFlags DirectFlags { get { return InstructionFlags.None; @@ -280,6 +279,21 @@ namespace ICSharpCode.Decompiler.IL return inst; } + /// + /// Gets the closest parent Block. + /// Returns null, if the instruction is not a descendant of a Block. + /// + public static Block FindClosestBlock(ILInstruction inst) + { + var curr = inst; + while (curr != null) { + if (curr is Block) + return (Block)curr; + curr = curr.Parent; + } + return null; + } + public bool MatchInlineAssignBlock(out CallInstruction call, out ILInstruction value) { call = null; @@ -315,13 +329,6 @@ namespace ICSharpCode.Decompiler.IL ObjectInitializer, StackAllocInitializer, /// - /// Block is used for postfix operator on local variable. - /// - /// - /// Postfix operators on non-locals use CompoundAssignmentInstruction with CompoundAssignmentType.EvaluatesToOldValue. - /// - PostfixOperator, - /// /// Block is used for using the result of a property setter inline. /// Example: Use(this.Property = value); /// This is only for inline assignments to property or indexers; other inline assignments work diff --git a/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs b/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs index 0e178853b..b824794a2 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs @@ -184,6 +184,7 @@ namespace ICSharpCode.Decompiler.IL Debug.Assert(EntryPoint == null || Parent is ILFunction || !HasILRange); Debug.Assert(Blocks.All(b => b.HasFlag(InstructionFlags.EndPointUnreachable))); Debug.Assert(Blocks.All(b => b.Kind == BlockKind.ControlFlow)); // this also implies that the blocks don't use FinalInstruction + Debug.Assert(TopologicalSort(deleteUnreachableBlocks: true).Count == Blocks.Count, "Container should not have any unreachable blocks"); Block bodyStartBlock; switch (Kind) { case ContainerKind.Normal: @@ -237,45 +238,56 @@ namespace ICSharpCode.Decompiler.IL return InstructionFlags.ControlFlow; } } - + /// - /// Sort the blocks in reverse post-order over the control flow graph between the blocks. + /// Topologically sort the blocks. + /// The new order is returned without modifying the BlockContainer. /// - public void SortBlocks(bool deleteUnreachableBlocks = false) + /// If true, unreachable blocks are not included in the new order. + public List TopologicalSort(bool deleteUnreachableBlocks = false) { - if (Blocks.Count < 2) - return; - // Visit blocks in post-order BitSet visited = new BitSet(Blocks.Count); List postOrder = new List(); - - Action visit = null; - visit = delegate(Block block) { + Visit(EntryPoint); + postOrder.Reverse(); + if (!deleteUnreachableBlocks) { + for (int i = 0; i < Blocks.Count; i++) { + if (!visited[i]) + postOrder.Add(Blocks[i]); + } + } + return postOrder; + + void Visit(Block block) + { Debug.Assert(block.Parent == this); if (!visited[block.ChildIndex]) { visited[block.ChildIndex] = true; foreach (var branch in block.Descendants.OfType()) { if (branch.TargetBlock.Parent == this) { - visit(branch.TargetBlock); + Visit(branch.TargetBlock); } } postOrder.Add(block); } }; - visit(EntryPoint); - - postOrder.Reverse(); - if (!deleteUnreachableBlocks) { - for (int i = 0; i < Blocks.Count; i++) { - if (!visited[i]) - postOrder.Add(Blocks[i]); - } - } - Debug.Assert(postOrder[0] == Blocks[0]); - Blocks.ReplaceList(postOrder); + } + + /// + /// Topologically sort the blocks. + /// + /// If true, delete unreachable blocks. + public void SortBlocks(bool deleteUnreachableBlocks = false) + { + if (Blocks.Count < 2) + return; + + var newOrder = TopologicalSort(deleteUnreachableBlocks); + Debug.Assert(newOrder[0] == Blocks[0]); + Blocks.ReplaceList(newOrder); } public static BlockContainer FindClosestContainer(ILInstruction inst) diff --git a/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs b/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs index 07f980fa7..e2b751218 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs @@ -32,7 +32,8 @@ namespace ICSharpCode.Decompiler.IL public readonly InstructionCollection Arguments; ILInstruction functionPointer; - + public bool IsInstance { get; } + public bool HasExplicitThis { get; } public System.Reflection.Metadata.SignatureCallingConvention CallingConvention { get; } public IType ReturnType { get; } public ImmutableArray ParameterTypes { get; } @@ -61,9 +62,11 @@ namespace ICSharpCode.Decompiler.IL functionPointer.ChildIndex = Arguments.Count; } - public CallIndirect(System.Reflection.Metadata.SignatureCallingConvention callingConvention, IType returnType, ImmutableArray parameterTypes, + public CallIndirect(bool isInstance, bool hasExplicitThis, System.Reflection.Metadata.SignatureCallingConvention callingConvention, IType returnType, ImmutableArray parameterTypes, IEnumerable arguments, ILInstruction functionPointer) : base(OpCode.CallIndirect) { + this.IsInstance = isInstance; + this.HasExplicitThis = hasExplicitThis; this.CallingConvention = callingConvention; this.ReturnType = returnType ?? throw new ArgumentNullException("returnType"); this.ParameterTypes = parameterTypes.ToImmutableArray(); @@ -74,7 +77,7 @@ namespace ICSharpCode.Decompiler.IL public override ILInstruction Clone() { - return new CallIndirect(CallingConvention, ReturnType, ParameterTypes, + return new CallIndirect(IsInstance, HasExplicitThis, CallingConvention, ReturnType, ParameterTypes, this.Arguments.Select(inst => inst.Clone()), functionPointer.Clone() ).WithILRange(this); } @@ -84,7 +87,7 @@ namespace ICSharpCode.Decompiler.IL internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); - Debug.Assert(Arguments.Count == ParameterTypes.Length); + Debug.Assert(Arguments.Count == ParameterTypes.Length + (IsInstance ? 1 : 0)); } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) @@ -94,7 +97,12 @@ namespace ICSharpCode.Decompiler.IL ReturnType.WriteTo(output); output.Write('('); bool first = true; - foreach (var (inst, type) in Arguments.Zip(ParameterTypes, (a,b) => (a,b))) { + int firstArgument = IsInstance ? 1 : 0; + if (firstArgument == 1) { + Arguments[0].WriteTo(output, options); + first = false; + } + foreach (var (inst, type) in Arguments.Skip(firstArgument).Zip(ParameterTypes, (a,b) => (a,b))) { if (first) first = false; else @@ -155,6 +163,10 @@ namespace ICSharpCode.Decompiler.IL bool EqualSignature(CallIndirect other) { + if (IsInstance != other.IsInstance) + return false; + if (HasExplicitThis != other.HasExplicitThis) + return false; if (CallingConvention != other.CallingConvention) return false; if (ParameterTypes.Length != other.ParameterTypes.Length) diff --git a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs index b3d3fe8ce..ba8c61586 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs @@ -23,36 +23,101 @@ using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { - public enum CompoundAssignmentType : byte + public enum CompoundEvalMode : byte { + /// + /// The compound.assign instruction will evaluate to the old value. + /// This mode is used only for post-increment/decrement. + /// EvaluatesToOldValue, + /// + /// The compound.assign instruction will evaluate to the new value. + /// This mode is used for compound assignments and pre-increment/decrement. + /// EvaluatesToNewValue } + public enum CompoundTargetKind : byte + { + /// + /// The target is an instruction computing an address, + /// and the compound.assign will implicitly load/store from/to that address. + /// + Address, + /// + /// The Target must be a call to a property getter, + /// and the compound.assign will implicitly call the corresponding property setter. + /// + Property, + /// + /// The target is a dynamic call. + /// + Dynamic + } + public abstract partial class CompoundAssignmentInstruction : ILInstruction { - public readonly CompoundAssignmentType CompoundAssignmentType; + public readonly CompoundEvalMode EvalMode; + + /// + /// If TargetIsProperty is true, the Target must be a call to a property getter, + /// and the compound.assign will implicitly call the corresponding property setter. + /// Otherwise, the Target can be any instruction that evaluates to an address, + /// and the compound.assign will implicit load and store from/to that address. + /// + public readonly CompoundTargetKind TargetKind; - public CompoundAssignmentInstruction(OpCode opCode, CompoundAssignmentType compoundAssignmentType, ILInstruction target, ILInstruction value) + public CompoundAssignmentInstruction(OpCode opCode, CompoundEvalMode evalMode, ILInstruction target, CompoundTargetKind targetKind, ILInstruction value) : base(opCode) { - this.CompoundAssignmentType = compoundAssignmentType; + this.EvalMode = evalMode; this.Target = target; + this.TargetKind = targetKind; this.Value = value; + CheckValidTarget(); } - internal static bool IsValidCompoundAssignmentTarget(ILInstruction inst) + internal override void CheckInvariant(ILPhase phase) { - switch (inst.OpCode) { - // case OpCode.LdLoc: -- not valid -- does not mark the variable as written to - case OpCode.LdObj: - return true; - case OpCode.Call: - case OpCode.CallVirt: - var owner = ((CallInstruction)inst).Method.AccessorOwner as IProperty; - return owner != null && owner.CanSet; - default: - return false; + base.CheckInvariant(phase); + CheckValidTarget(); + } + + [Conditional("DEBUG")] + void CheckValidTarget() + { + switch (TargetKind) { + case CompoundTargetKind.Address: + Debug.Assert(target.ResultType == StackType.Ref || target.ResultType == StackType.I); + break; + case CompoundTargetKind.Property: + Debug.Assert(target.OpCode == OpCode.Call || target.OpCode == OpCode.CallVirt); + var owner = ((CallInstruction)target).Method.AccessorOwner as IProperty; + Debug.Assert(owner != null && owner.CanSet); + break; + case CompoundTargetKind.Dynamic: + Debug.Assert(target.OpCode == OpCode.DynamicGetMemberInstruction || target.OpCode == OpCode.DynamicGetIndexInstruction); + break; + } + } + + protected void WriteSuffix(ITextOutput output) + { + switch (TargetKind) { + case CompoundTargetKind.Address: + output.Write(".address"); + break; + case CompoundTargetKind.Property: + output.Write(".property"); + break; + } + switch (EvalMode) { + case CompoundEvalMode.EvaluatesToNewValue: + output.Write(".new"); + break; + case CompoundEvalMode.EvaluatesToOldValue: + output.Write(".old"); + break; } } } @@ -82,8 +147,9 @@ namespace ICSharpCode.Decompiler.IL public bool IsLifted { get; } - public NumericCompoundAssign(BinaryNumericInstruction binary, ILInstruction target, ILInstruction value, IType type, CompoundAssignmentType compoundAssignmentType) - : base(OpCode.NumericCompoundAssign, compoundAssignmentType, target, value) + public NumericCompoundAssign(BinaryNumericInstruction binary, ILInstruction target, + CompoundTargetKind targetKind, ILInstruction value, IType type, CompoundEvalMode evalMode) + : base(OpCode.NumericCompoundAssign, evalMode, target, targetKind, value) { Debug.Assert(IsBinaryCompatibleWithType(binary, type)); this.CheckForOverflow = binary.CheckForOverflow; @@ -95,8 +161,8 @@ namespace ICSharpCode.Decompiler.IL this.IsLifted = binary.IsLifted; this.type = type; this.AddILRange(binary); - Debug.Assert(compoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue || (Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub)); - Debug.Assert(IsValidCompoundAssignmentTarget(Target)); + Debug.Assert(evalMode == CompoundEvalMode.EvaluatesToNewValue || (Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub)); + Debug.Assert(this.ResultType == (IsLifted ? StackType.O : UnderlyingResultType)); } /// @@ -175,16 +241,20 @@ namespace ICSharpCode.Decompiler.IL WriteILRange(output, options); output.Write(OpCode); output.Write("." + BinaryNumericInstruction.GetOperatorName(Operator)); - if (CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue) - output.Write(".new"); - else - output.Write(".old"); - if (CheckForOverflow) + if (CheckForOverflow) { output.Write(".ovf"); - if (Sign == Sign.Unsigned) + } + if (Sign == Sign.Unsigned) { output.Write(".unsigned"); - else if (Sign == Sign.Signed) + } else if (Sign == Sign.Signed) { output.Write(".signed"); + } + output.Write('.'); + output.Write(UnderlyingResultType.ToString().ToLowerInvariant()); + if (IsLifted) { + output.Write(".lifted"); + } + base.WriteSuffix(output); output.Write('('); Target.WriteTo(output, options); output.Write(", "); @@ -198,13 +268,13 @@ namespace ICSharpCode.Decompiler.IL public readonly IMethod Method; public bool IsLifted => false; // TODO: implement lifted user-defined compound assignments - public UserDefinedCompoundAssign(IMethod method, CompoundAssignmentType compoundAssignmentType, ILInstruction target, ILInstruction value) - : base(OpCode.UserDefinedCompoundAssign, compoundAssignmentType, target, value) + public UserDefinedCompoundAssign(IMethod method, CompoundEvalMode evalMode, + ILInstruction target, CompoundTargetKind targetKind, ILInstruction value) + : base(OpCode.UserDefinedCompoundAssign, evalMode, target, targetKind, value) { this.Method = method; Debug.Assert(Method.IsOperator || IsStringConcat(method)); - Debug.Assert(compoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue || (Method.Name == "op_Increment" || Method.Name == "op_Decrement")); - Debug.Assert(IsValidCompoundAssignmentTarget(Target)); + Debug.Assert(evalMode == CompoundEvalMode.EvaluatesToNewValue || (Method.Name == "op_Increment" || Method.Name == "op_Decrement")); } public static bool IsStringConcat(IMethod method) @@ -218,11 +288,7 @@ namespace ICSharpCode.Decompiler.IL { WriteILRange(output, options); output.Write(OpCode); - - if (CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue) - output.Write(".new"); - else - output.Write(".old"); + base.WriteSuffix(output); output.Write(' '); Method.WriteTo(output); output.Write('('); @@ -240,8 +306,11 @@ namespace ICSharpCode.Decompiler.IL public CSharpArgumentInfo ValueArgumentInfo { get; } public CSharpBinderFlags BinderFlags { get; } - public DynamicCompoundAssign(ExpressionType op, CSharpBinderFlags binderFlags, ILInstruction target, CSharpArgumentInfo targetArgumentInfo, ILInstruction value, CSharpArgumentInfo valueArgumentInfo) - : base(OpCode.DynamicCompoundAssign, CompoundAssignmentTypeFromOperation(op), target, value) + public DynamicCompoundAssign(ExpressionType op, CSharpBinderFlags binderFlags, + ILInstruction target, CSharpArgumentInfo targetArgumentInfo, + ILInstruction value, CSharpArgumentInfo valueArgumentInfo, + CompoundTargetKind targetKind = CompoundTargetKind.Dynamic) + : base(OpCode.DynamicCompoundAssign, CompoundEvalModeFromOperation(op), target, targetKind, value) { if (!IsExpressionTypeSupported(op)) throw new ArgumentOutOfRangeException("op"); @@ -257,10 +326,7 @@ namespace ICSharpCode.Decompiler.IL output.Write(OpCode); output.Write("." + Operation.ToString().ToLower()); DynamicInstruction.WriteBinderFlags(BinderFlags, output, options); - if (CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue) - output.Write(".new"); - else - output.Write(".old"); + base.WriteSuffix(output); output.Write(' '); DynamicInstruction.WriteArgumentList(output, options, (Target, TargetArgumentInfo), (Value, ValueArgumentInfo)); } @@ -286,14 +352,14 @@ namespace ICSharpCode.Decompiler.IL || type == ExpressionType.SubtractAssignChecked; } - static CompoundAssignmentType CompoundAssignmentTypeFromOperation(ExpressionType op) + static CompoundEvalMode CompoundEvalModeFromOperation(ExpressionType op) { switch (op) { case ExpressionType.PostIncrementAssign: case ExpressionType.PostDecrementAssign: - return CompoundAssignmentType.EvaluatesToOldValue; + return CompoundEvalMode.EvaluatesToOldValue; default: - return CompoundAssignmentType.EvaluatesToNewValue; + return CompoundEvalMode.EvaluatesToNewValue; } } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs b/ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs index 1eb663f63..5a3b64b54 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs @@ -349,14 +349,17 @@ namespace ICSharpCode.Decompiler.IL partial class DynamicInvokeConstructorInstruction { + readonly IType resultType; + public IReadOnlyList ArgumentInfo { get; } - public DynamicInvokeConstructorInstruction(CSharpBinderFlags binderFlags, IType context, CSharpArgumentInfo[] argumentInfo, ILInstruction[] arguments) + public DynamicInvokeConstructorInstruction(CSharpBinderFlags binderFlags, IType type, IType context, CSharpArgumentInfo[] argumentInfo, ILInstruction[] arguments) : base(OpCode.DynamicInvokeConstructorInstruction, binderFlags, context) { ArgumentInfo = argumentInfo; Arguments = new InstructionCollection(this, 0); Arguments.AddRange(arguments); + this.resultType = type; } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) @@ -365,11 +368,12 @@ namespace ICSharpCode.Decompiler.IL output.Write(OpCode); WriteBinderFlags(output, options); output.Write(' '); + resultType?.WriteTo(output); output.Write(".ctor"); WriteArgumentList(output, options, Arguments.Zip(ArgumentInfo)); } - public override StackType ResultType => StackType.O; + public override StackType ResultType => resultType?.GetStackType() ?? StackType.Unknown; public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) { diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs index f77106260..0086b3f2d 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs @@ -18,25 +18,60 @@ using System; using System.Collections.Generic; -using ICSharpCode.Decompiler.IL.Transforms; +using System.Diagnostics; using System.Linq; + +using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; -using System.Diagnostics; namespace ICSharpCode.Decompiler.IL { partial class ILFunction { + /// + /// Gets the method definition from metadata. + /// May be null for functions that were not constructed from metadata, + /// e.g., expression trees. + /// public readonly IMethod Method; + + /// + /// Gets the generic context of this function. + /// public readonly GenericContext GenericContext; + + /// + /// Gets the name of this function, usually this returns the name from metadata. + /// + /// For local functions: + /// This is the name that is used to declare and use the function. + /// It may not conflict with the names of local variables of ancestor functions + /// and may be overwritten by the AssignVariableNames step. + /// + /// For top-level functions, delegates and expressions trees modifying this usually + /// has no effect, as the name should not be used in the final AST construction. + /// + /// + public string Name; + /// /// Size of the IL code in this function. /// Note: after async/await transform, this is the code size of the MoveNext function. /// public int CodeSize; + + /// + /// List of ILVariables used in this function. + /// public readonly ILVariableCollection Variables; + /// + /// Gets the scope in which the local function is declared. + /// Returns null, if this is not a local function. + /// + public BlockContainer DeclarationScope { get; internal set; } + /// /// List of warnings of ILReader. /// @@ -51,6 +86,9 @@ namespace ICSharpCode.Decompiler.IL /// public bool IsIterator; + /// + /// Gets whether the YieldReturnDecompiler determined that the Mono C# compiler was used to compile this function. + /// public bool StateMachineCompiledWithMono; /// @@ -70,6 +108,11 @@ namespace ICSharpCode.Decompiler.IL /// public IMethod MoveNextMethod; + /// + /// If this function is a local function, this field stores the reduced version of the function. + /// + internal TypeSystem.Implementation.LocalFunctionMethod ReducedMethod; + internal DebugInfo.AsyncDebugInfo AsyncDebugInfo; int ctorCallStart = int.MinValue; @@ -96,37 +139,99 @@ namespace ICSharpCode.Decompiler.IL /// /// If this is an expression tree or delegate, returns the expression tree type Expression{T} or T. /// T is the delegate type that matches the signature of this method. + /// Otherwise this must be null. /// public IType DelegateType; - public bool IsExpressionTree => DelegateType != null && DelegateType.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1; + ILFunctionKind kind; + + /// + /// Gets which kind of function this is. + /// + public ILFunctionKind Kind { + get => kind; + internal set { + if (kind == ILFunctionKind.TopLevelFunction || kind == ILFunctionKind.LocalFunction) + throw new InvalidOperationException("ILFunction.Kind of a top-level or local function may not be changed."); + kind = value; + } + } + /// + /// Return type of this function. + /// Might be null, if this function was not created from metadata. + /// public readonly IType ReturnType; + /// + /// List of parameters of this function. + /// Might be null, if this function was not created from metadata. + /// public readonly IReadOnlyList Parameters; - public ILFunction(IMethod method, int codeSize, GenericContext genericContext, ILInstruction body) : base(OpCode.ILFunction) + /// + /// Constructs a new ILFunction from the given metadata and with the given ILAst body. + /// + /// + /// Use to create ILAst. + /// may be null. + /// + public ILFunction(IMethod method, int codeSize, GenericContext genericContext, ILInstruction body, ILFunctionKind kind = ILFunctionKind.TopLevelFunction) : base(OpCode.ILFunction) { this.Method = method; + this.Name = Method?.Name; this.CodeSize = codeSize; this.GenericContext = genericContext; this.Body = body; this.ReturnType = Method?.ReturnType; this.Parameters = Method?.Parameters; this.Variables = new ILVariableCollection(this); + this.LocalFunctions = new InstructionCollection(this, 1); + this.kind = kind; } - public ILFunction(IType returnType, IReadOnlyList parameters, GenericContext genericContext, ILInstruction body) : base(OpCode.ILFunction) + /// + /// This constructor is only to be used by the TransformExpressionTrees step. + /// + internal ILFunction(IType returnType, IReadOnlyList parameters, GenericContext genericContext, ILInstruction body) : base(OpCode.ILFunction) { this.GenericContext = genericContext; this.Body = body; this.ReturnType = returnType; this.Parameters = parameters; this.Variables = new ILVariableCollection(this); + this.LocalFunctions = new InstructionCollection(this, 1); + this.kind = ILFunctionKind.ExpressionTree; } internal override void CheckInvariant(ILPhase phase) { + switch (kind) { + case ILFunctionKind.TopLevelFunction: + Debug.Assert(Parent == null); + Debug.Assert(DelegateType == null); + Debug.Assert(DeclarationScope == null); + Debug.Assert(Method != null); + break; + case ILFunctionKind.Delegate: + Debug.Assert(Parent != null && !(Parent is Block)); + Debug.Assert(DelegateType != null); + Debug.Assert(DeclarationScope == null); + Debug.Assert(!(DelegateType?.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1)); + break; + case ILFunctionKind.ExpressionTree: + Debug.Assert(Parent != null && !(Parent is Block)); + Debug.Assert(DelegateType != null); + Debug.Assert(DeclarationScope == null); + Debug.Assert(DelegateType?.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1); + break; + case ILFunctionKind.LocalFunction: + Debug.Assert(Parent is ILFunction && SlotInfo == ILFunction.LocalFunctionsSlot); + Debug.Assert(DeclarationScope != null); + Debug.Assert(DelegateType == null); + Debug.Assert(Method != null); + break; + } for (int i = 0; i < Variables.Count; i++) { Debug.Assert(Variables[i].Function == this); Debug.Assert(Variables[i].IndexInFunction == i); @@ -148,8 +253,13 @@ namespace ICSharpCode.Decompiler.IL output.Write(' '); Method.WriteTo(output); } - if (IsExpressionTree) { - output.Write(".ET"); + switch (kind) { + case ILFunctionKind.ExpressionTree: + output.Write(".ET"); + break; + case ILFunctionKind.LocalFunction: + output.Write(".local"); + break; } if (DelegateType != null) { output.Write("["); @@ -165,6 +275,11 @@ namespace ICSharpCode.Decompiler.IL if (IsIterator) { output.WriteLine(".iterator"); } + if (DeclarationScope != null) { + output.Write("declared as " + Name + " in "); + output.WriteLocalReference(DeclarationScope.EntryPoint.Label, DeclarationScope); + output.WriteLine(); + } output.MarkFoldStart(Variables.Count + " variable(s)", true); foreach (var variable in Variables) { @@ -181,6 +296,11 @@ namespace ICSharpCode.Decompiler.IL body.WriteTo(output, options); output.WriteLine(); + foreach (var localFunction in LocalFunctions) { + output.WriteLine(); + localFunction.WriteTo(output, options); + } + if (options.ShowILRanges) { var unusedILRanges = FindUnusedILRanges(); if (!unusedILRanges.IsEmpty) { @@ -276,6 +396,8 @@ namespace ICSharpCode.Decompiler.IL /// internal void RecombineVariables(ILVariable variable1, ILVariable variable2) { + if (variable1 == variable2) + return; Debug.Assert(ILVariableEqualityComparer.Instance.Equals(variable1, variable2)); foreach (var ldloc in variable2.LoadInstructions.ToArray()) { ldloc.Variable = variable1; @@ -290,4 +412,33 @@ namespace ICSharpCode.Decompiler.IL Debug.Assert(ok); } } + + public enum ILFunctionKind + { + /// + /// ILFunction is a "top-level" function, i.e., method, accessor, constructor, destructor or operator. + /// + TopLevelFunction, + /// + /// ILFunction is a delegate or lambda expression. + /// + /// + /// This kind is introduced by the DelegateConstruction and TransformExpressionTrees steps in the decompiler pipeline. + /// + Delegate, + /// + /// ILFunction is an expression tree lambda. + /// + /// + /// This kind is introduced by the TransformExpressionTrees step in the decompiler pipeline. + /// + ExpressionTree, + /// + /// ILFunction is a C# 7.0 local function. + /// + /// + /// This kind is introduced by the LocalFunctionDecompiler step in the decompiler pipeline. + /// + LocalFunction + } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs index cf5677f7d..789634a57 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Threading; using ICSharpCode.Decompiler.IL.Patterns; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; @@ -124,32 +125,35 @@ namespace ICSharpCode.Decompiler.IL Debug.Assert(a == b); return a; } - + +#if DEBUG /// /// Gets whether this node (or any subnode) was modified since the last ResetDirty() call. /// /// - /// IsDirty is used by the LoopingTransform, and must not be used by individual transforms within the loop. + /// IsDirty is used by the StatementTransform, and must not be used by individual transforms within the loop. /// - public bool IsDirty { get; private set; } - - protected void MakeDirty() - { - for (ILInstruction inst = this; inst != null && !inst.IsDirty; inst = inst.parent) - inst.IsDirty = true; - } - + internal bool IsDirty { get; private set; } + /// /// Marks this node (and all subnodes) as IsDirty=false. /// - /// - /// IsDirty is used by the LoopingTransform, and must not be used by individual transforms within the loop. - /// - public void ResetDirty() + internal void ResetDirty() { foreach (ILInstruction inst in Descendants) inst.IsDirty = false; } +#endif + + [Conditional("DEBUG")] + protected private void MakeDirty() + { +#if DEBUG + for (ILInstruction inst = this; inst != null && !inst.IsDirty; inst = inst.parent) { + inst.IsDirty = true; + } +#endif + } const InstructionFlags invalidFlags = (InstructionFlags)(-1); @@ -307,7 +311,7 @@ namespace ICSharpCode.Decompiler.IL protected abstract SlotInfo GetChildSlot(int index); #region ChildrenCollection + ChildrenEnumerator - public struct ChildrenCollection : IReadOnlyList + public readonly struct ChildrenCollection : IReadOnlyList { readonly ILInstruction inst; @@ -627,7 +631,7 @@ namespace ICSharpCode.Decompiler.IL { ILInstruction oldValue = childPointer; Debug.Assert(oldValue == GetChild(index)); - if (oldValue == newValue) + if (oldValue == newValue && newValue?.parent == this && newValue.ChildIndex == index) return; childPointer = newValue; if (newValue != null) { @@ -758,6 +762,41 @@ namespace ICSharpCode.Decompiler.IL } return false; } + + /// + /// Extracts the this instruction. + /// The instruction is replaced with a load of a new temporary variable; + /// and the instruction is moved to a store to said variable at block-level. + /// Returns the new variable. + /// + /// If extraction is not possible, the ILAst is left unmodified and the function returns null. + /// May return null if extraction is not possible. + /// + public ILVariable Extract() + { + return Transforms.ExtractionContext.Extract(this); + } + + /// + /// Prepares "extracting" a descendant instruction out of this instruction. + /// This is the opposite of ILInlining. It may involve re-compiling high-level constructs into lower-level constructs. + /// + /// True if extraction is possible; false otherwise. + internal virtual bool PrepareExtract(int childIndex, Transforms.ExtractionContext ctx) + { + if (!GetChildSlot(childIndex).CanInlineInto) { + return false; + } + // Check whether re-ordering with predecessors is valid: + for (int i = childIndex - 1; i >= 0; --i) { + ILInstruction predecessor = GetChild(i); + if (!GetChildSlot(i).CanInlineInto) { + return false; + } + ctx.RegisterMoveIfNecessary(predecessor); + } + return true; + } } public interface IInstructionWithTypeOperand diff --git a/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs index 9f22e0d34..fb28f1c7b 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs @@ -30,7 +30,7 @@ namespace ICSharpCode.Decompiler.IL /// /// IfInstruction is also used to represent logical operators: /// "a || b" ==> if (a) (ldc.i4 1) else (b) - /// "a && b" ==> if (a) (b) else (ldc.i4 0) + /// "a && b" ==> if (a) (b) else (ldc.i4 0) /// "a ? b : c" ==> if (a) (b) else (c) /// partial class IfInstruction : ILInstruction diff --git a/ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs b/ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs index f9f311cf3..63d5fd97d 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs @@ -44,7 +44,7 @@ namespace ICSharpCode.Decompiler.IL get { return list[index]; } set { T oldValue = list[index]; - if (oldValue != value) { + if (!(oldValue == value && value.Parent == parentInstruction && value.ChildIndex == index)) { list[index] = value; value.ChildIndex = index + firstChildIndex; parentInstruction.InstructionCollectionAdded(value); diff --git a/ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs b/ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs index 87917b8d4..6fdd3e382 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs @@ -36,7 +36,9 @@ namespace ICSharpCode.Decompiler.IL break; case null: // field of unresolved type - Debug.Assert(target.ResultType == StackType.O || target.ResultType == StackType.I || target.ResultType == StackType.Ref); + Debug.Assert(target.ResultType == StackType.O || target.ResultType == StackType.I + || target.ResultType == StackType.Ref || target.ResultType == StackType.Unknown, + "Field of unresolved type with invalid target"); break; } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs b/ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs index 8e638ba33..bfe56d08f 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs @@ -18,6 +18,7 @@ using System.Diagnostics; using System.Linq; +using ICSharpCode.Decompiler.IL.Transforms; namespace ICSharpCode.Decompiler.IL { @@ -125,5 +126,11 @@ namespace ICSharpCode.Decompiler.IL return StackType.O; } } + + internal override bool PrepareExtract(int childIndex, ExtractionContext ctx) + { + return base.PrepareExtract(childIndex, ctx) + && (ctx.FlagsBeingMoved & InstructionFlags.MayUnwrapNull) == 0; + } } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs index 0158d557a..fe7753b2c 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs @@ -362,4 +362,9 @@ namespace ICSharpCode.Decompiler.IL } } } + + public partial class Throw + { + internal StackType resultType = StackType.Void; + } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs index 9cdf8bcb4..71d4a545c 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs @@ -162,6 +162,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms break; } } + foreach (var localFunction in function.LocalFunctions) { + if (!LocalFunctionDecompiler.ParseLocalFunctionName(localFunction.Name, out _, out var newName) || !IsValidName(newName)) + newName = null; + localFunction.Name = newName; + } // Now generate names: var mapping = new Dictionary(ILVariableEqualityComparer.Instance); foreach (var inst in function.Descendants.OfType()) { @@ -174,6 +179,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms v.Name = name; } } + foreach (var localFunction in function.LocalFunctions) { + var newName = localFunction.Name; + if (newName == null) { + newName = GetAlternativeName("f"); + } + localFunction.Name = newName; + } } /// diff --git a/ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs index 07b151d4f..9836b1242 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs @@ -85,7 +85,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms context.CancellationToken.ThrowIfCancellationRequested(); blockContext.ControlFlowGraph = new ControlFlowGraph(container, context.CancellationToken); VisitBlock(blockContext.ControlFlowGraph.GetNode(container.EntryPoint), blockContext); - // TODO: handle unreachable code? } } finally { running = false; diff --git a/ICSharpCode.Decompiler/IL/Transforms/CachedDelegateInitialization.cs b/ICSharpCode.Decompiler/IL/Transforms/CachedDelegateInitialization.cs index a0e365f92..38ba30608 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/CachedDelegateInitialization.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/CachedDelegateInitialization.cs @@ -68,7 +68,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!storeInst.MatchStsFld(out IField field2, out ILInstruction value) || !field.Equals(field2) || !field.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) return false; - if (!DelegateConstruction.IsDelegateConstruction(value as NewObj, true)) + if (!DelegateConstruction.IsDelegateConstruction(value.UnwrapConv(ConversionKind.Invalid) as NewObj, true)) return false; var nextInstruction = inst.Parent.Children.ElementAtOrDefault(inst.ChildIndex + 1); if (nextInstruction == null) diff --git a/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs b/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs index 3ca89cfb5..3ccf25e02 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// then we can replace the variable with the argument. /// 2) assignments of address-loading instructions to local variables /// - public class CopyPropagation : IBlockTransform + public class CopyPropagation : IBlockTransform, IILTransform { public static void Propagate(StLoc store, ILTransformContext context) { @@ -42,6 +42,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms } public void Run(Block block, BlockTransformContext context) + { + RunOnBlock(block, context); + } + + public void Run(ILFunction function, ILTransformContext context) + { + foreach (var block in function.Descendants.OfType()) { + if (block.Kind != BlockKind.ControlFlow) + continue; + RunOnBlock(block, context); + } + } + + static void RunOnBlock(Block block, ILTransformContext context) { for (int i = 0; i < block.Instructions.Count; i++) { ILVariable v; @@ -52,7 +66,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (SemanticHelper.IsPure(copiedExpr.Flags)) { // no-op -> delete context.Step("remove dead store to stack: no-op -> delete", block.Instructions[i]); - block.Instructions.RemoveAt(i--); + block.Instructions.RemoveAt(i); + // This can open up new inlining opportunities: + int c = ILInlining.InlineInto(block, i, InliningOptions.None, context: context); + i -= c + 1; } else { // evaluate the value for its side-effects context.Step("remove dead store to stack: evaluate the value for its side-effects", block.Instructions[i]); @@ -88,13 +105,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms // Parameters can be copied only if they aren't assigned to (directly or indirectly via ldarga) // note: the initialization by the caller is the first store -> StoreCount must be 1 return v.IsSingleDefinition; - case VariableKind.StackSlot: - case VariableKind.ExceptionStackSlot: - // Variables are be copied only if both they and the target copy variable are generated, - // and if the variable has only a single assignment - return v.IsSingleDefinition && target.Kind == VariableKind.StackSlot; default: - return false; + // Variables can be copied if both are single-definition. + // To avoid removing too many variables, we do this only if the target + // is either a stackslot or a ref local. + Debug.Assert(target.IsSingleDefinition); + return v.IsSingleDefinition && (target.Kind == VariableKind.StackSlot || target.StackType == StackType.Ref); } default: // All instructions without special behavior that target a stack-variable can be copied. diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs index dfdda704c..4d723a0b6 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs @@ -16,9 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler.CSharp; @@ -26,7 +24,10 @@ using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { - public class DelegateConstruction : IILTransform + /// + /// + /// + class DelegateConstruction : IILTransform { ILTransformContext context; ITypeResolveContext decompilationContext; @@ -37,9 +38,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms return; this.context = context; this.decompilationContext = new SimpleTypeResolveContext(function.Method); - var orphanedVariableInits = new List(); - var targetsToReplace = new List(); - var translatedDisplayClasses = new HashSet(); var cancellationToken = context.CancellationToken; foreach (var inst in function.Descendants) { cancellationToken.ThrowIfCancellationRequested(); @@ -50,69 +48,38 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (instWithVar.Variable.Kind == VariableKind.Local) { instWithVar.Variable.Kind = VariableKind.DisplayClassLocal; } - targetsToReplace.Add(instWithVar); + var displayClassTypeDef = instWithVar.Variable.Type.GetDefinition(); + if (instWithVar.Variable.IsSingleDefinition && instWithVar.Variable.StoreInstructions.SingleOrDefault() is StLoc store) { + if (store.Value is NewObj newObj) { + instWithVar.Variable.CaptureScope = BlockContainer.FindClosestContainer(store); + } + } } context.StepEndGroup(); } - if (inst.MatchStLoc(out ILVariable targetVariable, out ILInstruction value)) { - var newObj = value as NewObj; - // TODO : it is probably not a good idea to remove *all* display-classes - // is there a way to minimize the false-positives? - if (newObj != null && IsInSimpleDisplayClass(newObj.Method)) { - targetVariable.CaptureScope = BlockContainer.FindClosestContainer(inst); - targetsToReplace.Add((IInstructionWithVariableOperand)inst); - translatedDisplayClasses.Add(newObj.Method.DeclaringTypeDefinition); - } - } - } - foreach (var target in targetsToReplace.OrderByDescending(t => ((ILInstruction)t).StartILOffset)) { - context.Step($"TransformDisplayClassUsages {target.Variable}", (ILInstruction)target); - function.AcceptVisitor(new TransformDisplayClassUsages(function, target, target.Variable.CaptureScope, orphanedVariableInits, translatedDisplayClasses)); - } - context.Step($"Remove orphanedVariableInits", function); - foreach (var store in orphanedVariableInits) { - if (store.Parent is Block containingBlock) - containingBlock.Instructions.Remove(store); } } - static bool IsInSimpleDisplayClass(IMethod method) - { - if (!method.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) - return false; - return IsSimpleDisplayClass(method.DeclaringType); - } - - internal static bool IsSimpleDisplayClass(IType type) - { - if (!type.HasGeneratedName() || (!type.Name.Contains("DisplayClass") && !type.Name.Contains("AnonStorey"))) - return false; - if (type.DirectBaseTypes.Any(t => !t.IsKnownType(KnownTypeCode.Object))) - return false; - return true; - } - - #region TransformDelegateConstruction internal static bool IsDelegateConstruction(NewObj inst, bool allowTransformed = false) { - if (inst == null || inst.Arguments.Count != 2 || inst.Method.DeclaringType.Kind != TypeKind.Delegate) + if (inst == null || inst.Arguments.Count != 2) return false; var opCode = inst.Arguments[1].OpCode; - - return opCode == OpCode.LdFtn || opCode == OpCode.LdVirtFtn || (allowTransformed && opCode == OpCode.ILFunction); - } - - internal static bool IsPotentialClosure(ILTransformContext context, NewObj inst) - { - var decompilationContext = new SimpleTypeResolveContext(context.Function.Method); - return IsPotentialClosure(decompilationContext.CurrentTypeDefinition, inst.Method.DeclaringTypeDefinition); + if (!(opCode == OpCode.LdFtn || opCode == OpCode.LdVirtFtn || (allowTransformed && opCode == OpCode.ILFunction))) + return false; + var typeKind = inst.Method.DeclaringType.Kind; + return typeKind == TypeKind.Delegate || typeKind == TypeKind.Unknown; } static bool IsAnonymousMethod(ITypeDefinition decompiledTypeDefinition, IMethod method) { - if (method == null || !(method.HasGeneratedName() || method.Name.Contains("$") || ContainsAnonymousType(method))) + if (method == null) return false; - if (!(method.IsCompilerGeneratedOrIsInCompilerGeneratedClass() || IsPotentialClosure(decompiledTypeDefinition, method.DeclaringTypeDefinition))) + if (!(method.HasGeneratedName() + || method.Name.Contains("$") + || method.IsCompilerGeneratedOrIsInCompilerGeneratedClass() + || TransformDisplayClassUsage.IsPotentialClosure(decompiledTypeDefinition, method.DeclaringTypeDefinition) + || ContainsAnonymousType(method))) return false; return true; } @@ -127,18 +94,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms } return false; } - - static bool IsPotentialClosure(ITypeDefinition decompiledTypeDefinition, ITypeDefinition potentialDisplayClass) - { - if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) - return false; - while (potentialDisplayClass != decompiledTypeDefinition) { - potentialDisplayClass = potentialDisplayClass.DeclaringTypeDefinition; - if (potentialDisplayClass == null) - return false; - } - return true; - } internal static GenericContext? GenericContextFromTypeArguments(TypeParameterSubstitution subst) { @@ -171,11 +126,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms var targetMethod = ((IInstructionWithMethodOperand)value.Arguments[1]).Method; if (!IsAnonymousMethod(decompilationContext.CurrentTypeDefinition, targetMethod)) return null; - if (LocalFunctionDecompiler.IsLocalFunctionMethod(targetMethod.ParentModule.PEFile, (MethodDefinitionHandle)targetMethod.MetadataToken)) - return null; - target = value.Arguments[0]; if (targetMethod.MetadataToken.IsNil) return null; + if (LocalFunctionDecompiler.IsLocalFunctionMethod(targetMethod, context)) + return null; + target = value.Arguments[0]; var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken); if (!methodDefinition.HasBody()) return null; @@ -184,12 +139,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms return null; var ilReader = context.CreateILReader(); var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress); - var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, context.CancellationToken); + var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.Delegate, context.CancellationToken); function.DelegateType = value.Method.DeclaringType; - function.CheckInvariant(ILPhase.Normal); // Embed the lambda into the parent function's ILAst, so that "Show steps" can show // how the lambda body is being transformed. value.ReplaceWith(function); + function.CheckInvariant(ILPhase.Normal); var contextPrefix = targetMethod.Name; foreach (ILVariable v in function.Variables.Where(v => v.Kind != VariableKind.Parameter)) { @@ -219,7 +174,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// Replaces loads of 'this' with the target expression. /// Async delegates use: ldobj(ldloca this). /// - class ReplaceDelegateTargetVisitor : ILVisitor + internal class ReplaceDelegateTargetVisitor : ILVisitor { readonly ILVariable thisVariable; readonly ILInstruction target; @@ -255,139 +210,5 @@ namespace ICSharpCode.Decompiler.IL.Transforms base.VisitLdObj(inst); } } - - /// - /// 1. Stores to display class fields are replaced with stores to local variables (in some - /// cases existing variables are used; otherwise fresh variables are added to the - /// ILFunction-container) and all usages of those fields are replaced with the local variable. - /// (see initValues) - /// 2. Usages of the display class container (or any copy) are removed. - /// - class TransformDisplayClassUsages : ILVisitor - { - ILFunction currentFunction; - BlockContainer captureScope; - readonly IInstructionWithVariableOperand targetLoad; - readonly List targetAndCopies = new List(); - readonly List orphanedVariableInits; - readonly HashSet translatedDisplayClasses; - readonly Dictionary initValues = new Dictionary(); - - struct DisplayClassVariable - { - public ILVariable variable; - public ILInstruction value; - } - - public TransformDisplayClassUsages(ILFunction function, IInstructionWithVariableOperand targetLoad, BlockContainer captureScope, List orphanedVariableInits, HashSet translatedDisplayClasses) - { - this.currentFunction = function; - this.targetLoad = targetLoad; - this.captureScope = captureScope; - this.orphanedVariableInits = orphanedVariableInits; - this.translatedDisplayClasses = translatedDisplayClasses; - this.targetAndCopies.Add(targetLoad.Variable); - } - - protected override void Default(ILInstruction inst) - { - foreach (var child in inst.Children) { - child.AcceptVisitor(this); - } - } - - protected internal override void VisitStLoc(StLoc inst) - { - base.VisitStLoc(inst); - if (targetLoad is ILInstruction instruction && instruction.MatchLdThis()) - return; - if (inst.Variable == targetLoad.Variable) - orphanedVariableInits.Add(inst); - if (MatchesTargetOrCopyLoad(inst.Value)) { - targetAndCopies.Add(inst.Variable); - orphanedVariableInits.Add(inst); - } - } - - bool MatchesTargetOrCopyLoad(ILInstruction inst) - { - return targetAndCopies.Any(v => inst.MatchLdLoc(v)); - } - - protected internal override void VisitStObj(StObj inst) - { - base.VisitStObj(inst); - if (!inst.Target.MatchLdFlda(out ILInstruction target, out IField field) || !MatchesTargetOrCopyLoad(target) || target.MatchLdThis()) - return; - field = (IField)field.MemberDefinition; - ILInstruction value; - if (initValues.TryGetValue(field, out DisplayClassVariable info)) { - inst.ReplaceWith(new StLoc(info.variable, inst.Value).WithILRange(inst)); - } else { - if (inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter && currentFunction == v.Function) { - // special case for parameters: remove copies of parameter values. - orphanedVariableInits.Add(inst); - value = inst.Value; - } else { - if (!translatedDisplayClasses.Contains(field.DeclaringTypeDefinition)) - return; - v = currentFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name); - v.CaptureScope = captureScope; - inst.ReplaceWith(new StLoc(v, inst.Value).WithILRange(inst)); - value = new LdLoc(v); - } - initValues.Add(field, new DisplayClassVariable { value = value, variable = v }); - } - } - - protected internal override void VisitLdObj(LdObj inst) - { - base.VisitLdObj(inst); - if (!inst.Target.MatchLdFlda(out ILInstruction target, out IField field)) - return; - if (!initValues.TryGetValue((IField)field.MemberDefinition, out DisplayClassVariable info)) - return; - var replacement = info.value.Clone(); - replacement.SetILRange(inst); - inst.ReplaceWith(replacement); - } - - protected internal override void VisitLdFlda(LdFlda inst) - { - base.VisitLdFlda(inst); - if (inst.Target.MatchLdThis() && inst.Field.Name == "$this" - && inst.Field.MemberDefinition.ReflectionName.Contains("c__Iterator")) { - var variable = currentFunction.Variables.First((f) => f.Index == -1); - inst.ReplaceWith(new LdLoca(variable).WithILRange(inst)); - } - if (inst.Parent is LdObj || inst.Parent is StObj) - return; - if (!MatchesTargetOrCopyLoad(inst.Target)) - return; - var field = (IField)inst.Field.MemberDefinition; - if (!initValues.TryGetValue(field, out DisplayClassVariable info)) { - if (!translatedDisplayClasses.Contains(field.DeclaringTypeDefinition)) - return; - var v = currentFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name); - v.CaptureScope = captureScope; - inst.ReplaceWith(new LdLoca(v).WithILRange(inst)); - var value = new LdLoc(v); - initValues.Add(field, new DisplayClassVariable { value = value, variable = v }); - } else if (info.value is LdLoc l) { - inst.ReplaceWith(new LdLoca(l.Variable).WithILRange(inst)); - } else { - Debug.Fail("LdFlda pattern not supported!"); - } - } - - protected internal override void VisitNumericCompoundAssign(NumericCompoundAssign inst) - { - base.VisitNumericCompoundAssign(inst); - if (inst.Target.MatchLdLoc(out var v)) { - inst.ReplaceWith(new StLoc(v, new BinaryNumericInstruction(inst.Operator, inst.Target, inst.Value, inst.CheckForOverflow, inst.Sign).WithILRange(inst))); - } - } - } - #endregion } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs index 8d4a8bf0f..3b9537daf 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs @@ -183,12 +183,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms arguments: targetInvokeCall.Arguments.Skip(2).ToArray() ); case BinderMethodKind.InvokeConstructor: + var arguments = targetInvokeCall.Arguments.Skip(2).ToArray(); + // Extract type information from targetInvokeCall: + // Must either be an inlined type or + // a reference to a variable that is initialized with a type. + if (!TransformExpressionTrees.MatchGetTypeFromHandle(arguments[0], out var type)) { + if (!(arguments[0].MatchLdLoc(out var temp) && temp.IsSingleDefinition && temp.StoreInstructions.FirstOrDefault() is StLoc initStore)) + return null; + if (!TransformExpressionTrees.MatchGetTypeFromHandle(initStore.Value, out type)) + return null; + } deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); return new DynamicInvokeConstructorInstruction( binderFlags: callsite.Flags, + type: type ?? SpecialType.UnknownType, context: callsite.Context, argumentInfo: callsite.ArgumentInfos, - arguments: targetInvokeCall.Arguments.Skip(2).ToArray() + arguments: arguments ); case BinderMethodKind.InvokeMember: deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); @@ -500,7 +511,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; int i = 0; callSiteInfo.ArgumentInfos = new CSharpArgumentInfo[numberOfArguments]; - var compileTimeTypes = callSiteInfo.DelegateType.GetDelegateInvokeMethod().Parameters.SelectReadOnlyArray(p => p.Type); + IMethod invokeMethod = callSiteInfo.DelegateType.GetDelegateInvokeMethod(); + if (invokeMethod == null) + return false; + var compileTimeTypes = invokeMethod.Parameters.SelectReadOnlyArray(p => p.Type); foreach (var (_, arg) in arguments) { if (!(arg is Call createCall)) return false; diff --git a/ICSharpCode.Decompiler/IL/Transforms/DynamicIsEventAssignmentTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/DynamicIsEventAssignmentTransform.cs index bd9522458..5903ecef3 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DynamicIsEventAssignmentTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DynamicIsEventAssignmentTransform.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.Linq; + namespace ICSharpCode.Decompiler.IL.Transforms { public class DynamicIsEventAssignmentTransform : IStatementTransform @@ -24,6 +26,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// if (logic.not(ldloc V_1)) Block IL_004a { /// stloc V_2(dynamic.getmember B(target)) /// } + /// [stloc copyOfValue(value)] /// if (logic.not(ldloc V_1)) Block IL_0149 { /// dynamic.setmember.compound B(target, dynamic.binary.operator AddAssign(ldloc V_2, value)) /// } else Block IL_0151 { @@ -41,11 +44,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms return; if (!(flagVar.IsSingleDefinition && flagVar.LoadCount == 2)) return; - if (!(MatchLhsCacheIfInstruction(block.Instructions[pos + 1], flagVar, out var dynamicGetMemberStore))) + if (!MatchLhsCacheIfInstruction(block.Instructions[pos + 1], flagVar, out var dynamicGetMemberStore)) return; if (!(dynamicGetMemberStore.MatchStLoc(out var getMemberVar, out inst) && inst is DynamicGetMemberInstruction getMemberInst)) return; - foreach (var descendant in block.Instructions[pos + 2].Descendants) { + int offset = 2; + if (block.Instructions[pos + offset].MatchStLoc(out var valueVariable) + && pos + 4 < block.Instructions.Count && valueVariable.IsSingleDefinition && valueVariable.LoadCount == 2 + && valueVariable.LoadInstructions.All(ld => ld.Parent is DynamicInstruction)) { + offset++; + } + foreach (var descendant in block.Instructions[pos + offset].Descendants) { if (!MatchIsEventAssignmentIfInstruction(descendant, isEvent, flagVar, getMemberVar, out var setMemberInst, out var getMemberVarUse, out var isEventConditionUse)) continue; context.Step("DynamicIsEventAssignmentTransform", block.Instructions[pos]); @@ -87,7 +96,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } else return false; setMemberInst = Block.Unwrap(trueInst) as DynamicSetMemberInstruction; - if (!(setMemberInst != null)) + if (setMemberInst == null) return false; if (!isEvent.Argument.Match(setMemberInst.Target).Success) return false; diff --git a/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs index f60e2d490..5ee5b2fbe 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs @@ -66,6 +66,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms protected internal override void VisitLdObj(LdObj inst) { base.VisitLdObj(inst); + AddressOfLdLocToLdLoca(inst, context); LdObjToLdLoc(inst, context); } @@ -83,6 +84,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } + internal static void AddressOfLdLocToLdLoca(LdObj inst, ILTransformContext context) + { + // ldobj(...(addressof(ldloc V))) where ... can be zero or more ldflda instructions + // => + // ldobj(...(ldloca V)) + var temp = inst.Target; + var range = temp.ILRanges; + while (temp.MatchLdFlda(out var ldfldaTarget, out _)) { + temp = ldfldaTarget; + range = range.Concat(temp.ILRanges); + } + if (temp.MatchAddressOf(out var addressOfTarget) && addressOfTarget.MatchLdLoc(out var v)) { + context.Step($"ldobj(...(addressof(ldloca {v.Name}))) => ldobj(...(ldloca {v.Name}))", inst); + var replacement = new LdLoca(v).WithILRange(addressOfTarget); + foreach (var r in range) { + replacement = replacement.WithILRange(r); + } + temp.ReplaceWith(replacement); + } + } + protected internal override void VisitCall(Call inst) { var expr = HandleCall(inst, context); diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 05f7d2f5f..b2b9b4c13 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -17,13 +17,10 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; -using System.Reflection.Metadata; using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.IL.Transforms { @@ -62,10 +59,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } + protected internal override void VisitBlockContainer(BlockContainer container) + { + if (container.Kind == ContainerKind.Switch) { + // Special case for switch: Only visit the switch condition block. + var switchInst = (SwitchInstruction)container.EntryPoint.Instructions[0]; + switchInst.Value.AcceptVisitor(this); + } + // No need to call base.VisitBlockContainer, see comment in VisitBlock. + } + protected internal override void VisitBlock(Block block) { - // Don't visit child blocks; since this is a block transform - // we know those were already handled previously. + if (block.Kind == BlockKind.ControlFlow) { + // Don't visit child control flow blocks; + // since this is a block transform + // we know those were already handled previously. + return; + } + base.VisitBlock(block); } protected internal override void VisitComp(Comp inst) @@ -271,6 +283,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } Block block; if (TransformSpanTCtorContainingStackAlloc(inst, out ILInstruction locallocSpan)) { + context.Step("new Span(stackalloc) -> stackalloc Span", inst); inst.ReplaceWith(locallocSpan); block = null; ILInstruction stmt = locallocSpan; @@ -281,7 +294,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms } stmt = stmt.Parent; } - //ILInlining.InlineIfPossible(block, stmt.ChildIndex - 1, context); + // Special case to eliminate extra store + if (stmt.GetNextSibling() is StLoc storeStmt && storeStmt.Value is LdLoc) + ILInlining.InlineIfPossible(block, stmt.ChildIndex, context); return; } if (TransformArrayInitializers.TransformSpanTArrayInitialization(inst, context, out block)) { @@ -289,9 +304,37 @@ namespace ICSharpCode.Decompiler.IL.Transforms inst.ReplaceWith(block); return; } + if (TransformDelegateCtorLdVirtFtnToLdVirtDelegate(inst, out LdVirtDelegate ldVirtDelegate)) { + context.Step("new Delegate(target, ldvirtftn Method) -> ldvirtdelegate Delegate Method(target)", inst); + inst.ReplaceWith(ldVirtDelegate); + return; + } base.VisitNewObj(inst); } + /// + /// newobj Delegate..ctor(target, ldvirtftn TargetMethod(target)) + /// => + /// ldvirtdelegate System.Delegate TargetMethod(target) + /// + bool TransformDelegateCtorLdVirtFtnToLdVirtDelegate(NewObj inst, out LdVirtDelegate ldVirtDelegate) + { + ldVirtDelegate = null; + if (inst.Method.DeclaringType.Kind != TypeKind.Delegate) + return false; + if (inst.Arguments.Count != 2) + return false; + if (!(inst.Arguments[1] is LdVirtFtn ldVirtFtn)) + return false; + if (!SemanticHelper.IsPure(inst.Arguments[0].Flags)) + return false; + if (!inst.Arguments[0].Match(ldVirtFtn.Argument).Success) + return false; + ldVirtDelegate = new LdVirtDelegate(inst.Arguments[0], inst.Method.DeclaringType, ldVirtFtn.Method) + .WithILRange(inst).WithILRange(ldVirtFtn).WithILRange(ldVirtFtn.Argument); + return true; + } + /// /// newobj Span..ctor(localloc(conv i4->u <zero extend>(ldc.i4 sizeInBytes)), numberOfElementsExpr) /// => @@ -382,10 +425,37 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } + bool TransformDecimalFieldToConstant(LdObj inst, out LdcDecimal result) + { + if (inst.MatchLdsFld(out var field) && field.DeclaringType.IsKnownType(KnownTypeCode.Decimal)) { + decimal? value = null; + if (field.Name == "One") { + value = decimal.One; + } else if (field.Name == "MinusOne") { + value = decimal.MinusOne; + } else if (field.Name == "Zero") { + value = decimal.Zero; + } + if (value != null) { + result = new LdcDecimal(value.Value).WithILRange(inst).WithILRange(inst.Target); + return true; + } + } + result = null; + return false; + } + protected internal override void VisitLdObj(LdObj inst) { base.VisitLdObj(inst); - EarlyExpressionTransforms.LdObjToLdLoc(inst, context); + EarlyExpressionTransforms.AddressOfLdLocToLdLoca(inst, context); + if (EarlyExpressionTransforms.LdObjToLdLoc(inst, context)) + return; + if (TransformDecimalFieldToConstant(inst, out LdcDecimal decimalConstant)) { + context.Step("TransformDecimalFieldToConstant", inst); + inst.ReplaceWith(decimalConstant); + return; + } } protected internal override void VisitStObj(StObj inst) @@ -398,6 +468,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms TransformAssignment.HandleCompoundAssign(inst, context); } + protected internal override void VisitStLoc(StLoc inst) + { + base.VisitStLoc(inst); + TransformAssignment.HandleCompoundAssign(inst, context); + } + protected internal override void VisitIfInstruction(IfInstruction inst) { inst.TrueInst.AcceptVisitor(this); @@ -462,10 +538,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(dynamicCompoundAssign.Target is DynamicGetMemberInstruction getMember)) return false; - if (!isEvent.Argument.Match(getMember.Target).Success) - return false; if (!SemanticHelper.IsPure(isEvent.Argument.Flags)) return false; + if (!isEvent.Argument.Match(getMember.Target).Success) + return false; if (!(trueInst is DynamicInvokeMemberInstruction invokeMember)) return false; if (!(invokeMember.BinderFlags.HasFlag(CSharpBinderFlags.InvokeSpecialName) && invokeMember.BinderFlags.HasFlag(CSharpBinderFlags.ResultDiscarded))) @@ -541,7 +617,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms for (int j = 0; j < dynamicGetIndex.Arguments.Count; j++) { if (!SemanticHelper.IsPure(dynamicGetIndex.Arguments[j].Flags)) return; - if (!dynamicGetIndex.Arguments[j].Match(dynamicGetIndex.Arguments[j]).Success) + if (!dynamicGetIndex.Arguments[j].Match(inst.Arguments[j]).Success) return; } if (!DynamicCompoundAssign.IsExpressionTypeSupported(binaryOp.Operation)) @@ -614,18 +690,56 @@ namespace ICSharpCode.Decompiler.IL.Transforms } /// - /// Transform local exception variable. + /// catch ex : TException when (...) BlockContainer { + /// Block entryPoint (incoming: 1) { + /// stloc v(ldloc ex) + /// ... + /// } + /// } + /// => + /// catch v : TException when (...) BlockContainer { + /// Block entryPoint (incoming: 1) { + /// ... + /// } + /// } /// void TransformCatchVariable(TryCatchHandler handler, Block entryPoint) { - if (!entryPoint.Instructions[0].MatchStLoc(out var exceptionVar, out var exceptionSlotLoad)) + if (!handler.Variable.IsSingleDefinition || handler.Variable.LoadCount != 1) + return; // handle.Variable already has non-trivial uses + if (!entryPoint.Instructions[0].MatchStLoc(out var exceptionVar, out var exceptionSlotLoad)) { + // Not the pattern with a second exceptionVar. + // However, it is still possible that we need to remove a pointless UnboxAny: + if (handler.Variable.LoadInstructions.Single().Parent is UnboxAny inlinedUnboxAny) { + if (inlinedUnboxAny.Type.Equals(handler.Variable.Type)) { + context.Step("TransformCatchVariable - remove inlined UnboxAny", inlinedUnboxAny); + inlinedUnboxAny.ReplaceWith(inlinedUnboxAny.Argument); + } + } return; - if (!exceptionVar.IsSingleDefinition || exceptionVar.Kind != VariableKind.Local) + } + if (exceptionVar.Kind != VariableKind.Local && exceptionVar.Kind != VariableKind.StackSlot) return; - if (!exceptionSlotLoad.MatchLdLoc(handler.Variable) || !handler.Variable.IsSingleDefinition || handler.Variable.LoadCount != 1) + if (exceptionSlotLoad is UnboxAny unboxAny) { + // When catching a type parameter, csc emits an unbox.any instruction + if (!unboxAny.Type.Equals(handler.Variable.Type)) + return; + exceptionSlotLoad = unboxAny.Argument; + } + if (!exceptionSlotLoad.MatchLdLoc(handler.Variable)) return; - handler.Variable = exceptionVar; + // Check that exceptionVar is only used within the catch block: + var allUses = exceptionVar.LoadInstructions + .Concat(exceptionVar.StoreInstructions.Cast()) + .Concat(exceptionVar.AddressInstructions); + foreach (var inst in allUses) { + if (!inst.IsDescendantOf(handler)) + return; + } + context.Step("TransformCatchVariable", entryPoint.Instructions[0]); exceptionVar.Kind = VariableKind.ExceptionLocal; + exceptionVar.Type = handler.Variable.Type; + handler.Variable = exceptionVar; entryPoint.Instructions.RemoveAt(0); } @@ -636,6 +750,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { TransformCatchVariable(handler, entryPoint); if (entryPoint.Instructions.Count == 1 && entryPoint.Instructions[0].MatchLeave(out _, out var condition)) { + context.Step("TransformCatchWhen", entryPoint.Instructions[0]); handler.Filter = condition; } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/FixRemainingIncrements.cs b/ICSharpCode.Decompiler/IL/Transforms/FixRemainingIncrements.cs new file mode 100644 index 000000000..f48c345b9 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Transforms/FixRemainingIncrements.cs @@ -0,0 +1,65 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Text; +using System.Diagnostics; +using ICSharpCode.Decompiler.TypeSystem; + +namespace ICSharpCode.Decompiler.IL.Transforms +{ + public class FixRemainingIncrements : IILTransform + { + void IILTransform.Run(ILFunction function, ILTransformContext context) + { + var callsToFix = new List(); + foreach (var call in function.Descendants.OfType()) { + if (!(call.Method.IsOperator && (call.Method.Name == "op_Increment" || call.Method.Name == "op_Decrement"))) + continue; + if (call.Arguments.Count != 1) + continue; + if (call.Method.DeclaringType.IsKnownType(KnownTypeCode.Decimal)) { + // For decimal, legacy csc can optimize "d + 1m" to "op_Increment(d)". + // We can handle these calls in ReplaceMethodCallsWithOperators. + continue; + } + callsToFix.Add(call); + } + foreach (var call in callsToFix) { + // A user-defined increment/decrement that was not handled by TransformAssignment. + // This can happen because the variable-being-incremented was optimized out by Roslyn, + // e.g. + // public void Issue1552Pre(UserType a, UserType b) + // { + // UserType num = a + b; + // Console.WriteLine(++num); + // } + // can end up being compiled to: + // Console.WriteLine(UserType.op_Increment(a + b)); + if (call.SlotInfo == StLoc.ValueSlot && call.Parent.SlotInfo == Block.InstructionSlot) { + var store = (StLoc)call.Parent; + var block = (Block)store.Parent; + context.Step($"Fix {call.Method.Name} call at 0x{call.StartILOffset:x4} using {store.Variable.Name}", call); + // stloc V(call op_Increment(...)) + // -> + // stloc V(...) + // compound.assign op_Increment(V) + call.ReplaceWith(call.Arguments[0]); + block.Instructions.Insert(store.ChildIndex + 1, + new UserDefinedCompoundAssign(call.Method, CompoundEvalMode.EvaluatesToNewValue, + new LdLoca(store.Variable), CompoundTargetKind.Address, new LdcI4(1)).WithILRange(call)); + } else { + context.Step($"Fix {call.Method.Name} call at 0x{call.StartILOffset:x4} using new local", call); + var newVariable = call.Arguments[0].Extract(); + if (newVariable == null) { + Debug.Fail("Failed to extract argument of remaining increment/decrement"); + continue; + } + newVariable.Type = call.GetParameter(0).Type; + Debug.Assert(call.Arguments[0].MatchLdLoc(newVariable)); + call.ReplaceWith(new UserDefinedCompoundAssign(call.Method, CompoundEvalMode.EvaluatesToNewValue, + new LdLoca(newVariable), CompoundTargetKind.Address, new LdcI4(1)).WithILRange(call)); + } + } + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs index 0573d4114..2e4df8080 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs @@ -434,18 +434,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Returns true if the instruction is stloc v(add(ldloc v, arg)) - /// or stloc v(compound.assign(ldloc v, arg)) + /// or compound.assign(ldloca v, arg) /// public static bool MatchIncrement(ILInstruction inst, out ILVariable variable) { - if (!inst.MatchStLoc(out variable, out var value)) - return false; - if (!value.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right)) { - if (value is CompoundAssignmentInstruction cai) { - left = cai.Target; - } else return false; + if (inst.MatchStLoc(out variable, out var value)) { + if (value.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right)) { + return left.MatchLdLoc(variable); + } + } else if (inst is CompoundAssignmentInstruction cai) { + return cai.TargetKind == CompoundTargetKind.Address && cai.Target.MatchLdLoca(out variable); } - return left.MatchLdLoc(variable); + return false; } /// diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILExtraction.cs b/ICSharpCode.Decompiler/IL/Transforms/ILExtraction.cs new file mode 100644 index 000000000..5a1cbf01d --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Transforms/ILExtraction.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; + +namespace ICSharpCode.Decompiler.IL.Transforms +{ + /// + /// Context object for the ILInstruction.Extract() operation. + /// + class ExtractionContext + { + /// + /// Nearest function, used for registering the new locals that are created by extraction. + /// + readonly ILFunction Function; + + /// + /// Combined flags of all instructions being moved. + /// + internal InstructionFlags FlagsBeingMoved; + + /// + /// List of actions to be executed when performing the extraction. + /// + /// Each function in this list has the side-effect of replacing the instruction-to-be-moved + /// with a load of a fresh temporary variable; and returns the the store to the temporary variable, + /// which will be inserted at block-level. + /// + readonly List> MoveActions = new List>(); + + ExtractionContext(ILFunction function) + { + Debug.Assert(function != null); + this.Function = function; + } + + internal void RegisterMove(ILInstruction predecessor) + { + FlagsBeingMoved |= predecessor.Flags; + MoveActions.Add(delegate { + var v = Function.RegisterVariable(VariableKind.StackSlot, predecessor.ResultType); + predecessor.ReplaceWith(new LdLoc(v)); + return new StLoc(v, predecessor); + }); + } + + internal void RegisterMoveIfNecessary(ILInstruction predecessor) + { + if (!CanReorderWithInstructionsBeingMoved(predecessor)) { + RegisterMove(predecessor); + } + } + + /// + /// Currently, predecessor is evaluated before the instructions being moved. + /// If this function returns true, predecessor can stay as-is, despite the move changing the evaluation order. + /// If this function returns false, predecessor will need to also move, to ensure the evaluation order stays unchanged. + /// + public bool CanReorderWithInstructionsBeingMoved(ILInstruction predecessor) + { + // We could track the instructions being moved and be smarter about unnecessary moves, + // but given the limited scenarios where extraction is used so far, + // this seems unnecessary. + return predecessor.Flags == InstructionFlags.None; + } + + /// + /// Extracts the specified instruction: + /// The instruction is replaced with a load of a new temporary variable; + /// and the instruction is moved to a store to said variable at block-level. + /// + /// May return null if extraction is not possible. + /// + public static ILVariable Extract(ILInstruction instToExtract) + { + var function = instToExtract.Ancestors.OfType().First(); + ExtractionContext ctx = new ExtractionContext(function); + ctx.FlagsBeingMoved = instToExtract.Flags; + ILInstruction inst = instToExtract; + while (inst != null) { + if (inst.Parent is Block block && block.Kind == BlockKind.ControlFlow) { + // We've reached the target block, and extraction is possible all the way. + int insertIndex = inst.ChildIndex; + // Move instToExtract itself: + var v = function.RegisterVariable(VariableKind.StackSlot, instToExtract.ResultType); + instToExtract.ReplaceWith(new LdLoc(v)); + block.Instructions.Insert(insertIndex, new StLoc(v, instToExtract)); + // Apply the other move actions: + foreach (var moveAction in ctx.MoveActions) { + block.Instructions.Insert(insertIndex, moveAction()); + } + return v; + } + if (!inst.Parent.PrepareExtract(inst.ChildIndex, ctx)) + return null; + inst = inst.Parent; + } + return null; + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index 16a0865b2..d1d8a0e69 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -272,10 +272,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (ldloca.Variable.Type.IsReferenceType ?? false) return false; - switch (ldloca.Parent.OpCode) { + ILInstruction inst = ldloca; + while (inst.Parent is LdFlda ldflda) { + inst = ldflda; + } + switch (inst.Parent.OpCode) { case OpCode.Call: case OpCode.CallVirt: - var method = ((CallInstruction)ldloca.Parent).Method; + var method = ((CallInstruction)inst.Parent).Method; if (method.IsAccessor && method.AccessorKind != MethodSemanticsAttributes.Getter) { // C# doesn't allow calling setters on temporary structs return false; @@ -283,6 +287,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return !method.IsStatic; case OpCode.Await: return true; + case OpCode.NullableUnwrap: + return ((NullableUnwrap)inst.Parent).RefInput; default: return false; } @@ -344,13 +350,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms { switch (addr) { case LdFlda ldflda: - return ldflda.Field.IsReadOnly; + return ldflda.Field.IsReadOnly + || (ldflda.Field.DeclaringType.Kind == TypeKind.Struct && IsReadonlyReference(ldflda.Target)); case LdsFlda ldsflda: return ldsflda.Field.IsReadOnly; case LdLoc ldloc: return IsReadonlyRefLocal(ldloc.Variable); case Call call: return call.Method.ReturnTypeIsRefReadOnly; + case AddressOf _: + // C# doesn't allow mutation of value-type temporaries + return true; default: return false; } @@ -373,7 +383,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable. /// /// The next top-level expression - /// The load within 'next' + /// The variable being eliminated by inlining. /// The expression being inlined static bool NonAggressiveInlineInto(ILInstruction next, FindResult findResult, ILInstruction inlinedExpression, ILVariable v) { @@ -402,7 +412,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms } break; } - + if (inlinedExpression.ResultType == StackType.Ref) { + // VB likes to use ref locals for compound assignment + // (the C# compiler uses ref stack slots instead). + // We want to avoid unnecessary ref locals, so we'll always inline them if possible. + return true; + } + var parent = loadInst.Parent; if (NullableLiftingTransform.MatchNullableCtor(parent, out _, out _)) { // inline into nullable ctor call in lifted operator @@ -424,13 +440,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; // inline into (left slot of) user-defined && or || operator case OpCode.DynamicGetMemberInstruction: case OpCode.DynamicGetIndexInstruction: - case OpCode.LdObj: if (parent.Parent.OpCode == OpCode.DynamicCompoundAssign) return true; // inline into dynamic compound assignments break; + case OpCode.DynamicCompoundAssign: + return true; case OpCode.ArrayToPointer: case OpCode.LocAllocSpan: return true; // inline size-expressions into localloc.span + case OpCode.Call: + case OpCode.CallVirt: + // Aggressive inline into property/indexer getter calls for compound assignment calls + // (The compiler generates locals for these because it doesn't want to evalute the args twice for getter+setter) + if (parent.SlotInfo == CompoundAssignmentInstruction.TargetSlot) { + return true; + } + break; } // decide based on the top-level target instruction into which we are inlining: switch (next.OpCode) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index b54adc273..8c52794f0 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -1,24 +1,316 @@ -using System; +// Copyright (c) 2019 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; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Text; using System.Text.RegularExpressions; +using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { + /// + /// Decompiler step for C# 7.0 local functions + /// class LocalFunctionDecompiler : IILTransform { + ILTransformContext context; + ITypeResolveContext resolveContext; + + struct LocalFunctionInfo + { + public List UseSites; + public ILFunction Definition; + } + + /// + /// The transform works like this: + /// + /// + /// local functions can either be used in method calls, i.e., call and callvirt instructions, + /// or can be used as part of the "delegate construction" pattern, i.e., newobj Delegate(<target-expression>, ldftn <method>). + /// + /// As local functions can be declared practically anywhere, we have to take a look at all use-sites and infer the declaration location from that. Use-sites can be call, callvirt and ldftn instructions. + /// After all use-sites are collected we construct the ILAst of the local function and add it to the parent function. + /// Then all use-sites of the local-function are transformed to a call to the LocalFunctionMethod or a ldftn of the LocalFunctionMethod. + /// In a next step we handle all nested local functions. + /// After all local functions are transformed, we move all local functions that capture any variables to their respective declaration scope. + /// public void Run(ILFunction function, ILTransformContext context) { - throw new NotImplementedException(); + if (!context.Settings.LocalFunctions) + return; + this.context = context; + this.resolveContext = new SimpleTypeResolveContext(function.Method); + var localFunctions = new Dictionary(); + var cancellationToken = context.CancellationToken; + // Find all local functions declared inside this method, including nested local functions or local functions declared in lambdas. + FindUseSites(function, context, localFunctions); + foreach (var (method, info) in localFunctions) { + cancellationToken.ThrowIfCancellationRequested(); + var firstUseSite = info.UseSites[0]; + context.StepStartGroup($"Transform " + info.Definition.Name, info.Definition); + try { + var localFunction = info.Definition; + if (!localFunction.Method.IsStatic) { + var target = firstUseSite.Arguments[0]; + context.Step($"Replace 'this' with {target}", localFunction); + var thisVar = localFunction.Variables.SingleOrDefault(VariableKindExtensions.IsThis); + localFunction.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(target, thisVar)); + } + + foreach (var useSite in info.UseSites) { + context.Step($"Transform use site at IL_{useSite.StartILOffset:x4}", useSite); + if (useSite.OpCode == OpCode.NewObj) { + TransformToLocalFunctionReference(localFunction, useSite); + } else { + DetermineCaptureAndDeclarationScope(localFunction, useSite); + TransformToLocalFunctionInvocation(localFunction.ReducedMethod, useSite); + } + + if (function.Method.IsConstructor && localFunction.DeclarationScope == null) { + localFunction.DeclarationScope = BlockContainer.FindClosestContainer(useSite); + } + } + + if (localFunction.DeclarationScope == null) { + localFunction.DeclarationScope = (BlockContainer)function.Body; + } else if (localFunction.DeclarationScope != function.Body && localFunction.DeclarationScope.Parent is ILFunction declaringFunction) { + function.LocalFunctions.Remove(localFunction); + declaringFunction.LocalFunctions.Add(localFunction); + } + } finally { + context.StepEndGroup(); + } + } } - public static bool IsLocalFunctionMethod(PEFile module, MethodDefinitionHandle methodHandle) + void FindUseSites(ILFunction function, ILTransformContext context, Dictionary localFunctions) { + foreach (var inst in function.Body.Descendants) { + context.CancellationToken.ThrowIfCancellationRequested(); + if (inst is CallInstruction call && !call.Method.IsLocalFunction && IsLocalFunctionMethod(call.Method, context)) { + HandleUseSite(call.Method, call); + } else if (inst is LdFtn ldftn && !ldftn.Method.IsLocalFunction && ldftn.Parent is NewObj newObj && IsLocalFunctionMethod(ldftn.Method, context) && DelegateConstruction.IsDelegateConstruction(newObj)) { + HandleUseSite(ldftn.Method, newObj); + } + } + + void HandleUseSite(IMethod targetMethod, CallInstruction inst) + { + if (!localFunctions.TryGetValue((MethodDefinitionHandle)targetMethod.MetadataToken, out var info)) { + context.StepStartGroup($"Read local function '{targetMethod.Name}'", inst); + info = new LocalFunctionInfo() { + UseSites = new List() { inst }, + Definition = ReadLocalFunctionDefinition(context.Function, targetMethod) + }; + localFunctions.Add((MethodDefinitionHandle)targetMethod.MetadataToken, info); + FindUseSites(info.Definition, context, localFunctions); + context.StepEndGroup(); + } else { + info.UseSites.Add(inst); + } + } + } + + ILFunction ReadLocalFunctionDefinition(ILFunction rootFunction, IMethod targetMethod) + { + var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken); + if (!methodDefinition.HasBody()) + return null; + var genericContext = DelegateConstruction.GenericContextFromTypeArguments(targetMethod.Substitution); + if (genericContext == null) + return null; + var ilReader = context.CreateILReader(); + var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress); + var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.LocalFunction, context.CancellationToken); + // Embed the local function into the parent function's ILAst, so that "Show steps" can show + // how the local function body is being transformed. + rootFunction.LocalFunctions.Add(function); + function.DeclarationScope = (BlockContainer)rootFunction.Body; + function.CheckInvariant(ILPhase.Normal); + var nestedContext = new ILTransformContext(context, function); + function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is LocalFunctionDecompiler)), nestedContext); + function.DeclarationScope = null; + function.ReducedMethod = ReduceToLocalFunction(targetMethod); + return function; + } + + static T FindCommonAncestorInstruction(ILInstruction a, ILInstruction b) + where T : ILInstruction + { + var ancestorsOfB = new HashSet(b.Ancestors.OfType()); + return a.Ancestors.OfType().FirstOrDefault(ancestorsOfB.Contains); + } + + internal static bool IsClosureParameter(IParameter parameter, ITypeResolveContext context) + { + if (!parameter.IsRef) + return false; + var type = ((ByReferenceType)parameter.Type).ElementType.GetDefinition(); + return type != null + && type.Kind == TypeKind.Struct + && TransformDisplayClassUsage.IsPotentialClosure(context.CurrentTypeDefinition, type); + } + + static IType UnwrapByRef(IType type) + { + if (type is ByReferenceType byRef) { + type = byRef.ElementType; + } + return type; + } + + internal static ILInstruction GetStatement(ILInstruction inst) + { + while (inst.Parent != null) { + if (inst.Parent is Block b && b.Kind == BlockKind.ControlFlow) + return inst; + inst = inst.Parent; + } + return inst; + } + + LocalFunctionMethod ReduceToLocalFunction(IMethod method) + { + int parametersToRemove = 0; + for (int i = method.Parameters.Count - 1; i >= 0; i--) { + if (!IsClosureParameter(method.Parameters[i], resolveContext)) + break; + parametersToRemove++; + } + return new LocalFunctionMethod(method, parametersToRemove); + } + + static void TransformToLocalFunctionReference(ILFunction function, CallInstruction useSite) + { + useSite.Arguments[0].ReplaceWith(new LdNull().WithILRange(useSite.Arguments[0])); + var fnptr = (IInstructionWithMethodOperand)useSite.Arguments[1]; + var replacement = new LdFtn(function.ReducedMethod).WithILRange((ILInstruction)fnptr); + useSite.Arguments[1].ReplaceWith(replacement); + } + + void TransformToLocalFunctionInvocation(LocalFunctionMethod reducedMethod, CallInstruction useSite) + { + bool wasInstanceCall = !useSite.Method.IsStatic; + var replacement = new Call(reducedMethod); + int firstArgumentIndex = wasInstanceCall ? 1 : 0; + int argumentCount = useSite.Arguments.Count; + int reducedArgumentCount = argumentCount - (reducedMethod.NumberOfCompilerGeneratedParameters + firstArgumentIndex); + replacement.Arguments.AddRange(useSite.Arguments.Skip(firstArgumentIndex).Take(reducedArgumentCount)); + // copy flags + replacement.ConstrainedTo = useSite.ConstrainedTo; + replacement.ILStackWasEmpty = useSite.ILStackWasEmpty; + replacement.IsTail = useSite.IsTail; + // copy IL ranges + replacement.AddILRange(useSite); + if (wasInstanceCall) { + replacement.AddILRange(useSite.Arguments[0]); + if (useSite.Arguments[0].MatchLdLocRef(out var variable) && variable.Kind == VariableKind.NamedArgument) { + // remove the store instruction of the simple load, if it is a named argument. + var storeInst = (ILInstruction)variable.StoreInstructions[0]; + ((Block)storeInst.Parent).Instructions.RemoveAt(storeInst.ChildIndex); + } + } + for (int i = 0; i < reducedMethod.NumberOfCompilerGeneratedParameters; i++) { + replacement.AddILRange(useSite.Arguments[argumentCount - i - 1]); + } + useSite.ReplaceWith(replacement); + } + + void DetermineCaptureAndDeclarationScope(ILFunction function, CallInstruction useSite) + { + int firstArgumentIndex = function.Method.IsStatic ? 0 : 1; + for (int i = useSite.Arguments.Count - 1; i >= firstArgumentIndex; i--) { + if (!HandleArgument(i, useSite.Arguments[i])) + break; + } + if (firstArgumentIndex > 0) { + HandleArgument(0, useSite.Arguments[0]); + } + + bool HandleArgument(int i, ILInstruction arg) + { + ILVariable closureVar; + if (!(arg.MatchLdLoc(out closureVar) || arg.MatchLdLoca(out closureVar))) + return false; + if (closureVar.Kind == VariableKind.NamedArgument) + return false; + ITypeDefinition potentialDisplayClass = UnwrapByRef(closureVar.Type).GetDefinition(); + if (!TransformDisplayClassUsage.IsPotentialClosure(context, potentialDisplayClass)) + return false; + if (i - firstArgumentIndex >= 0) { + Debug.Assert(i - firstArgumentIndex < function.Method.Parameters.Count && IsClosureParameter(function.Method.Parameters[i - firstArgumentIndex], resolveContext)); + } + if (closureVar.AddressCount == 0 && closureVar.StoreInstructions.Count == 0) + return true; + // determine the capture scope of closureVar and the declaration scope of the function + var instructions = closureVar.StoreInstructions.OfType() + .Concat(closureVar.AddressInstructions).OrderBy(inst => inst.StartILOffset).ToList(); + var additionalScope = BlockContainer.FindClosestContainer(instructions.First()); + if (closureVar.CaptureScope == null) + closureVar.CaptureScope = additionalScope; + else + closureVar.CaptureScope = FindCommonAncestorInstruction(closureVar.CaptureScope, additionalScope); + if (function.DeclarationScope == null) + function.DeclarationScope = closureVar.CaptureScope; + else if (!IsInNestedLocalFunction(function.DeclarationScope, closureVar.CaptureScope.Ancestors.OfType().First())) + function.DeclarationScope = FindCommonAncestorInstruction(function.DeclarationScope, closureVar.CaptureScope); + return true; + } + } + + bool IsInNestedLocalFunction(BlockContainer declarationScope, ILFunction function) + { + return TreeTraversal.PreOrder(function, f => f.LocalFunctions).Any(f => declarationScope.IsDescendantOf(f.Body)); + } + + internal static bool IsLocalFunctionReference(NewObj inst, ILTransformContext context) + { + if (inst == null || inst.Arguments.Count != 2 || inst.Method.DeclaringType.Kind != TypeKind.Delegate) + return false; + var opCode = inst.Arguments[1].OpCode; + + return opCode == OpCode.LdFtn + && IsLocalFunctionMethod(((IInstructionWithMethodOperand)inst.Arguments[1]).Method, context); + } + + public static bool IsLocalFunctionMethod(IMethod method, ILTransformContext context) + { + if (method.MetadataToken.IsNil) + return false; + return IsLocalFunctionMethod(method.ParentModule.PEFile, (MethodDefinitionHandle)method.MetadataToken, context); + } + + public static bool IsLocalFunctionMethod(PEFile module, MethodDefinitionHandle methodHandle, ILTransformContext context = null) + { + if (context != null && context.PEFile != module) + return false; + var metadata = module.Metadata; var method = metadata.GetMethodDefinition(methodHandle); var declaringType = method.GetDeclaringType(); @@ -32,12 +324,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - public static bool IsLocalFunctionDisplayClass(PEFile module, TypeDefinitionHandle typeHandle) + public static bool IsLocalFunctionDisplayClass(PEFile module, TypeDefinitionHandle typeHandle, ILTransformContext context = null) { + if (context != null && context.PEFile != module) + return false; + var metadata = module.Metadata; var type = metadata.GetTypeDefinition(typeHandle); - if ((type.Attributes & TypeAttributes.NestedPrivate) == 0) + if ((type.Attributes & TypeAttributes.VisibilityMask) != TypeAttributes.NestedPrivate) return false; if (!type.HasGeneratedName(metadata)) return false; @@ -46,7 +341,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms var declaringType = metadata.GetTypeDefinition(declaringTypeHandle); foreach (var method in declaringType.GetMethods()) { - if (!IsLocalFunctionMethod(module, method)) + if (!IsLocalFunctionMethod(module, method, context)) continue; var md = metadata.GetMethodDefinition(method); if (md.DecodeSignature(new FindTypeDecoder(typeHandle), default).ParameterTypes.Any()) @@ -57,12 +352,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms } /// - /// Newer Roslyn versions use the format "<callerName>g__functionName|x_y" - /// Older versions use "<callerName>g__functionNamex_y" + /// Newer Roslyn versions use the format "<callerName>g__functionName|x_y" + /// Older versions use "<callerName>g__functionNamex_y" /// - static readonly Regex functionNameRegex = new Regex(@"^<(.*)>g__(.*)\|{0,1}\d+(_\d+)?$", RegexOptions.Compiled); + static readonly Regex functionNameRegex = new Regex(@"^<(.*)>g__([^\|]*)\|{0,1}\d+(_\d+)?$", RegexOptions.Compiled); - static bool ParseLocalFunctionName(string name, out string callerName, out string functionName) + internal static bool ParseLocalFunctionName(string name, out string callerName, out string functionName) { callerName = null; functionName = null; @@ -76,7 +371,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms struct FindTypeDecoder : ISignatureTypeProvider { - TypeDefinitionHandle handle; + readonly TypeDefinitionHandle handle; public FindTypeDecoder(TypeDefinitionHandle handle) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs index 0717bc112..2a0780636 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { @@ -34,18 +35,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms { public void Run(Block block, int pos, StatementTransformContext context) { - TransformRefTypes(block, pos, context); + if (!TransformRefTypes(block, pos, context)) { + TransformThrowExpressionValueTypes(block, pos, context); + } } /// /// Handles NullCoalescingInstruction case 1: reference types. - /// - /// stloc s(valueInst) - /// if (comp(ldloc s == ldnull)) { - /// stloc s(fallbackInst) - /// } - /// => - /// stloc s(if.notnull(valueInst, fallbackInst)) /// bool TransformRefTypes(Block block, int pos, StatementTransformContext context) { @@ -58,6 +54,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!(condition.MatchCompEquals(out var left, out var right) && left.MatchLdLoc(stloc.Variable) && right.MatchLdNull())) return false; trueInst = Block.Unwrap(trueInst); + // stloc s(valueInst) + // if (comp(ldloc s == ldnull)) { + // stloc s(fallbackInst) + // } + // => + // stloc s(if.notnull(valueInst, fallbackInst)) if (trueInst.MatchStLoc(stloc.Variable, out var fallbackValue)) { context.Step("NullCoalescingTransform: simple (reference types)", stloc); stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, fallbackValue); @@ -83,7 +85,71 @@ namespace ICSharpCode.Decompiler.IL.Transforms ILInlining.InlineOneIfPossible(block, pos, InliningOptions.None, context); return true; } + // stloc obj(valueInst) + // if (comp(ldloc obj == ldnull)) { + // throw(...) + // } + // => + // stloc obj(if.notnull(valueInst, throw(...))) + if (context.Settings.ThrowExpressions && trueInst is Throw throwInst) { + context.Step("NullCoalescingTransform (reference types + throw expression)", stloc); + throwInst.resultType = StackType.O; + stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, throwInst); + block.Instructions.RemoveAt(pos + 1); // remove if instruction + ILInlining.InlineOneIfPossible(block, pos, InliningOptions.None, context); + return true; + } return false; } + + /// + /// stloc v(value) + /// if (logic.not(call get_HasValue(ldloca v))) throw(...) + /// ... Call(arg1, arg2, call GetValueOrDefault(ldloca v), arg4) ... + /// => + /// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ... + /// + bool TransformThrowExpressionValueTypes(Block block, int pos, StatementTransformContext context) + { + if (pos + 2 >= block.Instructions.Count) + return false; + if (!(block.Instructions[pos] is StLoc stloc)) + return false; + ILVariable v = stloc.Variable; + if (!(v.StoreCount == 1 && v.LoadCount == 0 && v.AddressCount == 2)) + return false; + if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst)) + return false; + if (!(Block.Unwrap(trueInst) is Throw throwInst)) + return false; + if (!condition.MatchLogicNot(out var arg)) + return false; + if (!(arg is CallInstruction call && NullableLiftingTransform.MatchHasValueCall(call, v))) + return false; + var throwInstParent = throwInst.Parent; + var throwInstChildIndex = throwInst.ChildIndex; + var nullCoalescingWithThrow = new NullCoalescingInstruction( + NullCoalescingKind.NullableWithValueFallback, + stloc.Value, + throwInst); + var resultType = NullableType.GetUnderlyingType(call.Method.DeclaringType).GetStackType(); + nullCoalescingWithThrow.UnderlyingResultType = resultType; + var result = ILInlining.FindLoadInNext(block.Instructions[pos + 2], v, nullCoalescingWithThrow, InliningOptions.None); + if (result.Type == ILInlining.FindResultType.Found + && NullableLiftingTransform.MatchGetValueOrDefault(result.LoadInst.Parent, v)) + { + context.Step("NullCoalescingTransform (value types + throw expression)", stloc); + throwInst.resultType = resultType; + result.LoadInst.Parent.ReplaceWith(nullCoalescingWithThrow); + block.Instructions.RemoveRange(pos, 2); // remove store(s) and if instruction + return true; + } else { + // reset the primary position (see remarks on ILInstruction.Parent) + stloc.Value = stloc.Value; + var children = throwInstParent.Children; + children[throwInstChildIndex] = throwInst; + return false; + } + } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs index f65415587..9288c447b 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs @@ -17,22 +17,15 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; -using System.Linq; -using System.Text; -using ICSharpCode.Decompiler.CSharp.Syntax; -using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Transform that converts code patterns like "v != null ? v.M() : null" to "v?.M()" /// - struct NullPropagationTransform + readonly struct NullPropagationTransform { internal static bool IsProtectedIfInst(IfInstruction ifInst) { @@ -193,6 +186,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms return chainLength >= 1; } else if (inst.MatchLdFld(out var target, out _)) { inst = target; + } else if (inst.MatchLdFlda(out target, out var f)) { + if (target is AddressOf addressOf && f.DeclaringType.Kind == TypeKind.Struct) { + inst = addressOf.Value; + } else { + inst = target; + } } else if (inst is CallInstruction call && call.OpCode != OpCode.NewObj) { if (call.Arguments.Count == 0) { return false; @@ -214,6 +213,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms inst = ldLen.Array; } else if (inst is NullableUnwrap unwrap) { inst = unwrap.Argument; + if (unwrap.RefInput && inst is AddressOf addressOf) { + inst = addressOf.Value; + } } else if (inst is DynamicGetMemberInstruction dynGetMember) { inst = dynGetMember.Target; } else if (inst is DynamicInvokeMemberInstruction dynInvokeMember) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs index 85e2bcc76..bc3e05151 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs @@ -66,10 +66,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// VS2017.8 / Roslyn 2.9 started optimizing some cases of - /// "a.GetValueOrDefault() == b.GetValueOrDefault() && (a.HasValue & b.HasValue)" + /// "a.GetValueOrDefault() == b.GetValueOrDefault() && (a.HasValue & b.HasValue)" /// to - /// "(a.GetValueOrDefault() == b.GetValueOrDefault()) & (a.HasValue & b.HasValue)" - /// so this secondary entry point analyses logic.and as-if it was a short-circuting &&. + /// "(a.GetValueOrDefault() == b.GetValueOrDefault()) & (a.HasValue & b.HasValue)" + /// so this secondary entry point analyses logic.and as-if it was a short-circuting &&. /// public bool Run(BinaryNumericInstruction bni) { @@ -85,7 +85,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms public bool RunStatements(Block block, int pos) { - /// e.g.: + // e.g.: // if (!condition) Block { // leave IL_0000 (default.value System.Nullable`1[[System.Int64]]) // } @@ -541,7 +541,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// Performs nullable lifting. /// /// Produces a lifted instruction with semantics equivalent to: - /// (v1 != null && ... && vn != null) ? trueInst : falseInst, + /// (v1 != null && ... && vn != null) ? trueInst : falseInst, /// where the v1,...,vn are the this.nullableVars. /// If lifting fails, returns null. /// @@ -710,7 +710,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return (newInst, bits); } } else if (inst is Comp comp && !comp.IsLifted && comp.Kind == ComparisonKind.Equality - && MatchGetValueOrDefault(comp.Left, out ILVariable v) + && MatchGetValueOrDefault(comp.Left, out ILVariable v) && nullableVars.Contains(v) && NullableType.GetUnderlyingType(v.Type).IsKnownType(KnownTypeCode.Boolean) && comp.Right.MatchLdcI4(0) ) { @@ -833,7 +833,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Matches 'logic.not(call get_HasValue(ldloca v))' /// - static bool MatchNegatedHasValueCall(ILInstruction inst, ILVariable v) + internal static bool MatchNegatedHasValueCall(ILInstruction inst, ILVariable v) { return inst.MatchLogicNot(out var arg) && MatchHasValueCall(arg, v); } diff --git a/ICSharpCode.Decompiler/IL/Transforms/ProxyCallReplacer.cs b/ICSharpCode.Decompiler/IL/Transforms/ProxyCallReplacer.cs index 6b5383150..d9279f1bb 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ProxyCallReplacer.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ProxyCallReplacer.cs @@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // partially copied from CSharpDecompiler var ilReader = context.CreateILReader(); var body = context.PEFile.Reader.GetMethodBody(methodDef.RelativeVirtualAddress); - var proxyFunction = ilReader.ReadIL(handle, body, genericContext.Value, context.CancellationToken); + var proxyFunction = ilReader.ReadIL(handle, body, genericContext.Value, ILFunctionKind.TopLevelFunction, context.CancellationToken); var transformContext = new ILTransformContext(context, proxyFunction); proxyFunction.RunTransforms(CSharp.CSharpDecompiler.EarlyILTransforms(), transformContext); if (!(proxyFunction.Body is BlockContainer blockContainer)) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs index 03e3297f3..e73fab690 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs @@ -194,6 +194,18 @@ namespace ICSharpCode.Decompiler.IL // if (cond) { ...; exit; } // ...; exit; EnsureEndPointUnreachable(ifInst.TrueInst, exitInst); + if (ifInst.FalseInst.HasFlag(InstructionFlags.EndPointUnreachable)) { + Debug.Assert(ifInst.HasFlag(InstructionFlags.EndPointUnreachable)); + Debug.Assert(ifInst.Parent == block); + int removeAfter = ifInst.ChildIndex + 1; + if (removeAfter < block.Instructions.Count) { + // Remove all instructions that ended up dead + // (this should just be exitInst itself) + Debug.Assert(block.Instructions.SecondToLastOrDefault() == ifInst); + Debug.Assert(block.Instructions.Last() == exitInst); + block.Instructions.RemoveRange(removeAfter, block.Instructions.Count - removeAfter); + } + } ExtractElseBlock(ifInst); ifInst = elseIfInst; } while (ifInst != null); @@ -216,6 +228,8 @@ namespace ICSharpCode.Decompiler.IL var defaultSection = switchInst.Sections.MaxBy(s => s.Labels.Count()); if (!defaultSection.Body.MatchBranch(out var defaultBlock) || defaultBlock.IncomingEdgeCount != 1) return false; + if (defaultBlock.Parent != switchContainer) + return false; // tally stats for heuristic from each case block int maxStatements = 0, maxDepth = 0; diff --git a/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs b/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs index ff8f08ac4..43cb2e577 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs @@ -35,24 +35,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms { public void Run(ILFunction function, ILTransformContext context) { - var visitor = new DefiniteAssignmentVisitor(function, context.CancellationToken); - function.Body.AcceptVisitor(visitor); - foreach (var v in function.Variables) { - if (v.Kind != VariableKind.Parameter && !visitor.IsPotentiallyUsedUninitialized(v)) { - v.HasInitialValue = false; - } - } + ResetHasInitialValueFlag(function, context); // Remove dead stores to variables that are never read from. // If the stored value has some side-effect, the value is unwrapped. // This is necessary to remove useless stores generated by some compilers, e.g., the F# compiler. // In yield return + async, the C# compiler tends to store null/default(T) to variables // when the variable goes out of scope. - if (function.IsAsync || function.IsIterator || context.Settings.RemoveDeadCode) { + if (function.IsAsync || function.IsIterator || context.Settings.RemoveDeadStores) { var variableQueue = new Queue(function.Variables); while (variableQueue.Count > 0) { var v = variableQueue.Dequeue(); if (v.Kind != VariableKind.Local && v.Kind != VariableKind.StackSlot) continue; + // Skip variables that are captured in a mcs yield state-machine + // loads of these will only be visible after DelegateConstruction step. + if (function.StateMachineCompiledWithMono && v.StateMachineField != null) + continue; if (v.LoadCount != 0 || v.AddressCount != 0) continue; foreach (var stloc in v.StoreInstructions.OfType().ToArray()) { @@ -90,5 +88,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } } + + internal static void ResetHasInitialValueFlag(ILFunction function, ILTransformContext context) + { + var visitor = new DefiniteAssignmentVisitor(function, context.CancellationToken); + function.AcceptVisitor(visitor); + foreach (var v in function.Variables) { + if (v.Kind != VariableKind.Parameter && !visitor.IsPotentiallyUsedUninitialized(v)) { + v.HasInitialValue = false; + } + } + } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs b/ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs index 4dcf0720a..850f50fc1 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs @@ -109,7 +109,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms // Address stored in local variable: also check all uses of that variable. if (!(stloc.Variable.Kind == VariableKind.StackSlot || stloc.Variable.Kind == VariableKind.Local)) return AddressUse.Unknown; - if (stloc.Value.OpCode != OpCode.LdLoca) { + var value = stloc.Value; + while (value is LdFlda ldFlda) { + value = ldFlda.Target; + } + if (value.OpCode != OpCode.LdLoca) { // GroupStores.HandleLoad() only detects ref-locals when they are directly initialized with ldloca return AddressUse.Unknown; } @@ -162,7 +166,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms return null; // only single-definition variables can be supported ref locals var store = ldloc.Variable.StoreInstructions.SingleOrDefault(); if (store is StLoc stloc) { - return stloc.Value as LdLoca; + var value = stloc.Value; + while (value is LdFlda ldFlda) { + value = ldFlda.Target; + } + return value as LdLoca; } return null; } diff --git a/ICSharpCode.Decompiler/IL/Transforms/Stepper.cs b/ICSharpCode.Decompiler/IL/Transforms/Stepper.cs index 87175e5c2..0645066bc 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/Stepper.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/Stepper.cs @@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { /// - /// Exception thrown when an IL transform runs into the limit. + /// Exception thrown when an IL transform runs into the . /// public class StepLimitReachedException : Exception { diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index cb3a2d6b8..7c8575c20 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -409,11 +409,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!switchValueVar.Type.IsKnownType(KnownTypeCode.String)) return false; // either br nullCase or leave container - var leaveContainer = BlockContainer.FindClosestContainer(instructions[i]); - if (leaveContainer.Parent is TryInstruction) { - leaveContainer = BlockContainer.FindClosestContainer(leaveContainer.Parent); - } - if (!exitBlockJump.MatchBranch(out var nullValueCaseBlock) && !exitBlockJump.MatchLeave(leaveContainer)) + BlockContainer leaveContainer = null; + if (!exitBlockJump.MatchBranch(out var nullValueCaseBlock) && !exitBlockJump.MatchLeave(out leaveContainer)) return false; var nextBlockJump = instructions.ElementAtOrDefault(i + 1) as Branch; if (nextBlockJump == null || nextBlockJump.TargetBlock.IncomingEdgeCount != 1) @@ -823,8 +820,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms // extract target block if (!section.Body.MatchBranch(out Block target)) return false; - if (!MatchRoslynCaseBlockHead(target, switchValueLoad.Variable, out ILInstruction targetOrLeave, out Block currentExitBlock, out string stringValue)) + string stringValue; + if (MatchRoslynEmptyStringCaseBlockHead(target, switchValueLoad.Variable, out ILInstruction targetOrLeave, out Block currentExitBlock)) { + stringValue = ""; + } else if (!MatchRoslynCaseBlockHead(target, switchValueLoad.Variable, out targetOrLeave, out currentExitBlock, out stringValue)) { return false; + } + if (exitOrDefaultBlock != null && exitOrDefaultBlock != currentExitBlock) return false; exitOrDefaultBlock = currentExitBlock; @@ -905,7 +907,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Matches (and the negated version): - /// if (call op_Equality(ldloc V_0, ldstr "Fifth case")) br body + /// if (call op_Equality(ldloc switchValueVar, stringValue)) br body /// br exit /// bool MatchRoslynCaseBlockHead(Block target, ILVariable switchValueVar, out ILInstruction bodyOrLeave, out Block defaultOrExitBlock, out string stringValue) @@ -941,6 +943,72 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } + /// + /// Block target(incoming: 1) { + /// if (comp.o(ldloc switchValueVar == ldnull)) br exit + /// br lengthCheckBlock + /// } + /// + /// Block lengthCheckBlock(incoming: 1) { + /// if (logic.not(call get_Length(ldloc switchValueVar))) br body + /// br exit + /// } + /// + bool MatchRoslynEmptyStringCaseBlockHead(Block target, ILVariable switchValueVar, out ILInstruction bodyOrLeave, out Block defaultOrExitBlock) + { + bodyOrLeave = null; + defaultOrExitBlock = null; + if (target.Instructions.Count != 2 || target.IncomingEdgeCount != 1) + return false; + if (!target.Instructions[0].MatchIfInstruction(out var nullComparisonCondition, out var exitBranch)) + return false; + if (!nullComparisonCondition.MatchCompEqualsNull(out var arg) || !arg.MatchLdLoc(switchValueVar)) + return false; + if (!target.Instructions[1].MatchBranch(out Block lengthCheckBlock)) + return false; + if (lengthCheckBlock.Instructions.Count != 2 || lengthCheckBlock.IncomingEdgeCount != 1) + return false; + if (!lengthCheckBlock.Instructions[0].MatchIfInstruction(out var lengthCheckCondition, out var exitBranch2)) + return false; + ILInstruction bodyBranch; + if (lengthCheckCondition.MatchLogicNot(out arg)) { + bodyBranch = exitBranch2; + exitBranch2 = lengthCheckBlock.Instructions[1]; + lengthCheckCondition = arg; + } else { + bodyBranch = lengthCheckBlock.Instructions[1]; + } + if (!(exitBranch2.MatchBranch(out defaultOrExitBlock) || exitBranch2.MatchLeave(out _))) + return false; + if (!MatchStringLengthCall(lengthCheckCondition, switchValueVar)) + return false; + if (!(exitBranch.MatchBranch(out defaultOrExitBlock) && exitBranch2.MatchBranch(defaultOrExitBlock))) + return false; + if (bodyBranch.MatchLeave(out _)) { + bodyOrLeave = bodyBranch; + return true; + } + if (bodyBranch.MatchBranch(out var bodyBlock)) { + bodyOrLeave = bodyBlock; + return true; + } + return false; + } + + /// + /// call get_Length(ldloc switchValueVar) + /// + bool MatchStringLengthCall(ILInstruction inst, ILVariable switchValueVar) + { + return inst is Call call + && call.Method.DeclaringType.IsKnownType(KnownTypeCode.String) + && call.Method.IsAccessor + && call.Method.AccessorKind == System.Reflection.MethodSemanticsAttributes.Getter + && call.Method.AccessorOwner.Name == "Length" + && call.Arguments.Count == 1 + && call.Arguments[0].MatchLdLoc(switchValueVar); + } + /// /// Matches 'stloc(targetVar, call ComputeStringHash(ldloc switchValue))' /// diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs index 8d4ff3e14..6a9d149c1 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs @@ -45,6 +45,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return; if (context.Settings.StackAllocInitializers && DoTransformStackAllocInitializer(block, pos)) return; + if (DoTransformInlineRuntimeHelpersInitializeArray(block, pos)) + return; } finally { this.context = null; } @@ -56,7 +58,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; ILInstruction inst = body.Instructions[pos]; if (inst.MatchStLoc(out var v, out var newarrExpr) && MatchNewArr(newarrExpr, out var elementType, out var arrayLength)) { - if (HandleRuntimeHelperInitializeArray(body, pos + 1, v, elementType, arrayLength, out var values, out var initArrayPos)) { + if (HandleRuntimeHelpersInitializeArray(body, pos + 1, v, elementType, arrayLength, out var values, out var initArrayPos)) { context.Step("HandleRuntimeHelperInitializeArray: single-dim", inst); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type); var block = BlockFromInitializer(tempStore, elementType, arrayLength, values); @@ -146,8 +148,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; ILInstruction inst = body.Instructions[pos]; if (inst.MatchStLoc(out var v, out var newarrExpr) && MatchNewArr(newarrExpr, out var elementType, out var length)) { - if (HandleRuntimeHelperInitializeArray(body, pos + 1, v, elementType, length, out var values, out var initArrayPos)) { - context.Step("HandleRuntimeHelperInitializeArray: multi-dim", inst); + if (HandleRuntimeHelpersInitializeArray(body, pos + 1, v, elementType, length, out var values, out var initArrayPos)) { + context.Step("HandleRuntimeHelpersInitializeArray: multi-dim", inst); var block = BlockFromInitializer(v, elementType, length, values); body.Instructions[pos].ReplaceWith(new StLoc(v, block)); body.Instructions.RemoveAt(initArrayPos); @@ -534,7 +536,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - bool MatchInitializeArrayCall(ILInstruction instruction, out ILVariable array, out FieldDefinition field) + bool MatchInitializeArrayCall(ILInstruction instruction, out ILInstruction array, out FieldDefinition field) { array = null; field = default; @@ -549,8 +551,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { return false; } - if (!call.Arguments[0].MatchLdLoc(out array)) - return false; + array = call.Arguments[0]; if (!call.Arguments[1].MatchLdMemberToken(out var member)) return false; if (member.MetadataToken.IsNil) @@ -559,9 +560,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - bool HandleRuntimeHelperInitializeArray(Block body, int pos, ILVariable array, IType arrayType, int[] arrayLength, out ILInstruction[] values, out int foundPos) + bool HandleRuntimeHelpersInitializeArray(Block body, int pos, ILVariable array, IType arrayType, int[] arrayLength, out ILInstruction[] values, out int foundPos) { - if (MatchInitializeArrayCall(body.Instructions[pos], out var v2, out var field) && array == v2) { + if (MatchInitializeArrayCall(body.Instructions[pos], out var arrayInst, out var field) && arrayInst.MatchLdLoc(array)) { if (field.HasFlag(System.Reflection.FieldAttributes.HasFieldRVA)) { var valuesList = new List(); var initialValue = field.GetInitialValue(context.PEFile.Reader, context.TypeSystem); @@ -577,30 +578,60 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } + /// + /// call InitializeArray(newarr T(size), ldmembertoken fieldToken) + /// => + /// Block (ArrayInitializer) { + /// stloc i(newarr T(size)) + /// stobj T(ldelema T(... indices ...), value) + /// final: ldloc i + /// } + /// + bool DoTransformInlineRuntimeHelpersInitializeArray(Block body, int pos) + { + var inst = body.Instructions[pos]; + if (!MatchInitializeArrayCall(inst, out var arrayInst, out var field)) + return false; + if (!MatchNewArr(arrayInst, out var elementType, out var arrayLength)) + return false; + if (!field.HasFlag(System.Reflection.FieldAttributes.HasFieldRVA)) + return false; + var valuesList = new List(); + var initialValue = field.GetInitialValue(context.PEFile.Reader, context.TypeSystem); + if (!DecodeArrayInitializer(elementType, initialValue, arrayLength, valuesList)) + return false; + context.Step("InlineRuntimeHelpersInitializeArray: single-dim", inst); + var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new ArrayType(context.TypeSystem, elementType, arrayLength.Length)); + var block = BlockFromInitializer(tempStore, elementType, arrayLength, valuesList.ToArray()); + body.Instructions[pos] = block; + ILInlining.InlineIfPossible(body, pos, context); + return true; + } + static bool DecodeArrayInitializer(IType type, BlobReader initialValue, int[] arrayLength, List output) { TypeCode typeCode = ReflectionHelper.GetTypeCode(type); switch (typeCode) { case TypeCode.Boolean: case TypeCode.Byte: - return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcI4(r.ReadByte())); + return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcI4(r.ReadByte())); case TypeCode.SByte: - return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcI4(r.ReadSByte())); + return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcI4(r.ReadSByte())); case TypeCode.Int16: - return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcI4(r.ReadInt16())); + return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcI4(r.ReadInt16())); case TypeCode.Char: case TypeCode.UInt16: - return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcI4(r.ReadUInt16())); + return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcI4(r.ReadUInt16())); case TypeCode.Int32: case TypeCode.UInt32: - return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcI4(r.ReadInt32())); + return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcI4(r.ReadInt32())); case TypeCode.Int64: case TypeCode.UInt64: - return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcI8(r.ReadInt64())); + return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcI8(r.ReadInt64())); case TypeCode.Single: - return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcF4(r.ReadSingle())); + return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcF4(r.ReadSingle())); case TypeCode.Double: - return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcF8(r.ReadDouble())); + return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcF8(r.ReadDouble())); case TypeCode.Object: case TypeCode.Empty: var typeDef = type.GetDefinition(); @@ -615,7 +646,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms delegate ILInstruction ValueDecoder(ref BlobReader reader); static bool DecodeArrayInitializer(BlobReader initialValue, int[] arrayLength, - List output, TypeCode elementType, IType type, ValueDecoder decoder) + List output, TypeCode elementType, ValueDecoder decoder) { int elementSize = ElementSizeOf(elementType); var totalLength = arrayLength.Aggregate(1, (t, l) => t * l); @@ -627,7 +658,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms int next = i; for (int j = arrayLength.Length - 1; j >= 0; j--) { output.Add(new LdcI4(next % arrayLength[j])); - next = next / arrayLength[j]; + next /= arrayLength[j]; } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index 1f337c420..de201b887 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -28,6 +28,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Constructs compound assignments and inline assignments. /// + /// + /// This is a statement transform; + /// but some portions are executed as an expression transform instead + /// (with HandleCompoundAssign() as entry point) + /// public class TransformAssignment : IStatementTransform { StatementTransformContext context; @@ -44,8 +49,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } if (context.Settings.IntroduceIncrementAndDecrement) { if (TransformPostIncDecOperatorWithInlineStore(block, pos) - || TransformPostIncDecOperator(block, pos) - || TransformPostIncDecOperatorLocal(block, pos)) { + || TransformPostIncDecOperator(block, pos)) { // again, new top-level stloc might need inlining: context.RequestRerun(); return; @@ -175,10 +179,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // because the ExpressionTransforms don't look into inline blocks, manually trigger HandleCallCompoundAssign if (HandleCompoundAssign(call, context)) { // if we did construct a compound assignment, it should have made our inline block redundant: - if (inlineBlock.Instructions.Single().MatchStLoc(newVar, out var compoundAssign)) { - Debug.Assert(newVar.IsSingleDefinition && newVar.LoadCount == 1); - inlineBlock.ReplaceWith(compoundAssign); - } + Debug.Assert(!inlineBlock.IsConnected); } return true; } else { @@ -206,8 +207,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - static bool MatchingGetterAndSetterCalls(CallInstruction getterCall, CallInstruction setterCall) + static bool MatchingGetterAndSetterCalls(CallInstruction getterCall, CallInstruction setterCall, out Action finalizeMatch) { + finalizeMatch = null; if (getterCall == null || setterCall == null || !IsSameMember(getterCall.Method.AccessorOwner, setterCall.Method.AccessorOwner)) return false; if (setterCall.OpCode != getterCall.OpCode) @@ -219,12 +221,33 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; // Ensure that same arguments are passed to getterCall and setterCall: for (int j = 0; j < getterCall.Arguments.Count; j++) { + if (setterCall.Arguments[j].MatchStLoc(out var v) && v.IsSingleDefinition && v.LoadCount == 1) { + if (getterCall.Arguments[j].MatchLdLoc(v)) { + // OK, setter call argument is saved in temporary that is re-used for getter call + if (finalizeMatch == null) { + finalizeMatch = AdjustArguments; + } + continue; + } + } if (!SemanticHelper.IsPure(getterCall.Arguments[j].Flags)) return false; if (!getterCall.Arguments[j].Match(setterCall.Arguments[j]).Success) return false; } return true; + + void AdjustArguments(ILTransformContext context) + { + Debug.Assert(setterCall.Arguments.Count == getterCall.Arguments.Count + 1); + for (int j = 0; j < getterCall.Arguments.Count; j++) { + if (setterCall.Arguments[j].MatchStLoc(out var v, out var value)) { + Debug.Assert(v.IsSingleDefinition && v.LoadCount == 1); + Debug.Assert(getterCall.Arguments[j].MatchLdLoc(v)); + getterCall.Arguments[j] = value; + } + } + } } /// @@ -276,18 +299,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms } ILInstruction newInst; if (UnwrapSmallIntegerConv(setterValue, out var smallIntConv) is BinaryNumericInstruction binary) { - if (!IsMatchingCompoundLoad(binary.Left, compoundStore, forbiddenVariable: storeInSetter?.Variable)) + if (compoundStore is StLoc) { + // transform local variables only for user-defined operators + return false; + } + if (!IsMatchingCompoundLoad(binary.Left, compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable)) return false; if (!ValidateCompoundAssign(binary, smallIntConv, targetType)) return false; context.Step($"Compound assignment (binary.numeric)", compoundStore); + finalizeMatch?.Invoke(context); newInst = new NumericCompoundAssign( - binary, binary.Left, binary.Right, - targetType, CompoundAssignmentType.EvaluatesToNewValue); + binary, target, targetKind, binary.Right, + targetType, CompoundEvalMode.EvaluatesToNewValue); } else if (setterValue is Call operatorCall && operatorCall.Method.IsOperator) { if (operatorCall.Arguments.Count == 0) return false; - if (!IsMatchingCompoundLoad(operatorCall.Arguments[0], compoundStore, forbiddenVariable: storeInSetter?.Variable)) + if (!IsMatchingCompoundLoad(operatorCall.Arguments[0], compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable)) return false; ILInstruction rhs; if (operatorCall.Arguments.Count == 2) { @@ -305,24 +333,31 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (operatorCall.IsLifted) return false; // TODO: add tests and think about whether nullables need special considerations context.Step($"Compound assignment (user-defined binary)", compoundStore); - newInst = new UserDefinedCompoundAssign(operatorCall.Method, CompoundAssignmentType.EvaluatesToNewValue, - operatorCall.Arguments[0], rhs); + finalizeMatch?.Invoke(context); + newInst = new UserDefinedCompoundAssign(operatorCall.Method, CompoundEvalMode.EvaluatesToNewValue, + target, targetKind, rhs); } else if (setterValue is DynamicBinaryOperatorInstruction dynamicBinaryOp) { - if (!IsMatchingCompoundLoad(dynamicBinaryOp.Left, compoundStore, forbiddenVariable: storeInSetter?.Variable)) + if (!IsMatchingCompoundLoad(dynamicBinaryOp.Left, compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable)) return false; context.Step($"Compound assignment (dynamic binary)", compoundStore); - newInst = new DynamicCompoundAssign(dynamicBinaryOp.Operation, dynamicBinaryOp.BinderFlags, dynamicBinaryOp.Left, dynamicBinaryOp.LeftArgumentInfo, dynamicBinaryOp.Right, dynamicBinaryOp.RightArgumentInfo); + finalizeMatch?.Invoke(context); + newInst = new DynamicCompoundAssign(dynamicBinaryOp.Operation, dynamicBinaryOp.BinderFlags, target, dynamicBinaryOp.LeftArgumentInfo, dynamicBinaryOp.Right, dynamicBinaryOp.RightArgumentInfo, targetKind); } else if (setterValue is Call concatCall && UserDefinedCompoundAssign.IsStringConcat(concatCall.Method)) { // setterValue is a string.Concat() invocation + if (compoundStore is StLoc) { + // transform local variables only for user-defined operators + return false; + } if (concatCall.Arguments.Count != 2) return false; // for now we only support binary compound assignments if (!targetType.IsKnownType(KnownTypeCode.String)) return false; - if (!IsMatchingCompoundLoad(concatCall.Arguments[0], compoundStore, forbiddenVariable: storeInSetter?.Variable)) + if (!IsMatchingCompoundLoad(concatCall.Arguments[0], compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable)) return false; context.Step($"Compound assignment (string concatenation)", compoundStore); - newInst = new UserDefinedCompoundAssign(concatCall.Method, CompoundAssignmentType.EvaluatesToNewValue, - concatCall.Arguments[0], concatCall.Arguments[1]); + finalizeMatch?.Invoke(context); + newInst = new UserDefinedCompoundAssign(concatCall.Method, CompoundEvalMode.EvaluatesToNewValue, + target, targetKind, concatCall.Arguments[1]); } else { return false; } @@ -333,6 +368,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms context.RequestRerun(); // moving stloc to top-level might trigger inlining } compoundStore.ReplaceWith(newInst); + if (newInst.Parent is Block inlineAssignBlock && inlineAssignBlock.Kind == BlockKind.CallInlineAssign) { + // It's possible that we first replaced the instruction in an inline-assign helper block. + // In such a situation, we know from the block invariant that we're have a storeInSetter. + Debug.Assert(storeInSetter != null); + Debug.Assert(storeInSetter.Variable.IsSingleDefinition && storeInSetter.Variable.LoadCount == 1); + Debug.Assert(inlineAssignBlock.Instructions.Single() == storeInSetter); + Debug.Assert(inlineAssignBlock.FinalInstruction.MatchLdLoc(storeInSetter.Variable)); + // Block CallInlineAssign { stloc I_0(compound.op(...)); final: ldloc I_0 } + // --> compound.op(...) + inlineAssignBlock.ReplaceWith(storeInSetter.Value); + } return true; } @@ -428,48 +474,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - /// - /// stloc s(ldloc l) - /// stloc l(binary.op(ldloc s, ldc.i4 1)) - /// --> - /// stloc s(block { - /// stloc s2(ldloc l) - /// stloc l(binary.op(ldloc s2, ldc.i4 1)) - /// final: ldloc s2 - /// }) - /// - bool TransformPostIncDecOperatorLocal(Block block, int pos) - { - var inst = block.Instructions[pos] as StLoc; - var nextInst = block.Instructions.ElementAtOrDefault(pos + 1) as StLoc; - if (inst == null || nextInst == null || !inst.Value.MatchLdLoc(out var loadVar) || !ILVariableEqualityComparer.Instance.Equals(loadVar, nextInst.Variable)) - return false; - var binary = nextInst.Value as BinaryNumericInstruction; - if (inst.Variable.Kind != VariableKind.StackSlot || nextInst.Variable.Kind == VariableKind.StackSlot || binary == null) - return false; - if (binary.IsLifted) - return false; - if ((binary.Operator != BinaryNumericOperator.Add && binary.Operator != BinaryNumericOperator.Sub) || !binary.Left.MatchLdLoc(inst.Variable) || !binary.Right.MatchLdcI4(1)) - return false; - context.Step($"TransformPostIncDecOperatorLocal", inst); - if (loadVar != nextInst.Variable) { - // load and store are two different variables, that were split from the same variable - context.Function.RecombineVariables(loadVar, nextInst.Variable); - } - var tempStore = context.Function.RegisterVariable(VariableKind.StackSlot, inst.Variable.Type); - var assignment = new Block(BlockKind.PostfixOperator); - assignment.Instructions.Add(new StLoc(tempStore, new LdLoc(loadVar))); - assignment.Instructions.Add(new StLoc(loadVar, new BinaryNumericInstruction(binary.Operator, new LdLoc(tempStore), new LdcI4(1), binary.CheckForOverflow, binary.Sign))); - assignment.FinalInstruction = new LdLoc(tempStore); - inst.Value = assignment; - block.Instructions.RemoveAt(pos + 1); // remove nextInst - return true; - } - /// /// Gets whether 'inst' is a possible store for use as a compound store. /// - static bool IsCompoundStore(ILInstruction inst, out IType storeType, out ILInstruction value, ICompilation compilation) + /// + /// Output parameters: + /// storeType: The type of the value being stored. + /// value: The value being stored (will be analyzed further to detect compound assignments) + /// + /// Every IsCompoundStore() call should be followed by an IsMatchingCompoundLoad() call. + /// + static bool IsCompoundStore(ILInstruction inst, out IType storeType, + out ILInstruction value, ICompilation compilation) { value = null; storeType = null; @@ -478,9 +494,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms // Try to determine the real type of the object we're modifying: storeType = stobj.Target.InferType(compilation); if (storeType is ByReferenceType refType) { - storeType = refType.ElementType; + if (TypeUtils.IsCompatibleTypeForMemoryAccess(refType.ElementType, stobj.Type)) { + storeType = refType.ElementType; + } else { + storeType = stobj.Type; + } } else if (storeType is PointerType pointerType) { - storeType = pointerType.ElementType; + if (TypeUtils.IsCompatibleTypeForMemoryAccess(pointerType.ElementType, stobj.Type)) { + storeType = pointerType.ElementType; + } else { + storeType = stobj.Type; + } } else { storeType = stobj.Type; } @@ -491,6 +515,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } foreach (var arg in call.Arguments.SkipLast(1)) { + if (arg.MatchStLoc(out var v) && v.IsSingleDefinition && v.LoadCount == 1) { + continue; // OK, IsMatchingCompoundLoad can perform an adjustment in this special case + } if (!SemanticHelper.IsPure(arg.Flags)) { return false; } @@ -498,27 +525,95 @@ namespace ICSharpCode.Decompiler.IL.Transforms storeType = call.Method.Parameters.Last().Type; value = call.Arguments.Last(); return IsSameMember(call.Method, (call.Method.AccessorOwner as IProperty)?.Setter); + } else if (inst is StLoc stloc && (stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.Parameter)) { + storeType = stloc.Variable.Type; + value = stloc.Value; + return true; } else { return false; } } - static bool IsMatchingCompoundLoad(ILInstruction load, ILInstruction store, ILVariable forbiddenVariable) + /// + /// Checks whether 'load' and 'store' both access the same store, and can be combined to a compound assignment. + /// + /// The load instruction to test. + /// The compound store to test against. Must have previously been tested via IsCompoundStore() + /// The target to use for the compound assignment instruction. + /// The target kind to use for the compound assignment instruction. + /// If set to a non-null value, call this delegate to fix up minor mismatches between getter and setter. + /// + /// If given a non-null value, this function returns false if the forbiddenVariable is used in the load/store instructions. + /// Some transforms effectively move a store around, + /// which is only valid if the variable stored to does not occur in the compound load/store. + /// + /// + /// Instruction preceding the load. + /// + static bool IsMatchingCompoundLoad(ILInstruction load, ILInstruction store, + out ILInstruction target, out CompoundTargetKind targetKind, + out Action finalizeMatch, + ILVariable forbiddenVariable = null, + ILInstruction previousInstruction = null) { + target = null; + targetKind = 0; + finalizeMatch = null; if (load is LdObj ldobj && store is StObj stobj) { Debug.Assert(SemanticHelper.IsPure(stobj.Target.Flags)); if (!SemanticHelper.IsPure(ldobj.Target.Flags)) return false; if (forbiddenVariable != null && forbiddenVariable.IsUsedWithin(ldobj.Target)) return false; - return ldobj.Target.Match(stobj.Target).Success; - } else if (MatchingGetterAndSetterCalls(load as CallInstruction, store as CallInstruction)) { + target = ldobj.Target; + targetKind = CompoundTargetKind.Address; + if (ldobj.Target.Match(stobj.Target).Success) { + return true; + } else if (IsDuplicatedAddressComputation(stobj.Target, ldobj.Target)) { + // Use S_0 as target, so that S_0 can later be eliminated by inlining. + // (we can't eliminate previousInstruction right now, because it's before the transform's starting instruction) + target = stobj.Target; + return true; + } else { + return false; + } + } else if (MatchingGetterAndSetterCalls(load as CallInstruction, store as CallInstruction, out finalizeMatch)) { if (forbiddenVariable != null && forbiddenVariable.IsUsedWithin(load)) return false; + target = load; + targetKind = CompoundTargetKind.Property; + return true; + } else if (load is LdLoc ldloc && store is StLoc stloc && ILVariableEqualityComparer.Instance.Equals(ldloc.Variable, stloc.Variable)) { + if (ILVariableEqualityComparer.Instance.Equals(ldloc.Variable, forbiddenVariable)) + return false; + target = new LdLoca(ldloc.Variable).WithILRange(ldloc); + targetKind = CompoundTargetKind.Address; + finalizeMatch = context => context.Function.RecombineVariables(ldloc.Variable, stloc.Variable); return true; } else { return false; } + + bool IsDuplicatedAddressComputation(ILInstruction storeTarget, ILInstruction loadTarget) + { + // Sometimes roslyn duplicates the address calculation: + // stloc S_0(ldloc refParam) + // stloc V_0(ldobj System.Int32(ldloc refParam)) + // stobj System.Int32(ldloc S_0, binary.add.i4(ldloc V_0, ldc.i4 1)) + while (storeTarget is LdFlda storeLdFlda && loadTarget is LdFlda loadLdFlda) { + if (!storeLdFlda.Field.Equals(loadLdFlda.Field)) + return false; + storeTarget = storeLdFlda.Target; + loadTarget = loadLdFlda.Target; + } + if (!storeTarget.MatchLdLoc(out var s)) + return false; + if (!(s.Kind == VariableKind.StackSlot && s.IsSingleDefinition && s != forbiddenVariable)) + return false; + if (s.StoreInstructions.SingleOrDefault() != previousInstruction) + return false; + return previousInstruction is StLoc addressStore && addressStore.Value.Match(loadTarget).Success; + } } /// @@ -543,8 +638,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms bool TransformPostIncDecOperatorWithInlineStore(Block block, int pos) { var store = block.Instructions[pos]; - if (!IsCompoundStore(store, out var targetType, out var value, context.TypeSystem)) + if (!IsCompoundStore(store, out var targetType, out var value, context.TypeSystem)) { return false; + } StLoc stloc; var binary = UnwrapSmallIntegerConv(value, out var conv) as BinaryNumericInstruction; if (binary != null && binary.Right.MatchLdcI(1)) { @@ -566,28 +662,38 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.StackSlot)) return false; - if (!IsMatchingCompoundLoad(stloc.Value, store, stloc.Variable)) + if (!IsMatchingCompoundLoad(stloc.Value, store, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: stloc.Variable)) return false; if (IsImplicitTruncation(stloc.Value, stloc.Variable.Type, context.TypeSystem)) return false; context.Step("TransformPostIncDecOperatorWithInlineStore", store); + finalizeMatch?.Invoke(context); if (binary != null) { block.Instructions[pos] = new StLoc(stloc.Variable, new NumericCompoundAssign( - binary, stloc.Value, binary.Right, targetType, CompoundAssignmentType.EvaluatesToOldValue)); + binary, target, targetKind, binary.Right, targetType, CompoundEvalMode.EvaluatesToOldValue)); } else { Call operatorCall = (Call)value; block.Instructions[pos] = new StLoc(stloc.Variable, new UserDefinedCompoundAssign( - operatorCall.Method, CompoundAssignmentType.EvaluatesToOldValue, stloc.Value, new LdcI4(1))); + operatorCall.Method, CompoundEvalMode.EvaluatesToOldValue, target, targetKind, new LdcI4(1))); } return true; } /// - /// stloc l(ldobj(target)) - /// stobj(target, binary.op(ldloc l, ldc.i4 1)) - /// target is pure and does not use 'l', 'stloc does not truncate' + /// stloc tmp(ldobj(target)) + /// stobj(target, binary.op(ldloc tmp, ldc.i4 1)) + /// target is pure and does not use 'tmp', 'stloc does not truncate' /// --> - /// stloc l(compound.op.old(ldobj(target), ldc.i4 1)) + /// stloc tmp(compound.op.old(ldobj(target), ldc.i4 1)) + /// + /// This is usually followed by inlining or eliminating 'tmp'. + /// + /// Local variables use a similar pattern, also detected by this function: + /// + /// stloc tmp(ldloc target) + /// stloc target(binary.op(ldloc tmp, ldc.i4 1)) + /// --> + /// stloc tmp(compound.op.old(ldloca target, ldc.i4 1)) /// /// /// This pattern occurs with legacy csc for static fields, and with Roslyn for most post-increments. @@ -598,34 +704,40 @@ namespace ICSharpCode.Decompiler.IL.Transforms var store = block.Instructions.ElementAtOrDefault(i + 1); if (inst == null || store == null) return false; + var tmpVar = inst.Variable; if (!IsCompoundStore(store, out var targetType, out var value, context.TypeSystem)) return false; if (IsImplicitTruncation(inst.Value, targetType, context.TypeSystem)) { - // 'stloc l' is implicitly truncating the value + // 'stloc tmp' is implicitly truncating the value return false; } - if (!IsMatchingCompoundLoad(inst.Value, store, inst.Variable)) + if (!IsMatchingCompoundLoad(inst.Value, store, out var target, out var targetKind, out var finalizeMatch, + forbiddenVariable: inst.Variable, + previousInstruction: block.Instructions.ElementAtOrDefault(i - 1))) { return false; + } if (UnwrapSmallIntegerConv(value, out var conv) is BinaryNumericInstruction binary) { - if (!binary.Left.MatchLdLoc(inst.Variable) || !binary.Right.MatchLdcI(1)) + if (!binary.Left.MatchLdLoc(tmpVar) || !binary.Right.MatchLdcI(1)) return false; if (!(binary.Operator == BinaryNumericOperator.Add || binary.Operator == BinaryNumericOperator.Sub)) return false; if (!ValidateCompoundAssign(binary, conv, targetType)) return false; context.Step("TransformPostIncDecOperator (builtin)", inst); - inst.Value = new NumericCompoundAssign(binary, inst.Value, binary.Right, - targetType, CompoundAssignmentType.EvaluatesToOldValue); + finalizeMatch?.Invoke(context); + inst.Value = new NumericCompoundAssign(binary, target, targetKind, binary.Right, + targetType, CompoundEvalMode.EvaluatesToOldValue); } else if (value is Call operatorCall && operatorCall.Method.IsOperator && operatorCall.Arguments.Count == 1) { - if (!operatorCall.Arguments[0].MatchLdLoc(inst.Variable)) + if (!operatorCall.Arguments[0].MatchLdLoc(tmpVar)) return false; if (!(operatorCall.Method.Name == "op_Increment" || operatorCall.Method.Name == "op_Decrement")) return false; if (operatorCall.IsLifted) return false; // TODO: add tests and think about whether nullables need special considerations context.Step("TransformPostIncDecOperator (user-defined)", inst); + finalizeMatch?.Invoke(context); inst.Value = new UserDefinedCompoundAssign(operatorCall.Method, - CompoundAssignmentType.EvaluatesToOldValue, inst.Value, new LdcI4(1)); + CompoundEvalMode.EvaluatesToOldValue, target, targetKind, new LdcI4(1)); } else { return false; } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs index c982f5f35..894194b43 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs @@ -62,9 +62,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms } // Do not try to transform display class usages or delegate construction. // DelegateConstruction transform cannot deal with this. - if (DelegateConstruction.IsSimpleDisplayClass(newObjInst.Method.DeclaringType)) + if (TransformDisplayClassUsage.IsSimpleDisplayClass(newObjInst.Method.DeclaringType)) return false; - if (DelegateConstruction.IsDelegateConstruction(newObjInst) || DelegateConstruction.IsPotentialClosure(context, newObjInst)) + if (DelegateConstruction.IsDelegateConstruction(newObjInst) || TransformDisplayClassUsage.IsPotentialClosure(context, newObjInst)) return false; // Cannot build a collection/object initializer attached to an AnonymousTypeCreateExpression:s // anon = new { A = 5 } { 3,4,5 } is invalid syntax. @@ -216,15 +216,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms case AccessPathKind.Setter: if (isCollection || !pathStack.Peek().Add(lastElement)) return false; - if (values.Count == 1) { - blockKind = BlockKind.ObjectInitializer; - return true; - } - return false; + if (values.Count != 1 || !IsValidObjectInitializerTarget(currentPath)) + return false; + blockKind = BlockKind.ObjectInitializer; + return true; default: return false; } } + + bool IsValidObjectInitializerTarget(List path) + { + if (path.Count == 0) + return true; + var element = path.Last(); + var previous = path.SkipLast(1).LastOrDefault(); + if (!(element.Member is IProperty p)) + return true; + return !p.IsIndexer || (previous.Member?.ReturnType.Equals(element.Member.DeclaringType) == true); + } } public enum AccessPathKind diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs new file mode 100644 index 000000000..d892b9c48 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -0,0 +1,385 @@ +// Copyright (c) 2019 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; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using ICSharpCode.Decompiler.TypeSystem; + +namespace ICSharpCode.Decompiler.IL.Transforms +{ + /// + /// Transforms closure fields to local variables. + /// + /// This is a post-processing step of , and . + /// + class TransformDisplayClassUsage : ILVisitor, IILTransform + { + class DisplayClass + { + public bool IsMono; + public ILInstruction Initializer; + public ILVariable Variable; + public ITypeDefinition Definition; + public Dictionary Variables; + public BlockContainer CaptureScope; + public ILFunction DeclaringFunction; + } + + struct DisplayClassVariable + { + public ILVariable Variable; + public ILInstruction Value; + } + + ILTransformContext context; + ILFunction currentFunction; + readonly Dictionary displayClasses = new Dictionary(); + readonly List instructionsToRemove = new List(); + + public void Run(ILFunction function, ILTransformContext context) + { + try { + if (this.context != null || this.currentFunction != null) + throw new InvalidOperationException("Reentrancy in " + nameof(TransformDisplayClassUsage)); + this.context = context; + var decompilationContext = new SimpleTypeResolveContext(context.Function.Method); + // Traverse nested functions in post-order: + // Inner functions are transformed before outer functions + foreach (var f in function.Descendants.OfType()) { + foreach (var v in f.Variables.ToArray()) { + if (HandleMonoStateMachine(function, v, decompilationContext, f)) + continue; + if (IsClosure(v, out ITypeDefinition closureType, out var inst)) { + AddOrUpdateDisplayClass(f, v, closureType, inst, localFunctionClosureParameter: false); + } + if (context.Settings.LocalFunctions && f.Kind == ILFunctionKind.LocalFunction && v.Kind == VariableKind.Parameter && v.Index > -1 && f.Method.Parameters[v.Index.Value] is IParameter p && LocalFunctionDecompiler.IsClosureParameter(p, decompilationContext)) { + AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body, localFunctionClosureParameter: true); + } + } + foreach (var displayClass in displayClasses.Values.OrderByDescending(d => d.Initializer.StartILOffset).ToArray()) { + context.Step($"Transform references to " + displayClass.Variable, displayClass.Initializer); + this.currentFunction = f; + VisitILFunction(f); + } + } + if (instructionsToRemove.Count > 0) { + context.Step($"Remove instructions", function); + foreach (var store in instructionsToRemove) { + if (store.Parent is Block containingBlock) + containingBlock.Instructions.Remove(store); + } + } + RemoveDeadVariableInit.ResetHasInitialValueFlag(function, context); + } finally { + instructionsToRemove.Clear(); + displayClasses.Clear(); + this.context = null; + this.currentFunction = null; + } + } + + private void AddOrUpdateDisplayClass(ILFunction f, ILVariable v, ITypeDefinition closureType, ILInstruction inst, bool localFunctionClosureParameter) + { + var displayClass = displayClasses.Values.FirstOrDefault(c => c.Definition == closureType); + // TODO : figure out whether it is a mono compiled closure, without relying on the type name + bool isMono = f.StateMachineCompiledWithMono || closureType.Name.Contains("AnonStorey"); + if (displayClass == null) { + displayClasses.Add(v, new DisplayClass { + IsMono = isMono, + Initializer = inst, + Variable = v, + Definition = closureType, + Variables = new Dictionary(), + CaptureScope = (isMono && IsMonoNestedCaptureScope(closureType)) || localFunctionClosureParameter ? null : v.CaptureScope, + DeclaringFunction = localFunctionClosureParameter ? f.DeclarationScope.Ancestors.OfType().First() : f + }); + } else { + if (displayClass.CaptureScope == null && !localFunctionClosureParameter) + displayClass.CaptureScope = isMono && IsMonoNestedCaptureScope(closureType) ? null : v.CaptureScope; + if (displayClass.CaptureScope != null && !localFunctionClosureParameter) { + displayClass.DeclaringFunction = displayClass.CaptureScope.Ancestors.OfType().First(); + } + displayClass.Variable = v; + displayClass.Initializer = inst; + displayClasses.Add(v, displayClass); + } + } + + bool IsClosure(ILVariable variable, out ITypeDefinition closureType, out ILInstruction initializer) + { + closureType = null; + initializer = null; + if (variable.IsSingleDefinition && variable.StoreInstructions.SingleOrDefault() is StLoc inst) { + initializer = inst; + if (IsClosureInit(inst, out closureType)) { + instructionsToRemove.Add(inst); + return true; + } + } + closureType = variable.Type.GetDefinition(); + if (context.Settings.LocalFunctions && closureType?.Kind == TypeKind.Struct && variable.HasInitialValue && IsPotentialClosure(this.context, closureType)) { + initializer = LocalFunctionDecompiler.GetStatement(variable.AddressInstructions.OrderBy(i => i.StartILOffset).First()); + return true; + } + return false; + } + + bool IsClosureInit(StLoc inst, out ITypeDefinition closureType) + { + if (inst.Value is NewObj newObj) { + closureType = newObj.Method.DeclaringTypeDefinition; + return closureType != null && IsPotentialClosure(this.context, newObj); + } + closureType = null; + return false; + } + + bool IsOuterClosureReference(IField field) + { + return displayClasses.Values.Any(disp => disp.Definition == field.DeclaringTypeDefinition); + } + + bool IsMonoNestedCaptureScope(ITypeDefinition closureType) + { + var decompilationContext = new SimpleTypeResolveContext(context.Function.Method); + return closureType.Fields.Any(f => IsPotentialClosure(decompilationContext.CurrentTypeDefinition, f.ReturnType.GetDefinition())); + } + + /// + /// mcs likes to optimize closures in yield state machines away by moving the captured variables' fields into the state machine type, + /// We construct a that spans the whole method body. + /// + bool HandleMonoStateMachine(ILFunction currentFunction, ILVariable thisVariable, SimpleTypeResolveContext decompilationContext, ILFunction nestedFunction) + { + if (!(nestedFunction.StateMachineCompiledWithMono && thisVariable.IsThis())) + return false; + // Special case for Mono-compiled yield state machines + ITypeDefinition closureType = thisVariable.Type.GetDefinition(); + if (!(closureType != decompilationContext.CurrentTypeDefinition + && IsPotentialClosure(decompilationContext.CurrentTypeDefinition, closureType, allowTypeImplementingInterfaces: true))) + return false; + + var displayClass = new DisplayClass { + IsMono = true, + Initializer = nestedFunction.Body, + Variable = thisVariable, + Definition = thisVariable.Type.GetDefinition(), + Variables = new Dictionary(), + CaptureScope = (BlockContainer)nestedFunction.Body + }; + displayClasses.Add(thisVariable, displayClass); + foreach (var stateMachineVariable in nestedFunction.Variables) { + if (stateMachineVariable.StateMachineField == null || displayClass.Variables.ContainsKey(stateMachineVariable.StateMachineField)) + continue; + displayClass.Variables.Add(stateMachineVariable.StateMachineField, new DisplayClassVariable { + Variable = stateMachineVariable, + Value = new LdLoc(stateMachineVariable) + }); + } + if (!currentFunction.Method.IsStatic && FindThisField(out var thisField)) { + var thisVar = currentFunction.Variables + .FirstOrDefault(t => t.IsThis() && t.Type.GetDefinition() == decompilationContext.CurrentTypeDefinition); + if (thisVar == null) { + thisVar = new ILVariable(VariableKind.Parameter, decompilationContext.CurrentTypeDefinition, -1) { Name = "this" }; + currentFunction.Variables.Add(thisVar); + } + displayClass.Variables.Add(thisField, new DisplayClassVariable { Variable = thisVar, Value = new LdLoc(thisVar) }); + } + return true; + + bool FindThisField(out IField foundField) + { + foundField = null; + foreach (var field in closureType.GetFields(f2 => !f2.IsStatic && !displayClass.Variables.ContainsKey(f2) && f2.Type.GetDefinition() == decompilationContext.CurrentTypeDefinition)) { + thisField = field; + return true; + } + return false; + } + } + + internal static bool IsSimpleDisplayClass(IType type) + { + if (!type.HasGeneratedName() || (!type.Name.Contains("DisplayClass") && !type.Name.Contains("AnonStorey"))) + return false; + if (type.DirectBaseTypes.Any(t => !t.IsKnownType(KnownTypeCode.Object))) + return false; + return true; + } + + internal static bool IsPotentialClosure(ILTransformContext context, NewObj inst) + { + var decompilationContext = new SimpleTypeResolveContext(context.Function.Ancestors.OfType().Last().Method); + return IsPotentialClosure(decompilationContext.CurrentTypeDefinition, inst.Method.DeclaringTypeDefinition); + } + + internal static bool IsPotentialClosure(ILTransformContext context, ITypeDefinition potentialDisplayClass) + { + var decompilationContext = new SimpleTypeResolveContext(context.Function.Ancestors.OfType().Last().Method); + return IsPotentialClosure(decompilationContext.CurrentTypeDefinition, potentialDisplayClass); + } + + internal static bool IsPotentialClosure(ITypeDefinition decompiledTypeDefinition, ITypeDefinition potentialDisplayClass, bool allowTypeImplementingInterfaces = false) + { + if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) + return false; + switch (potentialDisplayClass.Kind) { + case TypeKind.Struct: + break; + case TypeKind.Class: + if (!potentialDisplayClass.IsSealed) + return false; + if (!allowTypeImplementingInterfaces) { + if (!potentialDisplayClass.DirectBaseTypes.All(t => t.IsKnownType(KnownTypeCode.Object))) + return false; + } + break; + default: + return false; + } + + while (potentialDisplayClass != decompiledTypeDefinition) { + potentialDisplayClass = potentialDisplayClass.DeclaringTypeDefinition; + if (potentialDisplayClass == null) + return false; + } + return true; + } + + bool IsDisplayClassLoad(ILInstruction target, out ILVariable variable) + { + if (target.MatchLdLoc(out variable) || target.MatchLdLoca(out variable)) + return true; + return false; + } + + protected override void Default(ILInstruction inst) + { + foreach (var child in inst.Children) { + child.AcceptVisitor(this); + } + } + + protected internal override void VisitStLoc(StLoc inst) + { + base.VisitStLoc(inst); + // Sometimes display class references are copied into other local variables. + // We remove the assignment and store the relationship between the display class and the variable in the + // displayClasses dictionary. + if (inst.Value.MatchLdLoc(out var closureVariable) && displayClasses.TryGetValue(closureVariable, out var displayClass)) { + displayClasses[inst.Variable] = displayClass; + instructionsToRemove.Add(inst); + } else if (inst.Variable.Kind == VariableKind.Local && inst.Variable.IsSingleDefinition && inst.Variable.LoadCount == 0 && inst.Value is StLoc) { + inst.ReplaceWith(inst.Value); + } + } + + protected internal override void VisitStObj(StObj inst) + { + base.VisitStObj(inst); + // This instruction has been marked deletable, do not transform it further + if (instructionsToRemove.Contains(inst)) + return; + // The target of the store instruction must be a field reference + if (!inst.Target.MatchLdFlda(out ILInstruction target, out IField field)) + return; + // Get display class info + if (!IsDisplayClassLoad(target, out var displayClassLoad) || !displayClasses.TryGetValue(displayClassLoad, out var displayClass)) + return; + field = (IField)field.MemberDefinition; + if (displayClass.Variables.TryGetValue(field, out DisplayClassVariable info)) { + // If the display class field was previously initialized, we use a simple assignment. + inst.ReplaceWith(new StLoc(info.Variable, inst.Value).WithILRange(inst)); + } else { + // This is an uninitialized variable: + ILInstruction value; + if (inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter && currentFunction == v.Function) { + // Special case for parameters: remove copies of parameter values. + instructionsToRemove.Add(inst); + value = inst.Value; + } else { + context.Step($"Introduce captured variable for {field.FullName}", inst); + Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition); + // Introduce a fresh variable for the display class field. + if (displayClass.IsMono && displayClass.CaptureScope == null && !IsOuterClosureReference(field)) { + displayClass.CaptureScope = BlockContainer.FindClosestContainer(inst); + } + v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name); + v.HasInitialValue = true; + v.CaptureScope = displayClass.CaptureScope; + inst.ReplaceWith(new StLoc(v, inst.Value).WithILRange(inst)); + value = new LdLoc(v); + } + displayClass.Variables.Add(field, new DisplayClassVariable { Value = value, Variable = v }); + } + } + + protected internal override void VisitLdObj(LdObj inst) + { + base.VisitLdObj(inst); + // The target of the store instruction must be a field reference + if (!inst.Target.MatchLdFlda(out var target, out IField field)) + return; + // Get display class info + if (!IsDisplayClassLoad(target, out var displayClassLoad) || !displayClasses.TryGetValue(displayClassLoad, out var displayClass)) + return; + // Get display class variable info + if (!displayClass.Variables.TryGetValue((IField)field.MemberDefinition, out DisplayClassVariable info)) + return; + // Replace usage of display class field with the variable. + var replacement = info.Value.Clone(); + replacement.SetILRange(inst); + inst.ReplaceWith(replacement); + } + + protected internal override void VisitLdFlda(LdFlda inst) + { + base.VisitLdFlda(inst); + // TODO : Figure out why this was added in https://github.com/icsharpcode/ILSpy/pull/1303 + if (inst.Target.MatchLdThis() && inst.Field.Name == "$this" + && inst.Field.MemberDefinition.ReflectionName.Contains("c__Iterator")) { + //Debug.Assert(false, "This should not be executed!"); + var variable = currentFunction.Variables.First((f) => f.Index == -1); + inst.ReplaceWith(new LdLoca(variable).WithILRange(inst)); + } + // Skip stfld/ldfld + if (inst.Parent is LdObj || inst.Parent is StObj) + return; + // Get display class info + if (!IsDisplayClassLoad(inst.Target, out var displayClassLoad) || !displayClasses.TryGetValue(displayClassLoad, out var displayClass)) + return; + var field = (IField)inst.Field.MemberDefinition; + if (!displayClass.Variables.TryGetValue(field, out DisplayClassVariable info)) { + context.Step($"Introduce captured variable for {field.FullName}", inst); + // Introduce a fresh variable for the display class field. + Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition); + var v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name); + v.HasInitialValue = true; + v.CaptureScope = displayClass.CaptureScope; + inst.ReplaceWith(new LdLoca(v).WithILRange(inst)); + displayClass.Variables.Add(field, new DisplayClassVariable { Value = new LdLoc(v), Variable = v }); + } else if (info.Value is LdLoc l) { + inst.ReplaceWith(new LdLoca(l.Variable).WithILRange(inst)); + } else { + Debug.Fail("LdFlda pattern not supported!"); + } + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs index 89c92bb69..07ba5adff 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs @@ -21,7 +21,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.CSharp.Resolver; -using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; @@ -157,6 +156,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms var returnType = functionType.GetDelegateInvokeMethod()?.ReturnType; var function = new ILFunction(returnType, parameterList, context.Function.GenericContext, container); function.DelegateType = functionType; + function.Kind = IsExpressionTree(functionType) ? ILFunctionKind.ExpressionTree : ILFunctionKind.Delegate; function.Variables.AddRange(parameterVariablesList); function.AddILRange(instruction); lambdaStack.Push(function); @@ -197,6 +197,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms void SetExpressionTreeFlag(ILFunction lambda, CallInstruction call) { + lambda.Kind = IsExpressionTree(call.Method.ReturnType) ? ILFunctionKind.ExpressionTree : ILFunctionKind.Delegate; lambda.DelegateType = call.Method.ReturnType; } @@ -237,6 +238,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (result.Item1 != null) { Debug.Assert(result.Item2 != null, "IType must be non-null!"); Debug.Assert(result.Item1.ResultType == result.Item2.GetStackType(), "StackTypes must match!"); + if (typeHint != null) { + var inst = result.Item1; + if (inst.ResultType != typeHint.GetStackType()) { + return (new Conv(inst, typeHint.GetStackType().ToPrimitiveType(), false, typeHint.GetSign()), typeHint); + } + } } return result; @@ -341,8 +348,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms } return (null, SpecialType.UnknownType); case ILFunction function: - if (function.IsExpressionTree) { + if (function.Kind == ILFunctionKind.ExpressionTree) { function.DelegateType = UnwrapExpressionTree(function.DelegateType); + function.Kind = ILFunctionKind.Delegate; } return (function, function.DelegateType); case LdLoc ldloc: @@ -350,7 +358,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // Replace an already mapped parameter with the actual ILVariable, // we generated earlier. if (parameterMapping.TryGetValue(ldloc.Variable, out var v)) { - if (typeHint.SkipModifiers() is ByReferenceType brt && !v.Type.IsByRefLike) + if (typeHint.SkipModifiers() is ByReferenceType && !v.Type.IsByRefLike) return (new LdLoca(v), typeHint); return (new LdLoc(v), v.Type); } @@ -362,7 +370,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // with references to mapped parameters. if (ldloc.Variable.IsSingleDefinition && ldloc.Variable.StoreInstructions[0] is ILInstruction inst) { if (MatchParameterVariableAssignment(inst, out _, out var type, out _)) - return (ldloc, type); + return (new ExpressionTreeCast(type, ldloc, false), type); } } return (null, SpecialType.UnknownType); @@ -372,6 +380,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } + bool IsExpressionTree(IType delegateType) => delegateType is ParameterizedType pt + && pt.FullName == "System.Linq.Expressions.Expression" + && pt.TypeArguments.Count == 1; + IType UnwrapExpressionTree(IType delegateType) { if (delegateType is ParameterizedType pt && pt.FullName == "System.Linq.Expressions.Expression" && pt.TypeArguments.Count == 1) { @@ -498,14 +510,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (arguments == null) return (null, SpecialType.UnknownType); IMethod method = (IMethod)member; - Debug.Assert(arguments.Count == method.Parameters.Count); - for (int i = 0; i < arguments.Count; i++) { - var expectedType = method.Parameters[i].Type; - var (argument, argumentType) = ConvertInstruction(arguments[i], expectedType); - if (argument == null) - return (null, SpecialType.UnknownType); - arguments[i] = argument; - } + if (!ConvertCallArguments(arguments, method)) + return (null, SpecialType.UnknownType); if (method.FullName == "System.Reflection.MethodInfo.CreateDelegate" && method.Parameters.Count == 2) { if (!MatchGetMethodFromHandle(target, out var targetMethod)) return (null, SpecialType.UnknownType); @@ -543,10 +549,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms return target; } default: + if (expectedType.Kind == TypeKind.Unknown && target.ResultType != StackType.Unknown) { + return new Conv(target, PrimitiveType.Unknown, false, Sign.None); + } return target; } } + bool ConvertCallArguments(IList arguments, IMethod method) + { + Debug.Assert(arguments.Count == method.Parameters.Count); + for (int i = 0; i < arguments.Count; i++) { + var expectedType = method.Parameters[i].Type; + var argument = ConvertInstruction(arguments[i], expectedType).Item1; + if (argument == null) + return false; + arguments[i] = argument; + } + return true; + } + (ILInstruction, IType) ConvertCast(CallInstruction invocation, bool isChecked) { if (invocation.Arguments.Count < 2) @@ -722,12 +744,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return (null, SpecialType.UnknownType); if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) return (null, SpecialType.UnknownType); - for (int i = 0; i < arguments.Count; i++) { - var arg = ConvertInstruction(arguments[i]).Item1; - if (arg == null) - return (null, SpecialType.UnknownType); - arguments[i] = arg; - } + if (!ConvertCallArguments(arguments, invokeMethod)) + return (null, SpecialType.UnknownType); var call = new CallVirt(invokeMethod); call.Arguments.Add(target); call.Arguments.AddRange(arguments); @@ -910,22 +928,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms return (null, SpecialType.UnknownType); if (!MatchArgumentList(invocation.Arguments[1], out arguments)) return (null, SpecialType.UnknownType); - var args = arguments.SelectArray(arg => ConvertInstruction(arg).Item1); - if (args.Any(a => a == null)) + IMethod method = (IMethod)member; + if (!ConvertCallArguments(arguments, method)) return (null, SpecialType.UnknownType); - newObj = new NewObj((IMethod)member); - newObj.Arguments.AddRange(args); + newObj = new NewObj(method); + newObj.Arguments.AddRange(arguments); return (newObj, member.DeclaringType); case 3: if (!MatchGetConstructorFromHandle(invocation.Arguments[0], out member)) return (null, SpecialType.UnknownType); if (!MatchArgumentList(invocation.Arguments[1], out arguments)) return (null, SpecialType.UnknownType); - var args2 = arguments.SelectArray(arg => ConvertInstruction(arg).Item1); - if (args2.Any(a => a == null)) + method = (IMethod)member; + if (!ConvertCallArguments(arguments, method)) return (null, SpecialType.UnknownType); - newObj = new NewObj((IMethod)member); - newObj.Arguments.AddRange(args2); + newObj = new NewObj(method); + newObj.Arguments.AddRange(arguments); return (newObj, member.DeclaringType); } return (null, SpecialType.UnknownType); @@ -969,11 +987,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments)) { arguments = new List(); } else { - for (int i = 0; i < arguments.Count; i++) { - arguments[i] = ConvertInstruction(arguments[i]).Item1; - if (arguments[i] == null) - return (null, SpecialType.UnknownType); - } + if (!ConvertCallArguments(arguments, (IMethod)member)) + return (null, SpecialType.UnknownType); } CallInstruction call; if (member.IsAbstract || member.IsVirtual || member.IsOverride) { @@ -1071,6 +1086,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms && v.StackType.IsIntegerType()) return new LdLoca(v); return null; + } else if (IsClosureReference(ldloc.Variable)) { + if (ldloc.Variable.Kind == VariableKind.Local) { + ldloc.Variable.Kind = VariableKind.DisplayClassLocal; + } + if (ldloc.Variable.CaptureScope == null) { + ldloc.Variable.CaptureScope = BlockContainer.FindClosestContainer(context); + } + return ldloc; } else { return ldloc; } @@ -1079,6 +1102,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } + bool IsClosureReference(ILVariable variable) + { + if (!variable.IsSingleDefinition || !(variable.StoreInstructions.SingleOrDefault() is StLoc store)) + return false; + if (!(store.Value is NewObj newObj)) + return false; + return TransformDisplayClassUsage.IsPotentialClosure(this.context, newObj); + } + bool IsExpressionTreeParameter(ILVariable variable) { return variable.Type.FullName == "System.Linq.Expressions.ParameterExpression"; diff --git a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs index adccfffa6..471319713 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs @@ -76,6 +76,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(storeInst.Value.MatchLdNull() || CheckResourceType(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)))) @@ -125,6 +127,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(storeInst.Value.MatchLdNull() || CheckResourceType(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)))) diff --git a/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs b/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs index 7d8f06f34..561c25704 100644 --- a/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs +++ b/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs @@ -101,24 +101,26 @@ namespace ICSharpCode.Decompiler.Metadata } } - public static string ToILNameString(this FullTypeName typeName) + public static string ToILNameString(this FullTypeName typeName, bool omitGenerics = false) { string name; if (typeName.IsNested) { name = typeName.Name; - int localTypeParameterCount = typeName.GetNestedTypeAdditionalTypeParameterCount(typeName.NestingLevel - 1); - if (localTypeParameterCount > 0) - name += "`" + localTypeParameterCount; + if (!omitGenerics) { + int localTypeParameterCount = typeName.GetNestedTypeAdditionalTypeParameterCount(typeName.NestingLevel - 1); + if (localTypeParameterCount > 0) + name += "`" + localTypeParameterCount; + } name = Disassembler.DisassemblerHelpers.Escape(name); - return $"{typeName.GetDeclaringType().ToILNameString()}/{name}"; + return $"{typeName.GetDeclaringType().ToILNameString(omitGenerics)}/{name}"; } if (!string.IsNullOrEmpty(typeName.TopLevelTypeName.Namespace)) { name = $"{typeName.TopLevelTypeName.Namespace}.{typeName.Name}"; - if (typeName.TypeParameterCount > 0) + if (!omitGenerics && typeName.TypeParameterCount > 0) name += "`" + typeName.TypeParameterCount; } else { name = typeName.Name; - if (typeName.TypeParameterCount > 0) + if (!omitGenerics && typeName.TypeParameterCount > 0) name += "`" + typeName.TypeParameterCount; } return Disassembler.DisassemblerHelpers.Escape(name); diff --git a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs index 2cbaa95eb..51a1c8cfe 100644 --- a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs +++ b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs @@ -29,6 +29,18 @@ namespace ICSharpCode.Decompiler.Metadata // This inspired by Mono.Cecil's BaseAssemblyResolver/DefaultAssemblyResolver. public class UniversalAssemblyResolver : IAssemblyResolver { + static UniversalAssemblyResolver() + { + // TODO : test whether this works with Mono on *Windows*, not sure if we'll + // ever need this... + if (Type.GetType("Mono.Runtime") != null) + decompilerRuntime = DecompilerRuntime.Mono; + else if (typeof(object).Assembly.GetName().Name == "System.Private.CoreLib") + decompilerRuntime = DecompilerRuntime.NETCoreApp; + else if (Environment.OSVersion.Platform == PlatformID.Unix) + decompilerRuntime = DecompilerRuntime.Mono; + } + DotNetCorePathFinder dotNetCorePathFinder; readonly bool throwOnError; readonly PEStreamOptions streamOptions; @@ -38,21 +50,7 @@ namespace ICSharpCode.Decompiler.Metadata readonly List directories = new List(); readonly List gac_paths = GetGacPaths(); HashSet targetFrameworkSearchPaths; - - /// - /// Detect whether we're in a Mono environment. - /// - /// This is used whenever we're trying to decompile a plain old .NET framework assembly on Unix. - static bool DetectMono() - { - // TODO : test whether this works with Mono on *Windows*, not sure if we'll - // ever need this... - if (Type.GetType("Mono.Runtime") != null) - return true; - if (Environment.OSVersion.Platform == PlatformID.Unix) - return true; - return false; - } + static readonly DecompilerRuntime decompilerRuntime; public void AddSearchDirectory(string directory) { @@ -77,6 +75,13 @@ namespace ICSharpCode.Decompiler.Metadata Silverlight } + enum DecompilerRuntime + { + NETFramework, + NETCoreApp, + Mono + } + string targetFramework; TargetFrameworkIdentifier targetFrameworkIdentifier; Version targetFrameworkVersion; @@ -291,7 +296,7 @@ namespace ICSharpCode.Decompiler.Metadata return assembly; var framework_dir = Path.GetDirectoryName(typeof(object).Module.FullyQualifiedName); - var framework_dirs = DetectMono() + var framework_dirs = decompilerRuntime == DecompilerRuntime.Mono ? new[] { framework_dir, Path.Combine(framework_dir, "Facades") } : new[] { framework_dir }; @@ -367,11 +372,13 @@ namespace ICSharpCode.Decompiler.Metadata var version = reference.Version; var corlib = typeof(object).Assembly.GetName(); - if (corlib.Version == version || IsSpecialVersionOrRetargetable(reference)) - return typeof(object).Module.FullyQualifiedName; + if (decompilerRuntime != DecompilerRuntime.NETCoreApp) { + if (corlib.Version == version || IsSpecialVersionOrRetargetable(reference)) + return typeof(object).Module.FullyQualifiedName; + } string path; - if (DetectMono()) { + if (decompilerRuntime == DecompilerRuntime.Mono) { path = GetMonoMscorlibBasePath(version); } else { path = GetMscorlibBasePath(version, reference.PublicKeyToken.ToHexString(8)); @@ -460,7 +467,7 @@ namespace ICSharpCode.Decompiler.Metadata static List GetGacPaths() { - if (DetectMono()) + if (decompilerRuntime == DecompilerRuntime.Mono) return GetDefaultMonoGacPaths(); var paths = new List(2); @@ -510,7 +517,7 @@ namespace ICSharpCode.Decompiler.Metadata if (reference.PublicKeyToken == null || reference.PublicKeyToken.Length == 0) return null; - if (DetectMono()) + if (decompilerRuntime == DecompilerRuntime.Mono) return GetAssemblyInMonoGac(reference); return GetAssemblyInNetGac(reference); diff --git a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs index 268b09ba3..85d62c704 100644 --- a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs +++ b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs @@ -21,8 +21,10 @@ using System.Collections.Generic; using System.Linq; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.OutputVisitor; +using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.IL; +using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler @@ -90,7 +92,7 @@ namespace ICSharpCode.Decompiler return; } - if (firstUsingDeclaration) { + if (firstUsingDeclaration && !lastUsingDeclaration) { output.MarkFoldStart(defaultCollapsed: !settings.ExpandUsingDeclarations); firstUsingDeclaration = false; } @@ -169,13 +171,18 @@ namespace ICSharpCode.Decompiler return variable; } - var label = node as LabelStatement; - if (label != null) { + if (node is LabelStatement label) { var method = nodeStack.Select(nd => nd.GetSymbol() as IMethod).FirstOrDefault(mr => mr != null); if (method != null) return method + label.Label; } + if (node is LocalFunctionDeclarationStatement) { + var localFunction = node.GetResolveResult() as MemberResolveResult; + if (localFunction != null) + return localFunction.Member; + } + return null; } @@ -264,11 +271,10 @@ namespace ICSharpCode.Decompiler public override void NewLine() { - if (lastUsingDeclaration) { + if (!firstUsingDeclaration && lastUsingDeclaration) { output.MarkFoldEnd(); lastUsingDeclaration = false; } -// lastEndOfLine = output.Location; output.WriteLine(); } @@ -366,9 +372,6 @@ namespace ICSharpCode.Decompiler } } -// Stack startLocations = new Stack(); -// Stack symbolsStack = new Stack(); - public override void StartNode(AstNode node) { if (nodeStack.Count == 0) { @@ -381,15 +384,6 @@ namespace ICSharpCode.Decompiler } } nodeStack.Push(node); -// startLocations.Push(output.Location); - -// if (node is EntityDeclaration && node.GetSymbol() != null && node.GetChildByRole(Roles.Identifier).IsNull) -// output.WriteDefinition("", node.GetSymbol(), false); - -// if (node.Annotation() != null) { -// symbolsStack.Push(node.Annotation()); -// symbolsStack.Peek().StartLocation = startLocations.Peek(); -// } } private bool IsUsingDeclaration(AstNode node) diff --git a/ICSharpCode.Decompiler/SRMExtensions.cs b/ICSharpCode.Decompiler/SRMExtensions.cs index a59d37c7b..39af674cd 100644 --- a/ICSharpCode.Decompiler/SRMExtensions.cs +++ b/ICSharpCode.Decompiler/SRMExtensions.cs @@ -1,13 +1,10 @@ using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using System.Reflection; using System.Reflection.Metadata; using SRM = System.Reflection.Metadata; using System.Reflection.PortableExecutable; using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; using System.Reflection.Metadata.Ecma335; @@ -69,12 +66,17 @@ namespace ICSharpCode.Decompiler return false; if (!baseType.IsKnownType(reader, KnownTypeCode.Enum)) return false; - var field = reader.GetFieldDefinition(typeDefinition.GetFields().First()); - var blob = reader.GetBlobReader(field.Signature); - if (blob.ReadSignatureHeader().Kind != SignatureKind.Field) - return false; - underlyingType = (PrimitiveTypeCode)blob.ReadByte(); - return true; + foreach (var handle in typeDefinition.GetFields()) { + var field = reader.GetFieldDefinition(handle); + if ((field.Attributes & FieldAttributes.Static) != 0) + continue; + var blob = reader.GetBlobReader(field.Signature); + if (blob.ReadSignatureHeader().Kind != SignatureKind.Field) + return false; + underlyingType = (PrimitiveTypeCode)blob.ReadByte(); + return true; + } + return false; } public static bool IsDelegate(this TypeDefinitionHandle handle, MetadataReader reader) @@ -307,6 +309,17 @@ namespace ICSharpCode.Decompiler return false; } + public static bool IsCompilerGeneratedOrIsInCompilerGeneratedClass(this TypeDefinitionHandle handle, MetadataReader metadata) + { + TypeDefinition type = metadata.GetTypeDefinition(handle); + if (type.IsCompilerGenerated(metadata)) + return true; + TypeDefinitionHandle declaringTypeHandle = type.GetDeclaringType(); + if (!declaringTypeHandle.IsNil && declaringTypeHandle.IsCompilerGenerated(metadata)) + return true; + return false; + } + public static bool IsCompilerGenerated(this MethodDefinition method, MetadataReader metadata) { return method.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.CompilerGenerated); @@ -366,6 +379,28 @@ namespace ICSharpCode.Decompiler { return attr.GetAttributeType(metadata).IsKnownType(metadata, attrType); } + + public static Nullability? GetNullableContext(this CustomAttributeHandleCollection customAttributes, MetadataReader metadata) + { + foreach (var handle in customAttributes) { + var customAttribute = metadata.GetCustomAttribute(handle); + if (customAttribute.IsKnownAttribute(metadata, KnownAttribute.NullableContext)) { + // Decode + CustomAttributeValue value; + try { + value = customAttribute.DecodeValue(Metadata.MetadataExtensions.MinimalAttributeTypeProvider); + } catch (BadImageFormatException) { + continue; + } catch (Metadata.EnumUnderlyingTypeResolveException) { + continue; + } + if (value.FixedArguments.Length == 1 && value.FixedArguments[0].Value is byte b && b <= 2) { + return (Nullability)b; + } + } + } + return null; + } #endregion public static unsafe SRM.BlobReader GetInitialValue(this FieldDefinition field, PEReader pefile, ICompilation typeSystem) diff --git a/ICSharpCode.Decompiler/Semantics/ByReferenceResolveResult.cs b/ICSharpCode.Decompiler/Semantics/ByReferenceResolveResult.cs index b5f927827..bbb525ca3 100644 --- a/ICSharpCode.Decompiler/Semantics/ByReferenceResolveResult.cs +++ b/ICSharpCode.Decompiler/Semantics/ByReferenceResolveResult.cs @@ -24,25 +24,27 @@ using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// - /// Represents the resolve result of an 'ref x' or 'out x' expression. + /// Represents the resolve result of an 'ref x', 'in x' or 'out x' expression. /// public class ByReferenceResolveResult : ResolveResult { - public bool IsOut { get; private set; } - public bool IsRef { get { return !IsOut;} } - + public ReferenceKind ReferenceKind { get; } + public bool IsOut => ReferenceKind == ReferenceKind.Out; + public bool IsRef => ReferenceKind == ReferenceKind.Ref; + public bool IsIn => ReferenceKind == ReferenceKind.In; + public readonly ResolveResult ElementResult; - public ByReferenceResolveResult(ResolveResult elementResult, bool isOut) - : this(elementResult.Type, isOut) + public ByReferenceResolveResult(ResolveResult elementResult, ReferenceKind kind) + : this(elementResult.Type, kind) { this.ElementResult = elementResult; } - public ByReferenceResolveResult(IType elementType, bool isOut) + public ByReferenceResolveResult(IType elementType, ReferenceKind kind) : base(new ByReferenceType(elementType)) { - this.IsOut = isOut; + this.ReferenceKind = kind; } public IType ElementType { @@ -59,7 +61,7 @@ namespace ICSharpCode.Decompiler.Semantics public override string ToString() { - return string.Format(CultureInfo.InvariantCulture, "[{0} {1} {2}]", GetType().Name, IsOut ? "out" : "ref", ElementType); + return string.Format(CultureInfo.InvariantCulture, "[{0} {1} {2}]", GetType().Name, ReferenceKind.ToString().ToLowerInvariant(), ElementType); } } } diff --git a/ICSharpCode.Decompiler/Semantics/Conversion.cs b/ICSharpCode.Decompiler/Semantics/Conversion.cs index 447da9891..b90107392 100644 --- a/ICSharpCode.Decompiler/Semantics/Conversion.cs +++ b/ICSharpCode.Decompiler/Semantics/Conversion.cs @@ -76,8 +76,16 @@ namespace ICSharpCode.Decompiler.Semantics /// public static readonly Conversion TryCast = new BuiltinConversion(false, 9); + /// + /// C# 6 string interpolation expression implicitly being converted to or . + /// public static readonly Conversion ImplicitInterpolatedStringConversion = new BuiltinConversion(true, 10); + /// + /// C# 7 throw expression being converted to an arbitrary type. + /// + public static readonly Conversion ThrowExpressionConversion = new BuiltinConversion(true, 11); + public static Conversion UserDefinedConversion(IMethod operatorMethod, bool isImplicit, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted = false, bool isAmbiguous = false) { if (operatorMethod == null) @@ -231,7 +239,11 @@ namespace ICSharpCode.Decompiler.Semantics } public override bool IsInterpolatedStringConversion => type == 10; - + + public override bool IsThrowExpressionConversion { + get { return type == 11; } + } + public override string ToString() { string name = null; @@ -263,6 +275,8 @@ namespace ICSharpCode.Decompiler.Semantics return "try cast"; case 10: return "interpolated string"; + case 11: + return "throw-expression conversion"; } return (isImplicit ? "implicit " : "explicit ") + name + " conversion"; } @@ -449,7 +463,11 @@ namespace ICSharpCode.Decompiler.Semantics public virtual bool IsTryCast { get { return false; } } - + + public virtual bool IsThrowExpressionConversion { + get { return false; } + } + public virtual bool IsIdentityConversion { get { return false; } } diff --git a/ICSharpCode.Decompiler/Semantics/LocalResolveResult.cs b/ICSharpCode.Decompiler/Semantics/LocalResolveResult.cs index 67034a2d6..e1ea775f3 100644 --- a/ICSharpCode.Decompiler/Semantics/LocalResolveResult.cs +++ b/ICSharpCode.Decompiler/Semantics/LocalResolveResult.cs @@ -42,7 +42,7 @@ namespace ICSharpCode.Decompiler.Semantics IType type = variable.Type; if (type.Kind == TypeKind.ByReference) { IParameter p = variable as IParameter; - if (p != null && (p.IsRef || p.IsOut)) + if (p != null && p.ReferenceKind != ReferenceKind.None) return ((ByReferenceType)type).ElementType; } return type; diff --git a/ICSharpCode.Decompiler/Semantics/ThrowResolveResult.cs b/ICSharpCode.Decompiler/Semantics/ThrowResolveResult.cs new file mode 100644 index 000000000..518e38e4f --- /dev/null +++ b/ICSharpCode.Decompiler/Semantics/ThrowResolveResult.cs @@ -0,0 +1,29 @@ +// Copyright (c) 2018 Daniel Grunwald +// +// 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 ICSharpCode.Decompiler.TypeSystem; + +namespace ICSharpCode.Decompiler.Semantics +{ + class ThrowResolveResult : ResolveResult + { + public ThrowResolveResult() : base(SpecialType.NoType) + { + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Transforms/LoopingTransform.cs b/ICSharpCode.Decompiler/Solution/ProjectId.cs similarity index 50% rename from ICSharpCode.Decompiler/IL/Transforms/LoopingTransform.cs rename to ICSharpCode.Decompiler/Solution/ProjectId.cs index 27f761feb..cfa3feb7c 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LoopingTransform.cs +++ b/ICSharpCode.Decompiler/Solution/ProjectId.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Siegfried Pammer +// Copyright (c) 2019 AlphaSierraPapa for the SharpDevelop Team // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software @@ -17,45 +17,39 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.Collections.Generic; -namespace ICSharpCode.Decompiler.IL.Transforms +namespace ICSharpCode.Decompiler.Solution { /// - /// Repeats the child transforms until the ILAst no longer changes (fixpoint iteration). + /// A container class that holds platform and GUID information about a Visual Studio project. /// - public class LoopingBlockTransform : IBlockTransform + public class ProjectId { - readonly IBlockTransform[] children; - bool running; - - public LoopingBlockTransform(params IBlockTransform[] children) - { - this.children = children; - } - - public void Run(Block block, BlockTransformContext context) + /// + /// Initializes a new instance of the class. + /// + /// The project platform. + /// The project GUID. + /// + /// Thrown when is null or empty. + public ProjectId(string projectPlatform, Guid projectGuid) { - if (running) - throw new InvalidOperationException("LoopingBlockTransform already running. Transforms (and the CSharpDecompiler) are neither thread-safe nor re-entrant."); - running = true; - try { - int count = 1; - do { - block.ResetDirty(); - block.RunTransforms(children, context); - if (block.IsDirty) - context.Step($"Block is dirty; running loop iteration #{++count}.", block); - } while (block.IsDirty); - } finally { - running = false; + if (string.IsNullOrWhiteSpace(projectPlatform)) { + throw new ArgumentException("The platform cannot be null or empty.", nameof(projectPlatform)); } - } - public IReadOnlyCollection Transforms { - get { return children; } + Guid = projectGuid; + PlatformName = projectPlatform; } - } -} + /// + /// Gets the GUID of this project. + /// + public Guid Guid { get; } + /// + /// Gets the platform name of this project. Only single platform per project is supported. + /// + public string PlatformName { get; } + } +} diff --git a/ICSharpCode.Decompiler/Solution/ProjectItem.cs b/ICSharpCode.Decompiler/Solution/ProjectItem.cs new file mode 100644 index 000000000..bf1222368 --- /dev/null +++ b/ICSharpCode.Decompiler/Solution/ProjectItem.cs @@ -0,0 +1,55 @@ +// Copyright (c) 2019 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.IO; + +namespace ICSharpCode.Decompiler.Solution +{ + /// + /// A container class that holds information about a Visual Studio project. + /// + public sealed class ProjectItem : ProjectId + { + /// + /// Initializes a new instance of the class. + /// + /// The full path of the project file. + /// The project platform. + /// The project GUID. + /// + /// Thrown when + /// or is null or empty. + public ProjectItem(string projectFile, string projectPlatform, Guid projectGuid) + : base(projectPlatform, projectGuid) + { + ProjectName = Path.GetFileNameWithoutExtension(projectFile); + FilePath = projectFile; + } + + /// + /// Gets the name of the project. + /// + public string ProjectName { get; } + + /// + /// Gets the full path to the project file. + /// + public string FilePath { get; } + } +} diff --git a/ICSharpCode.Decompiler/Solution/SolutionCreator.cs b/ICSharpCode.Decompiler/Solution/SolutionCreator.cs new file mode 100644 index 000000000..824f03a23 --- /dev/null +++ b/ICSharpCode.Decompiler/Solution/SolutionCreator.cs @@ -0,0 +1,201 @@ +// Copyright (c) 2019 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; + +namespace ICSharpCode.Decompiler.Solution +{ + /// + /// A helper class that can write a Visual Studio Solution file for the provided projects. + /// + public static class SolutionCreator + { + private static readonly XNamespace ProjectFileNamespace = XNamespace.Get("http://schemas.microsoft.com/developer/msbuild/2003"); + + /// + /// Writes a solution file to the specified . + /// + /// The full path of the file to write. + /// The projects contained in this solution. + /// + /// Thrown when is null or empty. + /// Thrown when is null. + /// Thrown when contains no items. + public static void WriteSolutionFile(string targetFile, IEnumerable projects) + { + if (string.IsNullOrWhiteSpace(targetFile)) { + throw new ArgumentException("The target file cannot be null or empty.", nameof(targetFile)); + } + + if (projects == null) { + throw new ArgumentNullException(nameof(projects)); + } + + if (!projects.Any()) { + throw new InvalidOperationException("At least one project is expected."); + } + + using (var writer = new StreamWriter(targetFile)) { + WriteSolutionFile(writer, projects, targetFile); + } + + FixProjectReferences(projects); + } + + private static void WriteSolutionFile(TextWriter writer, IEnumerable projects, string solutionFilePath) + { + WriteHeader(writer); + WriteProjects(writer, projects, solutionFilePath); + + writer.WriteLine("Global"); + + var platforms = WriteSolutionConfigurations(writer, projects); + WriteProjectConfigurations(writer, projects, platforms); + + writer.WriteLine("\tGlobalSection(SolutionProperties) = preSolution"); + writer.WriteLine("\t\tHideSolutionNode = FALSE"); + writer.WriteLine("\tEndGlobalSection"); + + writer.WriteLine("EndGlobal"); + } + + private static void WriteHeader(TextWriter writer) + { + writer.WriteLine("Microsoft Visual Studio Solution File, Format Version 12.00"); + writer.WriteLine("# Visual Studio 14"); + writer.WriteLine("VisualStudioVersion = 14.0.24720.0"); + writer.WriteLine("MinimumVisualStudioVersion = 10.0.40219.1"); + } + + private static void WriteProjects(TextWriter writer, IEnumerable projects, string solutionFilePath) + { + var solutionGuid = Guid.NewGuid().ToString("B").ToUpperInvariant(); + + foreach (var project in projects) { + var projectRelativePath = GetRelativePath(solutionFilePath, project.FilePath); + var projectGuid = project.Guid.ToString("B").ToUpperInvariant(); + + writer.WriteLine($"Project(\"{solutionGuid}\") = \"{project.ProjectName}\", \"{projectRelativePath}\", \"{projectGuid}\""); + writer.WriteLine("EndProject"); + } + } + + private static IEnumerable WriteSolutionConfigurations(TextWriter writer, IEnumerable projects) + { + var platforms = projects.GroupBy(p => p.PlatformName).Select(g => g.Key).ToList(); + + platforms.Sort(); + + writer.WriteLine("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution"); + foreach (var platform in platforms) { + writer.WriteLine($"\t\tDebug|{platform} = Debug|{platform}"); + } + + foreach (var platform in platforms) { + writer.WriteLine($"\t\tRelease|{platform} = Release|{platform}"); + } + + writer.WriteLine("\tEndGlobalSection"); + + return platforms; + } + + private static void WriteProjectConfigurations( + TextWriter writer, + IEnumerable projects, + IEnumerable solutionPlatforms) + { + writer.WriteLine("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution"); + + foreach (var project in projects) { + var projectGuid = project.Guid.ToString("B").ToUpperInvariant(); + + foreach (var platform in solutionPlatforms) { + writer.WriteLine($"\t\t{projectGuid}.Debug|{platform}.ActiveCfg = Debug|{project.PlatformName}"); + writer.WriteLine($"\t\t{projectGuid}.Debug|{platform}.Build.0 = Debug|{project.PlatformName}"); + } + + foreach (var platform in solutionPlatforms) { + writer.WriteLine($"\t\t{projectGuid}.Release|{platform}.ActiveCfg = Release|{project.PlatformName}"); + writer.WriteLine($"\t\t{projectGuid}.Release|{platform}.Build.0 = Release|{project.PlatformName}"); + } + } + + writer.WriteLine("\tEndGlobalSection"); + } + + private static void FixProjectReferences(IEnumerable projects) + { + var projectsMap = projects.ToDictionary(p => p.ProjectName, p => p); + + foreach (var project in projects) { + XDocument projectDoc = XDocument.Load(project.FilePath); + + var referencesItemGroups = projectDoc.Root + .Elements(ProjectFileNamespace + "ItemGroup") + .Where(e => e.Elements(ProjectFileNamespace + "Reference").Any()); + + foreach (var itemGroup in referencesItemGroups) { + FixProjectReferences(project.FilePath, itemGroup, projectsMap); + } + + projectDoc.Save(project.FilePath); + } + } + + private static void FixProjectReferences(string projectFilePath, XElement itemGroup, IDictionary projects) + { + foreach (var item in itemGroup.Elements(ProjectFileNamespace + "Reference").ToList()) { + var assemblyName = item.Attribute("Include")?.Value; + if (assemblyName != null && projects.TryGetValue(assemblyName, out var referencedProject)) { + item.Remove(); + + var projectReference = new XElement(ProjectFileNamespace + "ProjectReference", + new XElement(ProjectFileNamespace + "Project", referencedProject.Guid.ToString("B").ToUpperInvariant()), + new XElement(ProjectFileNamespace + "Name", referencedProject.ProjectName)); + projectReference.SetAttributeValue("Include", GetRelativePath(projectFilePath, referencedProject.FilePath)); + + itemGroup.Add(projectReference); + } + } + } + + private static string GetRelativePath(string fromFilePath, string toFilePath) + { + Uri fromUri = new Uri(fromFilePath); + Uri toUri = new Uri(toFilePath); + + if (fromUri.Scheme != toUri.Scheme) { + return toFilePath; + } + + Uri relativeUri = fromUri.MakeRelativeUri(toUri); + string relativePath = Uri.UnescapeDataString(relativeUri.ToString()); + + if (string.Equals(toUri.Scheme, Uri.UriSchemeFile, StringComparison.OrdinalIgnoreCase)) { + relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + } + + return relativePath; + } + } +} diff --git a/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs b/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs index a41841a0e..daef269bc 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs @@ -37,14 +37,19 @@ namespace ICSharpCode.Decompiler.TypeSystem SRM.CustomAttributeHandleCollection? attributes, SRM.MetadataReader metadata, TypeSystemOptions options, + Nullability nullableContext, bool typeChildrenOnly = false) { bool hasDynamicAttribute = false; bool[] dynamicAttributeData = null; string[] tupleElementNames = null; - bool hasNullableAttribute = false; - Nullability nullability = Nullability.Oblivious; + Nullability nullability; Nullability[] nullableAttributeData = null; + if ((options & TypeSystemOptions.NullabilityAnnotations) != 0) { + nullability = nullableContext; + } else { + nullability = Nullability.Oblivious; + } const TypeSystemOptions relevantOptions = TypeSystemOptions.Dynamic | TypeSystemOptions.Tuple | TypeSystemOptions.NullabilityAnnotations; if (attributes != null && (options & relevantOptions) != 0) { foreach (var attrHandle in attributes.Value) { @@ -70,7 +75,6 @@ namespace ICSharpCode.Decompiler.TypeSystem } } } else if ((options & TypeSystemOptions.NullabilityAnnotations) != 0 && attrType.IsKnownType(metadata, KnownAttribute.Nullable)) { - hasNullableAttribute = true; var ctor = attr.DecodeValue(Metadata.MetadataExtensions.minimalCorlibTypeProvider); if (ctor.FixedArguments.Length == 1) { var arg = ctor.FixedArguments[0]; @@ -78,13 +82,15 @@ namespace ICSharpCode.Decompiler.TypeSystem && values.All(v => v.Value is byte b && b <= 2)) { nullableAttributeData = values.SelectArray(v => (Nullability)(byte)v.Value); } else if (arg.Value is byte b && b <= 2) { - nullability = (Nullability)(byte)arg.Value; + nullability = (Nullability)b; } } } } } - if (hasDynamicAttribute || hasNullableAttribute || (options & (TypeSystemOptions.Tuple | TypeSystemOptions.KeepModifiers)) != TypeSystemOptions.KeepModifiers) { + if (hasDynamicAttribute || nullability != Nullability.Oblivious || nullableAttributeData != null + || (options & (TypeSystemOptions.Tuple | TypeSystemOptions.KeepModifiers)) != TypeSystemOptions.KeepModifiers) + { var visitor = new ApplyAttributeTypeVisitor( compilation, hasDynamicAttribute, dynamicAttributeData, options, tupleElementNames, @@ -148,16 +154,21 @@ namespace ICSharpCode.Decompiler.TypeSystem Nullability GetNullability() { if (nullabilityTypeIndex < nullableAttributeData?.Length) - return nullableAttributeData[nullabilityTypeIndex]; + return nullableAttributeData[nullabilityTypeIndex++]; else return defaultNullability; } + void ExpectDummyNullabilityForGenericValueType() + { + var n = GetNullability(); + Debug.Assert(n == Nullability.Oblivious); + } + public override IType VisitArrayType(ArrayType type) { var nullability = GetNullability(); dynamicTypeIndex++; - nullabilityTypeIndex++; return base.VisitArrayType(type).ChangeNullability(nullability); } @@ -181,18 +192,18 @@ namespace ICSharpCode.Decompiler.TypeSystem elementNames = ImmutableArray.CreateRange(extractedValues); } tupleTypeIndex += tupleCardinality; + ExpectDummyNullabilityForGenericValueType(); var elementTypes = ImmutableArray.CreateBuilder(tupleCardinality); do { int normalArgCount = Math.Min(type.TypeArguments.Count, TupleType.RestPosition - 1); for (int i = 0; i < normalArgCount; i++) { dynamicTypeIndex++; - nullabilityTypeIndex++; elementTypes.Add(type.TypeArguments[i].AcceptVisitor(this)); } if (type.TypeArguments.Count == TupleType.RestPosition) { type = type.TypeArguments.Last() as ParameterizedType; + ExpectDummyNullabilityForGenericValueType(); dynamicTypeIndex++; - nullabilityTypeIndex++; if (type != null && TupleType.IsTupleCompatible(type, out int nestedCardinality)) { tupleTypeIndex += nestedCardinality; } else { @@ -218,11 +229,13 @@ namespace ICSharpCode.Decompiler.TypeSystem // Visit generic type and type arguments. // Like base implementation, except that it increments dynamicTypeIndex. var genericType = type.GenericType.AcceptVisitor(this); + if (genericType.IsReferenceType != true && !genericType.IsKnownType(KnownTypeCode.NullableOfT)) { + ExpectDummyNullabilityForGenericValueType(); + } bool changed = type.GenericType != genericType; var arguments = new IType[type.TypeArguments.Count]; for (int i = 0; i < type.TypeArguments.Count; i++) { dynamicTypeIndex++; - nullabilityTypeIndex++; arguments[i] = type.TypeArguments[i].AcceptVisitor(this); changed = changed || arguments[i] != type.TypeArguments[i]; } @@ -240,8 +253,28 @@ namespace ICSharpCode.Decompiler.TypeSystem else if (dynamicAttributeData[dynamicTypeIndex]) newType = SpecialType.Dynamic; } + if (type.IsReferenceType == true) { + Nullability nullability = GetNullability(); + return newType.ChangeNullability(nullability); + } else { + return newType; + } + } + + public override IType VisitOtherType(IType type) + { + type = base.VisitOtherType(type); + if (type.Kind == TypeKind.Unknown && type.IsReferenceType == true) { + Nullability nullability = GetNullability(); + type = type.ChangeNullability(nullability); + } + return type; + } + + public override IType VisitTypeParameter(ITypeParameter type) + { Nullability nullability = GetNullability(); - return newType.ChangeNullability(nullability); + return type.ChangeNullability(nullability); } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/IMethod.cs b/ICSharpCode.Decompiler/TypeSystem/IMethod.cs index 64c8b3303..c53d1cc60 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IMethod.cs @@ -53,6 +53,7 @@ namespace ICSharpCode.Decompiler.TypeSystem IReadOnlyList TypeArguments { get; } bool IsExtensionMethod { get; } + bool IsLocalFunction { get; } bool IsConstructor { get; } bool IsDestructor { get; } bool IsOperator { get; } @@ -81,8 +82,9 @@ namespace ICSharpCode.Decompiler.TypeSystem MethodSemanticsAttributes AccessorKind { get; } /// - /// If this method is reduced from an extension method return the original method, null otherwise. - /// A reduced method doesn't contain the extension method parameter. That means that has one parameter less than it's definition. + /// If this method is reduced from an extension method or a local function returns the original method, null otherwise. + /// A reduced method doesn't contain the extension method parameter. That means that it has one parameter less than its definition. + /// A local function doesn't contain compiler-generated method parameters at the end. /// IMethod ReducedFrom { get; } diff --git a/ICSharpCode.Decompiler/TypeSystem/IParameter.cs b/ICSharpCode.Decompiler/TypeSystem/IParameter.cs index 68f10c532..7801a0e09 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IParameter.cs @@ -21,12 +21,28 @@ using System.Collections.Generic; namespace ICSharpCode.Decompiler.TypeSystem { + /// + /// Should match order in . + /// + public enum ReferenceKind + { + None, + Out, + Ref, + In + } + public interface IParameter : IVariable { /// /// Gets the attributes on this parameter. /// IEnumerable GetAttributes(); + + /// + /// Gets the reference kind of this parameter. + /// + ReferenceKind ReferenceKind { get; } /// /// Gets whether this parameter is a C# 'ref' parameter. @@ -47,10 +63,10 @@ namespace ICSharpCode.Decompiler.TypeSystem /// Gets whether this parameter is a C# 'params' parameter. /// bool IsParams { get; } - + /// /// Gets whether this parameter is optional. - /// The default value is given by the property. + /// The default value is given by the function. /// bool IsOptional { get; } diff --git a/ICSharpCode.Decompiler/TypeSystem/ISymbol.cs b/ICSharpCode.Decompiler/TypeSystem/ISymbol.cs index 89c1b7984..b1b59c404 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ISymbol.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ISymbol.cs @@ -69,6 +69,10 @@ namespace ICSharpCode.Decompiler.TypeSystem Parameter, /// TypeParameter, + /// + /// Constraint on a type parameter. + /// + Constraint, } /// diff --git a/ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs b/ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs index 2a3d750ed..19b727adc 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs @@ -67,5 +67,11 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// This property is used to speed up the search for extension methods. bool HasExtensionMethods { get; } + + /// + /// The nullability specified in the [NullableContext] attribute on the type. + /// This serves as default nullability for members of the type that do not have a [Nullable] attribute. + /// + Nullability NullableContext { get; } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs index 722b550d5..8ff935184 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs @@ -16,7 +16,9 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; using System.Collections.Generic; +using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem { @@ -30,10 +32,10 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// SymbolKind.TypeDefinition or SymbolKind.Method SymbolKind OwnerType { get; } - + /// /// Gets the owning method/class. - /// This property may return null (for example for the dummy type parameters used by ). + /// This property may return null (for example for the dummy type parameters used by ). /// /// /// For "class Outer<T> { class Inner {} }", @@ -99,6 +101,21 @@ namespace ICSharpCode.Decompiler.TypeSystem /// E.g. "T? GetNull<T>() where T : class => null;" /// Nullability NullabilityConstraint { get; } + + IReadOnlyList TypeConstraints { get; } + } + + public readonly struct TypeConstraint + { + public SymbolKind SymbolKind => SymbolKind.Constraint; + public IType Type { get; } + public IReadOnlyList Attributes { get; } + + public TypeConstraint(IType type, IReadOnlyList attributes = null) + { + this.Type = type ?? throw new ArgumentNullException(nameof(type)); + this.Attributes = attributes ?? EmptyList.Instance; + } } /// diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs index 7ad3d137c..373ea7433 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs @@ -194,11 +194,11 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } bool IType.IsByRefLike => false; - Nullability IType.Nullability => NullabilityConstraint; + Nullability IType.Nullability => Nullability.Oblivious; public IType ChangeNullability(Nullability nullability) { - if (nullability == NullabilityConstraint) + if (nullability == Nullability.Oblivious) return this; else return new NullabilityAnnotatedTypeParameter(this, nullability); @@ -220,7 +220,11 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation get { return EmptyList.Instance; } } - public abstract IEnumerable DirectBaseTypes { get; } + public IEnumerable DirectBaseTypes { + get { return TypeConstraints.Select(t => t.Type); } + } + + public abstract IReadOnlyList TypeConstraints { get; } public string Name { get { return name; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs index 3f08466ef..1bc076ad9 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs @@ -208,6 +208,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return (options & TypeSystemOptions.UnmanagedConstraints) != 0 && target == SymbolKind.TypeParameter; case "NullableAttribute": return (options & TypeSystemOptions.NullabilityAnnotations) != 0; + case "NullableContextAttribute": + return (options & TypeSystemOptions.NullabilityAnnotations) != 0 && (target == SymbolKind.TypeDefinition || target == SymbolKind.Method); default: return false; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs index e3ffc5688..c9b2d2b15 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs @@ -31,7 +31,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation readonly IType type; readonly string name; readonly IReadOnlyList attributes; - readonly bool isRef, isOut, isIn, isParams, isOptional; + readonly ReferenceKind referenceKind; + readonly bool isParams, isOptional; readonly object defaultValue; readonly IParameterizedMember owner; @@ -47,7 +48,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } public DefaultParameter(IType type, string name, IParameterizedMember owner = null, IReadOnlyList attributes = null, - bool isRef = false, bool isOut = false, bool isIn = false, bool isParams = false, bool isOptional = false, object defaultValue = null) + ReferenceKind referenceKind = ReferenceKind.None, bool isParams = false, bool isOptional = false, object defaultValue = null) { if (type == null) throw new ArgumentNullException("type"); @@ -57,9 +58,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation this.name = name; this.owner = owner; this.attributes = attributes ?? EmptyList.Instance; - this.isRef = isRef; - this.isOut = isOut; - this.isIn = isIn; + this.referenceKind = referenceKind; this.isParams = isParams; this.isOptional = isOptional; this.defaultValue = defaultValue; @@ -74,27 +73,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } public IEnumerable GetAttributes() => attributes; - - public bool IsRef { - get { return isRef; } - } - - public bool IsOut { - get { return isOut; } - } - public bool IsIn { - get { return isIn; } - } - - public bool IsParams { - get { return isParams; } - } - - public bool IsOptional { - get { return isOptional; } - } - + public ReferenceKind ReferenceKind => referenceKind; + public bool IsRef => referenceKind == ReferenceKind.Ref; + public bool IsOut => referenceKind == ReferenceKind.Out; + public bool IsIn => referenceKind == ReferenceKind.In; + + public bool IsParams => isParams; + + public bool IsOptional => isOptional; + public string Name { get { return name; } } @@ -128,6 +116,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation b.Append("ref "); if (parameter.IsOut) b.Append("out "); + if (parameter.IsIn) + b.Append("in "); if (parameter.IsParams) b.Append("params "); b.Append(parameter.Name); diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultTypeParameter.cs index e99e17af7..8ff30441a 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultTypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultTypeParameter.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; using System.Collections.Generic; using ICSharpCode.Decompiler.Util; @@ -43,10 +44,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation this.hasReferenceTypeConstraint = hasReferenceTypeConstraint; this.hasDefaultConstructorConstraint = hasDefaultConstructorConstraint; this.nullabilityConstraint = nullabilityConstraint; - this.constraints = constraints ?? EmptyList.Instance; + this.TypeConstraints = MakeConstraints(constraints); this.attributes = attributes ?? EmptyList.Instance; } - + public DefaultTypeParameter( ICompilation compilation, SymbolKind ownerType, int index, string name = null, @@ -60,7 +61,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation this.hasReferenceTypeConstraint = hasReferenceTypeConstraint; this.hasDefaultConstructorConstraint = hasDefaultConstructorConstraint; this.nullabilityConstraint = nullabilityConstraint; - this.constraints = constraints ?? EmptyList.Instance; + this.TypeConstraints = MakeConstraints(constraints); this.attributes = attributes ?? EmptyList.Instance; } @@ -72,19 +73,24 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public override bool HasUnmanagedConstraint => false; public override Nullability NullabilityConstraint => nullabilityConstraint; - public override IEnumerable DirectBaseTypes { - get { - bool hasNonInterfaceConstraint = false; + public override IReadOnlyList TypeConstraints { get; } + + IReadOnlyList MakeConstraints(IReadOnlyList constraints) + { + var result = new List(); + bool hasNonInterfaceConstraint = false; + if (constraints != null) { foreach (IType c in constraints) { - yield return c; + result.Add(new TypeConstraint(c)); if (c.Kind != TypeKind.Interface) hasNonInterfaceConstraint = true; } - // Do not add the 'System.Object' constraint if there is another constraint with a base class. - if (this.HasValueTypeConstraint || !hasNonInterfaceConstraint) { - yield return this.Compilation.FindType(this.HasValueTypeConstraint ? KnownTypeCode.ValueType : KnownTypeCode.Object); - } } + // Do not add the 'System.Object' constraint if there is another constraint with a base class. + if (this.HasValueTypeConstraint || !hasNonInterfaceConstraint) { + result.Add(new TypeConstraint(this.Compilation.FindType(this.HasValueTypeConstraint ? KnownTypeCode.ValueType : KnownTypeCode.Object))); + } + return result; } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs index 6cf394b2e..b92b6697f 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs @@ -169,6 +169,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation bool ITypeParameter.HasUnmanagedConstraint => false; Nullability ITypeParameter.NullabilityConstraint => Nullability.Oblivious; + IReadOnlyList ITypeParameter.TypeConstraints => EmptyList.Instance; + public override IType ChangeNullability(Nullability nullability) { if (nullability == Nullability.Oblivious) { diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs index 02a84fa79..02d439f84 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs @@ -139,6 +139,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation IReadOnlyList IMethod.TypeArguments => TypeParameters; bool IMethod.IsExtensionMethod => false; + bool IMethod.IsLocalFunction => false; bool IMethod.IsConstructor => symbolKind == SymbolKind.Constructor; bool IMethod.IsDestructor => symbolKind == SymbolKind.Destructor; bool IMethod.IsOperator => symbolKind == SymbolKind.Operator; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs index 7cee38c46..209d198eb 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs @@ -40,6 +40,7 @@ namespace ICSharpCode.Decompiler.TypeSystem Dynamic, TupleElementNames, Nullable, + NullableContext, Conditional, Obsolete, IsReadOnly, @@ -107,6 +108,7 @@ namespace ICSharpCode.Decompiler.TypeSystem new TopLevelTypeName("System.Runtime.CompilerServices", nameof(DynamicAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(TupleElementNamesAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", "NullableAttribute"), + new TopLevelTypeName("System.Runtime.CompilerServices", "NullableContextAttribute"), new TopLevelTypeName("System.Diagnostics", nameof(ConditionalAttribute)), new TopLevelTypeName("System", nameof(ObsoleteAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", "IsReadOnlyAttribute"), diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs new file mode 100644 index 000000000..6a42b4364 --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs @@ -0,0 +1,138 @@ +// Copyright (c) 2019 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; +using System.Collections.Generic; +using System.Reflection; +using ICSharpCode.Decompiler.Util; + +namespace ICSharpCode.Decompiler.TypeSystem.Implementation +{ + /// + /// A local function has zero or more compiler-generated parameters added at the end. + /// + class LocalFunctionMethod : IMethod + { + readonly IMethod baseMethod; + + public LocalFunctionMethod(IMethod baseMethod, int numberOfCompilerGeneratedParameters) + { + this.baseMethod = baseMethod; + this.NumberOfCompilerGeneratedParameters = numberOfCompilerGeneratedParameters; + } + + + public bool Equals(IMember obj, TypeVisitor typeNormalization) + { + if (!(obj is LocalFunctionMethod other)) + return false; + return baseMethod.Equals(other.baseMethod, typeNormalization) + && NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters; + } + + public override bool Equals(object obj) + { + if (!(obj is LocalFunctionMethod other)) + return false; + return baseMethod.Equals(other.baseMethod) + && NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters; + } + + public override int GetHashCode() + { + unchecked { + return baseMethod.GetHashCode() + NumberOfCompilerGeneratedParameters + 1; + } + } + + public override string ToString() + { + return string.Format("[LocalFunctionMethod: ReducedFrom={0}, NumberOfGeneratedParameters={1}]", ReducedFrom, NumberOfCompilerGeneratedParameters); + } + + internal int NumberOfCompilerGeneratedParameters { get; } + + public IMember MemberDefinition => this; + + public IType ReturnType => baseMethod.ReturnType; + IEnumerable IMember.ExplicitlyImplementedInterfaceMembers => baseMethod.ExplicitlyImplementedInterfaceMembers; + bool IMember.IsExplicitInterfaceImplementation => baseMethod.IsExplicitInterfaceImplementation; + public bool IsVirtual => baseMethod.IsVirtual; + public bool IsOverride => baseMethod.IsOverride; + public bool IsOverridable => baseMethod.IsOverridable; + public TypeParameterSubstitution Substitution => baseMethod.Substitution; + + public IMethod Specialize(TypeParameterSubstitution substitution) + { + return SpecializedMethod.Create(this, substitution); + } + + IMember IMember.Specialize(TypeParameterSubstitution substitution) + { + return Specialize(substitution); + } + + public IReadOnlyList TypeParameters => baseMethod.TypeParameters; + public bool IsExtensionMethod => baseMethod.IsExtensionMethod; + public bool IsLocalFunction => true; + public bool IsConstructor => baseMethod.IsConstructor; + public bool IsDestructor => baseMethod.IsDestructor; + public bool IsOperator => baseMethod.IsOperator; + public bool HasBody => baseMethod.HasBody; + public bool IsAccessor => baseMethod.IsAccessor; + public IMember AccessorOwner => baseMethod.AccessorOwner; + public MethodSemanticsAttributes AccessorKind => baseMethod.AccessorKind; + public IMethod ReducedFrom => baseMethod; + public IReadOnlyList TypeArguments => baseMethod.TypeArguments; + + List parameters; + public IReadOnlyList Parameters { + get { + if (parameters == null) + parameters = new List(baseMethod.Parameters.SkipLast(NumberOfCompilerGeneratedParameters)); + return parameters; + } + } + + public System.Reflection.Metadata.EntityHandle MetadataToken => baseMethod.MetadataToken; + public SymbolKind SymbolKind => baseMethod.SymbolKind; + public ITypeDefinition DeclaringTypeDefinition => baseMethod.DeclaringTypeDefinition; + public IType DeclaringType => baseMethod.DeclaringType; + public IModule ParentModule => baseMethod.ParentModule; + IEnumerable IEntity.GetAttributes() => baseMethod.GetAttributes(); + IEnumerable IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes(); + bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly; + /// + /// We consider local functions as always static, because they do not have a "this parameter". + /// Even local functions in instance methods capture this. + /// + public bool IsStatic => true; + public bool IsAbstract => baseMethod.IsAbstract; + public bool IsSealed => baseMethod.IsSealed; + + public Accessibility Accessibility => baseMethod.Accessibility; + + public string FullName => baseMethod.FullName; + public string Name => baseMethod.Name; + public string ReflectionName => baseMethod.ReflectionName; + public string Namespace => baseMethod.Namespace; + + public ICompilation Compilation => baseMethod.Compilation; + } +} + diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataEvent.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataEvent.cs index 43c4a868a..e85a559d3 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataEvent.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataEvent.cs @@ -79,8 +79,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return returnType; var metadata = module.metadata; var ev = metadata.GetEventDefinition(handle); - var context = new GenericContext(DeclaringTypeDefinition?.TypeParameters); - returnType = module.ResolveType(ev.Type, context, ev.GetCustomAttributes()); + var declaringTypeDef = DeclaringTypeDefinition; + var context = new GenericContext(declaringTypeDef?.TypeParameters); + var nullableContext = declaringTypeDef?.NullableContext ?? Nullability.Oblivious; + returnType = module.ResolveType(ev.Type, context, ev.GetCustomAttributes(), nullableContext); return LazyInit.GetOrSet(ref this.returnType, returnType); } } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs index 00d034bb5..18f513939 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs @@ -186,7 +186,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation ty = mod.ElementType; } ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, - fieldDef.GetCustomAttributes(), metadata, module.TypeSystemOptions); + fieldDef.GetCustomAttributes(), metadata, module.TypeSystemOptions, + DeclaringTypeDefinition?.NullableContext ?? Nullability.Oblivious); } catch (BadImageFormatException) { ty = SpecialType.UnknownType; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs index 2406051e3..d97382984 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs @@ -41,6 +41,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation readonly EntityHandle accessorOwner; public MethodSemanticsAttributes AccessorKind { get; } public bool IsExtensionMethod { get; } + bool IMethod.IsLocalFunction => false; // lazy-loaded fields: ITypeDefinition declaringType; @@ -148,6 +149,13 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } } + internal Nullability NullableContext { + get { + var methodDef = module.metadata.GetMethodDefinition(handle); + return methodDef.GetCustomAttributes().GetNullableContext(module.metadata) ?? DeclaringTypeDefinition.NullableContext; + } + } + private void DecodeSignature() { var methodDef = module.metadata.GetMethodDefinition(handle); @@ -155,8 +163,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation IType returnType; IParameter[] parameters; try { + var nullableContext = methodDef.GetCustomAttributes().GetNullableContext(module.metadata) ?? DeclaringTypeDefinition.NullableContext; var signature = methodDef.DecodeSignature(module.TypeProvider, genericContext); - (returnType, parameters) = DecodeSignature(module, this, signature, methodDef.GetParameters()); + (returnType, parameters) = DecodeSignature(module, this, signature, methodDef.GetParameters(), nullableContext); } catch (BadImageFormatException) { returnType = SpecialType.UnknownType; parameters = Empty.Array; @@ -165,7 +174,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation LazyInit.GetOrSet(ref this.parameters, parameters); } - internal static (IType, IParameter[]) DecodeSignature(MetadataModule module, IParameterizedMember owner, MethodSignature signature, ParameterHandleCollection? parameterHandles) + internal static (IType, IParameter[]) DecodeSignature(MetadataModule module, IParameterizedMember owner, MethodSignature signature, ParameterHandleCollection? parameterHandles, Nullability nullableContext) { var metadata = module.metadata; int i = 0; @@ -186,14 +195,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation // Fill gaps in the sequence with non-metadata parameters: while (i < par.SequenceNumber - 1) { parameterType = ApplyAttributeTypeVisitor.ApplyAttributesToType( - signature.ParameterTypes[i], module.Compilation, null, metadata, module.TypeSystemOptions); + signature.ParameterTypes[i], module.Compilation, null, metadata, module.TypeSystemOptions, nullableContext); parameters[i] = new DefaultParameter(parameterType, name: string.Empty, owner, - isRef: parameterType.Kind == TypeKind.ByReference); + referenceKind: parameterType.Kind == TypeKind.ByReference ? ReferenceKind.Ref : ReferenceKind.None); i++; } parameterType = ApplyAttributeTypeVisitor.ApplyAttributesToType( signature.ParameterTypes[i], module.Compilation, - par.GetCustomAttributes(), metadata, module.TypeSystemOptions); + par.GetCustomAttributes(), metadata, module.TypeSystemOptions, nullableContext); parameters[i] = new MetadataParameter(module, owner, parameterType, parameterHandle); i++; } @@ -201,9 +210,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } while (i < signature.RequiredParameterCount) { parameterType = ApplyAttributeTypeVisitor.ApplyAttributesToType( - signature.ParameterTypes[i], module.Compilation, null, metadata, module.TypeSystemOptions); + signature.ParameterTypes[i], module.Compilation, null, metadata, module.TypeSystemOptions, nullableContext); parameters[i] = new DefaultParameter(parameterType, name: string.Empty, owner, - isRef: parameterType.Kind == TypeKind.ByReference); + referenceKind: parameterType.Kind == TypeKind.ByReference ? ReferenceKind.Ref : ReferenceKind.None); i++; } if (signature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { @@ -212,7 +221,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } Debug.Assert(i == parameters.Length); var returnType = ApplyAttributeTypeVisitor.ApplyAttributesToType(signature.ReturnType, - module.Compilation, returnTypeAttributes, metadata, module.TypeSystemOptions); + module.Compilation, returnTypeAttributes, metadata, module.TypeSystemOptions, nullableContext); return (returnType, parameters); } #endregion diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs index 2b31d1f61..562d14ec7 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs @@ -81,25 +81,26 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation const ParameterAttributes inOut = ParameterAttributes.In | ParameterAttributes.Out; - public bool IsRef => DetectRefKind() == CSharp.Syntax.ParameterModifier.Ref; + public ReferenceKind ReferenceKind => DetectRefKind(); + public bool IsRef => DetectRefKind() == ReferenceKind.Ref; public bool IsOut => Type.Kind == TypeKind.ByReference && (attributes & inOut) == ParameterAttributes.Out; - public bool IsIn => DetectRefKind() == CSharp.Syntax.ParameterModifier.In; + public bool IsIn => DetectRefKind() == ReferenceKind.In; public bool IsOptional => (attributes & ParameterAttributes.Optional) != 0; - CSharp.Syntax.ParameterModifier DetectRefKind() + ReferenceKind DetectRefKind() { if (Type.Kind != TypeKind.ByReference) - return CSharp.Syntax.ParameterModifier.None; + return ReferenceKind.None; if ((attributes & inOut) == ParameterAttributes.Out) - return CSharp.Syntax.ParameterModifier.Out; + return ReferenceKind.Out; if ((module.TypeSystemOptions & TypeSystemOptions.ReadOnlyStructsAndParameters) != 0) { var metadata = module.metadata; var parameterDef = metadata.GetParameter(handle); if (parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsReadOnly)) - return CSharp.Syntax.ParameterModifier.In; + return ReferenceKind.In; } - return CSharp.Syntax.ParameterModifier.Ref; + return ReferenceKind.Ref; } public bool IsParams { diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs index 65eaeda55..264bb5e02 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs @@ -123,13 +123,22 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation var signature = propertyDef.DecodeSignature(module.TypeProvider, genericContext); var accessors = propertyDef.GetAccessors(); ParameterHandleCollection? parameterHandles; - if (!accessors.Getter.IsNil) - parameterHandles = module.metadata.GetMethodDefinition(accessors.Getter).GetParameters(); - else if (!accessors.Setter.IsNil) - parameterHandles = module.metadata.GetMethodDefinition(accessors.Setter).GetParameters(); - else + Nullability nullableContext; + if (!accessors.Getter.IsNil) { + var getter = module.metadata.GetMethodDefinition(accessors.Getter); + parameterHandles = getter.GetParameters(); + nullableContext = getter.GetCustomAttributes().GetNullableContext(module.metadata) + ?? DeclaringTypeDefinition?.NullableContext ?? Nullability.Oblivious; + } else if (!accessors.Setter.IsNil) { + var setter = module.metadata.GetMethodDefinition(accessors.Setter); + parameterHandles = setter.GetParameters(); + nullableContext = setter.GetCustomAttributes().GetNullableContext(module.metadata) + ?? DeclaringTypeDefinition?.NullableContext ?? Nullability.Oblivious; + } else { parameterHandles = null; - (returnType, parameters) = MetadataMethod.DecodeSignature(module, this, signature, parameterHandles); + nullableContext = DeclaringTypeDefinition?.NullableContext ?? Nullability.Oblivious; + } + (returnType, parameters) = MetadataMethod.DecodeSignature(module, this, signature, parameterHandles, nullableContext); } catch (BadImageFormatException) { returnType = SpecialType.UnknownType; parameters = Empty.Array; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs index 91ad0be3b..45fbaf158 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs @@ -51,6 +51,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public KnownTypeCode KnownTypeCode { get; } public IType EnumUnderlyingType { get; } public bool HasExtensionMethods { get; } + public Nullability NullableContext { get; } // lazy-loaded: IMember[] members; @@ -77,10 +78,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation // Create type parameters: this.TypeParameters = MetadataTypeParameter.Create(module, this.DeclaringTypeDefinition, this, td.GetGenericParameters()); + + this.NullableContext = td.GetCustomAttributes().GetNullableContext(metadata) ?? this.DeclaringTypeDefinition.NullableContext; } else { // Create type parameters: this.TypeParameters = MetadataTypeParameter.Create(module, this, td.GetGenericParameters()); + this.NullableContext = td.GetCustomAttributes().GetNullableContext(metadata) ?? module.NullableContext; + var topLevelTypeName = fullTypeName.TopLevelTypeName; for (int i = 0; i < KnownTypeReference.KnownTypeCodeCount; i++) { var ktr = KnownTypeReference.Get((KnownTypeCode)i); @@ -301,7 +306,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } foreach (var h in interfaceImplCollection) { var iface = metadata.GetInterfaceImplementation(h); - baseTypes.Add(module.ResolveType(iface.Interface, context, iface.GetCustomAttributes())); + baseTypes.Add(module.ResolveType(iface.Interface, context, iface.GetCustomAttributes(), Nullability.Oblivious)); } return LazyInit.GetOrSet(ref this.directBaseTypes, baseTypes); } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs index 8c84c0500..75597198c 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; @@ -34,7 +35,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation readonly GenericParameterAttributes attr; // lazy-loaded: - IReadOnlyList constraints; + IReadOnlyList constraints; byte unmanagedConstraint = ThreeState.Unknown; const byte nullabilityNotYetLoaded = 255; byte nullabilityConstraint = nullabilityNotYetLoaded; @@ -161,36 +162,58 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } } } - return Nullability.Oblivious; + if (Owner is MetadataMethod method) { + return method.NullableContext; + } else if (Owner is ITypeDefinition td) { + return td.NullableContext; + } else { + return Nullability.Oblivious; + } } - public override IEnumerable DirectBaseTypes { + public override IReadOnlyList TypeConstraints { get { var constraints = LazyInit.VolatileRead(ref this.constraints); - if (constraints != null) - return constraints; - return LazyInit.GetOrSet(ref this.constraints, DecodeConstraints()); + if (constraints == null) { + constraints = LazyInit.GetOrSet(ref this.constraints, DecodeConstraints()); + } + return constraints; } } - private IReadOnlyList DecodeConstraints() + private IReadOnlyList DecodeConstraints() { var metadata = module.metadata; var gp = metadata.GetGenericParameter(handle); + Nullability nullableContext; + if (Owner is ITypeDefinition typeDef) { + nullableContext = typeDef.NullableContext; + } else if (Owner is MetadataMethod method) { + nullableContext = method.NullableContext; + } else { + nullableContext = Nullability.Oblivious; + } var constraintHandleCollection = gp.GetConstraints(); - List result = new List(constraintHandleCollection.Count + 1); + var result = new List(constraintHandleCollection.Count + 1); bool hasNonInterfaceConstraint = false; foreach (var constraintHandle in constraintHandleCollection) { var constraint = metadata.GetGenericParameterConstraint(constraintHandle); - var ty = module.ResolveType(constraint.Type, new GenericContext(Owner), constraint.GetCustomAttributes()); - result.Add(ty); + var attrs = constraint.GetCustomAttributes(); + var ty = module.ResolveType(constraint.Type, new GenericContext(Owner), attrs, nullableContext); + if (attrs.Count == 0) { + result.Add(new TypeConstraint(ty)); + } else { + AttributeListBuilder b = new AttributeListBuilder(module); + b.Add(attrs, SymbolKind.Constraint); + result.Add(new TypeConstraint(ty, b.Build())); + } hasNonInterfaceConstraint |= (ty.Kind != TypeKind.Interface); } if (this.HasValueTypeConstraint) { - result.Add(Compilation.FindType(KnownTypeCode.ValueType)); + result.Add(new TypeConstraint(Compilation.FindType(KnownTypeCode.ValueType))); } else if (!hasNonInterfaceConstraint) { - result.Add(Compilation.FindType(KnownTypeCode.Object)); + result.Add(new TypeConstraint(Compilation.FindType(KnownTypeCode.Object))); } return result; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs index face71f47..fbab0db4b 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs @@ -177,6 +177,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation bool IType.IsByRefLike => false; Nullability IType.Nullability => Nullability.Oblivious; + Nullability ITypeDefinition.NullableContext => Nullability.Oblivious; IType IType.ChangeNullability(Nullability nullability) { diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs index cb0b8c67f..63e7b0885 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs @@ -19,6 +19,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation // the NullabilityAnnotatedType wrapper only in some limited places. Debug.Assert(type is ITypeDefinition || type.Kind == TypeKind.Dynamic + || type.Kind == TypeKind.Unknown || (type is ITypeParameter && this is ITypeParameter)); this.nullability = nullability; } @@ -50,10 +51,21 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public override IType VisitChildren(TypeVisitor visitor) { IType newBase = baseType.AcceptVisitor(visitor); - if (newBase != baseType) - return newBase.ChangeNullability(nullability); - else + if (newBase != baseType) { + if (newBase.Nullability == Nullability.Nullable) { + // `T!` with substitution T=`U?` becomes `U?` + // This happens during type substitution for generic methods. + return newBase; + } + if (newBase.Kind == TypeKind.TypeParameter || newBase.IsReferenceType == true) { + return newBase.ChangeNullability(nullability); + } else { + // `T!` with substitution T=`int` becomes `int`, not `int!` + return newBase; + } + } else { return this; + } } public override string ToString() @@ -80,6 +92,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation this.baseType = type; } + public ITypeParameter OriginalTypeParameter => baseType; + SymbolKind ITypeParameter.OwnerType => baseType.OwnerType; IEntity ITypeParameter.Owner => baseType.Owner; int ITypeParameter.Index => baseType.Index; @@ -93,6 +107,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation bool ITypeParameter.HasValueTypeConstraint => baseType.HasValueTypeConstraint; bool ITypeParameter.HasUnmanagedConstraint => baseType.HasUnmanagedConstraint; Nullability ITypeParameter.NullabilityConstraint => baseType.NullabilityConstraint; + IReadOnlyList ITypeParameter.TypeConstraints => baseType.TypeConstraints; SymbolKind ISymbol.SymbolKind => SymbolKind.TypeParameter; IEnumerable ITypeParameter.GetAttributes() => baseType.GetAttributes(); } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs index 8f9937d87..311395fc4 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs @@ -102,11 +102,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return specializedTypeParameters ?? methodDefinition.TypeParameters; } } - + public bool IsExtensionMethod { get { return methodDefinition.IsExtensionMethod; } } - + + public bool IsLocalFunction { + get { return methodDefinition.IsLocalFunction; } + } + public bool IsConstructor { get { return methodDefinition.IsConstructor; } } @@ -199,7 +203,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation b.Append('['); for (int i = 0; i < this.TypeArguments.Count; i++) { if (i > 0) b.Append(", "); - b.Append(this.TypeArguments[i].ReflectionName); + b.Append(this.TypeArguments[i].ToString()); } b.Append(']'); } else if (this.TypeParameters.Count > 0) { @@ -212,7 +216,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation b.Append(this.Parameters[i].ToString()); } b.Append("):"); - b.Append(this.ReturnType.ReflectionName); + b.Append(this.ReturnType.ToString()); b.Append(']'); return b.ToString(); } @@ -253,9 +257,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public override Nullability NullabilityConstraint => baseTp.NullabilityConstraint; - public override IEnumerable DirectBaseTypes { + IReadOnlyList typeConstraints; + + public override IReadOnlyList TypeConstraints { get { - return baseTp.DirectBaseTypes.Select(t => t.AcceptVisitor(substitution)); + var typeConstraints = LazyInit.VolatileRead(ref this.typeConstraints); + if (typeConstraints == null) { + typeConstraints = baseTp.TypeConstraints.SelectReadOnlyArray(c => new TypeConstraint(c.Type.AcceptVisitor(substitution), c.Attributes)); + typeConstraints = LazyInit.GetOrSet(ref this.typeConstraints, typeConstraints); + } + return typeConstraints; } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedParameter.cs index 4beb9d038..2c3fa6a90 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedParameter.cs @@ -36,6 +36,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } IEnumerable IParameter.GetAttributes() => baseParameter.GetAttributes(); + ReferenceKind IParameter.ReferenceKind => baseParameter.ReferenceKind; bool IParameter.IsRef => baseParameter.IsRef; bool IParameter.IsOut => baseParameter.IsOut; bool IParameter.IsIn => baseParameter.IsIn; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/ThreeState.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/ThreeState.cs index f558911e0..6b679f39e 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/ThreeState.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/ThreeState.cs @@ -19,7 +19,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// - /// Constants used instead of + /// Constants used instead of bool? /// in multithreaded code, as bool? might produce torn reads. /// static class ThreeState diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeWithElementType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeWithElementType.cs index eadea20cd..853d6acc7 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeWithElementType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeWithElementType.cs @@ -46,7 +46,12 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public override string ReflectionName { get { return elementType.ReflectionName + NameSuffix; } } - + + public override string ToString() + { + return elementType.ToString() + NameSuffix; + } + public abstract string NameSuffix { get; } public IType ElementType { diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs index 55c8aca94..83ae0801c 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs @@ -94,7 +94,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public override bool? IsReferenceType { get { return isReferenceType; } } - + + public override IType ChangeNullability(Nullability nullability) + { + if (nullability == Nullability.Oblivious) + return this; + else + return new NullabilityAnnotatedType(this, nullability); + } + public override int GetHashCode() { return (namespaceKnown ? 812571 : 12651) ^ fullTypeName.GetHashCode(); diff --git a/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs b/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs index ec6b0a9e7..1fb75d51d 100644 --- a/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs +++ b/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs @@ -40,6 +40,7 @@ namespace ICSharpCode.Decompiler.TypeSystem internal readonly MetadataReader metadata; readonly TypeSystemOptions options; internal readonly TypeProvider TypeProvider; + internal readonly Nullability NullableContext; readonly MetadataNamespace rootNamespace; readonly MetadataTypeDefinition[] typeDefs; @@ -66,6 +67,7 @@ namespace ICSharpCode.Decompiler.TypeSystem this.AssemblyName = metadata.GetString(moddef.Name); this.FullAssemblyName = this.AssemblyName; } + this.NullableContext = metadata.GetModuleDefinition().GetCustomAttributes().GetNullableContext(metadata) ?? Nullability.Oblivious; this.rootNamespace = new MetadataNamespace(this, null, string.Empty, metadata.GetNamespaceDefinitionRoot()); if (!options.HasFlag(TypeSystemOptions.Uncached)) { @@ -267,12 +269,12 @@ namespace ICSharpCode.Decompiler.TypeSystem #endregion #region Resolve Type - public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, CustomAttributeHandleCollection? typeAttributes = null) + public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, CustomAttributeHandleCollection? typeAttributes = null, Nullability nullableContext = Nullability.Oblivious) { - return ResolveType(typeRefDefSpec, context, options, typeAttributes); + return ResolveType(typeRefDefSpec, context, options, typeAttributes, nullableContext); } - public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, TypeSystemOptions customOptions, CustomAttributeHandleCollection? typeAttributes = null) + public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, TypeSystemOptions customOptions, CustomAttributeHandleCollection? typeAttributes = null, Nullability nullableContext = Nullability.Oblivious) { if (typeRefDefSpec.IsNil) return SpecialType.UnknownType; @@ -293,7 +295,7 @@ namespace ICSharpCode.Decompiler.TypeSystem default: throw new BadImageFormatException("Not a type handle"); } - ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, typeAttributes, metadata, customOptions); + ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, typeAttributes, metadata, customOptions, nullableContext); return ty; } @@ -303,14 +305,14 @@ namespace ICSharpCode.Decompiler.TypeSystem var ty = ResolveType(declaringTypeReference, context, options & ~(TypeSystemOptions.Dynamic | TypeSystemOptions.Tuple | TypeSystemOptions.NullabilityAnnotations)); // but substitute tuple types in type arguments: - ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, null, metadata, options, typeChildrenOnly: true); + ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, null, metadata, options, Nullability.Oblivious, typeChildrenOnly: true); return ty; } IType IntroduceTupleTypes(IType ty) { // run ApplyAttributeTypeVisitor without attributes, in order to introduce tuple types - return ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, null, metadata, options); + return ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, null, metadata, options, Nullability.Oblivious); } #endregion @@ -475,7 +477,9 @@ namespace ICSharpCode.Decompiler.TypeSystem typeParameters.Add(new DefaultTypeParameter(m, i)); } m.TypeParameters = typeParameters; - substitution = new TypeParameterSubstitution(null, typeParameters); + substitution = new TypeParameterSubstitution(declaringType.TypeArguments, typeParameters); + } else if (declaringType.TypeArguments.Count > 0) { + substitution = declaringType.GetSubstitution(); } var parameters = new List(); for (int i = 0; i < signature.RequiredParameterCount; i++) { @@ -551,6 +555,10 @@ namespace ICSharpCode.Decompiler.TypeSystem var field = declaringType.GetFields(f => f.Name == name && CompareTypes(f.ReturnType, signature), GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); if (field == null) { + // If it's a field in a generic type, we need to substitute the type arguments: + if (declaringType.TypeArguments.Count > 0) { + signature = signature.AcceptVisitor(declaringType.GetSubstitution()); + } field = new FakeField(Compilation) { ReturnType = signature, Name = name, diff --git a/ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs b/ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs index 203e1e971..0f8d8a2a4 100644 --- a/ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs +++ b/ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs @@ -42,6 +42,8 @@ namespace ICSharpCode.Decompiler.TypeSystem return DummyTypeParameter.GetMethodTypeParameter(type.Index); } else if (type.OwnerType == SymbolKind.TypeDefinition && ReplaceClassTypeParametersWithDummy) { return DummyTypeParameter.GetClassTypeParameter(type.Index); + } else if (RemoveNullability && type is NullabilityAnnotatedTypeParameter natp) { + return natp.TypeWithoutAnnotation.AcceptVisitor(this); } else { return base.VisitTypeParameter(type); } @@ -72,7 +74,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public override IType VisitNullabilityAnnotatedType(NullabilityAnnotatedType type) { if (RemoveNullability) - return base.VisitNullabilityAnnotatedType(type).ChangeNullability(Nullability.Oblivious); + return type.TypeWithoutAnnotation.AcceptVisitor(this); else return base.VisitNullabilityAnnotatedType(type); } diff --git a/ICSharpCode.Decompiler/TypeSystem/ParameterListComparer.cs b/ICSharpCode.Decompiler/TypeSystem/ParameterListComparer.cs index 84e99aea2..20be982d6 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ParameterListComparer.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ParameterListComparer.cs @@ -66,11 +66,7 @@ namespace ICSharpCode.Decompiler.TypeSystem return false; if (includeModifiers) { - if (a.IsIn != b.IsIn) - return false; - if (a.IsOut != b.IsOut) - return false; - if (a.IsRef != b.IsRef) + if (a.ReferenceKind != b.ReferenceKind) return false; if (a.IsParams != b.IsParams) return false; diff --git a/ICSharpCode.Decompiler/TypeSystem/ParameterizedType.cs b/ICSharpCode.Decompiler/TypeSystem/ParameterizedType.cs index de7a25c2b..5dd907ace 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ParameterizedType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ParameterizedType.cs @@ -140,10 +140,20 @@ namespace ICSharpCode.Decompiler.TypeSystem return b.ToString(); } } - + public override string ToString() { - return ReflectionName; + StringBuilder b = new StringBuilder(genericType.ToString()); + b.Append('['); + for (int i = 0; i < typeArguments.Length; i++) { + if (i > 0) + b.Append(','); + b.Append('['); + b.Append(typeArguments[i].ToString()); + b.Append(']'); + } + b.Append(']'); + return b.ToString(); } public IReadOnlyList TypeArguments => typeArguments; diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeKind.cs b/ICSharpCode.Decompiler/TypeSystem/TypeKind.cs index 9556ef249..5f107d6fa 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeKind.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeKind.cs @@ -41,7 +41,7 @@ namespace ICSharpCode.Decompiler.TypeSystem Enum, /// The System.Void type. - /// + /// Void, /// Type used for invalid expressions and for types whose definition could not be found. @@ -58,7 +58,7 @@ namespace ICSharpCode.Decompiler.TypeSystem Dynamic, /// Represents missing type arguments in partially parameterized types. /// - /// + /// IType.GetNestedTypes(Predicate{ITypeDefinition}, GetMemberOptions) UnboundTypeArgument, /// The type is a type parameter. @@ -74,9 +74,6 @@ namespace ICSharpCode.Decompiler.TypeSystem /// A managed reference type /// ByReference, - /// An anonymous type - /// - Anonymous, /// Intersection of several types /// diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs b/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs index ccf21ac9c..0ab8f4faa 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs @@ -141,8 +141,12 @@ namespace ICSharpCode.Decompiler.TypeSystem if (name == null) { return null; } - return ReflectionHelper.ParseReflectionName(name) - .Resolve(module != null ? new SimpleTypeResolveContext(module) : new SimpleTypeResolveContext(compilation)); + try { + return ReflectionHelper.ParseReflectionName(name) + .Resolve(module != null ? new SimpleTypeResolveContext(module) : new SimpleTypeResolveContext(compilation)); + } catch (ReflectionNameParseException ex) { + throw new BadImageFormatException($"Invalid type name: \"{name}\": {ex.Message}"); + } } public IType GetTypeFromSpecification(SRM.MetadataReader reader, GenericContext genericContext, SRM.TypeSpecificationHandle handle, byte rawTypeKind) diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs index bbba0455b..78c5a1892 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs @@ -384,9 +384,9 @@ namespace ICSharpCode.Decompiler.TypeSystem /// (if the given in an override) /// should be returned. /// - public static bool HasAttribute(this IEntity entity, KnownAttribute attrType, bool inherit=false) + public static bool HasAttribute(this IEntity entity, KnownAttribute attributeType, bool inherit=false) { - return GetAttribute(entity, attrType, inherit) != null; + return GetAttribute(entity, attributeType, inherit) != null; } /// @@ -445,9 +445,9 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// The parameter on which the attributes are declared. /// The attribute type to look for. - public static bool HasAttribute(this IParameter parameter, KnownAttribute attrType) + public static bool HasAttribute(this IParameter parameter, KnownAttribute attributeType) { - return GetAttribute(parameter, attrType) != null; + return GetAttribute(parameter, attributeType) != null; } /// @@ -521,9 +521,9 @@ namespace ICSharpCode.Decompiler.TypeSystem return SpecialType.UnknownType; } - public static bool FullNameIs(this IMethod method, string type, string name) + public static bool FullNameIs(this IMember member, string type, string name) { - return method.Name == name && method.DeclaringType?.FullName == type; + return member.Name == name && member.DeclaringType?.FullName == type; } public static KnownAttribute IsBuiltinAttribute(this ITypeDefinition type) diff --git a/ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs b/ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs index 0356f544a..a151d1464 100644 --- a/ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs @@ -129,6 +129,10 @@ namespace ICSharpCode.Decompiler.TypeSystem get { return baseMethod.IsExtensionMethod; } } + bool IMethod.IsLocalFunction { + get { return baseMethod.IsLocalFunction; } + } + public bool IsConstructor { get { return baseMethod.IsConstructor; } } diff --git a/ILSpy-tests b/ILSpy-tests index 28f74a2a8..aa8f1197e 160000 --- a/ILSpy-tests +++ b/ILSpy-tests @@ -1 +1 @@ -Subproject commit 28f74a2a8f4050fe455a982dbd361a981841a9bd +Subproject commit aa8f1197e6a513bcc10bcc38ec7d2143d27a2246 diff --git a/ILSpy.AddIn/ILSpy.AddIn.csproj b/ILSpy.AddIn/ILSpy.AddIn.csproj index a43410ba5..1126fcf82 100644 --- a/ILSpy.AddIn/ILSpy.AddIn.csproj +++ b/ILSpy.AddIn/ILSpy.AddIn.csproj @@ -51,6 +51,7 @@ + @@ -131,6 +132,7 @@ + diff --git a/ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs b/ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs index be9039601..682fc679e 100644 --- a/ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs +++ b/ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs @@ -107,6 +107,18 @@ namespace ILSpy.BamlDecompiler.Tests RunTest("cases/issue1435"); } + [Test] + public void Issue1546() + { + RunTest("cases/issue1546"); + } + + [Test] + public void Issue1547() + { + RunTest("cases/issue1547"); + } + #region RunTest void RunTest(string name) { diff --git a/ILSpy.BamlDecompiler.Tests/Cases/AvalonDockCommon.xaml b/ILSpy.BamlDecompiler.Tests/Cases/AvalonDockCommon.xaml index bcb18aed2..31d456472 100644 --- a/ILSpy.BamlDecompiler.Tests/Cases/AvalonDockCommon.xaml +++ b/ILSpy.BamlDecompiler.Tests/Cases/AvalonDockCommon.xaml @@ -35,8 +35,8 @@ - - + + diff --git a/ILSpy.BamlDecompiler.Tests/Cases/Issue1546.xaml b/ILSpy.BamlDecompiler.Tests/Cases/Issue1546.xaml new file mode 100644 index 000000000..c09ce5b77 --- /dev/null +++ b/ILSpy.BamlDecompiler.Tests/Cases/Issue1546.xaml @@ -0,0 +1,35 @@ + + #f1f1f1 + #2d2d30 + #3f3f41 + #007acc + #333337 + #3f3f3f + #999999 + #686868 + #9e9e9e + #f1f1f1 + #2d2d30 + #3f3f41 + #b20000 + #990000 + #009700 + #007400 + #1c97ea + #007acc + #333337 + #3f3f3f + #999999 + #686868 + #9e9e9e + + + + + + #FFD1D1D1 + + #FFA3A3A3 + + #FF747474 + \ No newline at end of file diff --git a/ILSpy.BamlDecompiler.Tests/Cases/Issue1547.xaml b/ILSpy.BamlDecompiler.Tests/Cases/Issue1547.xaml new file mode 100644 index 000000000..8943fcfa3 --- /dev/null +++ b/ILSpy.BamlDecompiler.Tests/Cases/Issue1547.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + diff --git a/ILSpy.BamlDecompiler.Tests/Cases/Issue1547.xaml.cs b/ILSpy.BamlDecompiler.Tests/Cases/Issue1547.xaml.cs new file mode 100644 index 000000000..f9be09f20 --- /dev/null +++ b/ILSpy.BamlDecompiler.Tests/Cases/Issue1547.xaml.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace ILSpy.BamlDecompiler.Tests.Cases +{ + /// + /// Interaction logic for Issue1547.xaml + /// + public partial class Issue1547 : Window + { + public Issue1547() + { + InitializeComponent(); + } + } +} diff --git a/ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj b/ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj index 198bbe621..1958ed447 100644 --- a/ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj +++ b/ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj @@ -31,7 +31,7 @@ - + @@ -47,6 +47,7 @@ AttachedEvent.xaml + MyControl.xaml @@ -74,6 +75,12 @@ MSBuild:Compile + + MSBuild:Compile + + + MSBuild:Compile + Designer diff --git a/ILSpy.BamlDecompiler.Tests/app.config b/ILSpy.BamlDecompiler.Tests/app.config index 5095b0e47..8eaf7eb82 100644 --- a/ILSpy.BamlDecompiler.Tests/app.config +++ b/ILSpy.BamlDecompiler.Tests/app.config @@ -7,8 +7,14 @@ + + + + + + - + diff --git a/ILSpy.BamlDecompiler/Baml/KnownThings.cs b/ILSpy.BamlDecompiler/Baml/KnownThings.cs index 5b82a0221..c5ab16072 100644 --- a/ILSpy.BamlDecompiler/Baml/KnownThings.cs +++ b/ILSpy.BamlDecompiler/Baml/KnownThings.cs @@ -45,11 +45,15 @@ namespace ILSpy.BamlDecompiler.Baml { strings = new Dictionary(); resources = new Dictionary(); - InitAssemblies(); - InitTypes(); - InitMembers(); - InitStrings(); - InitResources(); + try { + InitAssemblies(); + InitTypes(); + InitMembers(); + InitStrings(); + InitResources(); + } catch (Exception ex) { + throw new ICSharpCode.Decompiler.DecompilerException(typeSystem.MainModule.PEFile, ex.Message, ex); + } } public Func Types => id => types[id]; @@ -57,7 +61,14 @@ namespace ILSpy.BamlDecompiler.Baml { public Func Strings => id => strings[id]; public Func Resources => id => resources[id]; public IModule FrameworkAssembly => assemblies[0]; - IModule ResolveAssembly(string name) => typeSystem.Modules.First(m => m.FullAssemblyName == name); + IModule ResolveAssembly(string name) + { + IModule module = typeSystem.Modules.FirstOrDefault(m => m.FullAssemblyName == name); + if (module == null) + throw new Exception("Could not resolve known assembly '" + name + "'!"); + return module; + } + ITypeDefinition InitType(IModule assembly, string ns, string name) => assembly.GetTypeDefinition(new TopLevelTypeName(ns, name)); KnownMember InitMember(KnownTypes parent, string name, ITypeDefinition type) => new KnownMember(parent, types[parent], name, type); } diff --git a/ILSpy.BamlDecompiler/Baml/test.cs b/ILSpy.BamlDecompiler/Baml/test.cs deleted file mode 100644 index c2727645f..000000000 --- a/ILSpy.BamlDecompiler/Baml/test.cs +++ /dev/null @@ -1,2325 +0,0 @@ - internal enum KnownTypes : short { - Unknown = 0, - AccessText = 1, - AdornedElementPlaceholder = 2, - Adorner = 3, - AdornerDecorator = 4, - AdornerLayer = 5, - AffineTransform3D = 6, - AmbientLight = 7, - AnchoredBlock = 8, - Animatable = 9, - AnimationClock = 10, - AnimationTimeline = 11, - Application = 12, - ArcSegment = 13, - ArrayExtension = 14, - AxisAngleRotation3D = 15, - BaseIListConverter = 16, - BeginStoryboard = 17, - BevelBitmapEffect = 18, - BezierSegment = 19, - Binding = 20, - BindingBase = 21, - BindingExpression = 22, - BindingExpressionBase = 23, - BindingListCollectionView = 24, - BitmapDecoder = 25, - BitmapEffect = 26, - BitmapEffectCollection = 27, - BitmapEffectGroup = 28, - BitmapEffectInput = 29, - BitmapEncoder = 30, - BitmapFrame = 31, - BitmapImage = 32, - BitmapMetadata = 33, - BitmapPalette = 34, - BitmapSource = 35, - Block = 36, - BlockUIContainer = 37, - BlurBitmapEffect = 38, - BmpBitmapDecoder = 39, - BmpBitmapEncoder = 40, - Bold = 41, - BoolIListConverter = 42, - Boolean = 43, - BooleanAnimationBase = 44, - BooleanAnimationUsingKeyFrames = 45, - BooleanConverter = 46, - BooleanKeyFrame = 47, - BooleanKeyFrameCollection = 48, - BooleanToVisibilityConverter = 49, - Border = 50, - BorderGapMaskConverter = 51, - Brush = 52, - BrushConverter = 53, - BulletDecorator = 54, - Button = 55, - ButtonBase = 56, - Byte = 57, - ByteAnimation = 58, - ByteAnimationBase = 59, - ByteAnimationUsingKeyFrames = 60, - ByteConverter = 61, - ByteKeyFrame = 62, - ByteKeyFrameCollection = 63, - CachedBitmap = 64, - Camera = 65, - Canvas = 66, - Char = 67, - CharAnimationBase = 68, - CharAnimationUsingKeyFrames = 69, - CharConverter = 70, - CharIListConverter = 71, - CharKeyFrame = 72, - CharKeyFrameCollection = 73, - CheckBox = 74, - Clock = 75, - ClockController = 76, - ClockGroup = 77, - CollectionContainer = 78, - CollectionView = 79, - CollectionViewSource = 80, - Color = 81, - ColorAnimation = 82, - ColorAnimationBase = 83, - ColorAnimationUsingKeyFrames = 84, - ColorConvertedBitmap = 85, - ColorConvertedBitmapExtension = 86, - ColorConverter = 87, - ColorKeyFrame = 88, - ColorKeyFrameCollection = 89, - ColumnDefinition = 90, - CombinedGeometry = 91, - ComboBox = 92, - ComboBoxItem = 93, - CommandConverter = 94, - ComponentResourceKey = 95, - ComponentResourceKeyConverter = 96, - CompositionTarget = 97, - Condition = 98, - ContainerVisual = 99, - ContentControl = 100, - ContentElement = 101, - ContentPresenter = 102, - ContentPropertyAttribute = 103, - ContentWrapperAttribute = 104, - ContextMenu = 105, - ContextMenuService = 106, - Control = 107, - ControlTemplate = 108, - ControllableStoryboardAction = 109, - CornerRadius = 110, - CornerRadiusConverter = 111, - CroppedBitmap = 112, - CultureInfo = 113, - CultureInfoConverter = 114, - CultureInfoIetfLanguageTagConverter = 115, - Cursor = 116, - CursorConverter = 117, - DashStyle = 118, - DataChangedEventManager = 119, - DataTemplate = 120, - DataTemplateKey = 121, - DataTrigger = 122, - DateTime = 123, - DateTimeConverter = 124, - DateTimeConverter2 = 125, - Decimal = 126, - DecimalAnimation = 127, - DecimalAnimationBase = 128, - DecimalAnimationUsingKeyFrames = 129, - DecimalConverter = 130, - DecimalKeyFrame = 131, - DecimalKeyFrameCollection = 132, - Decorator = 133, - DefinitionBase = 134, - DependencyObject = 135, - DependencyProperty = 136, - DependencyPropertyConverter = 137, - DialogResultConverter = 138, - DiffuseMaterial = 139, - DirectionalLight = 140, - DiscreteBooleanKeyFrame = 141, - DiscreteByteKeyFrame = 142, - DiscreteCharKeyFrame = 143, - DiscreteColorKeyFrame = 144, - DiscreteDecimalKeyFrame = 145, - DiscreteDoubleKeyFrame = 146, - DiscreteInt16KeyFrame = 147, - DiscreteInt32KeyFrame = 148, - DiscreteInt64KeyFrame = 149, - DiscreteMatrixKeyFrame = 150, - DiscreteObjectKeyFrame = 151, - DiscretePoint3DKeyFrame = 152, - DiscretePointKeyFrame = 153, - DiscreteQuaternionKeyFrame = 154, - DiscreteRectKeyFrame = 155, - DiscreteRotation3DKeyFrame = 156, - DiscreteSingleKeyFrame = 157, - DiscreteSizeKeyFrame = 158, - DiscreteStringKeyFrame = 159, - DiscreteThicknessKeyFrame = 160, - DiscreteVector3DKeyFrame = 161, - DiscreteVectorKeyFrame = 162, - DockPanel = 163, - DocumentPageView = 164, - DocumentReference = 165, - DocumentViewer = 166, - DocumentViewerBase = 167, - Double = 168, - DoubleAnimation = 169, - DoubleAnimationBase = 170, - DoubleAnimationUsingKeyFrames = 171, - DoubleAnimationUsingPath = 172, - DoubleCollection = 173, - DoubleCollectionConverter = 174, - DoubleConverter = 175, - DoubleIListConverter = 176, - DoubleKeyFrame = 177, - DoubleKeyFrameCollection = 178, - Drawing = 179, - DrawingBrush = 180, - DrawingCollection = 181, - DrawingContext = 182, - DrawingGroup = 183, - DrawingImage = 184, - DrawingVisual = 185, - DropShadowBitmapEffect = 186, - Duration = 187, - DurationConverter = 188, - DynamicResourceExtension = 189, - DynamicResourceExtensionConverter = 190, - Ellipse = 191, - EllipseGeometry = 192, - EmbossBitmapEffect = 193, - EmissiveMaterial = 194, - EnumConverter = 195, - EventManager = 196, - EventSetter = 197, - EventTrigger = 198, - Expander = 199, - Expression = 200, - ExpressionConverter = 201, - Figure = 202, - FigureLength = 203, - FigureLengthConverter = 204, - FixedDocument = 205, - FixedDocumentSequence = 206, - FixedPage = 207, - Floater = 208, - FlowDocument = 209, - FlowDocumentPageViewer = 210, - FlowDocumentReader = 211, - FlowDocumentScrollViewer = 212, - FocusManager = 213, - FontFamily = 214, - FontFamilyConverter = 215, - FontSizeConverter = 216, - FontStretch = 217, - FontStretchConverter = 218, - FontStyle = 219, - FontStyleConverter = 220, - FontWeight = 221, - FontWeightConverter = 222, - FormatConvertedBitmap = 223, - Frame = 224, - FrameworkContentElement = 225, - FrameworkElement = 226, - FrameworkElementFactory = 227, - FrameworkPropertyMetadata = 228, - FrameworkPropertyMetadataOptions = 229, - FrameworkRichTextComposition = 230, - FrameworkTemplate = 231, - FrameworkTextComposition = 232, - Freezable = 233, - GeneralTransform = 234, - GeneralTransformCollection = 235, - GeneralTransformGroup = 236, - Geometry = 237, - Geometry3D = 238, - GeometryCollection = 239, - GeometryConverter = 240, - GeometryDrawing = 241, - GeometryGroup = 242, - GeometryModel3D = 243, - GestureRecognizer = 244, - GifBitmapDecoder = 245, - GifBitmapEncoder = 246, - GlyphRun = 247, - GlyphRunDrawing = 248, - GlyphTypeface = 249, - Glyphs = 250, - GradientBrush = 251, - GradientStop = 252, - GradientStopCollection = 253, - Grid = 254, - GridLength = 255, - GridLengthConverter = 256, - GridSplitter = 257, - GridView = 258, - GridViewColumn = 259, - GridViewColumnHeader = 260, - GridViewHeaderRowPresenter = 261, - GridViewRowPresenter = 262, - GridViewRowPresenterBase = 263, - GroupBox = 264, - GroupItem = 265, - Guid = 266, - GuidConverter = 267, - GuidelineSet = 268, - HeaderedContentControl = 269, - HeaderedItemsControl = 270, - HierarchicalDataTemplate = 271, - HostVisual = 272, - Hyperlink = 273, - IAddChild = 274, - IAddChildInternal = 275, - ICommand = 276, - IComponentConnector = 277, - INameScope = 278, - IStyleConnector = 279, - IconBitmapDecoder = 280, - Image = 281, - ImageBrush = 282, - ImageDrawing = 283, - ImageMetadata = 284, - ImageSource = 285, - ImageSourceConverter = 286, - InPlaceBitmapMetadataWriter = 287, - InkCanvas = 288, - InkPresenter = 289, - Inline = 290, - InlineCollection = 291, - InlineUIContainer = 292, - InputBinding = 293, - InputDevice = 294, - InputLanguageManager = 295, - InputManager = 296, - InputMethod = 297, - InputScope = 298, - InputScopeConverter = 299, - InputScopeName = 300, - InputScopeNameConverter = 301, - Int16 = 302, - Int16Animation = 303, - Int16AnimationBase = 304, - Int16AnimationUsingKeyFrames = 305, - Int16Converter = 306, - Int16KeyFrame = 307, - Int16KeyFrameCollection = 308, - Int32 = 309, - Int32Animation = 310, - Int32AnimationBase = 311, - Int32AnimationUsingKeyFrames = 312, - Int32Collection = 313, - Int32CollectionConverter = 314, - Int32Converter = 315, - Int32KeyFrame = 316, - Int32KeyFrameCollection = 317, - Int32Rect = 318, - Int32RectConverter = 319, - Int64 = 320, - Int64Animation = 321, - Int64AnimationBase = 322, - Int64AnimationUsingKeyFrames = 323, - Int64Converter = 324, - Int64KeyFrame = 325, - Int64KeyFrameCollection = 326, - Italic = 327, - ItemCollection = 328, - ItemsControl = 329, - ItemsPanelTemplate = 330, - ItemsPresenter = 331, - JournalEntry = 332, - JournalEntryListConverter = 333, - JournalEntryUnifiedViewConverter = 334, - JpegBitmapDecoder = 335, - JpegBitmapEncoder = 336, - KeyBinding = 337, - KeyConverter = 338, - KeyGesture = 339, - KeyGestureConverter = 340, - KeySpline = 341, - KeySplineConverter = 342, - KeyTime = 343, - KeyTimeConverter = 344, - KeyboardDevice = 345, - Label = 346, - LateBoundBitmapDecoder = 347, - LengthConverter = 348, - Light = 349, - Line = 350, - LineBreak = 351, - LineGeometry = 352, - LineSegment = 353, - LinearByteKeyFrame = 354, - LinearColorKeyFrame = 355, - LinearDecimalKeyFrame = 356, - LinearDoubleKeyFrame = 357, - LinearGradientBrush = 358, - LinearInt16KeyFrame = 359, - LinearInt32KeyFrame = 360, - LinearInt64KeyFrame = 361, - LinearPoint3DKeyFrame = 362, - LinearPointKeyFrame = 363, - LinearQuaternionKeyFrame = 364, - LinearRectKeyFrame = 365, - LinearRotation3DKeyFrame = 366, - LinearSingleKeyFrame = 367, - LinearSizeKeyFrame = 368, - LinearThicknessKeyFrame = 369, - LinearVector3DKeyFrame = 370, - LinearVectorKeyFrame = 371, - List = 372, - ListBox = 373, - ListBoxItem = 374, - ListCollectionView = 375, - ListItem = 376, - ListView = 377, - ListViewItem = 378, - Localization = 379, - LostFocusEventManager = 380, - MarkupExtension = 381, - Material = 382, - MaterialCollection = 383, - MaterialGroup = 384, - Matrix = 385, - Matrix3D = 386, - Matrix3DConverter = 387, - MatrixAnimationBase = 388, - MatrixAnimationUsingKeyFrames = 389, - MatrixAnimationUsingPath = 390, - MatrixCamera = 391, - MatrixConverter = 392, - MatrixKeyFrame = 393, - MatrixKeyFrameCollection = 394, - MatrixTransform = 395, - MatrixTransform3D = 396, - MediaClock = 397, - MediaElement = 398, - MediaPlayer = 399, - MediaTimeline = 400, - Menu = 401, - MenuBase = 402, - MenuItem = 403, - MenuScrollingVisibilityConverter = 404, - MeshGeometry3D = 405, - Model3D = 406, - Model3DCollection = 407, - Model3DGroup = 408, - ModelVisual3D = 409, - ModifierKeysConverter = 410, - MouseActionConverter = 411, - MouseBinding = 412, - MouseDevice = 413, - MouseGesture = 414, - MouseGestureConverter = 415, - MultiBinding = 416, - MultiBindingExpression = 417, - MultiDataTrigger = 418, - MultiTrigger = 419, - NameScope = 420, - NavigationWindow = 421, - NullExtension = 422, - NullableBoolConverter = 423, - NullableConverter = 424, - NumberSubstitution = 425, - Object = 426, - ObjectAnimationBase = 427, - ObjectAnimationUsingKeyFrames = 428, - ObjectDataProvider = 429, - ObjectKeyFrame = 430, - ObjectKeyFrameCollection = 431, - OrthographicCamera = 432, - OuterGlowBitmapEffect = 433, - Page = 434, - PageContent = 435, - PageFunctionBase = 436, - Panel = 437, - Paragraph = 438, - ParallelTimeline = 439, - ParserContext = 440, - PasswordBox = 441, - Path = 442, - PathFigure = 443, - PathFigureCollection = 444, - PathFigureCollectionConverter = 445, - PathGeometry = 446, - PathSegment = 447, - PathSegmentCollection = 448, - PauseStoryboard = 449, - Pen = 450, - PerspectiveCamera = 451, - PixelFormat = 452, - PixelFormatConverter = 453, - PngBitmapDecoder = 454, - PngBitmapEncoder = 455, - Point = 456, - Point3D = 457, - Point3DAnimation = 458, - Point3DAnimationBase = 459, - Point3DAnimationUsingKeyFrames = 460, - Point3DCollection = 461, - Point3DCollectionConverter = 462, - Point3DConverter = 463, - Point3DKeyFrame = 464, - Point3DKeyFrameCollection = 465, - Point4D = 466, - Point4DConverter = 467, - PointAnimation = 468, - PointAnimationBase = 469, - PointAnimationUsingKeyFrames = 470, - PointAnimationUsingPath = 471, - PointCollection = 472, - PointCollectionConverter = 473, - PointConverter = 474, - PointIListConverter = 475, - PointKeyFrame = 476, - PointKeyFrameCollection = 477, - PointLight = 478, - PointLightBase = 479, - PolyBezierSegment = 480, - PolyLineSegment = 481, - PolyQuadraticBezierSegment = 482, - Polygon = 483, - Polyline = 484, - Popup = 485, - PresentationSource = 486, - PriorityBinding = 487, - PriorityBindingExpression = 488, - ProgressBar = 489, - ProjectionCamera = 490, - PropertyPath = 491, - PropertyPathConverter = 492, - QuadraticBezierSegment = 493, - Quaternion = 494, - QuaternionAnimation = 495, - QuaternionAnimationBase = 496, - QuaternionAnimationUsingKeyFrames = 497, - QuaternionConverter = 498, - QuaternionKeyFrame = 499, - QuaternionKeyFrameCollection = 500, - QuaternionRotation3D = 501, - RadialGradientBrush = 502, - RadioButton = 503, - RangeBase = 504, - Rect = 505, - Rect3D = 506, - Rect3DConverter = 507, - RectAnimation = 508, - RectAnimationBase = 509, - RectAnimationUsingKeyFrames = 510, - RectConverter = 511, - RectKeyFrame = 512, - RectKeyFrameCollection = 513, - Rectangle = 514, - RectangleGeometry = 515, - RelativeSource = 516, - RemoveStoryboard = 517, - RenderOptions = 518, - RenderTargetBitmap = 519, - RepeatBehavior = 520, - RepeatBehaviorConverter = 521, - RepeatButton = 522, - ResizeGrip = 523, - ResourceDictionary = 524, - ResourceKey = 525, - ResumeStoryboard = 526, - RichTextBox = 527, - RotateTransform = 528, - RotateTransform3D = 529, - Rotation3D = 530, - Rotation3DAnimation = 531, - Rotation3DAnimationBase = 532, - Rotation3DAnimationUsingKeyFrames = 533, - Rotation3DKeyFrame = 534, - Rotation3DKeyFrameCollection = 535, - RoutedCommand = 536, - RoutedEvent = 537, - RoutedEventConverter = 538, - RoutedUICommand = 539, - RoutingStrategy = 540, - RowDefinition = 541, - Run = 542, - RuntimeNamePropertyAttribute = 543, - SByte = 544, - SByteConverter = 545, - ScaleTransform = 546, - ScaleTransform3D = 547, - ScrollBar = 548, - ScrollContentPresenter = 549, - ScrollViewer = 550, - Section = 551, - SeekStoryboard = 552, - Selector = 553, - Separator = 554, - SetStoryboardSpeedRatio = 555, - Setter = 556, - SetterBase = 557, - Shape = 558, - Single = 559, - SingleAnimation = 560, - SingleAnimationBase = 561, - SingleAnimationUsingKeyFrames = 562, - SingleConverter = 563, - SingleKeyFrame = 564, - SingleKeyFrameCollection = 565, - Size = 566, - Size3D = 567, - Size3DConverter = 568, - SizeAnimation = 569, - SizeAnimationBase = 570, - SizeAnimationUsingKeyFrames = 571, - SizeConverter = 572, - SizeKeyFrame = 573, - SizeKeyFrameCollection = 574, - SkewTransform = 575, - SkipStoryboardToFill = 576, - Slider = 577, - SolidColorBrush = 578, - SoundPlayerAction = 579, - Span = 580, - SpecularMaterial = 581, - SpellCheck = 582, - SplineByteKeyFrame = 583, - SplineColorKeyFrame = 584, - SplineDecimalKeyFrame = 585, - SplineDoubleKeyFrame = 586, - SplineInt16KeyFrame = 587, - SplineInt32KeyFrame = 588, - SplineInt64KeyFrame = 589, - SplinePoint3DKeyFrame = 590, - SplinePointKeyFrame = 591, - SplineQuaternionKeyFrame = 592, - SplineRectKeyFrame = 593, - SplineRotation3DKeyFrame = 594, - SplineSingleKeyFrame = 595, - SplineSizeKeyFrame = 596, - SplineThicknessKeyFrame = 597, - SplineVector3DKeyFrame = 598, - SplineVectorKeyFrame = 599, - SpotLight = 600, - StackPanel = 601, - StaticExtension = 602, - StaticResourceExtension = 603, - StatusBar = 604, - StatusBarItem = 605, - StickyNoteControl = 606, - StopStoryboard = 607, - Storyboard = 608, - StreamGeometry = 609, - StreamGeometryContext = 610, - StreamResourceInfo = 611, - String = 612, - StringAnimationBase = 613, - StringAnimationUsingKeyFrames = 614, - StringConverter = 615, - StringKeyFrame = 616, - StringKeyFrameCollection = 617, - StrokeCollection = 618, - StrokeCollectionConverter = 619, - Style = 620, - Stylus = 621, - StylusDevice = 622, - TabControl = 623, - TabItem = 624, - TabPanel = 625, - Table = 626, - TableCell = 627, - TableColumn = 628, - TableRow = 629, - TableRowGroup = 630, - TabletDevice = 631, - TemplateBindingExpression = 632, - TemplateBindingExpressionConverter = 633, - TemplateBindingExtension = 634, - TemplateBindingExtensionConverter = 635, - TemplateKey = 636, - TemplateKeyConverter = 637, - TextBlock = 638, - TextBox = 639, - TextBoxBase = 640, - TextComposition = 641, - TextCompositionManager = 642, - TextDecoration = 643, - TextDecorationCollection = 644, - TextDecorationCollectionConverter = 645, - TextEffect = 646, - TextEffectCollection = 647, - TextElement = 648, - TextSearch = 649, - ThemeDictionaryExtension = 650, - Thickness = 651, - ThicknessAnimation = 652, - ThicknessAnimationBase = 653, - ThicknessAnimationUsingKeyFrames = 654, - ThicknessConverter = 655, - ThicknessKeyFrame = 656, - ThicknessKeyFrameCollection = 657, - Thumb = 658, - TickBar = 659, - TiffBitmapDecoder = 660, - TiffBitmapEncoder = 661, - TileBrush = 662, - TimeSpan = 663, - TimeSpanConverter = 664, - Timeline = 665, - TimelineCollection = 666, - TimelineGroup = 667, - ToggleButton = 668, - ToolBar = 669, - ToolBarOverflowPanel = 670, - ToolBarPanel = 671, - ToolBarTray = 672, - ToolTip = 673, - ToolTipService = 674, - Track = 675, - Transform = 676, - Transform3D = 677, - Transform3DCollection = 678, - Transform3DGroup = 679, - TransformCollection = 680, - TransformConverter = 681, - TransformGroup = 682, - TransformedBitmap = 683, - TranslateTransform = 684, - TranslateTransform3D = 685, - TreeView = 686, - TreeViewItem = 687, - Trigger = 688, - TriggerAction = 689, - TriggerBase = 690, - TypeExtension = 691, - TypeTypeConverter = 692, - Typography = 693, - UIElement = 694, - UInt16 = 695, - UInt16Converter = 696, - UInt32 = 697, - UInt32Converter = 698, - UInt64 = 699, - UInt64Converter = 700, - UShortIListConverter = 701, - Underline = 702, - UniformGrid = 703, - Uri = 704, - UriTypeConverter = 705, - UserControl = 706, - Validation = 707, - Vector = 708, - Vector3D = 709, - Vector3DAnimation = 710, - Vector3DAnimationBase = 711, - Vector3DAnimationUsingKeyFrames = 712, - Vector3DCollection = 713, - Vector3DCollectionConverter = 714, - Vector3DConverter = 715, - Vector3DKeyFrame = 716, - Vector3DKeyFrameCollection = 717, - VectorAnimation = 718, - VectorAnimationBase = 719, - VectorAnimationUsingKeyFrames = 720, - VectorCollection = 721, - VectorCollectionConverter = 722, - VectorConverter = 723, - VectorKeyFrame = 724, - VectorKeyFrameCollection = 725, - VideoDrawing = 726, - ViewBase = 727, - Viewbox = 728, - Viewport3D = 729, - Viewport3DVisual = 730, - VirtualizingPanel = 731, - VirtualizingStackPanel = 732, - Visual = 733, - Visual3D = 734, - VisualBrush = 735, - VisualTarget = 736, - WeakEventManager = 737, - WhitespaceSignificantCollectionAttribute = 738, - Window = 739, - WmpBitmapDecoder = 740, - WmpBitmapEncoder = 741, - WrapPanel = 742, - WriteableBitmap = 743, - XamlBrushSerializer = 744, - XamlInt32CollectionSerializer = 745, - XamlPathDataSerializer = 746, - XamlPoint3DCollectionSerializer = 747, - XamlPointCollectionSerializer = 748, - XamlReader = 749, - XamlStyleSerializer = 750, - XamlTemplateSerializer = 751, - XamlVector3DCollectionSerializer = 752, - XamlWriter = 753, - XmlDataProvider = 754, - XmlLangPropertyAttribute = 755, - XmlLanguage = 756, - XmlLanguageConverter = 757, - XmlNamespaceMapping = 758, - ZoomPercentageConverter = 759, - } - - internal enum KnownMembers : short { - Unknown = 0, - AccessText_Text = 1, - BeginStoryboard_Storyboard = 2, - BitmapEffectGroup_Children = 3, - Border_Background = 4, - Border_BorderBrush = 5, - Border_BorderThickness = 6, - ButtonBase_Command = 7, - ButtonBase_CommandParameter = 8, - ButtonBase_CommandTarget = 9, - ButtonBase_IsPressed = 10, - ColumnDefinition_MaxWidth = 11, - ColumnDefinition_MinWidth = 12, - ColumnDefinition_Width = 13, - ContentControl_Content = 14, - ContentControl_ContentTemplate = 15, - ContentControl_ContentTemplateSelector = 16, - ContentControl_HasContent = 17, - ContentElement_Focusable = 18, - ContentPresenter_Content = 19, - ContentPresenter_ContentSource = 20, - ContentPresenter_ContentTemplate = 21, - ContentPresenter_ContentTemplateSelector = 22, - ContentPresenter_RecognizesAccessKey = 23, - Control_Background = 24, - Control_BorderBrush = 25, - Control_BorderThickness = 26, - Control_FontFamily = 27, - Control_FontSize = 28, - Control_FontStretch = 29, - Control_FontStyle = 30, - Control_FontWeight = 31, - Control_Foreground = 32, - Control_HorizontalContentAlignment = 33, - Control_IsTabStop = 34, - Control_Padding = 35, - Control_TabIndex = 36, - Control_Template = 37, - Control_VerticalContentAlignment = 38, - DockPanel_Dock = 39, - DockPanel_LastChildFill = 40, - DocumentViewerBase_Document = 41, - DrawingGroup_Children = 42, - FlowDocumentReader_Document = 43, - FlowDocumentScrollViewer_Document = 44, - FrameworkContentElement_Style = 45, - FrameworkElement_FlowDirection = 46, - FrameworkElement_Height = 47, - FrameworkElement_HorizontalAlignment = 48, - FrameworkElement_Margin = 49, - FrameworkElement_MaxHeight = 50, - FrameworkElement_MaxWidth = 51, - FrameworkElement_MinHeight = 52, - FrameworkElement_MinWidth = 53, - FrameworkElement_Name = 54, - FrameworkElement_Style = 55, - FrameworkElement_VerticalAlignment = 56, - FrameworkElement_Width = 57, - GeneralTransformGroup_Children = 58, - GeometryGroup_Children = 59, - GradientBrush_GradientStops = 60, - Grid_Column = 61, - Grid_ColumnSpan = 62, - Grid_Row = 63, - Grid_RowSpan = 64, - GridViewColumn_Header = 65, - HeaderedContentControl_HasHeader = 66, - HeaderedContentControl_Header = 67, - HeaderedContentControl_HeaderTemplate = 68, - HeaderedContentControl_HeaderTemplateSelector = 69, - HeaderedItemsControl_HasHeader = 70, - HeaderedItemsControl_Header = 71, - HeaderedItemsControl_HeaderTemplate = 72, - HeaderedItemsControl_HeaderTemplateSelector = 73, - Hyperlink_NavigateUri = 74, - Image_Source = 75, - Image_Stretch = 76, - ItemsControl_ItemContainerStyle = 77, - ItemsControl_ItemContainerStyleSelector = 78, - ItemsControl_ItemTemplate = 79, - ItemsControl_ItemTemplateSelector = 80, - ItemsControl_ItemsPanel = 81, - ItemsControl_ItemsSource = 82, - MaterialGroup_Children = 83, - Model3DGroup_Children = 84, - Page_Content = 85, - Panel_Background = 86, - Path_Data = 87, - PathFigure_Segments = 88, - PathGeometry_Figures = 89, - Popup_Child = 90, - Popup_IsOpen = 91, - Popup_Placement = 92, - Popup_PopupAnimation = 93, - RowDefinition_Height = 94, - RowDefinition_MaxHeight = 95, - RowDefinition_MinHeight = 96, - ScrollViewer_CanContentScroll = 97, - ScrollViewer_HorizontalScrollBarVisibility = 98, - ScrollViewer_VerticalScrollBarVisibility = 99, - Shape_Fill = 100, - Shape_Stroke = 101, - Shape_StrokeThickness = 102, - TextBlock_Background = 103, - TextBlock_FontFamily = 104, - TextBlock_FontSize = 105, - TextBlock_FontStretch = 106, - TextBlock_FontStyle = 107, - TextBlock_FontWeight = 108, - TextBlock_Foreground = 109, - TextBlock_Text = 110, - TextBlock_TextDecorations = 111, - TextBlock_TextTrimming = 112, - TextBlock_TextWrapping = 113, - TextBox_Text = 114, - TextElement_Background = 115, - TextElement_FontFamily = 116, - TextElement_FontSize = 117, - TextElement_FontStretch = 118, - TextElement_FontStyle = 119, - TextElement_FontWeight = 120, - TextElement_Foreground = 121, - TimelineGroup_Children = 122, - Track_IsDirectionReversed = 123, - Track_Maximum = 124, - Track_Minimum = 125, - Track_Orientation = 126, - Track_Value = 127, - Track_ViewportSize = 128, - Transform3DGroup_Children = 129, - TransformGroup_Children = 130, - UIElement_ClipToBounds = 131, - UIElement_Focusable = 132, - UIElement_IsEnabled = 133, - UIElement_RenderTransform = 134, - UIElement_Visibility = 135, - Viewport3D_Children = 136, - - AdornedElementPlaceholder_Child = 138, - AdornerDecorator_Child = 139, - AnchoredBlock_Blocks = 140, - ArrayExtension_Items = 141, - BlockUIContainer_Child = 142, - Bold_Inlines = 143, - BooleanAnimationUsingKeyFrames_KeyFrames = 144, - Border_Child = 145, - BulletDecorator_Child = 146, - Button_Content = 147, - ButtonBase_Content = 148, - ByteAnimationUsingKeyFrames_KeyFrames = 149, - Canvas_Children = 150, - CharAnimationUsingKeyFrames_KeyFrames = 151, - CheckBox_Content = 152, - ColorAnimationUsingKeyFrames_KeyFrames = 153, - ComboBox_Items = 154, - ComboBoxItem_Content = 155, - ContextMenu_Items = 156, - ControlTemplate_VisualTree = 157, - DataTemplate_VisualTree = 158, - DataTrigger_Setters = 159, - DecimalAnimationUsingKeyFrames_KeyFrames = 160, - Decorator_Child = 161, - DockPanel_Children = 162, - DocumentViewer_Document = 163, - DoubleAnimationUsingKeyFrames_KeyFrames = 164, - EventTrigger_Actions = 165, - Expander_Content = 166, - Figure_Blocks = 167, - FixedDocument_Pages = 168, - FixedDocumentSequence_References = 169, - FixedPage_Children = 170, - Floater_Blocks = 171, - FlowDocument_Blocks = 172, - FlowDocumentPageViewer_Document = 173, - FrameworkTemplate_VisualTree = 174, - Grid_Children = 175, - GridView_Columns = 176, - GridViewColumnHeader_Content = 177, - GroupBox_Content = 178, - GroupItem_Content = 179, - HeaderedContentControl_Content = 180, - HeaderedItemsControl_Items = 181, - HierarchicalDataTemplate_VisualTree = 182, - Hyperlink_Inlines = 183, - InkCanvas_Children = 184, - InkPresenter_Child = 185, - InlineUIContainer_Child = 186, - InputScopeName_NameValue = 187, - Int16AnimationUsingKeyFrames_KeyFrames = 188, - Int32AnimationUsingKeyFrames_KeyFrames = 189, - Int64AnimationUsingKeyFrames_KeyFrames = 190, - Italic_Inlines = 191, - ItemsControl_Items = 192, - ItemsPanelTemplate_VisualTree = 193, - Label_Content = 194, - LinearGradientBrush_GradientStops = 195, - List_ListItems = 196, - ListBox_Items = 197, - ListBoxItem_Content = 198, - ListItem_Blocks = 199, - ListView_Items = 200, - ListViewItem_Content = 201, - MatrixAnimationUsingKeyFrames_KeyFrames = 202, - Menu_Items = 203, - MenuBase_Items = 204, - MenuItem_Items = 205, - ModelVisual3D_Children = 206, - MultiBinding_Bindings = 207, - MultiDataTrigger_Setters = 208, - MultiTrigger_Setters = 209, - ObjectAnimationUsingKeyFrames_KeyFrames = 210, - PageContent_Child = 211, - PageFunctionBase_Content = 212, - Panel_Children = 213, - Paragraph_Inlines = 214, - ParallelTimeline_Children = 215, - Point3DAnimationUsingKeyFrames_KeyFrames = 216, - PointAnimationUsingKeyFrames_KeyFrames = 217, - PriorityBinding_Bindings = 218, - QuaternionAnimationUsingKeyFrames_KeyFrames = 219, - RadialGradientBrush_GradientStops = 220, - RadioButton_Content = 221, - RectAnimationUsingKeyFrames_KeyFrames = 222, - RepeatButton_Content = 223, - RichTextBox_Document = 224, - Rotation3DAnimationUsingKeyFrames_KeyFrames = 225, - Run_Text = 226, - ScrollViewer_Content = 227, - Section_Blocks = 228, - Selector_Items = 229, - SingleAnimationUsingKeyFrames_KeyFrames = 230, - SizeAnimationUsingKeyFrames_KeyFrames = 231, - Span_Inlines = 232, - StackPanel_Children = 233, - StatusBar_Items = 234, - StatusBarItem_Content = 235, - Storyboard_Children = 236, - StringAnimationUsingKeyFrames_KeyFrames = 237, - Style_Setters = 238, - TabControl_Items = 239, - TabItem_Content = 240, - TabPanel_Children = 241, - Table_RowGroups = 242, - TableCell_Blocks = 243, - TableRow_Cells = 244, - TableRowGroup_Rows = 245, - TextBlock_Inlines = 246, - ThicknessAnimationUsingKeyFrames_KeyFrames = 247, - ToggleButton_Content = 248, - ToolBar_Items = 249, - ToolBarOverflowPanel_Children = 250, - ToolBarPanel_Children = 251, - ToolBarTray_ToolBars = 252, - ToolTip_Content = 253, - TreeView_Items = 254, - TreeViewItem_Items = 255, - Trigger_Setters = 256, - Underline_Inlines = 257, - UniformGrid_Children = 258, - UserControl_Content = 259, - Vector3DAnimationUsingKeyFrames_KeyFrames = 260, - VectorAnimationUsingKeyFrames_KeyFrames = 261, - Viewbox_Child = 262, - Viewport3DVisual_Children = 263, - VirtualizingPanel_Children = 264, - VirtualizingStackPanel_Children = 265, - Window_Content = 266, - WrapPanel_Children = 267, - XmlDataProvider_XmlSerializer = 268, - } - - void InitAssemblies() { - assemblies[0] = ResolveAssembly("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); - assemblies[1] = ResolveAssembly("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); - assemblies[2] = ResolveAssembly("WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); - assemblies[3] = ResolveAssembly("PresentationCore, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); - assemblies[4] = ResolveAssembly("PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); - assemblies[5] = ResolveAssembly("System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); - } - - void InitTypes() { - - types[KnownTypes.AccessText] = InitType(assemblies[4], "System.Windows.Controls", "AccessText"); - types[KnownTypes.AdornedElementPlaceholder] = InitType(assemblies[4], "System.Windows.Controls", "AdornedElementPlaceholder"); - types[KnownTypes.Adorner] = InitType(assemblies[4], "System.Windows.Documents", "Adorner"); - types[KnownTypes.AdornerDecorator] = InitType(assemblies[4], "System.Windows.Documents", "AdornerDecorator"); - types[KnownTypes.AdornerLayer] = InitType(assemblies[4], "System.Windows.Documents", "AdornerLayer"); - types[KnownTypes.AffineTransform3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "AffineTransform3D"); - types[KnownTypes.AmbientLight] = InitType(assemblies[3], "System.Windows.Media.Media3D", "AmbientLight"); - types[KnownTypes.AnchoredBlock] = InitType(assemblies[4], "System.Windows.Documents", "AnchoredBlock"); - types[KnownTypes.Animatable] = InitType(assemblies[3], "System.Windows.Media.Animation", "Animatable"); - types[KnownTypes.AnimationClock] = InitType(assemblies[3], "System.Windows.Media.Animation", "AnimationClock"); - types[KnownTypes.AnimationTimeline] = InitType(assemblies[3], "System.Windows.Media.Animation", "AnimationTimeline"); - types[KnownTypes.Application] = InitType(assemblies[4], "System.Windows", "Application"); - types[KnownTypes.ArcSegment] = InitType(assemblies[3], "System.Windows.Media", "ArcSegment"); - types[KnownTypes.ArrayExtension] = InitType(assemblies[4], "System.Windows.Markup", "ArrayExtension"); - types[KnownTypes.AxisAngleRotation3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "AxisAngleRotation3D"); - types[KnownTypes.BaseIListConverter] = InitType(assemblies[3], "System.Windows.Media.Converters", "BaseIListConverter"); - types[KnownTypes.BeginStoryboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "BeginStoryboard"); - types[KnownTypes.BevelBitmapEffect] = InitType(assemblies[3], "System.Windows.Media.Effects", "BevelBitmapEffect"); - types[KnownTypes.BezierSegment] = InitType(assemblies[3], "System.Windows.Media", "BezierSegment"); - types[KnownTypes.Binding] = InitType(assemblies[4], "System.Windows.Data", "Binding"); - types[KnownTypes.BindingBase] = InitType(assemblies[4], "System.Windows.Data", "BindingBase"); - types[KnownTypes.BindingExpression] = InitType(assemblies[4], "System.Windows.Data", "BindingExpression"); - types[KnownTypes.BindingExpressionBase] = InitType(assemblies[4], "System.Windows.Data", "BindingExpressionBase"); - types[KnownTypes.BindingListCollectionView] = InitType(assemblies[4], "System.Windows.Data", "BindingListCollectionView"); - types[KnownTypes.BitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapDecoder"); - types[KnownTypes.BitmapEffect] = InitType(assemblies[3], "System.Windows.Media.Effects", "BitmapEffect"); - types[KnownTypes.BitmapEffectCollection] = InitType(assemblies[3], "System.Windows.Media.Effects", "BitmapEffectCollection"); - types[KnownTypes.BitmapEffectGroup] = InitType(assemblies[3], "System.Windows.Media.Effects", "BitmapEffectGroup"); - types[KnownTypes.BitmapEffectInput] = InitType(assemblies[3], "System.Windows.Media.Effects", "BitmapEffectInput"); - types[KnownTypes.BitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapEncoder"); - types[KnownTypes.BitmapFrame] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapFrame"); - types[KnownTypes.BitmapImage] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapImage"); - types[KnownTypes.BitmapMetadata] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapMetadata"); - types[KnownTypes.BitmapPalette] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapPalette"); - types[KnownTypes.BitmapSource] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapSource"); - types[KnownTypes.Block] = InitType(assemblies[4], "System.Windows.Documents", "Block"); - types[KnownTypes.BlockUIContainer] = InitType(assemblies[4], "System.Windows.Documents", "BlockUIContainer"); - types[KnownTypes.BlurBitmapEffect] = InitType(assemblies[3], "System.Windows.Media.Effects", "BlurBitmapEffect"); - types[KnownTypes.BmpBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BmpBitmapDecoder"); - types[KnownTypes.BmpBitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BmpBitmapEncoder"); - types[KnownTypes.Bold] = InitType(assemblies[4], "System.Windows.Documents", "Bold"); - types[KnownTypes.BoolIListConverter] = InitType(assemblies[3], "System.Windows.Media.Converters", "BoolIListConverter"); - types[KnownTypes.Boolean] = InitType(assemblies[0], "System", "Boolean"); - types[KnownTypes.BooleanAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "BooleanAnimationBase"); - types[KnownTypes.BooleanAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "BooleanAnimationUsingKeyFrames"); - types[KnownTypes.BooleanConverter] = InitType(assemblies[1], "System.ComponentModel", "BooleanConverter"); - types[KnownTypes.BooleanKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "BooleanKeyFrame"); - types[KnownTypes.BooleanKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "BooleanKeyFrameCollection"); - types[KnownTypes.BooleanToVisibilityConverter] = InitType(assemblies[4], "System.Windows.Controls", "BooleanToVisibilityConverter"); - types[KnownTypes.Border] = InitType(assemblies[4], "System.Windows.Controls", "Border"); - types[KnownTypes.BorderGapMaskConverter] = InitType(assemblies[4], "System.Windows.Controls", "BorderGapMaskConverter"); - types[KnownTypes.Brush] = InitType(assemblies[3], "System.Windows.Media", "Brush"); - types[KnownTypes.BrushConverter] = InitType(assemblies[3], "System.Windows.Media", "BrushConverter"); - types[KnownTypes.BulletDecorator] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "BulletDecorator"); - types[KnownTypes.Button] = InitType(assemblies[4], "System.Windows.Controls", "Button"); - types[KnownTypes.ButtonBase] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "ButtonBase"); - types[KnownTypes.Byte] = InitType(assemblies[0], "System", "Byte"); - types[KnownTypes.ByteAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "ByteAnimation"); - types[KnownTypes.ByteAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "ByteAnimationBase"); - types[KnownTypes.ByteAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "ByteAnimationUsingKeyFrames"); - types[KnownTypes.ByteConverter] = InitType(assemblies[1], "System.ComponentModel", "ByteConverter"); - types[KnownTypes.ByteKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "ByteKeyFrame"); - types[KnownTypes.ByteKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "ByteKeyFrameCollection"); - types[KnownTypes.CachedBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "CachedBitmap"); - types[KnownTypes.Camera] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Camera"); - types[KnownTypes.Canvas] = InitType(assemblies[4], "System.Windows.Controls", "Canvas"); - types[KnownTypes.Char] = InitType(assemblies[0], "System", "Char"); - types[KnownTypes.CharAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "CharAnimationBase"); - types[KnownTypes.CharAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "CharAnimationUsingKeyFrames"); - types[KnownTypes.CharConverter] = InitType(assemblies[1], "System.ComponentModel", "CharConverter"); - types[KnownTypes.CharIListConverter] = InitType(assemblies[3], "System.Windows.Media.Converters", "CharIListConverter"); - types[KnownTypes.CharKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "CharKeyFrame"); - types[KnownTypes.CharKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "CharKeyFrameCollection"); - types[KnownTypes.CheckBox] = InitType(assemblies[4], "System.Windows.Controls", "CheckBox"); - types[KnownTypes.Clock] = InitType(assemblies[3], "System.Windows.Media.Animation", "Clock"); - types[KnownTypes.ClockController] = InitType(assemblies[3], "System.Windows.Media.Animation", "ClockController"); - types[KnownTypes.ClockGroup] = InitType(assemblies[3], "System.Windows.Media.Animation", "ClockGroup"); - types[KnownTypes.CollectionContainer] = InitType(assemblies[4], "System.Windows.Data", "CollectionContainer"); - types[KnownTypes.CollectionView] = InitType(assemblies[4], "System.Windows.Data", "CollectionView"); - types[KnownTypes.CollectionViewSource] = InitType(assemblies[4], "System.Windows.Data", "CollectionViewSource"); - types[KnownTypes.Color] = InitType(assemblies[3], "System.Windows.Media", "Color"); - types[KnownTypes.ColorAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "ColorAnimation"); - types[KnownTypes.ColorAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "ColorAnimationBase"); - types[KnownTypes.ColorAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "ColorAnimationUsingKeyFrames"); - types[KnownTypes.ColorConvertedBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "ColorConvertedBitmap"); - types[KnownTypes.ColorConvertedBitmapExtension] = InitType(assemblies[4], "System.Windows", "ColorConvertedBitmapExtension"); - types[KnownTypes.ColorConverter] = InitType(assemblies[3], "System.Windows.Media", "ColorConverter"); - types[KnownTypes.ColorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "ColorKeyFrame"); - types[KnownTypes.ColorKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "ColorKeyFrameCollection"); - types[KnownTypes.ColumnDefinition] = InitType(assemblies[4], "System.Windows.Controls", "ColumnDefinition"); - types[KnownTypes.CombinedGeometry] = InitType(assemblies[3], "System.Windows.Media", "CombinedGeometry"); - types[KnownTypes.ComboBox] = InitType(assemblies[4], "System.Windows.Controls", "ComboBox"); - types[KnownTypes.ComboBoxItem] = InitType(assemblies[4], "System.Windows.Controls", "ComboBoxItem"); - types[KnownTypes.CommandConverter] = InitType(assemblies[4], "System.Windows.Input", "CommandConverter"); - types[KnownTypes.ComponentResourceKey] = InitType(assemblies[4], "System.Windows", "ComponentResourceKey"); - types[KnownTypes.ComponentResourceKeyConverter] = InitType(assemblies[4], "System.Windows.Markup", "ComponentResourceKeyConverter"); - types[KnownTypes.CompositionTarget] = InitType(assemblies[3], "System.Windows.Media", "CompositionTarget"); - types[KnownTypes.Condition] = InitType(assemblies[4], "System.Windows", "Condition"); - types[KnownTypes.ContainerVisual] = InitType(assemblies[3], "System.Windows.Media", "ContainerVisual"); - types[KnownTypes.ContentControl] = InitType(assemblies[4], "System.Windows.Controls", "ContentControl"); - types[KnownTypes.ContentElement] = InitType(assemblies[3], "System.Windows", "ContentElement"); - types[KnownTypes.ContentPresenter] = InitType(assemblies[4], "System.Windows.Controls", "ContentPresenter"); - types[KnownTypes.ContentPropertyAttribute] = InitType(assemblies[2], "System.Windows.Markup", "ContentPropertyAttribute"); - types[KnownTypes.ContentWrapperAttribute] = InitType(assemblies[2], "System.Windows.Markup", "ContentWrapperAttribute"); - types[KnownTypes.ContextMenu] = InitType(assemblies[4], "System.Windows.Controls", "ContextMenu"); - types[KnownTypes.ContextMenuService] = InitType(assemblies[4], "System.Windows.Controls", "ContextMenuService"); - types[KnownTypes.Control] = InitType(assemblies[4], "System.Windows.Controls", "Control"); - types[KnownTypes.ControlTemplate] = InitType(assemblies[4], "System.Windows.Controls", "ControlTemplate"); - types[KnownTypes.ControllableStoryboardAction] = InitType(assemblies[4], "System.Windows.Media.Animation", "ControllableStoryboardAction"); - types[KnownTypes.CornerRadius] = InitType(assemblies[4], "System.Windows", "CornerRadius"); - types[KnownTypes.CornerRadiusConverter] = InitType(assemblies[4], "System.Windows", "CornerRadiusConverter"); - types[KnownTypes.CroppedBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "CroppedBitmap"); - types[KnownTypes.CultureInfo] = InitType(assemblies[0], "System.Globalization", "CultureInfo"); - types[KnownTypes.CultureInfoConverter] = InitType(assemblies[1], "System.ComponentModel", "CultureInfoConverter"); - types[KnownTypes.CultureInfoIetfLanguageTagConverter] = InitType(assemblies[3], "System.Windows", "CultureInfoIetfLanguageTagConverter"); - types[KnownTypes.Cursor] = InitType(assemblies[3], "System.Windows.Input", "Cursor"); - types[KnownTypes.CursorConverter] = InitType(assemblies[3], "System.Windows.Input", "CursorConverter"); - types[KnownTypes.DashStyle] = InitType(assemblies[3], "System.Windows.Media", "DashStyle"); - types[KnownTypes.DataChangedEventManager] = InitType(assemblies[4], "System.Windows.Data", "DataChangedEventManager"); - types[KnownTypes.DataTemplate] = InitType(assemblies[4], "System.Windows", "DataTemplate"); - types[KnownTypes.DataTemplateKey] = InitType(assemblies[4], "System.Windows", "DataTemplateKey"); - types[KnownTypes.DataTrigger] = InitType(assemblies[4], "System.Windows", "DataTrigger"); - types[KnownTypes.DateTime] = InitType(assemblies[0], "System", "DateTime"); - types[KnownTypes.DateTimeConverter] = InitType(assemblies[1], "System.ComponentModel", "DateTimeConverter"); - types[KnownTypes.DateTimeConverter2] = InitType(assemblies[2], "System.Windows.Markup", "DateTimeConverter2"); - types[KnownTypes.Decimal] = InitType(assemblies[0], "System", "Decimal"); - types[KnownTypes.DecimalAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "DecimalAnimation"); - types[KnownTypes.DecimalAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "DecimalAnimationBase"); - types[KnownTypes.DecimalAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "DecimalAnimationUsingKeyFrames"); - types[KnownTypes.DecimalConverter] = InitType(assemblies[1], "System.ComponentModel", "DecimalConverter"); - types[KnownTypes.DecimalKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DecimalKeyFrame"); - types[KnownTypes.DecimalKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "DecimalKeyFrameCollection"); - types[KnownTypes.Decorator] = InitType(assemblies[4], "System.Windows.Controls", "Decorator"); - types[KnownTypes.DefinitionBase] = InitType(assemblies[4], "System.Windows.Controls", "DefinitionBase"); - types[KnownTypes.DependencyObject] = InitType(assemblies[2], "System.Windows", "DependencyObject"); - types[KnownTypes.DependencyProperty] = InitType(assemblies[2], "System.Windows", "DependencyProperty"); - types[KnownTypes.DependencyPropertyConverter] = InitType(assemblies[4], "System.Windows.Markup", "DependencyPropertyConverter"); - types[KnownTypes.DialogResultConverter] = InitType(assemblies[4], "System.Windows", "DialogResultConverter"); - types[KnownTypes.DiffuseMaterial] = InitType(assemblies[3], "System.Windows.Media.Media3D", "DiffuseMaterial"); - types[KnownTypes.DirectionalLight] = InitType(assemblies[3], "System.Windows.Media.Media3D", "DirectionalLight"); - types[KnownTypes.DiscreteBooleanKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteBooleanKeyFrame"); - types[KnownTypes.DiscreteByteKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteByteKeyFrame"); - types[KnownTypes.DiscreteCharKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteCharKeyFrame"); - types[KnownTypes.DiscreteColorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteColorKeyFrame"); - types[KnownTypes.DiscreteDecimalKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteDecimalKeyFrame"); - types[KnownTypes.DiscreteDoubleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteDoubleKeyFrame"); - types[KnownTypes.DiscreteInt16KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteInt16KeyFrame"); - types[KnownTypes.DiscreteInt32KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteInt32KeyFrame"); - types[KnownTypes.DiscreteInt64KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteInt64KeyFrame"); - types[KnownTypes.DiscreteMatrixKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteMatrixKeyFrame"); - types[KnownTypes.DiscreteObjectKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteObjectKeyFrame"); - types[KnownTypes.DiscretePoint3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscretePoint3DKeyFrame"); - types[KnownTypes.DiscretePointKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscretePointKeyFrame"); - types[KnownTypes.DiscreteQuaternionKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteQuaternionKeyFrame"); - types[KnownTypes.DiscreteRectKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteRectKeyFrame"); - types[KnownTypes.DiscreteRotation3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteRotation3DKeyFrame"); - types[KnownTypes.DiscreteSingleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteSingleKeyFrame"); - types[KnownTypes.DiscreteSizeKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteSizeKeyFrame"); - types[KnownTypes.DiscreteStringKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteStringKeyFrame"); - types[KnownTypes.DiscreteThicknessKeyFrame] = InitType(assemblies[4], "System.Windows.Media.Animation", "DiscreteThicknessKeyFrame"); - types[KnownTypes.DiscreteVector3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteVector3DKeyFrame"); - types[KnownTypes.DiscreteVectorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteVectorKeyFrame"); - types[KnownTypes.DockPanel] = InitType(assemblies[4], "System.Windows.Controls", "DockPanel"); - types[KnownTypes.DocumentPageView] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "DocumentPageView"); - types[KnownTypes.DocumentReference] = InitType(assemblies[4], "System.Windows.Documents", "DocumentReference"); - types[KnownTypes.DocumentViewer] = InitType(assemblies[4], "System.Windows.Controls", "DocumentViewer"); - types[KnownTypes.DocumentViewerBase] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "DocumentViewerBase"); - types[KnownTypes.Double] = InitType(assemblies[0], "System", "Double"); - types[KnownTypes.DoubleAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleAnimation"); - types[KnownTypes.DoubleAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleAnimationBase"); - types[KnownTypes.DoubleAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleAnimationUsingKeyFrames"); - types[KnownTypes.DoubleAnimationUsingPath] = InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleAnimationUsingPath"); - types[KnownTypes.DoubleCollection] = InitType(assemblies[3], "System.Windows.Media", "DoubleCollection"); - types[KnownTypes.DoubleCollectionConverter] = InitType(assemblies[3], "System.Windows.Media", "DoubleCollectionConverter"); - types[KnownTypes.DoubleConverter] = InitType(assemblies[1], "System.ComponentModel", "DoubleConverter"); - types[KnownTypes.DoubleIListConverter] = InitType(assemblies[3], "System.Windows.Media.Converters", "DoubleIListConverter"); - types[KnownTypes.DoubleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleKeyFrame"); - types[KnownTypes.DoubleKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleKeyFrameCollection"); - types[KnownTypes.Drawing] = InitType(assemblies[3], "System.Windows.Media", "Drawing"); - types[KnownTypes.DrawingBrush] = InitType(assemblies[3], "System.Windows.Media", "DrawingBrush"); - types[KnownTypes.DrawingCollection] = InitType(assemblies[3], "System.Windows.Media", "DrawingCollection"); - types[KnownTypes.DrawingContext] = InitType(assemblies[3], "System.Windows.Media", "DrawingContext"); - types[KnownTypes.DrawingGroup] = InitType(assemblies[3], "System.Windows.Media", "DrawingGroup"); - types[KnownTypes.DrawingImage] = InitType(assemblies[3], "System.Windows.Media", "DrawingImage"); - types[KnownTypes.DrawingVisual] = InitType(assemblies[3], "System.Windows.Media", "DrawingVisual"); - types[KnownTypes.DropShadowBitmapEffect] = InitType(assemblies[3], "System.Windows.Media.Effects", "DropShadowBitmapEffect"); - types[KnownTypes.Duration] = InitType(assemblies[3], "System.Windows", "Duration"); - types[KnownTypes.DurationConverter] = InitType(assemblies[3], "System.Windows", "DurationConverter"); - types[KnownTypes.DynamicResourceExtension] = InitType(assemblies[4], "System.Windows", "DynamicResourceExtension"); - types[KnownTypes.DynamicResourceExtensionConverter] = InitType(assemblies[4], "System.Windows", "DynamicResourceExtensionConverter"); - types[KnownTypes.Ellipse] = InitType(assemblies[4], "System.Windows.Shapes", "Ellipse"); - types[KnownTypes.EllipseGeometry] = InitType(assemblies[3], "System.Windows.Media", "EllipseGeometry"); - types[KnownTypes.EmbossBitmapEffect] = InitType(assemblies[3], "System.Windows.Media.Effects", "EmbossBitmapEffect"); - types[KnownTypes.EmissiveMaterial] = InitType(assemblies[3], "System.Windows.Media.Media3D", "EmissiveMaterial"); - types[KnownTypes.EnumConverter] = InitType(assemblies[1], "System.ComponentModel", "EnumConverter"); - types[KnownTypes.EventManager] = InitType(assemblies[3], "System.Windows", "EventManager"); - types[KnownTypes.EventSetter] = InitType(assemblies[4], "System.Windows", "EventSetter"); - types[KnownTypes.EventTrigger] = InitType(assemblies[4], "System.Windows", "EventTrigger"); - types[KnownTypes.Expander] = InitType(assemblies[4], "System.Windows.Controls", "Expander"); - types[KnownTypes.Expression] = InitType(assemblies[2], "System.Windows", "Expression"); - types[KnownTypes.ExpressionConverter] = InitType(assemblies[2], "System.Windows", "ExpressionConverter"); - types[KnownTypes.Figure] = InitType(assemblies[4], "System.Windows.Documents", "Figure"); - types[KnownTypes.FigureLength] = InitType(assemblies[4], "System.Windows", "FigureLength"); - types[KnownTypes.FigureLengthConverter] = InitType(assemblies[4], "System.Windows", "FigureLengthConverter"); - types[KnownTypes.FixedDocument] = InitType(assemblies[4], "System.Windows.Documents", "FixedDocument"); - types[KnownTypes.FixedDocumentSequence] = InitType(assemblies[4], "System.Windows.Documents", "FixedDocumentSequence"); - types[KnownTypes.FixedPage] = InitType(assemblies[4], "System.Windows.Documents", "FixedPage"); - types[KnownTypes.Floater] = InitType(assemblies[4], "System.Windows.Documents", "Floater"); - types[KnownTypes.FlowDocument] = InitType(assemblies[4], "System.Windows.Documents", "FlowDocument"); - types[KnownTypes.FlowDocumentPageViewer] = InitType(assemblies[4], "System.Windows.Controls", "FlowDocumentPageViewer"); - types[KnownTypes.FlowDocumentReader] = InitType(assemblies[4], "System.Windows.Controls", "FlowDocumentReader"); - types[KnownTypes.FlowDocumentScrollViewer] = InitType(assemblies[4], "System.Windows.Controls", "FlowDocumentScrollViewer"); - types[KnownTypes.FocusManager] = InitType(assemblies[3], "System.Windows.Input", "FocusManager"); - types[KnownTypes.FontFamily] = InitType(assemblies[3], "System.Windows.Media", "FontFamily"); - types[KnownTypes.FontFamilyConverter] = InitType(assemblies[3], "System.Windows.Media", "FontFamilyConverter"); - types[KnownTypes.FontSizeConverter] = InitType(assemblies[4], "System.Windows", "FontSizeConverter"); - types[KnownTypes.FontStretch] = InitType(assemblies[3], "System.Windows", "FontStretch"); - types[KnownTypes.FontStretchConverter] = InitType(assemblies[3], "System.Windows", "FontStretchConverter"); - types[KnownTypes.FontStyle] = InitType(assemblies[3], "System.Windows", "FontStyle"); - types[KnownTypes.FontStyleConverter] = InitType(assemblies[3], "System.Windows", "FontStyleConverter"); - types[KnownTypes.FontWeight] = InitType(assemblies[3], "System.Windows", "FontWeight"); - types[KnownTypes.FontWeightConverter] = InitType(assemblies[3], "System.Windows", "FontWeightConverter"); - types[KnownTypes.FormatConvertedBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "FormatConvertedBitmap"); - types[KnownTypes.Frame] = InitType(assemblies[4], "System.Windows.Controls", "Frame"); - types[KnownTypes.FrameworkContentElement] = InitType(assemblies[4], "System.Windows", "FrameworkContentElement"); - types[KnownTypes.FrameworkElement] = InitType(assemblies[4], "System.Windows", "FrameworkElement"); - types[KnownTypes.FrameworkElementFactory] = InitType(assemblies[4], "System.Windows", "FrameworkElementFactory"); - types[KnownTypes.FrameworkPropertyMetadata] = InitType(assemblies[4], "System.Windows", "FrameworkPropertyMetadata"); - types[KnownTypes.FrameworkPropertyMetadataOptions] = InitType(assemblies[4], "System.Windows", "FrameworkPropertyMetadataOptions"); - types[KnownTypes.FrameworkRichTextComposition] = InitType(assemblies[4], "System.Windows.Documents", "FrameworkRichTextComposition"); - types[KnownTypes.FrameworkTemplate] = InitType(assemblies[4], "System.Windows", "FrameworkTemplate"); - types[KnownTypes.FrameworkTextComposition] = InitType(assemblies[4], "System.Windows.Documents", "FrameworkTextComposition"); - types[KnownTypes.Freezable] = InitType(assemblies[2], "System.Windows", "Freezable"); - types[KnownTypes.GeneralTransform] = InitType(assemblies[3], "System.Windows.Media", "GeneralTransform"); - types[KnownTypes.GeneralTransformCollection] = InitType(assemblies[3], "System.Windows.Media", "GeneralTransformCollection"); - types[KnownTypes.GeneralTransformGroup] = InitType(assemblies[3], "System.Windows.Media", "GeneralTransformGroup"); - types[KnownTypes.Geometry] = InitType(assemblies[3], "System.Windows.Media", "Geometry"); - types[KnownTypes.Geometry3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Geometry3D"); - types[KnownTypes.GeometryCollection] = InitType(assemblies[3], "System.Windows.Media", "GeometryCollection"); - types[KnownTypes.GeometryConverter] = InitType(assemblies[3], "System.Windows.Media", "GeometryConverter"); - types[KnownTypes.GeometryDrawing] = InitType(assemblies[3], "System.Windows.Media", "GeometryDrawing"); - types[KnownTypes.GeometryGroup] = InitType(assemblies[3], "System.Windows.Media", "GeometryGroup"); - types[KnownTypes.GeometryModel3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "GeometryModel3D"); - types[KnownTypes.GestureRecognizer] = InitType(assemblies[3], "System.Windows.Ink", "GestureRecognizer"); - types[KnownTypes.GifBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "GifBitmapDecoder"); - types[KnownTypes.GifBitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "GifBitmapEncoder"); - types[KnownTypes.GlyphRun] = InitType(assemblies[3], "System.Windows.Media", "GlyphRun"); - types[KnownTypes.GlyphRunDrawing] = InitType(assemblies[3], "System.Windows.Media", "GlyphRunDrawing"); - types[KnownTypes.GlyphTypeface] = InitType(assemblies[3], "System.Windows.Media", "GlyphTypeface"); - types[KnownTypes.Glyphs] = InitType(assemblies[4], "System.Windows.Documents", "Glyphs"); - types[KnownTypes.GradientBrush] = InitType(assemblies[3], "System.Windows.Media", "GradientBrush"); - types[KnownTypes.GradientStop] = InitType(assemblies[3], "System.Windows.Media", "GradientStop"); - types[KnownTypes.GradientStopCollection] = InitType(assemblies[3], "System.Windows.Media", "GradientStopCollection"); - types[KnownTypes.Grid] = InitType(assemblies[4], "System.Windows.Controls", "Grid"); - types[KnownTypes.GridLength] = InitType(assemblies[4], "System.Windows", "GridLength"); - types[KnownTypes.GridLengthConverter] = InitType(assemblies[4], "System.Windows", "GridLengthConverter"); - types[KnownTypes.GridSplitter] = InitType(assemblies[4], "System.Windows.Controls", "GridSplitter"); - types[KnownTypes.GridView] = InitType(assemblies[4], "System.Windows.Controls", "GridView"); - types[KnownTypes.GridViewColumn] = InitType(assemblies[4], "System.Windows.Controls", "GridViewColumn"); - types[KnownTypes.GridViewColumnHeader] = InitType(assemblies[4], "System.Windows.Controls", "GridViewColumnHeader"); - types[KnownTypes.GridViewHeaderRowPresenter] = InitType(assemblies[4], "System.Windows.Controls", "GridViewHeaderRowPresenter"); - types[KnownTypes.GridViewRowPresenter] = InitType(assemblies[4], "System.Windows.Controls", "GridViewRowPresenter"); - types[KnownTypes.GridViewRowPresenterBase] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "GridViewRowPresenterBase"); - types[KnownTypes.GroupBox] = InitType(assemblies[4], "System.Windows.Controls", "GroupBox"); - types[KnownTypes.GroupItem] = InitType(assemblies[4], "System.Windows.Controls", "GroupItem"); - types[KnownTypes.Guid] = InitType(assemblies[0], "System", "Guid"); - types[KnownTypes.GuidConverter] = InitType(assemblies[1], "System.ComponentModel", "GuidConverter"); - types[KnownTypes.GuidelineSet] = InitType(assemblies[3], "System.Windows.Media", "GuidelineSet"); - types[KnownTypes.HeaderedContentControl] = InitType(assemblies[4], "System.Windows.Controls", "HeaderedContentControl"); - types[KnownTypes.HeaderedItemsControl] = InitType(assemblies[4], "System.Windows.Controls", "HeaderedItemsControl"); - types[KnownTypes.HierarchicalDataTemplate] = InitType(assemblies[4], "System.Windows", "HierarchicalDataTemplate"); - types[KnownTypes.HostVisual] = InitType(assemblies[3], "System.Windows.Media", "HostVisual"); - types[KnownTypes.Hyperlink] = InitType(assemblies[4], "System.Windows.Documents", "Hyperlink"); - types[KnownTypes.IAddChild] = InitType(assemblies[3], "System.Windows.Markup", "IAddChild"); - types[KnownTypes.IAddChildInternal] = InitType(assemblies[3], "System.Windows.Markup", "IAddChildInternal"); - types[KnownTypes.ICommand] = InitType(assemblies[3], "System.Windows.Input", "ICommand"); - types[KnownTypes.IComponentConnector] = InitType(assemblies[2], "System.Windows.Markup", "IComponentConnector"); - types[KnownTypes.INameScope] = InitType(assemblies[2], "System.Windows.Markup", "INameScope"); - types[KnownTypes.IStyleConnector] = InitType(assemblies[4], "System.Windows.Markup", "IStyleConnector"); - types[KnownTypes.IconBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "IconBitmapDecoder"); - types[KnownTypes.Image] = InitType(assemblies[4], "System.Windows.Controls", "Image"); - types[KnownTypes.ImageBrush] = InitType(assemblies[3], "System.Windows.Media", "ImageBrush"); - types[KnownTypes.ImageDrawing] = InitType(assemblies[3], "System.Windows.Media", "ImageDrawing"); - types[KnownTypes.ImageMetadata] = InitType(assemblies[3], "System.Windows.Media", "ImageMetadata"); - types[KnownTypes.ImageSource] = InitType(assemblies[3], "System.Windows.Media", "ImageSource"); - types[KnownTypes.ImageSourceConverter] = InitType(assemblies[3], "System.Windows.Media", "ImageSourceConverter"); - types[KnownTypes.InPlaceBitmapMetadataWriter] = InitType(assemblies[3], "System.Windows.Media.Imaging", "InPlaceBitmapMetadataWriter"); - types[KnownTypes.InkCanvas] = InitType(assemblies[4], "System.Windows.Controls", "InkCanvas"); - types[KnownTypes.InkPresenter] = InitType(assemblies[4], "System.Windows.Controls", "InkPresenter"); - types[KnownTypes.Inline] = InitType(assemblies[4], "System.Windows.Documents", "Inline"); - types[KnownTypes.InlineCollection] = InitType(assemblies[4], "System.Windows.Documents", "InlineCollection"); - types[KnownTypes.InlineUIContainer] = InitType(assemblies[4], "System.Windows.Documents", "InlineUIContainer"); - types[KnownTypes.InputBinding] = InitType(assemblies[3], "System.Windows.Input", "InputBinding"); - types[KnownTypes.InputDevice] = InitType(assemblies[3], "System.Windows.Input", "InputDevice"); - types[KnownTypes.InputLanguageManager] = InitType(assemblies[3], "System.Windows.Input", "InputLanguageManager"); - types[KnownTypes.InputManager] = InitType(assemblies[3], "System.Windows.Input", "InputManager"); - types[KnownTypes.InputMethod] = InitType(assemblies[3], "System.Windows.Input", "InputMethod"); - types[KnownTypes.InputScope] = InitType(assemblies[3], "System.Windows.Input", "InputScope"); - types[KnownTypes.InputScopeConverter] = InitType(assemblies[3], "System.Windows.Input", "InputScopeConverter"); - types[KnownTypes.InputScopeName] = InitType(assemblies[3], "System.Windows.Input", "InputScopeName"); - types[KnownTypes.InputScopeNameConverter] = InitType(assemblies[3], "System.Windows.Input", "InputScopeNameConverter"); - types[KnownTypes.Int16] = InitType(assemblies[0], "System", "Int16"); - types[KnownTypes.Int16Animation] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int16Animation"); - types[KnownTypes.Int16AnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int16AnimationBase"); - types[KnownTypes.Int16AnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int16AnimationUsingKeyFrames"); - types[KnownTypes.Int16Converter] = InitType(assemblies[1], "System.ComponentModel", "Int16Converter"); - types[KnownTypes.Int16KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int16KeyFrame"); - types[KnownTypes.Int16KeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int16KeyFrameCollection"); - types[KnownTypes.Int32] = InitType(assemblies[0], "System", "Int32"); - types[KnownTypes.Int32Animation] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int32Animation"); - types[KnownTypes.Int32AnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int32AnimationBase"); - types[KnownTypes.Int32AnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int32AnimationUsingKeyFrames"); - types[KnownTypes.Int32Collection] = InitType(assemblies[3], "System.Windows.Media", "Int32Collection"); - types[KnownTypes.Int32CollectionConverter] = InitType(assemblies[3], "System.Windows.Media", "Int32CollectionConverter"); - types[KnownTypes.Int32Converter] = InitType(assemblies[1], "System.ComponentModel", "Int32Converter"); - types[KnownTypes.Int32KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int32KeyFrame"); - types[KnownTypes.Int32KeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int32KeyFrameCollection"); - types[KnownTypes.Int32Rect] = InitType(assemblies[2], "System.Windows", "Int32Rect"); - types[KnownTypes.Int32RectConverter] = InitType(assemblies[2], "System.Windows", "Int32RectConverter"); - types[KnownTypes.Int64] = InitType(assemblies[0], "System", "Int64"); - types[KnownTypes.Int64Animation] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int64Animation"); - types[KnownTypes.Int64AnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int64AnimationBase"); - types[KnownTypes.Int64AnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int64AnimationUsingKeyFrames"); - types[KnownTypes.Int64Converter] = InitType(assemblies[1], "System.ComponentModel", "Int64Converter"); - types[KnownTypes.Int64KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int64KeyFrame"); - types[KnownTypes.Int64KeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int64KeyFrameCollection"); - types[KnownTypes.Italic] = InitType(assemblies[4], "System.Windows.Documents", "Italic"); - types[KnownTypes.ItemCollection] = InitType(assemblies[4], "System.Windows.Controls", "ItemCollection"); - types[KnownTypes.ItemsControl] = InitType(assemblies[4], "System.Windows.Controls", "ItemsControl"); - types[KnownTypes.ItemsPanelTemplate] = InitType(assemblies[4], "System.Windows.Controls", "ItemsPanelTemplate"); - types[KnownTypes.ItemsPresenter] = InitType(assemblies[4], "System.Windows.Controls", "ItemsPresenter"); - types[KnownTypes.JournalEntry] = InitType(assemblies[4], "System.Windows.Navigation", "JournalEntry"); - types[KnownTypes.JournalEntryListConverter] = InitType(assemblies[4], "System.Windows.Navigation", "JournalEntryListConverter"); - types[KnownTypes.JournalEntryUnifiedViewConverter] = InitType(assemblies[4], "System.Windows.Navigation", "JournalEntryUnifiedViewConverter"); - types[KnownTypes.JpegBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "JpegBitmapDecoder"); - types[KnownTypes.JpegBitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "JpegBitmapEncoder"); - types[KnownTypes.KeyBinding] = InitType(assemblies[3], "System.Windows.Input", "KeyBinding"); - types[KnownTypes.KeyConverter] = InitType(assemblies[2], "System.Windows.Input", "KeyConverter"); - types[KnownTypes.KeyGesture] = InitType(assemblies[3], "System.Windows.Input", "KeyGesture"); - types[KnownTypes.KeyGestureConverter] = InitType(assemblies[3], "System.Windows.Input", "KeyGestureConverter"); - types[KnownTypes.KeySpline] = InitType(assemblies[3], "System.Windows.Media.Animation", "KeySpline"); - types[KnownTypes.KeySplineConverter] = InitType(assemblies[3], "System.Windows", "KeySplineConverter"); - types[KnownTypes.KeyTime] = InitType(assemblies[3], "System.Windows.Media.Animation", "KeyTime"); - types[KnownTypes.KeyTimeConverter] = InitType(assemblies[3], "System.Windows", "KeyTimeConverter"); - types[KnownTypes.KeyboardDevice] = InitType(assemblies[3], "System.Windows.Input", "KeyboardDevice"); - types[KnownTypes.Label] = InitType(assemblies[4], "System.Windows.Controls", "Label"); - types[KnownTypes.LateBoundBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "LateBoundBitmapDecoder"); - types[KnownTypes.LengthConverter] = InitType(assemblies[4], "System.Windows", "LengthConverter"); - types[KnownTypes.Light] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Light"); - types[KnownTypes.Line] = InitType(assemblies[4], "System.Windows.Shapes", "Line"); - types[KnownTypes.LineBreak] = InitType(assemblies[4], "System.Windows.Documents", "LineBreak"); - types[KnownTypes.LineGeometry] = InitType(assemblies[3], "System.Windows.Media", "LineGeometry"); - types[KnownTypes.LineSegment] = InitType(assemblies[3], "System.Windows.Media", "LineSegment"); - types[KnownTypes.LinearByteKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearByteKeyFrame"); - types[KnownTypes.LinearColorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearColorKeyFrame"); - types[KnownTypes.LinearDecimalKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearDecimalKeyFrame"); - types[KnownTypes.LinearDoubleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearDoubleKeyFrame"); - types[KnownTypes.LinearGradientBrush] = InitType(assemblies[3], "System.Windows.Media", "LinearGradientBrush"); - types[KnownTypes.LinearInt16KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearInt16KeyFrame"); - types[KnownTypes.LinearInt32KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearInt32KeyFrame"); - types[KnownTypes.LinearInt64KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearInt64KeyFrame"); - types[KnownTypes.LinearPoint3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearPoint3DKeyFrame"); - types[KnownTypes.LinearPointKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearPointKeyFrame"); - types[KnownTypes.LinearQuaternionKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearQuaternionKeyFrame"); - types[KnownTypes.LinearRectKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearRectKeyFrame"); - types[KnownTypes.LinearRotation3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearRotation3DKeyFrame"); - types[KnownTypes.LinearSingleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearSingleKeyFrame"); - types[KnownTypes.LinearSizeKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearSizeKeyFrame"); - types[KnownTypes.LinearThicknessKeyFrame] = InitType(assemblies[4], "System.Windows.Media.Animation", "LinearThicknessKeyFrame"); - types[KnownTypes.LinearVector3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearVector3DKeyFrame"); - types[KnownTypes.LinearVectorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearVectorKeyFrame"); - types[KnownTypes.List] = InitType(assemblies[4], "System.Windows.Documents", "List"); - types[KnownTypes.ListBox] = InitType(assemblies[4], "System.Windows.Controls", "ListBox"); - types[KnownTypes.ListBoxItem] = InitType(assemblies[4], "System.Windows.Controls", "ListBoxItem"); - types[KnownTypes.ListCollectionView] = InitType(assemblies[4], "System.Windows.Data", "ListCollectionView"); - types[KnownTypes.ListItem] = InitType(assemblies[4], "System.Windows.Documents", "ListItem"); - types[KnownTypes.ListView] = InitType(assemblies[4], "System.Windows.Controls", "ListView"); - types[KnownTypes.ListViewItem] = InitType(assemblies[4], "System.Windows.Controls", "ListViewItem"); - types[KnownTypes.Localization] = InitType(assemblies[4], "System.Windows", "Localization"); - types[KnownTypes.LostFocusEventManager] = InitType(assemblies[4], "System.Windows", "LostFocusEventManager"); - types[KnownTypes.MarkupExtension] = InitType(assemblies[2], "System.Windows.Markup", "MarkupExtension"); - types[KnownTypes.Material] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Material"); - types[KnownTypes.MaterialCollection] = InitType(assemblies[3], "System.Windows.Media.Media3D", "MaterialCollection"); - types[KnownTypes.MaterialGroup] = InitType(assemblies[3], "System.Windows.Media.Media3D", "MaterialGroup"); - types[KnownTypes.Matrix] = InitType(assemblies[2], "System.Windows.Media", "Matrix"); - types[KnownTypes.Matrix3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Matrix3D"); - types[KnownTypes.Matrix3DConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Matrix3DConverter"); - types[KnownTypes.MatrixAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "MatrixAnimationBase"); - types[KnownTypes.MatrixAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "MatrixAnimationUsingKeyFrames"); - types[KnownTypes.MatrixAnimationUsingPath] = InitType(assemblies[3], "System.Windows.Media.Animation", "MatrixAnimationUsingPath"); - types[KnownTypes.MatrixCamera] = InitType(assemblies[3], "System.Windows.Media.Media3D", "MatrixCamera"); - types[KnownTypes.MatrixConverter] = InitType(assemblies[2], "System.Windows.Media", "MatrixConverter"); - types[KnownTypes.MatrixKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "MatrixKeyFrame"); - types[KnownTypes.MatrixKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "MatrixKeyFrameCollection"); - types[KnownTypes.MatrixTransform] = InitType(assemblies[3], "System.Windows.Media", "MatrixTransform"); - types[KnownTypes.MatrixTransform3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "MatrixTransform3D"); - types[KnownTypes.MediaClock] = InitType(assemblies[3], "System.Windows.Media", "MediaClock"); - types[KnownTypes.MediaElement] = InitType(assemblies[4], "System.Windows.Controls", "MediaElement"); - types[KnownTypes.MediaPlayer] = InitType(assemblies[3], "System.Windows.Media", "MediaPlayer"); - types[KnownTypes.MediaTimeline] = InitType(assemblies[3], "System.Windows.Media", "MediaTimeline"); - types[KnownTypes.Menu] = InitType(assemblies[4], "System.Windows.Controls", "Menu"); - types[KnownTypes.MenuBase] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "MenuBase"); - types[KnownTypes.MenuItem] = InitType(assemblies[4], "System.Windows.Controls", "MenuItem"); - types[KnownTypes.MenuScrollingVisibilityConverter] = InitType(assemblies[4], "System.Windows.Controls", "MenuScrollingVisibilityConverter"); - types[KnownTypes.MeshGeometry3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "MeshGeometry3D"); - types[KnownTypes.Model3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Model3D"); - types[KnownTypes.Model3DCollection] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Model3DCollection"); - types[KnownTypes.Model3DGroup] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Model3DGroup"); - types[KnownTypes.ModelVisual3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "ModelVisual3D"); - types[KnownTypes.ModifierKeysConverter] = InitType(assemblies[2], "System.Windows.Input", "ModifierKeysConverter"); - types[KnownTypes.MouseActionConverter] = InitType(assemblies[3], "System.Windows.Input", "MouseActionConverter"); - types[KnownTypes.MouseBinding] = InitType(assemblies[3], "System.Windows.Input", "MouseBinding"); - types[KnownTypes.MouseDevice] = InitType(assemblies[3], "System.Windows.Input", "MouseDevice"); - types[KnownTypes.MouseGesture] = InitType(assemblies[3], "System.Windows.Input", "MouseGesture"); - types[KnownTypes.MouseGestureConverter] = InitType(assemblies[3], "System.Windows.Input", "MouseGestureConverter"); - types[KnownTypes.MultiBinding] = InitType(assemblies[4], "System.Windows.Data", "MultiBinding"); - types[KnownTypes.MultiBindingExpression] = InitType(assemblies[4], "System.Windows.Data", "MultiBindingExpression"); - types[KnownTypes.MultiDataTrigger] = InitType(assemblies[4], "System.Windows", "MultiDataTrigger"); - types[KnownTypes.MultiTrigger] = InitType(assemblies[4], "System.Windows", "MultiTrigger"); - types[KnownTypes.NameScope] = InitType(assemblies[4], "System.Windows", "NameScope"); - types[KnownTypes.NavigationWindow] = InitType(assemblies[4], "System.Windows.Navigation", "NavigationWindow"); - types[KnownTypes.NullExtension] = InitType(assemblies[4], "System.Windows.Markup", "NullExtension"); - types[KnownTypes.NullableBoolConverter] = InitType(assemblies[4], "System.Windows", "NullableBoolConverter"); - types[KnownTypes.NullableConverter] = InitType(assemblies[1], "System.ComponentModel", "NullableConverter"); - types[KnownTypes.NumberSubstitution] = InitType(assemblies[3], "System.Windows.Media", "NumberSubstitution"); - types[KnownTypes.Object] = InitType(assemblies[0], "System", "Object"); - types[KnownTypes.ObjectAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "ObjectAnimationBase"); - types[KnownTypes.ObjectAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "ObjectAnimationUsingKeyFrames"); - types[KnownTypes.ObjectDataProvider] = InitType(assemblies[4], "System.Windows.Data", "ObjectDataProvider"); - types[KnownTypes.ObjectKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "ObjectKeyFrame"); - types[KnownTypes.ObjectKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "ObjectKeyFrameCollection"); - types[KnownTypes.OrthographicCamera] = InitType(assemblies[3], "System.Windows.Media.Media3D", "OrthographicCamera"); - types[KnownTypes.OuterGlowBitmapEffect] = InitType(assemblies[3], "System.Windows.Media.Effects", "OuterGlowBitmapEffect"); - types[KnownTypes.Page] = InitType(assemblies[4], "System.Windows.Controls", "Page"); - types[KnownTypes.PageContent] = InitType(assemblies[4], "System.Windows.Documents", "PageContent"); - types[KnownTypes.PageFunctionBase] = InitType(assemblies[4], "System.Windows.Navigation", "PageFunctionBase"); - types[KnownTypes.Panel] = InitType(assemblies[4], "System.Windows.Controls", "Panel"); - types[KnownTypes.Paragraph] = InitType(assemblies[4], "System.Windows.Documents", "Paragraph"); - types[KnownTypes.ParallelTimeline] = InitType(assemblies[3], "System.Windows.Media.Animation", "ParallelTimeline"); - types[KnownTypes.ParserContext] = InitType(assemblies[4], "System.Windows.Markup", "ParserContext"); - types[KnownTypes.PasswordBox] = InitType(assemblies[4], "System.Windows.Controls", "PasswordBox"); - types[KnownTypes.Path] = InitType(assemblies[4], "System.Windows.Shapes", "Path"); - types[KnownTypes.PathFigure] = InitType(assemblies[3], "System.Windows.Media", "PathFigure"); - types[KnownTypes.PathFigureCollection] = InitType(assemblies[3], "System.Windows.Media", "PathFigureCollection"); - types[KnownTypes.PathFigureCollectionConverter] = InitType(assemblies[3], "System.Windows.Media", "PathFigureCollectionConverter"); - types[KnownTypes.PathGeometry] = InitType(assemblies[3], "System.Windows.Media", "PathGeometry"); - types[KnownTypes.PathSegment] = InitType(assemblies[3], "System.Windows.Media", "PathSegment"); - types[KnownTypes.PathSegmentCollection] = InitType(assemblies[3], "System.Windows.Media", "PathSegmentCollection"); - types[KnownTypes.PauseStoryboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "PauseStoryboard"); - types[KnownTypes.Pen] = InitType(assemblies[3], "System.Windows.Media", "Pen"); - types[KnownTypes.PerspectiveCamera] = InitType(assemblies[3], "System.Windows.Media.Media3D", "PerspectiveCamera"); - types[KnownTypes.PixelFormat] = InitType(assemblies[3], "System.Windows.Media", "PixelFormat"); - types[KnownTypes.PixelFormatConverter] = InitType(assemblies[3], "System.Windows.Media", "PixelFormatConverter"); - types[KnownTypes.PngBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "PngBitmapDecoder"); - types[KnownTypes.PngBitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "PngBitmapEncoder"); - types[KnownTypes.Point] = InitType(assemblies[2], "System.Windows", "Point"); - types[KnownTypes.Point3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Point3D"); - types[KnownTypes.Point3DAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "Point3DAnimation"); - types[KnownTypes.Point3DAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "Point3DAnimationBase"); - types[KnownTypes.Point3DAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "Point3DAnimationUsingKeyFrames"); - types[KnownTypes.Point3DCollection] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Point3DCollection"); - types[KnownTypes.Point3DCollectionConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Point3DCollectionConverter"); - types[KnownTypes.Point3DConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Point3DConverter"); - types[KnownTypes.Point3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "Point3DKeyFrame"); - types[KnownTypes.Point3DKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "Point3DKeyFrameCollection"); - types[KnownTypes.Point4D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Point4D"); - types[KnownTypes.Point4DConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Point4DConverter"); - types[KnownTypes.PointAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "PointAnimation"); - types[KnownTypes.PointAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "PointAnimationBase"); - types[KnownTypes.PointAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "PointAnimationUsingKeyFrames"); - types[KnownTypes.PointAnimationUsingPath] = InitType(assemblies[3], "System.Windows.Media.Animation", "PointAnimationUsingPath"); - types[KnownTypes.PointCollection] = InitType(assemblies[3], "System.Windows.Media", "PointCollection"); - types[KnownTypes.PointCollectionConverter] = InitType(assemblies[3], "System.Windows.Media", "PointCollectionConverter"); - types[KnownTypes.PointConverter] = InitType(assemblies[2], "System.Windows", "PointConverter"); - types[KnownTypes.PointIListConverter] = InitType(assemblies[3], "System.Windows.Media.Converters", "PointIListConverter"); - types[KnownTypes.PointKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "PointKeyFrame"); - types[KnownTypes.PointKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "PointKeyFrameCollection"); - types[KnownTypes.PointLight] = InitType(assemblies[3], "System.Windows.Media.Media3D", "PointLight"); - types[KnownTypes.PointLightBase] = InitType(assemblies[3], "System.Windows.Media.Media3D", "PointLightBase"); - types[KnownTypes.PolyBezierSegment] = InitType(assemblies[3], "System.Windows.Media", "PolyBezierSegment"); - types[KnownTypes.PolyLineSegment] = InitType(assemblies[3], "System.Windows.Media", "PolyLineSegment"); - types[KnownTypes.PolyQuadraticBezierSegment] = InitType(assemblies[3], "System.Windows.Media", "PolyQuadraticBezierSegment"); - types[KnownTypes.Polygon] = InitType(assemblies[4], "System.Windows.Shapes", "Polygon"); - types[KnownTypes.Polyline] = InitType(assemblies[4], "System.Windows.Shapes", "Polyline"); - types[KnownTypes.Popup] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "Popup"); - types[KnownTypes.PresentationSource] = InitType(assemblies[3], "System.Windows", "PresentationSource"); - types[KnownTypes.PriorityBinding] = InitType(assemblies[4], "System.Windows.Data", "PriorityBinding"); - types[KnownTypes.PriorityBindingExpression] = InitType(assemblies[4], "System.Windows.Data", "PriorityBindingExpression"); - types[KnownTypes.ProgressBar] = InitType(assemblies[4], "System.Windows.Controls", "ProgressBar"); - types[KnownTypes.ProjectionCamera] = InitType(assemblies[3], "System.Windows.Media.Media3D", "ProjectionCamera"); - types[KnownTypes.PropertyPath] = InitType(assemblies[4], "System.Windows", "PropertyPath"); - types[KnownTypes.PropertyPathConverter] = InitType(assemblies[4], "System.Windows", "PropertyPathConverter"); - types[KnownTypes.QuadraticBezierSegment] = InitType(assemblies[3], "System.Windows.Media", "QuadraticBezierSegment"); - types[KnownTypes.Quaternion] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Quaternion"); - types[KnownTypes.QuaternionAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "QuaternionAnimation"); - types[KnownTypes.QuaternionAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "QuaternionAnimationBase"); - types[KnownTypes.QuaternionAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "QuaternionAnimationUsingKeyFrames"); - types[KnownTypes.QuaternionConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "QuaternionConverter"); - types[KnownTypes.QuaternionKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "QuaternionKeyFrame"); - types[KnownTypes.QuaternionKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "QuaternionKeyFrameCollection"); - types[KnownTypes.QuaternionRotation3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "QuaternionRotation3D"); - types[KnownTypes.RadialGradientBrush] = InitType(assemblies[3], "System.Windows.Media", "RadialGradientBrush"); - types[KnownTypes.RadioButton] = InitType(assemblies[4], "System.Windows.Controls", "RadioButton"); - types[KnownTypes.RangeBase] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "RangeBase"); - types[KnownTypes.Rect] = InitType(assemblies[2], "System.Windows", "Rect"); - types[KnownTypes.Rect3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Rect3D"); - types[KnownTypes.Rect3DConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Rect3DConverter"); - types[KnownTypes.RectAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "RectAnimation"); - types[KnownTypes.RectAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "RectAnimationBase"); - types[KnownTypes.RectAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "RectAnimationUsingKeyFrames"); - types[KnownTypes.RectConverter] = InitType(assemblies[2], "System.Windows", "RectConverter"); - types[KnownTypes.RectKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "RectKeyFrame"); - types[KnownTypes.RectKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "RectKeyFrameCollection"); - types[KnownTypes.Rectangle] = InitType(assemblies[4], "System.Windows.Shapes", "Rectangle"); - types[KnownTypes.RectangleGeometry] = InitType(assemblies[3], "System.Windows.Media", "RectangleGeometry"); - types[KnownTypes.RelativeSource] = InitType(assemblies[4], "System.Windows.Data", "RelativeSource"); - types[KnownTypes.RemoveStoryboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "RemoveStoryboard"); - types[KnownTypes.RenderOptions] = InitType(assemblies[3], "System.Windows.Media", "RenderOptions"); - types[KnownTypes.RenderTargetBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "RenderTargetBitmap"); - types[KnownTypes.RepeatBehavior] = InitType(assemblies[3], "System.Windows.Media.Animation", "RepeatBehavior"); - types[KnownTypes.RepeatBehaviorConverter] = InitType(assemblies[3], "System.Windows.Media.Animation", "RepeatBehaviorConverter"); - types[KnownTypes.RepeatButton] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "RepeatButton"); - types[KnownTypes.ResizeGrip] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "ResizeGrip"); - types[KnownTypes.ResourceDictionary] = InitType(assemblies[4], "System.Windows", "ResourceDictionary"); - types[KnownTypes.ResourceKey] = InitType(assemblies[4], "System.Windows", "ResourceKey"); - types[KnownTypes.ResumeStoryboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "ResumeStoryboard"); - types[KnownTypes.RichTextBox] = InitType(assemblies[4], "System.Windows.Controls", "RichTextBox"); - types[KnownTypes.RotateTransform] = InitType(assemblies[3], "System.Windows.Media", "RotateTransform"); - types[KnownTypes.RotateTransform3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "RotateTransform3D"); - types[KnownTypes.Rotation3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Rotation3D"); - types[KnownTypes.Rotation3DAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "Rotation3DAnimation"); - types[KnownTypes.Rotation3DAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "Rotation3DAnimationBase"); - types[KnownTypes.Rotation3DAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "Rotation3DAnimationUsingKeyFrames"); - types[KnownTypes.Rotation3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "Rotation3DKeyFrame"); - types[KnownTypes.Rotation3DKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "Rotation3DKeyFrameCollection"); - types[KnownTypes.RoutedCommand] = InitType(assemblies[3], "System.Windows.Input", "RoutedCommand"); - types[KnownTypes.RoutedEvent] = InitType(assemblies[3], "System.Windows", "RoutedEvent"); - types[KnownTypes.RoutedEventConverter] = InitType(assemblies[4], "System.Windows.Markup", "RoutedEventConverter"); - types[KnownTypes.RoutedUICommand] = InitType(assemblies[3], "System.Windows.Input", "RoutedUICommand"); - types[KnownTypes.RoutingStrategy] = InitType(assemblies[3], "System.Windows", "RoutingStrategy"); - types[KnownTypes.RowDefinition] = InitType(assemblies[4], "System.Windows.Controls", "RowDefinition"); - types[KnownTypes.Run] = InitType(assemblies[4], "System.Windows.Documents", "Run"); - types[KnownTypes.RuntimeNamePropertyAttribute] = InitType(assemblies[2], "System.Windows.Markup", "RuntimeNamePropertyAttribute"); - types[KnownTypes.SByte] = InitType(assemblies[0], "System", "SByte"); - types[KnownTypes.SByteConverter] = InitType(assemblies[1], "System.ComponentModel", "SByteConverter"); - types[KnownTypes.ScaleTransform] = InitType(assemblies[3], "System.Windows.Media", "ScaleTransform"); - types[KnownTypes.ScaleTransform3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "ScaleTransform3D"); - types[KnownTypes.ScrollBar] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "ScrollBar"); - types[KnownTypes.ScrollContentPresenter] = InitType(assemblies[4], "System.Windows.Controls", "ScrollContentPresenter"); - types[KnownTypes.ScrollViewer] = InitType(assemblies[4], "System.Windows.Controls", "ScrollViewer"); - types[KnownTypes.Section] = InitType(assemblies[4], "System.Windows.Documents", "Section"); - types[KnownTypes.SeekStoryboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "SeekStoryboard"); - types[KnownTypes.Selector] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "Selector"); - types[KnownTypes.Separator] = InitType(assemblies[4], "System.Windows.Controls", "Separator"); - types[KnownTypes.SetStoryboardSpeedRatio] = InitType(assemblies[4], "System.Windows.Media.Animation", "SetStoryboardSpeedRatio"); - types[KnownTypes.Setter] = InitType(assemblies[4], "System.Windows", "Setter"); - types[KnownTypes.SetterBase] = InitType(assemblies[4], "System.Windows", "SetterBase"); - types[KnownTypes.Shape] = InitType(assemblies[4], "System.Windows.Shapes", "Shape"); - types[KnownTypes.Single] = InitType(assemblies[0], "System", "Single"); - types[KnownTypes.SingleAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "SingleAnimation"); - types[KnownTypes.SingleAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "SingleAnimationBase"); - types[KnownTypes.SingleAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "SingleAnimationUsingKeyFrames"); - types[KnownTypes.SingleConverter] = InitType(assemblies[1], "System.ComponentModel", "SingleConverter"); - types[KnownTypes.SingleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SingleKeyFrame"); - types[KnownTypes.SingleKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "SingleKeyFrameCollection"); - types[KnownTypes.Size] = InitType(assemblies[2], "System.Windows", "Size"); - types[KnownTypes.Size3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Size3D"); - types[KnownTypes.Size3DConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Size3DConverter"); - types[KnownTypes.SizeAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "SizeAnimation"); - types[KnownTypes.SizeAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "SizeAnimationBase"); - types[KnownTypes.SizeAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "SizeAnimationUsingKeyFrames"); - types[KnownTypes.SizeConverter] = InitType(assemblies[2], "System.Windows", "SizeConverter"); - types[KnownTypes.SizeKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SizeKeyFrame"); - types[KnownTypes.SizeKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "SizeKeyFrameCollection"); - types[KnownTypes.SkewTransform] = InitType(assemblies[3], "System.Windows.Media", "SkewTransform"); - types[KnownTypes.SkipStoryboardToFill] = InitType(assemblies[4], "System.Windows.Media.Animation", "SkipStoryboardToFill"); - types[KnownTypes.Slider] = InitType(assemblies[4], "System.Windows.Controls", "Slider"); - types[KnownTypes.SolidColorBrush] = InitType(assemblies[3], "System.Windows.Media", "SolidColorBrush"); - types[KnownTypes.SoundPlayerAction] = InitType(assemblies[4], "System.Windows.Controls", "SoundPlayerAction"); - types[KnownTypes.Span] = InitType(assemblies[4], "System.Windows.Documents", "Span"); - types[KnownTypes.SpecularMaterial] = InitType(assemblies[3], "System.Windows.Media.Media3D", "SpecularMaterial"); - types[KnownTypes.SpellCheck] = InitType(assemblies[4], "System.Windows.Controls", "SpellCheck"); - types[KnownTypes.SplineByteKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineByteKeyFrame"); - types[KnownTypes.SplineColorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineColorKeyFrame"); - types[KnownTypes.SplineDecimalKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineDecimalKeyFrame"); - types[KnownTypes.SplineDoubleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineDoubleKeyFrame"); - types[KnownTypes.SplineInt16KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineInt16KeyFrame"); - types[KnownTypes.SplineInt32KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineInt32KeyFrame"); - types[KnownTypes.SplineInt64KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineInt64KeyFrame"); - types[KnownTypes.SplinePoint3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplinePoint3DKeyFrame"); - types[KnownTypes.SplinePointKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplinePointKeyFrame"); - types[KnownTypes.SplineQuaternionKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineQuaternionKeyFrame"); - types[KnownTypes.SplineRectKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineRectKeyFrame"); - types[KnownTypes.SplineRotation3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineRotation3DKeyFrame"); - types[KnownTypes.SplineSingleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineSingleKeyFrame"); - types[KnownTypes.SplineSizeKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineSizeKeyFrame"); - types[KnownTypes.SplineThicknessKeyFrame] = InitType(assemblies[4], "System.Windows.Media.Animation", "SplineThicknessKeyFrame"); - types[KnownTypes.SplineVector3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineVector3DKeyFrame"); - types[KnownTypes.SplineVectorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineVectorKeyFrame"); - types[KnownTypes.SpotLight] = InitType(assemblies[3], "System.Windows.Media.Media3D", "SpotLight"); - types[KnownTypes.StackPanel] = InitType(assemblies[4], "System.Windows.Controls", "StackPanel"); - types[KnownTypes.StaticExtension] = InitType(assemblies[4], "System.Windows.Markup", "StaticExtension"); - types[KnownTypes.StaticResourceExtension] = InitType(assemblies[4], "System.Windows", "StaticResourceExtension"); - types[KnownTypes.StatusBar] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "StatusBar"); - types[KnownTypes.StatusBarItem] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "StatusBarItem"); - types[KnownTypes.StickyNoteControl] = InitType(assemblies[4], "System.Windows.Controls", "StickyNoteControl"); - types[KnownTypes.StopStoryboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "StopStoryboard"); - types[KnownTypes.Storyboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "Storyboard"); - types[KnownTypes.StreamGeometry] = InitType(assemblies[3], "System.Windows.Media", "StreamGeometry"); - types[KnownTypes.StreamGeometryContext] = InitType(assemblies[3], "System.Windows.Media", "StreamGeometryContext"); - types[KnownTypes.StreamResourceInfo] = InitType(assemblies[4], "System.Windows.Resources", "StreamResourceInfo"); - types[KnownTypes.String] = InitType(assemblies[0], "System", "String"); - types[KnownTypes.StringAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "StringAnimationBase"); - types[KnownTypes.StringAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "StringAnimationUsingKeyFrames"); - types[KnownTypes.StringConverter] = InitType(assemblies[1], "System.ComponentModel", "StringConverter"); - types[KnownTypes.StringKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "StringKeyFrame"); - types[KnownTypes.StringKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "StringKeyFrameCollection"); - types[KnownTypes.StrokeCollection] = InitType(assemblies[3], "System.Windows.Ink", "StrokeCollection"); - types[KnownTypes.StrokeCollectionConverter] = InitType(assemblies[3], "System.Windows", "StrokeCollectionConverter"); - types[KnownTypes.Style] = InitType(assemblies[4], "System.Windows", "Style"); - types[KnownTypes.Stylus] = InitType(assemblies[3], "System.Windows.Input", "Stylus"); - types[KnownTypes.StylusDevice] = InitType(assemblies[3], "System.Windows.Input", "StylusDevice"); - types[KnownTypes.TabControl] = InitType(assemblies[4], "System.Windows.Controls", "TabControl"); - types[KnownTypes.TabItem] = InitType(assemblies[4], "System.Windows.Controls", "TabItem"); - types[KnownTypes.TabPanel] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "TabPanel"); - types[KnownTypes.Table] = InitType(assemblies[4], "System.Windows.Documents", "Table"); - types[KnownTypes.TableCell] = InitType(assemblies[4], "System.Windows.Documents", "TableCell"); - types[KnownTypes.TableColumn] = InitType(assemblies[4], "System.Windows.Documents", "TableColumn"); - types[KnownTypes.TableRow] = InitType(assemblies[4], "System.Windows.Documents", "TableRow"); - types[KnownTypes.TableRowGroup] = InitType(assemblies[4], "System.Windows.Documents", "TableRowGroup"); - types[KnownTypes.TabletDevice] = InitType(assemblies[3], "System.Windows.Input", "TabletDevice"); - types[KnownTypes.TemplateBindingExpression] = InitType(assemblies[4], "System.Windows", "TemplateBindingExpression"); - types[KnownTypes.TemplateBindingExpressionConverter] = InitType(assemblies[4], "System.Windows", "TemplateBindingExpressionConverter"); - types[KnownTypes.TemplateBindingExtension] = InitType(assemblies[4], "System.Windows", "TemplateBindingExtension"); - types[KnownTypes.TemplateBindingExtensionConverter] = InitType(assemblies[4], "System.Windows", "TemplateBindingExtensionConverter"); - types[KnownTypes.TemplateKey] = InitType(assemblies[4], "System.Windows", "TemplateKey"); - types[KnownTypes.TemplateKeyConverter] = InitType(assemblies[4], "System.Windows.Markup", "TemplateKeyConverter"); - types[KnownTypes.TextBlock] = InitType(assemblies[4], "System.Windows.Controls", "TextBlock"); - types[KnownTypes.TextBox] = InitType(assemblies[4], "System.Windows.Controls", "TextBox"); - types[KnownTypes.TextBoxBase] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "TextBoxBase"); - types[KnownTypes.TextComposition] = InitType(assemblies[3], "System.Windows.Input", "TextComposition"); - types[KnownTypes.TextCompositionManager] = InitType(assemblies[3], "System.Windows.Input", "TextCompositionManager"); - types[KnownTypes.TextDecoration] = InitType(assemblies[3], "System.Windows", "TextDecoration"); - types[KnownTypes.TextDecorationCollection] = InitType(assemblies[3], "System.Windows", "TextDecorationCollection"); - types[KnownTypes.TextDecorationCollectionConverter] = InitType(assemblies[3], "System.Windows", "TextDecorationCollectionConverter"); - types[KnownTypes.TextEffect] = InitType(assemblies[3], "System.Windows.Media", "TextEffect"); - types[KnownTypes.TextEffectCollection] = InitType(assemblies[3], "System.Windows.Media", "TextEffectCollection"); - types[KnownTypes.TextElement] = InitType(assemblies[4], "System.Windows.Documents", "TextElement"); - types[KnownTypes.TextSearch] = InitType(assemblies[4], "System.Windows.Controls", "TextSearch"); - types[KnownTypes.ThemeDictionaryExtension] = InitType(assemblies[4], "System.Windows", "ThemeDictionaryExtension"); - types[KnownTypes.Thickness] = InitType(assemblies[4], "System.Windows", "Thickness"); - types[KnownTypes.ThicknessAnimation] = InitType(assemblies[4], "System.Windows.Media.Animation", "ThicknessAnimation"); - types[KnownTypes.ThicknessAnimationBase] = InitType(assemblies[4], "System.Windows.Media.Animation", "ThicknessAnimationBase"); - types[KnownTypes.ThicknessAnimationUsingKeyFrames] = InitType(assemblies[4], "System.Windows.Media.Animation", "ThicknessAnimationUsingKeyFrames"); - types[KnownTypes.ThicknessConverter] = InitType(assemblies[4], "System.Windows", "ThicknessConverter"); - types[KnownTypes.ThicknessKeyFrame] = InitType(assemblies[4], "System.Windows.Media.Animation", "ThicknessKeyFrame"); - types[KnownTypes.ThicknessKeyFrameCollection] = InitType(assemblies[4], "System.Windows.Media.Animation", "ThicknessKeyFrameCollection"); - types[KnownTypes.Thumb] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "Thumb"); - types[KnownTypes.TickBar] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "TickBar"); - types[KnownTypes.TiffBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "TiffBitmapDecoder"); - types[KnownTypes.TiffBitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "TiffBitmapEncoder"); - types[KnownTypes.TileBrush] = InitType(assemblies[3], "System.Windows.Media", "TileBrush"); - types[KnownTypes.TimeSpan] = InitType(assemblies[0], "System", "TimeSpan"); - types[KnownTypes.TimeSpanConverter] = InitType(assemblies[1], "System.ComponentModel", "TimeSpanConverter"); - types[KnownTypes.Timeline] = InitType(assemblies[3], "System.Windows.Media.Animation", "Timeline"); - types[KnownTypes.TimelineCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "TimelineCollection"); - types[KnownTypes.TimelineGroup] = InitType(assemblies[3], "System.Windows.Media.Animation", "TimelineGroup"); - types[KnownTypes.ToggleButton] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "ToggleButton"); - types[KnownTypes.ToolBar] = InitType(assemblies[4], "System.Windows.Controls", "ToolBar"); - types[KnownTypes.ToolBarOverflowPanel] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "ToolBarOverflowPanel"); - types[KnownTypes.ToolBarPanel] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "ToolBarPanel"); - types[KnownTypes.ToolBarTray] = InitType(assemblies[4], "System.Windows.Controls", "ToolBarTray"); - types[KnownTypes.ToolTip] = InitType(assemblies[4], "System.Windows.Controls", "ToolTip"); - types[KnownTypes.ToolTipService] = InitType(assemblies[4], "System.Windows.Controls", "ToolTipService"); - types[KnownTypes.Track] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "Track"); - types[KnownTypes.Transform] = InitType(assemblies[3], "System.Windows.Media", "Transform"); - types[KnownTypes.Transform3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Transform3D"); - types[KnownTypes.Transform3DCollection] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Transform3DCollection"); - types[KnownTypes.Transform3DGroup] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Transform3DGroup"); - types[KnownTypes.TransformCollection] = InitType(assemblies[3], "System.Windows.Media", "TransformCollection"); - types[KnownTypes.TransformConverter] = InitType(assemblies[3], "System.Windows.Media", "TransformConverter"); - types[KnownTypes.TransformGroup] = InitType(assemblies[3], "System.Windows.Media", "TransformGroup"); - types[KnownTypes.TransformedBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "TransformedBitmap"); - types[KnownTypes.TranslateTransform] = InitType(assemblies[3], "System.Windows.Media", "TranslateTransform"); - types[KnownTypes.TranslateTransform3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "TranslateTransform3D"); - types[KnownTypes.TreeView] = InitType(assemblies[4], "System.Windows.Controls", "TreeView"); - types[KnownTypes.TreeViewItem] = InitType(assemblies[4], "System.Windows.Controls", "TreeViewItem"); - types[KnownTypes.Trigger] = InitType(assemblies[4], "System.Windows", "Trigger"); - types[KnownTypes.TriggerAction] = InitType(assemblies[4], "System.Windows", "TriggerAction"); - types[KnownTypes.TriggerBase] = InitType(assemblies[4], "System.Windows", "TriggerBase"); - types[KnownTypes.TypeExtension] = InitType(assemblies[4], "System.Windows.Markup", "TypeExtension"); - types[KnownTypes.TypeTypeConverter] = InitType(assemblies[2], "System.Windows.Markup", "TypeTypeConverter"); - types[KnownTypes.Typography] = InitType(assemblies[4], "System.Windows.Documents", "Typography"); - types[KnownTypes.UIElement] = InitType(assemblies[3], "System.Windows", "UIElement"); - types[KnownTypes.UInt16] = InitType(assemblies[0], "System", "UInt16"); - types[KnownTypes.UInt16Converter] = InitType(assemblies[1], "System.ComponentModel", "UInt16Converter"); - types[KnownTypes.UInt32] = InitType(assemblies[0], "System", "UInt32"); - types[KnownTypes.UInt32Converter] = InitType(assemblies[1], "System.ComponentModel", "UInt32Converter"); - types[KnownTypes.UInt64] = InitType(assemblies[0], "System", "UInt64"); - types[KnownTypes.UInt64Converter] = InitType(assemblies[1], "System.ComponentModel", "UInt64Converter"); - types[KnownTypes.UShortIListConverter] = InitType(assemblies[3], "System.Windows.Media.Converters", "UShortIListConverter"); - types[KnownTypes.Underline] = InitType(assemblies[4], "System.Windows.Documents", "Underline"); - types[KnownTypes.UniformGrid] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "UniformGrid"); - types[KnownTypes.Uri] = InitType(assemblies[1], "System", "Uri"); - types[KnownTypes.UriTypeConverter] = InitType(assemblies[1], "System", "UriTypeConverter"); - types[KnownTypes.UserControl] = InitType(assemblies[4], "System.Windows.Controls", "UserControl"); - types[KnownTypes.Validation] = InitType(assemblies[4], "System.Windows.Controls", "Validation"); - types[KnownTypes.Vector] = InitType(assemblies[2], "System.Windows", "Vector"); - types[KnownTypes.Vector3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Vector3D"); - types[KnownTypes.Vector3DAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "Vector3DAnimation"); - types[KnownTypes.Vector3DAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "Vector3DAnimationBase"); - types[KnownTypes.Vector3DAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "Vector3DAnimationUsingKeyFrames"); - types[KnownTypes.Vector3DCollection] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Vector3DCollection"); - types[KnownTypes.Vector3DCollectionConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Vector3DCollectionConverter"); - types[KnownTypes.Vector3DConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Vector3DConverter"); - types[KnownTypes.Vector3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "Vector3DKeyFrame"); - types[KnownTypes.Vector3DKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "Vector3DKeyFrameCollection"); - types[KnownTypes.VectorAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "VectorAnimation"); - types[KnownTypes.VectorAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "VectorAnimationBase"); - types[KnownTypes.VectorAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "VectorAnimationUsingKeyFrames"); - types[KnownTypes.VectorCollection] = InitType(assemblies[3], "System.Windows.Media", "VectorCollection"); - types[KnownTypes.VectorCollectionConverter] = InitType(assemblies[3], "System.Windows.Media", "VectorCollectionConverter"); - types[KnownTypes.VectorConverter] = InitType(assemblies[2], "System.Windows", "VectorConverter"); - types[KnownTypes.VectorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "VectorKeyFrame"); - types[KnownTypes.VectorKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "VectorKeyFrameCollection"); - types[KnownTypes.VideoDrawing] = InitType(assemblies[3], "System.Windows.Media", "VideoDrawing"); - types[KnownTypes.ViewBase] = InitType(assemblies[4], "System.Windows.Controls", "ViewBase"); - types[KnownTypes.Viewbox] = InitType(assemblies[4], "System.Windows.Controls", "Viewbox"); - types[KnownTypes.Viewport3D] = InitType(assemblies[4], "System.Windows.Controls", "Viewport3D"); - types[KnownTypes.Viewport3DVisual] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Viewport3DVisual"); - types[KnownTypes.VirtualizingPanel] = InitType(assemblies[4], "System.Windows.Controls", "VirtualizingPanel"); - types[KnownTypes.VirtualizingStackPanel] = InitType(assemblies[4], "System.Windows.Controls", "VirtualizingStackPanel"); - types[KnownTypes.Visual] = InitType(assemblies[3], "System.Windows.Media", "Visual"); - types[KnownTypes.Visual3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Visual3D"); - types[KnownTypes.VisualBrush] = InitType(assemblies[3], "System.Windows.Media", "VisualBrush"); - types[KnownTypes.VisualTarget] = InitType(assemblies[3], "System.Windows.Media", "VisualTarget"); - types[KnownTypes.WeakEventManager] = InitType(assemblies[2], "System.Windows", "WeakEventManager"); - types[KnownTypes.WhitespaceSignificantCollectionAttribute] = InitType(assemblies[2], "System.Windows.Markup", "WhitespaceSignificantCollectionAttribute"); - types[KnownTypes.Window] = InitType(assemblies[4], "System.Windows", "Window"); - types[KnownTypes.WmpBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "WmpBitmapDecoder"); - types[KnownTypes.WmpBitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "WmpBitmapEncoder"); - types[KnownTypes.WrapPanel] = InitType(assemblies[4], "System.Windows.Controls", "WrapPanel"); - types[KnownTypes.WriteableBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "WriteableBitmap"); - types[KnownTypes.XamlBrushSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlBrushSerializer"); - types[KnownTypes.XamlInt32CollectionSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlInt32CollectionSerializer"); - types[KnownTypes.XamlPathDataSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlPathDataSerializer"); - types[KnownTypes.XamlPoint3DCollectionSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlPoint3DCollectionSerializer"); - types[KnownTypes.XamlPointCollectionSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlPointCollectionSerializer"); - types[KnownTypes.XamlReader] = InitType(assemblies[4], "System.Windows.Markup", "XamlReader"); - types[KnownTypes.XamlStyleSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlStyleSerializer"); - types[KnownTypes.XamlTemplateSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlTemplateSerializer"); - types[KnownTypes.XamlVector3DCollectionSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlVector3DCollectionSerializer"); - types[KnownTypes.XamlWriter] = InitType(assemblies[4], "System.Windows.Markup", "XamlWriter"); - types[KnownTypes.XmlDataProvider] = InitType(assemblies[4], "System.Windows.Data", "XmlDataProvider"); - types[KnownTypes.XmlLangPropertyAttribute] = InitType(assemblies[2], "System.Windows.Markup", "XmlLangPropertyAttribute"); - types[KnownTypes.XmlLanguage] = InitType(assemblies[3], "System.Windows.Markup", "XmlLanguage"); - types[KnownTypes.XmlLanguageConverter] = InitType(assemblies[3], "System.Windows.Markup", "XmlLanguageConverter"); - types[KnownTypes.XmlNamespaceMapping] = InitType(assemblies[4], "System.Windows.Data", "XmlNamespaceMapping"); - types[KnownTypes.ZoomPercentageConverter] = InitType(assemblies[4], "System.Windows.Documents", "ZoomPercentageConverter"); - } - - void InitMembers() { - - members[KnownMembers.AccessText_Text] = InitMember(KnownTypes.AccessText, "Text", InitType(assemblies[0], "System", "String")); - members[KnownMembers.BeginStoryboard_Storyboard] = InitMember(KnownTypes.BeginStoryboard, "Storyboard", InitType(assemblies[4], "System.Windows.Media.Animation", "Storyboard")); - members[KnownMembers.BitmapEffectGroup_Children] = InitMember(KnownTypes.BitmapEffectGroup, "Children", InitType(assemblies[3], "System.Windows.Media.Effects", "BitmapEffectCollection")); - members[KnownMembers.Border_Background] = InitMember(KnownTypes.Border, "Background", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.Border_BorderBrush] = InitMember(KnownTypes.Border, "BorderBrush", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.Border_BorderThickness] = InitMember(KnownTypes.Border, "BorderThickness", InitType(assemblies[4], "System.Windows", "Thickness")); - members[KnownMembers.ButtonBase_Command] = InitMember(KnownTypes.ButtonBase, "Command", InitType(assemblies[3], "System.Windows.Input", "ICommand")); - members[KnownMembers.ButtonBase_CommandParameter] = InitMember(KnownTypes.ButtonBase, "CommandParameter", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.ButtonBase_CommandTarget] = InitMember(KnownTypes.ButtonBase, "CommandTarget", InitType(assemblies[3], "System.Windows", "IInputElement")); - members[KnownMembers.ButtonBase_IsPressed] = InitMember(KnownTypes.ButtonBase, "IsPressed", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.ColumnDefinition_MaxWidth] = InitMember(KnownTypes.ColumnDefinition, "MaxWidth", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.ColumnDefinition_MinWidth] = InitMember(KnownTypes.ColumnDefinition, "MinWidth", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.ColumnDefinition_Width] = InitMember(KnownTypes.ColumnDefinition, "Width", InitType(assemblies[4], "System.Windows", "GridLength")); - members[KnownMembers.ContentControl_Content] = InitMember(KnownTypes.ContentControl, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.ContentControl_ContentTemplate] = InitMember(KnownTypes.ContentControl, "ContentTemplate", InitType(assemblies[4], "System.Windows", "DataTemplate")); - members[KnownMembers.ContentControl_ContentTemplateSelector] = InitMember(KnownTypes.ContentControl, "ContentTemplateSelector", InitType(assemblies[4], "System.Windows.Controls", "DataTemplateSelector")); - members[KnownMembers.ContentControl_HasContent] = InitMember(KnownTypes.ContentControl, "HasContent", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.ContentElement_Focusable] = InitMember(KnownTypes.ContentElement, "Focusable", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.ContentPresenter_Content] = InitMember(KnownTypes.ContentPresenter, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.ContentPresenter_ContentSource] = InitMember(KnownTypes.ContentPresenter, "ContentSource", InitType(assemblies[0], "System", "String")); - members[KnownMembers.ContentPresenter_ContentTemplate] = InitMember(KnownTypes.ContentPresenter, "ContentTemplate", InitType(assemblies[4], "System.Windows", "DataTemplate")); - members[KnownMembers.ContentPresenter_ContentTemplateSelector] = InitMember(KnownTypes.ContentPresenter, "ContentTemplateSelector", InitType(assemblies[4], "System.Windows.Controls", "DataTemplateSelector")); - members[KnownMembers.ContentPresenter_RecognizesAccessKey] = InitMember(KnownTypes.ContentPresenter, "RecognizesAccessKey", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.Control_Background] = InitMember(KnownTypes.Control, "Background", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.Control_BorderBrush] = InitMember(KnownTypes.Control, "BorderBrush", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.Control_BorderThickness] = InitMember(KnownTypes.Control, "BorderThickness", InitType(assemblies[4], "System.Windows", "Thickness")); - members[KnownMembers.Control_FontFamily] = InitMember(KnownTypes.Control, "FontFamily", InitType(assemblies[3], "System.Windows.Media", "FontFamily")); - members[KnownMembers.Control_FontSize] = InitMember(KnownTypes.Control, "FontSize", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.Control_FontStretch] = InitMember(KnownTypes.Control, "FontStretch", InitType(assemblies[3], "System.Windows", "FontStretch")); - members[KnownMembers.Control_FontStyle] = InitMember(KnownTypes.Control, "FontStyle", InitType(assemblies[3], "System.Windows", "FontStyle")); - members[KnownMembers.Control_FontWeight] = InitMember(KnownTypes.Control, "FontWeight", InitType(assemblies[3], "System.Windows", "FontWeight")); - members[KnownMembers.Control_Foreground] = InitMember(KnownTypes.Control, "Foreground", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.Control_HorizontalContentAlignment] = InitMember(KnownTypes.Control, "HorizontalContentAlignment", InitType(assemblies[4], "System.Windows", "HorizontalAlignment")); - members[KnownMembers.Control_IsTabStop] = InitMember(KnownTypes.Control, "IsTabStop", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.Control_Padding] = InitMember(KnownTypes.Control, "Padding", InitType(assemblies[4], "System.Windows", "Thickness")); - members[KnownMembers.Control_TabIndex] = InitMember(KnownTypes.Control, "TabIndex", InitType(assemblies[0], "System", "Int32")); - members[KnownMembers.Control_Template] = InitMember(KnownTypes.Control, "Template", InitType(assemblies[4], "System.Windows.Controls", "ControlTemplate")); - members[KnownMembers.Control_VerticalContentAlignment] = InitMember(KnownTypes.Control, "VerticalContentAlignment", InitType(assemblies[4], "System.Windows", "VerticalAlignment")); - members[KnownMembers.DockPanel_Dock] = InitMember(KnownTypes.DockPanel, "Dock", InitType(assemblies[4], "System.Windows.Controls", "Dock")); - members[KnownMembers.DockPanel_LastChildFill] = InitMember(KnownTypes.DockPanel, "LastChildFill", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.DocumentViewerBase_Document] = InitMember(KnownTypes.DocumentViewerBase, "Document", InitType(assemblies[3], "System.Windows.Documents", "IDocumentPaginatorSource")); - members[KnownMembers.DrawingGroup_Children] = InitMember(KnownTypes.DrawingGroup, "Children", InitType(assemblies[3], "System.Windows.Media", "DrawingCollection")); - members[KnownMembers.FlowDocumentReader_Document] = InitMember(KnownTypes.FlowDocumentReader, "Document", InitType(assemblies[4], "System.Windows.Documents", "FlowDocument")); - members[KnownMembers.FlowDocumentScrollViewer_Document] = InitMember(KnownTypes.FlowDocumentScrollViewer, "Document", InitType(assemblies[4], "System.Windows.Documents", "FlowDocument")); - members[KnownMembers.FrameworkContentElement_Style] = InitMember(KnownTypes.FrameworkContentElement, "Style", InitType(assemblies[4], "System.Windows", "Style")); - members[KnownMembers.FrameworkElement_FlowDirection] = InitMember(KnownTypes.FrameworkElement, "FlowDirection", InitType(assemblies[3], "System.Windows", "FlowDirection")); - members[KnownMembers.FrameworkElement_Height] = InitMember(KnownTypes.FrameworkElement, "Height", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.FrameworkElement_HorizontalAlignment] = InitMember(KnownTypes.FrameworkElement, "HorizontalAlignment", InitType(assemblies[4], "System.Windows", "HorizontalAlignment")); - members[KnownMembers.FrameworkElement_Margin] = InitMember(KnownTypes.FrameworkElement, "Margin", InitType(assemblies[4], "System.Windows", "Thickness")); - members[KnownMembers.FrameworkElement_MaxHeight] = InitMember(KnownTypes.FrameworkElement, "MaxHeight", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.FrameworkElement_MaxWidth] = InitMember(KnownTypes.FrameworkElement, "MaxWidth", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.FrameworkElement_MinHeight] = InitMember(KnownTypes.FrameworkElement, "MinHeight", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.FrameworkElement_MinWidth] = InitMember(KnownTypes.FrameworkElement, "MinWidth", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.FrameworkElement_Name] = InitMember(KnownTypes.FrameworkElement, "Name", InitType(assemblies[0], "System", "String")); - members[KnownMembers.FrameworkElement_Style] = InitMember(KnownTypes.FrameworkElement, "Style", InitType(assemblies[4], "System.Windows", "Style")); - members[KnownMembers.FrameworkElement_VerticalAlignment] = InitMember(KnownTypes.FrameworkElement, "VerticalAlignment", InitType(assemblies[4], "System.Windows", "VerticalAlignment")); - members[KnownMembers.FrameworkElement_Width] = InitMember(KnownTypes.FrameworkElement, "Width", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.GeneralTransformGroup_Children] = InitMember(KnownTypes.GeneralTransformGroup, "Children", InitType(assemblies[3], "System.Windows.Media", "GeneralTransformCollection")); - members[KnownMembers.GeometryGroup_Children] = InitMember(KnownTypes.GeometryGroup, "Children", InitType(assemblies[3], "System.Windows.Media", "GeometryCollection")); - members[KnownMembers.GradientBrush_GradientStops] = InitMember(KnownTypes.GradientBrush, "GradientStops", InitType(assemblies[3], "System.Windows.Media", "GradientStopCollection")); - members[KnownMembers.Grid_Column] = InitMember(KnownTypes.Grid, "Column", InitType(assemblies[0], "System", "Int32")); - members[KnownMembers.Grid_ColumnSpan] = InitMember(KnownTypes.Grid, "ColumnSpan", InitType(assemblies[0], "System", "Int32")); - members[KnownMembers.Grid_Row] = InitMember(KnownTypes.Grid, "Row", InitType(assemblies[0], "System", "Int32")); - members[KnownMembers.Grid_RowSpan] = InitMember(KnownTypes.Grid, "RowSpan", InitType(assemblies[0], "System", "Int32")); - members[KnownMembers.GridViewColumn_Header] = InitMember(KnownTypes.GridViewColumn, "Header", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.HeaderedContentControl_HasHeader] = InitMember(KnownTypes.HeaderedContentControl, "HasHeader", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.HeaderedContentControl_Header] = InitMember(KnownTypes.HeaderedContentControl, "Header", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.HeaderedContentControl_HeaderTemplate] = InitMember(KnownTypes.HeaderedContentControl, "HeaderTemplate", InitType(assemblies[4], "System.Windows", "DataTemplate")); - members[KnownMembers.HeaderedContentControl_HeaderTemplateSelector] = InitMember(KnownTypes.HeaderedContentControl, "HeaderTemplateSelector", InitType(assemblies[4], "System.Windows.Controls", "DataTemplateSelector")); - members[KnownMembers.HeaderedItemsControl_HasHeader] = InitMember(KnownTypes.HeaderedItemsControl, "HasHeader", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.HeaderedItemsControl_Header] = InitMember(KnownTypes.HeaderedItemsControl, "Header", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.HeaderedItemsControl_HeaderTemplate] = InitMember(KnownTypes.HeaderedItemsControl, "HeaderTemplate", InitType(assemblies[4], "System.Windows", "DataTemplate")); - members[KnownMembers.HeaderedItemsControl_HeaderTemplateSelector] = InitMember(KnownTypes.HeaderedItemsControl, "HeaderTemplateSelector", InitType(assemblies[4], "System.Windows.Controls", "DataTemplateSelector")); - members[KnownMembers.Hyperlink_NavigateUri] = InitMember(KnownTypes.Hyperlink, "NavigateUri", InitType(assemblies[1], "System", "Uri")); - members[KnownMembers.Image_Source] = InitMember(KnownTypes.Image, "Source", InitType(assemblies[3], "System.Windows.Media", "ImageSource")); - members[KnownMembers.Image_Stretch] = InitMember(KnownTypes.Image, "Stretch", InitType(assemblies[3], "System.Windows.Media", "Stretch")); - members[KnownMembers.ItemsControl_ItemContainerStyle] = InitMember(KnownTypes.ItemsControl, "ItemContainerStyle", InitType(assemblies[4], "System.Windows", "Style")); - members[KnownMembers.ItemsControl_ItemContainerStyleSelector] = InitMember(KnownTypes.ItemsControl, "ItemContainerStyleSelector", InitType(assemblies[4], "System.Windows.Controls", "StyleSelector")); - members[KnownMembers.ItemsControl_ItemTemplate] = InitMember(KnownTypes.ItemsControl, "ItemTemplate", InitType(assemblies[4], "System.Windows", "DataTemplate")); - members[KnownMembers.ItemsControl_ItemTemplateSelector] = InitMember(KnownTypes.ItemsControl, "ItemTemplateSelector", InitType(assemblies[4], "System.Windows.Controls", "DataTemplateSelector")); - members[KnownMembers.ItemsControl_ItemsPanel] = InitMember(KnownTypes.ItemsControl, "ItemsPanel", InitType(assemblies[4], "System.Windows.Controls", "ItemsPanelTemplate")); - members[KnownMembers.ItemsControl_ItemsSource] = InitMember(KnownTypes.ItemsControl, "ItemsSource", InitType(assemblies[0], "System.Collections", "IEnumerable")); - members[KnownMembers.MaterialGroup_Children] = InitMember(KnownTypes.MaterialGroup, "Children", InitType(assemblies[3], "System.Windows.Media.Media3D", "MaterialCollection")); - members[KnownMembers.Model3DGroup_Children] = InitMember(KnownTypes.Model3DGroup, "Children", InitType(assemblies[3], "System.Windows.Media.Media3D", "Model3DCollection")); - members[KnownMembers.Page_Content] = InitMember(KnownTypes.Page, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.Panel_Background] = InitMember(KnownTypes.Panel, "Background", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.Path_Data] = InitMember(KnownTypes.Path, "Data", InitType(assemblies[3], "System.Windows.Media", "Geometry")); - members[KnownMembers.PathFigure_Segments] = InitMember(KnownTypes.PathFigure, "Segments", InitType(assemblies[3], "System.Windows.Media", "PathSegmentCollection")); - members[KnownMembers.PathGeometry_Figures] = InitMember(KnownTypes.PathGeometry, "Figures", InitType(assemblies[3], "System.Windows.Media", "PathFigureCollection")); - members[KnownMembers.Popup_Child] = InitMember(KnownTypes.Popup, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.Popup_IsOpen] = InitMember(KnownTypes.Popup, "IsOpen", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.Popup_Placement] = InitMember(KnownTypes.Popup, "Placement", InitType(assemblies[4], "System.Windows.Controls.Primitives", "PlacementMode")); - members[KnownMembers.Popup_PopupAnimation] = InitMember(KnownTypes.Popup, "PopupAnimation", InitType(assemblies[4], "System.Windows.Controls.Primitives", "PopupAnimation")); - members[KnownMembers.RowDefinition_Height] = InitMember(KnownTypes.RowDefinition, "Height", InitType(assemblies[4], "System.Windows", "GridLength")); - members[KnownMembers.RowDefinition_MaxHeight] = InitMember(KnownTypes.RowDefinition, "MaxHeight", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.RowDefinition_MinHeight] = InitMember(KnownTypes.RowDefinition, "MinHeight", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.ScrollViewer_CanContentScroll] = InitMember(KnownTypes.ScrollViewer, "CanContentScroll", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.ScrollViewer_HorizontalScrollBarVisibility] = InitMember(KnownTypes.ScrollViewer, "HorizontalScrollBarVisibility", InitType(assemblies[4], "System.Windows.Controls", "ScrollBarVisibility")); - members[KnownMembers.ScrollViewer_VerticalScrollBarVisibility] = InitMember(KnownTypes.ScrollViewer, "VerticalScrollBarVisibility", InitType(assemblies[4], "System.Windows.Controls", "ScrollBarVisibility")); - members[KnownMembers.Shape_Fill] = InitMember(KnownTypes.Shape, "Fill", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.Shape_Stroke] = InitMember(KnownTypes.Shape, "Stroke", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.Shape_StrokeThickness] = InitMember(KnownTypes.Shape, "StrokeThickness", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.TextBlock_Background] = InitMember(KnownTypes.TextBlock, "Background", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.TextBlock_FontFamily] = InitMember(KnownTypes.TextBlock, "FontFamily", InitType(assemblies[3], "System.Windows.Media", "FontFamily")); - members[KnownMembers.TextBlock_FontSize] = InitMember(KnownTypes.TextBlock, "FontSize", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.TextBlock_FontStretch] = InitMember(KnownTypes.TextBlock, "FontStretch", InitType(assemblies[3], "System.Windows", "FontStretch")); - members[KnownMembers.TextBlock_FontStyle] = InitMember(KnownTypes.TextBlock, "FontStyle", InitType(assemblies[3], "System.Windows", "FontStyle")); - members[KnownMembers.TextBlock_FontWeight] = InitMember(KnownTypes.TextBlock, "FontWeight", InitType(assemblies[3], "System.Windows", "FontWeight")); - members[KnownMembers.TextBlock_Foreground] = InitMember(KnownTypes.TextBlock, "Foreground", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.TextBlock_Text] = InitMember(KnownTypes.TextBlock, "Text", InitType(assemblies[0], "System", "String")); - members[KnownMembers.TextBlock_TextDecorations] = InitMember(KnownTypes.TextBlock, "TextDecorations", InitType(assemblies[3], "System.Windows", "TextDecorationCollection")); - members[KnownMembers.TextBlock_TextTrimming] = InitMember(KnownTypes.TextBlock, "TextTrimming", InitType(assemblies[3], "System.Windows", "TextTrimming")); - members[KnownMembers.TextBlock_TextWrapping] = InitMember(KnownTypes.TextBlock, "TextWrapping", InitType(assemblies[3], "System.Windows", "TextWrapping")); - members[KnownMembers.TextBox_Text] = InitMember(KnownTypes.TextBox, "Text", InitType(assemblies[0], "System", "String")); - members[KnownMembers.TextElement_Background] = InitMember(KnownTypes.TextElement, "Background", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.TextElement_FontFamily] = InitMember(KnownTypes.TextElement, "FontFamily", InitType(assemblies[3], "System.Windows.Media", "FontFamily")); - members[KnownMembers.TextElement_FontSize] = InitMember(KnownTypes.TextElement, "FontSize", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.TextElement_FontStretch] = InitMember(KnownTypes.TextElement, "FontStretch", InitType(assemblies[3], "System.Windows", "FontStretch")); - members[KnownMembers.TextElement_FontStyle] = InitMember(KnownTypes.TextElement, "FontStyle", InitType(assemblies[3], "System.Windows", "FontStyle")); - members[KnownMembers.TextElement_FontWeight] = InitMember(KnownTypes.TextElement, "FontWeight", InitType(assemblies[3], "System.Windows", "FontWeight")); - members[KnownMembers.TextElement_Foreground] = InitMember(KnownTypes.TextElement, "Foreground", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.TimelineGroup_Children] = InitMember(KnownTypes.TimelineGroup, "Children", InitType(assemblies[3], "System.Windows.Media.Animation", "TimelineCollection")); - members[KnownMembers.Track_IsDirectionReversed] = InitMember(KnownTypes.Track, "IsDirectionReversed", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.Track_Maximum] = InitMember(KnownTypes.Track, "Maximum", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.Track_Minimum] = InitMember(KnownTypes.Track, "Minimum", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.Track_Orientation] = InitMember(KnownTypes.Track, "Orientation", InitType(assemblies[4], "System.Windows.Controls", "Orientation")); - members[KnownMembers.Track_Value] = InitMember(KnownTypes.Track, "Value", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.Track_ViewportSize] = InitMember(KnownTypes.Track, "ViewportSize", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.Transform3DGroup_Children] = InitMember(KnownTypes.Transform3DGroup, "Children", InitType(assemblies[3], "System.Windows.Media.Media3D", "Transform3DCollection")); - members[KnownMembers.TransformGroup_Children] = InitMember(KnownTypes.TransformGroup, "Children", InitType(assemblies[3], "System.Windows.Media", "TransformCollection")); - members[KnownMembers.UIElement_ClipToBounds] = InitMember(KnownTypes.UIElement, "ClipToBounds", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.UIElement_Focusable] = InitMember(KnownTypes.UIElement, "Focusable", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.UIElement_IsEnabled] = InitMember(KnownTypes.UIElement, "IsEnabled", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.UIElement_RenderTransform] = InitMember(KnownTypes.UIElement, "RenderTransform", InitType(assemblies[3], "System.Windows.Media", "Transform")); - members[KnownMembers.UIElement_Visibility] = InitMember(KnownTypes.UIElement, "Visibility", InitType(assemblies[3], "System.Windows", "Visibility")); - members[KnownMembers.Viewport3D_Children] = InitMember(KnownTypes.Viewport3D, "Children", InitType(assemblies[3], "System.Windows.Media.Media3D", "Visual3DCollection")); - - members[KnownMembers.AdornedElementPlaceholder_Child] = InitMember(KnownTypes.AdornedElementPlaceholder, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.AdornerDecorator_Child] = InitMember(KnownTypes.AdornerDecorator, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.AnchoredBlock_Blocks] = InitMember(KnownTypes.AnchoredBlock, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); - members[KnownMembers.ArrayExtension_Items] = InitMember(KnownTypes.ArrayExtension, "Items", InitType(assemblies[0], "System.Collections", "IList")); - members[KnownMembers.BlockUIContainer_Child] = InitMember(KnownTypes.BlockUIContainer, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.Bold_Inlines] = InitMember(KnownTypes.Bold, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); - members[KnownMembers.BooleanAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.BooleanAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "BooleanKeyFrameCollection")); - members[KnownMembers.Border_Child] = InitMember(KnownTypes.Border, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.BulletDecorator_Child] = InitMember(KnownTypes.BulletDecorator, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.Button_Content] = InitMember(KnownTypes.Button, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.ButtonBase_Content] = InitMember(KnownTypes.ButtonBase, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.ByteAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.ByteAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "ByteKeyFrameCollection")); - members[KnownMembers.Canvas_Children] = InitMember(KnownTypes.Canvas, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.CharAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.CharAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "CharKeyFrameCollection")); - members[KnownMembers.CheckBox_Content] = InitMember(KnownTypes.CheckBox, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.ColorAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.ColorAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "ColorKeyFrameCollection")); - members[KnownMembers.ComboBox_Items] = InitMember(KnownTypes.ComboBox, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.ComboBoxItem_Content] = InitMember(KnownTypes.ComboBoxItem, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.ContextMenu_Items] = InitMember(KnownTypes.ContextMenu, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.ControlTemplate_VisualTree] = InitMember(KnownTypes.ControlTemplate, "VisualTree", InitType(assemblies[4], "System.Windows", "FrameworkElementFactory")); - members[KnownMembers.DataTemplate_VisualTree] = InitMember(KnownTypes.DataTemplate, "VisualTree", InitType(assemblies[4], "System.Windows", "FrameworkElementFactory")); - members[KnownMembers.DataTrigger_Setters] = InitMember(KnownTypes.DataTrigger, "Setters", InitType(assemblies[4], "System.Windows", "SetterBaseCollection")); - members[KnownMembers.DecimalAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.DecimalAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "DecimalKeyFrameCollection")); - members[KnownMembers.Decorator_Child] = InitMember(KnownTypes.Decorator, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.DockPanel_Children] = InitMember(KnownTypes.DockPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.DocumentViewer_Document] = InitMember(KnownTypes.DocumentViewer, "Document", InitType(assemblies[3], "System.Windows.Documents", "IDocumentPaginatorSource")); - members[KnownMembers.DoubleAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.DoubleAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleKeyFrameCollection")); - members[KnownMembers.EventTrigger_Actions] = InitMember(KnownTypes.EventTrigger, "Actions", InitType(assemblies[4], "System.Windows", "TriggerActionCollection")); - members[KnownMembers.Expander_Content] = InitMember(KnownTypes.Expander, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.Figure_Blocks] = InitMember(KnownTypes.Figure, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); - members[KnownMembers.FixedDocument_Pages] = InitMember(KnownTypes.FixedDocument, "Pages", InitType(assemblies[4], "System.Windows.Documents", "PageContentCollection")); - members[KnownMembers.FixedDocumentSequence_References] = InitMember(KnownTypes.FixedDocumentSequence, "References", InitType(assemblies[4], "System.Windows.Documents", "DocumentReferenceCollection")); - members[KnownMembers.FixedPage_Children] = InitMember(KnownTypes.FixedPage, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.Floater_Blocks] = InitMember(KnownTypes.Floater, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); - members[KnownMembers.FlowDocument_Blocks] = InitMember(KnownTypes.FlowDocument, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); - members[KnownMembers.FlowDocumentPageViewer_Document] = InitMember(KnownTypes.FlowDocumentPageViewer, "Document", InitType(assemblies[3], "System.Windows.Documents", "IDocumentPaginatorSource")); - members[KnownMembers.FrameworkTemplate_VisualTree] = InitMember(KnownTypes.FrameworkTemplate, "VisualTree", InitType(assemblies[4], "System.Windows", "FrameworkElementFactory")); - members[KnownMembers.Grid_Children] = InitMember(KnownTypes.Grid, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.GridView_Columns] = InitMember(KnownTypes.GridView, "Columns", InitType(assemblies[4], "System.Windows.Controls", "GridViewColumnCollection")); - members[KnownMembers.GridViewColumnHeader_Content] = InitMember(KnownTypes.GridViewColumnHeader, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.GroupBox_Content] = InitMember(KnownTypes.GroupBox, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.GroupItem_Content] = InitMember(KnownTypes.GroupItem, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.HeaderedContentControl_Content] = InitMember(KnownTypes.HeaderedContentControl, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.HeaderedItemsControl_Items] = InitMember(KnownTypes.HeaderedItemsControl, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.HierarchicalDataTemplate_VisualTree] = InitMember(KnownTypes.HierarchicalDataTemplate, "VisualTree", InitType(assemblies[4], "System.Windows", "FrameworkElementFactory")); - members[KnownMembers.Hyperlink_Inlines] = InitMember(KnownTypes.Hyperlink, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); - members[KnownMembers.InkCanvas_Children] = InitMember(KnownTypes.InkCanvas, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.InkPresenter_Child] = InitMember(KnownTypes.InkPresenter, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.InlineUIContainer_Child] = InitMember(KnownTypes.InlineUIContainer, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.InputScopeName_NameValue] = InitMember(KnownTypes.InputScopeName, "NameValue", InitType(assemblies[3], "System.Windows.Input", "InputScopeNameValue")); - members[KnownMembers.Int16AnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.Int16AnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "Int16KeyFrameCollection")); - members[KnownMembers.Int32AnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.Int32AnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "Int32KeyFrameCollection")); - members[KnownMembers.Int64AnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.Int64AnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "Int64KeyFrameCollection")); - members[KnownMembers.Italic_Inlines] = InitMember(KnownTypes.Italic, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); - members[KnownMembers.ItemsControl_Items] = InitMember(KnownTypes.ItemsControl, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.ItemsPanelTemplate_VisualTree] = InitMember(KnownTypes.ItemsPanelTemplate, "VisualTree", InitType(assemblies[4], "System.Windows", "FrameworkElementFactory")); - members[KnownMembers.Label_Content] = InitMember(KnownTypes.Label, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.LinearGradientBrush_GradientStops] = InitMember(KnownTypes.LinearGradientBrush, "GradientStops", InitType(assemblies[3], "System.Windows.Media", "GradientStopCollection")); - members[KnownMembers.List_ListItems] = InitMember(KnownTypes.List, "ListItems", InitType(assemblies[4], "System.Windows.Documents", "ListItemCollection")); - members[KnownMembers.ListBox_Items] = InitMember(KnownTypes.ListBox, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.ListBoxItem_Content] = InitMember(KnownTypes.ListBoxItem, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.ListItem_Blocks] = InitMember(KnownTypes.ListItem, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); - members[KnownMembers.ListView_Items] = InitMember(KnownTypes.ListView, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.ListViewItem_Content] = InitMember(KnownTypes.ListViewItem, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.MatrixAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.MatrixAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "MatrixKeyFrameCollection")); - members[KnownMembers.Menu_Items] = InitMember(KnownTypes.Menu, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.MenuBase_Items] = InitMember(KnownTypes.MenuBase, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.MenuItem_Items] = InitMember(KnownTypes.MenuItem, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.ModelVisual3D_Children] = InitMember(KnownTypes.ModelVisual3D, "Children", InitType(assemblies[3], "System.Windows.Media.Media3D", "Visual3DCollection")); - members[KnownMembers.MultiBinding_Bindings] = InitMember(KnownTypes.MultiBinding, "Bindings", InitType(assemblies[0], "System.Collections.ObjectModel", "Collection`1")); - members[KnownMembers.MultiDataTrigger_Setters] = InitMember(KnownTypes.MultiDataTrigger, "Setters", InitType(assemblies[4], "System.Windows", "SetterBaseCollection")); - members[KnownMembers.MultiTrigger_Setters] = InitMember(KnownTypes.MultiTrigger, "Setters", InitType(assemblies[4], "System.Windows", "SetterBaseCollection")); - members[KnownMembers.ObjectAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.ObjectAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "ObjectKeyFrameCollection")); - members[KnownMembers.PageContent_Child] = InitMember(KnownTypes.PageContent, "Child", InitType(assemblies[4], "System.Windows.Documents", "FixedPage")); - members[KnownMembers.PageFunctionBase_Content] = InitMember(KnownTypes.PageFunctionBase, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.Panel_Children] = InitMember(KnownTypes.Panel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.Paragraph_Inlines] = InitMember(KnownTypes.Paragraph, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); - members[KnownMembers.ParallelTimeline_Children] = InitMember(KnownTypes.ParallelTimeline, "Children", InitType(assemblies[3], "System.Windows.Media.Animation", "TimelineCollection")); - members[KnownMembers.Point3DAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.Point3DAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "Point3DKeyFrameCollection")); - members[KnownMembers.PointAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.PointAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "PointKeyFrameCollection")); - members[KnownMembers.PriorityBinding_Bindings] = InitMember(KnownTypes.PriorityBinding, "Bindings", InitType(assemblies[0], "System.Collections.ObjectModel", "Collection`1")); - members[KnownMembers.QuaternionAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.QuaternionAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "QuaternionKeyFrameCollection")); - members[KnownMembers.RadialGradientBrush_GradientStops] = InitMember(KnownTypes.RadialGradientBrush, "GradientStops", InitType(assemblies[3], "System.Windows.Media", "GradientStopCollection")); - members[KnownMembers.RadioButton_Content] = InitMember(KnownTypes.RadioButton, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.RectAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.RectAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "RectKeyFrameCollection")); - members[KnownMembers.RepeatButton_Content] = InitMember(KnownTypes.RepeatButton, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.RichTextBox_Document] = InitMember(KnownTypes.RichTextBox, "Document", InitType(assemblies[4], "System.Windows.Documents", "FlowDocument")); - members[KnownMembers.Rotation3DAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.Rotation3DAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "Rotation3DKeyFrameCollection")); - members[KnownMembers.Run_Text] = InitMember(KnownTypes.Run, "Text", InitType(assemblies[0], "System", "String")); - members[KnownMembers.ScrollViewer_Content] = InitMember(KnownTypes.ScrollViewer, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.Section_Blocks] = InitMember(KnownTypes.Section, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); - members[KnownMembers.Selector_Items] = InitMember(KnownTypes.Selector, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.SingleAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.SingleAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "SingleKeyFrameCollection")); - members[KnownMembers.SizeAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.SizeAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "SizeKeyFrameCollection")); - members[KnownMembers.Span_Inlines] = InitMember(KnownTypes.Span, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); - members[KnownMembers.StackPanel_Children] = InitMember(KnownTypes.StackPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.StatusBar_Items] = InitMember(KnownTypes.StatusBar, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.StatusBarItem_Content] = InitMember(KnownTypes.StatusBarItem, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.Storyboard_Children] = InitMember(KnownTypes.Storyboard, "Children", InitType(assemblies[3], "System.Windows.Media.Animation", "TimelineCollection")); - members[KnownMembers.StringAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.StringAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "StringKeyFrameCollection")); - members[KnownMembers.Style_Setters] = InitMember(KnownTypes.Style, "Setters", InitType(assemblies[4], "System.Windows", "SetterBaseCollection")); - members[KnownMembers.TabControl_Items] = InitMember(KnownTypes.TabControl, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.TabItem_Content] = InitMember(KnownTypes.TabItem, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.TabPanel_Children] = InitMember(KnownTypes.TabPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.Table_RowGroups] = InitMember(KnownTypes.Table, "RowGroups", InitType(assemblies[4], "System.Windows.Documents", "TableRowGroupCollection")); - members[KnownMembers.TableCell_Blocks] = InitMember(KnownTypes.TableCell, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); - members[KnownMembers.TableRow_Cells] = InitMember(KnownTypes.TableRow, "Cells", InitType(assemblies[4], "System.Windows.Documents", "TableCellCollection")); - members[KnownMembers.TableRowGroup_Rows] = InitMember(KnownTypes.TableRowGroup, "Rows", InitType(assemblies[4], "System.Windows.Documents", "TableRowCollection")); - members[KnownMembers.TextBlock_Inlines] = InitMember(KnownTypes.TextBlock, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); - members[KnownMembers.ThicknessAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.ThicknessAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[4], "System.Windows.Media.Animation", "ThicknessKeyFrameCollection")); - members[KnownMembers.ToggleButton_Content] = InitMember(KnownTypes.ToggleButton, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.ToolBar_Items] = InitMember(KnownTypes.ToolBar, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.ToolBarOverflowPanel_Children] = InitMember(KnownTypes.ToolBarOverflowPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.ToolBarPanel_Children] = InitMember(KnownTypes.ToolBarPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.ToolBarTray_ToolBars] = InitMember(KnownTypes.ToolBarTray, "ToolBars", InitType(assemblies[0], "System.Collections.ObjectModel", "Collection`1")); - members[KnownMembers.ToolTip_Content] = InitMember(KnownTypes.ToolTip, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.TreeView_Items] = InitMember(KnownTypes.TreeView, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.TreeViewItem_Items] = InitMember(KnownTypes.TreeViewItem, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.Trigger_Setters] = InitMember(KnownTypes.Trigger, "Setters", InitType(assemblies[4], "System.Windows", "SetterBaseCollection")); - members[KnownMembers.Underline_Inlines] = InitMember(KnownTypes.Underline, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); - members[KnownMembers.UniformGrid_Children] = InitMember(KnownTypes.UniformGrid, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.UserControl_Content] = InitMember(KnownTypes.UserControl, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.Vector3DAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.Vector3DAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "Vector3DKeyFrameCollection")); - members[KnownMembers.VectorAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.VectorAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "VectorKeyFrameCollection")); - members[KnownMembers.Viewbox_Child] = InitMember(KnownTypes.Viewbox, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.Viewport3DVisual_Children] = InitMember(KnownTypes.Viewport3DVisual, "Children", InitType(assemblies[3], "System.Windows.Media.Media3D", "Visual3DCollection")); - members[KnownMembers.VirtualizingPanel_Children] = InitMember(KnownTypes.VirtualizingPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.VirtualizingStackPanel_Children] = InitMember(KnownTypes.VirtualizingStackPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.Window_Content] = InitMember(KnownTypes.Window, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.WrapPanel_Children] = InitMember(KnownTypes.WrapPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.XmlDataProvider_XmlSerializer] = InitMember(KnownTypes.XmlDataProvider, "XmlSerializer", InitType(assemblies[5], "System.Xml.Serialization", "IXmlSerializable")); - } - - void InitStrings() { - strings[1] = "Name"; - strings[2] = "Uid"; - } - - void InitResources() { - - resources[1] = ("SystemColors", "ActiveBorderBrushKey", "ActiveBorderBrush"); - resources[2] = ("SystemColors", "ActiveCaptionBrushKey", "ActiveCaptionBrush"); - resources[3] = ("SystemColors", "ActiveCaptionTextBrushKey", "ActiveCaptionTextBrush"); - resources[4] = ("SystemColors", "AppWorkspaceBrushKey", "AppWorkspaceBrush"); - resources[5] = ("SystemColors", "ControlBrushKey", "ControlBrush"); - resources[6] = ("SystemColors", "ControlDarkBrushKey", "ControlDarkBrush"); - resources[7] = ("SystemColors", "ControlDarkDarkBrushKey", "ControlDarkDarkBrush"); - resources[8] = ("SystemColors", "ControlLightBrushKey", "ControlLightBrush"); - resources[9] = ("SystemColors", "ControlLightLightBrushKey", "ControlLightLightBrush"); - resources[10] = ("SystemColors", "ControlTextBrushKey", "ControlTextBrush"); - resources[11] = ("SystemColors", "DesktopBrushKey", "DesktopBrush"); - resources[12] = ("SystemColors", "GradientActiveCaptionBrushKey", "GradientActiveCaptionBrush"); - resources[13] = ("SystemColors", "GradientInactiveCaptionBrushKey", "GradientInactiveCaptionBrush"); - resources[14] = ("SystemColors", "GrayTextBrushKey", "GrayTextBrush"); - resources[15] = ("SystemColors", "HighlightBrushKey", "HighlightBrush"); - resources[16] = ("SystemColors", "HighlightTextBrushKey", "HighlightTextBrush"); - resources[17] = ("SystemColors", "HotTrackBrushKey", "HotTrackBrush"); - resources[18] = ("SystemColors", "InactiveBorderBrushKey", "InactiveBorderBrush"); - resources[19] = ("SystemColors", "InactiveCaptionBrushKey", "InactiveCaptionBrush"); - resources[20] = ("SystemColors", "InactiveCaptionTextBrushKey", "InactiveCaptionTextBrush"); - resources[21] = ("SystemColors", "InfoBrushKey", "InfoBrush"); - resources[22] = ("SystemColors", "InfoTextBrushKey", "InfoTextBrush"); - resources[23] = ("SystemColors", "MenuBrushKey", "MenuBrush"); - resources[24] = ("SystemColors", "MenuBarBrushKey", "MenuBarBrush"); - resources[25] = ("SystemColors", "MenuHighlightBrushKey", "MenuHighlightBrush"); - resources[26] = ("SystemColors", "MenuTextBrushKey", "MenuTextBrush"); - resources[27] = ("SystemColors", "ScrollBarBrushKey", "ScrollBarBrush"); - resources[28] = ("SystemColors", "WindowBrushKey", "WindowBrush"); - resources[29] = ("SystemColors", "WindowFrameBrushKey", "WindowFrameBrush"); - resources[30] = ("SystemColors", "WindowTextBrushKey", "WindowTextBrush"); - resources[31] = ("SystemColors", "ActiveBorderColorKey", "ActiveBorderColor"); - resources[32] = ("SystemColors", "ActiveCaptionColorKey", "ActiveCaptionColor"); - resources[33] = ("SystemColors", "ActiveCaptionTextColorKey", "ActiveCaptionTextColor"); - resources[34] = ("SystemColors", "AppWorkspaceColorKey", "AppWorkspaceColor"); - resources[35] = ("SystemColors", "ControlColorKey", "ControlColor"); - resources[36] = ("SystemColors", "ControlDarkColorKey", "ControlDarkColor"); - resources[37] = ("SystemColors", "ControlDarkDarkColorKey", "ControlDarkDarkColor"); - resources[38] = ("SystemColors", "ControlLightColorKey", "ControlLightColor"); - resources[39] = ("SystemColors", "ControlLightLightColorKey", "ControlLightLightColor"); - resources[40] = ("SystemColors", "ControlTextColorKey", "ControlTextColor"); - resources[41] = ("SystemColors", "DesktopColorKey", "DesktopColor"); - resources[42] = ("SystemColors", "GradientActiveCaptionColorKey", "GradientActiveCaptionColor"); - resources[43] = ("SystemColors", "GradientInactiveCaptionColorKey", "GradientInactiveCaptionColor"); - resources[44] = ("SystemColors", "GrayTextColorKey", "GrayTextColor"); - resources[45] = ("SystemColors", "HighlightColorKey", "HighlightColor"); - resources[46] = ("SystemColors", "HighlightTextColorKey", "HighlightTextColor"); - resources[47] = ("SystemColors", "HotTrackColorKey", "HotTrackColor"); - resources[48] = ("SystemColors", "InactiveBorderColorKey", "InactiveBorderColor"); - resources[49] = ("SystemColors", "InactiveCaptionColorKey", "InactiveCaptionColor"); - resources[50] = ("SystemColors", "InactiveCaptionTextColorKey", "InactiveCaptionTextColor"); - resources[51] = ("SystemColors", "InfoColorKey", "InfoColor"); - resources[52] = ("SystemColors", "InfoTextColorKey", "InfoTextColor"); - resources[53] = ("SystemColors", "MenuColorKey", "MenuColor"); - resources[54] = ("SystemColors", "MenuBarColorKey", "MenuBarColor"); - resources[55] = ("SystemColors", "MenuHighlightColorKey", "MenuHighlightColor"); - resources[56] = ("SystemColors", "MenuTextColorKey", "MenuTextColor"); - resources[57] = ("SystemColors", "ScrollBarColorKey", "ScrollBarColor"); - resources[58] = ("SystemColors", "WindowColorKey", "WindowColor"); - resources[59] = ("SystemColors", "WindowFrameColorKey", "WindowFrameColor"); - resources[60] = ("SystemColors", "WindowTextColorKey", "WindowTextColor"); - - - resources[63] = ("SystemFonts", "CaptionFontSizeKey", "CaptionFontSize"); - resources[64] = ("SystemFonts", "CaptionFontFamilyKey", "CaptionFontFamily"); - resources[65] = ("SystemFonts", "CaptionFontStyleKey", "CaptionFontStyle"); - resources[66] = ("SystemFonts", "CaptionFontWeightKey", "CaptionFontWeight"); - resources[67] = ("SystemFonts", "CaptionFontTextDecorationsKey", "CaptionFontTextDecorations"); - resources[68] = ("SystemFonts", "SmallCaptionFontSizeKey", "SmallCaptionFontSize"); - resources[69] = ("SystemFonts", "SmallCaptionFontFamilyKey", "SmallCaptionFontFamily"); - resources[70] = ("SystemFonts", "SmallCaptionFontStyleKey", "SmallCaptionFontStyle"); - resources[71] = ("SystemFonts", "SmallCaptionFontWeightKey", "SmallCaptionFontWeight"); - resources[72] = ("SystemFonts", "SmallCaptionFontTextDecorationsKey", "SmallCaptionFontTextDecorations"); - resources[73] = ("SystemFonts", "MenuFontSizeKey", "MenuFontSize"); - resources[74] = ("SystemFonts", "MenuFontFamilyKey", "MenuFontFamily"); - resources[75] = ("SystemFonts", "MenuFontStyleKey", "MenuFontStyle"); - resources[76] = ("SystemFonts", "MenuFontWeightKey", "MenuFontWeight"); - resources[77] = ("SystemFonts", "MenuFontTextDecorationsKey", "MenuFontTextDecorations"); - resources[78] = ("SystemFonts", "StatusFontSizeKey", "StatusFontSize"); - resources[79] = ("SystemFonts", "StatusFontFamilyKey", "StatusFontFamily"); - resources[80] = ("SystemFonts", "StatusFontStyleKey", "StatusFontStyle"); - resources[81] = ("SystemFonts", "StatusFontWeightKey", "StatusFontWeight"); - resources[82] = ("SystemFonts", "StatusFontTextDecorationsKey", "StatusFontTextDecorations"); - resources[83] = ("SystemFonts", "MessageFontSizeKey", "MessageFontSize"); - resources[84] = ("SystemFonts", "MessageFontFamilyKey", "MessageFontFamily"); - resources[85] = ("SystemFonts", "MessageFontStyleKey", "MessageFontStyle"); - resources[86] = ("SystemFonts", "MessageFontWeightKey", "MessageFontWeight"); - resources[87] = ("SystemFonts", "MessageFontTextDecorationsKey", "MessageFontTextDecorations"); - resources[88] = ("SystemFonts", "IconFontSizeKey", "IconFontSize"); - resources[89] = ("SystemFonts", "IconFontFamilyKey", "IconFontFamily"); - resources[90] = ("SystemFonts", "IconFontStyleKey", "IconFontStyle"); - resources[91] = ("SystemFonts", "IconFontWeightKey", "IconFontWeight"); - resources[92] = ("SystemFonts", "IconFontTextDecorationsKey", "IconFontTextDecorations"); - - - resources[95] = ("SystemParameters", "ThinHorizontalBorderHeightKey", "ThinHorizontalBorderHeight"); - resources[96] = ("SystemParameters", "ThinVerticalBorderWidthKey", "ThinVerticalBorderWidth"); - resources[97] = ("SystemParameters", "CursorWidthKey", "CursorWidth"); - resources[98] = ("SystemParameters", "CursorHeightKey", "CursorHeight"); - resources[99] = ("SystemParameters", "ThickHorizontalBorderHeightKey", "ThickHorizontalBorderHeight"); - resources[100] = ("SystemParameters", "ThickVerticalBorderWidthKey", "ThickVerticalBorderWidth"); - resources[101] = ("SystemParameters", "FixedFrameHorizontalBorderHeightKey", "FixedFrameHorizontalBorderHeight"); - resources[102] = ("SystemParameters", "FixedFrameVerticalBorderWidthKey", "FixedFrameVerticalBorderWidth"); - resources[103] = ("SystemParameters", "FocusHorizontalBorderHeightKey", "FocusHorizontalBorderHeight"); - resources[104] = ("SystemParameters", "FocusVerticalBorderWidthKey", "FocusVerticalBorderWidth"); - resources[105] = ("SystemParameters", "FullPrimaryScreenWidthKey", "FullPrimaryScreenWidth"); - resources[106] = ("SystemParameters", "FullPrimaryScreenHeightKey", "FullPrimaryScreenHeight"); - resources[107] = ("SystemParameters", "HorizontalScrollBarButtonWidthKey", "HorizontalScrollBarButtonWidth"); - resources[108] = ("SystemParameters", "HorizontalScrollBarHeightKey", "HorizontalScrollBarHeight"); - resources[109] = ("SystemParameters", "HorizontalScrollBarThumbWidthKey", "HorizontalScrollBarThumbWidth"); - resources[110] = ("SystemParameters", "IconWidthKey", "IconWidth"); - resources[111] = ("SystemParameters", "IconHeightKey", "IconHeight"); - resources[112] = ("SystemParameters", "IconGridWidthKey", "IconGridWidth"); - resources[113] = ("SystemParameters", "IconGridHeightKey", "IconGridHeight"); - resources[114] = ("SystemParameters", "MaximizedPrimaryScreenWidthKey", "MaximizedPrimaryScreenWidth"); - resources[115] = ("SystemParameters", "MaximizedPrimaryScreenHeightKey", "MaximizedPrimaryScreenHeight"); - resources[116] = ("SystemParameters", "MaximumWindowTrackWidthKey", "MaximumWindowTrackWidth"); - resources[117] = ("SystemParameters", "MaximumWindowTrackHeightKey", "MaximumWindowTrackHeight"); - resources[118] = ("SystemParameters", "MenuCheckmarkWidthKey", "MenuCheckmarkWidth"); - resources[119] = ("SystemParameters", "MenuCheckmarkHeightKey", "MenuCheckmarkHeight"); - resources[120] = ("SystemParameters", "MenuButtonWidthKey", "MenuButtonWidth"); - resources[121] = ("SystemParameters", "MenuButtonHeightKey", "MenuButtonHeight"); - resources[122] = ("SystemParameters", "MinimumWindowWidthKey", "MinimumWindowWidth"); - resources[123] = ("SystemParameters", "MinimumWindowHeightKey", "MinimumWindowHeight"); - resources[124] = ("SystemParameters", "MinimizedWindowWidthKey", "MinimizedWindowWidth"); - resources[125] = ("SystemParameters", "MinimizedWindowHeightKey", "MinimizedWindowHeight"); - resources[126] = ("SystemParameters", "MinimizedGridWidthKey", "MinimizedGridWidth"); - resources[127] = ("SystemParameters", "MinimizedGridHeightKey", "MinimizedGridHeight"); - resources[128] = ("SystemParameters", "MinimumWindowTrackWidthKey", "MinimumWindowTrackWidth"); - resources[129] = ("SystemParameters", "MinimumWindowTrackHeightKey", "MinimumWindowTrackHeight"); - resources[130] = ("SystemParameters", "PrimaryScreenWidthKey", "PrimaryScreenWidth"); - resources[131] = ("SystemParameters", "PrimaryScreenHeightKey", "PrimaryScreenHeight"); - resources[132] = ("SystemParameters", "WindowCaptionButtonWidthKey", "WindowCaptionButtonWidth"); - resources[133] = ("SystemParameters", "WindowCaptionButtonHeightKey", "WindowCaptionButtonHeight"); - resources[134] = ("SystemParameters", "ResizeFrameHorizontalBorderHeightKey", "ResizeFrameHorizontalBorderHeight"); - resources[135] = ("SystemParameters", "ResizeFrameVerticalBorderWidthKey", "ResizeFrameVerticalBorderWidth"); - resources[136] = ("SystemParameters", "SmallIconWidthKey", "SmallIconWidth"); - resources[137] = ("SystemParameters", "SmallIconHeightKey", "SmallIconHeight"); - resources[138] = ("SystemParameters", "SmallWindowCaptionButtonWidthKey", "SmallWindowCaptionButtonWidth"); - resources[139] = ("SystemParameters", "SmallWindowCaptionButtonHeightKey", "SmallWindowCaptionButtonHeight"); - resources[140] = ("SystemParameters", "VirtualScreenWidthKey", "VirtualScreenWidth"); - resources[141] = ("SystemParameters", "VirtualScreenHeightKey", "VirtualScreenHeight"); - resources[142] = ("SystemParameters", "VerticalScrollBarWidthKey", "VerticalScrollBarWidth"); - resources[143] = ("SystemParameters", "VerticalScrollBarButtonHeightKey", "VerticalScrollBarButtonHeight"); - resources[144] = ("SystemParameters", "WindowCaptionHeightKey", "WindowCaptionHeight"); - resources[145] = ("SystemParameters", "KanjiWindowHeightKey", "KanjiWindowHeight"); - resources[146] = ("SystemParameters", "MenuBarHeightKey", "MenuBarHeight"); - resources[147] = ("SystemParameters", "SmallCaptionHeightKey", "SmallCaptionHeight"); - resources[148] = ("SystemParameters", "VerticalScrollBarThumbHeightKey", "VerticalScrollBarThumbHeight"); - resources[149] = ("SystemParameters", "IsImmEnabledKey", "IsImmEnabled"); - resources[150] = ("SystemParameters", "IsMediaCenterKey", "IsMediaCenter"); - resources[151] = ("SystemParameters", "IsMenuDropRightAlignedKey", "IsMenuDropRightAligned"); - resources[152] = ("SystemParameters", "IsMiddleEastEnabledKey", "IsMiddleEastEnabled"); - resources[153] = ("SystemParameters", "IsMousePresentKey", "IsMousePresent"); - resources[154] = ("SystemParameters", "IsMouseWheelPresentKey", "IsMouseWheelPresent"); - resources[155] = ("SystemParameters", "IsPenWindowsKey", "IsPenWindows"); - resources[156] = ("SystemParameters", "IsRemotelyControlledKey", "IsRemotelyControlled"); - resources[157] = ("SystemParameters", "IsRemoteSessionKey", "IsRemoteSession"); - resources[158] = ("SystemParameters", "ShowSoundsKey", "ShowSounds"); - resources[159] = ("SystemParameters", "IsSlowMachineKey", "IsSlowMachine"); - resources[160] = ("SystemParameters", "SwapButtonsKey", "SwapButtons"); - resources[161] = ("SystemParameters", "IsTabletPCKey", "IsTabletPC"); - resources[162] = ("SystemParameters", "VirtualScreenLeftKey", "VirtualScreenLeft"); - resources[163] = ("SystemParameters", "VirtualScreenTopKey", "VirtualScreenTop"); - resources[164] = ("SystemParameters", "FocusBorderWidthKey", "FocusBorderWidth"); - resources[165] = ("SystemParameters", "FocusBorderHeightKey", "FocusBorderHeight"); - resources[166] = ("SystemParameters", "HighContrastKey", "HighContrast"); - resources[167] = ("SystemParameters", "DropShadowKey", "DropShadow"); - resources[168] = ("SystemParameters", "FlatMenuKey", "FlatMenu"); - resources[169] = ("SystemParameters", "WorkAreaKey", "WorkArea"); - resources[170] = ("SystemParameters", "IconHorizontalSpacingKey", "IconHorizontalSpacing"); - resources[171] = ("SystemParameters", "IconVerticalSpacingKey", "IconVerticalSpacing"); - resources[172] = ("SystemParameters", "IconTitleWrapKey", "IconTitleWrap"); - resources[173] = ("SystemParameters", "KeyboardCuesKey", "KeyboardCues"); - resources[174] = ("SystemParameters", "KeyboardDelayKey", "KeyboardDelay"); - resources[175] = ("SystemParameters", "KeyboardPreferenceKey", "KeyboardPreference"); - resources[176] = ("SystemParameters", "KeyboardSpeedKey", "KeyboardSpeed"); - resources[177] = ("SystemParameters", "SnapToDefaultButtonKey", "SnapToDefaultButton"); - resources[178] = ("SystemParameters", "WheelScrollLinesKey", "WheelScrollLines"); - resources[179] = ("SystemParameters", "MouseHoverTimeKey", "MouseHoverTime"); - resources[180] = ("SystemParameters", "MouseHoverHeightKey", "MouseHoverHeight"); - resources[181] = ("SystemParameters", "MouseHoverWidthKey", "MouseHoverWidth"); - resources[182] = ("SystemParameters", "MenuDropAlignmentKey", "MenuDropAlignment"); - resources[183] = ("SystemParameters", "MenuFadeKey", "MenuFade"); - resources[184] = ("SystemParameters", "MenuShowDelayKey", "MenuShowDelay"); - resources[185] = ("SystemParameters", "ComboBoxAnimationKey", "ComboBoxAnimation"); - resources[186] = ("SystemParameters", "ClientAreaAnimationKey", "ClientAreaAnimation"); - resources[187] = ("SystemParameters", "CursorShadowKey", "CursorShadow"); - resources[188] = ("SystemParameters", "GradientCaptionsKey", "GradientCaptions"); - resources[189] = ("SystemParameters", "HotTrackingKey", "HotTracking"); - resources[190] = ("SystemParameters", "ListBoxSmoothScrollingKey", "ListBoxSmoothScrolling"); - resources[191] = ("SystemParameters", "MenuAnimationKey", "MenuAnimation"); - resources[192] = ("SystemParameters", "SelectionFadeKey", "SelectionFade"); - resources[193] = ("SystemParameters", "StylusHotTrackingKey", "StylusHotTracking"); - resources[194] = ("SystemParameters", "ToolTipAnimationKey", "ToolTipAnimation"); - resources[195] = ("SystemParameters", "ToolTipFadeKey", "ToolTipFade"); - resources[196] = ("SystemParameters", "UIEffectsKey", "UIEffects"); - resources[197] = ("SystemParameters", "MinimizeAnimationKey", "MinimizeAnimation"); - resources[198] = ("SystemParameters", "BorderKey", "Border"); - resources[199] = ("SystemParameters", "CaretWidthKey", "CaretWidth"); - resources[200] = ("SystemParameters", "ForegroundFlashCountKey", "ForegroundFlashCount"); - resources[201] = ("SystemParameters", "DragFullWindowsKey", "DragFullWindows"); - resources[202] = ("SystemParameters", "BorderWidthKey", "BorderWidth"); - resources[203] = ("SystemParameters", "ScrollWidthKey", "ScrollWidth"); - resources[204] = ("SystemParameters", "ScrollHeightKey", "ScrollHeight"); - resources[205] = ("SystemParameters", "CaptionWidthKey", "CaptionWidth"); - resources[206] = ("SystemParameters", "CaptionHeightKey", "CaptionHeight"); - resources[207] = ("SystemParameters", "SmallCaptionWidthKey", "SmallCaptionWidth"); - resources[208] = ("SystemParameters", "MenuWidthKey", "MenuWidth"); - resources[209] = ("SystemParameters", "MenuHeightKey", "MenuHeight"); - resources[210] = ("SystemParameters", "ComboBoxPopupAnimationKey", "ComboBoxPopupAnimation"); - resources[211] = ("SystemParameters", "MenuPopupAnimationKey", "MenuPopupAnimation"); - resources[212] = ("SystemParameters", "ToolTipPopupAnimationKey", "ToolTipPopupAnimation"); - resources[213] = ("SystemParameters", "PowerLineStatusKey", "PowerLineStatus"); - - resources[215] = ("SystemParameters", "FocusVisualStyleKey", "FocusVisualStyle"); - resources[216] = ("SystemParameters", "NavigationChromeDownLevelStyleKey", "NavigationChromeDownLevelStyle"); - resources[217] = ("SystemParameters", "NavigationChromeStyleKey", "NavigationChromeStyle"); - - resources[219] = ("MenuItem", "SeparatorStyleKey", "MenuItemSeparatorStyle"); - resources[220] = ("GridView", "GridViewScrollViewerStyleKey", "GridViewScrollViewerStyle"); - resources[221] = ("GridView", "GridViewStyleKey", "GridViewStyle"); - resources[222] = ("GridView", "GridViewItemContainerStyleKey", "GridViewItemContainerStyle"); - resources[223] = ("StatusBar", "SeparatorStyleKey", "StatusBarSeparatorStyle"); - resources[224] = ("ToolBar", "ButtonStyleKey", "ToolBarButtonStyle"); - resources[225] = ("ToolBar", "ToggleButtonStyleKey", "ToolBarToggleButtonStyle"); - resources[226] = ("ToolBar", "SeparatorStyleKey", "ToolBarSeparatorStyle"); - resources[227] = ("ToolBar", "CheckBoxStyleKey", "ToolBarCheckBoxStyle"); - resources[228] = ("ToolBar", "RadioButtonStyleKey", "ToolBarRadioButtonStyle"); - resources[229] = ("ToolBar", "ComboBoxStyleKey", "ToolBarComboBoxStyle"); - resources[230] = ("ToolBar", "TextBoxStyleKey", "ToolBarTextBoxStyle"); - resources[231] = ("ToolBar", "MenuStyleKey", "ToolBarMenuStyle"); - - - resources[234] = ("SystemColors", "InactiveSelectionHighlightBrushKey", "InactiveSelectionHighlightBrush"); - resources[235] = ("SystemColors", "InactiveSelectionHighlightTextBrushKey", "InactiveSelectionHighlightTextBrush"); - - } - - diff --git a/ILSpy.BamlDecompiler/Handlers/Blocks/KeyElementStartHandler.cs b/ILSpy.BamlDecompiler/Handlers/Blocks/KeyElementStartHandler.cs index 12d0c22fe..f5844bd41 100644 --- a/ILSpy.BamlDecompiler/Handlers/Blocks/KeyElementStartHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Blocks/KeyElementStartHandler.cs @@ -38,7 +38,7 @@ namespace ILSpy.BamlDecompiler.Handlers { var key = (XamlResourceKey)node.Annotation; var bamlElem = new BamlElement(node); - bamlElem.Xaml = new XElement(ctx.GetXamlNsName("Key", parent.Xaml)); + bamlElem.Xaml = new XElement(ctx.GetKnownNamespace("Key", XamlContext.KnownNamespace_Xaml, parent.Xaml)); parent.Xaml.Element.Add(bamlElem.Xaml.Element); key.KeyElement = bamlElem; base.Translate(ctx, node, bamlElem); diff --git a/ILSpy.BamlDecompiler/Handlers/Records/ConstructorParameterTypeHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/ConstructorParameterTypeHandler.cs index 7bc103727..278586687 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/ConstructorParameterTypeHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/ConstructorParameterTypeHandler.cs @@ -31,7 +31,7 @@ namespace ILSpy.BamlDecompiler.Handlers { public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (ConstructorParameterTypeRecord)((BamlRecordNode)node).Record; - var elem = new XElement(ctx.GetXamlNsName("TypeExtension", parent.Xaml)); + var elem = new XElement(ctx.GetKnownNamespace("TypeExtension", XamlContext.KnownNamespace_Xaml, parent.Xaml)); elem.AddAnnotation(ctx.ResolveType(0xfd4d)); // Known type - TypeExtension var bamlElem = new BamlElement(node); diff --git a/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeHandler.cs index 221c29aa3..8b141d002 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeHandler.cs @@ -31,7 +31,7 @@ namespace ILSpy.BamlDecompiler.Handlers { var record = (DefAttributeRecord)((BamlRecordNode)node).Record; var attrName = ctx.ResolveString(record.NameId); - parent.Xaml.Element.Add(new XAttribute(ctx.GetXamlNsName(attrName), record.Value)); + parent.Xaml.Element.Add(new XAttribute(ctx.GetKnownNamespace(attrName, XamlContext.KnownNamespace_Xaml), record.Value)); return null; } diff --git a/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeKeyStringHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeKeyStringHandler.cs index b92a5588a..0068ba4ac 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeKeyStringHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeKeyStringHandler.cs @@ -38,7 +38,7 @@ namespace ILSpy.BamlDecompiler.Handlers { var key = (XamlResourceKey)node.Annotation; var bamlElem = new BamlElement(node); - bamlElem.Xaml = new XElement(ctx.GetXamlNsName("Key", parent.Xaml)); + bamlElem.Xaml = new XElement(ctx.GetKnownNamespace("Key", XamlContext.KnownNamespace_Xaml, parent.Xaml)); parent.Xaml.Element.Add(bamlElem.Xaml.Element); bamlElem.Xaml.Element.Value = ctx.ResolveString(record.ValueId); key.KeyElement = bamlElem; diff --git a/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeKeyTypeHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeKeyTypeHandler.cs index 0b45d8583..8b6080248 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeKeyTypeHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeKeyTypeHandler.cs @@ -40,10 +40,10 @@ namespace ILSpy.BamlDecompiler.Handlers { var key = (XamlResourceKey)node.Annotation; var bamlElem = new BamlElement(node); - bamlElem.Xaml = new XElement(ctx.GetXamlNsName("Key", parent.Xaml)); + bamlElem.Xaml = new XElement(ctx.GetKnownNamespace("Key", XamlContext.KnownNamespace_Xaml, parent.Xaml)); parent.Xaml.Element.Add(bamlElem.Xaml.Element); - var typeElem = new XElement(ctx.GetXamlNsName("TypeExtension", parent.Xaml)); + var typeElem = new XElement(ctx.GetKnownNamespace("TypeExtension", XamlContext.KnownNamespace_Xaml, parent.Xaml)); typeElem.AddAnnotation(ctx.ResolveType(0xfd4d)); // Known type - TypeExtension typeElem.Add(new XElement(ctx.GetPseudoName("Ctor"), typeName)); bamlElem.Xaml.Element.Add(typeElem); diff --git a/ILSpy.BamlDecompiler/Handlers/Records/LiteralContentHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/LiteralContentHandler.cs index d5b0e73aa..ab36554b4 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/LiteralContentHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/LiteralContentHandler.cs @@ -30,7 +30,7 @@ namespace ILSpy.BamlDecompiler.Handlers { public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (LiteralContentRecord)((BamlRecordNode)node).Record; - var elem = new XElement(ctx.GetXamlNsName("XData", parent.Xaml)); + var elem = new XElement(ctx.GetKnownNamespace("XData", XamlContext.KnownNamespace_Xaml, parent.Xaml)); var content = XElement.Parse(record.Value); elem.Add(content); diff --git a/ILSpy.BamlDecompiler/Handlers/Records/OptimizedStaticResourceHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/OptimizedStaticResourceHandler.cs index 64a54695b..463acedc2 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/OptimizedStaticResourceHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/OptimizedStaticResourceHandler.cs @@ -43,7 +43,7 @@ namespace ILSpy.BamlDecompiler.Handlers { if (record.IsType) { var value = ctx.ResolveType(record.ValueId); - var typeElem = new XElement(ctx.GetXamlNsName("TypeExtension", parent.Xaml)); + var typeElem = new XElement(ctx.GetKnownNamespace("TypeExtension", XamlContext.KnownNamespace_Xaml, parent.Xaml)); typeElem.AddAnnotation(ctx.ResolveType(0xfd4d)); // Known type - TypeExtension typeElem.Add(new XElement(ctx.GetPseudoName("Ctor"), ctx.ToString(parent.Xaml, value))); key = typeElem; @@ -82,7 +82,7 @@ namespace ILSpy.BamlDecompiler.Handlers { attrName = ctx.ToString(parent.Xaml, xName); } - var staticElem = new XElement(ctx.GetXamlNsName("StaticExtension", parent.Xaml)); + var staticElem = new XElement(ctx.GetKnownNamespace("StaticExtension", XamlContext.KnownNamespace_Xaml, parent.Xaml)); staticElem.AddAnnotation(ctx.ResolveType(0xfda6)); // Known type - StaticExtension staticElem.Add(new XElement(ctx.GetPseudoName("Ctor"), attrName)); key = staticElem; diff --git a/ILSpy.BamlDecompiler/Handlers/Records/PresentationOptionsAttributeHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/PresentationOptionsAttributeHandler.cs index 01d648ef7..e0f3f728a 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/PresentationOptionsAttributeHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/PresentationOptionsAttributeHandler.cs @@ -31,7 +31,7 @@ namespace ILSpy.BamlDecompiler.Handlers { var record = (PresentationOptionsAttributeRecord)((BamlRecordNode)node).Record; var attrName = ctx.ResolveString(record.NameId); - var attr = new XAttribute(ctx.GetXamlNsName(attrName, parent.Xaml), record.Value); + var attr = new XAttribute(ctx.GetKnownNamespace(attrName, XamlContext.KnownNamespace_PresentationOptions, parent.Xaml), record.Value); parent.Xaml.Element.Add(attr); return null; diff --git a/ILSpy.BamlDecompiler/Handlers/Records/PropertyCustomHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/PropertyCustomHandler.cs index 2ae98ebfe..7c0d92d8c 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/PropertyCustomHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/PropertyCustomHandler.cs @@ -47,7 +47,7 @@ namespace ILSpy.BamlDecompiler.Handlers { case KnownTypes.DependencyPropertyConverter: { if (value.Length == 2) { var property = ctx.ResolveProperty(reader.ReadUInt16()); - return ctx.ToString(elem, property.ToXName(ctx, elem, false)); + return ctx.ToString(elem, property.ToXName(ctx, elem, NeedsFullName(property, ctx, elem))); } else { var type = ctx.ResolveType(reader.ReadUInt16()); @@ -142,6 +142,18 @@ namespace ILSpy.BamlDecompiler.Handlers { throw new NotSupportedException(ser.ToString()); } + private bool NeedsFullName(XamlProperty property, XamlContext ctx, XElement elem) + { + XElement p = elem.Parent; + while (p != null && p.Annotation()?.ResolvedType.FullName != "System.Windows.Style") { + p = p.Parent; + } + var type = p?.Annotation()?.Type; + if (type == null) + return true; + return property.IsAttachedTo(type); + } + public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (PropertyCustomRecord)((BamlRecordNode)node).Record; var serTypeId = ((short)record.SerializerTypeId & 0xfff); diff --git a/ILSpy.BamlDecompiler/Handlers/Records/PropertyHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/PropertyHandler.cs index c084b76e6..822fc0627 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/PropertyHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/PropertyHandler.cs @@ -45,7 +45,7 @@ namespace ILSpy.BamlDecompiler.Handlers { return new XAttribute(xamlProp.ToXName(ctx, parent.Xaml, true), value); if (xamlProp.PropertyName == "Name" && elemType.ResolvedType.GetDefinition()?.ParentModule.IsMainModule == true) - return new XAttribute(ctx.GetXamlNsName("Name"), value); + return new XAttribute(ctx.GetKnownNamespace("Name", XamlContext.KnownNamespace_Xaml), value); return new XAttribute(xamlProp.ToXName(ctx, parent.Xaml, false), value); } diff --git a/ILSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs index 45a94f91e..148ded0e2 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs @@ -23,6 +23,7 @@ using System.Xml.Linq; using ILSpy.BamlDecompiler.Baml; using ILSpy.BamlDecompiler.Xaml; +using ICSharpCode.Decompiler.TypeSystem; namespace ILSpy.BamlDecompiler.Handlers { internal class PropertyTypeReferenceHandler : IHandler { @@ -39,10 +40,14 @@ namespace ILSpy.BamlDecompiler.Handlers { var elemAttr = ctx.ResolveProperty(record.AttributeId); elem.Xaml = new XElement(elemAttr.ToXName(ctx, null)); + if (attr.ResolvedMember?.FullNameIs("System.Windows.Style", "TargetType") == true) { + parent.Xaml.Element.AddAnnotation(new TargetTypeAnnotation(type)); + } + elem.Xaml.Element.AddAnnotation(elemAttr); parent.Xaml.Element.Add(elem.Xaml.Element); - var typeElem = new XElement(ctx.GetXamlNsName("TypeExtension", parent.Xaml)); + var typeElem = new XElement(ctx.GetKnownNamespace("TypeExtension", XamlContext.KnownNamespace_Xaml, parent.Xaml)); typeElem.AddAnnotation(ctx.ResolveType(0xfd4d)); // Known type - TypeExtension typeElem.Add(new XElement(ctx.GetPseudoName("Ctor"), typeName)); elem.Xaml.Element.Add(typeElem); @@ -53,4 +58,14 @@ namespace ILSpy.BamlDecompiler.Handlers { return elem; } } + + internal class TargetTypeAnnotation + { + public XamlType Type { get; } + + public TargetTypeAnnotation(XamlType type) + { + this.Type = type; + } + } } \ No newline at end of file diff --git a/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj b/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj index 87269f706..92f6ef0ff 100644 --- a/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj +++ b/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj @@ -37,7 +37,7 @@ - + diff --git a/ILSpy.BamlDecompiler/Rewrite/AttributeRewritePass.cs b/ILSpy.BamlDecompiler/Rewrite/AttributeRewritePass.cs index b8e902119..a8c5ac22b 100644 --- a/ILSpy.BamlDecompiler/Rewrite/AttributeRewritePass.cs +++ b/ILSpy.BamlDecompiler/Rewrite/AttributeRewritePass.cs @@ -29,7 +29,7 @@ namespace ILSpy.BamlDecompiler.Rewrite { XName key; public void Run(XamlContext ctx, XDocument document) { - key = ctx.GetXamlNsName("Key"); + key = ctx.GetKnownNamespace("Key", XamlContext.KnownNamespace_Xaml); bool doWork; do { diff --git a/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs b/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs index fe2912ff9..d4875c0af 100644 --- a/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs +++ b/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs @@ -59,7 +59,7 @@ namespace ILSpy.BamlDecompiler.Rewrite { var result = new List<(LongSet, EventRegistration[])>(); - var xClass = document.Root.Elements().First().Attribute(ctx.GetXamlNsName("Class")); + var xClass = document.Root.Elements().First().Attribute(ctx.GetKnownNamespace("Class", XamlContext.KnownNamespace_Xaml)); if (xClass == null) return result; @@ -94,7 +94,7 @@ namespace ILSpy.BamlDecompiler.Rewrite // decompile method and optimize the switch var ilReader = new ILReader(ctx.TypeSystem.MainModule); - var function = ilReader.ReadIL((MethodDefinitionHandle)method.MetadataToken, body, genericContext, ctx.CancellationToken); + var function = ilReader.ReadIL((MethodDefinitionHandle)method.MetadataToken, body, genericContext, ILFunctionKind.TopLevelFunction, ctx.CancellationToken); var context = new ILTransformContext(function, ctx.TypeSystem, null) { CancellationToken = ctx.CancellationToken diff --git a/ILSpy.BamlDecompiler/Rewrite/MarkupExtensionRewritePass.cs b/ILSpy.BamlDecompiler/Rewrite/MarkupExtensionRewritePass.cs index e05eee62e..bd2bf8e31 100644 --- a/ILSpy.BamlDecompiler/Rewrite/MarkupExtensionRewritePass.cs +++ b/ILSpy.BamlDecompiler/Rewrite/MarkupExtensionRewritePass.cs @@ -31,7 +31,7 @@ namespace ILSpy.BamlDecompiler.Rewrite { XName ctor; public void Run(XamlContext ctx, XDocument document) { - key = ctx.GetXamlNsName("Key"); + key = ctx.GetKnownNamespace("Key", XamlContext.KnownNamespace_Xaml); ctor = ctx.GetPseudoName("Ctor"); bool doWork; @@ -77,14 +77,16 @@ namespace ILSpy.BamlDecompiler.Rewrite { var attrName = elem.Name; if (attrName != key) attrName = property.ToXName(ctx, parent, property.IsAttachedTo(type)); - var attr = new XAttribute(attrName, extValue); - var list = new List(parent.Attributes()); - if (attrName == key) - list.Insert(0, attr); - else - list.Add(attr); - parent.RemoveAttributes(); - parent.ReplaceAttributes(list); + if (!parent.Attributes(attrName).Any()) { + var attr = new XAttribute(attrName, extValue); + var list = new List(parent.Attributes()); + if (attrName == key) + list.Insert(0, attr); + else + list.Add(attr); + parent.RemoveAttributes(); + parent.ReplaceAttributes(list); + } elem.Remove(); return true; diff --git a/ILSpy.BamlDecompiler/Rewrite/XClassRewritePass.cs b/ILSpy.BamlDecompiler/Rewrite/XClassRewritePass.cs index 332454d17..2e2d25e59 100644 --- a/ILSpy.BamlDecompiler/Rewrite/XClassRewritePass.cs +++ b/ILSpy.BamlDecompiler/Rewrite/XClassRewritePass.cs @@ -48,11 +48,11 @@ namespace ILSpy.BamlDecompiler.Rewrite { elem.Name = xamlType.ToXName(ctx); - var attrName = ctx.GetXamlNsName("Class", elem); + var attrName = ctx.GetKnownNamespace("Class", XamlContext.KnownNamespace_Xaml, elem); var attrs = elem.Attributes().ToList(); if (typeDef.Accessibility != ICSharpCode.Decompiler.TypeSystem.Accessibility.Public) { - var classModifierName = ctx.GetXamlNsName("ClassModifier", elem); + var classModifierName = ctx.GetKnownNamespace("ClassModifier", XamlContext.KnownNamespace_Xaml, elem); attrs.Insert(0, new XAttribute(classModifierName, "internal")); } attrs.Insert(0, new XAttribute(attrName, type.ResolvedType.FullName)); diff --git a/ILSpy.BamlDecompiler/XamlContext.cs b/ILSpy.BamlDecompiler/XamlContext.cs index 6afe81eee..50063cf6e 100644 --- a/ILSpy.BamlDecompiler/XamlContext.cs +++ b/ILSpy.BamlDecompiler/XamlContext.cs @@ -164,6 +164,10 @@ namespace ILSpy.BamlDecompiler { return ns; } + public const string KnownNamespace_Xaml = "http://schemas.microsoft.com/winfx/2006/xaml"; + public const string KnownNamespace_Presentation = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"; + public const string KnownNamespace_PresentationOptions = "http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"; + public string TryGetXmlNamespace(IModule assembly, string typeNamespace) { if (assembly == null) return null; @@ -184,16 +188,16 @@ namespace ILSpy.BamlDecompiler { possibleXmlNs.Add(xmlNs); } - if (possibleXmlNs.Contains("http://schemas.microsoft.com/winfx/2006/xaml/presentation")) - return "http://schemas.microsoft.com/winfx/2006/xaml/presentation"; + if (possibleXmlNs.Contains(KnownNamespace_Presentation)) + return KnownNamespace_Presentation; return possibleXmlNs.FirstOrDefault(); } - public XName GetXamlNsName(string name, XElement elem = null) { - var xNs = GetXmlNamespace("http://schemas.microsoft.com/winfx/2006/xaml"); + public XName GetKnownNamespace(string name, string xmlNamespace, XElement context = null) { + var xNs = GetXmlNamespace(xmlNamespace); XName xName; - if (elem != null && xNs == elem.GetDefaultNamespace()) + if (context != null && xNs == context.GetDefaultNamespace()) xName = name; else xName = xNs + name; diff --git a/ILSpy/Analyzers/AnalyzerScope.cs b/ILSpy/Analyzers/AnalyzerScope.cs index 976491573..6a6e27344 100644 --- a/ILSpy/Analyzers/AnalyzerScope.cs +++ b/ILSpy/Analyzers/AnalyzerScope.cs @@ -130,6 +130,10 @@ namespace ICSharpCode.ILSpy.Analyzers { yield return self; + string reflectionTypeScopeName = typeScope.Name; + if (typeScope.TypeParameterCount > 0) + reflectionTypeScopeName += "`" + typeScope.TypeParameterCount; + foreach (var assembly in AssemblyList.GetAssemblies()) { ct.ThrowIfCancellationRequested(); bool found = false; @@ -145,7 +149,7 @@ namespace ICSharpCode.ILSpy.Analyzers } } } - if (found && ModuleReferencesScopeType(module.Metadata, typeScope.Name, typeScope.Namespace)) + if (found && ModuleReferencesScopeType(module.Metadata, reflectionTypeScopeName, typeScope.Namespace)) yield return module; } } diff --git a/ILSpy/Commands/SaveCodeContextMenuEntry.cs b/ILSpy/Commands/SaveCodeContextMenuEntry.cs new file mode 100644 index 000000000..ef395f265 --- /dev/null +++ b/ILSpy/Commands/SaveCodeContextMenuEntry.cs @@ -0,0 +1,123 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Windows; +using System.Windows.Input; +using ICSharpCode.ILSpy.Properties; +using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.TreeView; +using Microsoft.Win32; + +namespace ICSharpCode.ILSpy.TextView +{ + [ExportContextMenuEntry(Header = nameof(Resources._SaveCode), Category = nameof(Resources.Save), Icon = "Images/SaveFile.png")] + sealed class SaveCodeContextMenuEntry : IContextMenuEntry + { + public void Execute(TextViewContext context) + { + Execute(context.SelectedTreeNodes); + } + + public bool IsEnabled(TextViewContext context) => true; + + public bool IsVisible(TextViewContext context) + { + return CanExecute(context.SelectedTreeNodes); + } + + public static bool CanExecute(IReadOnlyList selectedNodes) + { + if (selectedNodes == null || selectedNodes.Any(n => !(n is ILSpyTreeNode))) + return false; + return selectedNodes.Count == 1 + || (selectedNodes.Count > 1 && (selectedNodes.All(n => n is AssemblyTreeNode) || selectedNodes.All(n => n is IMemberTreeNode))); + } + + public static void Execute(IReadOnlyList selectedNodes) + { + var currentLanguage = MainWindow.Instance.CurrentLanguage; + var textView = MainWindow.Instance.TextView; + if (selectedNodes.Count == 1 && selectedNodes[0] is ILSpyTreeNode singleSelection) { + // if there's only one treenode selected + // we will invoke the custom Save logic + if (singleSelection.Save(textView)) + return; + } else if (selectedNodes.Count > 1 && selectedNodes.All(n => n is AssemblyTreeNode)) { + var selectedPath = SelectSolutionFile(); + + if (!string.IsNullOrEmpty(selectedPath)) { + var assemblies = selectedNodes.OfType() + .Select(n => n.LoadedAssembly) + .Where(a => !a.HasLoadError).ToArray(); + SolutionWriter.CreateSolution(textView, selectedPath, currentLanguage, assemblies); + } + return; + } + + // Fallback: if nobody was able to handle the request, use default behavior. + // try to save all nodes to disk. + var options = new DecompilationOptions() { FullDecompilation = true }; + textView.SaveToDisk(currentLanguage, selectedNodes.OfType(), options); + } + + /// + /// Shows a File Selection dialog where the user can select the target file for the solution. + /// + /// The initial path to show in the dialog. If not specified, the 'Documents' directory + /// will be used. + /// + /// The full path of the selected target file, or null if the user canceled. + static string SelectSolutionFile() + { + SaveFileDialog dlg = new SaveFileDialog(); + dlg.FileName = "Solution.sln"; + dlg.Filter = "Visual Studio Solution file|*.sln|All files|*.*"; + + if (dlg.ShowDialog() != true) { + return null; + } + + string selectedPath = Path.GetDirectoryName(dlg.FileName); + bool directoryNotEmpty; + try { + directoryNotEmpty = Directory.EnumerateFileSystemEntries(selectedPath).Any(); + } catch (Exception e) when (e is IOException || e is UnauthorizedAccessException || e is System.Security.SecurityException) { + MessageBox.Show( + "The directory cannot be accessed. Please ensure it exists and you have sufficient rights to access it.", + "Solution directory not accessible", + MessageBoxButton.OK, MessageBoxImage.Error); + return null; + } + + if (directoryNotEmpty) { + var result = MessageBox.Show( + Resources.AssemblySaveCodeDirectoryNotEmpty, + Resources.AssemblySaveCodeDirectoryNotEmptyTitle, + MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No); + if (result == MessageBoxResult.No) + return null; // -> abort + } + + return dlg.FileName; + } + } +} diff --git a/ILSpy/GacInterop.cs b/ILSpy/GacInterop.cs index a260a9daf..7955b37c8 100644 --- a/ILSpy/GacInterop.cs +++ b/ILSpy/GacInterop.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Text; +using System.Windows; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy @@ -38,7 +39,11 @@ namespace ICSharpCode.ILSpy IAssemblyEnum assemblyEnum = null; IAssemblyName assemblyName = null; - Fusion.CreateAssemblyEnum(out assemblyEnum, null, null, 2, 0); + uint result = unchecked((uint)Fusion.CreateAssemblyEnum(out assemblyEnum, null, null, 2, 0)); + if (result == 0x80070005) { + MessageBox.Show($"Cannot access GAC, please restart with elevated privileges! (HRESULT 0x{result:X})", "ILSpy", MessageBoxButton.OK, MessageBoxImage.Error); + yield break; + } while (assemblyEnum.GetNextAssembly(out applicationContext, out assemblyName, 0) == 0) { if (assemblyName == null) continue; uint nChars = 0; diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 20d396e4a..092f5edec 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -49,7 +49,7 @@ - + @@ -141,6 +141,7 @@ + @@ -205,9 +206,12 @@ + + + @@ -265,9 +269,7 @@ - - Always - + diff --git a/ILSpy/Languages/CSharpBracketSearcher.cs b/ILSpy/Languages/CSharpBracketSearcher.cs new file mode 100644 index 000000000..bc12dd7f5 --- /dev/null +++ b/ILSpy/Languages/CSharpBracketSearcher.cs @@ -0,0 +1,359 @@ +// 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; +using System.Collections.Generic; +using System.Diagnostics; +using ICSharpCode.AvalonEdit.Document; +using ICSharpCode.ILSpy.TextView; + +namespace ICSharpCode.ILSpy +{ + /// + /// Searches matching brackets for C#. + /// + class CSharpBracketSearcher : IBracketSearcher + { + string openingBrackets = "([{"; + string closingBrackets = ")]}"; + + public BracketSearchResult SearchBracket(IDocument document, int offset) + { + if (offset > 0) { + char c = document.GetCharAt(offset - 1); + int index = openingBrackets.IndexOf(c); + int otherOffset = -1; + if (index > -1) + otherOffset = SearchBracketForward(document, offset, openingBrackets[index], closingBrackets[index]); + + index = closingBrackets.IndexOf(c); + if (index > -1) + otherOffset = SearchBracketBackward(document, offset - 2, openingBrackets[index], closingBrackets[index]); + + if (otherOffset > -1) { + var result = new BracketSearchResult(Math.Min(offset - 1, otherOffset), 1, + Math.Max(offset - 1, otherOffset), 1); + return result; + } + } + + return null; + } + + #region SearchBracket helper functions + static int ScanLineStart(IDocument document, int offset) + { + for (int i = offset - 1; i > 0; --i) { + if (document.GetCharAt(i) == '\n') + return i + 1; + } + return 0; + } + + /// + /// Gets the type of code at offset.
+ /// 0 = Code,
+ /// 1 = Comment,
+ /// 2 = String
+ /// Block comments and multiline strings are not supported. + ///
+ static int GetStartType(IDocument document, int linestart, int offset) + { + bool inString = false; + bool inChar = false; + bool verbatim = false; + int result = 0; + for (int i = linestart; i < offset; i++) { + switch (document.GetCharAt(i)) { + case '/': + if (!inString && !inChar && i + 1 < document.TextLength) { + if (document.GetCharAt(i + 1) == '/') { + result = 1; + } + } + break; + case '"': + if (!inChar) { + if (inString && verbatim) { + if (i + 1 < document.TextLength && document.GetCharAt(i + 1) == '"') { + ++i; // skip escaped quote + inString = false; // let the string go on + } else { + verbatim = false; + } + } else if (!inString && i > 0 && document.GetCharAt(i - 1) == '@') { + verbatim = true; + } + inString = !inString; + } + break; + case '\'': + if (!inString) inChar = !inChar; + break; + case '\\': + if ((inString && !verbatim) || inChar) + ++i; // skip next character + break; + } + } + + return (inString || inChar) ? 2 : result; + } + #endregion + + #region SearchBracketBackward + int SearchBracketBackward(IDocument document, int offset, char openBracket, char closingBracket) + { + if (offset + 1 >= document.TextLength) return -1; + // this method parses a c# document backwards to find the matching bracket + + // first try "quick find" - find the matching bracket if there is no string/comment in the way + int quickResult = QuickSearchBracketBackward(document, offset, openBracket, closingBracket); + if (quickResult >= 0) return quickResult; + + // we need to parse the line from the beginning, so get the line start position + int linestart = ScanLineStart(document, offset + 1); + + // we need to know where offset is - in a string/comment or in normal code? + // ignore cases where offset is in a block comment + int starttype = GetStartType(document, linestart, offset + 1); + if (starttype == 1) { + return -1; // start position is in a comment + } + + // I don't see any possibility to parse a C# document backwards... + // We have to do it forwards and push all bracket positions on a stack. + Stack bracketStack = new Stack(); + bool blockComment = false; + bool lineComment = false; + bool inChar = false; + bool inString = false; + bool verbatim = false; + + for (int i = 0; i <= offset; ++i) { + char ch = document.GetCharAt(i); + switch (ch) { + case '\r': + case '\n': + lineComment = false; + inChar = false; + if (!verbatim) inString = false; + break; + case '/': + if (blockComment) { + Debug.Assert(i > 0); + if (document.GetCharAt(i - 1) == '*') { + blockComment = false; + } + } + if (!inString && !inChar && i + 1 < document.TextLength) { + if (!blockComment && document.GetCharAt(i + 1) == '/') { + lineComment = true; + } + if (!lineComment && document.GetCharAt(i + 1) == '*') { + blockComment = true; + } + } + break; + case '"': + if (!(inChar || lineComment || blockComment)) { + if (inString && verbatim) { + if (i + 1 < document.TextLength && document.GetCharAt(i + 1) == '"') { + ++i; // skip escaped quote + inString = false; // let the string go + } else { + verbatim = false; + } + } else if (!inString && offset > 0 && document.GetCharAt(i - 1) == '@') { + verbatim = true; + } + inString = !inString; + } + break; + case '\'': + if (!(inString || lineComment || blockComment)) { + inChar = !inChar; + } + break; + case '\\': + if ((inString && !verbatim) || inChar) + ++i; // skip next character + break; + default: + if (ch == openBracket) { + if (!(inString || inChar || lineComment || blockComment)) { + bracketStack.Push(i); + } + } else if (ch == closingBracket) { + if (!(inString || inChar || lineComment || blockComment)) { + if (bracketStack.Count > 0) + bracketStack.Pop(); + } + } + break; + } + } + if (bracketStack.Count > 0) return (int)bracketStack.Pop(); + return -1; + } + #endregion + + #region SearchBracketForward + int SearchBracketForward(IDocument document, int offset, char openBracket, char closingBracket) + { + bool inString = false; + bool inChar = false; + bool verbatim = false; + + bool lineComment = false; + bool blockComment = false; + + if (offset < 0) return -1; + + // first try "quick find" - find the matching bracket if there is no string/comment in the way + int quickResult = QuickSearchBracketForward(document, offset, openBracket, closingBracket); + if (quickResult >= 0) return quickResult; + + // we need to parse the line from the beginning, so get the line start position + int linestart = ScanLineStart(document, offset); + + // we need to know where offset is - in a string/comment or in normal code? + // ignore cases where offset is in a block comment + int starttype = GetStartType(document, linestart, offset); + if (starttype != 0) return -1; // start position is in a comment/string + + int brackets = 1; + + while (offset < document.TextLength) { + char ch = document.GetCharAt(offset); + switch (ch) { + case '\r': + case '\n': + lineComment = false; + inChar = false; + if (!verbatim) inString = false; + break; + case '/': + if (blockComment) { + Debug.Assert(offset > 0); + if (document.GetCharAt(offset - 1) == '*') { + blockComment = false; + } + } + if (!inString && !inChar && offset + 1 < document.TextLength) { + if (!blockComment && document.GetCharAt(offset + 1) == '/') { + lineComment = true; + } + if (!lineComment && document.GetCharAt(offset + 1) == '*') { + blockComment = true; + } + } + break; + case '"': + if (!(inChar || lineComment || blockComment)) { + if (inString && verbatim) { + if (offset + 1 < document.TextLength && document.GetCharAt(offset + 1) == '"') { + ++offset; // skip escaped quote + inString = false; // let the string go + } else { + verbatim = false; + } + } else if (!inString && offset > 0 && document.GetCharAt(offset - 1) == '@') { + verbatim = true; + } + inString = !inString; + } + break; + case '\'': + if (!(inString || lineComment || blockComment)) { + inChar = !inChar; + } + break; + case '\\': + if ((inString && !verbatim) || inChar) + ++offset; // skip next character + break; + default: + if (ch == openBracket) { + if (!(inString || inChar || lineComment || blockComment)) { + ++brackets; + } + } else if (ch == closingBracket) { + if (!(inString || inChar || lineComment || blockComment)) { + --brackets; + if (brackets == 0) { + return offset; + } + } + } + break; + } + ++offset; + } + return -1; + } + #endregion + + int QuickSearchBracketBackward(IDocument document, int offset, char openBracket, char closingBracket) + { + int brackets = -1; + // first try "quick find" - find the matching bracket if there is no string/comment in the way + for (int i = offset; i >= 0; --i) { + char ch = document.GetCharAt(i); + if (ch == openBracket) { + ++brackets; + if (brackets == 0) return i; + } else if (ch == closingBracket) { + --brackets; + } else if (ch == '"') { + break; + } else if (ch == '\'') { + break; + } else if (ch == '/' && i > 0) { + if (document.GetCharAt(i - 1) == '/') break; + if (document.GetCharAt(i - 1) == '*') break; + } + } + return -1; + } + + int QuickSearchBracketForward(IDocument document, int offset, char openBracket, char closingBracket) + { + int brackets = 1; + // try "quick find" - find the matching bracket if there is no string/comment in the way + for (int i = offset; i < document.TextLength; ++i) { + char ch = document.GetCharAt(i); + if (ch == openBracket) { + ++brackets; + } else if (ch == closingBracket) { + --brackets; + if (brackets == 0) return i; + } else if (ch == '"') { + break; + } else if (ch == '\'') { + break; + } else if (ch == '/' && i > 0) { + if (document.GetCharAt(i - 1) == '/') break; + } else if (ch == '*' && i > 0) { + if (document.GetCharAt(i - 1) == '/') break; + } + } + return -1; + } + } +} diff --git a/ILSpy/Languages/CSharpHighlightingTokenWriter.cs b/ILSpy/Languages/CSharpHighlightingTokenWriter.cs index 827e774b3..ddd989831 100644 --- a/ILSpy/Languages/CSharpHighlightingTokenWriter.cs +++ b/ILSpy/Languages/CSharpHighlightingTokenWriter.cs @@ -1,4 +1,22 @@ -using System; +// 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; using System.Collections.Generic; using System.Linq; using ICSharpCode.AvalonEdit.Highlighting; @@ -254,7 +272,9 @@ namespace ICSharpCode.ILSpy HighlightingColor color = null; switch (type) { case "new": - color = typeKeywordsColor; + case "notnull": + // Not sure if reference type or value type + color = referenceTypeKeywordsColor; break; case "bool": case "byte": @@ -271,6 +291,7 @@ namespace ICSharpCode.ILSpy case "uint": case "ushort": case "ulong": + case "unmanaged": color = valueTypeKeywordsColor; break; case "class": diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index 2441e0eb2..087aaec61 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -34,8 +34,10 @@ using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Transforms; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Output; +using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; +using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy @@ -343,12 +345,12 @@ namespace ICSharpCode.ILSpy } } - public override void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) + public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) { var module = assembly.GetPEFileOrNull(); if (options.FullDecompilation && options.SaveAsProjectDirectory != null) { var decompiler = new ILSpyWholeProjectDecompiler(assembly, options); - decompiler.DecompileProject(module, options.SaveAsProjectDirectory, new TextOutputWriter(output), options.CancellationToken); + return decompiler.DecompileProject(module, options.SaveAsProjectDirectory, new TextOutputWriter(output), options.CancellationToken); } else { AddReferenceAssemblyWarningMessage(module, output); AddReferenceWarningMessage(module, output); @@ -389,7 +391,7 @@ namespace ICSharpCode.ILSpy } if (metadata.IsAssembly) { var asm = metadata.GetAssemblyDefinition(); - if (asm.HashAlgorithm != System.Reflection.AssemblyHashAlgorithm.None) + if (asm.HashAlgorithm != AssemblyHashAlgorithm.None) output.WriteLine("// Hash algorithm: " + asm.HashAlgorithm.ToString().ToUpper()); if (!asm.PublicKey.IsNil) { output.Write("// Public key: "); @@ -415,6 +417,7 @@ namespace ICSharpCode.ILSpy } WriteCode(output, options.DecompilerSettings, st, decompiler.TypeSystem); } + return null; } } @@ -516,7 +519,7 @@ namespace ICSharpCode.ILSpy return EntityToString(@event, includeDeclaringTypeName, includeNamespace, includeNamespaceOfDeclaringTypeName); } - string ToCSharpString(MetadataReader metadata, TypeDefinitionHandle handle, bool fullName) + string ToCSharpString(MetadataReader metadata, TypeDefinitionHandle handle, bool fullName, bool omitGenerics) { StringBuilder builder = new StringBuilder(); var currentTypeDefHandle = handle; @@ -528,7 +531,7 @@ namespace ICSharpCode.ILSpy typeDef = metadata.GetTypeDefinition(currentTypeDefHandle); var part = ReflectionHelper.SplitTypeParameterCountFromReflectionName(metadata.GetString(typeDef.Name), out int typeParamCount); var genericParams = typeDef.GetGenericParameters(); - if (genericParams.Count > 0) { + if (!omitGenerics && genericParams.Count > 0) { builder.Insert(0, '>'); int firstIndex = genericParams.Count - typeParamCount; for (int i = genericParams.Count - 1; i >= genericParams.Count - typeParamCount; i--) { @@ -549,17 +552,17 @@ namespace ICSharpCode.ILSpy return builder.ToString(); } - public override string GetEntityName(PEFile module, EntityHandle handle, bool fullName) + public override string GetEntityName(PEFile module, EntityHandle handle, bool fullName, bool omitGenerics) { MetadataReader metadata = module.Metadata; switch (handle.Kind) { case HandleKind.TypeDefinition: - return ToCSharpString(metadata, (TypeDefinitionHandle)handle, fullName); + return ToCSharpString(metadata, (TypeDefinitionHandle)handle, fullName, omitGenerics); case HandleKind.FieldDefinition: var fd = metadata.GetFieldDefinition((FieldDefinitionHandle)handle); var declaringType = fd.GetDeclaringType(); if (fullName) - return ToCSharpString(metadata, declaringType, fullName) + "." + metadata.GetString(fd.Name); + return ToCSharpString(metadata, declaringType, fullName, omitGenerics) + "." + metadata.GetString(fd.Name); return metadata.GetString(fd.Name); case HandleKind.MethodDefinition: var md = metadata.GetMethodDefinition((MethodDefinitionHandle)handle); @@ -583,7 +586,7 @@ namespace ICSharpCode.ILSpy break; default: var genericParams = md.GetGenericParameters(); - if (genericParams.Count > 0) { + if (!omitGenerics && genericParams.Count > 0) { methodName += "<"; int i = 0; foreach (var h in genericParams) { @@ -597,19 +600,19 @@ namespace ICSharpCode.ILSpy break; } if (fullName) - return ToCSharpString(metadata, declaringType, fullName) + "." + methodName; + return ToCSharpString(metadata, declaringType, fullName, omitGenerics) + "." + methodName; return methodName; case HandleKind.EventDefinition: var ed = metadata.GetEventDefinition((EventDefinitionHandle)handle); declaringType = metadata.GetMethodDefinition(ed.GetAccessors().GetAny()).GetDeclaringType(); if (fullName) - return ToCSharpString(metadata, declaringType, fullName) + "." + metadata.GetString(ed.Name); + return ToCSharpString(metadata, declaringType, fullName, omitGenerics) + "." + metadata.GetString(ed.Name); return metadata.GetString(ed.Name); case HandleKind.PropertyDefinition: var pd = metadata.GetPropertyDefinition((PropertyDefinitionHandle)handle); declaringType = metadata.GetMethodDefinition(pd.GetAccessors().GetAny()).GetDeclaringType(); if (fullName) - return ToCSharpString(metadata, declaringType, fullName) + "." + metadata.GetString(pd.Name); + return ToCSharpString(metadata, declaringType, fullName, omitGenerics) + "." + metadata.GetString(pd.Name); return metadata.GetString(pd.Name); default: return null; @@ -632,5 +635,9 @@ namespace ICSharpCode.ILSpy { return CSharpDecompiler.GetCodeMappingInfo(module, member); } + + CSharpBracketSearcher bracketSearcher = new CSharpBracketSearcher(); + + public override IBracketSearcher BracketSearcher => bracketSearcher; } } diff --git a/ILSpy/Languages/ILAstLanguage.cs b/ILSpy/Languages/ILAstLanguage.cs index fa278e490..3d0fbdc8a 100644 --- a/ILSpy/Languages/ILAstLanguage.cs +++ b/ILSpy/Languages/ILAstLanguage.cs @@ -119,7 +119,7 @@ namespace ICSharpCode.ILSpy var reader = new ILReader(typeSystem.MainModule); reader.UseDebugSymbols = options.DecompilerSettings.UseDebugSymbols; var methodBody = module.Reader.GetMethodBody(methodDef.RelativeVirtualAddress); - ILFunction il = reader.ReadIL((SRM.MethodDefinitionHandle)method.MetadataToken, methodBody, cancellationToken: options.CancellationToken); + ILFunction il = reader.ReadIL((SRM.MethodDefinitionHandle)method.MetadataToken, methodBody, kind: ILFunctionKind.TopLevelFunction, cancellationToken: options.CancellationToken); var namespaces = new HashSet(); var decompiler = new CSharpDecompiler(typeSystem, options.DecompilerSettings) { CancellationToken = options.CancellationToken }; ILTransformContext context = decompiler.CreateILTransformContext(il); diff --git a/ILSpy/Languages/ILLanguage.cs b/ILSpy/Languages/ILLanguage.cs index e2ab9d1f3..3dd1ec1ab 100644 --- a/ILSpy/Languages/ILLanguage.cs +++ b/ILSpy/Languages/ILLanguage.cs @@ -27,6 +27,8 @@ using System.Reflection.Metadata.Ecma335; using System.Linq; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.Util; +using ICSharpCode.Decompiler.Solution; namespace ICSharpCode.ILSpy { @@ -150,7 +152,7 @@ namespace ICSharpCode.ILSpy dis.DisassembleNamespace(nameSpace, module, types.Select(t => (TypeDefinitionHandle)t.MetadataToken)); } - public override void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) + public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) { output.WriteLine("// " + assembly.FileName); output.WriteLine(); @@ -174,6 +176,7 @@ namespace ICSharpCode.ILSpy dis.WriteModuleContents(module); } } + return null; } } } diff --git a/ILSpy/Languages/Language.cs b/ILSpy/Languages/Language.cs index b131c6429..08429ae85 100644 --- a/ILSpy/Languages/Language.cs +++ b/ILSpy/Languages/Language.cs @@ -23,6 +23,7 @@ using System.Reflection.PortableExecutable; using System.Text; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; @@ -101,6 +102,12 @@ namespace ICSharpCode.ILSpy } } + public virtual TextView.IBracketSearcher BracketSearcher { + get { + return TextView.DefaultBracketSearcher.DefaultInstance; + } + } + public virtual void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(method.DeclaringTypeDefinition, includeNamespace: true) + "." + method.Name); @@ -131,11 +138,11 @@ namespace ICSharpCode.ILSpy WriteCommentLine(output, nameSpace); } - public virtual void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) + public virtual ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, assembly.FileName); var asm = assembly.GetPEFileOrNull(); - if (asm == null) return; + if (asm == null) return null; var metadata = asm.Metadata; if (metadata.IsAssembly) { var name = metadata.GetAssemblyDefinition(); @@ -147,6 +154,7 @@ namespace ICSharpCode.ILSpy } else { WriteCommentLine(output, metadata.GetString(metadata.GetModuleDefinition().Name)); } + return null; } public virtual void WriteCommentLine(ITextOutput output, string comment) @@ -445,42 +453,42 @@ namespace ICSharpCode.ILSpy /// /// This should produce a string representation of the entity for search to match search strings against. /// - public virtual string GetEntityName(PEFile module, EntityHandle handle, bool fullName) + public virtual string GetEntityName(PEFile module, EntityHandle handle, bool fullName, bool omitGenerics) { MetadataReader metadata = module.Metadata; switch (handle.Kind) { case HandleKind.TypeDefinition: if (fullName) - return EscapeName(((TypeDefinitionHandle)handle).GetFullTypeName(metadata).ToILNameString()); + return EscapeName(((TypeDefinitionHandle)handle).GetFullTypeName(metadata).ToILNameString(omitGenerics)); var td = metadata.GetTypeDefinition((TypeDefinitionHandle)handle); return EscapeName(metadata.GetString(td.Name)); case HandleKind.FieldDefinition: var fd = metadata.GetFieldDefinition((FieldDefinitionHandle)handle); - var declaringType = fd.GetDeclaringType(); if (fullName) - return EscapeName(fd.GetDeclaringType().GetFullTypeName(metadata).ToILNameString() + "." + metadata.GetString(fd.Name)); + return EscapeName(fd.GetDeclaringType().GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(fd.Name)); return EscapeName(metadata.GetString(fd.Name)); case HandleKind.MethodDefinition: var md = metadata.GetMethodDefinition((MethodDefinitionHandle)handle); - declaringType = md.GetDeclaringType(); string methodName = metadata.GetString(md.Name); - int genericParamCount = md.GetGenericParameters().Count; - if (genericParamCount > 0) - methodName += "``" + genericParamCount; + if (!omitGenerics) { + int genericParamCount = md.GetGenericParameters().Count; + if (genericParamCount > 0) + methodName += "``" + genericParamCount; + } if (fullName) - return EscapeName(md.GetDeclaringType().GetFullTypeName(metadata).ToILNameString() + "." + methodName); + return EscapeName(md.GetDeclaringType().GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + methodName); return EscapeName(methodName); case HandleKind.EventDefinition: var ed = metadata.GetEventDefinition((EventDefinitionHandle)handle); - declaringType = metadata.GetMethodDefinition(ed.GetAccessors().GetAny()).GetDeclaringType(); + var declaringType = metadata.GetMethodDefinition(ed.GetAccessors().GetAny()).GetDeclaringType(); if (fullName) - return EscapeName(declaringType.GetFullTypeName(metadata).ToILNameString() + "." + metadata.GetString(ed.Name)); + return EscapeName(declaringType.GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(ed.Name)); return EscapeName(metadata.GetString(ed.Name)); case HandleKind.PropertyDefinition: var pd = metadata.GetPropertyDefinition((PropertyDefinitionHandle)handle); declaringType = metadata.GetMethodDefinition(pd.GetAccessors().GetAny()).GetDeclaringType(); if (fullName) - return EscapeName(declaringType.GetFullTypeName(metadata).ToILNameString() + "." + metadata.GetString(pd.Name)); + return EscapeName(declaringType.GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(pd.Name)); return EscapeName(metadata.GetString(pd.Name)); default: return null; diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index a168d2e66..a83b6d799 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -28,6 +28,7 @@ Executed="RefreshCommandExecuted" /> - diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index c532eafd1..5732b5673 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -38,6 +38,7 @@ using ICSharpCode.Decompiler.Documentation; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; +using ICSharpCode.ILSpy.Controls; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.TreeView; @@ -897,18 +898,18 @@ namespace ICSharpCode.ILSpy } decompilationTask = decompilerTextView.DecompileAsync(this.CurrentLanguage, this.SelectedNodes, new DecompilationOptions() { TextViewState = state }); } - + + void SaveCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.Handled = true; + e.CanExecute = SaveCodeContextMenuEntry.CanExecute(SelectedNodes.ToList()); + } + void SaveCommandExecuted(object sender, ExecutedRoutedEventArgs e) { - if (this.SelectedNodes.Count() == 1) { - if (this.SelectedNodes.Single().Save(this.TextView)) - return; - } - this.TextView.SaveToDisk(this.CurrentLanguage, - this.SelectedNodes, - new DecompilationOptions() { FullDecompilation = true }); + SaveCodeContextMenuEntry.Execute(SelectedNodes.ToList()); } - + public void RefreshDecompiledView() { try { diff --git a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs index c1c734173..134f9faf0 100644 --- a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs +++ b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs @@ -54,7 +54,9 @@ namespace ICSharpCode.ILSpy.Options var properties = typeof(Decompiler.DecompilerSettings).GetProperties() .Where(p => p.GetCustomAttribute()?.Browsable != false); foreach (var p in properties) { - p.SetValue(newSettings, (bool?)e.Attribute(p.Name) ?? true); + var value = (bool?)e.Attribute(p.Name); + if (value.HasValue) + p.SetValue(newSettings, value.Value); } return newSettings; } diff --git a/ILSpy/Options/DisplaySettings.cs b/ILSpy/Options/DisplaySettings.cs index 3d6053961..a887e2913 100644 --- a/ILSpy/Options/DisplaySettings.cs +++ b/ILSpy/Options/DisplaySettings.cs @@ -215,6 +215,18 @@ namespace ICSharpCode.ILSpy.Options } } + bool highlightMatchingBraces = true; + + public bool HighlightMatchingBraces { + get { return highlightMatchingBraces; } + set { + if (highlightMatchingBraces != value) { + highlightMatchingBraces = value; + OnPropertyChanged(); + } + } + } + public void CopyValues(DisplaySettings s) { this.SelectedFont = s.selectedFont; @@ -231,6 +243,7 @@ namespace ICSharpCode.ILSpy.Options this.IndentationUseTabs = s.indentationUseTabs; this.IndentationTabSize = s.indentationTabSize; this.IndentationSize = s.indentationSize; + this.HighlightMatchingBraces = s.highlightMatchingBraces; } } } diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml b/ILSpy/Options/DisplaySettingsPanel.xaml index ab2ad7398..13d149e6d 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml +++ b/ILSpy/Options/DisplaySettingsPanel.xaml @@ -80,6 +80,7 @@ + diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml.cs b/ILSpy/Options/DisplaySettingsPanel.xaml.cs index b08a139fb..56ff2622d 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml.cs +++ b/ILSpy/Options/DisplaySettingsPanel.xaml.cs @@ -114,6 +114,7 @@ namespace ICSharpCode.ILSpy.Options s.IndentationUseTabs = (bool?)e.Attribute("IndentationUseTabs") ?? true; s.IndentationSize = (int?)e.Attribute("IndentationSize") ?? 4; s.IndentationTabSize = (int?)e.Attribute("IndentationTabSize") ?? 4; + s.HighlightMatchingBraces = (bool?)e.Attribute("HighlightMatchingBraces") ?? true; return s; } @@ -136,6 +137,7 @@ namespace ICSharpCode.ILSpy.Options section.SetAttributeValue("IndentationUseTabs", s.IndentationUseTabs); section.SetAttributeValue("IndentationSize", s.IndentationSize); section.SetAttributeValue("IndentationTabSize", s.IndentationTabSize); + section.SetAttributeValue("HighlightMatchingBraces", s.HighlightMatchingBraces); XElement existingElement = root.Element("DisplaySettings"); if (existingElement != null) diff --git a/ILSpy/Properties/AssemblyInfo.template.cs b/ILSpy/Properties/AssemblyInfo.template.cs index 4f475f577..cdbdfecbf 100644 --- a/ILSpy/Properties/AssemblyInfo.template.cs +++ b/ILSpy/Properties/AssemblyInfo.template.cs @@ -39,7 +39,7 @@ internal static class RevisionClass public const string Minor = "0"; public const string Build = "0"; public const string Revision = "$INSERTREVISION$"; - public const string VersionName = "preview2"; + public const string VersionName = "preview4"; public const string FullVersion = Major + "." + Minor + "." + Build + ".$INSERTREVISION$$INSERTBRANCHPOSTFIX$$INSERTVERSIONNAMEPOSTFIX$"; } diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 5c86e06e2..b555a63a4 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -303,6 +303,25 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to The directory is not empty. File will be overwritten. + ///Are you sure you want to continue?. + /// + public static string AssemblySaveCodeDirectoryNotEmpty { + get { + return ResourceManager.GetString("AssemblySaveCodeDirectoryNotEmpty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Project Directory not empty. + /// + public static string AssemblySaveCodeDirectoryNotEmptyTitle { + get { + return ResourceManager.GetString("AssemblySaveCodeDirectoryNotEmptyTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to Automatically check for updates every week. /// @@ -474,6 +493,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Use 'ref' extension methods. + /// + public static string DecompilerSettings_AllowExtensionMethodSyntaxOnRef { + get { + return ResourceManager.GetString("DecompilerSettings.AllowExtensionMethodSyntaxOnRef", resourceCulture); + } + } + /// /// Looks up a localized string similar to Always cast targets of explicit interface implementation calls. /// @@ -700,11 +728,11 @@ namespace ICSharpCode.ILSpy.Properties { } /// - /// Looks up a localized string similar to Introduce local functions (NOT IMPLEMENTED!). + /// Looks up a localized string similar to Introduce local functions. /// - public static string DecompilerSettings_IntroduceLocalFunctionsNOTIMPLEMENTED { + public static string DecompilerSettings_IntroduceLocalFunctions { get { - return ResourceManager.GetString("DecompilerSettings.IntroduceLocalFunctionsNOTIMPLEMENTED", resourceCulture); + return ResourceManager.GetString("DecompilerSettings.IntroduceLocalFunctions", resourceCulture); } } @@ -773,6 +801,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Remove dead stores (use with caution!). + /// + public static string DecompilerSettings_RemoveDeadStores { + get { + return ResourceManager.GetString("DecompilerSettings.RemoveDeadStores", resourceCulture); + } + } + /// /// Looks up a localized string similar to Remove optional arguments, if possible. /// @@ -908,6 +945,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Use throw expressions. + /// + public static string DecompilerSettings_UseThrowExpressions { + get { + return ResourceManager.GetString("DecompilerSettings.UseThrowExpressions", resourceCulture); + } + } + /// /// Looks up a localized string similar to Use tuple type syntax. /// @@ -1115,6 +1161,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Highlight matching braces. + /// + public static string HighlightMatchingBraces { + get { + return ResourceManager.GetString("HighlightMatchingBraces", resourceCulture); + } + } + /// /// Looks up a localized string similar to ILSpy version . /// @@ -1386,7 +1441,7 @@ namespace ICSharpCode.ILSpy.Properties { } /// - /// Looks up a localized string similar to ReloadAssemblies. + /// Looks up a localized string similar to Reload all assemblies. /// public static string RefreshCommand_ReloadAssemblies { get { @@ -1520,6 +1575,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Select language to decompile to. + /// + public static string SelectLanguageDropdownTooltip { + get { + return ResourceManager.GetString("SelectLanguageDropdownTooltip", resourceCulture); + } + } + /// /// Looks up a localized string similar to Select a list:. /// @@ -1529,6 +1593,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Select version of language to output. + /// + public static string SelectVersionDropdownTooltip { + get { + return ResourceManager.GetString("SelectVersionDropdownTooltip", resourceCulture); + } + } + /// /// Looks up a localized string similar to Shell. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index 0128389d9..d98c8cdcc 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -172,7 +172,7 @@ Generate portable PDB - ReloadAssemblies + Reload all assemblies _Reload @@ -702,8 +702,8 @@ Remove optional arguments, if possible - - Introduce local functions (NOT IMPLEMENTED!) + + Introduce local functions Nullable reference types @@ -729,4 +729,29 @@ Entity could not be resolved. Cannot analyze entities from missing assembly references. Add the missing reference and try again. + + Use throw expressions + + + Use 'ref' extension methods + + + The directory is not empty. File will be overwritten. +Are you sure you want to continue? + + + Project Directory not empty + + + Highlight matching braces + + + Select language to decompile to + + + Select version of language to output + + + Remove dead stores (use with caution!) + \ No newline at end of file diff --git a/ILSpy/Properties/Resources.zh-Hans.resx b/ILSpy/Properties/Resources.zh-Hans.resx index 4ab21794f..4f04dba0b 100644 --- a/ILSpy/Properties/Resources.zh-Hans.resx +++ b/ILSpy/Properties/Resources.zh-Hans.resx @@ -676,7 +676,7 @@ IsByRefLikeAttribute应替换为结构上的 "ref" 修饰符 - IsReadOnlyAttribute 应替为结构参数上的 "readonly"/"中的修饰符 + IsReadOnlyAttribute 应替为结构参数上的 "readonly"/"in"中的修饰符 类型参数上的IsUnmanagedAttribute 应替换为 "非托管" 约束 @@ -702,8 +702,8 @@ 如果可能, 删除可选参数 - - 引入本地功能 (未实现!) + + 引入本地功能 C# 7.0 本地函数未实现! diff --git a/ILSpy/Properties/app.config.template b/ILSpy/Properties/app.config.template index f12271be6..58183137f 100644 --- a/ILSpy/Properties/app.config.template +++ b/ILSpy/Properties/app.config.template @@ -14,7 +14,7 @@ - + diff --git a/ILSpy/Search/AbstractSearchStrategy.cs b/ILSpy/Search/AbstractSearchStrategy.cs index ae787621f..1a25f1698 100644 --- a/ILSpy/Search/AbstractSearchStrategy.cs +++ b/ILSpy/Search/AbstractSearchStrategy.cs @@ -14,6 +14,7 @@ namespace ICSharpCode.ILSpy.Search protected readonly string[] searchTerm; protected readonly Regex regex; protected readonly bool fullNameSearch; + protected readonly bool omitGenerics; protected readonly Language language; protected readonly ApiVisibility apiVisibility; private readonly IProducerConsumerCollection resultQueue; @@ -29,11 +30,13 @@ namespace ICSharpCode.ILSpy.Search if (search.StartsWith("/", StringComparison.Ordinal) && search.Length > 4) { var regexString = search.Substring(1, search.Length - 1); fullNameSearch = search.Contains("\\."); + omitGenerics = !search.Contains("<"); if (regexString.EndsWith("/", StringComparison.Ordinal)) regexString = regexString.Substring(0, regexString.Length - 1); regex = SafeNewRegex(regexString); } else { fullNameSearch = search.Contains("."); + omitGenerics = !search.Contains("<"); } } searchTerm = terms; diff --git a/ILSpy/Search/MemberSearchStrategy.cs b/ILSpy/Search/MemberSearchStrategy.cs index ef68010fb..4a631986b 100644 --- a/ILSpy/Search/MemberSearchStrategy.cs +++ b/ILSpy/Search/MemberSearchStrategy.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.ILSpy.Search if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Type) { foreach (var handle in metadata.TypeDefinitions) { cancellationToken.ThrowIfCancellationRequested(); - string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); + string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch, omitGenerics); if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; var type = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); @@ -43,7 +43,7 @@ namespace ICSharpCode.ILSpy.Search if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Method) { foreach (var handle in metadata.MethodDefinitions) { cancellationToken.ThrowIfCancellationRequested(); - string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); + string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch, omitGenerics); if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; var method = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); @@ -55,7 +55,7 @@ namespace ICSharpCode.ILSpy.Search if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Field) { foreach (var handle in metadata.FieldDefinitions) { cancellationToken.ThrowIfCancellationRequested(); - string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); + string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch, omitGenerics); if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; var field = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); @@ -67,7 +67,7 @@ namespace ICSharpCode.ILSpy.Search if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Property) { foreach (var handle in metadata.PropertyDefinitions) { cancellationToken.ThrowIfCancellationRequested(); - string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); + string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch, omitGenerics); if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; var property = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); @@ -79,7 +79,7 @@ namespace ICSharpCode.ILSpy.Search if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Event) { foreach (var handle in metadata.EventDefinitions) { cancellationToken.ThrowIfCancellationRequested(); - string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); + string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch, omitGenerics); if (!IsMatch(languageSpecificName)) continue; var @event = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); diff --git a/ILSpy/SolutionWriter.cs b/ILSpy/SolutionWriter.cs new file mode 100644 index 000000000..d892e22ec --- /dev/null +++ b/ILSpy/SolutionWriter.cs @@ -0,0 +1,182 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Solution; +using ICSharpCode.Decompiler.Util; +using ICSharpCode.ILSpy.TextView; + +namespace ICSharpCode.ILSpy +{ + /// + /// An utility class that creates a Visual Studio solution containing projects for the + /// decompiled assemblies. + /// + internal class SolutionWriter + { + /// + /// Creates a Visual Studio solution that contains projects with decompiled code + /// of the specified . The solution file will be saved + /// to the . The directory of this file must either + /// be empty or not exist. + /// + /// A reference to the instance. + /// The target file path of the solution file. + /// The assembly nodes to decompile. + /// + /// Thrown when is null, + /// an empty or a whitespace string. + /// Thrown when > or + /// is null. + public static void CreateSolution(DecompilerTextView textView, string solutionFilePath, Language language, IEnumerable assemblies) + { + if (textView == null) { + throw new ArgumentNullException(nameof(textView)); + } + + if (string.IsNullOrWhiteSpace(solutionFilePath)) { + throw new ArgumentException("The solution file path cannot be null or empty.", nameof(solutionFilePath)); + } + + if (assemblies == null) { + throw new ArgumentNullException(nameof(assemblies)); + } + + var writer = new SolutionWriter(solutionFilePath); + + textView + .RunWithCancellation(ct => writer.CreateSolution(assemblies, language, ct)) + .Then(output => textView.ShowText(output)) + .HandleExceptions(); + } + + readonly string solutionFilePath; + readonly string solutionDirectory; + readonly ConcurrentBag projects; + readonly ConcurrentBag statusOutput; + + SolutionWriter(string solutionFilePath) + { + this.solutionFilePath = solutionFilePath; + solutionDirectory = Path.GetDirectoryName(solutionFilePath); + statusOutput = new ConcurrentBag(); + projects = new ConcurrentBag(); + } + + async Task CreateSolution(IEnumerable assemblies, Language language, CancellationToken ct) + { + var result = new AvalonEditTextOutput(); + + var duplicates = new HashSet(); + if (assemblies.Any(asm => !duplicates.Add(asm.ShortName))) { + result.WriteLine("Duplicate assembly names selected, cannot generate a solution."); + return result; + } + + Stopwatch stopwatch = Stopwatch.StartNew(); + + try { + await Task.Run(() => Parallel.ForEach(assemblies, n => WriteProject(n, language, solutionDirectory, ct))) + .ConfigureAwait(false); + + await Task.Run(() => SolutionCreator.WriteSolutionFile(solutionFilePath, projects)) + .ConfigureAwait(false); + } catch (AggregateException ae) { + if (ae.Flatten().InnerExceptions.All(e => e is OperationCanceledException)) { + result.WriteLine(); + result.WriteLine("Generation was cancelled."); + return result; + } + + result.WriteLine(); + result.WriteLine("Failed to generate the Visual Studio Solution. Errors:"); + ae.Handle(e => { + result.WriteLine(e.Message); + return true; + }); + + return result; + } + + foreach (var item in statusOutput) { + result.WriteLine(item); + } + + if (statusOutput.Count == 0) { + result.WriteLine("Successfully decompiled the following assemblies into Visual Studio projects:"); + foreach (var item in assemblies.Select(n => n.Text.ToString())) { + result.WriteLine(item); + } + + result.WriteLine(); + + if (assemblies.Count() == projects.Count) { + result.WriteLine("Created the Visual Studio Solution file."); + } + + result.WriteLine(); + result.WriteLine("Elapsed time: " + stopwatch.Elapsed.TotalSeconds.ToString("F1") + " seconds."); + result.WriteLine(); + result.AddButton(null, "Open Explorer", delegate { Process.Start("explorer", "/select,\"" + solutionFilePath + "\""); }); + } + + return result; + } + + void WriteProject(LoadedAssembly loadedAssembly, Language language, string targetDirectory, CancellationToken ct) + { + targetDirectory = Path.Combine(targetDirectory, loadedAssembly.ShortName); + string projectFileName = Path.Combine(targetDirectory, loadedAssembly.ShortName + language.ProjectFileExtension); + + if (!Directory.Exists(targetDirectory)) { + try { + Directory.CreateDirectory(targetDirectory); + } catch (Exception e) { + statusOutput.Add($"Failed to create a directory '{targetDirectory}':{Environment.NewLine}{e}"); + return; + } + } + + try { + using (var projectFileWriter = new StreamWriter(projectFileName)) { + var projectFileOutput = new PlainTextOutput(projectFileWriter); + var options = new DecompilationOptions() { + FullDecompilation = true, + CancellationToken = ct, + SaveAsProjectDirectory = targetDirectory + }; + + var projectInfo = language.DecompileAssembly(loadedAssembly, projectFileOutput, options); + if (projectInfo != null) { + projects.Add(new ProjectItem(projectFileName, projectInfo.PlatformName, projectInfo.Guid)); + } + } + } catch (Exception e) when (!(e is OperationCanceledException)) { + statusOutput.Add($"Failed to decompile the assembly '{loadedAssembly.FileName}':{Environment.NewLine}{e}"); + } + } + } +} diff --git a/ILSpy/TextView/BracketHighlightRenderer.cs b/ILSpy/TextView/BracketHighlightRenderer.cs new file mode 100644 index 000000000..6a814b7c4 --- /dev/null +++ b/ILSpy/TextView/BracketHighlightRenderer.cs @@ -0,0 +1,112 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Windows.Media; +using ICSharpCode.AvalonEdit.Document; +using ICSharpCode.AvalonEdit.Rendering; + +namespace ICSharpCode.ILSpy.TextView +{ + /// + /// Allows language specific search for matching brackets. + /// + public interface IBracketSearcher + { + /// + /// Searches for a matching bracket from the given offset to the start of the document. + /// + /// A BracketSearchResult that contains the positions and lengths of the brackets. Return null if there is nothing to highlight. + BracketSearchResult SearchBracket(IDocument document, int offset); + } + + public class DefaultBracketSearcher : IBracketSearcher + { + public static readonly DefaultBracketSearcher DefaultInstance = new DefaultBracketSearcher(); + + public BracketSearchResult SearchBracket(IDocument document, int offset) + { + return null; + } + } + + /// + /// Describes a pair of matching brackets found by . + /// + public class BracketSearchResult + { + public int OpeningBracketOffset { get; private set; } + + public int OpeningBracketLength { get; private set; } + + public int ClosingBracketOffset { get; private set; } + + public int ClosingBracketLength { get; private set; } + + public BracketSearchResult(int openingBracketOffset, int openingBracketLength, + int closingBracketOffset, int closingBracketLength) + { + this.OpeningBracketOffset = openingBracketOffset; + this.OpeningBracketLength = openingBracketLength; + this.ClosingBracketOffset = closingBracketOffset; + this.ClosingBracketLength = closingBracketLength; + } + } + + public class BracketHighlightRenderer : IBackgroundRenderer + { + BracketSearchResult result; + Pen borderPen; + Brush backgroundBrush; + ICSharpCode.AvalonEdit.Rendering.TextView textView; + + public void SetHighlight(BracketSearchResult result) + { + if (this.result != result) { + this.result = result; + textView.InvalidateLayer(this.Layer); + } + } + + public BracketHighlightRenderer(ICSharpCode.AvalonEdit.Rendering.TextView textView) + { + if (textView == null) + throw new ArgumentNullException("textView"); + + this.borderPen = new Pen(new SolidColorBrush(Color.FromArgb(52, 0, 0, 255)), 1); + this.borderPen.Freeze(); + + this.backgroundBrush = new SolidColorBrush(Color.FromArgb(22, 0, 0, 255)); + this.backgroundBrush.Freeze(); + + this.textView = textView; + + this.textView.BackgroundRenderers.Add(this); + } + + public KnownLayer Layer { + get { + return KnownLayer.Selection; + } + } + + public void Draw(ICSharpCode.AvalonEdit.Rendering.TextView textView, DrawingContext drawingContext) + { + if (this.result == null) + return; + + BackgroundGeometryBuilder builder = new BackgroundGeometryBuilder(); + + builder.CornerRadius = 1; + + builder.AddSegment(textView, new TextSegment() { StartOffset = result.OpeningBracketOffset, Length = result.OpeningBracketLength }); + builder.CloseFigure(); // prevent connecting the two segments + builder.AddSegment(textView, new TextSegment() { StartOffset = result.ClosingBracketOffset, Length = result.ClosingBracketLength }); + + Geometry geometry = builder.CreateGeometry(); + if (geometry != null) { + drawingContext.DrawGeometry(backgroundBrush, borderPen, geometry); + } + } + } +} \ No newline at end of file diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 4bc85ca58..5d714cb5f 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -68,6 +68,7 @@ namespace ICSharpCode.ILSpy.TextView readonly UIElementGenerator uiElementGenerator; readonly List activeCustomElementGenerators = new List(); RichTextColorizer activeRichTextColorizer; + BracketHighlightRenderer bracketHighlightRenderer; FoldingManager foldingManager; ILSpyTreeNode[] decompiledNodes; @@ -106,12 +107,14 @@ namespace ICSharpCode.ILSpy.TextView this.referenceElementGenerator = new ReferenceElementGenerator(this.JumpToReference, this.IsLink); textEditor.TextArea.TextView.ElementGenerators.Add(referenceElementGenerator); this.uiElementGenerator = new UIElementGenerator(); + this.bracketHighlightRenderer = new BracketHighlightRenderer(textEditor.TextArea.TextView); textEditor.TextArea.TextView.ElementGenerators.Add(uiElementGenerator); textEditor.Options.RequireControlModifierForHyperlinkClick = false; textEditor.TextArea.TextView.MouseHover += TextViewMouseHover; textEditor.TextArea.TextView.MouseHoverStopped += TextViewMouseHoverStopped; textEditor.TextArea.PreviewMouseDown += TextAreaMouseDown; textEditor.TextArea.PreviewMouseUp += TextAreaMouseUp; + textEditor.TextArea.Caret.PositionChanged += HighlightBrackets; textEditor.MouseMove += TextEditorMouseMove; textEditor.MouseLeave += TextEditorMouseLeave; textEditor.SetBinding(Control.FontFamilyProperty, new Binding { Source = DisplaySettingsPanel.CurrentDisplaySettings, Path = new PropertyPath("SelectedFont") }); @@ -423,6 +426,18 @@ namespace ICSharpCode.ILSpy.TextView } #endregion + #region Highlight brackets + void HighlightBrackets(object sender, EventArgs e) + { + if (DisplaySettingsPanel.CurrentDisplaySettings.HighlightMatchingBraces) { + var result = MainWindow.Instance.CurrentLanguage.BracketSearcher.SearchBracket(textEditor.Document, textEditor.CaretOffset); + bracketHighlightRenderer.SetHighlight(result); + } else { + bracketHighlightRenderer.SetHighlight(null); + } + } + #endregion + #region RunWithCancellation /// /// Switches the GUI into "waiting" mode, then calls to create diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index 51aa65bdf..13e2c1df2 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -292,9 +292,8 @@ namespace ICSharpCode.ILSpy.TreeNodes foreach (string entry in Directory.GetFileSystemEntries(options.SaveAsProjectDirectory)) { if (!string.Equals(entry, dlg.FileName, StringComparison.OrdinalIgnoreCase)) { var result = MessageBox.Show( - "The directory is not empty. File will be overwritten." + Environment.NewLine + - "Are you sure you want to continue?", - "Project Directory not empty", + Resources.AssemblySaveCodeDirectoryNotEmpty, + Resources.AssemblySaveCodeDirectoryNotEmptyTitle, MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No); if (result == MessageBoxResult.No) return true; // don't save, but mark the Save operation as handled diff --git a/ILSpy/TreeNodes/DerivedTypesEntryNode.cs b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs index 91da3e033..e75214afe 100644 --- a/ILSpy/TreeNodes/DerivedTypesEntryNode.cs +++ b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs @@ -42,7 +42,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override object Text { - get { return type.FullName + type.MetadataToken.ToSuffixString(); } + get { return Language.TypeToString(type, includeNamespace: true) + type.MetadataToken.ToSuffixString(); } } public override object Icon => TypeTreeNode.GetIcon(type); diff --git a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs index 331f8da08..fde4630f1 100644 --- a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs +++ b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs @@ -86,7 +86,6 @@ namespace ICSharpCode.ILSpy.TreeNodes output.Unindent(); output.WriteLine(); } - } } } diff --git a/README.md b/README.md index 8901e4bea..7815ad7c0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ILSpy [![Join the chat at https://gitter.im/icsharpcode/ILSpy](https://badges.gitter.im/icsharpcode/ILSpy.svg)](https://gitter.im/icsharpcode/ILSpy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![NuGet](https://img.shields.io/nuget/v/ICSharpCode.Decompiler.svg)](https://nuget.org/packages/ICSharpCode.Decompiler) [![Build status](https://ci.appveyor.com/api/projects/status/imgec05g0wwv25ij/branch/master?svg=true)](https://ci.appveyor.com/project/icsharpcode/ilspy/branch/master) [![Twitter Follow](https://img.shields.io/twitter/follow/ILSpy.svg?label=Follow%20@ILSpy)](https://twitter.com/ilspy) [![ilspy.net](https://img.shields.io/badge/@-ilspy.net-blue.svg)](http://www.ilspy.net) [![ILSpy VS extension](https://img.shields.io/badge/VS%20Extension-ILSpy-blue.svg)](https://visualstudiogallery.msdn.microsoft.com/8ef1d688-f80c-4380-8004-2ec7f814e7de) +# ILSpy [![Join the chat at https://gitter.im/icsharpcode/ILSpy](https://badges.gitter.im/icsharpcode/ILSpy.svg)](https://gitter.im/icsharpcode/ILSpy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![NuGet](https://img.shields.io/nuget/v/ICSharpCode.Decompiler.svg)](https://nuget.org/packages/ICSharpCode.Decompiler) [![Build status](https://ci.appveyor.com/api/projects/status/imgec05g0wwv25ij/branch/master?svg=true)](https://ci.appveyor.com/project/icsharpcode/ilspy/branch/master) [![Twitter Follow](https://img.shields.io/twitter/follow/ILSpy.svg?label=Follow%20@ILSpy)](https://twitter.com/ilspy) [![ILSpy VS extension](https://img.shields.io/badge/VS%20Extension-ILSpy-blue.svg)](https://visualstudiogallery.msdn.microsoft.com/8ef1d688-f80c-4380-8004-2ec7f814e7de) [![Build Status](https://icsharpcode.visualstudio.com/icsharpcode-pipelines/_apis/build/status/icsharpcode.ILSpy?branchName=master)](https://icsharpcode.visualstudio.com/icsharpcode-pipelines/_build/latest?definitionId=1&branchName=master) ILSpy is the open-source .NET assembly browser and decompiler. @@ -48,12 +48,13 @@ How to build ------------ Windows: -- Install Visual Studio (minimum version: 2017.7) with the following components: +- Install Visual Studio (minimum version: 2019.2) with the following components: - Workload ".NET Desktop Development" - .NET Framework 4.6.2 Targeting Pack (if the VS installer does not offer this option, install the [.NET 4.6.2 developer pack](https://www.microsoft.com/en-us/download/details.aspx?id=53321) separately) - - Individual Component "VC++ 2017 version 15.9 v14.16 latest v141 tools" (or similar) + - Individual Component "MSVC v142 - VS 2019 C++ x64/x86 build tools (v14.22)" (or similar) - The VC++ toolset is optional; if present it is used for `editbin.exe` to modify the stack size used by ILSpy.exe from 1MB to 16MB, because the decompiler makes heavy use of recursion, where small stack sizes lead to problems in very complex methods. - Install the [.NET Core SDK 2.2](https://dotnet.microsoft.com/download) +- Install the [.NET Core SDK 3](https://dotnet.microsoft.com/download/dotnet-core) - Check out the ILSpy repository using git. - Execute `git submodule update --init --recursive` to download the ILSpy-Tests submodule (used by some test cases). - Open ILSpy.sln in Visual Studio. @@ -63,6 +64,7 @@ Windows: Unix: - Make sure .NET Core 2.2 is installed (you can get it here: https://get.dot.net). +- Make sure [.NET Core SDK 3](https://dotnet.microsoft.com/download/dotnet-core) is installed. - Check out the repository using git. - Execute `git submodule update --init --recursive` to download the ILSpy-Tests submodule (used by some test cases). - Use `dotnet build Frontends.sln` to build the non-Windows flavors of ILSpy (cli and powershell core). diff --git a/appveyor.yml b/appveyor.yml index 08de22677..13806b0ce 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,28 +1,39 @@ platform: Any CPU + configuration: - Debug - Release -image: Visual Studio 2019 Preview + +image: Visual Studio 2019 + install: +- cmd: choco install dotnetcore-sdk --pre - git submodule update --init --recursive - ps: .\BuildTools\appveyor-install.ps1 + nuget: account_feed: false project_feed: true disable_publish_on_pr: true + before_build: - nuget restore ILSpy.sln + build_script: - msbuild ILSpy.sln /v:minimal /p:ResolveNuGetPackages=false "/logger:%ProgramFiles%\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" + after_build: - 7z a ILSpy_binaries.zip %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net462\*.dll %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net462\*.exe %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net462\*.config %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net462\*\ILSpy.resources.dll + test: assemblies: - 'ICSharpCode.Decompiler.Tests\bin\%configuration%\net462\ICSharpCode.Decompiler.Tests.exe' - 'ILSpy.Tests\bin\%configuration%\net462\ILSpy.Tests.exe' - 'ILSpy.BamlDecompiler.Tests\bin\%configuration%\net462\ILSpy.BamlDecompiler.Tests.dll' + after_test: - python BuildTools\tidy.py + for: - branches: except: diff --git a/clean.bat b/clean.bat index 690435b2d..b19f16be7 100644 --- a/clean.bat +++ b/clean.bat @@ -1,12 +1,12 @@ @setlocal enabledelayedexpansion @set MSBUILD= -@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2017"\*) do ( - @if exist "%%M\MSBuild\15.0\Bin\MSBuild.exe" ( - @set "MSBUILD=%%M\MSBuild\15.0\Bin\MSBuild.exe" +@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2019"\*) do ( + @if exist "%%M\MSBuild\Current\Bin\MSBuild.exe" ( + @set "MSBUILD=%%M\MSBuild\Current\Bin\MSBuild.exe" ) ) @if "%MSBUILD%" == "" ( - @echo Could not find VS2017 MSBuild + @echo Could not find VS2019 MSBuild @exit /b 1 ) "%MSBUILD%" /m ILSpy.sln /t:Clean /p:Configuration=Debug "/p:Platform=Any CPU" || pause diff --git a/debugbuild.bat b/debugbuild.bat index 08d5425e6..90325ccab 100644 --- a/debugbuild.bat +++ b/debugbuild.bat @@ -1,12 +1,12 @@ @setlocal enabledelayedexpansion @set MSBUILD= -@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2017"\*) do ( - @if exist "%%M\MSBuild\15.0\Bin\MSBuild.exe" ( - @set "MSBUILD=%%M\MSBuild\15.0\Bin\MSBuild.exe" +@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2019"\*) do ( + @if exist "%%M\MSBuild\Current\Bin\MSBuild.exe" ( + @set "MSBUILD=%%M\MSBuild\Current\Bin\MSBuild.exe" ) ) @if "%MSBUILD%" == "" ( - @echo Could not find VS2017 MSBuild + @echo Could not find VS2019 MSBuild @exit /b 1 ) "%MSBUILD%" ILSpy.sln /p:Configuration=Debug "/p:Platform=Any CPU" diff --git a/preparerelease.bat b/preparerelease.bat index 60fadaabe..d2b141357 100644 --- a/preparerelease.bat +++ b/preparerelease.bat @@ -1,12 +1,12 @@ @setlocal enabledelayedexpansion @set MSBUILD= -@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2017"\*) do ( - @if exist "%%M\MSBuild\15.0\Bin\MSBuild.exe" ( - @set "MSBUILD=%%M\MSBuild\15.0\Bin\MSBuild.exe" +@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2019"\*) do ( + @if exist "%%M\MSBuild\Current\Bin\MSBuild.exe" ( + @set "MSBUILD=%%M\MSBuild\Current\Bin\MSBuild.exe" ) ) @if "%MSBUILD%" == "" ( - @echo Could not find VS2017 MSBuild + @echo Could not find VS2019 MSBuild @exit /b 1 ) @del ICSharpCode.Decompiler\bin\Release\*.nupkg diff --git a/releasebuild.bat b/releasebuild.bat index a753390fa..cd881dd67 100644 --- a/releasebuild.bat +++ b/releasebuild.bat @@ -1,12 +1,12 @@ @setlocal enabledelayedexpansion @set MSBUILD= -@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2017"\*) do ( - @if exist "%%M\MSBuild\15.0\Bin\MSBuild.exe" ( - @set "MSBUILD=%%M\MSBuild\15.0\Bin\MSBuild.exe" +@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2019"\*) do ( + @if exist "%%M\MSBuild\Current\Bin\MSBuild.exe" ( + @set "MSBUILD=%%M\MSBuild\Current\Bin\MSBuild.exe" ) ) @if "%MSBUILD%" == "" ( - @echo Could not find VS2017 MSBuild + @echo Could not find VS2019 MSBuild @exit /b 1 ) "%MSBUILD%" ILSpy.sln /p:Configuration=Release "/p:Platform=Any CPU"