Browse Source

Fix #1963: Support decompiling `calli` instructions into C# 9 function pointer syntax.

pull/2045/head
Daniel Grunwald 5 years ago
parent
commit
2c0554f129
  1. 2
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 6
      ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
  3. 8
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.cs
  4. 39
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.il
  5. 21
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  6. 13
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  7. 15
      ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs
  8. 83
      ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerType.cs
  9. 3
      ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs
  10. 7
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs
  11. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  12. 4
      ICSharpCode.Decompiler/IL/ILReader.cs
  13. 48
      ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs

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

@ -61,6 +61,7 @@ @@ -61,6 +61,7 @@
<None Include="TestCases\Correctness\StackTests.il" />
<None Include="TestCases\Correctness\StackTypes.il" />
<None Include="TestCases\Correctness\Uninit.vb" />
<None Include="TestCases\ILPretty\CallIndirect.il" />
<None Include="TestCases\ILPretty\Issue1454.il" />
<None Include="TestCases\ILPretty\Issue1681.il" />
<None Include="TestCases\ILPretty\Issue1922.il" />
@ -91,6 +92,7 @@ @@ -91,6 +92,7 @@
<Compile Include="ProjectDecompiler\TargetFrameworkTests.cs" />
<Compile Include="TestAssemblyResolver.cs" />
<Compile Include="TestCases\Correctness\StringConcat.cs" />
<None Include="TestCases\ILPretty\CallIndirect.cs" />
<Compile Include="TestCases\ILPretty\Issue1681.cs" />
<Compile Include="TestCases\Ugly\AggressiveScalarReplacementOfAggregates.cs" />
<None Include="TestCases\Ugly\AggressiveScalarReplacementOfAggregates.Expected.cs" />

6
ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs

@ -196,6 +196,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -196,6 +196,12 @@ namespace ICSharpCode.Decompiler.Tests
Run();
}
[Test]
public void CallIndirect()
{
Run();
}
[Test]
public void FSharpLoops_Debug()
{

8
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.cs

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
using System;
internal class CallIndirect
{
private unsafe void Test(IntPtr f)
{
((delegate* stdcall<int, void>)f)(42);
}
}

39
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.il

@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
#define CORE_ASSEMBLY "System.Runtime"
.assembly extern CORE_ASSEMBLY
{
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....:
.ver 4:0:0:0
}
.class private auto ansi beforefieldinit CallIndirect
extends [System.Private.CoreLib]System.Object
{
// Methods
.method private hidebysig
instance void Test (
native int f
) cil managed
{
.maxstack 8
ldc.i4.s 42
ldarg.1
calli unmanaged stdcall void(int32)
ret
} // end of method Example::Test
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x206e
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [System.Private.CoreLib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method Example::.ctor
} // end of class Example

21
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -3244,6 +3244,27 @@ namespace ICSharpCode.Decompiler.CSharp @@ -3244,6 +3244,27 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(inst);
}
protected internal override TranslatedExpression VisitCallIndirect(CallIndirect inst, TranslationContext context)
{
if (inst.IsInstance) {
return ErrorExpression("calli with instance method signature not supportd");
}
var ty = new FunctionPointerType();
if (inst.CallingConvention != System.Reflection.Metadata.SignatureCallingConvention.Default) {
ty.CallingConvention = inst.CallingConvention.ToString().ToLowerInvariant();
}
foreach (var parameterType in inst.ParameterTypes) {
ty.TypeArguments.Add(astBuilder.ConvertType(parameterType));
}
ty.TypeArguments.Add(astBuilder.ConvertType(inst.ReturnType));
var functionPointer = Translate(inst.FunctionPointer);
var invocation = new InvocationExpression(new CastExpression(ty, functionPointer));
foreach (var (arg, paramType) in inst.Arguments.Zip(inst.ParameterTypes)) {
invocation.Arguments.Add(Translate(arg, typeHint: paramType).ConvertTo(paramType, this, allowImplicitConversion: true));
}
return invocation.WithRR(new ResolveResult(inst.ReturnType)).WithILInstruction(inst);
}
protected internal override TranslatedExpression VisitInvalidBranch(InvalidBranch inst, TranslationContext context)
{
string message = "Error";

13
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -2335,6 +2335,19 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -2335,6 +2335,19 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
EndNode(tupleTypeElement);
}
public virtual void VisitFunctionPointerType(FunctionPointerType functionPointerType)
{
StartNode(functionPointerType);
WriteKeyword(Roles.DelegateKeyword);
WriteToken(FunctionPointerType.PointerRole);
if (!functionPointerType.CallingConventionIdentifier.IsNull) {
Space();
WriteIdentifier(functionPointerType.CallingConventionIdentifier);
}
WriteTypeArguments(functionPointerType.TypeArguments);
EndNode(functionPointerType);
}
public virtual void VisitComposedType(ComposedType composedType)
{
StartNode(composedType);

15
ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs

@ -126,6 +126,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -126,6 +126,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
VisitChildren (tupleTypeElement);
}
public virtual void VisitFunctionPointerType(FunctionPointerType functionPointerType)
{
VisitChildren(functionPointerType);
}
public virtual void VisitAttribute (Attribute attribute)
{
VisitChildren (attribute);
@ -778,6 +783,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -778,6 +783,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return VisitChildren (tupleTypeElement);
}
public virtual T VisitFunctionPointerType(FunctionPointerType functionPointerType)
{
return VisitChildren (functionPointerType);
}
public virtual T VisitAttribute (Attribute attribute)
{
return VisitChildren (attribute);
@ -1430,6 +1440,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1430,6 +1440,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return VisitChildren (tupleTypeElement, data);
}
public virtual S VisitFunctionPointerType(FunctionPointerType functionPointerType, T data)
{
return VisitChildren (functionPointerType, data);
}
public virtual S VisitAttribute (Attribute attribute, T data)
{
return VisitChildren (attribute, data);

83
ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerType.cs

@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
//
// FullTypeName.cs
//
// Author:
// Mike Krüger <mkrueger@novell.com>
//
// Copyright (c) 2010 Novell, Inc (http://www.novell.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 ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.TypeSystem;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.CSharp.Syntax
{
public class FunctionPointerType : AstType
{
public static readonly TokenRole PointerRole = new TokenRole("*");
public static readonly Role<Identifier> CallingConventionRole = new Role<Identifier>("Target", Identifier.Null);
public string CallingConvention {
get {
return GetChildByRole (CallingConventionRole).Name;
}
set {
SetChildByRole (CallingConventionRole, Identifier.Create (value));
}
}
public Identifier CallingConventionIdentifier => GetChildByRole(CallingConventionRole);
public AstNodeCollection<AstType> TypeArguments {
get { return GetChildrenByRole (Roles.TypeArgument); }
}
public override void AcceptVisitor (IAstVisitor visitor)
{
visitor.VisitFunctionPointerType(this);
}
public override T AcceptVisitor<T> (IAstVisitor<T> visitor)
{
return visitor.VisitFunctionPointerType(this);
}
public override S AcceptVisitor<T, S> (IAstVisitor<T, S> visitor, T data)
{
return visitor.VisitFunctionPointerType(this, data);
}
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{
return other is FunctionPointerType o && MatchString(this.CallingConvention, o.CallingConvention)
&& this.TypeArguments.DoMatch(o.TypeArguments, match);
}
public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null)
{
throw new NotImplementedException();
}
}
}

3
ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs

@ -137,6 +137,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -137,6 +137,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
void VisitMemberType(MemberType memberType);
void VisitTupleType(TupleAstType tupleType);
void VisitTupleTypeElement(TupleTypeElement tupleTypeElement);
void VisitFunctionPointerType(FunctionPointerType functionPointerType);
void VisitComposedType(ComposedType composedType);
void VisitArraySpecifier(ArraySpecifier arraySpecifier);
void VisitPrimitiveType(PrimitiveType primitiveType);
@ -279,6 +280,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -279,6 +280,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitMemberType(MemberType memberType);
S VisitTupleType(TupleAstType tupleType);
S VisitTupleTypeElement(TupleTypeElement tupleTypeElement);
S VisitFunctionPointerType(FunctionPointerType functionPointerType);
S VisitComposedType(ComposedType composedType);
S VisitArraySpecifier(ArraySpecifier arraySpecifier);
S VisitPrimitiveType(PrimitiveType primitiveType);
@ -421,6 +423,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -421,6 +423,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitMemberType(MemberType memberType, T data);
S VisitTupleType(TupleAstType tupleType, T data);
S VisitTupleTypeElement(TupleTypeElement tupleTypeElement, T data);
S VisitFunctionPointerType(FunctionPointerType functionPointerType, T data);
S VisitComposedType(ComposedType composedType, T data);
S VisitArraySpecifier(ArraySpecifier arraySpecifier, T data);
S VisitPrimitiveType(PrimitiveType primitiveType, T data);

7
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs

@ -73,7 +73,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -73,7 +73,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
else
return base.VisitComposedType(composedType);
}
public override bool VisitFunctionPointerType(FunctionPointerType functionPointerType)
{
return true;
}
public override bool VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression)
{
bool result = base.VisitUnaryOperatorExpression(unaryOperatorExpression);

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -70,6 +70,7 @@ @@ -70,6 +70,7 @@
<Compile Include="CSharp\SequencePointBuilder.cs" />
<Compile Include="CSharp\ProjectDecompiler\TargetFramework.cs" />
<Compile Include="CSharp\ProjectDecompiler\TargetServices.cs" />
<Compile Include="CSharp\Syntax\FunctionPointerType.cs" />
<Compile Include="IL\Transforms\IndexRangeTransform.cs" />
<Compile Include="CSharp\TranslatedStatement.cs" />
<Compile Include="DebugInfo\KnownGuids.cs" />

4
ICSharpCode.Decompiler/IL/ILReader.cs

@ -1456,8 +1456,8 @@ namespace ICSharpCode.Decompiler.IL @@ -1456,8 +1456,8 @@ namespace ICSharpCode.Decompiler.IL
signature.Header.CallingConvention,
signature.ReturnType,
signature.ParameterTypes,
arguments,
functionPointer
functionPointer,
arguments
);
if (call.ResultType != StackType.Void)
return Push(call);

48
ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs

@ -27,11 +27,13 @@ namespace ICSharpCode.Decompiler.IL @@ -27,11 +27,13 @@ namespace ICSharpCode.Decompiler.IL
{
partial class CallIndirect
{
public static readonly SlotInfo ArgumentSlot = new SlotInfo("Argument", canInlineInto: true, isCollection: true);
// Note: while in IL the arguments come first and the function pointer last;
// in the ILAst we're handling it as in C#: the function pointer is evaluated first, the arguments later.
public static readonly SlotInfo FunctionPointerSlot = new SlotInfo("FunctionPointer", canInlineInto: true);
public static readonly SlotInfo ArgumentSlot = new SlotInfo("Argument", canInlineInto: true, isCollection: true);
public readonly InstructionCollection<ILInstruction> Arguments;
ILInstruction functionPointer;
public readonly InstructionCollection<ILInstruction> Arguments;
public bool IsInstance { get; }
public bool HasExplicitThis { get; }
public System.Reflection.Metadata.SignatureCallingConvention CallingConvention { get; }
@ -51,34 +53,27 @@ namespace ICSharpCode.Decompiler.IL @@ -51,34 +53,27 @@ namespace ICSharpCode.Decompiler.IL
}
set {
ValidateChild(value);
SetChildInstruction(ref functionPointer, value, Arguments.Count);
SetChildInstruction(ref functionPointer, value, 0);
}
}
protected internal override void InstructionCollectionUpdateComplete()
{
base.InstructionCollectionUpdateComplete();
if (functionPointer?.Parent == this)
functionPointer.ChildIndex = Arguments.Count;
}
public CallIndirect(bool isInstance, bool hasExplicitThis, System.Reflection.Metadata.SignatureCallingConvention callingConvention, IType returnType, ImmutableArray<IType> parameterTypes,
IEnumerable<ILInstruction> arguments, ILInstruction functionPointer) : base(OpCode.CallIndirect)
ILInstruction functionPointer, IEnumerable<ILInstruction> arguments) : base(OpCode.CallIndirect)
{
this.IsInstance = isInstance;
this.HasExplicitThis = hasExplicitThis;
this.CallingConvention = callingConvention;
this.ReturnType = returnType ?? throw new ArgumentNullException(nameof(returnType));
this.ParameterTypes = parameterTypes.ToImmutableArray();
this.Arguments = new InstructionCollection<ILInstruction>(this, 0);
this.Arguments.AddRange(arguments);
this.FunctionPointer = functionPointer;
this.Arguments = new InstructionCollection<ILInstruction>(this, 1);
this.Arguments.AddRange(arguments);
}
public override ILInstruction Clone()
{
return new CallIndirect(IsInstance, HasExplicitThis, CallingConvention, ReturnType, ParameterTypes,
this.Arguments.Select(inst => inst.Clone()), functionPointer.Clone()
functionPointer.Clone(), this.Arguments.Select(inst => inst.Clone())
).WithILRange(this);
}
@ -96,24 +91,19 @@ namespace ICSharpCode.Decompiler.IL @@ -96,24 +91,19 @@ namespace ICSharpCode.Decompiler.IL
output.Write("call.indirect ");
ReturnType.WriteTo(output);
output.Write('(');
bool first = true;
functionPointer.WriteTo(output, options);
int firstArgument = IsInstance ? 1 : 0;
if (firstArgument == 1) {
output.Write(", ");
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
output.Write(", ");
foreach (var (inst, type) in Arguments.Zip(ParameterTypes, (a,b) => (a,b))) {
output.Write(", ");
inst.WriteTo(output, options);
output.Write(" : ");
type.WriteTo(output);
}
if (Arguments.Count > 0)
output.Write(", ");
functionPointer.WriteTo(output, options);
output.Write(')');
}
@ -124,22 +114,22 @@ namespace ICSharpCode.Decompiler.IL @@ -124,22 +114,22 @@ namespace ICSharpCode.Decompiler.IL
protected override ILInstruction GetChild(int index)
{
if (index == Arguments.Count)
if (index == 0)
return functionPointer;
return Arguments[index];
return Arguments[index - 1];
}
protected override void SetChild(int index, ILInstruction value)
{
if (index == Arguments.Count)
if (index == 0)
FunctionPointer = value;
else
Arguments[index] = value;
Arguments[index - 1] = value;
}
protected override SlotInfo GetChildSlot(int index)
{
if (index == Arguments.Count)
if (index == 0)
return FunctionPointerSlot;
else
return ArgumentSlot;
@ -148,10 +138,10 @@ namespace ICSharpCode.Decompiler.IL @@ -148,10 +138,10 @@ namespace ICSharpCode.Decompiler.IL
protected override InstructionFlags ComputeFlags()
{
var flags = this.DirectFlags;
flags |= functionPointer.Flags;
foreach (var inst in Arguments) {
flags |= inst.Flags;
}
flags |= functionPointer.Flags;
return flags;
}

Loading…
Cancel
Save