Browse Source

Add support for collection initializers.

pull/100/head
Daniel Grunwald 14 years ago
parent
commit
b6d832d212
  1. 17
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  3. 158
      ICSharpCode.Decompiler/ILAst/ArrayInitializers.cs
  4. 2
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  5. 10
      ICSharpCode.Decompiler/ILAst/ILInlining.cs
  6. 213
      ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs
  7. 4
      ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  8. 12
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  9. 2
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  10. 21
      ICSharpCode.Decompiler/Tests/InitializerTests.cs
  11. 2
      ILSpy/ILAstLanguage.cs

17
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -510,6 +510,23 @@ namespace ICSharpCode.Decompiler.Ast @@ -510,6 +510,23 @@ namespace ICSharpCode.Decompiler.Ast
return new Ast.YieldBreakStatement();
case ILCode.YieldReturn:
return new Ast.YieldStatement { Expression = arg1 };
case ILCode.InitCollection: {
ObjectCreateExpression oce = (ObjectCreateExpression)arg1;
oce.Initializer = new ArrayInitializerExpression();
for (int i = 1; i < args.Count; i++) {
ArrayInitializerExpression aie = args[i] as ArrayInitializerExpression;
if (aie != null && aie.Elements.Count == 1)
oce.Initializer.Elements.Add(aie.Elements.Single().Detach());
else
oce.Initializer.Elements.Add(args[i]);
}
return oce;
}
case ILCode.InitCollectionAddMethod: {
var collectionInit = new ArrayInitializerExpression();
collectionInit.Elements.AddRange(args);
return collectionInit;
}
default: throw new Exception("Unknown OpCode: " + byteCode.Code);
}
}

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -86,7 +86,7 @@ @@ -86,7 +86,7 @@
<Compile Include="FlowAnalysis\SsaVariable.cs" />
<Compile Include="FlowAnalysis\TransformToSsa.cs" />
<Compile Include="GraphVizGraph.cs" />
<Compile Include="ILAst\ArrayInitializers.cs" />
<Compile Include="ILAst\InitializerPeepholeTransforms.cs" />
<Compile Include="ILAst\DefaultDictionary.cs" />
<Compile Include="ILAst\GotoRemoval.cs" />
<Compile Include="ILAst\ILAstBuilder.cs" />

158
ICSharpCode.Decompiler/ILAst/ArrayInitializers.cs

@ -1,158 +0,0 @@ @@ -1,158 +0,0 @@
// 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 System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst
{
/// <summary>
/// IL AST transformation that introduces array initializers.
/// </summary>
public static class ArrayInitializers
{
public static PeepholeTransform Transform(ILBlock method)
{
var newArrPattern = new StoreToVariable(new ILExpression(ILCode.Newarr, ILExpression.AnyOperand, new ILExpression(ILCode.Ldc_I4, ILExpression.AnyOperand)));
var initializeArrayPattern = new ILCall(
"System.Runtime.CompilerServices.RuntimeHelpers", "InitializeArray",
new LoadFromVariable(newArrPattern), new ILExpression(ILCode.Ldtoken, ILExpression.AnyOperand));
return delegate(ILBlock block, ref int i) {
if (!newArrPattern.Match(block.Body[i]))
return;
ILExpression newArrInst = ((ILExpression)block.Body[i]).Arguments[0];
int arrayLength = (int)newArrInst.Arguments[0].Operand;
if (arrayLength == 0)
return;
if (initializeArrayPattern.Match(block.Body.ElementAtOrDefault(i + 1))) {
if (HandleStaticallyInitializedArray(newArrPattern, block, i, newArrInst, arrayLength)) {
i -= new ILInlining(method).InlineInto(block, i + 1, aggressive: true) - 1;
}
return;
}
List<ILExpression> operands = new List<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)
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, numberOfInstructionsToRemove);
i -= new ILInlining(method).InlineInto(block, i + 1, aggressive: true) - 1;
}
};
}
static bool IsStoreToArray(ILCode code)
{
switch (code) {
case ILCode.Stelem_Any:
case ILCode.Stelem_I:
case ILCode.Stelem_I1:
case ILCode.Stelem_I2:
case ILCode.Stelem_I4:
case ILCode.Stelem_I8:
case ILCode.Stelem_R4:
case ILCode.Stelem_R8:
case ILCode.Stelem_Ref:
return true;
default:
return false;
}
}
static bool HandleStaticallyInitializedArray(StoreToVariable newArrPattern, ILBlock block, int i, ILExpression newArrInst, int arrayLength)
{
FieldDefinition field = ((ILExpression)block.Body[i + 1]).Arguments[1].Operand as FieldDefinition;
if (field == null || field.InitialValue == null)
return false;
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 (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;
}
}
}
}

2
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -259,6 +259,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -259,6 +259,8 @@ namespace ICSharpCode.Decompiler.ILAst
LogicAnd,
LogicOr,
InitArray, // Array Initializer
InitCollection, // Collection Initializer: first arg is newobj, remaining args are InitCollectionAddMethod method calls
InitCollectionAddMethod,
TernaryOp, // ?:
LoopOrSwitchBreak,
LoopContinue,

10
ICSharpCode.Decompiler/ILAst/ILInlining.cs

@ -166,6 +166,16 @@ namespace ICSharpCode.Decompiler.ILAst @@ -166,6 +166,16 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
/// <summary>
/// Gets whether 'expressionBeingMoved' can be inlined into 'expr'.
/// </summary>
public bool CanInlineInto(ILExpression expr, ILVariable v, ILExpression expressionBeingMoved)
{
ILExpression parent;
int pos;
return FindLoadInNext(expr, v, expressionBeingMoved, out parent, out pos) == true;
}
/// <summary>
/// Finds the position to inline to.
/// </summary>

213
ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs

@ -0,0 +1,213 @@ @@ -0,0 +1,213 @@
// 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 System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst
{
/// <summary>
/// IL AST transformation that introduces array, object and collection initializers.
/// </summary>
public class InitializerPeepholeTransforms
{
readonly ILBlock method;
#region Array Initializers
StoreToVariable newArrPattern;
ILCall initializeArrayPattern;
public InitializerPeepholeTransforms(ILBlock method)
{
this.method = method;
newArrPattern = new StoreToVariable(new ILExpression(
ILCode.Newarr, ILExpression.AnyOperand, new ILExpression(ILCode.Ldc_I4, ILExpression.AnyOperand)));
initializeArrayPattern = new ILCall(
"System.Runtime.CompilerServices.RuntimeHelpers", "InitializeArray",
new LoadFromVariable(newArrPattern), new ILExpression(ILCode.Ldtoken, ILExpression.AnyOperand));
}
public void TransformArrayInitializers(ILBlock block, ref int i)
{
if (!newArrPattern.Match(block.Body[i]))
return;
ILExpression newArrInst = ((ILExpression)block.Body[i]).Arguments[0];
int arrayLength = (int)newArrInst.Arguments[0].Operand;
if (arrayLength == 0)
return;
if (initializeArrayPattern.Match(block.Body.ElementAtOrDefault(i + 1))) {
if (HandleStaticallyInitializedArray(newArrPattern, block, i, newArrInst, arrayLength)) {
i -= new ILInlining(method).InlineInto(block, i + 1, aggressive: true) - 1;
}
return;
}
List<ILExpression> operands = new List<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)
break;
int pos = (int)expr.Arguments[1].Operand;
const int maxConsecutiveDefaultValueExpressions = 10;
if (pos < operands.Count || pos > operands.Count + maxConsecutiveDefaultValueExpressions)
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, numberOfInstructionsToRemove);
i -= new ILInlining(method).InlineInto(block, i + 1, aggressive: true) - 1;
}
}
static bool IsStoreToArray(ILCode code)
{
switch (code) {
case ILCode.Stelem_Any:
case ILCode.Stelem_I:
case ILCode.Stelem_I1:
case ILCode.Stelem_I2:
case ILCode.Stelem_I4:
case ILCode.Stelem_I8:
case ILCode.Stelem_R4:
case ILCode.Stelem_R8:
case ILCode.Stelem_Ref:
return true;
default:
return false;
}
}
static bool HandleStaticallyInitializedArray(StoreToVariable newArrPattern, ILBlock block, int i, ILExpression newArrInst, int arrayLength)
{
FieldDefinition field = ((ILExpression)block.Body[i + 1]).Arguments[1].Operand as FieldDefinition;
if (field == null || field.InitialValue == null)
return false;
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 (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;
}
}
#endregion
#region Collection Initilializers
public void TransformCollectionInitializers(ILBlock block, ref int i)
{
ILVariable v;
ILExpression expr;
if (!block.Body[i].Match(ILCode.Stloc, out v, out expr) || expr.Code != ILCode.Newobj)
return;
MethodReference ctor = (MethodReference)expr.Operand;
TypeDefinition td = ctor.DeclaringType.Resolve();
if (td == null || !td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections"))
return;
// This is a collection: we can convert Add() calls into a collection initializer
ILExpression collectionInitializer = new ILExpression(ILCode.InitCollection, null, expr);
for (int j = i + 1; j < block.Body.Count; j++) {
MethodReference addMethod;
List<ILExpression> args;
if (!block.Body[j].Match(ILCode.Callvirt, out addMethod, out args))
break;
if (addMethod.Name != "Add" || !addMethod.HasThis || args.Count < 2 || args[0].Code != ILCode.Ldloc || args[0].Operand != v)
break;
collectionInitializer.Arguments.Add((ILExpression)block.Body[j]);
}
// ensure we added at least one additional arg to the collection initializer:
if (collectionInitializer.Arguments.Count == 1)
return;
ILInlining inline = new ILInlining(method);
ILExpression followingExpr = block.Body.ElementAtOrDefault(i + collectionInitializer.Arguments.Count) as ILExpression;
if (inline.CanInlineInto(followingExpr, v, collectionInitializer)) {
block.Body.RemoveRange(i + 1, collectionInitializer.Arguments.Count - 1);
((ILExpression)block.Body[i]).Arguments[0] = collectionInitializer;
// Change add methods into InitCollectionAddMethod:
for (int j = 1; j < collectionInitializer.Arguments.Count; j++) {
collectionInitializer.Arguments[j].Arguments.RemoveAt(0);
collectionInitializer.Arguments[j].Code = ILCode.InitCollectionAddMethod;
}
inline = new ILInlining(method); // refresh variable usage info
if (inline.InlineIfPossible(block, ref i))
i++; // retry transformations on the new combined instruction
}
}
#endregion
}
}

4
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -25,8 +25,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -25,8 +25,10 @@ namespace ICSharpCode.Decompiler.ILAst
transforms.context = context;
transforms.method = method;
InitializerPeepholeTransforms initializerTransforms = new InitializerPeepholeTransforms(method);
PeepholeTransform[] blockTransforms = {
ArrayInitializers.Transform(method),
initializerTransforms.TransformArrayInitializers,
initializerTransforms.TransformCollectionInitializers,
transforms.CachedDelegateInitialization,
transforms.MakeAssignmentExpression
};

12
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -203,6 +203,18 @@ namespace ICSharpCode.Decompiler.ILAst @@ -203,6 +203,18 @@ namespace ICSharpCode.Decompiler.ILAst
}
return ctor.DeclaringType;
}
case ILCode.InitCollection:
return InferTypeForExpression(expr.Arguments[0], expectedType);
case ILCode.InitCollectionAddMethod:
{
MethodReference addMethod = (MethodReference)expr.Operand;
if (forceInferChildren) {
for (int i = 1; i < addMethod.Parameters.Count; i++) {
InferTypeForExpression(expr.Arguments[i-1], SubstituteTypeArgs(addMethod.Parameters[i].ParameterType, addMethod));
}
}
return addMethod.DeclaringType;
}
#endregion
#region Load/Store Fields
case ILCode.Ldfld:

2
ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj

@ -69,7 +69,7 @@ @@ -69,7 +69,7 @@
<Compile Include="CodeSampleFileParser.cs" />
<Compile Include="CustomAttributes\CustomAttributeTests.cs" />
<Compile Include="DecompilerTestBase.cs" />
<Compile Include="ArrayInitializers.cs" />
<Compile Include="InitializerTests.cs" />
<Compile Include="ExceptionHandling.cs" />
<Compile Include="Generics.cs" />
<Compile Include="MultidimensionalArray.cs" />

21
ICSharpCode.Decompiler/Tests/ArrayInitializers.cs → ICSharpCode.Decompiler/Tests/InitializerTests.cs

@ -2,10 +2,11 @@ @@ -2,10 +2,11 @@
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
public class ArrayInitializers
public class InitializerTests
{
// Helper methods used to ensure array initializers used within expressions work correctly
// Helper methods used to ensure initializers used within expressions work correctly
static void X(object a, object b)
{
}
@ -15,6 +16,7 @@ public class ArrayInitializers @@ -15,6 +16,7 @@ public class ArrayInitializers
return null;
}
#region Array Initializers
public static void Array1()
{
X(Y(), new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
@ -98,4 +100,19 @@ public class ArrayInitializers @@ -98,4 +100,19 @@ public class ArrayInitializers
{
X(Y(), new string[] { "", null, "Hello", "World" });
}
#endregion
public static void CollectionInitializerList()
{
X(Y(), new List<int> { 1, 2, 3 });
}
public static void CollectionInitializerDictionary()
{
X(Y(), new Dictionary<string, int> {
{ "First", 1 },
{ "Second", 2 },
{ "Third" , 3 }
});
}
}

2
ILSpy/ILAstLanguage.cs

@ -80,7 +80,7 @@ namespace ICSharpCode.ILSpy @@ -80,7 +80,7 @@ namespace ICSharpCode.ILSpy
internal static IEnumerable<ILAstLanguage> GetDebugLanguages()
{
yield return new ILAstLanguage { name = "ILAst (unoptimized)", inlineVariables = false };
string nextName = "ILAst (variable inlining)";
string nextName = "ILAst (variable splitting)";
foreach (ILAstOptimizationStep step in Enum.GetValues(typeof(ILAstOptimizationStep))) {
yield return new ILAstLanguage { name = nextName, abortBeforeStep = step };
nextName = "ILAst (after " + step + ")";

Loading…
Cancel
Save