Browse Source

Add support for pre- and post-increment of dereferenced pointers "(*ptr)++".

pull/100/head
Daniel Grunwald 14 years ago
parent
commit
d91b56b033
  1. 16
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 8
      ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs
  3. 20
      ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs
  4. 12
      ICSharpCode.Decompiler/DecompilerSettings.cs
  5. 62
      ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  6. 8
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  7. 35
      ICSharpCode.Decompiler/Tests/IncrementDecrement.cs
  8. 2
      ICSharpCode.Decompiler/Tests/MultidimensionalArray.cs

16
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -16,8 +16,6 @@ namespace ICSharpCode.Decompiler.Ast @@ -16,8 +16,6 @@ namespace ICSharpCode.Decompiler.Ast
using Ast = ICSharpCode.NRefactory.CSharp;
using Cecil = Mono.Cecil;
public class ArrayAccessAnnotation {}
public class AstMethodBodyBuilder
{
MethodDefinition methodDef;
@ -25,8 +23,6 @@ namespace ICSharpCode.Decompiler.Ast @@ -25,8 +23,6 @@ namespace ICSharpCode.Decompiler.Ast
DecompilerContext context;
HashSet<ILVariable> localVariablesToDefine = new HashSet<ILVariable>(); // local variables that are missing a definition
static readonly ArrayAccessAnnotation arrayAccessAnnotation = new ArrayAccessAnnotation();
/// <summary>
/// Creates the body for the method definition.
/// </summary>
@ -327,9 +323,9 @@ namespace ICSharpCode.Decompiler.Ast @@ -327,9 +323,9 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Ldelem_R8:
case ILCode.Ldelem_Ref:
case ILCode.Ldelem_Any:
return arg1.Indexer(arg2).WithAnnotation(arrayAccessAnnotation);
return arg1.Indexer(arg2);
case ILCode.Ldelema:
return MakeRef(arg1.Indexer(arg2).WithAnnotation(arrayAccessAnnotation));
return MakeRef(arg1.Indexer(arg2));
case ILCode.Stelem_I:
case ILCode.Stelem_I1:
case ILCode.Stelem_I2:
@ -339,7 +335,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -339,7 +335,7 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Stelem_R8:
case ILCode.Stelem_Ref:
case ILCode.Stelem_Any:
return new Ast.AssignmentExpression(arg1.Indexer(arg2).WithAnnotation(arrayAccessAnnotation), arg3);
return new Ast.AssignmentExpression(arg1.Indexer(arg2), arg3);
case ILCode.CompoundAssignment:
{
BinaryOperatorExpression boe = (BinaryOperatorExpression)arg1;
@ -938,8 +934,10 @@ namespace ICSharpCode.Decompiler.Ast @@ -938,8 +934,10 @@ namespace ICSharpCode.Decompiler.Ast
return expr.CastTo(AstBuilder.ConvertType(actualType));
}
bool actualIsPrimitiveType = actualIsIntegerOrEnum || actualType.MetadataType == MetadataType.Single || actualType.MetadataType == MetadataType.Double;
bool requiredIsPrimitiveType = requiredIsIntegerOrEnum || reqType.MetadataType == MetadataType.Single || reqType.MetadataType == MetadataType.Double;
bool actualIsPrimitiveType = actualIsIntegerOrEnum
|| (actualType != null && (actualType.MetadataType == MetadataType.Single || actualType.MetadataType == MetadataType.Double));
bool requiredIsPrimitiveType = requiredIsIntegerOrEnum
|| (reqType != null && (reqType.MetadataType == MetadataType.Single || reqType.MetadataType == MetadataType.Double));
if (actualIsPrimitiveType && requiredIsPrimitiveType) {
if (actualType.FullName == reqType.FullName)
return expr;

8
ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs

@ -24,14 +24,12 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -24,14 +24,12 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
public void Run(AstNode compilationUnit)
{
if (!context.Settings.UsingDeclarations)
return;
// First determine all the namespaces that need to be imported:
compilationUnit.AcceptVisitor(this, null);
if (importedNamespaces.Count == 0) {
// abort when no namespaces have to be imported
return;
}
importedNamespaces.Add("System"); // always import System, even when not necessary
// Now add using declarations for those namespaces:

20
ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs

@ -176,7 +176,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -176,7 +176,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
// Combine "x = x op y" into "x op= y"
BinaryOperatorExpression binary = assignment.Right as BinaryOperatorExpression;
if (binary != null && assignment.Operator == AssignmentOperatorType.Assign) {
if (IsWithoutSideEffects(assignment.Left) && assignment.Left.Match(binary.Left) != null) {
if (CanConvertToCompoundAssignment(assignment.Left) && assignment.Left.Match(binary.Left) != null) {
assignment.Operator = GetAssignmentOperatorForBinaryOperator(binary.Operator);
if (assignment.Operator != AssignmentOperatorType.Assign) {
// If we found a shorter operator, get rid of the BinaryOperatorExpression:
@ -233,17 +233,23 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -233,17 +233,23 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
}
static bool IsWithoutSideEffects(Expression left)
static bool CanConvertToCompoundAssignment(Expression left)
{
if (left is ThisReferenceExpression || left is IdentifierExpression || left is TypeReferenceExpression)
return true;
MemberReferenceExpression mre = left as MemberReferenceExpression;
if (mre != null)
return mre.Annotation<FieldReference>() != null && IsWithoutSideEffects(mre.Target);
return IsWithoutSideEffects(mre.Target);
IndexerExpression ie = left as IndexerExpression;
if (ie != null && ie.Annotation<ArrayAccessAnnotation>() != null)
if (ie != null)
return IsWithoutSideEffects(ie.Target) && ie.Arguments.All(IsWithoutSideEffects);
return false;
UnaryOperatorExpression uoe = left as UnaryOperatorExpression;
if (uoe != null && uoe.Operator == UnaryOperatorType.Dereference)
return IsWithoutSideEffects(uoe.Expression);
return IsWithoutSideEffects(left);
}
static bool IsWithoutSideEffects(Expression left)
{
return left is ThisReferenceExpression || left is IdentifierExpression || left is TypeReferenceExpression;
}
void IAstTransform.Run(AstNode node)

12
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -128,6 +128,18 @@ namespace ICSharpCode.Decompiler @@ -128,6 +128,18 @@ namespace ICSharpCode.Decompiler
}
}
bool usingDeclarations = true;
public bool UsingDeclarations {
get { return usingDeclarations; }
set {
if (usingDeclarations != value) {
usingDeclarations = value;
OnPropertyChanged("UsingDeclarations");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)

62
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -240,7 +240,9 @@ namespace ICSharpCode.Decompiler.ILAst @@ -240,7 +240,9 @@ namespace ICSharpCode.Decompiler.ILAst
bool StoreCanBeConvertedToAssignment(ILExpression store, ILVariable exprVar)
{
if (store != null && (store.Code == ILCode.Stloc || store.Code == ILCode.Stfld || store.Code == ILCode.Stsfld || store.Code.IsStoreToArray())) {
if (store != null && (store.Code == ILCode.Stloc || store.Code == ILCode.Stfld || store.Code == ILCode.Stsfld
|| store.Code.IsStoreToArray() || store.Code == ILCode.Stobj))
{
return store.Arguments.Last().Code == ILCode.Ldloc && store.Arguments.Last().Operand == exprVar;
}
return false;
@ -251,7 +253,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -251,7 +253,7 @@ namespace ICSharpCode.Decompiler.ILAst
bool MakeCompoundAssignments(List<ILNode> body, ILExpression expr, int pos)
{
bool modified = false;
modified |= MakeCompoundAssignmentForArray(expr);
modified |= MakeCompoundAssignmentForArrayOrPointerAccess(expr);
modified |= MakeCompoundAssignmentForInstanceField(expr);
// Static fields and local variables are not handled here - those are expressions without side effects
// and get handled by ReplaceMethodCallsWithOperators
@ -264,26 +266,43 @@ namespace ICSharpCode.Decompiler.ILAst @@ -264,26 +266,43 @@ namespace ICSharpCode.Decompiler.ILAst
return modified;
}
bool MakeCompoundAssignmentForArray(ILExpression expr)
bool MakeCompoundAssignmentForArrayOrPointerAccess(ILExpression expr)
{
// stelem.any(..., ldloc(array), ldloc(pos), <OP>(ldelem.any(int32, ldloc(array), ldloc(pos)), <RIGHT>))
if (!expr.Code.IsStoreToArray())
return false;
ILVariable arrayVar, posVar;
if (!(expr.Arguments[0].Match(ILCode.Ldloc, out arrayVar) && expr.Arguments[1].Match(ILCode.Ldloc, out posVar)))
// stelem.any(T, ldloc(array), ldloc(pos), <OP>(ldelem.any(T, ldloc(array), ldloc(pos)), <RIGHT>))
// or
// stobj(T, ldloc(ptr), <OP>(ldobj(T, ldloc(ptr)), <RIGHT>))
if (!(expr.Code.IsStoreToArray() || expr.Code == ILCode.Stobj))
return false;
// all arguments except the last (so either array+pos, or ptr):
bool hasGeneratedVar = false;
for (int i = 0; i < expr.Arguments.Count - 1; i++) {
ILVariable inputVar;
if (!expr.Arguments[i].Match(ILCode.Ldloc, out inputVar))
return false;
hasGeneratedVar |= inputVar.IsGenerated;
}
// At least one of the variables must be generated; otherwise we just keep the expanded form.
if (!(arrayVar.IsGenerated || posVar.IsGenerated))
// We do this because we want compound assignments to be represented in ILAst only when strictly necessary;
// other compound assignments will be introduced by ReplaceMethodCallsWithOperator
// (which uses a reversible transformation, see ReplaceMethodCallsWithOperator.RestoreOriginalAssignOperatorAnnotation)
if (!hasGeneratedVar)
return false;
ILExpression op = expr.Arguments[2];
ILExpression op = expr.Arguments.Last();
if (!CanBeRepresentedAsCompoundAssignment(op.Code))
return false;
ILExpression ldelem = op.Arguments[0];
if (!(ldelem.Code == ILCode.Ldelem_Any && ldelem.Arguments[0].MatchLdloc(arrayVar) && ldelem.Arguments[1].MatchLdloc(posVar)))
if (ldelem.Code != (expr.Code == ILCode.Stobj ? ILCode.Ldobj : ILCode.Ldelem_Any))
return false;
Debug.Assert(ldelem.Arguments.Count == expr.Arguments.Count - 1);
for (int i = 0; i < ldelem.Arguments.Count; i++) {
if (!ldelem.Arguments[i].MatchLdloc((ILVariable)expr.Arguments[i].Operand))
return false;
}
expr.Code = ILCode.CompoundAssignment;
expr.Operand = null;
expr.Arguments.RemoveRange(0, 2);
expr.Arguments.RemoveRange(0, ldelem.Arguments.Count);
// result is "CompoundAssignment(<OP>(ldelem.any(...), <RIGHT>))"
return true;
}
@ -391,16 +410,18 @@ namespace ICSharpCode.Decompiler.ILAst @@ -391,16 +410,18 @@ namespace ICSharpCode.Decompiler.ILAst
// ->
// stloc(helperVar, postincrement(1, ldflda(field, ldloc(instance))))
// Also works for array elements:
// Also works for array elements and pointers:
// stelem.any(T, ldloc(instance), ldloc(pos), add(stloc(helperVar, ldelem.any(T, ldloc(instance), ldloc(pos))), ldc.i4:int32(1)))
// ->
// stloc(helperVar, postincrement(1, ldelema(ldloc(instance), ldloc(pos))))
if (!(expr.Code == ILCode.Stfld || expr.Code.IsStoreToArray()))
// stobj(T, ldloc(ptr), add(stloc(helperVar, ldobj(T, ldloc(ptr)), ldc.i4:int32(1))))
if (!(expr.Code == ILCode.Stfld || expr.Code.IsStoreToArray() || expr.Code == ILCode.Stobj))
return null;
// Test that all arguments except the last are ldloc (1 arg for fields, 2 args for arrays)
// Test that all arguments except the last are ldloc (1 arg for fields and pointers, 2 args for arrays)
for (int i = 0; i < expr.Arguments.Count - 1; i++) {
if (expr.Arguments[i].Code != ILCode.Ldloc)
return null;
@ -417,6 +438,9 @@ namespace ICSharpCode.Decompiler.ILAst @@ -417,6 +438,9 @@ namespace ICSharpCode.Decompiler.ILAst
if (expr.Code == ILCode.Stfld) {
if (!(initialValue.Code == ILCode.Ldfld && initialValue.Operand == expr.Operand))
return null;
} else if (expr.Code == ILCode.Stobj) {
if (!(initialValue.Code == ILCode.Ldobj && initialValue.Operand == expr.Operand))
return null;
} else {
if (!initialValue.Code.IsLoadFromArray())
return null;
@ -428,8 +452,12 @@ namespace ICSharpCode.Decompiler.ILAst @@ -428,8 +452,12 @@ namespace ICSharpCode.Decompiler.ILAst
}
ILExpression stloc = addExpr.Arguments[0];
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue);
initialValue.Code = (expr.Code == ILCode.Stfld ? ILCode.Ldflda : ILCode.Ldelema);
if (expr.Code == ILCode.Stobj) {
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue.Arguments[0]);
} else {
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue);
initialValue.Code = (expr.Code == ILCode.Stfld ? ILCode.Ldflda : ILCode.Ldelema);
}
// TODO: ILRanges?
return stloc;

8
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -435,8 +435,12 @@ namespace ICSharpCode.Decompiler.ILAst @@ -435,8 +435,12 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.PostIncrement_Ovf:
case ILCode.PostIncrement_Ovf_Un:
{
ByReferenceType byReferenceType = InferTypeForExpression(expr.Arguments[0], null) as ByReferenceType;
return byReferenceType != null ? byReferenceType.ElementType : null;
TypeReference type = UnpackPointer(InferTypeForExpression(expr.Arguments[0], null));
if (forceInferChildren) {
// Assign expected type to the child expression
InferTypeForExpression(expr.Arguments[0], new ByReferenceType(type));
}
return type;
}
#endregion
#region Arithmetic instructions

35
ICSharpCode.Decompiler/Tests/IncrementDecrement.cs

@ -17,6 +17,11 @@ public class IncrementDecrement @@ -17,6 +17,11 @@ public class IncrementDecrement
return new IncrementDecrement.MutableClass();
}
private unsafe int* GetPointer()
{
return null;
}
public int PreIncrementInAddition(int i, int j)
{
return i + ++j;
@ -42,6 +47,16 @@ public class IncrementDecrement @@ -42,6 +47,16 @@ public class IncrementDecrement
return ++IncrementDecrement.StaticField;
}
public int PreIncrementByRef(ref int i)
{
return ++i;
}
public unsafe int PreIncrementByPointer()
{
return ++(*this.GetPointer());
}
public int CompoundMultiplyInstanceField()
{
return this.M().Field *= 10;
@ -62,6 +77,16 @@ public class IncrementDecrement @@ -62,6 +77,16 @@ public class IncrementDecrement
return array[Environment.TickCount] *= 10;
}
public int CompoundShiftByRef(ref int i)
{
return i <<= 2;
}
public unsafe double CompoundDivideByPointer(double* ptr)
{
return *ptr /= 1.5;
}
public int PostIncrementInAddition(int i, int j)
{
return i++ + j;
@ -86,4 +111,14 @@ public class IncrementDecrement @@ -86,4 +111,14 @@ public class IncrementDecrement
{
return this.M().Field--;
}
public int PostIncrementByRef(ref int i)
{
return i++;
}
public unsafe int PostIncrementByPointer()
{
return (*this.GetPointer())++;
}
}

2
ICSharpCode.Decompiler/Tests/MultidimensionalArray.cs

@ -1,6 +1,8 @@ @@ -1,6 +1,8 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
public static class MultidimensionalArray
{
internal class Generic<T, S> where T : new()

Loading…
Cancel
Save