diff --git a/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs b/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs index 43548e38d..a9e72564f 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs @@ -35,7 +35,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms protected override bool VisitChildren(AstNode node, object data) { bool result = false; - for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { + AstNode next; + for (AstNode child = node.FirstChild; child != null; child = next) { + // Store next to allow the loop to continue + // if the visitor removes/replaces child. + next = child.NextSibling; result |= child.AcceptVisitor(this, data); } if (result && node is EntityDeclaration && !(node is Accessor)) { diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index bd8543cf5..ce62816d7 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -349,15 +349,15 @@ namespace ICSharpCode.Decompiler.ILAst expr.ILRanges.Clear(); continue; case ILCode.__Brfalse: op = ILCode.LogicNot; break; - case ILCode.__Beq: op = ILCode.Ceq; break; + case ILCode.__Beq: op = ILCode.Ceq; break; case ILCode.__Bne_Un: op = ILCode.Cne; break; - case ILCode.__Bgt: op = ILCode.Cgt; break; + case ILCode.__Bgt: op = ILCode.Cgt; break; case ILCode.__Bgt_Un: op = ILCode.Cgt_Un; break; - case ILCode.__Ble: op = ILCode.Cle; break; + case ILCode.__Ble: op = ILCode.Cle; break; case ILCode.__Ble_Un: op = ILCode.Cle_Un; break; - case ILCode.__Blt: op = ILCode.Clt; break; + case ILCode.__Blt: op = ILCode.Clt; break; case ILCode.__Blt_Un: op = ILCode.Clt_Un; break; - case ILCode.__Bge: op = ILCode.Cge; break; + case ILCode.__Bge: op = ILCode.Cge; break; case ILCode.__Bge_Un: op = ILCode.Cge_Un; break; default: continue; @@ -436,9 +436,9 @@ namespace ICSharpCode.Decompiler.ILAst // Might be 'newobj(SomeDelegate, target, ldvirtftn(F, target))'. ILVariable target; if (expr.Arguments[0].Match(ILCode.Ldloc, out target) - && expr.Arguments[1].Code == ILCode.Ldvirtftn - && expr.Arguments[1].Arguments.Count == 1 - && expr.Arguments[1].Arguments[0].MatchLdloc(target)) + && expr.Arguments[1].Code == ILCode.Ldvirtftn + && expr.Arguments[1].Arguments.Count == 1 + && expr.Arguments[1].Arguments[0].MatchLdloc(target)) { // Remove the 'target' argument from the ldvirtftn instruction. // It's not needed in the translation to C#, and needs to be eliminated so that the target expression @@ -473,9 +473,9 @@ namespace ICSharpCode.Decompiler.ILAst // Start a new basic block if necessary if (currNode is ILLabel || - currNode is ILTryCatchBlock || // Counts as label - lastNode.IsConditionalControlFlow() || - lastNode.IsUnconditionalControlFlow()) + currNode is ILTryCatchBlock || // Counts as label + lastNode.IsConditionalControlFlow() || + lastNode.IsUnconditionalControlFlow()) { // Try to reuse the label ILLabel label = currNode as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++).ToString() }; @@ -669,32 +669,86 @@ namespace ICSharpCode.Decompiler.ILAst }); } - static void HandlePointerArithmetic(ILNode method) + void HandlePointerArithmetic(ILNode method) { foreach (ILExpression expr in method.GetSelfAndChildrenRecursive()) { List args = expr.Arguments; switch (expr.Code) { case ILCode.Localloc: - args[0] = DivideBySize(args[0], ((PointerType)expr.InferredType).ElementType); + { + PointerType type = expr.InferredType as PointerType; + if (type != null) { + ILExpression arg0 = args[0]; + ILExpression expr2 = expr; + DivideOrMultiplyBySize(ref expr2, ref arg0, type.ElementType, true); + // expr shouldn't change + if (expr2 != expr) + throw new InvalidOperationException(); + args[0] = arg0; + } break; + } case ILCode.Add: case ILCode.Add_Ovf: case ILCode.Add_Ovf_Un: + { + ILExpression arg0 = args[0]; + ILExpression arg1 = args[1]; if (expr.InferredType is PointerType) { - if (args[0].ExpectedType is PointerType) - args[1] = DivideBySize(args[1], ((PointerType)expr.InferredType).ElementType); - else if (args[1].ExpectedType is PointerType) - args[0] = DivideBySize(args[0], ((PointerType)expr.InferredType).ElementType); + if (arg0.ExpectedType is PointerType) { + DivideOrMultiplyBySize(ref arg0, ref arg1, ((PointerType)expr.InferredType).ElementType, true); + } else if (arg1.ExpectedType is PointerType) + DivideOrMultiplyBySize(ref arg1, ref arg0, ((PointerType)expr.InferredType).ElementType, true); } + args[0] = arg0; + args[1] = arg1; break; + } case ILCode.Sub: case ILCode.Sub_Ovf: case ILCode.Sub_Ovf_Un: + { + ILExpression arg0 = args[0]; + ILExpression arg1 = args[1]; if (expr.InferredType is PointerType) { - if (args[0].ExpectedType is PointerType) - args[1] = DivideBySize(args[1], ((PointerType)expr.InferredType).ElementType); + if (arg0.ExpectedType is PointerType && !(arg1.InferredType is PointerType)) + DivideOrMultiplyBySize(ref arg0, ref arg1, ((PointerType)expr.InferredType).ElementType, true); } + args[0] = arg0; + args[1] = arg1; break; + } + case ILCode.Conv_I8: + { + ILExpression arg0 = args[0]; + // conv.i8(div:intptr(p0 - p1)) + if (arg0.Code == ILCode.Div && arg0.InferredType.FullName == "System.IntPtr") + { + ILExpression dividend = arg0.Arguments[0]; + if (dividend.InferredType.FullName == "System.IntPtr" && + (dividend.Code == ILCode.Sub || dividend.Code == ILCode.Sub_Ovf || dividend.Code == ILCode.Sub_Ovf_Un)) + { + PointerType pointerType0 = dividend.Arguments[0].InferredType as PointerType; + PointerType pointerType1 = dividend.Arguments[1].InferredType as PointerType; + + if (pointerType0 != null && pointerType1 != null) { + if (pointerType0.ElementType.FullName == "System.Void" || + pointerType0.ElementType.FullName != pointerType1.ElementType.FullName) { + pointerType0 = pointerType1 = new PointerType(typeSystem.Byte); + dividend.Arguments[0] = Cast(dividend.Arguments[0], pointerType0); + dividend.Arguments[1] = Cast(dividend.Arguments[1], pointerType1); + } + + DivideOrMultiplyBySize(ref dividend, ref arg0, pointerType0.ElementType, false); + // dividend shouldn't change + if (args[0].Arguments[0] != dividend) + throw new InvalidOperationException(); + } + } + } + args[0] = arg0; + break; + } } } } @@ -720,12 +774,24 @@ namespace ICSharpCode.Decompiler.ILAst return expr; } - static ILExpression DivideBySize(ILExpression expr, TypeReference type) + static ILExpression Cast(ILExpression expr, TypeReference type) + { + return new ILExpression(ILCode.Castclass, type, expr) + { + InferredType = type, + ExpectedType = type + }; + } + + void DivideOrMultiplyBySize(ref ILExpression pointerExpr, ref ILExpression adjustmentExpr, TypeReference elementType, bool divide) { - expr = UnwrapIntPtrCast(expr); + adjustmentExpr = UnwrapIntPtrCast(adjustmentExpr); ILExpression sizeOfExpression; - switch (TypeAnalysis.GetInformationAmount(type)) { + switch (TypeAnalysis.GetInformationAmount(elementType)) { + case 0: // System.Void + pointerExpr = Cast(pointerExpr, new PointerType(typeSystem.Byte)); + goto case 1; case 1: case 8: sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 1); @@ -740,34 +806,41 @@ namespace ICSharpCode.Decompiler.ILAst sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 8); break; default: - sizeOfExpression = new ILExpression(ILCode.Sizeof, type); + sizeOfExpression = new ILExpression(ILCode.Sizeof, elementType); break; } - if (expr.Code == ILCode.Mul || expr.Code == ILCode.Mul_Ovf || expr.Code == ILCode.Mul_Ovf_Un) { - ILExpression mulArg = expr.Arguments[1]; - if (mulArg.Code == sizeOfExpression.Code && sizeOfExpression.Operand.Equals(mulArg.Operand)) - return UnwrapIntPtrCast(expr.Arguments[0]); + if (divide && (adjustmentExpr.Code == ILCode.Mul || adjustmentExpr.Code == ILCode.Mul_Ovf || adjustmentExpr.Code == ILCode.Mul_Ovf_Un) || + !divide && (adjustmentExpr.Code == ILCode.Div || adjustmentExpr.Code == ILCode.Div_Un)) { + ILExpression mulArg = adjustmentExpr.Arguments[1]; + if (mulArg.Code == sizeOfExpression.Code && sizeOfExpression.Operand.Equals(mulArg.Operand)) { + adjustmentExpr = UnwrapIntPtrCast(adjustmentExpr.Arguments[0]); + return; + } } - if (expr.Code == sizeOfExpression.Code) { - if (sizeOfExpression.Operand.Equals(expr.Operand)) - return new ILExpression(ILCode.Ldc_I4, 1); + if (adjustmentExpr.Code == sizeOfExpression.Code) { + if (sizeOfExpression.Operand.Equals(adjustmentExpr.Operand)) { + adjustmentExpr = new ILExpression(ILCode.Ldc_I4, 1); + return; + } - if (expr.Code == ILCode.Ldc_I4) { - int offsetInBytes = (int)expr.Operand; + if (adjustmentExpr.Code == ILCode.Ldc_I4) { + int offsetInBytes = (int)adjustmentExpr.Operand; int elementSize = (int)sizeOfExpression.Operand; - int offsetInElements = offsetInBytes / elementSize; - // ensure integer division - if (offsetInElements * elementSize == offsetInBytes) { - expr.Operand = offsetInElements; - return expr; + if (offsetInBytes % elementSize != 0) { + pointerExpr = Cast(pointerExpr, new PointerType(typeSystem.Byte)); + return; } + + adjustmentExpr.Operand = offsetInBytes / elementSize; + return; } } - return new ILExpression(ILCode.Div_Un, null, expr, sizeOfExpression); + if (!(sizeOfExpression.Code == ILCode.Ldc_I4 && (int)sizeOfExpression.Operand == 1)) + adjustmentExpr = new ILExpression(divide ? ILCode.Div_Un : ILCode.Mul, null, adjustmentExpr, sizeOfExpression); } public static void ReplaceVariables(ILNode node, Func variableMapping) diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index 1931d390d..6ed1f4dc0 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -1044,7 +1044,10 @@ namespace ICSharpCode.Decompiler.ILAst TypeReference leftPreferred = DoInferTypeForExpression(left, expectedType); if (leftPreferred is PointerType) { left.InferredType = left.ExpectedType = leftPreferred; - InferTypeForExpression(right, null); + TypeReference rightPreferred = InferTypeForExpression(right, null); + // subtracting two pointers is not a pointer + if (rightPreferred is PointerType) + return typeSystem.IntPtr; return leftPreferred; } if (IsEnum(leftPreferred)) { diff --git a/ICSharpCode.Decompiler/Tests/UnsafeCode.cs b/ICSharpCode.Decompiler/Tests/UnsafeCode.cs index 1a3bd6a0f..cce6d525c 100644 --- a/ICSharpCode.Decompiler/Tests/UnsafeCode.cs +++ b/ICSharpCode.Decompiler/Tests/UnsafeCode.cs @@ -130,7 +130,7 @@ public class UnsafeCode public unsafe byte* PointerArithmetic2(long* p, int y, int x) { - return (byte*)p + (y * x); + return (byte*)((short*)p + (y * x)); } public unsafe long* PointerArithmetic3(long* p) @@ -138,6 +138,31 @@ public class UnsafeCode 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);