diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index ee4393ea7..0d3ef2bbe 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -137,6 +137,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index ae6860798..9b4184aa7 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -730,6 +730,12 @@ namespace ICSharpCode.Decompiler.Tests await RunForLibrary(cscOptions: cscOptions); } + [Test] + public async Task PointerArithmetic([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) + { + await RunForLibrary(cscOptions: cscOptions); + } + async Task RunForLibrary([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, Action configureDecompiler = null) { await Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, configureDecompiler); diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1918.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1918.cs index 969057eec..c64649147 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1918.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1918.cs @@ -11,7 +11,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty fixed (Guid* ptr = A_0) { void* ptr2 = ptr; - UIntPtr* ptr3 = (UIntPtr*)((byte*)ptr2 - sizeof(UIntPtr)); + UIntPtr* ptr3 = (UIntPtr*)ptr2 - 1; UIntPtr uIntPtr = *ptr3; try { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs index 4beb7148b..179a6f85d 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs @@ -222,16 +222,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty #if OPT byte* num = stackalloc byte[12]; *(int*)num = 1; - *(int*)(num - 4) = 2; - *(int*)(num - 8) = 3; + *((int*)num - 1) = 2; + *((int*)num - 2) = 3; int* ptr = (int*)num; Console.WriteLine(*ptr); return UsePointer((byte*)ptr); #else byte* ptr = stackalloc byte[12]; *(int*)ptr = 1; - *(int*)(ptr - 4) = 2; - *(int*)(ptr - 8) = 3; + *((int*)ptr - 1) = 2; + *((int*)ptr - 2) = 3; int* ptr2 = (int*)ptr; Console.WriteLine(*ptr2); return UsePointer((byte*)ptr2); diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PointerArithmetic.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PointerArithmetic.cs new file mode 100644 index 000000000..4fe487658 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PointerArithmetic.cs @@ -0,0 +1,127 @@ +using System; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + public class PointerArithmetic + { + public unsafe static void AssignmentVoidPointerToIntPointer(void* ptr) + { + ((int*)ptr)[2] = 1; + } + + public unsafe static int AccessVoidPointerToIntPointer(void* ptr) + { + return ((int*)ptr)[2]; + } + + public unsafe static void AssignmentLongPointerToIntPointer_2(long* ptr) + { + ((int*)ptr)[2] = 1; + } + + public unsafe static int AccessLongPointerToIntPointer_2(long* ptr) + { + return ((int*)ptr)[2]; + } + + public unsafe static void AssignmentLongPointerToIntPointer_3(long* ptr) + { + ((int*)ptr)[3] = 1; + } + + public unsafe static int AccessLongPointerToIntPointer_3(long* ptr) + { + return ((int*)ptr)[3]; + } + + public unsafe static void AssignmentGuidPointerToIntPointer(Guid* ptr) + { + ((int*)ptr)[2] = 1; + } + + public unsafe static int AccessGuidPointerToIntPointer(Guid* ptr) + { + return ((int*)ptr)[2]; + } + + public unsafe static uint AccessGuidPointerToUIntPointer(Guid* ptr) + { + return ((uint*)ptr)[2]; + } + + public unsafe static void AssignmentGuidPointerToDateTimePointer(Guid* ptr) + { + ((DateTime*)ptr)[2] = DateTime.Now; + } + + public unsafe static void AssignmentGuidPointerToDateTimePointerDefault(Guid* ptr) + { + ((DateTime*)ptr)[2] = default(DateTime); + } + + public unsafe static void AssignmentGuidPointerToDateTimePointer_2(Guid* ptr) + { + *(DateTime*)(ptr + 2) = DateTime.Now; + } + + public unsafe static void AssignmentGuidPointerToDateTimePointerDefault_2(Guid* ptr) + { + *(DateTime*)(ptr + 2) = default(DateTime); + } + + public unsafe static DateTime AccessGuidPointerToDateTimePointer(Guid* ptr) + { + return ((DateTime*)ptr)[2]; + } + + public unsafe static DateTime AccessGuidPointerToDateTimePointer_2(Guid* ptr) + { + return *(DateTime*)(ptr + 2); + } + + public unsafe static void AssignmentIntPointer(int* ptr) + { + ptr[2] = 1; + } + + public unsafe static int AccessIntPointer(int* ptr) + { + return ptr[2]; + } + + public unsafe static void AssignmentGuidPointer(Guid* ptr) + { + ptr[2] = Guid.NewGuid(); + } + + public unsafe static Guid AccessGuidPointer(Guid* ptr) + { + return ptr[2]; + } + + public unsafe static void AssignmentVoidPointerToGuidPointer(void* ptr) + { + ((Guid*)ptr)[2] = Guid.NewGuid(); + } + + public unsafe static Guid AccessVoidPointerToGuidPointer(void* ptr) + { + return ((Guid*)ptr)[2]; + } + + public unsafe static void AssignmentIntPointerToGuidPointer(int* ptr) + { + ((Guid*)ptr)[2] = Guid.NewGuid(); + } + + public unsafe static void AssignmentIntPointerToGuidPointer_2(int* ptr) + { + *(Guid*)(ptr + 2) = Guid.NewGuid(); + } + + public unsafe static Guid AccessIntPointerToGuidPointer(int* ptr) + { + return ((Guid*)ptr)[2]; + } + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs index 3b993096e..c0f738e1d 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs @@ -212,7 +212,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { int result = 0; *(float*)(&result) = 0.5f; - ((byte*)(&result))[3] = 3; + ((sbyte*)(&result))[3] = 3; + ((sbyte*)(&result))[3] = -1; return result; } diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 8398be47a..19c21fd34 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1220,7 +1220,7 @@ namespace ICSharpCode.Decompiler.CSharp /// Returns null if 'inst' is not performing pointer arithmetic. /// 'ptr - ptr' is not handled here, but in HandlePointerSubtraction()! /// - TranslatedExpression? HandlePointerArithmetic(BinaryNumericInstruction inst, TranslatedExpression left, TranslatedExpression right) + TranslatedExpression? HandlePointerArithmetic(BinaryNumericInstruction inst, TranslatedExpression left, TranslatedExpression right, TranslationContext context) { if (!(inst.Operator == BinaryNumericOperator.Add || inst.Operator == BinaryNumericOperator.Sub)) return null; @@ -1249,7 +1249,27 @@ namespace ICSharpCode.Decompiler.CSharp { return null; } - TranslatedExpression offsetExpr = GetPointerArithmeticOffset(byteOffsetInst, byteOffsetExpr, pointerType.ElementType, inst.CheckForOverflow) + TranslatedExpression? offsetExpressionFromTypeHint = null; + if (context.TypeHint.Kind == TypeKind.Pointer) + { + // We use the type hint if one of the following is true: + // * The current element type is a non-primitive struct. + // * The current element type has a different size than the type hint element type. + // This prevents the type hint from overriding in undesirable situations (eg changing char* to short*). + + var typeHint = (PointerType)context.TypeHint; + int elementTypeSize = pointerType.ElementType.GetSize(); + if (elementTypeSize == 0 || typeHint.ElementType.GetSize() != elementTypeSize) + { + offsetExpressionFromTypeHint = GetPointerArithmeticOffset(byteOffsetInst, byteOffsetExpr, typeHint.ElementType, inst.CheckForOverflow); + if (offsetExpressionFromTypeHint != null) + { + pointerType = typeHint; + } + } + } + TranslatedExpression offsetExpr = offsetExpressionFromTypeHint + ?? GetPointerArithmeticOffset(byteOffsetInst, byteOffsetExpr, pointerType.ElementType, inst.CheckForOverflow) ?? FallBackToBytePointer(); if (left.Type.Kind == TypeKind.Pointer) @@ -1534,7 +1554,7 @@ namespace ICSharpCode.Decompiler.CSharp } if (left.Type.Kind == TypeKind.Pointer || right.Type.Kind == TypeKind.Pointer) { - var ptrResult = HandlePointerArithmetic(inst, left, right); + var ptrResult = HandlePointerArithmetic(inst, left, right, context); if (ptrResult != null) return ptrResult.Value; }