Browse Source

Fix missing casts on call targets

pull/728/head
Siegfried Pammer 9 years ago
parent
commit
38b0dadf33
  1. 101
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 1
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  3. 2
      ICSharpCode.Decompiler/Tests/RoundtripAssembly.cs
  4. 76
      ICSharpCode.Decompiler/Tests/TestCases/MemberLookup.cs
  5. 9
      ICSharpCode.Decompiler/Tests/TestRunner.cs

101
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -16,19 +16,22 @@ @@ -16,19 +16,22 @@
// 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.CSharp.Transforms;
using ICSharpCode.NRefactory.CSharp.TypeSystem;
using ExpressionType = System.Linq.Expressions.ExpressionType;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.Refactoring;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.CSharp.TypeSystem;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.TypeSystem;
using System;
using System.Collections.Generic;
using System.Linq;
using ExpressionType = System.Linq.Expressions.ExpressionType;
namespace ICSharpCode.Decompiler.CSharp
{
@ -127,7 +130,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -127,7 +130,14 @@ namespace ICSharpCode.Decompiler.CSharp
ExpressionWithResolveResult ConvertField(IField field, ILInstruction target = null)
{
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly);
var targetExpression = TranslateTarget(field, target, true);
var result = lookup.Lookup(targetExpression.ResolveResult, field.Name, EmptyList<IType>.Instance, false) as MemberResolveResult;
if (result == null || !result.Member.Equals(field))
targetExpression = targetExpression.ConvertTo(field.DeclaringType, this);
return new MemberReferenceExpression(targetExpression, field.Name)
.WithRR(new MemberResolveResult(targetExpression.ResolveResult, field));
}
@ -569,6 +579,19 @@ namespace ICSharpCode.Decompiler.CSharp @@ -569,6 +579,19 @@ namespace ICSharpCode.Decompiler.CSharp
method = ((LdVirtFtn)func).Method;
}
var target = TranslateTarget(method, inst.Arguments[0], func.OpCode == OpCode.LdFtn);
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly);
var or = new OverloadResolution(resolver.Compilation, method.Parameters.SelectArray(p => new TypeResolveResult(p.Type)));
var result = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, true) as MethodGroupResolveResult;
if (result == null) {
target = target.ConvertTo(method.DeclaringType, this);
} else {
or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());
if (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(method, or.BestCandidate, func.OpCode == OpCode.LdVirtFtn))
target = target.ConvertTo(method.DeclaringType, this);
}
var mre = new MemberReferenceExpression(target, method.Name);
mre.TypeArguments.AddRange(method.TypeArguments.Select(a => ConvertType(a)));
return new ObjectCreateExpression(ConvertType(inst.Method.DeclaringType), mre)
@ -592,7 +615,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -592,7 +615,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (translatedTarget.Expression is DirectionExpression) {
translatedTarget = translatedTarget.UnwrapChild(((DirectionExpression)translatedTarget).Expression);
}
return translatedTarget.ConvertTo(member.DeclaringType, this);
return translatedTarget;
}
} else {
return new TypeReferenceExpression(ConvertType(member.DeclaringType))
@ -655,9 +678,41 @@ namespace ICSharpCode.Decompiler.CSharp @@ -655,9 +678,41 @@ namespace ICSharpCode.Decompiler.CSharp
} else {
Expression expr;
if (method.IsAccessor) {
expr = HandleAccessorCall(target, method, argumentExpressions);
} else {
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly);
var or = new OverloadResolution(resolver.Compilation, arguments.Skip(firstParamIndex).Select(a => a.ResolveResult).ToArray());
var result = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, true) as MethodGroupResolveResult;
if (result == null) {
target = target.ConvertTo(method.DeclaringType, this);
} else {
or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());
if (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(method, or.BestCandidate, inst.OpCode == OpCode.CallVirt))
target = target.ConvertTo(method.DeclaringType, this);
}
Expression targetExpr = target.Expression;
string methodName = method.Name;
// HACK : convert this.Dispose() to ((IDisposable)this).Dispose(), if Dispose is an explicitly implemented interface method.
if (inst.Method.IsExplicitInterfaceImplementation && targetExpr is ThisReferenceExpression) {
targetExpr = targetExpr.CastTo(ConvertType(method.ImplementedInterfaceMembers[0].DeclaringType));
methodName = method.ImplementedInterfaceMembers[0].Name;
}
var mre = new MemberReferenceExpression(targetExpr, methodName);
mre.TypeArguments.AddRange(method.TypeArguments.Select(a => ConvertType(a)));
expr = new InvocationExpression(mre, argumentExpressions);
}
return expr.WithILInstruction(inst).WithRR(rr);
}
}
Expression HandleAccessorCall(TranslatedExpression target, IMethod method, IList<Expression> argumentExpressions)
{
if (method.ReturnType.IsKnownType(KnownTypeCode.Void)) {
var value = argumentExpressions.Last();
argumentExpressions.Remove(value);
Expression expr;
if (argumentExpressions.Count == 0)
expr = new MemberReferenceExpression(target.Expression, method.AccessorOwner.Name);
else
@ -672,27 +727,29 @@ namespace ICSharpCode.Decompiler.CSharp @@ -672,27 +727,29 @@ namespace ICSharpCode.Decompiler.CSharp
op = AssignmentOperatorType.Subtract;
}
}
expr = new AssignmentExpression(expr, op, value);
return new AssignmentExpression(expr, op, value);
} else {
if (argumentExpressions.Count == 0)
expr = new MemberReferenceExpression(target.Expression, method.AccessorOwner.Name);
return new MemberReferenceExpression(target.Expression, method.AccessorOwner.Name);
else
expr = new IndexerExpression(target.Expression, argumentExpressions);
return new IndexerExpression(target.Expression, argumentExpressions);
}
} else {
Expression targetExpr = target.Expression;
string methodName = method.Name;
// HACK : convert this.Dispose() to ((IDisposable)this).Dispose(), if Dispose is an explicitly implemented interface method.
if (inst.Method.IsExplicitInterfaceImplementation && targetExpr is ThisReferenceExpression) {
targetExpr = targetExpr.CastTo(ConvertType(method.ImplementedInterfaceMembers[0].DeclaringType));
methodName = method.ImplementedInterfaceMembers[0].Name;
}
var mre = new MemberReferenceExpression(targetExpr, methodName);
mre.TypeArguments.AddRange(method.TypeArguments.Select(a => ConvertType(a)));
expr = new InvocationExpression(mre, argumentExpressions);
bool IsAppropriateCallTarget(IMember expectedTarget, IMember actualTarget, bool isVirtCall)
{
if (expectedTarget.Equals(actualTarget))
return true;
if (isVirtCall && actualTarget.IsOverride) {
foreach (var possibleTarget in InheritanceHelper.GetBaseMembers(actualTarget, false)) {
if (expectedTarget.Equals(possibleTarget))
return true;
if (!possibleTarget.IsOverride)
break;
}
return expr.WithILInstruction(inst).WithRR(rr);
}
return false;
}
protected internal override TranslatedExpression VisitLdObj(LdObj inst)

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

@ -124,6 +124,7 @@ @@ -124,6 +124,7 @@
<Compile Include="TestCases\Generics.cs" />
<Compile Include="TestCases\HelloWorld.cs" />
<Compile Include="TestCases\InitializerTests.cs" />
<Compile Include="TestCases\MemberLookup.cs" />
<Compile Include="TestCases\PropertiesAndEvents.cs" />
<Compile Include="TestCases\Switch.cs" />
<Compile Include="TestCases\UndocumentedExpressions.cs" />

2
ICSharpCode.Decompiler/Tests/RoundtripAssembly.cs

@ -46,7 +46,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -46,7 +46,7 @@ namespace ICSharpCode.Decompiler.Tests
void Run(string dir, string fileToRoundtrip, string fileToTest)
{
if (!Directory.Exists(testDir)) {
Assert.Ignore($"Assembly-roundtrip test ignored: test directory '{testDir}' needs to be checked out seperately." + Environment.NewLine +
Assert.Ignore($"Assembly-roundtrip test ignored: test directory '{testDir}' needs to be checked out separately." + Environment.NewLine +
$"git clone https://github.com/icsharpcode/ILSpy-tests \"{testDir}\"");
}
string inputDir = Path.Combine(testDir, dir);

76
ICSharpCode.Decompiler/Tests/TestCases/MemberLookup.cs

@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
// 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;
namespace ICSharpCode.Decompiler.Tests.TestCases
{
public class MemberLookup
{
static readonly Action delegateConstruction = (new Child1() as Base1).TestAction;
public static int Main()
{
Console.WriteLine((new Child1() as Base1).Field);
Child1.Test();
delegateConstruction();
return 0;
}
class Base1
{
public int Field = 1;
protected virtual void TestMethod()
{
Console.WriteLine("Base1.TestMethod()");
}
public void TestAction()
{
Console.WriteLine("Base1.TestAction()");
}
}
class Child1 : Base1
{
Child1 child;
new public int Field = 2;
public static void Test()
{
var o = new Child1();
o.child = new Child1();
o.TestMethod();
}
protected override void TestMethod()
{
base.TestMethod();
if (child != null)
child.TestMethod();
Console.WriteLine("Child1.TestMethod()");
}
new public void TestAction()
{
Console.WriteLine("Child1.TestAction()");
}
}
}
}

9
ICSharpCode.Decompiler/Tests/TestRunner.cs

@ -102,6 +102,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -102,6 +102,12 @@ namespace ICSharpCode.Decompiler.Tests
TestCompileDecompileCompileOutputAll("UndocumentedExpressions.cs");
}
[Test]
public void MemberLookup()
{
TestCompileDecompileCompileOutputAll("MemberLookup.cs");
}
void TestCompileDecompileCompileOutputAll(string testFileName)
{
TestCompileDecompileCompileOutput(testFileName, CompilerOptions.None);
@ -143,7 +149,8 @@ namespace ICSharpCode.Decompiler.Tests @@ -143,7 +149,8 @@ namespace ICSharpCode.Decompiler.Tests
}
}
Assert.AreEqual(result1, result2, "Exit codes differ; did the decompiled code crash?");
Assert.AreEqual(0, result1, "Exit code != 0; did the test case crash?" + Environment.NewLine + error1);
Assert.AreEqual(0, result2, "Exit code != 0; did the decompiled code crash?" + Environment.NewLine + error2);
Assert.AreEqual(error1, error2);
Assert.AreEqual(output1, output2);

Loading…
Cancel
Save