Browse Source

Fix subtle issues with BitNot operator.

pull/728/head
Daniel Grunwald 9 years ago
parent
commit
3faca4372e
  1. 127
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 4
      ICSharpCode.Decompiler/IL/ILReader.cs
  3. 2
      ICSharpCode.Decompiler/IL/Instructions.cs
  4. 2
      ICSharpCode.Decompiler/IL/Instructions.tt
  5. 5
      ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs
  6. 52
      ICSharpCode.Decompiler/Tests/Helpers/Tester.cs
  7. 2
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  8. 8
      ICSharpCode.Decompiler/Tests/RoundtripAssembly.cs
  9. 96
      ICSharpCode.Decompiler/Tests/TestCases/BitNot.il
  10. 93
      ICSharpCode.Decompiler/Tests/TestCases/ILTest.il
  11. 10
      ICSharpCode.Decompiler/Tests/TestRunner.cs

127
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -279,34 +279,38 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitBitNot(BitNot inst) protected internal override TranslatedExpression VisitBitNot(BitNot inst)
{ {
var argument = Translate(inst.Argument); var argument = Translate(inst.Argument);
var compatibleType = argument.Type.GetEnumUnderlyingType();
var type = compatibleType.GetDefinition();
if (argument.Type.GetStackType().GetSize() < inst.ResultType.GetSize()
|| argument.Type.Kind == TypeKind.Enum && argument.Type.IsSmallIntegerType())
{
// Argument is undersized (even after implicit integral promotion to I4)
// -> we need to perform sign/zero-extension before the BitNot.
// Same if the argument is an enum based on a small integer type
// (those don't undergo numeric promotion in C# the way non-enum small integer types do).
argument = argument.ConvertTo(compilation.FindType(inst.ResultType.ToKnownTypeCode(argument.Type.GetSign())), this);
}
var type = argument.Type.GetDefinition();
if (type != null) { if (type != null) {
// Handle those types that don't support operator ~
// Note that it's OK to use a type that's larger than necessary.
switch (type.KnownTypeCode) { switch (type.KnownTypeCode) {
case KnownTypeCode.Boolean: case KnownTypeCode.Boolean:
case KnownTypeCode.Char: case KnownTypeCode.Char:
compatibleType = compilation.FindType(KnownTypeCode.UInt32); argument = argument.ConvertTo(compilation.FindType(KnownTypeCode.UInt32), this);
break; break;
case KnownTypeCode.IntPtr: case KnownTypeCode.IntPtr:
compatibleType = compilation.FindType(KnownTypeCode.Int64); argument = argument.ConvertTo(compilation.FindType(KnownTypeCode.Int64), this);
break; break;
case KnownTypeCode.UIntPtr: case KnownTypeCode.UIntPtr:
compatibleType = compilation.FindType(KnownTypeCode.UInt64); argument = argument.ConvertTo(compilation.FindType(KnownTypeCode.UInt64), this);
break; break;
} }
} }
argument = argument.ConvertTo(compatibleType, this); return new UnaryOperatorExpression(UnaryOperatorType.BitNot, argument)
var rr = resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, argument.ResolveResult); .WithRR(resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, argument.ResolveResult))
var result = new UnaryOperatorExpression(UnaryOperatorType.BitNot, argument)
.WithRR(rr)
.WithILInstruction(inst); .WithILInstruction(inst);
if (type != null && (type.KnownTypeCode == KnownTypeCode.IntPtr || type.KnownTypeCode == KnownTypeCode.UIntPtr)) {
return result.ConvertTo(type, this);
} else {
return result;
}
} }
ExpressionWithResolveResult LogicNot(TranslatedExpression expr) ExpressionWithResolveResult LogicNot(TranslatedExpression expr)
@ -510,44 +514,54 @@ namespace ICSharpCode.Decompiler.CSharp
var resolverWithOverflowCheck = resolver.WithCheckForOverflow(inst.CheckForOverflow); var resolverWithOverflowCheck = resolver.WithCheckForOverflow(inst.CheckForOverflow);
var left = Translate(inst.Left); var left = Translate(inst.Left);
var right = Translate(inst.Right); var right = Translate(inst.Right);
ResolveResult rr; left = PrepareArithmeticArgument(left, inst.Left.ResultType, inst.Sign);
if (left.Type.GetStackType() == StackType.I || right.Type.GetStackType() == StackType.I) { right = PrepareArithmeticArgument(right, inst.Right.ResultType, inst.Sign);
// IntPtr or pointers as input.
// C# doesn't allow adding IntPtr values, and adding pointer values has the wrong semantics var rr = resolverWithOverflowCheck.ResolveBinaryOperator(op, left.ResolveResult, right.ResolveResult);
// (adding number of elements instead of number of bytes), so switch to long/ulong in both cases. if (rr.IsError || rr.Type.GetStackType() != inst.ResultType
IType targetType; || !IsCompatibleWithSign(left.Type, inst.Sign) || !IsCompatibleWithSign(right.Type, inst.Sign))
if (inst.Sign == Sign.Unsigned) { {
targetType = compilation.FindType(KnownTypeCode.UInt64); // Left and right operands are incompatible, so convert them to a common type
} else { StackType targetStackType = inst.ResultType == StackType.I ? StackType.I8 : inst.ResultType;
targetType = compilation.FindType(KnownTypeCode.Int64); IType targetType = compilation.FindType(targetStackType.ToKnownTypeCode(inst.Sign));
}
left = left.ConvertTo(targetType, this); left = left.ConvertTo(targetType, this);
right = right.ConvertTo(targetType, this); right = right.ConvertTo(targetType, this);
rr = new OperatorResolveResult(targetType, BinaryOperatorExpression.GetLinqNodeType(op, inst.CheckForOverflow), left.ResolveResult, right.ResolveResult);
var resultExpr = new BinaryOperatorExpression(left.Expression, op, right.Expression)
.WithILInstruction(inst)
.WithRR(rr);
if (BinaryOperatorMightCheckForOverflow(op))
resultExpr.Expression.AddAnnotation(inst.CheckForOverflow ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation);
return resultExpr.ConvertTo(compilation.FindType(inst.ResultType.ToKnownTypeCode()), this);
} else {
rr = resolverWithOverflowCheck.ResolveBinaryOperator(op, left.ResolveResult, right.ResolveResult); rr = resolverWithOverflowCheck.ResolveBinaryOperator(op, left.ResolveResult, right.ResolveResult);
if (rr.IsError || rr.Type.GetStackType() != inst.ResultType
|| !IsCompatibleWithSign(left.Type, inst.Sign) || !IsCompatibleWithSign(right.Type, inst.Sign))
{
// Left and right operands are incompatible, so convert them to a common type
IType targetType = compilation.FindType(inst.ResultType.ToKnownTypeCode(inst.Sign));
left = left.ConvertTo(targetType, this);
right = right.ConvertTo(targetType, this);
rr = resolverWithOverflowCheck.ResolveBinaryOperator(op, left.ResolveResult, right.ResolveResult);
}
var resultExpr = new BinaryOperatorExpression(left.Expression, op, right.Expression)
.WithILInstruction(inst)
.WithRR(rr);
if (BinaryOperatorMightCheckForOverflow(op))
resultExpr.Expression.AddAnnotation(inst.CheckForOverflow ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation);
return resultExpr;
} }
var resultExpr = new BinaryOperatorExpression(left.Expression, op, right.Expression)
.WithILInstruction(inst)
.WithRR(rr);
if (BinaryOperatorMightCheckForOverflow(op))
resultExpr.Expression.AddAnnotation(inst.CheckForOverflow ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation);
return resultExpr;
}
/// <summary>
/// Handle oversized arguments needing truncation; and avoid IntPtr/pointers in arguments.
/// </summary>
TranslatedExpression PrepareArithmeticArgument(TranslatedExpression arg, StackType argStackType, Sign sign)
{
if (argStackType.IsIntegerType() && argStackType.GetSize() < arg.Type.GetSize()) {
// If the argument is oversized (needs truncation to match stack size of its ILInstruction),
// perform the truncation now.
arg = arg.ConvertTo(compilation.FindType(argStackType.ToKnownTypeCode(sign)), this);
}
if (arg.Type.GetStackType() == StackType.I) {
// None of the operators we might want to apply are supported by IntPtr/UIntPtr.
// Also, pointer arithmetic has different semantics (works in number of elements, not bytes).
// So any inputs of size StackType.I must be converted to long/ulong.
arg = arg.ConvertTo(compilation.FindType(StackType.I8.ToKnownTypeCode(sign)), this);
}
return arg;
}
/// <summary>
/// Gets whether <paramref name="type"/> has the specified <paramref name="sign"/>.
/// If <paramref name="sign"/> is None, always returns true.
/// </summary>
static bool IsCompatibleWithSign(IType type, Sign sign)
{
return sign == Sign.None || type.GetSign() == sign;
} }
static bool BinaryOperatorMightCheckForOverflow(BinaryOperatorType op) static bool BinaryOperatorMightCheckForOverflow(BinaryOperatorType op)
@ -564,15 +578,6 @@ namespace ICSharpCode.Decompiler.CSharp
} }
} }
/// <summary>
/// Gets whether <paramref name="type"/> has the specified <paramref name="sign"/>.
/// If <paramref name="sign"/> is None, always returns true.
/// </summary>
bool IsCompatibleWithSign(IType type, Sign sign)
{
return sign == Sign.None || type.GetSign() == sign;
}
protected internal override TranslatedExpression VisitShl(Shl inst) protected internal override TranslatedExpression VisitShl(Shl inst)
{ {
return HandleShift(inst, BinaryOperatorType.ShiftLeft); return HandleShift(inst, BinaryOperatorType.ShiftLeft);
@ -689,6 +694,8 @@ namespace ICSharpCode.Decompiler.CSharp
// If the target type is a small integer type, IL will implicitly sign- or zero-extend // If the target type is a small integer type, IL will implicitly sign- or zero-extend
// the result after the truncation back to StackType.I4. // the result after the truncation back to StackType.I4.
// (which means there's actually 3 conversions involved!) // (which means there's actually 3 conversions involved!)
// Note that we must handle truncation to small integer types ourselves:
// our caller only sees the StackType.I4 and doesn't know to truncate to the small type.
if (arg.Type.GetSize() <= inst.TargetType.GetSize() && arg.Type.GetSign() == inst.TargetType.GetSign()) { if (arg.Type.GetSize() <= inst.TargetType.GetSize() && arg.Type.GetSign() == inst.TargetType.GetSign()) {
// There's no actual truncation involved, and the result of the Conv instruction is extended // There's no actual truncation involved, and the result of the Conv instruction is extended
@ -989,17 +996,15 @@ namespace ICSharpCode.Decompiler.CSharp
if (arrayExpr.Type.Kind != TypeKind.Array) { if (arrayExpr.Type.Kind != TypeKind.Array) {
arrayExpr = arrayExpr.ConvertTo(compilation.FindType(KnownTypeCode.Array), this); arrayExpr = arrayExpr.ConvertTo(compilation.FindType(KnownTypeCode.Array), this);
} }
TranslatedExpression lenExpr;
if (inst.ResultType == StackType.I4) { if (inst.ResultType == StackType.I4) {
lenExpr = arrayExpr.Expression.Member("Length") return arrayExpr.Expression.Member("Length")
.WithILInstruction(inst) .WithILInstruction(inst)
.WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Int32))); .WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Int32)));
} else { } else {
lenExpr = arrayExpr.Expression.Member("LongLength") return arrayExpr.Expression.Member("LongLength")
.WithILInstruction(inst) .WithILInstruction(inst)
.WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Int64))); .WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Int64)));
} }
return lenExpr.ConvertTo(compilation.FindType(inst.ResultType.ToKnownTypeCode()), this);
} }
protected internal override TranslatedExpression VisitLdFlda(LdFlda inst) protected internal override TranslatedExpression VisitLdFlda(LdFlda inst)

4
ICSharpCode.Decompiler/IL/ILReader.cs

@ -337,7 +337,7 @@ namespace ICSharpCode.Decompiler.IL
case StackType.I4: case StackType.I4:
return Push(new Sub(new LdcI4(0), Pop(), checkForOverflow: false, sign: Sign.None)); return Push(new Sub(new LdcI4(0), Pop(), checkForOverflow: false, sign: Sign.None));
case StackType.I: case StackType.I:
return Push(new Sub(new Conv(new LdcI4(0), PrimitiveType.I, false, Sign.Signed), Pop(), checkForOverflow: false, sign: Sign.None)); return Push(new Sub(new Conv(new LdcI4(0), PrimitiveType.I, false, Sign.None), Pop(), checkForOverflow: false, sign: Sign.None));
case StackType.I8: case StackType.I8:
return Push(new Sub(new LdcI8(0), Pop(), checkForOverflow: false, sign: Sign.None)); return Push(new Sub(new LdcI8(0), Pop(), checkForOverflow: false, sign: Sign.None));
case StackType.F: case StackType.F:
@ -880,6 +880,8 @@ namespace ICSharpCode.Decompiler.IL
if (expectedType != inst.ResultType) { if (expectedType != inst.ResultType) {
if (expectedType == StackType.I && inst.ResultType == StackType.I4) { if (expectedType == StackType.I && inst.ResultType == StackType.I4) {
inst = new Conv(inst, PrimitiveType.I, false, Sign.None); inst = new Conv(inst, PrimitiveType.I, false, Sign.None);
} else if (inst is InvalidInstruction) {
((InvalidInstruction)inst).ExpectedResultType = expectedType;
} else { } else {
Warn($"Expected {expectedType}, but got {inst.ResultType}"); Warn($"Expected {expectedType}, but got {inst.ResultType}");
} }

2
ICSharpCode.Decompiler/IL/Instructions.cs

@ -452,7 +452,7 @@ namespace ICSharpCode.Decompiler.IL
public InvalidInstruction() : base(OpCode.InvalidInstruction) public InvalidInstruction() : base(OpCode.InvalidInstruction)
{ {
} }
public override StackType ResultType { get { return StackType.Void; } }
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
return InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable; return InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable;

2
ICSharpCode.Decompiler/IL/Instructions.tt

@ -37,7 +37,7 @@
OpCode[] opCodes = { OpCode[] opCodes = {
new OpCode("invalid", "Represents invalid IL. Semantically, this instruction is considered to throw some kind of exception.", new OpCode("invalid", "Represents invalid IL. Semantically, this instruction is considered to throw some kind of exception.",
CustomClassName("InvalidInstruction"), NoArguments, MayThrow, UnconditionalBranch), CustomClassName("InvalidInstruction"), NoArguments, MayThrow, HasFlag("InstructionFlags.EndPointUnreachable")),
new OpCode("nop", "No operation. Takes 0 arguments and returns void.", new OpCode("nop", "No operation. Takes 0 arguments and returns void.",
VoidResult, NoArguments), VoidResult, NoArguments),
new OpCode("ILFunction", "A container of IL blocks.", new OpCode("ILFunction", "A container of IL blocks.",

5
ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs

@ -40,12 +40,17 @@ namespace ICSharpCode.Decompiler.IL
partial class InvalidInstruction : SimpleInstruction partial class InvalidInstruction : SimpleInstruction
{ {
public string Message; public string Message;
public StackType ExpectedResultType = StackType.Unknown;
public InvalidInstruction(string message) : this() public InvalidInstruction(string message) : this()
{ {
this.Message = message; this.Message = message;
} }
public override StackType ResultType {
get { return ExpectedResultType; }
}
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
output.Write(OpCode); output.Write(OpCode);

52
ICSharpCode.Decompiler/Tests/Helpers/Tester.cs

@ -56,7 +56,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
public static string AssembleIL(string sourceFileName, AssemblerOptions options = AssemblerOptions.UseDebug) public static string AssembleIL(string sourceFileName, AssemblerOptions options = AssemblerOptions.UseDebug)
{ {
string ilasmPath = Path.Combine(Environment.GetEnvironmentVariable("windir"), @"Microsoft.NET\Framework\v4.0.30319\ilasm.exe"); string ilasmPath = Path.Combine(Environment.GetEnvironmentVariable("windir"), @"Microsoft.NET\Framework\v4.0.30319\ilasm.exe");
string outputFile = Path.GetTempFileName(); string outputFile = sourceFileName + ".exe";
string otherOptions = " "; string otherOptions = " ";
@ -155,34 +155,52 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
int result1 = Tester.Run(outputFile, out output1, out error1); int result1 = Tester.Run(outputFile, out output1, out error1);
int result2 = Tester.Run(decompiledOutputFile, out output2, out error2); int result2 = Tester.Run(decompiledOutputFile, out output2, out error2);
if (result1 != result2 || output1 != output2 || error1 != error2) { Assert.AreEqual(0, result1, "Exit code != 0; did the test case crash?" + Environment.NewLine + error1);
Console.WriteLine("original: " + outputFile); Assert.AreEqual(0, result2, "Exit code != 0; did the decompiled code crash?" + Environment.NewLine + error2);
Console.WriteLine("decompiled: " + decompiledOutputFile);
Console.WriteLine("Test {0} failed.", testFileName); if (output1 != output2 || error1 != error2) {
if (decompiledCodeFile != null) StringBuilder b = new StringBuilder();
Console.WriteLine("Decompiled code in {0}:line 1", decompiledCodeFile); b.AppendLine($"Test {testFileName} failed: output does not match.");
if (error1 == "" && error2 != "") { if (decompiledCodeFile != null) {
Console.WriteLine(error2); b.AppendLine($"Decompiled code in {decompiledCodeFile}:line 1");
} else { }
if (error1 != error2) {
b.AppendLine("Got different error output.");
b.AppendLine("Original error:");
b.AppendLine(error1);
b.AppendLine();
b.AppendLine("Error after de+re-compiling:");
b.AppendLine(error2);
b.AppendLine();
}
if (output1 != output2) {
string outputFileName = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(testFileName)); string outputFileName = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(testFileName));
File.WriteAllText(outputFileName + ".original.out", output1); File.WriteAllText(outputFileName + ".original.out", output1);
File.WriteAllText(outputFileName + ".decompiled.out", output2); File.WriteAllText(outputFileName + ".decompiled.out", output2);
int diffLine = 0; int diffLine = 0;
foreach (var pair in output1.Split('\n').Zip(output2.Split('\n'), Tuple.Create)) { string lastHeader = null;
Tuple<string, string> errorItem = null;
foreach (var pair in output1.Replace("\r", "").Split('\n').Zip(output2.Replace("\r", "").Split('\n'), Tuple.Create)) {
diffLine++; diffLine++;
if (pair.Item1 != pair.Item2) { if (pair.Item1 != pair.Item2) {
errorItem = pair;
break; break;
} }
if (pair.Item1.EndsWith(":", StringComparison.Ordinal)) {
lastHeader = pair.Item1;
}
}
b.AppendLine($"Output differs; first difference in line {diffLine}");
if (lastHeader != null) {
b.AppendLine(lastHeader);
} }
Console.WriteLine("Output: {0}.original.out:line {1}", outputFileName, diffLine); b.AppendLine($"{outputFileName}.original.out:line {diffLine}");
Console.WriteLine("Output: {0}.decompiled.out:line {1}", outputFileName, diffLine); b.AppendLine(errorItem.Item1);
b.AppendLine($"{outputFileName}.decompiled.out:line {diffLine}");
b.AppendLine(errorItem.Item2);
} }
Assert.Fail(b.ToString());
} }
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);
} }
} }
} }

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

@ -89,7 +89,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />
<None Include="TestCases\ConvTest.il" /> <None Include="TestCases\BitNot.il" />
<None Include="TestCases\ILTest.il" /> <None Include="TestCases\ILTest.il" />
<None Include="TestCases\Readme.txt" /> <None Include="TestCases\Readme.txt" />
</ItemGroup> </ItemGroup>

8
ICSharpCode.Decompiler/Tests/RoundtripAssembly.cs

@ -62,25 +62,25 @@ namespace ICSharpCode.Decompiler.Tests
} }
[Test] [Test]
public void Implicit_Conversions() public void ImplicitConversions()
{ {
RunWithOutput("Random Tests\\TestCases", "ImplicitConversions.exe"); RunWithOutput("Random Tests\\TestCases", "ImplicitConversions.exe");
} }
[Test] [Test]
public void Implicit_Conversions_32() public void ImplicitConversions_32()
{ {
RunWithOutput("Random Tests\\TestCases", "ImplicitConversions_32.exe"); RunWithOutput("Random Tests\\TestCases", "ImplicitConversions_32.exe");
} }
[Test] [Test]
public void Explicit_Conversions() public void ExplicitConversions()
{ {
RunWithOutput("Random Tests\\TestCases", "ExplicitConversions.exe"); RunWithOutput("Random Tests\\TestCases", "ExplicitConversions.exe");
} }
[Test] [Test]
public void Explicit_Conversions_32() public void ExplicitConversions_32()
{ {
RunWithOutput("Random Tests\\TestCases", "ExplicitConversions_32.exe"); RunWithOutput("Random Tests\\TestCases", "ExplicitConversions_32.exe");
} }

96
ICSharpCode.Decompiler/Tests/TestCases/BitNot.il

@ -0,0 +1,96 @@
.assembly extern mscorlib
{
.publickeytoken = ( b7 7a 5c 56 19 34 e0 89 )
.ver 4:0:0:0
}
.assembly 'ConvTest'
{
.ver 0:0:0:0
}
.module ConvTest.exe
.corflags 0x00000001 // ILOnly
.class private auto ansi abstract sealed beforefieldinit Program
extends [mscorlib]System.Object
{
.method public hidebysig static void Main (string[] args) cil managed
{
.maxstack 8
.entrypoint
ldc.i8 0xCCCCCCCCCCCCCCCC
call int64 Program::BitwiseComplementInNativeSize(int64)
call void Program::PrintHex(int64)
ldc.i4 0x99999999
call int64 Program::BitwiseComplementWithUndersizedValue(int32)
call void Program::PrintHex(int64)
ldc.i4 0x9999
call int32 Program::BitwiseComplementWithSmallInteger(uint16)
call void Program::PrintHex(int32)
ldc.i4 0x9999
call int32 Program::BitwiseComplementWithSmallEnum(valuetype Enums.EUInt16)
call void Program::PrintHex(int32)
ret
} // end of method Main
.method public static int64 BitwiseComplementInNativeSize(int64 val)
{
ldarg.0
conv.i // truncate 64-bits to native-size bits
not // negate those native size bits
conv.i8 // sign extend back to 64-bits
ret
}
.method public static int64 BitwiseComplementWithUndersizedValue(int32 val)
{
ldarg.0
conv.u8 // zero extend up to 64-bits
not // negate those 64-bits
ret
}
.method public static int32 BitwiseComplementWithSmallInteger(uint16 val)
{
ldarg.0 // zero extend up to 32-bits
not // negate those 32-bits
ret
}
.method public static int32 BitwiseComplementWithSmallEnum(valuetype Enums.EUInt16 val)
{
ldarg.0 // zero extend up to 32-bits
not // negate those 32-bits
ret
}
.method public static void PrintHex(int32 val)
{
ldstr "{0:x8}"
ldarg.0
box valuetype [mscorlib]System.UInt32
call void [mscorlib]System.Console::WriteLine(string, object)
ret
}
.method public static void PrintHex(int64 val)
{
ldstr "{0:x16}"
ldarg.0
box valuetype [mscorlib]System.UInt64
call void [mscorlib]System.Console::WriteLine(string, object)
ret
}
}
.class public auto ansi sealed Enums.EUInt16
extends [mscorlib]System.Enum
{
.field public specialname uint16 __value
}

93
ICSharpCode.Decompiler/Tests/TestCases/ILTest.il

@ -2,101 +2,28 @@
.assembly extern mscorlib .assembly extern mscorlib
{ {
.publickeytoken = ( .publickeytoken = ( b7 7a 5c 56 19 34 e0 89 )
b7 7a 5c 56 19 34 e0 89
)
.ver 4:0:0:0 .ver 4:0:0:0
} }
.assembly HelloWorld .assembly HelloWorld
{ {
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( .ver 0:0:0:0
01 00 08 00 00 00 00 00
)
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = (
01 00 01 00 54 02 16 57 72 61 70 4e 6f 6e 45 78
63 65 70 74 69 6f 6e 54 68 72 6f 77 73 01
)
.custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = (
01 00 07 01 00 00 00 00
)
.custom instance void [mscorlib]System.Reflection.AssemblyTitleAttribute::.ctor(string) = (
01 00 0a 48 65 6c 6c 6f 57 6f 72 6c 64 00 00
)
.custom instance void [mscorlib]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) = (
01 00 00 00 00
)
.custom instance void [mscorlib]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = (
01 00 00 00 00
)
.custom instance void [mscorlib]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = (
01 00 00 00 00
)
.custom instance void [mscorlib]System.Reflection.AssemblyProductAttribute::.ctor(string) = (
01 00 0a 48 65 6c 6c 6f 57 6f 72 6c 64 00 00
)
.custom instance void [mscorlib]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = (
01 00 0e 43 6f 70 79 72 69 67 68 74 20 32 30 31
36 00 00
)
.custom instance void [mscorlib]System.Reflection.AssemblyTrademarkAttribute::.ctor(string) = (
01 00 00 00 00
)
.custom instance void [mscorlib]System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = (
01 00 00 00 00
)
.custom instance void [mscorlib]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = (
01 00 1a 2e 4e 45 54 46 72 61 6d 65 77 6f 72 6b
2c 56 65 72 73 69 6f 6e 3d 76 34 2e 30 01 00 54
0e 14 46 72 61 6d 65 77 6f 72 6b 44 69 73 70 6c
61 79 4e 61 6d 65 10 2e 4e 45 54 20 46 72 61 6d
65 77 6f 72 6b 20 34
)
.hash algorithm 0x00008004 // SHA1
.ver 1:0:6020:38157
} }
.module HelloWorld.exe .module HelloWorld.exe
// MVID: {987E1A15-519A-400C-B879-759CFB7F990B} .corflags 0x00000001 // ILOnly
.corflags 0x00000003 // ILOnly, Required32Bit
.class private auto ansi '<Module>' .class private auto ansi abstract sealed beforefieldinit Program
{
} // end of class <Module>
.class private auto ansi beforefieldinit HelloWorld.Program
extends [mscorlib]System.Object extends [mscorlib]System.Object
{ {
// Methods .method public hidebysig static void Main (string[] args) cil managed
.method public hidebysig static
void Main (
string[] args
) cil managed
{ {
// Method begins at RVA 0x2050
// Code size 13 (0xd)
.maxstack 8 .maxstack 8
.entrypoint .entrypoint
IL_0000: nop ldstr "Hello World!"
IL_0001: ldstr "Hello World!" call void [mscorlib]System.Console::WriteLine(string)
IL_0006: call void [mscorlib]System.Console::WriteLine(string) ret
IL_000b: nop } // end of method Main
IL_000c: ret } // end of class <Module>
} // end of method Program::Main
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x205e
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method Program::.ctor
} // end of class HelloWorld.Program

10
ICSharpCode.Decompiler/Tests/TestRunner.cs

@ -23,7 +23,7 @@ namespace ICSharpCode.Decompiler.Tests
.Select(m => m.Name) .Select(m => m.Name)
.ToArray(); .ToArray();
foreach (var file in new DirectoryInfo(TestCasePath).EnumerateFiles()) { foreach (var file in new DirectoryInfo(TestCasePath).EnumerateFiles()) {
if (file.Extension == ".txt") if (file.Extension == ".txt" || file.Extension == ".exe")
continue; continue;
var testName = Path.GetFileNameWithoutExtension(file.Name); var testName = Path.GetFileNameWithoutExtension(file.Name);
Assert.Contains(testName, testNames); Assert.Contains(testName, testNames);
@ -121,6 +121,13 @@ namespace ICSharpCode.Decompiler.Tests
TestAssembleDecompileCompileOutput("ConvTest.il", CompilerOptions.UseDebug | CompilerOptions.Force32Bit, AssemblerOptions.Force32Bit); TestAssembleDecompileCompileOutput("ConvTest.il", CompilerOptions.UseDebug | CompilerOptions.Force32Bit, AssemblerOptions.Force32Bit);
} }
[Test]
public void BitNot()
{
TestAssembleDecompileCompileOutput("BitNot.il");
TestAssembleDecompileCompileOutput("BitNot.il", CompilerOptions.UseDebug | CompilerOptions.Force32Bit, AssemblerOptions.Force32Bit);
}
[Test, Ignore("Fixed statements are broken")] [Test, Ignore("Fixed statements are broken")]
public void UnsafeCode() public void UnsafeCode()
{ {
@ -170,7 +177,6 @@ namespace ICSharpCode.Decompiler.Tests
Tester.RunAndCompareOutput(testFileName, outputFile, decompiledOutputFile.PathToAssembly, decompiledCodeFile); Tester.RunAndCompareOutput(testFileName, outputFile, decompiledOutputFile.PathToAssembly, decompiledCodeFile);
File.Delete(decompiledCodeFile); File.Delete(decompiledCodeFile);
File.Delete(outputFile);
File.Delete(decompiledOutputFile.PathToAssembly); File.Delete(decompiledOutputFile.PathToAssembly);
} finally { } finally {
if (decompiledOutputFile != null) if (decompiledOutputFile != null)

Loading…
Cancel
Save