Browse Source

#1563: Where possible, replace an explicit interface implementation call with a call to the interface member.

pull/1612/head
Daniel Grunwald 6 years ago
parent
commit
7e3b36aaa7
  1. 2
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 6
      ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
  3. 14
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.cs
  4. 36
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il
  5. 10
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs

2
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -66,6 +66,7 @@ @@ -66,6 +66,7 @@
<None Include="TestCases\ILPretty\FSharpLoops_Release.il" />
<None Include="TestCases\ILPretty\FSharpUsing.fs" />
<None Include="TestCases\ILPretty\FSharpUsing_Debug.il" />
<None Include="TestCases\ILPretty\DirectCallToExplicitInterfaceImpl.il" />
<None Include="TestCases\ILPretty\FSharpUsing_Release.il" />
<None Include="TestCases\Correctness\BitNot.il" />
<None Include="TestCases\Correctness\Readme.txt" />
@ -74,6 +75,7 @@ @@ -74,6 +75,7 @@
<ItemGroup>
<Compile Include="DisassemblerPrettyTestRunner.cs" />
<Compile Include="TestCases\ILPretty\ConstantBlobs.cs" />
<None Include="TestCases\ILPretty\DirectCallToExplicitInterfaceImpl.cs" />
<Compile Include="TestCases\ILPretty\Issue1389.cs" />
<Compile Include="TestCases\ILPretty\Issue1454.cs" />
<Compile Include="TestCases\Pretty\Discards.cs" />

6
ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs

@ -100,6 +100,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -100,6 +100,12 @@ namespace ICSharpCode.Decompiler.Tests
Run(settings: new DecompilerSettings { RemoveDeadCode = true });
}
[Test]
public void DirectCallToExplicitInterfaceImpl()
{
Run();
}
[Test]
public void CS1xSwitch_Debug()
{

14
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.cs

@ -0,0 +1,14 @@ @@ -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();
}
}

36
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il

@ -0,0 +1,36 @@ @@ -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 DirectCallToExplicitInterfaceImpl

10
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -172,6 +172,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -172,6 +172,16 @@ namespace ICSharpCode.Decompiler.CSharp
IReadOnlyList<int> 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

Loading…
Cancel
Save