Browse Source

Add support for pointer arithmetic (ptr + offset, offset + ptr, ptr - offset).

pull/924/head
Daniel Grunwald 8 years ago
parent
commit
19a7d8f7ff
  1. 8
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/UnsafeCode.cs
  2. 118
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  3. 22
      ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs

8
ICSharpCode.Decompiler.Tests/TestCases/Correctness/UnsafeCode.cs

@ -66,6 +66,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -66,6 +66,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
return *(float*)(&d);
}
public unsafe int PointerCasts()
{
int i = 0;
*(float*)&i = 0.5f;
((byte*)&i)[3] = 3;
return i;
}
public unsafe void PassRefParameterAsPointer(ref int p)
{
fixed (int* ptr = &p) {

118
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -662,15 +662,128 @@ namespace ICSharpCode.Decompiler.CSharp @@ -662,15 +662,128 @@ namespace ICSharpCode.Decompiler.CSharp
throw new ArgumentOutOfRangeException();
}
}
/// <summary>
/// Translates pointer arithmetic:
/// ptr + int
/// int + ptr
/// ptr - int
/// Returns null if 'inst' is not performing pointer arithmetic.
/// This function not handle 'ptr - ptr'!
/// </summary>
TranslatedExpression? HandlePointerArithmetic(BinaryNumericInstruction inst, TranslatedExpression left, TranslatedExpression right)
{
if (!(inst.Operator == BinaryNumericOperator.Add || inst.Operator == BinaryNumericOperator.Sub))
return null;
if (inst.CheckForOverflow || inst.IsLifted)
return null;
if (!(inst.LeftInputType == StackType.I && inst.RightInputType == StackType.I))
return null;
PointerType pointerType;
ILInstruction byteOffsetInst;
TranslatedExpression byteOffsetExpr;
if (left.Type.Kind == TypeKind.Pointer) {
byteOffsetInst = inst.Right;
byteOffsetExpr = right;
pointerType = (PointerType)left.Type;
} else if (right.Type.Kind == TypeKind.Pointer) {
if (inst.Operator != BinaryNumericOperator.Add)
return null;
byteOffsetInst = inst.Left;
byteOffsetExpr = left;
pointerType = (PointerType)right.Type;
} else {
return null;
}
TranslatedExpression offsetExpr = GetPointerArithmeticOffset() ?? FallBackToBytePointer();
if (!offsetExpr.Type.IsCSharpPrimitiveIntegerType()) {
// pointer arithmetic accepts all primitive integer types, but no enums etc.
StackType targetType = offsetExpr.Type.GetStackType() == StackType.I4 ? StackType.I4 : StackType.I8;
offsetExpr = offsetExpr.ConvertTo(
compilation.FindType(targetType.ToKnownTypeCode(offsetExpr.Type.GetSign())),
this);
}
if (left.Type.Kind == TypeKind.Pointer) {
Debug.Assert(inst.Operator == BinaryNumericOperator.Add || inst.Operator == BinaryNumericOperator.Sub);
left = left.ConvertTo(pointerType, this);
right = offsetExpr;
} else {
Debug.Assert(inst.Operator == BinaryNumericOperator.Add);
Debug.Assert(right.Type.Kind == TypeKind.Pointer);
left = offsetExpr;
right = right.ConvertTo(pointerType, this);
}
var operatorType = inst.Operator == BinaryNumericOperator.Add ? BinaryOperatorType.Add : BinaryOperatorType.Subtract;
return new BinaryOperatorExpression(left, operatorType, right)
.WithILInstruction(inst)
.WithRR(new OperatorResolveResult(
pointerType, BinaryOperatorExpression.GetLinqNodeType(operatorType, inst.CheckForOverflow),
left.ResolveResult, right.ResolveResult));
TranslatedExpression? GetPointerArithmeticOffset()
{
int? elementSize = ComputeSizeOf(pointerType.ElementType);
int val;
if (elementSize == 1) {
return byteOffsetExpr;
} else if (byteOffsetInst is BinaryNumericInstruction mul && mul.Operator == BinaryNumericOperator.Mul) {
if (mul.CheckForOverflow != inst.CheckForOverflow)
return null;
if (mul.IsLifted)
return null;
if (elementSize > 0 && mul.Right.UnwrapConv(ConversionKind.SignExtend).MatchLdcI4(elementSize.Value)) {
return Translate(mul.Left);
} else {
return null;
}
} else if (byteOffsetInst.UnwrapConv(ConversionKind.SignExtend).MatchLdcI4(out val)) {
// If the offset is a constant, it's possible that the compiler
// constant-folded the multiplication.
if (elementSize > 0 && (val % elementSize == 0) && val > 0) {
val /= elementSize.Value;
return new PrimitiveExpression(val)
.WithILInstruction(byteOffsetInst)
.WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), val));
} else {
return null;
}
} else {
return null;
}
}
TranslatedExpression FallBackToBytePointer()
{
pointerType = new PointerType(compilation.FindType(KnownTypeCode.Byte));
return byteOffsetExpr;
}
}
int? ComputeSizeOf(IType type)
{
var rr = resolver.ResolveSizeOf(type);
if (rr.IsCompileTimeConstant && rr.ConstantValue is int size)
return size;
else
return null;
}
TranslatedExpression HandleBinaryNumeric(BinaryNumericInstruction inst, BinaryOperatorType op)
{
var resolverWithOverflowCheck = resolver.WithCheckForOverflow(inst.CheckForOverflow);
var left = Translate(inst.Left);
var right = Translate(inst.Right);
if (left.Type.Kind == TypeKind.Pointer || right.Type.Kind == TypeKind.Pointer) {
var ptrResult = HandlePointerArithmetic(inst, left, right);
if (ptrResult != null)
return ptrResult.Value;
}
left = PrepareArithmeticArgument(left, inst.LeftInputType, inst.Sign, inst.IsLifted);
right = PrepareArithmeticArgument(right, inst.RightInputType, inst.Sign, inst.IsLifted);
if (op == BinaryOperatorType.Subtract && inst.Left.MatchLdcI(0)) {
IType rightUType = NullableType.GetUnderlyingType(right.Type);
if (rightUType.IsKnownType(KnownTypeCode.Int32) || rightUType.IsKnownType(KnownTypeCode.Int64) || rightUType.IsCSharpSmallIntegerType()) {
@ -719,6 +832,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -719,6 +832,7 @@ namespace ICSharpCode.Decompiler.CSharp
// If the argument is oversized (needs truncation to match stack size of its ILInstruction),
// perform the truncation now.
IType targetType = compilation.FindType(argStackType.ToKnownTypeCode(sign));
argUType = targetType;
if (isLifted)
targetType = NullableType.Create(compilation, targetType);
arg = arg.ConvertTo(targetType, this);

22
ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs

@ -127,6 +127,28 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -127,6 +127,28 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
}
/// <summary>
/// Gets whether the type is a C# primitive integer type: byte, sbyte, short, ushort, int, uint, long and ulong.
///
/// Unlike the ILAst, C# does not consider bool, enums, pointers or IntPtr to be integers.
/// </summary>
public static bool IsCSharpPrimitiveIntegerType(this IType type)
{
switch (type.GetDefinition()?.KnownTypeCode) {
case KnownTypeCode.Byte:
case KnownTypeCode.SByte:
case KnownTypeCode.Int16:
case KnownTypeCode.UInt16:
case KnownTypeCode.Int32:
case KnownTypeCode.UInt32:
case KnownTypeCode.Int64:
case KnownTypeCode.UInt64:
return true;
default:
return false;
}
}
/// <summary>
/// Gets whether the type is an IL integer type.
/// Returns true for I4, I, or I8.

Loading…
Cancel
Save