From d75da053a8c31070629e993d3577cbb9cb954ec5 Mon Sep 17 00:00:00 2001 From: ds5678 <49847914+ds5678@users.noreply.github.com> Date: Tue, 25 Feb 2025 14:03:59 -0800 Subject: [PATCH] Use type hint in pointer arithmetic when appropriate --- .../ICSharpCode.Decompiler.Tests.csproj | 1 + .../PrettyTestRunner.cs | 6 ++ .../Pretty/CS73_StackAllocInitializers.cs | 8 +- .../TestCases/Pretty/PointerArithmetic.cs | 87 +++++++++++++++++++ .../TestCases/Pretty/UnsafeCode.cs | 3 +- .../CSharp/ExpressionBuilder.cs | 18 +++- 6 files changed, 116 insertions(+), 7 deletions(-) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/PointerArithmetic.cs diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index f19374fd5..df5f31ec5 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -136,6 +136,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/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..07991187e --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PointerArithmetic.cs @@ -0,0 +1,87 @@ +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 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 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 90df1890a..4d046fbf2 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,6 +1249,20 @@ namespace ICSharpCode.Decompiler.CSharp { return 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) + { + pointerType = typeHint; + } + } TranslatedExpression offsetExpr = GetPointerArithmeticOffset(byteOffsetInst, byteOffsetExpr, pointerType.ElementType, inst.CheckForOverflow) ?? FallBackToBytePointer(); @@ -1534,7 +1548,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; }