Browse Source

Add pre- and post-increment support for properties (both instance and static) and for multi-dimensional arrays.

pull/100/head
Daniel Grunwald 14 years ago
parent
commit
cc0ab56869
  1. 16
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 10
      ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
  3. 8
      ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs
  4. 40
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  5. 39
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  6. 38
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  7. 137
      ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  8. 23
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  9. 111
      ICSharpCode.Decompiler/Tests/IncrementDecrement.cs
  10. 9
      ICSharpCode.Decompiler/Tests/MultidimensionalArray.cs
  11. 7
      ICSharpCode.Decompiler/Tests/UnsafeCode.cs

16
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -455,8 +455,14 @@ namespace ICSharpCode.Decompiler.Ast @@ -455,8 +455,14 @@ namespace ICSharpCode.Decompiler.Ast
#endregion
case ILCode.Arglist: return InlineAssembly(byteCode, args);
case ILCode.Break: return InlineAssembly(byteCode, args);
case ILCode.Call: return TransformCall(false, operand, methodDef, args);
case ILCode.Callvirt: return TransformCall(true, operand, methodDef, args);
case ILCode.Call:
case ILCode.CallGetter:
case ILCode.CallSetter:
return TransformCall(false, operand, args);
case ILCode.Callvirt:
case ILCode.CallvirtGetter:
case ILCode.CallvirtSetter:
return TransformCall(true, operand, args);
case ILCode.Ldftn: {
Cecil.MethodReference cecilMethod = ((MethodReference)operand);
var expr = new Ast.IdentifierExpression(cecilMethod.Name);
@ -689,7 +695,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -689,7 +695,7 @@ namespace ICSharpCode.Decompiler.Ast
return new DefaultValueExpression { Type = AstBuilder.ConvertType(type) };
}
static AstNode TransformCall(bool isVirtual, object operand, MethodDefinition methodDef, List<Ast.Expression> args)
AstNode TransformCall(bool isVirtual, object operand, List<Ast.Expression> args)
{
Cecil.MethodReference cecilMethod = ((MethodReference)operand);
Ast.Expression target;
@ -727,6 +733,8 @@ namespace ICSharpCode.Decompiler.Ast @@ -727,6 +733,8 @@ namespace ICSharpCode.Decompiler.Ast
if (cecilMethod.Name == "Get" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) {
return target.Indexer(methodArgs);
} else if (cecilMethod.Name == "Address" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) {
return MakeRef(target.Indexer(methodArgs));
} else if (cecilMethod.Name == "Set" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 2) {
return new AssignmentExpression(target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)), methodArgs.Last());
}
@ -936,7 +944,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -936,7 +944,7 @@ namespace ICSharpCode.Decompiler.Ast
bool actualIsPrimitiveType = actualIsIntegerOrEnum
|| (actualType != null && (actualType.MetadataType == MetadataType.Single || actualType.MetadataType == MetadataType.Double));
bool requiredIsPrimitiveType = requiredIsIntegerOrEnum
bool requiredIsPrimitiveType = requiredIsIntegerOrEnum
|| (reqType != null && (reqType.MetadataType == MetadataType.Single || reqType.MetadataType == MetadataType.Double));
if (actualIsPrimitiveType && requiredIsPrimitiveType) {
if (actualType.FullName == reqType.FullName)

10
ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs

@ -584,7 +584,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -584,7 +584,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
Modifiers = Modifiers.Any,
Body = new BlockStatement {
new ReturnStatement {
Expression = new NamedNode("fieldReference", new MemberReferenceExpression { Target = new ThisReferenceExpression() })
Expression = new AnyNode("fieldReference")
}
}
},
@ -608,7 +608,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -608,7 +608,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
Match m = automaticPropertyPattern.Match(property);
if (m != null) {
FieldDefinition field = m.Get("fieldReference").Single().Annotation<FieldReference>().ResolveWithinSameModule();
if (field.IsCompilerGenerated()) {
if (field.IsCompilerGenerated() && field.DeclaringType == cecilProperty.DeclaringType) {
RemoveCompilerGeneratedAttribute(property.Getter.Attributes);
RemoveCompilerGeneratedAttribute(property.Setter.Attributes);
property.Getter.Body = null;
@ -643,7 +643,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -643,7 +643,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
new AssignmentExpression {
Left = new NamedNode("var1", new IdentifierExpression()),
Operator = AssignmentOperatorType.Assign,
Right = new NamedNode("field", new MemberReferenceExpression { Target = new ThisReferenceExpression() })
Right = new NamedNode(
"field",
new MemberReferenceExpression {
Target = new Choice { new ThisReferenceExpression(), new TypeReferenceExpression { Type = new AnyNode() } }
})
},
new DoWhileStatement {
EmbeddedStatement = new BlockStatement {

8
ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs

@ -694,7 +694,13 @@ namespace ICSharpCode.Decompiler.ILAst @@ -694,7 +694,13 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression expr = new ILExpression(byteCode.Code, byteCode.Operand);
expr.ILRanges.Add(ilRange);
expr.Prefixes = byteCode.Prefixes;
if (byteCode.Prefixes != null && byteCode.Prefixes.Length > 0) {
ILExpressionPrefix[] prefixes = new ILExpressionPrefix[byteCode.Prefixes.Length];
for (int i = 0; i < prefixes.Length; i++) {
prefixes[i] = new ILExpressionPrefix((ILCode)byteCode.Prefixes[i].OpCode.Code, byteCode.Prefixes[i].Operand);
}
expr.Prefixes = prefixes;
}
// Label for this instruction
if (byteCode.Label != null) {

40
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -16,6 +16,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -16,6 +16,7 @@ namespace ICSharpCode.Decompiler.ILAst
InlineVariables,
CopyPropagation,
YieldReturn,
PropertyAccessInstructions,
SplitToMovableBlocks,
TypeInference,
SimplifyShortCircuit,
@ -80,6 +81,9 @@ namespace ICSharpCode.Decompiler.ILAst @@ -80,6 +81,9 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.YieldReturn) return;
YieldReturnDecompiler.Run(context, method);
if (abortBeforeStep == ILAstOptimizationStep.PropertyAccessInstructions) return;
IntroducePropertyAccessInstructions(method);
if (abortBeforeStep == ILAstOptimizationStep.SplitToMovableBlocks) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
SplitToBasicBlocks(block);
@ -286,6 +290,42 @@ namespace ICSharpCode.Decompiler.ILAst @@ -286,6 +290,42 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
/// <summary>
/// Converts call and callvirt instructions that read/write properties into CallGetter/CallSetter instructions.
///
/// CallGetter/CallSetter is used to allow the ILAst to represent "while ((SomeProperty = value) != null)".
/// </summary>
void IntroducePropertyAccessInstructions(ILBlock method)
{
foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
if (expr.Code == ILCode.Call || expr.Code == ILCode.Callvirt) {
MethodReference cecilMethod = (MethodReference)expr.Operand;
if (cecilMethod.DeclaringType is ArrayType) {
switch (cecilMethod.Name) {
case "Get":
expr.Code = ILCode.CallGetter;
break;
case "Set":
expr.Code = ILCode.CallSetter;
break;
case "Address":
expr.Code = ILCode.CallGetter;
expr.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress));
break;
}
} else {
MethodDefinition cecilMethodDef = cecilMethod.Resolve();
if (cecilMethodDef != null) {
if (cecilMethodDef.IsGetter)
expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallGetter : ILCode.CallvirtGetter;
else if (cecilMethodDef.IsSetter)
expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallSetter : ILCode.CallvirtSetter;
}
}
}
}
}
/// <summary>
/// Group input into a set of blocks that can be later arbitraliby schufled.
/// The method adds necessary branches to make control flow between blocks

39
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -247,12 +247,24 @@ namespace ICSharpCode.Decompiler.ILAst @@ -247,12 +247,24 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
public class ILExpressionPrefix
{
public readonly ILCode Code;
public readonly object Operand;
public ILExpressionPrefix(ILCode code, object operand = null)
{
this.Code = code;
this.Operand = operand;
}
}
public class ILExpression : ILNode
{
public ILCode Code { get; set; }
public object Operand { get; set; }
public List<ILExpression> Arguments { get; set; }
public Instruction[] Prefixes { get; set; }
public ILExpressionPrefix[] Prefixes { get; set; }
// Mapping to the original instructions (useful for debugging)
public List<ILRange> ILRanges { get; set; }
@ -283,13 +295,24 @@ namespace ICSharpCode.Decompiler.ILAst @@ -283,13 +295,24 @@ namespace ICSharpCode.Decompiler.ILAst
this.ILRanges = new List<ILRange>(1);
}
public Instruction GetPrefix(Code code)
public void AddPrefix(ILExpressionPrefix prefix)
{
ILExpressionPrefix[] arr = this.Prefixes;
if (arr == null)
arr = new ILExpressionPrefix[1];
else
Array.Resize(ref arr, arr.Length + 1);
arr[arr.Length - 1] = prefix;
this.Prefixes = arr;
}
public ILExpressionPrefix GetPrefix(ILCode code)
{
var prefixes = this.Prefixes;
if (prefixes != null) {
foreach (Instruction i in prefixes) {
if (i.OpCode.Code == code)
return i;
foreach (ILExpressionPrefix p in prefixes) {
if (p.Code == code)
return p;
}
}
return null;
@ -340,9 +363,9 @@ namespace ICSharpCode.Decompiler.ILAst @@ -340,9 +363,9 @@ namespace ICSharpCode.Decompiler.ILAst
}
if (this.Prefixes != null) {
foreach (Instruction prefix in this.Prefixes) {
output.Write(prefix.OpCode.Name);
output.Write(' ');
foreach (var prefix in this.Prefixes) {
output.Write(prefix.Code.GetName());
output.Write(". ");
}
}

38
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -1,27 +1,5 @@ @@ -1,27 +1,5 @@
// Author:
// Jb Evain (jbevain@gmail.com)
//
// Copyright (c) 2008 - 2010 Jb Evain
//
// 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.
//
// 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;
using Mono.Cecil;
@ -289,6 +267,18 @@ namespace ICSharpCode.Decompiler.ILAst @@ -289,6 +267,18 @@ namespace ICSharpCode.Decompiler.ILAst
PostIncrement,
PostIncrement_Ovf, // checked variant of PostIncrement
PostIncrement_Ovf_Un, // checked variant of PostIncrement, for unsigned integers
/// <summary>Calls the getter of a static property (or indexer), or of an instance property on 'base'</summary>
CallGetter,
/// <summary>Calls the getter of an instance property (or indexer)</summary>
CallvirtGetter,
/// <summary>Calls the setter of a static property (or indexer), or of an instance property on 'base'</summary>
/// <remarks>This allows us to represent "while ((SomeProperty = val) != null) {}"</remarks>
CallSetter,
/// <summary>Calls the setter of a instance property (or indexer)</summary>
CallvirtSetter,
/// <summary>Simulates getting the address of a property. Used as prefix on CallGetter or CallvirtGetter.</summary>
/// <remarks>Used for postincrement for properties, and to represent the Address() method on multi-dimensional arrays</remarks>
PropertyAddress
}
public static class ILCodeUtil

137
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -222,14 +222,13 @@ namespace ICSharpCode.Decompiler.ILAst @@ -222,14 +222,13 @@ namespace ICSharpCode.Decompiler.ILAst
nextExpr.Arguments[0] = initializer;
((ILExpression)body[pos]).Arguments[0] = nextExpr;
return true;
} else {
} else if ((nextExpr.Code == ILCode.Stsfld || nextExpr.Code == ILCode.CallSetter || nextExpr.Code == ILCode.CallvirtSetter) && nextExpr.Arguments.Count == 1) {
// exprVar = ...
// stsfld(fld, exprVar)
// ->
// exprVar = stsfld(fld, ...))
FieldReference field;
if (nextExpr.Match(ILCode.Stsfld, out field, out stLocArg) && stLocArg.MatchLdloc(exprVar)) {
body.RemoveAt(pos + 1); // remove stfld
if (nextExpr.Arguments[0].MatchLdloc(exprVar)) {
body.RemoveAt(pos + 1); // remove stsfld
nextExpr.Arguments[0] = initializer;
((ILExpression)body[pos]).Arguments[0] = nextExpr;
return true;
@ -240,12 +239,22 @@ namespace ICSharpCode.Decompiler.ILAst @@ -240,12 +239,22 @@ 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() || store.Code == ILCode.Stobj))
{
return store.Arguments.Last().Code == ILCode.Ldloc && store.Arguments.Last().Operand == exprVar;
if (store == null)
return false;
switch (store.Code) {
case ILCode.Stloc:
case ILCode.Stfld:
case ILCode.Stsfld:
case ILCode.Stobj:
case ILCode.CallSetter:
case ILCode.CallvirtSetter:
break;
default:
if (!store.Code.IsStoreToArray())
return false;
break;
}
return false;
return store.Arguments.Last().Code == ILCode.Ldloc && store.Arguments.Last().Operand == exprVar;
}
#endregion
@ -253,8 +262,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -253,8 +262,7 @@ namespace ICSharpCode.Decompiler.ILAst
bool MakeCompoundAssignments(List<ILNode> body, ILExpression expr, int pos)
{
bool modified = false;
modified |= MakeCompoundAssignmentForArrayOrPointerAccess(expr);
modified |= MakeCompoundAssignmentForInstanceField(expr);
modified |= MakeCompoundAssignment(expr);
// Static fields and local variables are not handled here - those are expressions without side effects
// and get handled by ReplaceMethodCallsWithOperators
// (which does a reversible transform to the short operator form, as the introduction of checked/unchecked might have to revert to the long form).
@ -266,13 +274,31 @@ namespace ICSharpCode.Decompiler.ILAst @@ -266,13 +274,31 @@ namespace ICSharpCode.Decompiler.ILAst
return modified;
}
bool MakeCompoundAssignmentForArrayOrPointerAccess(ILExpression expr)
bool MakeCompoundAssignment(ILExpression expr)
{
// 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;
ILCode expectedLdelemCode;
switch (expr.Code) {
case ILCode.Stelem_Any:
expectedLdelemCode = ILCode.Ldelem_Any;
break;
case ILCode.Stfld:
expectedLdelemCode = ILCode.Ldfld;
break;
case ILCode.Stobj:
expectedLdelemCode = ILCode.Ldobj;
break;
case ILCode.CallSetter:
expectedLdelemCode = ILCode.CallGetter;
break;
case ILCode.CallvirtSetter:
expectedLdelemCode = ILCode.CallvirtGetter;
break;
default:
return false;
}
// all arguments except the last (so either array+pos, or ptr):
bool hasGeneratedVar = false;
@ -293,7 +319,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -293,7 +319,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (!CanBeRepresentedAsCompoundAssignment(op.Code))
return false;
ILExpression ldelem = op.Arguments[0];
if (ldelem.Code != (expr.Code == ILCode.Stobj ? ILCode.Ldobj : ILCode.Ldelem_Any))
if (ldelem.Code != expectedLdelemCode)
return false;
Debug.Assert(ldelem.Arguments.Count == expr.Arguments.Count - 1);
for (int i = 0; i < ldelem.Arguments.Count; i++) {
@ -307,26 +333,6 @@ namespace ICSharpCode.Decompiler.ILAst @@ -307,26 +333,6 @@ namespace ICSharpCode.Decompiler.ILAst
return true;
}
bool MakeCompoundAssignmentForInstanceField(ILExpression expr)
{
// stfld(field, expr, <OP>(ldfld(field, expr), <RIGHT>))
FieldReference field;
ILExpression firstLoad, op;
ILVariable exprVar;
if (!(expr.Match(ILCode.Stfld, out field, out firstLoad, out op) && firstLoad.Match(ILCode.Ldloc, out exprVar) && exprVar.IsGenerated))
return false;
if (!CanBeRepresentedAsCompoundAssignment(op.Code))
return false;
ILExpression ldfld = op.Arguments[0];
if (!(ldfld.Code == ILCode.Ldfld && ldfld.Operand == field && ldfld.Arguments[0].MatchLdloc(exprVar)))
return false;
expr.Code = ILCode.CompoundAssignment;
expr.Operand = null;
expr.Arguments.RemoveAt(0);
// result is "CompoundAssignment(<OP>(ldfld(...), <RIGHT>))"
return true;
}
static bool CanBeRepresentedAsCompoundAssignment(ILCode code)
{
switch (code) {
@ -372,7 +378,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -372,7 +378,7 @@ namespace ICSharpCode.Decompiler.ILAst
bool IntroducePostIncrementForVariables(List<ILNode> body, ILExpression expr, int pos)
{
// Works for variables and static fields
// Works for variables and static fields/properties
// expr = ldloc(i)
// stloc(i, add(expr, ldc.i4(1)))
@ -382,12 +388,19 @@ namespace ICSharpCode.Decompiler.ILAst @@ -382,12 +388,19 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression exprInit;
if (!(expr.Match(ILCode.Stloc, out exprVar, out exprInit) && exprVar.IsGenerated))
return false;
if (!(exprInit.Code == ILCode.Ldloc || exprInit.Code == ILCode.Ldsfld))
if (!(exprInit.Code == ILCode.Ldloc || exprInit.Code == ILCode.Ldsfld || (exprInit.Code == ILCode.CallGetter && exprInit.Arguments.Count == 0)))
return false;
ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression;
if (nextExpr == null || !(nextExpr.Code == (exprInit.Code == ILCode.Ldloc ? ILCode.Stloc : ILCode.Stsfld) && nextExpr.Operand == exprInit.Operand))
if (nextExpr == null)
return false;
if (exprInit.Code == ILCode.CallGetter) {
if (!(nextExpr.Code == ILCode.CallSetter && IsGetterSetterPair(exprInit.Operand, nextExpr.Operand)))
return false;
} else {
if (!(nextExpr.Code == (exprInit.Code == ILCode.Ldloc ? ILCode.Stloc : ILCode.Stsfld) && nextExpr.Operand == exprInit.Operand))
return false;
}
ILExpression addExpr = nextExpr.Arguments[0];
int incrementAmount;
@ -397,6 +410,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -397,6 +410,8 @@ namespace ICSharpCode.Decompiler.ILAst
if (exprInit.Code == ILCode.Ldloc)
exprInit.Code = ILCode.Ldloca;
else if (exprInit.Code == ILCode.CallGetter)
exprInit.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress));
else
exprInit.Code = ILCode.Ldsflda;
expr.Arguments[0] = new ILExpression(incrementCode, incrementAmount, exprInit);
@ -404,21 +419,42 @@ namespace ICSharpCode.Decompiler.ILAst @@ -404,21 +419,42 @@ namespace ICSharpCode.Decompiler.ILAst
return true;
}
static bool IsGetterSetterPair(object getterOperand, object setterOperand)
{
MethodReference getter = getterOperand as MethodReference;
MethodReference setter = setterOperand as MethodReference;
if (getter == null || setter == null)
return false;
if (getter.DeclaringType != setter.DeclaringType)
return false;
MethodDefinition getterDef = getter.Resolve();
MethodDefinition setterDef = setter.Resolve();
if (getterDef == null || setterDef == null)
return false;
foreach (PropertyDefinition prop in getterDef.DeclaringType.Properties) {
if (prop.GetMethod == getterDef)
return prop.SetMethod == setterDef;
}
return false;
}
ILExpression IntroducePostIncrementForInstanceFields(ILExpression expr)
{
// stfld(field, ldloc(instance), add(stloc(helperVar, ldfld(field, ldloc(instance))), ldc.i4:int32(1)))
// ->
// stloc(helperVar, postincrement(1, ldflda(field, ldloc(instance))))
// stfld(field, ldloc(instance), add(stloc(helperVar, ldfld(field, ldloc(instance))), ldc.i4(1)))
// -> stloc(helperVar, postincrement(1, ldflda(field, ldloc(instance))))
// 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))))
// stelem.any(T, ldloc(instance), ldloc(pos), add(stloc(helperVar, ldelem.any(T, ldloc(instance), ldloc(pos))), ldc.i4(1)))
// -> stloc(helperVar, postincrement(1, ldelema(ldloc(instance), ldloc(pos))))
// stobj(T, ldloc(ptr), add(stloc(helperVar, ldobj(T, ldloc(ptr)), ldc.i4(1))))
// -> stloc(helperVar, postIncrement(1, ldloc(ptr)))
// stobj(T, ldloc(ptr), add(stloc(helperVar, ldobj(T, ldloc(ptr)), ldc.i4:int32(1))))
// callsetter(set_P, ldloc(instance), add(stloc(helperVar, callgetter(get_P, ldloc(instance))), ldc.i4(1)))
// -> stloc(helperVar, postIncrement(1, propertyaddress. callgetter(get_P, ldloc(instance))))
if (!(expr.Code == ILCode.Stfld || expr.Code.IsStoreToArray() || expr.Code == ILCode.Stobj))
if (!(expr.Code == ILCode.Stfld || expr.Code.IsStoreToArray() || expr.Code == ILCode.Stobj || expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter))
return null;
// Test that all arguments except the last are ldloc (1 arg for fields and pointers, 2 args for arrays)
@ -441,6 +477,12 @@ namespace ICSharpCode.Decompiler.ILAst @@ -441,6 +477,12 @@ namespace ICSharpCode.Decompiler.ILAst
} else if (expr.Code == ILCode.Stobj) {
if (!(initialValue.Code == ILCode.Ldobj && initialValue.Operand == expr.Operand))
return null;
} else if (expr.Code == ILCode.CallSetter) {
if (!(initialValue.Code == ILCode.CallGetter && IsGetterSetterPair(initialValue.Operand, expr.Operand)))
return null;
} else if (expr.Code == ILCode.CallvirtSetter) {
if (!(initialValue.Code == ILCode.CallvirtGetter && IsGetterSetterPair(initialValue.Operand, expr.Operand)))
return null;
} else {
if (!initialValue.Code.IsLoadFromArray())
return null;
@ -454,6 +496,9 @@ namespace ICSharpCode.Decompiler.ILAst @@ -454,6 +496,9 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression stloc = addExpr.Arguments[0];
if (expr.Code == ILCode.Stobj) {
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue.Arguments[0]);
} else if (expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter) {
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue);
initialValue.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress));
} else {
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue);
initialValue.Code = (expr.Code == ILCode.Stfld ? ILCode.Ldflda : ILCode.Ldelema);

23
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -295,12 +295,16 @@ namespace ICSharpCode.Decompiler.ILAst @@ -295,12 +295,16 @@ namespace ICSharpCode.Decompiler.ILAst
#region Call / NewObj
case ILCode.Call:
case ILCode.Callvirt:
case ILCode.CallGetter:
case ILCode.CallvirtGetter:
case ILCode.CallSetter:
case ILCode.CallvirtSetter:
{
MethodReference method = (MethodReference)expr.Operand;
if (forceInferChildren) {
for (int i = 0; i < expr.Arguments.Count; i++) {
if (i == 0 && method.HasThis) {
Instruction constraint = expr.GetPrefix(Code.Constrained);
ILExpressionPrefix constraint = expr.GetPrefix(ILCode.Constrained);
if (constraint != null)
InferTypeForExpression(expr.Arguments[i], new ByReferenceType((TypeReference)constraint.Operand));
else if (method.DeclaringType.IsValueType)
@ -312,7 +316,14 @@ namespace ICSharpCode.Decompiler.ILAst @@ -312,7 +316,14 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
}
return SubstituteTypeArgs(method.ReturnType, method);
if (expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter) {
return SubstituteTypeArgs(method.Parameters.Last().ParameterType, method);
} else {
TypeReference type = SubstituteTypeArgs(method.ReturnType, method);
if (expr.GetPrefix(ILCode.PropertyAddress) != null && !(type is ByReferenceType))
type = new ByReferenceType(type);
return type;
}
}
case ILCode.Newobj:
{
@ -435,12 +446,12 @@ namespace ICSharpCode.Decompiler.ILAst @@ -435,12 +446,12 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.PostIncrement_Ovf:
case ILCode.PostIncrement_Ovf_Un:
{
TypeReference type = UnpackPointer(InferTypeForExpression(expr.Arguments[0], null));
if (forceInferChildren) {
TypeReference elementType = UnpackPointer(InferTypeForExpression(expr.Arguments[0], null));
if (forceInferChildren && elementType != null) {
// Assign expected type to the child expression
InferTypeForExpression(expr.Arguments[0], new ByReferenceType(type));
InferTypeForExpression(expr.Arguments[0], new ByReferenceType(elementType));
}
return type;
return elementType;
}
#endregion
#region Arithmetic instructions

111
ICSharpCode.Decompiler/Tests/IncrementDecrement.cs

@ -8,15 +8,43 @@ public class IncrementDecrement @@ -8,15 +8,43 @@ public class IncrementDecrement
public class MutableClass
{
public int Field;
public int Property
{
get;
set;
}
public uint this[string name]
{
get
{
return 0u;
}
set
{
}
}
}
public static int StaticField;
public static int StaticProperty
{
get;
set;
}
private IncrementDecrement.MutableClass M()
{
return new IncrementDecrement.MutableClass();
}
private int[,] Array()
{
return null;
}
private unsafe int* GetPointer()
{
return null;
@ -27,7 +55,7 @@ public class IncrementDecrement @@ -27,7 +55,7 @@ public class IncrementDecrement
return i + ++j;
}
public int PreDecrementArrayElement(int[] array, int pos)
public int PreIncrementArrayElement(int[] array, int pos)
{
return --array[pos];
}
@ -42,11 +70,26 @@ public class IncrementDecrement @@ -42,11 +70,26 @@ public class IncrementDecrement
return ++m.Field;
}
public int PreIncrementInstanceProperty()
{
return ++this.M().Property;
}
public int PreIncrementStaticField()
{
return ++IncrementDecrement.StaticField;
}
public int PreIncrementStaticProperty()
{
return ++IncrementDecrement.StaticProperty;
}
// public uint PreIncrementIndexer(string name)
// {
// return ++this.M()[name];
// }
public int PreIncrementByRef(ref int i)
{
return ++i;
@ -57,32 +100,57 @@ public class IncrementDecrement @@ -57,32 +100,57 @@ public class IncrementDecrement
return ++(*this.GetPointer());
}
public int CompoundMultiplyInstanceField()
public int PreIncrement2DArray()
{
return ++this.Array()[1, 2];
}
public int CompoundAssignInstanceField()
{
return this.M().Field *= 10;
}
public int CompoundXorStaticField()
public int CompoundAssignInstanceProperty()
{
return this.M().Property *= 10;
}
public int CompoundAssignStaticField()
{
return IncrementDecrement.StaticField ^= 100;
}
public int CompoundMultiplyArrayElement1(int[] array, int pos)
public int CompoundAssignStaticProperty()
{
return IncrementDecrement.StaticProperty &= 10;
}
public int CompoundAssignArrayElement1(int[] array, int pos)
{
return array[pos] *= 10;
}
public int CompoundMultiplyArrayElement2(int[] array)
public int CompoundAssignArrayElement2(int[] array)
{
return array[Environment.TickCount] *= 10;
}
public int CompoundShiftByRef(ref int i)
// public uint CompoundAssignIndexer(string name)
// {
// return this.M()[name] -= 2;
// }
public int CompoundAssignIncrement2DArray()
{
return this.Array()[1, 2] %= 10;
}
public int CompoundAssignByRef(ref int i)
{
return i <<= 2;
}
public unsafe double CompoundDivideByPointer(double* ptr)
public unsafe double CompoundAssignByPointer(double* ptr)
{
return *ptr /= 1.5;
}
@ -92,7 +160,7 @@ public class IncrementDecrement @@ -92,7 +160,7 @@ public class IncrementDecrement
return i++ + j;
}
public int PostDecrementArrayElement(int[] array, int pos)
public int PostIncrementArrayElement(int[] array, int pos)
{
return array[pos]--;
}
@ -102,16 +170,36 @@ public class IncrementDecrement @@ -102,16 +170,36 @@ public class IncrementDecrement
return IncrementDecrement.StaticField++;
}
public int PostIncrementStaticProperty()
{
return IncrementDecrement.StaticProperty++;
}
public int PostIncrementInstanceField(IncrementDecrement.MutableClass m)
{
return m.Field++;
}
public int PostDecrementInstanceField()
// public uint PostIncrementIndexer(string name)
// {
// return this.M()[name]++;
// }
public int PostIncrementInstanceField()
{
return this.M().Field--;
}
public int PostIncrementInstanceProperty()
{
return this.M().Property--;
}
public int PostIncrement2DArray()
{
return this.Array()[IncrementDecrement.StaticField, IncrementDecrement.StaticProperty]++;
}
public int PostIncrementByRef(ref int i)
{
return i++;
@ -121,4 +209,9 @@ public class IncrementDecrement @@ -121,4 +209,9 @@ public class IncrementDecrement
{
return (*this.GetPointer())++;
}
// public unsafe int PostIncrementOfPointer(int* ptr)
// {
// return *(ptr++);
// }
}

9
ICSharpCode.Decompiler/Tests/MultidimensionalArray.cs

@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
using System;
public static class MultidimensionalArray
public class MultidimensionalArray
{
internal class Generic<T, S> where T : new()
{
@ -29,9 +29,14 @@ public static class MultidimensionalArray @@ -29,9 +29,14 @@ public static class MultidimensionalArray
this.b[5, 3][1] = x;
this.b[5, 3][2] = y;
}
public void PassByReference(ref T arr)
{
this.PassByReference(ref this.a[10, 10]);
}
}
public static int[][,] MakeArray()
public int[][,] MakeArray()
{
return new int[10][,];
}

7
ICSharpCode.Decompiler/Tests/UnsafeCode.cs

@ -31,6 +31,13 @@ public class UnsafeCode @@ -31,6 +31,13 @@ public class UnsafeCode
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)

Loading…
Cancel
Save