Browse Source

Merge branch 'newdecompiler' of https://github.com/icsharpcode/ILSpy into netstd-support

# Conflicts:
#	ILSpy/ILSpy.csproj
pull/848/head
Siegfried Pammer 8 years ago
parent
commit
e74d7e886e
  1. 4
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 50
      ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
  3. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  4. 4
      ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs
  5. 19
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/InitializerTests.cs
  6. 19
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/OverloadResolution.cs
  7. 11
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue379.cs
  8. 31
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue379.il
  9. 184
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.cs
  10. 4742
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.il
  11. 3967
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.opt.il
  12. 4471
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.opt.roslyn.il
  13. 4636
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.roslyn.il
  14. 5
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  15. 109
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  16. 45
      ICSharpCode.Decompiler/CSharp/Transforms/AddXmlDocumentationTransform.cs
  17. 15
      ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs
  18. 2
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
  19. 2
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs
  20. 22
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs
  21. 2
      ICSharpCode.Decompiler/DecompilerSettings.cs
  22. 2
      ICSharpCode.Decompiler/Documentation/XmlDocKeyProvider.cs
  23. 3
      ICSharpCode.Decompiler/Documentation/XmlDocLoader.cs
  24. 5
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  25. 193
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs
  26. 37
      ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs
  27. 2
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  28. 13
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  29. 5
      ILSpy/ILSpy.csproj
  30. 4
      ILSpy/Languages/CSharpLanguage.cs
  31. 2
      ILSpy/MainWindow.xaml.cs
  32. 1
      ILSpy/TextView/DecompilerTextView.cs
  33. 2
      ILSpy/TextView/XmlDocRenderer.cs

4
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -54,7 +54,9 @@ @@ -54,7 +54,9 @@
<Compile Include="Helpers\RemoveCompilerAttribute.cs" />
<Compile Include="Helpers\Tester.cs" />
<Compile Include="Helpers\TypeSystemHelper.cs" />
<Compile Include="ILPrettyTestRunner.cs" />
<Compile Include="Stub.cs" />
<Compile Include="TestCases\ILPretty\Issue379.cs" />
<Compile Include="TestCases\Pretty\LiftedOperators.cs" />
<Compile Include="PrettyTestRunner.cs" />
<Compile Include="RoundtripAssembly.cs" />
@ -86,6 +88,7 @@ @@ -86,6 +88,7 @@
<Compile Include="TestCases\Pretty\HelloWorld.cs" />
<Compile Include="TestCases\Pretty\InlineAssignmentTest.cs" />
<Compile Include="TestCases\Pretty\PropertiesAndEvents.cs" />
<Compile Include="TestCases\Pretty\QueryExpressions.cs" />
<Compile Include="TestCases\Pretty\ShortCircuit.cs" />
<Compile Include="TestTraceListener.cs" />
<Compile Include="Util\IntervalTests.cs" />
@ -108,6 +111,7 @@ @@ -108,6 +111,7 @@
<None Include="TestCases\Pretty\InlineAssignmentTest.il">
<DependentUpon>InlineAssignmentTest.cs</DependentUpon>
</None>
<None Include="TestCases\ILPretty\Issue379.il" />
<None Include="TestCases\Pretty\Readme.txt" />
</ItemGroup>

50
ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs

@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using ICSharpCode.Decompiler.Tests.Helpers;
using NUnit.Framework;
namespace ICSharpCode.Decompiler.Tests
{
public class ILPrettyTestRunner
{
const string TestCasePath = @"../../../../ICSharpCode.Decompiler.Tests/TestCases/ILPretty";
[Test]
public void AllFilesHaveTests()
{
var testNames = typeof(ILPrettyTestRunner).GetMethods()
.Where(m => m.GetCustomAttributes(typeof(TestAttribute), false).Any())
.Select(m => m.Name)
.ToArray();
foreach (var file in new DirectoryInfo(TestCasePath).EnumerateFiles()) {
if (file.Extension.Equals(".il", StringComparison.OrdinalIgnoreCase)) {
var testName = file.Name.Split('.')[0];
Assert.Contains(testName, testNames);
Assert.IsTrue(File.Exists(Path.Combine(TestCasePath, testName + ".cs")));
}
}
}
[Test, Ignore("Need to decide how to represent virtual methods without 'newslot' flag")]
public void Issue379()
{
Run();
}
void Run([CallerMemberName] string testName = null)
{
var ilFile = Path.Combine(TestCasePath, testName + ".il");
var csFile = Path.Combine(TestCasePath, testName + ".cs");
var executable = Tester.AssembleIL(ilFile, AssemblerOptions.Library);
var decompiled = Tester.DecompileCSharp(executable);
CodeAssert.FilesAreEqual(csFile, decompiled);
}
}
}

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -133,6 +133,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -133,6 +133,12 @@ namespace ICSharpCode.Decompiler.Tests
Run(cscOptions: cscOptions);
}
[Test]
public void QueryExpressions([ValueSource("defaultOptions")] CompilerOptions cscOptions)
{
Run(cscOptions: cscOptions);
}
void Run([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None)
{
var ilFile = Path.Combine(TestCasePath, testName);

4
ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs

@ -42,9 +42,9 @@ namespace ICSharpCode.Decompiler.Tests @@ -42,9 +42,9 @@ namespace ICSharpCode.Decompiler.Tests
}
[Test]
public void NewtonsoftJson_net40()
public void NewtonsoftJson_net45()
{
RunWithTest("Newtonsoft.Json-net40", "Newtonsoft.Json.dll", "Newtonsoft.Json.Tests.dll");
RunWithTest("Newtonsoft.Json-net45", "Newtonsoft.Json.dll", "Newtonsoft.Json.Tests.dll");
}
[Test]

19
ICSharpCode.Decompiler.Tests/TestCases/Correctness/InitializerTests.cs

@ -18,6 +18,9 @@ @@ -18,6 +18,9 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
@ -662,6 +665,22 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -662,6 +665,22 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
});
}
public static void Bug270_NestedInitialisers()
{
NumberFormatInfo[] numberFormats = null;
Thread t = new Thread(Bug270_NestedInitialisers) {
Priority = ThreadPriority.BelowNormal,
CurrentCulture = new CultureInfo(0) {
DateTimeFormat = new DateTimeFormatInfo {
ShortDatePattern = "ddmmyy"
},
NumberFormat = (from format in numberFormats where format.CurrencySymbol == "$" select format).First()
}
};
}
public int[,] MultidimensionalInit()
{
return new int[,]

19
ICSharpCode.Decompiler.Tests/TestCases/Correctness/OverloadResolution.cs

@ -33,6 +33,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -33,6 +33,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
TestIssue180();
TestExtensionMethod();
TestParamsMethod();
Generics();
}
#region params with nulls
@ -132,5 +133,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -132,5 +133,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
Console.WriteLine("ExtensionMethod(obj)");
}
#endregion
#region Generics
static void Generics()
{
GenericsTest<int>(null);
GenericsTest<long>((object)null);
}
static void GenericsTest<T>(string x) where T : struct
{
Console.WriteLine("GenericsTest<" + typeof(T).Name + ">(string: " + x + ");");
}
static void GenericsTest<T>(object x) where T : struct
{
Console.WriteLine("GenericsTest<" + typeof(T).Name + ">(object: " + x + ");");
}
#endregion
}
}

11
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue379.cs

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
{
internal class Issue379
{
public virtual void Test<T>() where T : new()
{
}
}
}

31
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue379.il

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly ConsoleApp11
{
.ver 1:0:0:0
}
.module ConsoleApp11.exe
// MVID: {B973FCD6-A9C4-48A9-8291-26DDC248E208}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00020003 // ILONLY 32BITPREFERRED
// Image base: 0x000001C4B6C90000
.class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue379
extends [mscorlib]System.Object
{
.method public hidebysig virtual
instance void Test<.ctor T>() cil managed
{
// Code size 2 (0x2)
.maxstack 8
IL_0000: nop
IL_0001: ret
} // end of method Program::Test
}

184
ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.cs

@ -0,0 +1,184 @@ @@ -0,0 +1,184 @@
// 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 ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class QueryExpressions
{
public class Customer
{
public int CustomerID;
public IEnumerable<Order> Orders;
public string Name;
public string Country;
public string City;
}
public class Order
{
public int OrderID;
public DateTime OrderDate;
public Customer Customer;
public int CustomerID;
public decimal Total;
public IEnumerable<OrderDetail> Details;
}
public class OrderDetail
{
public decimal UnitPrice;
public int Quantity;
}
public IEnumerable<Customer> customers;
public IEnumerable<Order> orders;
public object MultipleWhere()
{
return from c in this.customers
where c.Orders.Count() > 10
where c.Country == "DE"
select c;
}
public object SelectManyFollowedBySelect()
{
return from c in this.customers
from o in c.Orders
select new {
c.Name,
o.OrderID,
o.Total
};
}
public object SelectManyFollowedByOrderBy()
{
return from c in this.customers
from o in c.Orders
orderby o.Total descending
select new {
c.Name,
o.OrderID,
o.Total
};
}
public object MultipleSelectManyFollowedBySelect()
{
return from c in this.customers
from o in c.Orders
from d in o.Details
select new {
c.Name,
o.OrderID,
d.Quantity
};
}
public object MultipleSelectManyFollowedByLet()
{
return from c in this.customers
from o in c.Orders
from d in o.Details
let x = d.Quantity * d.UnitPrice
select new {
c.Name,
o.OrderID,
x
};
}
public object FromLetWhereSelect()
{
return from o in this.orders
let t = o.Details.Sum((Func<OrderDetail, decimal>)((OrderDetail d) => d.UnitPrice * d.Quantity))
where t >= 1000m
select new {
OrderID = o.OrderID,
Total = t
};
}
public object MultipleLet()
{
return from a in this.customers
let b = a.Country
let c = a.Name
select b + c;
}
public object Join()
{
return from c in this.customers
join o in this.orders on c.CustomerID equals o.CustomerID
select new {
c.Name,
o.OrderDate,
o.Total
};
}
public object JoinInto()
{
return from c in this.customers
join o in this.orders on c.CustomerID equals o.CustomerID into co
let n = co.Count()
// should be n >= 10
where !(n < 10)
select new {
Name = c.Name,
OrderCount = n
};
}
public object OrderBy()
{
return from o in this.orders
orderby o.Customer.Name, o.Total descending
select o;
}
public object GroupBy()
{
return from c in this.customers
group c.Name by c.Country;
}
public object ExplicitType()
{
return from Customer c in this.customers
where c.City == "London"
select c;
}
public object QueryContinuation()
{
return from c in this.customers
group c by c.Country into g
select new {
Country = g.Key,
CustCount = g.Count()
};
}
}
}

4742
ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.il

File diff suppressed because it is too large Load Diff

3967
ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.opt.il

File diff suppressed because it is too large Load Diff

4471
ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.opt.roslyn.il

File diff suppressed because it is too large Load Diff

4636
ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.roslyn.il

File diff suppressed because it is too large Load Diff

5
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -133,10 +133,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -133,10 +133,11 @@ namespace ICSharpCode.Decompiler.CSharp
new DecimalConstantTransform(),
new IntroduceUsingDeclarations(),
new IntroduceExtensionMethods(), // must run after IntroduceUsingDeclarations
//new IntroduceQueryExpressions(context), // must run after IntroduceExtensionMethods
//new CombineQueryExpressions(context),
new IntroduceQueryExpressions(), // must run after IntroduceExtensionMethods
new CombineQueryExpressions(),
//new FlattenSwitchBlocks(),
new FixNameCollisions(),
new AddXmlDocumentationTransform(),
};
public CancellationToken CancellationToken { get; set; }

109
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1136,55 +1136,54 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1136,55 +1136,54 @@ namespace ICSharpCode.Decompiler.CSharp
if (method.IsAccessor && (method.AccessorOwner.SymbolKind == SymbolKind.Indexer || method.Parameters.Count == allowedParamCount)) {
expr = HandleAccessorCall(inst, target, method, arguments.ToList());
} else {
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly);
var result = lookup.Lookup(target.ResolveResult, method.Name, EmptyList<IType>.Instance, true) as MethodGroupResolveResult;
// 1.
// Try overload resolution and check if the correct call is selected with the given casts.
// If anything goes wrong, apply explicit casts to all arguments.
if (result == null) {
for (int i = 0; i < arguments.Length; i++) {
if (!method.Parameters[i].Type.ContainsAnonymousType())
arguments[i] = arguments[i].ConvertTo(method.Parameters[i].Type, this);
}
} else {
var or = new OverloadResolution(resolver.Compilation, arguments.Select(a => a.ResolveResult).ToArray());
or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());
if (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(method, or.BestCandidate, inst.OpCode == OpCode.CallVirt)) {
for (int i = 0; i < arguments.Length; i++) {
if (!method.Parameters[i].Type.ContainsAnonymousType())
arguments[i] = arguments[i].ConvertTo(method.Parameters[i].Type, this);
}
}
}
result = lookup.Lookup(target.ResolveResult, method.Name, EmptyList<IType>.Instance, true) as MethodGroupResolveResult;
bool requireTypeArguments = false;
bool targetCasted = false;
bool argumentsCasted = false;
IType[] typeArguments = Array.Empty<IType>();
// 2.
// Try overload resolution again and if anything goes wrong,
// fix the call by explicitly casting to method.DeclaringType.
if (result == null) {
target = target.ConvertTo(method.DeclaringType, this);
} else {
var or = new OverloadResolution(resolver.Compilation, arguments.Select(a => a.ResolveResult).ToArray());
OverloadResolutionErrors IsUnambiguousCall()
{
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly);
var result = lookup.Lookup(target.ResolveResult, method.Name, EmptyList<IType>.Instance, true) as MethodGroupResolveResult;
if (result == null)
return OverloadResolutionErrors.AmbiguousMatch;
var or = new OverloadResolution(resolver.Compilation, arguments.SelectArray(a => a.ResolveResult), typeArguments: typeArguments);
or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());
if (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(method, or.BestCandidate, inst.OpCode == OpCode.CallVirt))
target = target.ConvertTo(method.DeclaringType, this);
if (or.BestCandidateErrors != OverloadResolutionErrors.None)
return or.BestCandidateErrors;
if (!IsAppropriateCallTarget(method, or.GetBestCandidateWithSubstitutedTypeArguments(), inst.OpCode == OpCode.CallVirt))
return OverloadResolutionErrors.AmbiguousMatch;
return OverloadResolutionErrors.None;
}
bool requireTypeArguments = false;
result = lookup.Lookup(target.ResolveResult, method.Name, EmptyList<IType>.Instance, true) as MethodGroupResolveResult;
// 3.
// Try overload resolution again and if anything goes wrong,
// fix the call by explicitly stating the type arguments.
if (result == null) {
requireTypeArguments = true;
} else {
var or = new OverloadResolution(resolver.Compilation, arguments.Select(a => a.ResolveResult).ToArray());
or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());
if (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(method, or.BestCandidate, inst.OpCode == OpCode.CallVirt))
requireTypeArguments = true;
OverloadResolutionErrors errors;
while ((errors = IsUnambiguousCall()) != OverloadResolutionErrors.None) {
switch (errors) {
case OverloadResolutionErrors.TypeInferenceFailed:
case OverloadResolutionErrors.WrongNumberOfTypeArguments:
if (requireTypeArguments) goto default;
requireTypeArguments = true;
typeArguments = method.TypeArguments.ToArray();
continue;
default:
if (!argumentsCasted) {
argumentsCasted = true;
for (int i = 0; i < arguments.Length; i++) {
if (!method.Parameters[i].Type.ContainsAnonymousType())
arguments[i] = arguments[i].ConvertTo(method.Parameters[i].Type, this);
}
} else if (!targetCasted) {
targetCasted = true;
target = target.ConvertTo(method.DeclaringType, this);
} else if (!requireTypeArguments) {
requireTypeArguments = true;
typeArguments = method.TypeArguments.ToArray();
} else {
break;
}
continue;
}
break;
}
Expression targetExpr = target.Expression;
@ -1732,10 +1731,26 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1732,10 +1731,26 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitAwait(Await inst, TranslationContext context)
{
var value = Translate(inst.Value);
IType expectedType = null;
if (inst.GetAwaiterMethod != null) {
if (inst.GetAwaiterMethod.IsStatic) {
expectedType = inst.GetAwaiterMethod.Parameters.FirstOrDefault()?.Type;
} else {
expectedType = inst.GetAwaiterMethod.DeclaringType;
}
}
var value = Translate(inst.Value, typeHint: expectedType);
if (value.Expression is DirectionExpression) {
// we can deference the managed reference by stripping away the 'ref'
value = value.UnwrapChild(((DirectionExpression)value.Expression).Expression);
}
if (expectedType != null) {
value = value.ConvertTo(expectedType, this, allowImplicitConversion: true);
}
return new UnaryOperatorExpression(UnaryOperatorType.Await, value.Expression)
.WithILInstruction(inst)
.WithRR(new ResolveResult(inst?.GetResultMethod.ReturnType ?? SpecialType.UnknownType));
.WithRR(new ResolveResult(inst.GetResultMethod?.ReturnType ?? SpecialType.UnknownType));
}
protected internal override TranslatedExpression VisitInvalidBranch(InvalidBranch inst, TranslationContext context)

45
ILSpy/XmlDoc/AddXmlDocTransform.cs → ICSharpCode.Decompiler/CSharp/Transforms/AddXmlDocumentationTransform.cs

@ -18,34 +18,43 @@ @@ -18,34 +18,43 @@
using System;
using System.IO;
using System.Linq;
using ICSharpCode.Decompiler.CSharp.Syntax;
using Mono.Cecil;
using ICSharpCode.Decompiler.Documentation;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.ILSpy.XmlDoc
namespace ICSharpCode.Decompiler.CSharp.Transforms
{
/// <summary>
/// Adds XML documentation for member definitions.
/// </summary>
static class AddXmlDocTransform
public class AddXmlDocumentationTransform : IAstTransform
{
public static void Run(AstNode node)
public void Run(AstNode rootNode, TransformContext context)
{
if (node is EntityDeclaration) {
MemberReference mr = node.Annotation<MemberReference>();
if (mr != null && mr.Module != null) {
var xmldoc = XmlDocLoader.LoadDocumentation(mr.Module);
if (xmldoc != null) {
string doc = xmldoc.GetDocumentation(XmlDocKeyProvider.GetKey(mr));
if (doc != null) {
InsertXmlDocumentation(node, new StringReader(doc));
}
}
if (!context.Settings.ShowXmlDocumentation)
return;
var xmldoc = XmlDocLoader.LoadDocumentation(context.TypeSystem.ModuleDefinition);
if (xmldoc == null)
return;
foreach (var entity in rootNode.DescendantsAndSelf.OfType<EntityDeclaration>()) {
var symbol = entity.GetSymbol();
Mono.Cecil.MemberReference mr;
switch (symbol) {
case IMember member:
mr = context.TypeSystem.GetCecil(member);
break;
case IType type:
mr = context.TypeSystem.GetCecil(type.GetDefinition());
break;
default:
continue;
}
string doc = xmldoc.GetDocumentation(XmlDocKeyProvider.GetKey(mr));
if (doc != null) {
InsertXmlDocumentation(entity, new StringReader(doc));
}
if (!(node is TypeDeclaration))
return; // don't recurse into attributed nodes, except for type definitions
}
foreach (AstNode child in node.Children)
Run(child);
}
static void InsertXmlDocumentation(AstNode node, StringReader r)

15
ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs

@ -18,8 +18,8 @@ @@ -18,8 +18,8 @@
using System;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
namespace ICSharpCode.Decompiler.CSharp.Transforms
{
@ -28,18 +28,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -28,18 +28,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// </summary>
public class CombineQueryExpressions : IAstTransform
{
readonly DecompilerContext context;
public CombineQueryExpressions(DecompilerContext context)
{
this.context = context;
}
public void Run(AstNode compilationUnit)
public void Run(AstNode rootNode, TransformContext context)
{
if (!context.Settings.QueryExpressions)
return;
CombineQueries(compilationUnit);
CombineQueries(rootNode);
}
static readonly InvocationExpression castPattern = new InvocationExpression {

2
ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs

@ -361,7 +361,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -361,7 +361,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (v.Type.Kind == TypeKind.ByReference) {
expectedExpr = new DirectionExpression(FieldDirection.Ref, expectedExpr);
}
if (assignment != null && assignment.Left.IsMatch(expectedExpr)) {
if (assignment != null && assignment.Operator == AssignmentOperatorType.Assign && assignment.Left.IsMatch(expectedExpr)) {
AstType type;
if (v.Type.ContainsAnonymousType()) {
type = new SimpleType("var");

2
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs

@ -128,7 +128,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -128,7 +128,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
var firstArgument = invocationExpression.Arguments.First();
var target = firstArgument.GetResolveResult();
var args = invocationExpression.Arguments.Skip(1).Select(a => a.GetResolveResult()).ToArray();
var rr = resolver.ResolveMemberAccess(target, method.Name, EmptyList<IType>.Instance, NameLookupMode.InvocationTarget) as MethodGroupResolveResult;
var rr = resolver.ResolveMemberAccess(target, method.Name, method.TypeArguments, NameLookupMode.InvocationTarget) as MethodGroupResolveResult;
if (rr == null)
return;
var or = rr.PerformOverloadResolution(resolveContext.Compilation, args, allowExtensionMethods: true);

22
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
using System;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.Decompiler.CSharp.Syntax;
namespace ICSharpCode.Decompiler.CSharp.Transforms
{
@ -29,21 +29,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -29,21 +29,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// </summary>
public class IntroduceQueryExpressions : IAstTransform
{
readonly DecompilerContext context;
public IntroduceQueryExpressions(DecompilerContext context)
{
this.context = context;
}
public void Run(AstNode compilationUnit)
public void Run(AstNode rootNode, TransformContext context)
{
if (!context.Settings.QueryExpressions)
return;
DecompileQueries(compilationUnit);
DecompileQueries(rootNode);
// After all queries were decompiled, detect degenerate queries (queries not property terminated with 'select' or 'group')
// and fix them, either by adding a degenerate select, or by combining them with another query.
foreach (QueryExpression query in compilationUnit.Descendants.OfType<QueryExpression>()) {
foreach (QueryExpression query in rootNode.Descendants.OfType<QueryExpression>()) {
QueryFromClause fromClause = (QueryFromClause)query.Clauses.First();
if (IsDegenerateQuery(query)) {
// introduce select for degenerate query
@ -278,7 +271,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -278,7 +271,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// <summary>Matches simple lambdas of the form "a => b"</summary>
bool MatchSimpleLambda(Expression expr, out string parameterName, out Expression body)
{
LambdaExpression lambda = expr as LambdaExpression;
// HACK : remove workaround after all unnecessary casts are eliminated.
LambdaExpression lambda;
if (expr is CastExpression cast)
lambda = cast.Expression as LambdaExpression;
else
lambda = expr as LambdaExpression;
if (lambda != null && lambda.Parameters.Count == 1 && lambda.Body is Expression) {
ParameterDeclaration p = lambda.Parameters.Single();
if (p.ParameterModifier == ParameterModifier.None) {

2
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -72,7 +72,7 @@ namespace ICSharpCode.Decompiler @@ -72,7 +72,7 @@ namespace ICSharpCode.Decompiler
}
}
bool asyncAwait = false;
bool asyncAwait = true;
/// <summary>
/// Decompile async methods.

2
ILSpy/XmlDoc/XmlDocKeyProvider.cs → ICSharpCode.Decompiler/Documentation/XmlDocKeyProvider.cs

@ -27,7 +27,7 @@ using ArrayType = Mono.Cecil.ArrayType; @@ -27,7 +27,7 @@ using ArrayType = Mono.Cecil.ArrayType;
using ByReferenceType = Mono.Cecil.ByReferenceType;
using PointerType = Mono.Cecil.PointerType;
namespace ICSharpCode.ILSpy.XmlDoc
namespace ICSharpCode.Decompiler.Documentation
{
/// <summary>
/// Provides XML documentation tags.

3
ILSpy/XmlDoc/XmlDocLoader.cs → ICSharpCode.Decompiler/Documentation/XmlDocLoader.cs

@ -23,7 +23,7 @@ using System.Runtime.CompilerServices; @@ -23,7 +23,7 @@ using System.Runtime.CompilerServices;
using ICSharpCode.Decompiler.Documentation;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.XmlDoc
namespace ICSharpCode.Decompiler.Documentation
{
/// <summary>
/// Helps finding and loading .xml documentation.
@ -62,6 +62,7 @@ namespace ICSharpCode.ILSpy.XmlDoc @@ -62,6 +62,7 @@ namespace ICSharpCode.ILSpy.XmlDoc
xmlDoc = new XmlDocumentationProvider(xmlDocFile);
cache.Add(module, xmlDoc);
} else {
cache.Add(module, null); // add missing documentation files as well
xmlDoc = null;
}
}

5
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -219,7 +219,9 @@ @@ -219,7 +219,9 @@
<Compile Include="CSharp\Resolver\ReducedExtensionMethod.cs" />
<Compile Include="CSharp\Resolver\RenameCallbackArguments.cs" />
<Compile Include="CSharp\Resolver\TypeInference.cs" />
<Compile Include="CSharp\Transforms\CombineQueryExpressions.cs" />
<Compile Include="CSharp\Transforms\IntroduceExtensionMethods.cs" />
<Compile Include="CSharp\Transforms\IntroduceQueryExpressions.cs" />
<Compile Include="CSharp\Transforms\RemoveCLSCompliantAttribute.cs" />
<Compile Include="CSharp\TranslationContext.cs" />
<Compile Include="CSharp\TypeSystem\AliasNamespaceReference.cs" />
@ -254,9 +256,12 @@ @@ -254,9 +256,12 @@
<Compile Include="CSharp\Transforms\IntroduceUnsafeModifier.cs" />
<Compile Include="CSharp\Transforms\ReplaceMethodCallsWithOperators.cs" />
<Compile Include="CSharp\WholeProjectDecompiler.cs" />
<Compile Include="CSharp\Transforms\AddXmlDocumentationTransform.cs" />
<Compile Include="Documentation\GetPotentiallyNestedClassTypeReference.cs" />
<Compile Include="Documentation\IdStringMemberReference.cs" />
<Compile Include="Documentation\IdStringProvider.cs" />
<Compile Include="Documentation\XmlDocKeyProvider.cs" />
<Compile Include="Documentation\XmlDocLoader.cs" />
<Compile Include="Documentation\XmlDocumentationProvider.cs" />
<Compile Include="DotNetCore\DotNetCorePathFinder.cs" />
<Compile Include="DotNetCore\DotNetCorePathFinderExtensions.cs" />

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

@ -47,6 +47,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -47,6 +47,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
IField stateField;
int initialState;
Dictionary<IField, ILVariable> fieldToParameterMap = new Dictionary<IField, ILVariable>();
Dictionary<ILVariable, ILVariable> cachedFieldToParameterMap = new Dictionary<ILVariable, ILVariable>();
// These fields are set by AnalyzeMoveNext():
ILFunction moveNextFunction;
@ -58,6 +59,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -58,6 +59,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
ILVariable doFinallyBodies;
// These fields are set by AnalyzeStateMachine():
int smallestAwaiterVarIndex;
// For each block containing an 'await', stores the awaiter variable, and the field storing the awaiter
// across the yield point.
@ -69,6 +71,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -69,6 +71,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return; // abort if async/await decompilation is disabled
this.context = context;
fieldToParameterMap.Clear();
cachedFieldToParameterMap.Clear();
awaitBlocks.Clear();
if (!MatchTaskCreationPattern(function))
return;
@ -80,17 +83,16 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -80,17 +83,16 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
InlineBodyOfMoveNext(function);
// Copy-propagate temporaries holding a copy of 'this'.
foreach (var stloc in function.Descendants.OfType<StLoc>().Where(s => s.Variable.IsSingleDefinition && s.Value.MatchLdThis()).ToList()) {
CopyPropagation.Propagate(stloc, context);
}
CleanUpBodyOfMoveNext(function);
AnalyzeStateMachine(function);
DetectAwaitPattern(function);
CleanDoFinallyBodies(function);
context.Step("Translate fields to local accesses", function);
MarkGeneratedVariables(function);
YieldReturnDecompiler.TranslateFieldsToLocalAccess(function, function, fieldToParameterMap);
TranslateCachedFieldsToLocals();
FinalizeInlineMoveNext(function);
@ -99,6 +101,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -99,6 +101,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
function.RunTransforms(CSharpDecompiler.EarlyILTransforms(), context);
}
private void CleanUpBodyOfMoveNext(ILFunction function)
{
context.StepStartGroup("CleanUpBodyOfMoveNext", function);
// Simplify stobj(ldloca) -> stloc
foreach (var stobj in function.Descendants.OfType<StObj>()) {
ExpressionTransforms.StObjToStLoc(stobj, context);
}
// Copy-propagate temporaries holding a copy of 'this'.
foreach (var stloc in function.Descendants.OfType<StLoc>().Where(s => s.Variable.IsSingleDefinition && s.Value.MatchLdThis()).ToList()) {
CopyPropagation.Propagate(stloc, context);
}
new RemoveDeadVariableInit().Run(function, context);
// Run inlining, but don't remove dead variables (they might get revived by TranslateFieldsToLocalAccess)
foreach (var block in function.Descendants.OfType<Block>()) {
ILInlining.InlineAllInBlock(block, context);
}
context.StepEndGroup();
}
#region MatchTaskCreationPattern
bool MatchTaskCreationPattern(ILFunction function)
{
@ -290,7 +312,22 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -290,7 +312,22 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
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))
throw new SymbolicAnalysisFailedException();
cachedFieldToParameterMap[stloc.Variable] = param;
pos++;
}
mainTryCatch = blockContainer.EntryPoint.Instructions[pos] as TryCatch;
if (mainTryCatch == null)
throw new SymbolicAnalysisFailedException();
// CatchHandler will be validated in ValidateCatchBlock()
if (((BlockContainer)mainTryCatch.TryBlock).EntryPoint.Instructions[0] is StLoc initDoFinallyBodies
@ -444,6 +481,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -444,6 +481,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
void AnalyzeStateMachine(ILFunction function)
{
context.Step("AnalyzeStateMachine()", function);
smallestAwaiterVarIndex = int.MaxValue;
foreach (var container in function.Descendants.OfType<BlockContainer>()) {
// Use a separate state range analysis per container.
var sra = new StateRangeAnalysis(StateRangeAnalysisMode.AsyncMoveNext, stateField, cachedStateVar);
@ -464,6 +502,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -464,6 +502,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
block.Instructions.Add(new InvalidBranch("Could not find block for state " + state));
}
awaitBlocks.Add(block, (awaiterVar, awaiterField));
if (awaiterVar.Index < smallestAwaiterVarIndex) {
smallestAwaiterVarIndex = awaiterVar.Index;
}
}
}
}
@ -626,9 +667,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -626,9 +667,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return;
if (awaitBlockData.awaiterVar != awaiterVar)
return;
if (!CheckAwaitBlock(awaitBlock, out var resumeBlock))
if (!CheckAwaitBlock(awaitBlock, out var resumeBlock, out var stackField))
return;
if (!CheckResumeBlock(resumeBlock, awaiterVar, awaitBlockData.awaiterField, completedBlock))
if (!CheckResumeBlock(resumeBlock, awaiterVar, awaitBlockData.awaiterField, completedBlock, stackField))
return;
// Check completedBlock:
ILInstruction getResultCall;
@ -663,25 +704,38 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -663,25 +704,38 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
}
}
bool CheckAwaitBlock(Block block, out Block resumeBlock)
bool CheckAwaitBlock(Block block, out Block resumeBlock, out IField stackField)
{
// awaitBlock:
// (pre-roslyn: save stack)
// await(ldloca V_2)
// br resumeBlock
resumeBlock = null;
if (block.Instructions.Count != 2)
stackField = null;
if (block.Instructions.Count < 2)
return false;
if (block.Instructions[0].OpCode != OpCode.Await)
return false;
if (!block.Instructions[1].MatchBranch(out resumeBlock))
int pos = 0;
if (block.Instructions[pos] is StLoc stloc && stloc.Variable.IsSingleDefinition) {
if (!block.Instructions[pos + 1].MatchStFld(out var target, out stackField, out var value))
return false;
if (!target.MatchLdThis())
return false;
pos += 2;
}
// await(ldloca awaiterVar)
if (block.Instructions[pos].OpCode != OpCode.Await)
return false;
return true;
// br resumeBlock
return block.Instructions[pos + 1].MatchBranch(out resumeBlock);
}
bool CheckResumeBlock(Block block, ILVariable awaiterVar, IField awaiterField, Block completedBlock)
bool CheckResumeBlock(Block block, ILVariable awaiterVar, IField awaiterField, Block completedBlock, IField stackField)
{
int pos = 0;
if (!RestoreStack(block, ref pos, stackField))
return false;
// stloc awaiterVar(ldfld awaiterField(ldloc this))
if (!block.Instructions[pos].MatchStLoc(awaiterVar, out var value))
return false;
@ -711,7 +765,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -711,7 +765,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
pos++;
}
if (block.Instructions[pos] is StLoc stlocCachedState) {
if (stlocCachedState.Variable.Kind == VariableKind.Local && stlocCachedState.Variable.Index == cachedStateVar.Index) {
if (stlocCachedState.Variable.Kind == VariableKind.Local && stlocCachedState.Variable.Index == cachedStateVar?.Index) {
if (stlocCachedState.Value.MatchLdLoc(m1Var) || stlocCachedState.Value.MatchLdcI4(initialState))
pos++;
}
@ -730,6 +784,113 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -730,6 +784,113 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return block.Instructions[pos].MatchBranch(completedBlock);
}
private bool RestoreStack(Block block, ref int pos, IField stackField)
{
if (stackField == null) {
return true; // nothing to restore
}
// stloc temp(unbox.any T(ldfld <>t__stack(ldloc this)))
if (!(block.Instructions[pos] is StLoc stloc))
return false;
if (!stloc.Variable.IsSingleDefinition)
return false;
if (!(stloc.Value is UnboxAny unbox))
return false;
if (!unbox.Argument.MatchLdFld(out var target, out var field))
return false;
if (!target.MatchLdThis())
return false;
if (!field.Equals(stackField))
return false;
pos++;
// restoring stack slots
while (block.Instructions[pos].MatchStLoc(out var v) && v.Kind == VariableKind.StackSlot) {
pos++;
}
// stfld <>t__stack(ldloc this, ldnull)
if (block.Instructions[pos].MatchStFld(out target, out field, out var value)) {
if (target.MatchLdThis() && field.Equals(stackField) && value.MatchLdNull()) {
pos++;
}
}
return true;
}
#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;
}
}
}
/// <summary>
/// Eliminates usage of doFinallyBodies
/// </summary>
private void CleanDoFinallyBodies(ILFunction function)
{
if (doFinallyBodies == null) {
return; // roslyn-compiled code doesn't use doFinallyBodies
}
context.StepStartGroup("CleanDoFinallyBodies", function);
Block entryPoint = GetBodyEntryPoint(function.Body as BlockContainer);
if (entryPoint != null && entryPoint.Instructions[0].MatchStLoc(doFinallyBodies, out var value) && value.MatchLdcI4(1)) {
// Remove initial doFinallyBodies assignment, if it wasn't already removed when
// we rearranged the control flow.
entryPoint.Instructions.RemoveAt(0);
}
if (doFinallyBodies.StoreInstructions.Count != 0 || doFinallyBodies.AddressCount != 0) {
// misdetected another variable as doFinallyBodies?
// reintroduce the initial store of ldc.i4(1)
context.Step("Re-introduce misdetected doFinallyBodies", function);
((BlockContainer)function.Body).EntryPoint.Instructions.Insert(0,
new StLoc(doFinallyBodies, new LdcI4(1)));
return;
}
foreach (var tryFinally in function.Descendants.OfType<TryFinally>()) {
entryPoint = GetBodyEntryPoint(tryFinally.FinallyBlock as BlockContainer);
if (entryPoint?.Instructions[0] is IfInstruction ifInst) {
if (ifInst.Condition is LogicNot logicNot && logicNot.Argument.MatchLdLoc(doFinallyBodies)) {
context.Step("Remove if(doFinallyBodies) from try-finally", tryFinally);
// condition will always be false now that we're using 'await' instructions
entryPoint.Instructions.RemoveAt(0);
}
}
}
// if there's any remaining loads (there shouldn't be), replace them with the constant 1
foreach (LdLoc load in doFinallyBodies.LoadInstructions) {
load.ReplaceWith(new LdcI4(1) { ILRange = load.ILRange });
}
context.StepEndGroup(keepIfEmpty: true);
}
Block GetBodyEntryPoint(BlockContainer body)
{
if (body == null)
return null;
Block entryPoint = body.EntryPoint;
while (entryPoint.Instructions[0].MatchBranch(out var targetBlock) && targetBlock.IncomingEdgeCount == 1) {
entryPoint = targetBlock;
}
return entryPoint;
}
void TranslateCachedFieldsToLocals()
{
foreach (var (cachedVar, param) in cachedFieldToParameterMap) {
Debug.Assert(cachedVar.StoreCount <= 1);
foreach (var inst in cachedVar.LoadInstructions.ToArray()) {
inst.Variable = param;
}
foreach (var inst in cachedVar.AddressInstructions.ToArray()) {
inst.Variable = param;
}
}
}
}
}

37
ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs

@ -82,6 +82,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -82,6 +82,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
void SimplifyBranchChains(ILFunction function, ILTransformContext context)
{
List<(BlockContainer, Block)> blocksToAdd = new List<(BlockContainer, Block)>();
HashSet<Block> visitedBlocks = new HashSet<Block>();
foreach (var branch in function.Descendants.OfType<Branch>()) {
// Resolve chained branches to the final target:
@ -100,10 +101,24 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -100,10 +101,24 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
targetBlock.Instructions.Clear(); // mark the block for deletion
targetBlock = branch.TargetBlock;
}
if (ShouldSimplifyBranchToReturnBlock(branch)) {
// Replace branches to 'return blocks' with the return instruction
context.Step("Replace branch to return with return", branch);
branch.ReplaceWith(targetBlock.Instructions[0].Clone());
if (IsBranchToReturnBlock(branch)) {
if (aggressivelyDuplicateReturnBlocks) {
// Replace branches to 'return blocks' with the return instruction
context.Step("Replace branch to return with return", branch);
branch.ReplaceWith(targetBlock.Instructions[0].Clone());
} else if (branch.TargetContainer != branch.Ancestors.OfType<BlockContainer>().First()) {
// We don't want to always inline the return directly, because this
// might force us to place the return within a loop, when it's better
// placed outside.
// But we do want to move the return block into the correct try-finally scope,
// so that loop detection at least has the option to put it inside
// the loop body.
context.Step("Copy return block into try block", branch);
Block blockCopy = (Block)branch.TargetBlock.Clone();
BlockContainer localContainer = branch.Ancestors.OfType<BlockContainer>().First();
blocksToAdd.Add((localContainer, blockCopy));
branch.TargetBlock = blockCopy;
}
} else if (targetBlock.Instructions.Count == 1 && targetBlock.Instructions[0].OpCode == OpCode.Leave) {
context.Step("Replace branch to leave with leave", branch);
// Replace branches to 'leave' instruction with the leave instruction
@ -113,6 +128,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -113,6 +128,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if (targetBlock.IncomingEdgeCount == 0)
targetBlock.Instructions.Clear(); // mark the block for deletion
}
foreach (var (container, block) in blocksToAdd) {
container.Blocks.Add(block);
}
}
void CleanUpEmptyBlocks(ILFunction function)
@ -131,19 +149,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -131,19 +149,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
}
bool ShouldSimplifyBranchToReturnBlock(Branch branch)
bool IsBranchToReturnBlock(Branch branch)
{
var targetBlock = branch.TargetBlock;
if (targetBlock.Instructions.Count != 1 || targetBlock.FinalInstruction.OpCode != OpCode.Nop)
return false;
if (targetBlock.Parent == branch.Ancestors.OfType<BlockContainer>().FirstOrDefault()
&& !aggressivelyDuplicateReturnBlocks) {
// only simplify when jumping out of a try-finally
return false;
}
var inst = targetBlock.Instructions[0];
return (inst is Return ret && ret.Value is LdLoc
|| inst is Leave leave && leave.IsLeavingFunction);
return targetBlock.Instructions[0] is Return ret && ret.Value is LdLoc;
}
static bool CombineBlockWithNextBlock(BlockContainer container, Block block)

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

@ -237,7 +237,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -237,7 +237,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
&& TypeUtils.IsCompatibleTypeForMemoryAccess(new ByReferenceType(v.Type), inst.Type)
&& inst.UnalignedPrefix == 0
&& !inst.IsVolatile) {
context.Step("stobj(ldloca(v), ...) => stloc(v, ...)", inst);
context.Step($"stobj(ldloca {v.Name}, ...) => stloc {v.Name}(...)", inst);
inst.ReplaceWith(new StLoc(v, inst.Value));
return true;
}

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

@ -28,24 +28,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -28,24 +28,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// </summary>
public class ILInlining : IILTransform, IBlockTransform
{
ILTransformContext context;
public void Run(ILFunction function, ILTransformContext context)
{
this.context = context;
foreach (var block in function.Descendants.OfType<Block>()) {
InlineAllInBlock(block);
InlineAllInBlock(block, context);
}
function.Variables.RemoveDead();
}
public void Run(Block block, BlockTransformContext context)
{
this.context = context;
InlineAllInBlock(block);
InlineAllInBlock(block, context);
}
public bool InlineAllInBlock(Block block)
public static bool InlineAllInBlock(Block block, ILTransformContext context)
{
bool modified = false;
int i = 0;
@ -240,8 +236,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -240,8 +236,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case OpCode.CallVirt:
return !((CallVirt)parent).Method.IsStatic;
case OpCode.LdFlda:
// TODO : Reimplement Await
//case OpCode.Await:
case OpCode.Await:
return true;
}
}

5
ILSpy/ILSpy.csproj

@ -167,6 +167,7 @@ @@ -167,6 +167,7 @@
<Compile Include="TaskHelper.cs" />
<Compile Include="TextView\EditorCommands.cs" />
<Compile Include="TextView\FoldingCommands.cs" />
<Compile Include="TextView\XmlDocRenderer.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzeContextMenuEntry.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedAssemblyTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedAttributeAppliedToTreeNode.cs" />
@ -205,10 +206,6 @@ @@ -205,10 +206,6 @@
<Compile Include="TreeNodes\Analyzer\AnalyzedPropertyTreeNode.cs" />
<Compile Include="TreeNodes\SearchMsdnContextMenuEntry.cs" />
<Compile Include="TreeNodes\UIHelper.cs" />
<Compile Include="XmlDoc\AddXmlDocTransform.cs" />
<Compile Include="XmlDoc\XmlDocKeyProvider.cs" />
<Compile Include="XmlDoc\XmlDocLoader.cs" />
<Compile Include="XmlDoc\XmlDocRenderer.cs" />
<EmbeddedResource Include="..\README.txt">
<Link>README.txt</Link>
</EmbeddedResource>

4
ILSpy/Languages/CSharpLanguage.cs

@ -35,7 +35,9 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -35,7 +35,9 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// Decompiler logic for C#.
/// C# decompiler integration into ILSpy.
/// Note: if you're interested in using the decompiler without the ILSpy UI,
/// please directly use the CSharpDecompiler class.
/// </summary>
[Export(typeof(Language))]
public class CSharpLanguage : Language

2
ILSpy/MainWindow.xaml.cs

@ -32,9 +32,9 @@ using System.Windows.Interop; @@ -32,9 +32,9 @@ using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Documentation;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.XmlDoc;
using ICSharpCode.TreeView;
using Microsoft.Win32;
using Mono.Cecil;

1
ILSpy/TextView/DecompilerTextView.cs

@ -47,7 +47,6 @@ using ICSharpCode.Decompiler.Documentation; @@ -47,7 +47,6 @@ using ICSharpCode.Decompiler.Documentation;
using ICSharpCode.ILSpy.AvalonEdit;
using ICSharpCode.ILSpy.Options;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.XmlDoc;
using Microsoft.Win32;
using Mono.Cecil;

2
ILSpy/XmlDoc/XmlDocRenderer.cs → ILSpy/TextView/XmlDocRenderer.cs

@ -24,7 +24,7 @@ using System.Text.RegularExpressions; @@ -24,7 +24,7 @@ using System.Text.RegularExpressions;
using System.Windows.Controls;
using System.Xml;
namespace ICSharpCode.ILSpy.XmlDoc
namespace ICSharpCode.ILSpy.TextView
{
/// <summary>
/// Renders XML documentation into a WPF <see cref="TextBlock"/>.
Loading…
Cancel
Save