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. 93
      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 @@ -393,9 +393,11 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Initblk: return InlineAssembly(byteCode, args);
case ILCode.Initobj:
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
return InlineAssembly(byteCode, args);
case ILCode.DefaultValue:
return MakeDefaultValue((TypeReference)operand);
case ILCode.Jmp: return InlineAssembly(byteCode, args);
case ILCode.Ldarg: {
if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) {
@ -418,7 +420,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -418,7 +420,7 @@ namespace ICSharpCode.Decompiler.Ast
return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand));
}
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_R8:
case ILCode.Ldc_Decimal:
@ -512,6 +514,28 @@ namespace ICSharpCode.Decompiler.Ast @@ -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)
{
Cecil.MethodReference cecilMethod = ((MethodReference)operand);

93
ICSharpCode.Decompiler/ILAst/ArrayInitializers.cs

@ -30,28 +30,33 @@ namespace ICSharpCode.Decompiler.ILAst @@ -30,28 +30,33 @@ namespace ICSharpCode.Decompiler.ILAst
return;
if (initializeArrayPattern.Match(block.Body.ElementAtOrDefault(i + 1))) {
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;
}
if (i + 1 + arrayLength > block.Body.Count)
return;
List<ILExpression> operands = new List<ILExpression>();
for (int j = 0; j < arrayLength; j++) {
ILExpression expr = block.Body[i + 1 + j] as ILExpression;
int numberOfInstructionsToRemove = 0;
for (int j = i + 1; j < block.Body.Count; j++) {
ILExpression expr = block.Body[j] as ILExpression;
if (expr == null || !IsStoreToArray(expr.Code))
break;
if (!(expr.Arguments[0].Code == ILCode.Ldloc && expr.Arguments[0].Operand == newArrPattern.LastVariable))
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;
while (operands.Count < pos)
operands.Add(new ILExpression(ILCode.DefaultValue, newArrInst.Operand));
operands.Add(expr.Arguments[2]);
numberOfInstructionsToRemove++;
}
if (operands.Count == arrayLength) {
((ILExpression)block.Body[i]).Arguments[0] = new ILExpression(
ILCode.InitArray, newArrInst.Operand, operands.ToArray());
block.Body.RemoveRange(i + 1, arrayLength);
i -= new ILInlining(method).InlineInto(block, i + 1) - 1;
block.Body.RemoveRange(i + 1, numberOfInstructionsToRemove);
i -= new ILInlining(method).InlineInto(block, i + 1, aggressive: true) - 1;
}
};
}
@ -79,21 +84,75 @@ namespace ICSharpCode.Decompiler.ILAst @@ -79,21 +84,75 @@ namespace ICSharpCode.Decompiler.ILAst
FieldDefinition field = ((ILExpression)block.Body[i + 1]).Arguments[1].Operand as FieldDefinition;
if (field == null || field.InitialValue == null)
return false;
switch (TypeAnalysis.GetTypeCode(newArrInst.Operand as TypeReference)) {
ILExpression[] newArr = new ILExpression[arrayLength];
if (DecodeArrayInitializer(TypeAnalysis.GetTypeCode(newArrInst.Operand as TypeReference), field.InitialValue, newArr)) {
block.Body[i] = new ILExpression(ILCode.Stloc, newArrPattern.LastVariable, new ILExpression(ILCode.InitArray, newArrInst.Operand, newArr));
block.Body.RemoveAt(i + 1);
return true;
}
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;
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 (field.InitialValue.Length == arrayLength * 4) {
ILExpression[] newArr = new ILExpression[arrayLength];
for (int j = 0; j < newArr.Length; j++) {
newArr[j] = new ILExpression(ILCode.Ldc_I4, BitConverter.ToInt32(field.InitialValue, j * 4));
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));
}
block.Body[i] = new ILExpression(ILCode.Stloc, newArrPattern.LastVariable, new ILExpression(ILCode.InitArray, newArrInst.Operand, newArr));
block.Body.RemoveAt(i + 1);
return true;
}
break;
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;
}
return false;
}
}
}

1
ICSharpCode.Decompiler/ILAst/ILCodes.cs

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

8
ICSharpCode.Decompiler/ILAst/ILInlining.cs

@ -76,7 +76,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -76,7 +76,7 @@ namespace ICSharpCode.Decompiler.ILAst
/// Inlines instructions before pos into block.Body[pos].
/// </summary>
/// <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)
return 0;
@ -85,7 +85,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -85,7 +85,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression expr = block.Body[pos] as ILExpression;
if (expr == null || expr.Code != ILCode.Stloc)
break;
if (InlineIfPossible(block, pos))
if (InlineIfPossible(block, pos, aggressive))
count++;
else
break;
@ -96,7 +96,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -96,7 +96,7 @@ namespace ICSharpCode.Decompiler.ILAst
/// <summary>
/// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible.
/// </summary>
public bool InlineIfPossible(ILBlock block, int pos, bool aggressive = true)
public bool InlineIfPossible(ILBlock block, int pos, bool aggressive)
{
ILVariable v;
ILExpression inlinedExpression;
@ -251,7 +251,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -251,7 +251,7 @@ namespace ICSharpCode.Decompiler.ILAst
}
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--;
}
}

82
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -30,8 +30,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -30,8 +30,8 @@ namespace ICSharpCode.Decompiler.ILAst
transforms.CachedDelegateInitialization
};
Func<ILExpression, ILExpression>[] exprTransforms = {
EliminateDups,
HandleDecimalConstants
HandleDecimalConstants,
SimplifyLdObjAndStObj
};
// 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.
@ -45,9 +45,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -45,9 +45,10 @@ namespace ICSharpCode.Decompiler.ILAst
expr = block.Body[i] as ILExpression;
if (expr != null) {
// apply expr transforms to top-level expr in block
foreach (var t in exprTransforms)
expr = t(expr);
bool modified = ApplyExpressionTransforms(ref expr, exprTransforms);
block.Body[i] = expr;
if (modified)
new ILInlining(method).InlineIfPossible(block, i, aggressive: false);
}
// apply block transforms
foreach (var t in blockTransforms) {
@ -63,20 +64,29 @@ namespace ICSharpCode.Decompiler.ILAst @@ -63,20 +64,29 @@ namespace ICSharpCode.Decompiler.ILAst
// apply expr transforms to all arguments
for (int i = 0; i < expr.Arguments.Count; i++) {
ILExpression arg = expr.Arguments[i];
foreach (var t in exprTransforms)
arg = t(arg);
ApplyExpressionTransforms(ref arg, exprTransforms);
expr.Arguments[i] = arg;
}
}
}
}
static ILExpression EliminateDups(ILExpression expr)
static bool ApplyExpressionTransforms(ref ILExpression expr, Func<ILExpression, ILExpression>[] exprTransforms)
{
if (expr.Code == ILCode.Dup)
return expr.Arguments.Single();
else
return expr;
bool modifiedInAnyIteration = false;
bool modified;
do {
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
@ -120,6 +130,54 @@ namespace ICSharpCode.Decompiler.ILAst @@ -120,6 +130,54 @@ namespace ICSharpCode.Decompiler.ILAst
}
#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
void CachedDelegateInitialization(ILBlock block, ref int i)
{
@ -163,7 +221,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -163,7 +221,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (parent.Arguments[j].Code == ILCode.Ldsfld && parent.Arguments[j].Operand == field) {
parent.Arguments[j] = newObj;
block.Body.RemoveAt(i);
i -= new ILInlining(method).InlineInto(block, i);
i -= new ILInlining(method).InlineInto(block, i, aggressive: true);
return;
}
}

2
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

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

67
ICSharpCode.Decompiler/Tests/ArrayInitializers.cs

@ -22,7 +22,7 @@ public class ArrayInitializers @@ -22,7 +22,7 @@ public class ArrayInitializers
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)
@ -33,4 +33,69 @@ public class ArrayInitializers @@ -33,4 +33,69 @@ public class ArrayInitializers
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 @@ @@ -53,6 +53,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Switch.cs" />
<Compile Include="YieldReturn.cs" />
<None Include="Types\S_EnumSamples.cs" />
<None Include="CustomAttributes\S_AssemblyCustomAttribute.cs" />

37
ICSharpCode.Decompiler/Tests/Switch.cs

@ -0,0 +1,37 @@ @@ -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