From 14c87caea4e30de383dd7edf296867e536ceb379 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 26 Jun 2016 18:27:58 +0200 Subject: [PATCH] Fix ref<->pointer conversions. --- .../CSharp/ExpressionBuilder.cs | 9 + .../CSharp/TranslatedExpression.cs | 13 ++ .../Tests/TestCases/UnsafeCode.cs | 176 ++++++++++++++++++ ICSharpCode.Decompiler/Tests/TestRunner.cs | 6 + 4 files changed, 204 insertions(+) create mode 100644 ICSharpCode.Decompiler/Tests/TestCases/UnsafeCode.cs diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 79a9c2586..863c82970 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -589,6 +589,15 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitConv(Conv inst) { var arg = Translate(inst.Argument); + if (inst.Kind == ConversionKind.StopGCTracking && arg.Type.Kind == TypeKind.ByReference) { + // cast to corresponding pointer type: + var pointerType = new PointerType(((ByReferenceType)arg.Type).ElementType); + arg = arg.ConvertTo(pointerType, this); + // if we want a native int, just return the pointer directly + if (inst.ResultType == StackType.I) + return arg; + // otherwise, continue converting + } if (inst.Sign != Sign.None && arg.Type.GetSign() != inst.Sign) { // we need to cast the input to a type of appropriate sign var inputType = inst.Argument.ResultType.ToKnownTypeCode(inst.Sign); diff --git a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs index 514803423..6db8f0289 100644 --- a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs @@ -176,6 +176,19 @@ namespace ICSharpCode.Decompiler.CSharp .WithRR(new ResolveResult(pointerType)); // perform remaining pointer cast, if necessary return pointerExpr.ConvertTo(targetType, expressionBuilder); + } else if (targetType.Kind == TypeKind.ByReference) { + // Convert from integer/pointer to reference. + // First, convert to the corresponding pointer type: + var elementType = ((ByReferenceType)targetType).ElementType; + var arg = this.ConvertTo(new PointerType(elementType), expressionBuilder, checkForOverflow, addUncheckedAnnotations); + // Then dereference the pointer: + var derefExpr = new UnaryOperatorExpression(UnaryOperatorType.Dereference, arg.Expression); + var elementRR = new ResolveResult(elementType); + derefExpr.AddAnnotation(elementRR); + // And then take a reference: + return new DirectionExpression(FieldDirection.Ref, derefExpr) + .WithoutILInstruction() + .WithRR(new ByReferenceResolveResult(elementRR, false)); } if (type.IsKnownType(KnownTypeCode.Boolean) && targetType.GetStackType().IsIntegerType()) { // convert from boolean to integer (or enum) diff --git a/ICSharpCode.Decompiler/Tests/TestCases/UnsafeCode.cs b/ICSharpCode.Decompiler/Tests/TestCases/UnsafeCode.cs new file mode 100644 index 000000000..5e2ca2554 --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/TestCases/UnsafeCode.cs @@ -0,0 +1,176 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team +// +// 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; + +public class UnsafeCode +{ + static void Main() + { + // TODO: test behavior, or convert this into a pretty-test + // (but for now, it's already valuable knowing that the decompiled code can be re-compiled) + } + + public unsafe int* NullPointer + { + get + { + return null; + } + } + + public unsafe long ConvertDoubleToLong(double d) + { + return *(long*)(&d); + } + + public unsafe double ConvertLongToDouble(long d) + { + return *(double*)(&d); + } + + public unsafe int ConvertFloatToInt(float d) + { + return *(int*)(&d); + } + + public unsafe float ConvertIntToFloat(int d) + { + return *(float*)(&d); + } + + public unsafe void PassRefParameterAsPointer(ref int p) + { + fixed (int* ptr = &p) + { + this.PassPointerAsRefParameter(ptr); + } + } + + public unsafe void PassPointerAsRefParameter(int* p) + { + this.PassRefParameterAsPointer(ref *p); + } + + public unsafe void AddressInMultiDimensionalArray(double[,] matrix) + { + fixed (double* ptr = &matrix[1, 2]) + { + this.PointerReferenceExpression(ptr); + } + } + + public unsafe void FixedStringAccess(string text) + { + fixed (char* ptr = text) + { + char* ptr2 = ptr; + while (*ptr2 != '\0') + { + *ptr2 = 'A'; + ptr2++; + } + } + } + + public unsafe void PutDoubleIntoLongArray1(long[] array, int index, double val) + { + fixed (long* ptr = array) + { + ((double*)ptr)[index] = val; + } + } + + public unsafe void PutDoubleIntoLongArray2(long[] array, int index, double val) + { + fixed (long* ptr = &array[index]) + { + *(double*)ptr = val; + } + } + + public unsafe string PointerReferenceExpression(double* d) + { + return d->ToString(); + } + + public unsafe void FixMultipleStrings(string text) + { + fixed (char* ptr = text, userName = Environment.UserName, ptr2 = text) + { + *ptr = 'c'; + *userName = 'd'; + *ptr2 = 'e'; + } + } + + public unsafe string StackAlloc(int count) + { + char* ptr = stackalloc char[count]; + for (int i = 0; i < count; i++) + { + ptr[i] = (char)i; + } + return this.PointerReferenceExpression((double*)ptr); + } + + public unsafe int* PointerArithmetic(int* p) + { + return p + 2; + } + + public unsafe byte* PointerArithmetic2(long* p, int y, int x) + { + return (byte*)((short*)p + (y * x)); + } + + public unsafe long* PointerArithmetic3(long* p) + { + return (long*)((byte*)p + 3); + } + + public unsafe long* PointerArithmetic4(void* p) + { + return (long*)((byte*)p + 3); + } + + public unsafe int PointerArithmetic5(void* p, byte* q, int i) + { + return (int)(q[i] + *(byte*)p); + } + + public unsafe int PointerSubtraction(long* p, long* q) + { + return (int)((long)(p - q)); + } + + public unsafe int PointerSubtraction2(long* p, short* q) + { + return (int)((long)((byte*)p - (byte*)q)); + } + + public unsafe int PointerSubtraction3(void* p, void* q) + { + return (int)((long)((byte*)p - (byte*)q)); + } + + unsafe ~UnsafeCode() + { + this.PassPointerAsRefParameter(this.NullPointer); + } +} diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs index 0e82bf1b0..b97d98d5a 100644 --- a/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -114,6 +114,12 @@ namespace ICSharpCode.Decompiler.Tests TestAssembleDecompileCompileOutput("ILTest.il"); } + [Test, Ignore("Fixed statements and pointer arithmetic are broken")] + public void UnsafeCode() + { + TestCompileDecompileCompileOutputAll("UnsafeCode.cs"); + } + void TestCompileDecompileCompileOutputAll(string testFileName) { TestCompileDecompileCompileOutput(testFileName, CompilerOptions.None);