Browse Source

Merge pull request #903 from mohe2015/fix-proxy-calls

Fix proxy calls to base method
pull/892/merge
Daniel Grunwald 8 years ago committed by GitHub
parent
commit
f3807855f9
  1. 17
      ICSharpCode.Decompiler.Tests/Helpers/SdkUtility.cs
  2. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  3. 2
      ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
  4. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  5. 118
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.cs
  6. 1579
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.il
  7. 1323
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.opt.il
  8. 1294
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.opt.roslyn.il
  9. 1424
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.roslyn.il
  10. 3
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  11. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  12. 102
      ICSharpCode.Decompiler/IL/Transforms/ProxyCallReplacer.cs

17
ICSharpCode.Decompiler.Tests/Helpers/SdkUtility.cs

@ -160,6 +160,19 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -160,6 +160,19 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
return windowsSdk80InstallRoot;
}
}
static string WindowsSdk461InstallRoot = null;
/// <summary>
/// Location of the .NET 4.6.1 SDK install root.
/// </summary>
public static string WindowsSdk461NetFxTools {
get {
if (WindowsSdk461InstallRoot == null) {
WindowsSdk461InstallRoot = GetPathFromRegistryX86(@"SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\NETFXSDK\4.6.1\WinSDK-NetFx40Tools", "InstallationFolder") ?? string.Empty;
}
return WindowsSdk461InstallRoot;
}
}
#endregion
/// <summary>
@ -170,6 +183,10 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -170,6 +183,10 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
/// <returns>The path of the executable, or null if the exe is not found.</returns>
public static string GetSdkPath(string exeName) {
string execPath;
if (!string.IsNullOrEmpty(WindowsSdk461NetFxTools)) {
execPath = Path.Combine(WindowsSdk461NetFxTools, exeName);
if (File.Exists(execPath)) { return execPath; }
}
if (!string.IsNullOrEmpty(WindowsSdk80NetFxTools)) {
execPath = Path.Combine(WindowsSdk80NetFxTools, exeName);
if (File.Exists(execPath)) { return execPath; }

1
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -64,6 +64,7 @@ @@ -64,6 +64,7 @@
<Compile Include="TestCases\Correctness\TrickyTypes.cs" />
<Compile Include="TestCases\Correctness\Using.cs" />
<Compile Include="TestCases\ILPretty\Issue379.cs" />
<Compile Include="TestCases\Pretty\FixProxyCalls.cs" />
<None Include="TestCases\ILPretty\Issue646.cs" />
<Compile Include="TestCases\Pretty\Async.cs" />
<Compile Include="TestCases\Pretty\CheckedUnchecked.cs" />

2
ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs

@ -41,7 +41,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -41,7 +41,7 @@ namespace ICSharpCode.Decompiler.Tests
{
Run();
}
void Run([CallerMemberName] string testName = null)
{
var ilFile = Path.Combine(TestCasePath, testName + ".il");

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -182,6 +182,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -182,6 +182,12 @@ namespace ICSharpCode.Decompiler.Tests
Run(cscOptions: cscOptions, asmOptions: AssemblerOptions.UseOwnDisassembler);
}
[Test]
public void FixProxyCalls([Values(CompilerOptions.None, CompilerOptions.Optimize, CompilerOptions.UseRoslyn)] 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);

118
ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.cs

@ -0,0 +1,118 @@ @@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
{
internal class A
{
protected internal virtual Task<string> Test(string test)
{
return Task.Run((Func<string>)(() => test.ToUpper()));
}
}
internal class B : A
{
protected internal override async Task<string> Test(string test)
{
return await base.Test(test);
}
}
internal class C
{
protected internal virtual string Test(string test)
{
return string.Join(test, "fsdf");
}
}
internal class D : C
{
protected internal IEnumerable<string> Test2(string test)
{
yield return base.Test(test);
}
}
internal class E
{
protected internal virtual string Test(string test)
{
return string.Join(test, "fsdf");
}
}
internal class F : E
{
protected internal override string Test(string test)
{
Func<string, string> func = (Func<string, string>)((string a) => base.Test(a));
test = string.Join(test, "aa");
return func(test);
}
}
internal class G
{
protected internal virtual void Test(string test)
{
string.Join(test, "fsdf");
}
}
internal class H : G
{
protected internal override void Test(string test)
{
Action<string> action = (Action<string>)delegate(string a) {
base.Test(a);
};
if (test.Equals(1)) {
throw new Exception("roslyn optimize is inlining the assignment which lets the test fail");
}
action(test);
}
}
internal class I
{
protected internal virtual void Test(int a)
{
}
}
internal class J : I
{
protected internal override void Test(int a)
{
Action action = (Action)delegate() {
base.Test(a);
};
if (a.Equals(1)) {
throw new Exception("roslyn optimize is inlining the assignment which lets the test fail");
}
action();
}
}
internal class K
{
protected internal virtual IEnumerable<int> Test(int p)
{
yield return p + 1;
yield return p + 2;
}
}
internal class L : K
{
protected internal override IEnumerable<int> Test(int p)
{
yield return base.Test(base.Test(0).GetEnumerator().Current).GetEnumerator().Current;
}
}
}

1579
ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.il

File diff suppressed because it is too large Load Diff

1323
ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.opt.il

File diff suppressed because it is too large Load Diff

1294
ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.opt.roslyn.il

File diff suppressed because it is too large Load Diff

1424
ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.roslyn.il

File diff suppressed because it is too large Load Diff

3
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -121,8 +121,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -121,8 +121,9 @@ namespace ICSharpCode.Decompiler.CSharp
)
}
},
new ProxyCallReplacer(),
new DelegateConstruction(),
new AssignVariableNames()
new AssignVariableNames(),
};
}

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -273,6 +273,7 @@ @@ -273,6 +273,7 @@
<Compile Include="DotNetCore\UnresolvedAssemblyNameReference.cs" />
<Compile Include="IL\Instructions\CallIndirect.cs" />
<Compile Include="IL\Transforms\EarlyExpressionTransforms.cs" />
<Compile Include="IL\Transforms\ProxyCallReplacer.cs" />
<Compile Include="IL\Transforms\UsingTransform.cs" />
<Compile Include="IL\ControlFlow\AsyncAwaitDecompiler.cs" />
<Compile Include="IL\ControlFlow\ControlFlowGraph.cs" />

102
ICSharpCode.Decompiler/IL/Transforms/ProxyCallReplacer.cs

@ -0,0 +1,102 @@ @@ -0,0 +1,102 @@
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.TypeSystem;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.IL.Transforms
{
class ProxyCallReplacer : IILTransform
{
ILTransformContext context;
public void Run(ILFunction function, ILTransformContext context)
{
this.context = context;
foreach (var inst in function.Descendants.OfType<CallInstruction>()) {
MethodDefinition methodDef = context.TypeSystem.GetCecil(inst.Method) as MethodDefinition;
if (methodDef != null && methodDef.Body != null) {
if (inst.Method.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) {
// partially copied from CSharpDecompiler
var specializingTypeSystem = this.context.TypeSystem.GetSpecializingTypeSystem(this.context.TypeSystem.Compilation.TypeResolveContext);
var ilReader = new ILReader(specializingTypeSystem);
System.Threading.CancellationToken cancellationToken = new System.Threading.CancellationToken();
var proxyFunction = ilReader.ReadIL(methodDef.Body, cancellationToken);
var transformContext = new ILTransformContext(proxyFunction, specializingTypeSystem, this.context.Settings) {
CancellationToken = cancellationToken
};
foreach (var transform in CSharp.CSharpDecompiler.GetILTransforms()) {
if (transform.GetType() != typeof(ProxyCallReplacer)) { // don't call itself on itself
cancellationToken.ThrowIfCancellationRequested();
transform.Run(proxyFunction, transformContext);
}
}
if (!(proxyFunction.Body is BlockContainer blockContainer))
return;
if (blockContainer.Blocks.Count != 1)
return;
var block = blockContainer.Blocks[0];
Call call = null;
if (block.Instructions.Count == 1) {
// leave IL_0000 (call Test(ldloc this, ldloc A_1))
if (!block.Instructions[0].MatchLeave(blockContainer, out ILInstruction returnValue))
return;
call = returnValue as Call;
} else if (block.Instructions.Count == 2) {
// call Test(ldloc this, ldloc A_1)
// leave IL_0000(nop)
call = block.Instructions[0] as Call;
if (!block.Instructions[1].MatchLeave(blockContainer, out ILInstruction returnValue))
return;
if (!returnValue.MatchNop())
return;
}
if (call == null) {
return;
}
if (call.Method.IsConstructor)
return;
// check if original arguments are only correct ldloc calls
for (int i = 0; i < call.Arguments.Count; i++) {
var originalArg = call.Arguments[i];
if (!originalArg.MatchLdLoc(out ILVariable var) ||
var.Kind != VariableKind.Parameter ||
var.Index != i - 1) {
return;
}
}
Call newInst = (Call)call.Clone();
newInst.Arguments.Clear();
if (inst.Arguments.Count > 0) {
ILInstruction thisArg = inst.Arguments[0];
// special handling for first argument (this) - the underlying issue may be somewhere else
// normally
// leave IL_0000(await(callvirt<> n__0(ldobj xxHandler(ldloca this), ldobj System.Net.Http.HttpRequestMessage(ldloca request), ldobj System.Threading.CancellationToken(ldloca cancellationToken))))
// would be decompiled to
// return await((DelegatingHandler)this).SendAsync(request, cancellationToken);
// this changes it to
// return await base.SendAsync(request, cancellationToken);
if (thisArg.MatchLdObj(out ILInstruction loadedObject, out IType objectType) &&
loadedObject.MatchLdLoca(out ILVariable loadedVar)) {
thisArg = new LdLoc(loadedVar);
}
newInst.Arguments.Add(thisArg);
// add everything except first argument
for (int i = 1; i < inst.Arguments.Count; i++) {
newInst.Arguments.Add(inst.Arguments[i]);
}
}
inst.ReplaceWith(newInst);
}
}
}
}
}
}
Loading…
Cancel
Save