mirror of https://github.com/icsharpcode/ILSpy.git
14 changed files with 498 additions and 88 deletions
@ -0,0 +1,113 @@
@@ -0,0 +1,113 @@
|
||||
// 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 Decompiler |
||||
{ |
||||
/// <summary>
|
||||
/// IL AST transformation that introduces array initializers.
|
||||
/// </summary>
|
||||
public class ArrayInitializers |
||||
{ |
||||
public static void Transform(ILBlock method) |
||||
{ |
||||
// TODO: move this somewhere else
|
||||
// Eliminate 'dups':
|
||||
foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) { |
||||
for (int i = 0; i < expr.Arguments.Count; i++) { |
||||
if (expr.Arguments[i].Code == ILCode.Dup) |
||||
expr.Arguments[i] = expr.Arguments[i].Arguments[0]; |
||||
} |
||||
} |
||||
|
||||
var newArrPattern = new StoreToVariable(new ILExpression(ILCode.Newarr, ILExpression.AnyOperand, new ILExpression(ILCode.Ldc_I4, ILExpression.AnyOperand))); |
||||
var arg1 = new StoreToVariable(new LoadFromVariable(newArrPattern)) { MustBeGenerated = true }; |
||||
var arg2 = new StoreToVariable(new LoadFromVariable(newArrPattern)) { MustBeGenerated = true }; |
||||
var initializeArrayPattern = new ILCall( |
||||
"System.Runtime.CompilerServices.RuntimeHelpers", "InitializeArray", |
||||
new LoadFromVariable(arg1), new ILExpression(ILCode.Ldtoken, ILExpression.AnyOperand)); |
||||
foreach (ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { |
||||
for (int i = block.Body.Count - 1; i >= 0; i--) { |
||||
if (!newArrPattern.Match(block.Body[i])) |
||||
continue; |
||||
ILExpression newArrInst = ((ILExpression)block.Body[i]).Arguments[0]; |
||||
int arrayLength = (int)newArrInst.Arguments[0].Operand; |
||||
if (arrayLength == 0) |
||||
continue; |
||||
if (arg1.Match(block.Body.ElementAtOrDefault(i + 1)) && arg2.Match(block.Body.ElementAtOrDefault(i + 2))) { |
||||
if (initializeArrayPattern.Match(block.Body.ElementAtOrDefault(i + 3))) { |
||||
if (HandleStaticallyInitializedArray(arg2, block, i, newArrInst, arrayLength)) { |
||||
i -= ILInlining.InlineInto(block, i + 1, method) - 1; |
||||
} |
||||
continue; |
||||
} |
||||
} |
||||
if (i + 1 + arrayLength > block.Body.Count) |
||||
continue; |
||||
List<ILExpression> operands = new List<ILExpression>(); |
||||
for (int j = 0; j < arrayLength; j++) { |
||||
ILExpression expr = block.Body[i + 1 + 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)) |
||||
break; |
||||
operands.Add(expr.Arguments[2]); |
||||
} |
||||
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 -= ILInlining.InlineInto(block, i + 1, method) - 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 arg2, ILBlock block, int i, ILExpression newArrInst, int arrayLength) |
||||
{ |
||||
FieldDefinition field = ((ILExpression)block.Body[i + 3]).Arguments[1].Operand as FieldDefinition; |
||||
if (field == null || field.InitialValue == null) |
||||
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]; |
||||
for (int j = 0; j < newArr.Length; j++) { |
||||
newArr[j] = new ILExpression(ILCode.Ldc_I4, BitConverter.ToInt32(field.InitialValue, j * 4)); |
||||
} |
||||
block.Body[i] = new ILExpression(ILCode.Stloc, arg2.LastVariable, new ILExpression(ILCode.InitArray, newArrInst.Operand, newArr)); |
||||
block.Body.RemoveRange(i + 1, 3); |
||||
return true; |
||||
} |
||||
break; |
||||
} |
||||
return false; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,92 @@
@@ -0,0 +1,92 @@
|
||||
// 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.Diagnostics; |
||||
using System.Linq; |
||||
|
||||
namespace Decompiler |
||||
{ |
||||
/// <summary>
|
||||
/// Performs inlining transformations.
|
||||
/// </summary>
|
||||
public class ILInlining |
||||
{ |
||||
/// <summary>
|
||||
/// Inlines instructions before pos into block.Body[pos].
|
||||
/// </summary>
|
||||
/// <returns>The number of instructions that were inlined.</returns>
|
||||
public static int InlineInto(ILBlock block, int pos, ILBlock method) |
||||
{ |
||||
int count = 0; |
||||
while (--pos >= 0) { |
||||
ILExpression expr = block.Body[pos] as ILExpression; |
||||
if (expr == null || expr.Code != ILCode.Stloc) |
||||
break; |
||||
if (InlineIfPossible(block, pos, method)) |
||||
count++; |
||||
else |
||||
break; |
||||
} |
||||
return count; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible.
|
||||
/// </summary>
|
||||
public static bool InlineIfPossible(ILBlock block, int pos, ILBlock method) |
||||
{ |
||||
if (InlineIfPossible((ILExpression)block.Body[pos], block.Body.ElementAtOrDefault(pos+1), method)) { |
||||
block.Body.RemoveAt(pos); |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Inlines 'expr' into 'next', if possible.
|
||||
/// </summary>
|
||||
public static bool InlineIfPossible(ILExpression expr, ILNode next, ILBlock method) |
||||
{ |
||||
if (expr.Code != ILCode.Stloc) |
||||
throw new ArgumentException("expr must be stloc"); |
||||
// ensure the variable is only accessed only a single time
|
||||
if (method.GetSelfAndChildrenRecursive<ILExpression>().Count(e => e != expr && e.Operand == expr.Operand) != 1) |
||||
return false; |
||||
ILExpression parent; |
||||
int pos; |
||||
if (FindLoadInNext(next as ILExpression, (ILVariable)expr.Operand, out parent, out pos) == true) { |
||||
parent.Arguments[pos] = expr.Arguments[0]; |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Finds the position to inline to.
|
||||
/// </summary>
|
||||
/// <returns>true = found; false = cannot continue search; null = not found</returns>
|
||||
static bool? FindLoadInNext(ILExpression expr, ILVariable v, out ILExpression parent, out int pos) |
||||
{ |
||||
for (int i = 0; i < expr.Arguments.Count; i++) { |
||||
ILExpression arg = expr.Arguments[i]; |
||||
if (arg.Code == ILCode.Ldloc && arg.Operand == v) { |
||||
parent = expr; |
||||
pos = i; |
||||
return true; |
||||
} |
||||
bool? r = FindLoadInNext(arg, v, out parent, out pos); |
||||
if (r != null) |
||||
return r; |
||||
} |
||||
parent = null; |
||||
pos = 0; |
||||
return IsWithoutSideEffects(expr.Code) ? (bool?)null : false; |
||||
} |
||||
|
||||
static bool IsWithoutSideEffects(ILCode code) |
||||
{ |
||||
return code == ILCode.Ldloc; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,103 @@
@@ -0,0 +1,103 @@
|
||||
// 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; |
||||
|
||||
namespace Decompiler |
||||
{ |
||||
public interface IVariablePattern |
||||
{ |
||||
bool MatchVariable(ILVariable v); |
||||
} |
||||
|
||||
public class StoreToVariable : ILExpression, IVariablePattern |
||||
{ |
||||
public bool MustBeGenerated; |
||||
public ILExpression LastMatch; |
||||
|
||||
public ILVariable LastVariable { |
||||
get { |
||||
return LastMatch != null ? LastMatch.Operand as ILVariable : null; |
||||
} |
||||
} |
||||
|
||||
public StoreToVariable(ILExpression arg) : base(ILCode.Pattern, null, arg) |
||||
{ |
||||
} |
||||
|
||||
public override bool Match(ILNode other) |
||||
{ |
||||
ILExpression expr = other as ILExpression; |
||||
if (expr != null && expr.Code == ILCode.Stloc && (!MustBeGenerated || ((ILVariable)expr.Operand).IsGenerated) && Match(this.Arguments, expr.Arguments)) { |
||||
this.LastMatch = expr; |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
bool IVariablePattern.MatchVariable(ILVariable v) |
||||
{ |
||||
return v == LastMatch.Operand; |
||||
} |
||||
} |
||||
|
||||
public class LoadFromVariable : ILExpression |
||||
{ |
||||
IVariablePattern v; |
||||
|
||||
public LoadFromVariable(IVariablePattern v) : base(ILCode.Pattern, null) |
||||
{ |
||||
this.v = v; |
||||
} |
||||
|
||||
public override bool Match(ILNode other) |
||||
{ |
||||
ILExpression expr = other as ILExpression; |
||||
return expr != null && expr.Code == ILCode.Ldloc && v.MatchVariable(expr.Operand as ILVariable); |
||||
} |
||||
} |
||||
|
||||
public class AnyILExpression : ILExpression |
||||
{ |
||||
public ILExpression LastMatch; |
||||
|
||||
public AnyILExpression() : base(ILCode.Pattern, null) |
||||
{ |
||||
} |
||||
|
||||
public override bool Match(ILNode other) |
||||
{ |
||||
if (other is ILExpression) { |
||||
LastMatch = (ILExpression)other; |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public class ILCall : ILExpression |
||||
{ |
||||
string fullClassName; |
||||
string methodName; |
||||
|
||||
public ILCall(string fullClassName, string methodName, params ILExpression[] args) : base(ILCode.Pattern, null, args) |
||||
{ |
||||
this.fullClassName = fullClassName; |
||||
this.methodName = methodName; |
||||
} |
||||
|
||||
public override bool Match(ILNode other) |
||||
{ |
||||
ILExpression expr = other as ILExpression; |
||||
if (expr != null && expr.Code == ILCode.Call) { |
||||
MethodReference r = (MethodReference)expr.Operand; |
||||
if (r.Name == methodName && r.DeclaringType.FullName == fullClassName) |
||||
return Match(this.Arguments, expr.Arguments); |
||||
} |
||||
return false; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
// 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 class ArrayInitializers |
||||
{ |
||||
// Helper methods used to ensure array initializers used within expressions work correctly
|
||||
static void X(object a, object b) |
||||
{ |
||||
} |
||||
|
||||
static object Y() |
||||
{ |
||||
return null; |
||||
} |
||||
|
||||
public static void Array1() |
||||
{ |
||||
X(Y(), new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); |
||||
} |
||||
|
||||
public static void Array2(int a, int b, int c) |
||||
{ |
||||
X(Y(), new int[] { a, b, c }); |
||||
} |
||||
|
||||
public static void NestedArray(int a, int b, int c) |
||||
{ |
||||
X(Y(), new int[][] { |
||||
new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, |
||||
new int[] { a, b, c }, |
||||
new int[] { 1, 2, 3, 4, 5, 6 } |
||||
}); |
||||
} |
||||
} |
Loading…
Reference in new issue