diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 3bb3746fe..30867662e 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -108,10 +108,13 @@ namespace ICSharpCode.Decompiler.CSharp return new ExpressionWithResolveResult(expr, exprRR); } - public TranslatedExpression Translate(ILInstruction inst) + public TranslatedExpression Translate(ILInstruction inst, IType typeHint = null) { Debug.Assert(inst != null); - var cexpr = inst.AcceptVisitor(this, new TranslationContext()); + TranslationContext context = new TranslationContext { + TypeHint = typeHint ?? SpecialType.UnknownType + }; + var cexpr = inst.AcceptVisitor(this, context); #if DEBUG if (inst.ResultType != StackType.Void && cexpr.Type.Kind != TypeKind.Unknown) { if (inst.ResultType.IsIntegerType()) { @@ -127,7 +130,7 @@ namespace ICSharpCode.Decompiler.CSharp public TranslatedExpression TranslateCondition(ILInstruction condition) { - var expr = Translate(condition); + var expr = Translate(condition, compilation.FindType(KnownTypeCode.Boolean)); return expr.ConvertToBoolean(this); } @@ -206,11 +209,51 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitLocAlloc(LocAlloc inst, TranslationContext context) { - var byteType = compilation.FindType(KnownTypeCode.Byte); + IType elementType; + TranslatedExpression countExpression = TranslatePointerArgument(inst.Argument, context, out elementType); + countExpression = countExpression.ConvertTo(compilation.FindType(KnownTypeCode.Int32), this); return new StackAllocExpression { - Type = ConvertType(byteType), - CountExpression = Translate(inst.Argument) - }.WithILInstruction(inst).WithRR(new ResolveResult(new PointerType(byteType))); + Type = ConvertType(elementType ?? compilation.FindType(KnownTypeCode.Byte)), + CountExpression = countExpression + }.WithILInstruction(inst).WithRR(new ResolveResult(new PointerType(elementType))); + } + + /// + /// Translate the argument of an operation that deals with pointers: + /// * undoes the implicit multiplication with `sizeof(elementType)` and returns `elementType` + /// * on failure, translates the whole expression and returns `elementType = null`. + /// + TranslatedExpression TranslatePointerArgument(ILInstruction countExpr, TranslationContext context, out IType elementType) + { + ILInstruction left; + ILInstruction right; + if (countExpr.MatchBinaryNumericInstruction(BinaryNumericOperator.Mul, out left, out right) + && right.UnwrapConv(ConversionKind.SignExtend).UnwrapConv(ConversionKind.ZeroExtend).MatchSizeOf(out elementType)) + { + return Translate(left); + } + + var pointerTypeHint = context.TypeHint as PointerType; + if (pointerTypeHint == null) { + elementType = null; + return Translate(countExpr); + } + ResolveResult sizeofRR = resolver.ResolveSizeOf(pointerTypeHint.ElementType); + if (!(sizeofRR.IsCompileTimeConstant && sizeofRR.ConstantValue is int)) { + elementType = null; + return Translate(countExpr); + } + int typeSize = (int)sizeofRR.ConstantValue; + + if (countExpr.MatchBinaryNumericInstruction(BinaryNumericOperator.Mul, out left, out right) + && right.UnwrapConv(ConversionKind.SignExtend).UnwrapConv(ConversionKind.ZeroExtend).MatchLdcI4(typeSize)) + { + elementType = pointerTypeHint.ElementType; + return Translate(left); + } + + elementType = null; + return Translate(countExpr); } protected internal override TranslatedExpression VisitLdcI4(LdcI4 inst, TranslationContext context) @@ -346,7 +389,7 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitStLoc(StLoc inst, TranslationContext context) { - var translatedValue = Translate(inst.Value); + var translatedValue = Translate(inst.Value, typeHint: inst.Variable.Type); if (inst.Variable.Kind == VariableKind.StackSlot && inst.Variable.IsSingleDefinition && inst.Variable.StackType == translatedValue.Type.GetStackType() && translatedValue.Type.Kind != TypeKind.Null && !loadedVariablesSet.Contains(inst.Variable)) { @@ -1167,7 +1210,6 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitStObj(StObj inst, TranslationContext context) { var target = Translate(inst.Target); - var value = Translate(inst.Value); TranslatedExpression result; if (target.Expression is DirectionExpression && TypeUtils.IsCompatibleTypeForMemoryAccess(target.Type, inst.Type)) { // we can deference the managed reference by stripping away the 'ref' @@ -1181,6 +1223,7 @@ namespace ICSharpCode.Decompiler.CSharp .WithoutILInstruction() .WithRR(new ResolveResult(((TypeWithElementType)target.Type).ElementType)); } + var value = Translate(inst.Value, typeHint: result.Type); return Assignment(result, value).WithILInstruction(inst); } @@ -1261,7 +1304,7 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitBox(Box inst, TranslationContext context) { var obj = compilation.FindType(KnownTypeCode.Object); - var arg = Translate(inst.Argument).ConvertTo(inst.Type, this); + var arg = Translate(inst.Argument, typeHint: inst.Type).ConvertTo(inst.Type, this); return new CastExpression(ConvertType(obj), arg.Expression) .WithILInstruction(inst) .WithRR(new ConversionResolveResult(obj, arg.ResolveResult, Conversion.BoxingConversion)); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs index 0ccd6fdb9..01514aa0b 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs @@ -99,6 +99,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms pre.CopyAnnotationsFrom(memberReferenceExpression); memberReferenceExpression.ReplaceWith(pre); } + var rr = memberReferenceExpression.GetResolveResult(); + if (rr != null && rr.Type is PointerType) + return true; return result; } diff --git a/ICSharpCode.Decompiler/CSharp/TranslationContext.cs b/ICSharpCode.Decompiler/CSharp/TranslationContext.cs index 58d901ffc..87a8ee3f9 100644 --- a/ICSharpCode.Decompiler/CSharp/TranslationContext.cs +++ b/ICSharpCode.Decompiler/CSharp/TranslationContext.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using ICSharpCode.NRefactory.TypeSystem; namespace ICSharpCode.Decompiler.CSharp { @@ -25,6 +26,10 @@ namespace ICSharpCode.Decompiler.CSharp /// public struct TranslationContext { - + /// + /// The expected type during ILAst->C# translation; or SpecialType.Unknown + /// if no specific type is expected. + /// + public IType TypeHint; } } diff --git a/ICSharpCode.Decompiler/Tests/CorrectnessTestRunner.cs b/ICSharpCode.Decompiler/Tests/CorrectnessTestRunner.cs index 24f40be17..8deb5d3f6 100644 --- a/ICSharpCode.Decompiler/Tests/CorrectnessTestRunner.cs +++ b/ICSharpCode.Decompiler/Tests/CorrectnessTestRunner.cs @@ -130,7 +130,7 @@ namespace ICSharpCode.Decompiler.Tests TestAssembleDecompileCompileOutput("BitNot.il", CompilerOptions.UseDebug | CompilerOptions.Force32Bit, AssemblerOptions.Force32Bit); } - [Test, Ignore("Fixed statements are broken")] + [Test, Ignore("Pointer reference expression is not supported")] public void UnsafeCode() { TestCompileDecompileCompileOutputAll("UnsafeCode.cs"); diff --git a/ICSharpCode.Decompiler/Tests/TestCases/Correctness/UnsafeCode.cs b/ICSharpCode.Decompiler/Tests/TestCases/Correctness/UnsafeCode.cs index 79d393c1b..477136371 100644 --- a/ICSharpCode.Decompiler/Tests/TestCases/Correctness/UnsafeCode.cs +++ b/ICSharpCode.Decompiler/Tests/TestCases/Correctness/UnsafeCode.cs @@ -20,6 +20,12 @@ using System; public class UnsafeCode { + struct SimpleStruct + { + public int X; + public double Y; + } + static void Main() { // TODO: test behavior, or convert this into a pretty-test @@ -145,6 +151,7 @@ public class UnsafeCode public unsafe string StackAlloc(int count) { char* ptr = stackalloc char[count]; + char* ptr2 = stackalloc char[100]; for (int i = 0; i < count; i++) { ptr[i] = (char)i; @@ -152,6 +159,13 @@ public class UnsafeCode return this.PointerReferenceExpression((double*)ptr); } + public unsafe string StackAllocStruct(int count) + { + SimpleStruct* s = stackalloc SimpleStruct[checked(count * 2)]; + SimpleStruct* p = stackalloc SimpleStruct[10]; + return this.PointerReferenceExpression(&s->Y); + } + public unsafe int* PointerArithmetic(int* p) { return p + 2; diff --git a/NRefactory b/NRefactory index 19497cfae..5ca6d4172 160000 --- a/NRefactory +++ b/NRefactory @@ -1 +1 @@ -Subproject commit 19497cfae13c3dcebed7dfeb1cba549e4c2deded +Subproject commit 5ca6d4172c8540d01b65d120f46db416d29740e3