Browse Source

#899: Add calli opcode to ILAst

pull/904/head
Daniel Grunwald 8 years ago
parent
commit
ebab7d5de0
  1. 13
      ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
  2. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  3. 35
      ICSharpCode.Decompiler/IL/ILReader.cs
  4. 40
      ICSharpCode.Decompiler/IL/Instructions.cs
  5. 6
      ICSharpCode.Decompiler/IL/Instructions.tt
  6. 170
      ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs
  7. 2
      ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs

13
ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs

@ -289,6 +289,19 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -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);

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

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

35
ICSharpCode.Decompiler/IL/ILReader.cs

@ -443,7 +443,7 @@ namespace ICSharpCode.Decompiler.IL @@ -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 @@ -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 @@ -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;

40
ICSharpCode.Decompiler/IL/Instructions.cs

@ -83,6 +83,8 @@ namespace ICSharpCode.Decompiler.IL @@ -83,6 +83,8 @@ namespace ICSharpCode.Decompiler.IL
Call,
/// <summary>Virtual method call.</summary>
CallVirt,
/// <summary>Unsafe function pointer call.</summary>
CallIndirect,
/// <summary>Checks that the input float is not NaN or infinite.</summary>
Ckfinite,
/// <summary>Numeric cast.</summary>
@ -1954,6 +1956,31 @@ namespace ICSharpCode.Decompiler.IL @@ -1954,6 +1956,31 @@ namespace ICSharpCode.Decompiler.IL
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Unsafe function pointer call.</summary>
public sealed partial class CallIndirect : ILInstruction
{
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitCallIndirect(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitCallIndirect(this);
}
public override T AcceptVisitor<C, T>(ILVisitor<C, T> 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
{
/// <summary>Checks that the input float is not NaN or infinite.</summary>
public sealed partial class Ckfinite : UnaryInstruction
@ -4769,6 +4796,10 @@ namespace ICSharpCode.Decompiler.IL @@ -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 @@ -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 @@ -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 @@ -5565,6 +5604,7 @@ namespace ICSharpCode.Decompiler.IL
"comp",
"call",
"callvirt",
"calli",
"ckfinite",
"conv",
"ldloc",

6
ICSharpCode.Decompiler/IL/Instructions.tt

@ -135,6 +135,12 @@ @@ -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.",

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

@ -0,0 +1,170 @@ @@ -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<ILInstruction> Arguments;
ILInstruction functionPointer;
public Mono.Cecil.MethodCallingConvention CallingConvention { get; }
public IType ReturnType { get; }
public ImmutableArray<IType> ParameterTypes { get; }
/// <summary>
/// A 'final instruction' that gets executed after the <c>Instructions</c> collection.
/// Provides the return value for the block.
/// </summary>
/// <remarks>
/// Blocks in containers must have 'Nop' as a final instruction.
/// </remarks>
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<IType> parameterTypes,
IEnumerable<ILInstruction> arguments, ILInstruction functionPointer) : base(OpCode.CallIndirect)
{
this.CallingConvention = callingConvention;
this.ReturnType = returnType ?? throw new ArgumentNullException("returnType");
this.ParameterTypes = parameterTypes.ToImmutableArray();
this.Arguments = new InstructionCollection<ILInstruction>(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);
}
}
}

2
ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs

@ -350,6 +350,8 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -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;

Loading…
Cancel
Save