Browse Source

Improve array initializer support.

pull/100/head
Daniel Grunwald 14 years ago
parent
commit
72ee5d309c
  1. 28
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 91
      ICSharpCode.Decompiler/ILAst/ArrayInitializers.cs
  3. 1
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  4. 8
      ICSharpCode.Decompiler/ILAst/ILInlining.cs
  5. 82
      ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  6. 2
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  7. 67
      ICSharpCode.Decompiler/Tests/ArrayInitializers.cs
  8. 1
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  9. 37
      ICSharpCode.Decompiler/Tests/Switch.cs

28
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -393,9 +393,11 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Initblk: return InlineAssembly(byteCode, args); case ILCode.Initblk: return InlineAssembly(byteCode, args);
case ILCode.Initobj: case ILCode.Initobj:
if (args[0] is DirectionExpression) if (args[0] is DirectionExpression)
return new AssignmentExpression(((DirectionExpression)args[0]).Expression.Detach(), new DefaultValueExpression { Type = operandAsTypeRef }); return new AssignmentExpression(((DirectionExpression)args[0]).Expression.Detach(), MakeDefaultValue((TypeReference)operand));
else else
return InlineAssembly(byteCode, args); return InlineAssembly(byteCode, args);
case ILCode.DefaultValue:
return MakeDefaultValue((TypeReference)operand);
case ILCode.Jmp: return InlineAssembly(byteCode, args); case ILCode.Jmp: return InlineAssembly(byteCode, args);
case ILCode.Ldarg: { case ILCode.Ldarg: {
if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) { if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) {
@ -418,7 +420,7 @@ namespace ICSharpCode.Decompiler.Ast
return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand)); return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand));
} }
case ILCode.Ldc_I4: return AstBuilder.MakePrimitive((int)operand, byteCode.InferredType); case ILCode.Ldc_I4: return AstBuilder.MakePrimitive((int)operand, byteCode.InferredType);
case ILCode.Ldc_I8: case ILCode.Ldc_I8: return AstBuilder.MakePrimitive((long)operand, byteCode.InferredType);
case ILCode.Ldc_R4: case ILCode.Ldc_R4:
case ILCode.Ldc_R8: case ILCode.Ldc_R8:
case ILCode.Ldc_Decimal: case ILCode.Ldc_Decimal:
@ -512,6 +514,28 @@ namespace ICSharpCode.Decompiler.Ast
} }
} }
Expression MakeDefaultValue(TypeReference type)
{
TypeDefinition typeDef = type.Resolve();
if (typeDef != null) {
if (TypeAnalysis.IsIntegerOrEnum(typeDef))
return AstBuilder.MakePrimitive(0, typeDef);
else if (!typeDef.IsValueType)
return new NullReferenceExpression();
switch (typeDef.FullName) {
case "System.Nullable`1":
return new NullReferenceExpression();
case "System.Single":
return new PrimitiveExpression(0f);
case "System.Double":
return new PrimitiveExpression(0.0);
case "System.Decimal":
return new PrimitiveExpression(0m);
}
}
return new DefaultValueExpression { Type = AstBuilder.ConvertType(type) };
}
static AstNode TransformCall(bool isVirtual, object operand, MethodDefinition methodDef, List<Ast.Expression> args) static AstNode TransformCall(bool isVirtual, object operand, MethodDefinition methodDef, List<Ast.Expression> args)
{ {
Cecil.MethodReference cecilMethod = ((MethodReference)operand); Cecil.MethodReference cecilMethod = ((MethodReference)operand);

91
ICSharpCode.Decompiler/ILAst/ArrayInitializers.cs

@ -30,28 +30,33 @@ namespace ICSharpCode.Decompiler.ILAst
return; return;
if (initializeArrayPattern.Match(block.Body.ElementAtOrDefault(i + 1))) { if (initializeArrayPattern.Match(block.Body.ElementAtOrDefault(i + 1))) {
if (HandleStaticallyInitializedArray(newArrPattern, block, i, newArrInst, arrayLength)) { if (HandleStaticallyInitializedArray(newArrPattern, block, i, newArrInst, arrayLength)) {
i -= new ILInlining(method).InlineInto(block, i + 1) - 1; i -= new ILInlining(method).InlineInto(block, i + 1, aggressive: true) - 1;
} }
return; return;
} }
if (i + 1 + arrayLength > block.Body.Count)
return;
List<ILExpression> operands = new List<ILExpression>(); List<ILExpression> operands = new List<ILExpression>();
for (int j = 0; j < arrayLength; j++) { int numberOfInstructionsToRemove = 0;
ILExpression expr = block.Body[i + 1 + j] as ILExpression; for (int j = i + 1; j < block.Body.Count; j++) {
ILExpression expr = block.Body[j] as ILExpression;
if (expr == null || !IsStoreToArray(expr.Code)) if (expr == null || !IsStoreToArray(expr.Code))
break; break;
if (!(expr.Arguments[0].Code == ILCode.Ldloc && expr.Arguments[0].Operand == newArrPattern.LastVariable)) if (!(expr.Arguments[0].Code == ILCode.Ldloc && expr.Arguments[0].Operand == newArrPattern.LastVariable))
break; break;
if (!(expr.Arguments[1].Code == ILCode.Ldc_I4 && (int)expr.Arguments[1].Operand == j)) if (expr.Arguments[1].Code != ILCode.Ldc_I4)
break;
int pos = (int)expr.Arguments[1].Operand;
if (pos < operands.Count || pos > operands.Count + 10)
break; break;
while (operands.Count < pos)
operands.Add(new ILExpression(ILCode.DefaultValue, newArrInst.Operand));
operands.Add(expr.Arguments[2]); operands.Add(expr.Arguments[2]);
numberOfInstructionsToRemove++;
} }
if (operands.Count == arrayLength) { if (operands.Count == arrayLength) {
((ILExpression)block.Body[i]).Arguments[0] = new ILExpression( ((ILExpression)block.Body[i]).Arguments[0] = new ILExpression(
ILCode.InitArray, newArrInst.Operand, operands.ToArray()); ILCode.InitArray, newArrInst.Operand, operands.ToArray());
block.Body.RemoveRange(i + 1, arrayLength); block.Body.RemoveRange(i + 1, numberOfInstructionsToRemove);
i -= new ILInlining(method).InlineInto(block, i + 1) - 1; i -= new ILInlining(method).InlineInto(block, i + 1, aggressive: true) - 1;
} }
}; };
} }
@ -79,21 +84,75 @@ namespace ICSharpCode.Decompiler.ILAst
FieldDefinition field = ((ILExpression)block.Body[i + 1]).Arguments[1].Operand as FieldDefinition; FieldDefinition field = ((ILExpression)block.Body[i + 1]).Arguments[1].Operand as FieldDefinition;
if (field == null || field.InitialValue == null) if (field == null || field.InitialValue == null)
return false; return false;
switch (TypeAnalysis.GetTypeCode(newArrInst.Operand as TypeReference)) {
case TypeCode.Int32:
case TypeCode.UInt32:
if (field.InitialValue.Length == arrayLength * 4) {
ILExpression[] newArr = new ILExpression[arrayLength]; ILExpression[] newArr = new ILExpression[arrayLength];
for (int j = 0; j < newArr.Length; j++) { if (DecodeArrayInitializer(TypeAnalysis.GetTypeCode(newArrInst.Operand as TypeReference), field.InitialValue, newArr)) {
newArr[j] = new ILExpression(ILCode.Ldc_I4, BitConverter.ToInt32(field.InitialValue, j * 4));
}
block.Body[i] = new ILExpression(ILCode.Stloc, newArrPattern.LastVariable, new ILExpression(ILCode.InitArray, newArrInst.Operand, newArr)); block.Body[i] = new ILExpression(ILCode.Stloc, newArrPattern.LastVariable, new ILExpression(ILCode.InitArray, newArrInst.Operand, newArr));
block.Body.RemoveAt(i + 1); block.Body.RemoveAt(i + 1);
return true; return true;
} }
break; return false;
}
static bool DecodeArrayInitializer(TypeCode elementType, byte[] initialValue, ILExpression[] output)
{
switch (elementType) {
case TypeCode.Boolean:
case TypeCode.SByte:
case TypeCode.Byte:
if (initialValue.Length == output.Length) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_I4, (int)initialValue[j]);
}
return true;
} }
return false; return false;
case TypeCode.Char:
case TypeCode.Int16:
case TypeCode.UInt16:
if (initialValue.Length == output.Length * 2) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_I4, (int)BitConverter.ToInt16(initialValue, j * 2));
}
return true;
}
return false;
case TypeCode.Int32:
case TypeCode.UInt32:
if (initialValue.Length == output.Length * 4) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_I4, BitConverter.ToInt32(initialValue, j * 4));
}
return true;
}
return false;
case TypeCode.Int64:
case TypeCode.UInt64:
if (initialValue.Length == output.Length * 8) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_I8, BitConverter.ToInt64(initialValue, j * 8));
}
return true;
}
return false;
case TypeCode.Single:
if (initialValue.Length == output.Length * 4) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_R4, BitConverter.ToSingle(initialValue, j * 4));
}
return true;
}
return false;
case TypeCode.Double:
if (initialValue.Length == output.Length * 8) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_R8, BitConverter.ToDouble(initialValue, j * 8));
}
return true;
}
return false;
default:
return false;
}
} }
} }
} }

1
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -265,6 +265,7 @@ namespace ICSharpCode.Decompiler.ILAst
Ldc_Decimal, Ldc_Decimal,
YieldBreak, YieldBreak,
YieldReturn, YieldReturn,
DefaultValue, // default(T)
Pattern // used for ILAst pattern nodes Pattern // used for ILAst pattern nodes
} }

8
ICSharpCode.Decompiler/ILAst/ILInlining.cs

@ -76,7 +76,7 @@ namespace ICSharpCode.Decompiler.ILAst
/// Inlines instructions before pos into block.Body[pos]. /// Inlines instructions before pos into block.Body[pos].
/// </summary> /// </summary>
/// <returns>The number of instructions that were inlined.</returns> /// <returns>The number of instructions that were inlined.</returns>
public int InlineInto(ILBlock block, int pos) public int InlineInto(ILBlock block, int pos, bool aggressive)
{ {
if (pos >= block.Body.Count) if (pos >= block.Body.Count)
return 0; return 0;
@ -85,7 +85,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression expr = block.Body[pos] as ILExpression; ILExpression expr = block.Body[pos] as ILExpression;
if (expr == null || expr.Code != ILCode.Stloc) if (expr == null || expr.Code != ILCode.Stloc)
break; break;
if (InlineIfPossible(block, pos)) if (InlineIfPossible(block, pos, aggressive))
count++; count++;
else else
break; break;
@ -96,7 +96,7 @@ namespace ICSharpCode.Decompiler.ILAst
/// <summary> /// <summary>
/// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible. /// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible.
/// </summary> /// </summary>
public bool InlineIfPossible(ILBlock block, int pos, bool aggressive = true) public bool InlineIfPossible(ILBlock block, int pos, bool aggressive)
{ {
ILVariable v; ILVariable v;
ILExpression inlinedExpression; ILExpression inlinedExpression;
@ -251,7 +251,7 @@ namespace ICSharpCode.Decompiler.ILAst
} }
block.Body.RemoveAt(i); block.Body.RemoveAt(i);
InlineInto(block, i); // maybe inlining gets possible after the removal of block.Body[i] InlineInto(block, i, aggressive: false); // maybe inlining gets possible after the removal of block.Body[i]
i--; i--;
} }
} }

82
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -30,8 +30,8 @@ namespace ICSharpCode.Decompiler.ILAst
transforms.CachedDelegateInitialization transforms.CachedDelegateInitialization
}; };
Func<ILExpression, ILExpression>[] exprTransforms = { Func<ILExpression, ILExpression>[] exprTransforms = {
EliminateDups, HandleDecimalConstants,
HandleDecimalConstants SimplifyLdObjAndStObj
}; };
// Traverse in post order so that nested blocks are transformed first. This is required so that // Traverse in post order so that nested blocks are transformed first. This is required so that
// patterns on the parent block can assume that all nested blocks are already transformed. // patterns on the parent block can assume that all nested blocks are already transformed.
@ -45,9 +45,10 @@ namespace ICSharpCode.Decompiler.ILAst
expr = block.Body[i] as ILExpression; expr = block.Body[i] as ILExpression;
if (expr != null) { if (expr != null) {
// apply expr transforms to top-level expr in block // apply expr transforms to top-level expr in block
foreach (var t in exprTransforms) bool modified = ApplyExpressionTransforms(ref expr, exprTransforms);
expr = t(expr);
block.Body[i] = expr; block.Body[i] = expr;
if (modified)
new ILInlining(method).InlineIfPossible(block, i, aggressive: false);
} }
// apply block transforms // apply block transforms
foreach (var t in blockTransforms) { foreach (var t in blockTransforms) {
@ -63,20 +64,29 @@ namespace ICSharpCode.Decompiler.ILAst
// apply expr transforms to all arguments // apply expr transforms to all arguments
for (int i = 0; i < expr.Arguments.Count; i++) { for (int i = 0; i < expr.Arguments.Count; i++) {
ILExpression arg = expr.Arguments[i]; ILExpression arg = expr.Arguments[i];
foreach (var t in exprTransforms) ApplyExpressionTransforms(ref arg, exprTransforms);
arg = t(arg);
expr.Arguments[i] = arg; expr.Arguments[i] = arg;
} }
} }
} }
} }
static ILExpression EliminateDups(ILExpression expr) static bool ApplyExpressionTransforms(ref ILExpression expr, Func<ILExpression, ILExpression>[] exprTransforms)
{ {
if (expr.Code == ILCode.Dup) bool modifiedInAnyIteration = false;
return expr.Arguments.Single(); bool modified;
else do {
return expr; modified = false;
ILExpression oldExpr = expr;
ILCode oldOpCode = oldExpr.Code;
foreach (var t in exprTransforms)
expr = t(expr);
if (expr != oldExpr || oldOpCode != expr.Code) {
modified = true;
modifiedInAnyIteration = true;
}
} while (modified);
return modifiedInAnyIteration;
} }
#region HandleDecimalConstants #region HandleDecimalConstants
@ -120,6 +130,54 @@ namespace ICSharpCode.Decompiler.ILAst
} }
#endregion #endregion
#region SimplifyLdObjAndStObj
static ILExpression SimplifyLdObjAndStObj(ILExpression expr)
{
if (expr.Code == ILCode.Initobj) {
expr.Code = ILCode.Stobj;
expr.Arguments.Add(new ILExpression(ILCode.DefaultValue, expr.Operand));
}
if (expr.Code == ILCode.Stobj) {
switch (expr.Arguments[0].Code) {
case ILCode.Ldelema:
return SimplifyLdObjOrStObj(expr, ILCode.Stelem_Any);
case ILCode.Ldloca:
return SimplifyLdObjOrStObj(expr, ILCode.Stloc);
case ILCode.Ldarga:
return SimplifyLdObjOrStObj(expr, ILCode.Starg);
case ILCode.Ldflda:
return SimplifyLdObjOrStObj(expr, ILCode.Stfld);
case ILCode.Ldsflda:
return SimplifyLdObjOrStObj(expr, ILCode.Stsfld);
}
} else if (expr.Code == ILCode.Ldobj) {
switch (expr.Arguments[0].Code) {
case ILCode.Ldelema:
return SimplifyLdObjOrStObj(expr, ILCode.Ldelem_Any);
case ILCode.Ldloca:
return SimplifyLdObjOrStObj(expr, ILCode.Ldloc);
case ILCode.Ldarga:
return SimplifyLdObjOrStObj(expr, ILCode.Ldarg);
case ILCode.Ldflda:
return SimplifyLdObjOrStObj(expr, ILCode.Ldfld);
case ILCode.Ldsflda:
return SimplifyLdObjOrStObj(expr, ILCode.Ldsfld);
}
}
return expr;
}
static ILExpression SimplifyLdObjOrStObj(ILExpression expr, ILCode newCode)
{
ILExpression lda = expr.Arguments[0];
lda.Code = newCode;
if (expr.Code == ILCode.Stobj)
lda.Arguments.Add(expr.Arguments[1]);
lda.ILRanges.AddRange(expr.ILRanges);
return lda;
}
#endregion
#region CachedDelegateInitialization #region CachedDelegateInitialization
void CachedDelegateInitialization(ILBlock block, ref int i) void CachedDelegateInitialization(ILBlock block, ref int i)
{ {
@ -163,7 +221,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (parent.Arguments[j].Code == ILCode.Ldsfld && parent.Arguments[j].Operand == field) { if (parent.Arguments[j].Code == ILCode.Ldsfld && parent.Arguments[j].Operand == field) {
parent.Arguments[j] = newObj; parent.Arguments[j] = newObj;
block.Body.RemoveAt(i); block.Body.RemoveAt(i);
i -= new ILInlining(method).InlineInto(block, i); i -= new ILInlining(method).InlineInto(block, i, aggressive: true);
return; return;
} }
} }

2
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -260,6 +260,8 @@ namespace ICSharpCode.Decompiler.ILAst
return null; return null;
case ILCode.Initobj: case ILCode.Initobj:
return null; return null;
case ILCode.DefaultValue:
return (TypeReference)expr.Operand;
case ILCode.Localloc: case ILCode.Localloc:
if (forceInferChildren) { if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], typeSystem.Int32); InferTypeForExpression(expr.Arguments[0], typeSystem.Int32);

67
ICSharpCode.Decompiler/Tests/ArrayInitializers.cs

@ -22,7 +22,7 @@ public class ArrayInitializers
public static void Array2(int a, int b, int c) public static void Array2(int a, int b, int c)
{ {
X(Y(), new int[] { a, b, c }); X(Y(), new int[] { a, 0, b, 0, c });
} }
public static void NestedArray(int a, int b, int c) public static void NestedArray(int a, int b, int c)
@ -33,4 +33,69 @@ public class ArrayInitializers
new int[] { 1, 2, 3, 4, 5, 6 } new int[] { 1, 2, 3, 4, 5, 6 }
}); });
} }
public static void ArrayBoolean()
{
X(Y(), new bool[] { true, false, true, false, false, false, true, true });
}
public static void ArrayByte()
{
X(Y(), new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 254, 255 });
}
public static void ArraySByte()
{
X(Y(), new sbyte[] { -128, -127, 0, 1, 2, 3, 4, 127 });
}
public static void ArrayShort()
{
X(Y(), new short[] { -32768, -1, 0, 1, 32767 });
}
public static void ArrayUShort()
{
X(Y(), new ushort[] { 0, 1, 32767, 32768, 65534, 65535 });
}
public static void ArrayInt()
{
X(Y(), new int[] { 1, -2, 2000000000, 4, 5, -6, 7, 8, 9, 10 });
}
public static void ArrayUInt()
{
X(Y(), new uint[] { 1, 2000000000, 3000000000, 4, 5, 6, 7, 8, 9, 10 });
}
public static void ArrayLong()
{
X(Y(), new long[] { -4999999999999999999, -1, 0, 1, 4999999999999999999 });
}
public static void ArrayULong()
{
X(Y(), new ulong[] { 1, 2000000000, 3000000000, 4, 5, 6, 7, 8, 4999999999999999999, 9999999999999999999 });
}
public static void ArrayFloat()
{
X(Y(), new float[] { -1.5f, 0f, 1.5f, float.NegativeInfinity, float.PositiveInfinity, float.NaN });
}
public static void ArrayDouble()
{
X(Y(), new double[] { -1.5, 0.0, 1.5, double.NegativeInfinity, double.PositiveInfinity, double.NaN });
}
public static void ArrayDecimal()
{
X(Y(), new decimal[] { -100m, 0m, 100m, decimal.MinValue, decimal.MaxValue, 0.0000001m });
}
public static void ArrayString()
{
X(Y(), new string[] { "", null, "Hello", "World" });
}
} }

1
ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj

@ -53,6 +53,7 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Switch.cs" />
<Compile Include="YieldReturn.cs" /> <Compile Include="YieldReturn.cs" />
<None Include="Types\S_EnumSamples.cs" /> <None Include="Types\S_EnumSamples.cs" />
<None Include="CustomAttributes\S_AssemblyCustomAttribute.cs" /> <None Include="CustomAttributes\S_AssemblyCustomAttribute.cs" />

37
ICSharpCode.Decompiler/Tests/Switch.cs

@ -0,0 +1,37 @@
// 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 Switch
{
public static string ShortSwitchOverString(string text)
{
switch (text) {
case "First case":
return "Text";
default:
return "Default";
}
}
public static string SwitchOverString(string text)
{
switch (text) {
case "First case":
return "Text1";
case "Second case":
return "Text2";
case "Third case":
return "Text3";
case "Fourth case":
return "Text4";
case "Fifth case":
return "Text5";
case "Sixth case":
return "Text6";
default:
return "Default";
}
}
}
Loading…
Cancel
Save