Browse Source

Make implicit I4->I conversions explicit in ILAst.

pull/728/head
Daniel Grunwald 9 years ago
parent
commit
ae318eca02
  1. 147
      ICSharpCode.Decompiler/IL/ILReader.cs
  2. 2
      ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs
  3. 11
      ICSharpCode.Decompiler/IL/Instructions/Comp.cs
  4. 16
      ICSharpCode.Decompiler/IL/StackType.cs
  5. 6
      ICSharpCode.Decompiler/Tests/RoundtripAssembly.cs

147
ICSharpCode.Decompiler/IL/ILReader.cs

@ -194,6 +194,9 @@ namespace ICSharpCode.Decompiler.IL @@ -194,6 +194,9 @@ namespace ICSharpCode.Decompiler.IL
bool ok1 = enum1.MoveNext();
bool ok2 = enum2.MoveNext();
while (ok1 && ok2) {
if (enum1.Current.StackType != enum2.Current.StackType) {
Warn("Incompatible stack types: " + enum1.Current.StackType + " vs " + enum2.Current.StackType);
}
unionFind.Merge(enum1.Current, enum2.Current);
ok1 = enum1.MoveNext();
ok2 = enum2.MoveNext();
@ -330,8 +333,9 @@ namespace ICSharpCode.Decompiler.IL @@ -330,8 +333,9 @@ namespace ICSharpCode.Decompiler.IL
{
switch (PeekStackType()) {
case StackType.I4:
case StackType.I:
return Push(new Sub(new LdcI4(0), Pop(), checkForOverflow: false, sign: Sign.None));
case StackType.I:
return Push(new Sub(new Conv(new LdcI4(0), PrimitiveType.I, false, Sign.Signed), Pop(), checkForOverflow: false, sign: Sign.None));
case StackType.I8:
return Push(new Sub(new LdcI8(0), Pop(), checkForOverflow: false, sign: Sign.None));
case StackType.F:
@ -560,27 +564,27 @@ namespace ICSharpCode.Decompiler.IL @@ -560,27 +564,27 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Ldftn:
return Push(new LdFtn(ReadAndDecodeMethodReference()));
case ILOpCode.Ldind_I1:
return Push(new LdObj(Pop(), compilation.FindType(KnownTypeCode.SByte)));
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.SByte)));
case ILOpCode.Ldind_I2:
return Push(new LdObj(Pop(), compilation.FindType(KnownTypeCode.Int16)));
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Int16)));
case ILOpCode.Ldind_I4:
return Push(new LdObj(Pop(), compilation.FindType(KnownTypeCode.Int32)));
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Int32)));
case ILOpCode.Ldind_I8:
return Push(new LdObj(Pop(), compilation.FindType(KnownTypeCode.Int64)));
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Int64)));
case ILOpCode.Ldind_U1:
return Push(new LdObj(Pop(), compilation.FindType(KnownTypeCode.Byte)));
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Byte)));
case ILOpCode.Ldind_U2:
return Push(new LdObj(Pop(), compilation.FindType(KnownTypeCode.UInt16)));
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.UInt16)));
case ILOpCode.Ldind_U4:
return Push(new LdObj(Pop(), compilation.FindType(KnownTypeCode.UInt32)));
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.UInt32)));
case ILOpCode.Ldind_R4:
return Push(new LdObj(Pop(), compilation.FindType(KnownTypeCode.Single)));
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Single)));
case ILOpCode.Ldind_R8:
return Push(new LdObj(Pop(), compilation.FindType(KnownTypeCode.Double)));
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Double)));
case ILOpCode.Ldind_I:
return Push(new LdObj(Pop(), compilation.FindType(KnownTypeCode.IntPtr)));
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.IntPtr)));
case ILOpCode.Ldind_Ref:
return Push(new LdObj(Pop(), compilation.FindType(KnownTypeCode.Object)));
return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Object)));
case ILOpCode.Ldloc:
return Push(Ldloc(reader.ReadUInt16()));
case ILOpCode.Ldloc_S:
@ -636,21 +640,21 @@ namespace ICSharpCode.Decompiler.IL @@ -636,21 +640,21 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Starg_S:
return Starg(reader.ReadByte());
case ILOpCode.Stind_I1:
return new StObj(value: Pop(), target: Pop(), type: compilation.FindType(KnownTypeCode.SByte));
return new StObj(value: Pop(StackType.I4), target: PopPointer(), type: compilation.FindType(KnownTypeCode.SByte));
case ILOpCode.Stind_I2:
return new StObj(value: Pop(), target: Pop(), type: compilation.FindType(KnownTypeCode.Int16));
return new StObj(value: Pop(StackType.I4), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Int16));
case ILOpCode.Stind_I4:
return new StObj(value: Pop(), target: Pop(), type: compilation.FindType(KnownTypeCode.Int32));
return new StObj(value: Pop(StackType.I4), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Int32));
case ILOpCode.Stind_I8:
return new StObj(value: Pop(), target: Pop(), type: compilation.FindType(KnownTypeCode.Int64));
return new StObj(value: Pop(StackType.I8), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Int64));
case ILOpCode.Stind_R4:
return new StObj(value: Pop(), target: Pop(), type: compilation.FindType(KnownTypeCode.Single));
return new StObj(value: Pop(StackType.F), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Single));
case ILOpCode.Stind_R8:
return new StObj(value: Pop(), target: Pop(), type: compilation.FindType(KnownTypeCode.Double));
return new StObj(value: Pop(StackType.F), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Double));
case ILOpCode.Stind_I:
return new StObj(value: Pop(), target: Pop(), type: compilation.FindType(KnownTypeCode.IntPtr));
return new StObj(value: Pop(StackType.I), target: PopPointer(), type: compilation.FindType(KnownTypeCode.IntPtr));
case ILOpCode.Stind_Ref:
return new StObj(value: Pop(), target: Pop(), type: compilation.FindType(KnownTypeCode.Object));
return new StObj(value: Pop(StackType.O), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Object));
case ILOpCode.Stloc:
return Stloc(reader.ReadUInt16());
case ILOpCode.Stloc_S:
@ -671,19 +675,22 @@ namespace ICSharpCode.Decompiler.IL @@ -671,19 +675,22 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Xor:
return BinaryNumeric(OpCode.BitXor);
case ILOpCode.Box:
return Push(new Box(Pop(), ReadAndDecodeTypeReference()));
{
var type = ReadAndDecodeTypeReference();
return Push(new Box(Pop(type.GetStackType()), type));
}
case ILOpCode.Castclass:
return Push(new CastClass(Pop(), ReadAndDecodeTypeReference()));
return Push(new CastClass(Pop(StackType.O), ReadAndDecodeTypeReference()));
case ILOpCode.Cpobj:
{
var type = ReadAndDecodeTypeReference();
var ld = new LdObj(Pop(), type);
return new StObj(Pop(), ld, type);
var ld = new LdObj(PopPointer(), type);
return new StObj(PopPointer(), ld, type);
}
case ILOpCode.Initobj:
return InitObj(Pop(), ReadAndDecodeTypeReference());
return InitObj(PopPointer(), ReadAndDecodeTypeReference());
case ILOpCode.Isinst:
return Push(new IsInst(Pop(), ReadAndDecodeTypeReference()));
return Push(new IsInst(Pop(StackType.O), ReadAndDecodeTypeReference()));
case ILOpCode.Ldelem:
return LdElem(ReadAndDecodeTypeReference());
case ILOpCode.Ldelem_I1:
@ -715,23 +722,29 @@ namespace ICSharpCode.Decompiler.IL @@ -715,23 +722,29 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Ldflda:
return Push(new LdFlda(Pop(), ReadAndDecodeFieldReference()));
case ILOpCode.Stfld:
return new StFld(value: Pop(), target: Pop(), field: ReadAndDecodeFieldReference());
{
var field = ReadAndDecodeFieldReference();
return new StFld(value: Pop(field.Type.GetStackType()), target: Pop(), field: field);
}
case ILOpCode.Ldlen:
return Push(new LdLen(StackType.I, Pop()));
case ILOpCode.Ldobj:
return Push(new LdObj(Pop(), ReadAndDecodeTypeReference()));
return Push(new LdObj(PopPointer(), ReadAndDecodeTypeReference()));
case ILOpCode.Ldsfld:
return Push(new LdsFld(ReadAndDecodeFieldReference()));
case ILOpCode.Ldsflda:
return Push(new LdsFlda(ReadAndDecodeFieldReference()));
case ILOpCode.Stsfld:
return new StsFld(Pop(), ReadAndDecodeFieldReference());
{
var field = ReadAndDecodeFieldReference();
return new StsFld(Pop(field.Type.GetStackType()), field);
}
case ILOpCode.Ldtoken:
return Push(LdToken(ReadAndDecodeMetadataToken()));
case ILOpCode.Ldvirtftn:
return Push(new LdVirtFtn(Pop(), ReadAndDecodeMethodReference()));
case ILOpCode.Mkrefany:
return Push(new MakeRefAny(Pop(), ReadAndDecodeTypeReference()));
return Push(new MakeRefAny(PopPointer(), ReadAndDecodeTypeReference()));
case ILOpCode.Newarr:
return Push(new NewArr(ReadAndDecodeTypeReference(), Pop()));
case ILOpCode.Refanytype:
@ -761,7 +774,10 @@ namespace ICSharpCode.Decompiler.IL @@ -761,7 +774,10 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Stelem_Ref:
return StElem(compilation.FindType(KnownTypeCode.Object));
case ILOpCode.Stobj:
return new StObj(value: Pop(), target: Pop(), type: ReadAndDecodeTypeReference());
{
var type = ReadAndDecodeTypeReference();
return new StObj(value: Pop(type.GetStackType()), target: PopPointer(), type: type);
}
case ILOpCode.Throw:
return new Throw(Pop());
case ILOpCode.Unbox:
@ -856,12 +872,42 @@ namespace ICSharpCode.Decompiler.IL @@ -856,12 +872,42 @@ namespace ICSharpCode.Decompiler.IL
return new LdLoc(v);
}
ILInstruction Pop(StackType expectedType)
{
ILInstruction inst = Pop();
if (expectedType != inst.ResultType) {
if (expectedType == StackType.I && inst.ResultType == StackType.I4) {
inst = new Conv(inst, PrimitiveType.I, false, Sign.Signed);
} else {
Warn($"Expected {expectedType}, but got {inst.ResultType}");
}
}
return inst;
}
ILInstruction PopPointer()
{
ILInstruction inst = Pop();
switch (inst.ResultType) {
case StackType.I4:
return new Conv(inst, PrimitiveType.I, false, Sign.Signed);
case StackType.I:
case StackType.Ref:
case StackType.Unknown:
return inst;
default:
Warn("Expected native int or pointer, but got " + inst.ResultType);
return inst;
}
}
private ILInstruction Return()
{
if (body.Method.ReturnType.GetStackType() == StackType.Void)
var stackType = body.Method.ReturnType.GetStackType();
if (stackType == StackType.Void)
return new IL.Return();
else
return new IL.Return(Pop());
return new IL.Return(Pop(stackType));
}
private ILInstruction DecodeLdstr()
@ -882,7 +928,7 @@ namespace ICSharpCode.Decompiler.IL @@ -882,7 +928,7 @@ namespace ICSharpCode.Decompiler.IL
private ILInstruction Starg(ushort v)
{
return new StLoc(parameterVariables[v], Pop());
return new StLoc(parameterVariables[v], Pop(parameterVariables[v].StackType));
}
private ILInstruction Ldloc(ushort v)
@ -897,7 +943,7 @@ namespace ICSharpCode.Decompiler.IL @@ -897,7 +943,7 @@ namespace ICSharpCode.Decompiler.IL
private ILInstruction Stloc(ushort v)
{
return new StLoc(localVariables[v], Pop());
return new StLoc(localVariables[v], Pop(localVariables[v].StackType));
}
private ILInstruction LdElem(IType type)
@ -907,7 +953,7 @@ namespace ICSharpCode.Decompiler.IL @@ -907,7 +953,7 @@ namespace ICSharpCode.Decompiler.IL
private ILInstruction StElem(IType type)
{
var value = Pop();
var value = Pop(type.GetStackType());
var index = Pop();
var array = Pop();
return new StObj(new LdElema(type, array, index), value, type);
@ -978,9 +1024,13 @@ namespace ICSharpCode.Decompiler.IL @@ -978,9 +1024,13 @@ namespace ICSharpCode.Decompiler.IL
ILInstruction DecodeCall(OpCode opCode)
{
var method = ReadAndDecodeMethodReference();
var arguments = new ILInstruction[GetPopCount(opCode, method)];
for (int i = arguments.Length - 1; i >= 0; i--) {
arguments[i] = Pop();
int firstArgument = (opCode != OpCode.NewObj && !method.IsStatic) ? 1 : 0;
var arguments = new ILInstruction[firstArgument + method.Parameters.Count];
for (int i = method.Parameters.Count - 1; i >= 0; i--) {
arguments[firstArgument + i] = Pop(method.Parameters[i].Type.GetStackType());
}
if (firstArgument == 1) {
arguments[0] = Pop();
}
switch (method.DeclaringType.Kind) {
case TypeKind.Array:
@ -1034,6 +1084,13 @@ namespace ICSharpCode.Decompiler.IL @@ -1034,6 +1084,13 @@ namespace ICSharpCode.Decompiler.IL
{
var right = Pop();
var left = Pop();
// make the implicit I4->I conversion explicit:
if (left.ResultType == StackType.I4 && right.ResultType == StackType.I) {
left = new Conv(left, PrimitiveType.I, false, Sign.Signed);
} else if (left.ResultType == StackType.I && right.ResultType == StackType.I4) {
right = new Conv(right, PrimitiveType.I, false, Sign.Signed);
}
// Based on Table 4: Binary Comparison or Branch Operation
if (left.ResultType == StackType.F && right.ResultType == StackType.F) {
if (un) {
@ -1079,7 +1136,7 @@ namespace ICSharpCode.Decompiler.IL @@ -1079,7 +1136,7 @@ namespace ICSharpCode.Decompiler.IL
// introduce explicit comparison with 0
condition = new Comp(
negate ? ComparisonKind.Equality : ComparisonKind.Inequality,
Sign.None, condition, new LdcI4(0));
Sign.None, condition, new Conv(new LdcI4(0), PrimitiveType.I, false, Sign.Signed));
break;
case StackType.I8:
// introduce explicit comparison with 0
@ -1118,7 +1175,7 @@ namespace ICSharpCode.Decompiler.IL @@ -1118,7 +1175,7 @@ namespace ICSharpCode.Decompiler.IL
{
uint length = reader.ReadUInt32();
int baseOffset = 4 * (int)length + reader.Position;
var instr = new SwitchInstruction(Pop());
var instr = new SwitchInstruction(Pop(StackType.I4));
for (uint i = 0; i < length; i++) {
var section = new SwitchSection();
@ -1136,6 +1193,14 @@ namespace ICSharpCode.Decompiler.IL @@ -1136,6 +1193,14 @@ namespace ICSharpCode.Decompiler.IL
{
var right = Pop();
var left = Pop();
if (opCode != OpCode.Shl && opCode != OpCode.Shr) {
// make the implicit I4->I conversion explicit:
if (left.ResultType == StackType.I4 && right.ResultType == StackType.I) {
left = new Conv(left, PrimitiveType.I, false, Sign.Signed);
} else if (left.ResultType == StackType.I && right.ResultType == StackType.I4) {
right = new Conv(right, PrimitiveType.I, false, Sign.Signed);
}
}
return Push(BinaryNumericInstruction.Create(opCode, left, right, checkForOverflow, sign));
}

2
ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs

@ -99,8 +99,6 @@ namespace ICSharpCode.Decompiler.IL @@ -99,8 +99,6 @@ namespace ICSharpCode.Decompiler.IL
return StackType.Ref;
}
}
if (left == StackType.I || right == StackType.I)
return StackType.I;
return StackType.Unknown;
}

11
ICSharpCode.Decompiler/IL/Instructions/Comp.cs

@ -119,17 +119,10 @@ namespace ICSharpCode.Decompiler.IL @@ -119,17 +119,10 @@ namespace ICSharpCode.Decompiler.IL
{
this.kind = kind;
this.Sign = sign;
this.inputType = ComputeInputType(left.ResultType, right.ResultType);
this.inputType = left.ResultType;
Debug.Assert(left.ResultType == right.ResultType);
}
static StackType ComputeInputType(StackType left, StackType right)
{
if (left == StackType.I || right == StackType.I)
return StackType.I;
Debug.Assert(left == right);
return left;
}
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);

16
ICSharpCode.Decompiler/IL/StackType.cs

@ -31,12 +31,28 @@ namespace ICSharpCode.Decompiler.IL @@ -31,12 +31,28 @@ namespace ICSharpCode.Decompiler.IL
{
Unknown,
/// <summary>32-bit integer</summary>
/// <remarks>
/// Used for C# <c>int</c>, <c>uint</c>,
/// C# small integer types <c>byte</c>, <c>sbyte</c>, <c>short</c>, <c>ushort</c>,
/// <c>bool</c> and <c>char</c>,
/// and any enums with one of the above as underlying type.
/// </remarks>
I4,
/// <summary>64-bit integer</summary>
/// <remarks>
/// Used for C# <c>long</c>, <c>ulong</c>,
/// and any enums with one of the above as underlying type.
/// </remarks>
I8,
/// <summary>native-size integer, or unmanaged pointer</summary>
/// <remarks>
/// Used for C# <c>IntPtr</c>, <c>UIntPtr</c> and any native pointer types (<c>void*</c> etc.)
/// </remarks>
I,
/// <summary>Floating point number</summary>
/// <remarks>
/// Used for C# <c>float</c> and <c>double</c>.
/// </remarks>
F,
/// <summary>Another stack type. Includes objects, value types, function pointers, ...</summary>
O,

6
ICSharpCode.Decompiler/Tests/RoundtripAssembly.cs

@ -53,11 +53,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -53,11 +53,7 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void Random_Tests_Implicit_Conversions()
{
try {
RunWithOutput("Random Tests\\TestCases", "ImplicitConversions.exe");
} catch (AssertionException ex) {
Assert.Ignore(ex.Message);
}
RunWithOutput("Random Tests\\TestCases", "ImplicitConversions.exe");
}
[Test]

Loading…
Cancel
Save