diff --git a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
index 481b2b5c5..ba895ed79 100644
--- a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
+++ b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
@@ -289,6 +289,19 @@ namespace ICSharpCode.Decompiler.Disassembler
writer.Write(" modreq(");
((RequiredModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write(") ");
+ } else if (type is FunctionPointerType fpt) {
+ writer.Write("method ");
+ fpt.ReturnType.WriteTo(writer, syntax);
+ writer.Write(" *(");
+ bool first = true;
+ foreach (var p in fpt.Parameters) {
+ if (first)
+ first = false;
+ else
+ writer.Write(", ");
+ p.ParameterType.WriteTo(writer, syntax);
+ }
+ writer.Write(')');
} else if (type is SentinelType) {
writer.Write("..., ");
((SentinelType)type).ElementType.WriteTo(writer, syntax);
diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
index 581380e6d..912fa636d 100644
--- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
+++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
@@ -271,6 +271,7 @@
+
diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs
index ac122c74a..dbf152ee7 100644
--- a/ICSharpCode.Decompiler/IL/ILReader.cs
+++ b/ICSharpCode.Decompiler/IL/ILReader.cs
@@ -443,7 +443,7 @@ namespace ICSharpCode.Decompiler.IL
case Cil.Code.Callvirt:
return DecodeCall(OpCode.CallVirt);
case Cil.Code.Calli:
- throw new NotImplementedException();
+ return DecodeCallIndirect();
case Cil.Code.Ceq:
return Push(Comparison(ComparisonKind.Equality));
case Cil.Code.Cgt:
@@ -916,9 +916,16 @@ namespace ICSharpCode.Decompiler.IL
ILInstruction inst = Pop();
if (expectedType != inst.ResultType) {
if (expectedType == StackType.I && inst.ResultType == StackType.I4) {
+ // IL allows implicit I4->I conversions
inst = new Conv(inst, PrimitiveType.I, false, Sign.None);
+ } else if (expectedType == StackType.I4 && inst.ResultType == StackType.I) {
+ // C++/CLI also sometimes implicitly converts in the other direction:
+ inst = new Conv(inst, PrimitiveType.I4, false, Sign.None);
} else if (expectedType == StackType.Ref && inst.ResultType == StackType.I) {
// implicitly start GC tracking
+ } else if (expectedType == StackType.I && inst.ResultType == StackType.Ref) {
+ // Implicitly stop GC tracking; this occurs when passing the result of 'ldloca' or 'ldsflda'
+ // to a method expecting a native pointer.
} else if (inst is InvalidExpression) {
((InvalidExpression)inst).ExpectedResultType = expectedType;
} else {
@@ -1105,7 +1112,31 @@ namespace ICSharpCode.Decompiler.IL
return call;
}
}
-
+
+ ILInstruction DecodeCallIndirect()
+ {
+ var functionPointer = Pop(StackType.I);
+ var signature = (CallSite)currentInstruction.Operand;
+ Debug.Assert(!signature.HasThis);
+ var parameterTypes = new IType[signature.Parameters.Count];
+ var arguments = new ILInstruction[parameterTypes.Length];
+ for (int i = signature.Parameters.Count - 1; i >= 0; i--) {
+ parameterTypes[i] = typeSystem.Resolve(signature.Parameters[i].ParameterType);
+ arguments[i] = Pop(parameterTypes[i].GetStackType());
+ }
+ var call = new CallIndirect(
+ signature.CallingConvention,
+ typeSystem.Resolve(signature.ReturnType),
+ parameterTypes.ToImmutableArray(),
+ arguments,
+ functionPointer
+ );
+ if (call.ResultType != StackType.Void)
+ return Push(call);
+ else
+ return call;
+ }
+
static int GetPopCount(OpCode callCode, MethodReference methodReference)
{
int popCount = methodReference.Parameters.Count;
diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs
index fc83ed656..a98f573bf 100644
--- a/ICSharpCode.Decompiler/IL/Instructions.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions.cs
@@ -83,6 +83,8 @@ namespace ICSharpCode.Decompiler.IL
Call,
/// Virtual method call.
CallVirt,
+ /// Unsafe function pointer call.
+ CallIndirect,
/// Checks that the input float is not NaN or infinite.
Ckfinite,
/// Numeric cast.
@@ -1954,6 +1956,31 @@ namespace ICSharpCode.Decompiler.IL
}
}
namespace ICSharpCode.Decompiler.IL
+{
+ /// Unsafe function pointer call.
+ public sealed partial class CallIndirect : ILInstruction
+ {
+
+ public override void AcceptVisitor(ILVisitor visitor)
+ {
+ visitor.VisitCallIndirect(this);
+ }
+ public override T AcceptVisitor(ILVisitor visitor)
+ {
+ return visitor.VisitCallIndirect(this);
+ }
+ public override T AcceptVisitor(ILVisitor visitor, C context)
+ {
+ return visitor.VisitCallIndirect(this, context);
+ }
+ protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
+ {
+ var o = other as CallIndirect;
+ return o != null && EqualSignature(o) && Patterns.ListMatch.DoMatch(this.Arguments, o.Arguments, ref match) && this.FunctionPointer.PerformMatch(o.FunctionPointer, ref match);
+ }
+ }
+}
+namespace ICSharpCode.Decompiler.IL
{
/// Checks that the input float is not NaN or infinite.
public sealed partial class Ckfinite : UnaryInstruction
@@ -4769,6 +4796,10 @@ namespace ICSharpCode.Decompiler.IL
{
Default(inst);
}
+ protected internal virtual void VisitCallIndirect(CallIndirect inst)
+ {
+ Default(inst);
+ }
protected internal virtual void VisitCkfinite(Ckfinite inst)
{
Default(inst);
@@ -5063,6 +5094,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst);
}
+ protected internal virtual T VisitCallIndirect(CallIndirect inst)
+ {
+ return Default(inst);
+ }
protected internal virtual T VisitCkfinite(Ckfinite inst)
{
return Default(inst);
@@ -5357,6 +5392,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst, context);
}
+ protected internal virtual T VisitCallIndirect(CallIndirect inst, C context)
+ {
+ return Default(inst, context);
+ }
protected internal virtual T VisitCkfinite(Ckfinite inst, C context)
{
return Default(inst, context);
@@ -5565,6 +5604,7 @@ namespace ICSharpCode.Decompiler.IL
"comp",
"call",
"callvirt",
+ "calli",
"ckfinite",
"conv",
"ldloc",
diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt
index 927b59c24..eacecfe0f 100644
--- a/ICSharpCode.Decompiler/IL/Instructions.tt
+++ b/ICSharpCode.Decompiler/IL/Instructions.tt
@@ -135,6 +135,12 @@
new OpCode("call", "Non-virtual method call.", Call),
new OpCode("callvirt", "Virtual method call.",
CustomClassName("CallVirt"), Call),
+ new OpCode("calli", "Unsafe function pointer call.",
+ CustomClassName("CallIndirect"),
+ CustomConstructor, CustomWriteTo,
+ MatchCondition("EqualSignature(o)"),
+ MatchCondition("Patterns.ListMatch.DoMatch(this.Arguments, o.Arguments, ref match)"),
+ MatchCondition("this.FunctionPointer.PerformMatch(o.FunctionPointer, ref match)")),
new OpCode("ckfinite", "Checks that the input float is not NaN or infinite.",
Unary, MayThrow, VoidResult),
new OpCode("conv", "Numeric cast.",
diff --git a/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs b/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs
new file mode 100644
index 000000000..7b74939a2
--- /dev/null
+++ b/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs
@@ -0,0 +1,170 @@
+// Copyright (c) 2017 Daniel Grunwald
+//
+// 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.Collections.Immutable;
+using System.Diagnostics;
+using System.Linq;
+using ICSharpCode.Decompiler.TypeSystem;
+
+namespace ICSharpCode.Decompiler.IL
+{
+ partial class CallIndirect
+ {
+ public static readonly SlotInfo ArgumentSlot = new SlotInfo("Argument", canInlineInto: true, isCollection: true);
+ public static readonly SlotInfo FunctionPointerSlot = new SlotInfo("FunctionPointer", canInlineInto: true);
+
+ public readonly InstructionCollection Arguments;
+ ILInstruction functionPointer;
+
+ public Mono.Cecil.MethodCallingConvention CallingConvention { get; }
+ public IType ReturnType { get; }
+ public ImmutableArray ParameterTypes { get; }
+
+ ///
+ /// A 'final instruction' that gets executed after the Instructions collection.
+ /// Provides the return value for the block.
+ ///
+ ///
+ /// Blocks in containers must have 'Nop' as a final instruction.
+ ///
+ public ILInstruction FunctionPointer {
+ get {
+ return functionPointer;
+ }
+ set {
+ ValidateChild(value);
+ SetChildInstruction(ref functionPointer, value, Arguments.Count);
+ }
+ }
+
+ protected internal override void InstructionCollectionUpdateComplete()
+ {
+ base.InstructionCollectionUpdateComplete();
+ if (functionPointer?.Parent == this)
+ functionPointer.ChildIndex = Arguments.Count;
+ }
+
+ public CallIndirect(Mono.Cecil.MethodCallingConvention callingConvention, IType returnType, ImmutableArray parameterTypes,
+ IEnumerable arguments, ILInstruction functionPointer) : base(OpCode.CallIndirect)
+ {
+ this.CallingConvention = callingConvention;
+ this.ReturnType = returnType ?? throw new ArgumentNullException("returnType");
+ this.ParameterTypes = parameterTypes.ToImmutableArray();
+ this.Arguments = new InstructionCollection(this, 0);
+ this.Arguments.AddRange(arguments);
+ this.FunctionPointer = functionPointer;
+ }
+
+ public override ILInstruction Clone()
+ {
+ return new CallIndirect(CallingConvention, ReturnType, ParameterTypes,
+ this.Arguments.Select(inst => inst.Clone()), functionPointer.Clone()
+ ) {
+ ILRange = this.ILRange
+ };
+ }
+
+ public override StackType ResultType => ReturnType.GetStackType();
+
+ internal override void CheckInvariant(ILPhase phase)
+ {
+ base.CheckInvariant(phase);
+ Debug.Assert(Arguments.Count == ParameterTypes.Length);
+ }
+
+ public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
+ {
+ output.Write("call.indirect ");
+ ReturnType.WriteTo(output);
+ output.Write('(');
+ bool first = true;
+ foreach (var (inst, type) in Arguments.Zip(ParameterTypes, (a,b) => (a,b))) {
+ if (first)
+ first = false;
+ else
+ output.Write(", ");
+ inst.WriteTo(output, options);
+ output.Write(" : ");
+ type.WriteTo(output);
+ }
+ if (Arguments.Count > 0)
+ output.Write(", ");
+ functionPointer.WriteTo(output, options);
+ output.Write(')');
+ }
+
+ protected override int GetChildCount()
+ {
+ return Arguments.Count + 1;
+ }
+
+ protected override ILInstruction GetChild(int index)
+ {
+ if (index == Arguments.Count)
+ return functionPointer;
+ return Arguments[index];
+ }
+
+ protected override void SetChild(int index, ILInstruction value)
+ {
+ if (index == Arguments.Count)
+ FunctionPointer = value;
+ else
+ Arguments[index] = value;
+ }
+
+ protected override SlotInfo GetChildSlot(int index)
+ {
+ if (index == Arguments.Count)
+ return FunctionPointerSlot;
+ else
+ return ArgumentSlot;
+ }
+
+ protected override InstructionFlags ComputeFlags()
+ {
+ var flags = this.DirectFlags;
+ foreach (var inst in Arguments) {
+ flags |= inst.Flags;
+ }
+ flags |= functionPointer.Flags;
+ return flags;
+ }
+
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.MayThrow | InstructionFlags.SideEffect;
+ }
+ }
+
+ bool EqualSignature(CallIndirect other)
+ {
+ if (CallingConvention != other.CallingConvention)
+ return false;
+ if (ParameterTypes.Length != other.ParameterTypes.Length)
+ return false;
+ for (int i = 0; i < ParameterTypes.Length; i++) {
+ if (!ParameterTypes[i].Equals(other.ParameterTypes[i]))
+ return false;
+ }
+ return ReturnType.Equals(other.ReturnType);
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs b/ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs
index c52d0df6b..b07a0c835 100644
--- a/ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs
@@ -350,6 +350,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
} else if (type is GenericParameter) {
GenericParameter typeGP = (GenericParameter)type;
return TypeParameterReference.Create(typeGP.Owner is MethodReference ? SymbolKind.Method : SymbolKind.TypeDefinition, typeGP.Position);
+ } else if (type is FunctionPointerType) {
+ return KnownTypeReference.Get(KnownTypeCode.IntPtr);
} else if (type.IsNested) {
ITypeReference typeRef = CreateType(type.DeclaringType, typeAttributes, ref typeIndex);
int partTypeParameterCount;