Browse Source

add TransformArrayInitializers

pull/728/head
Siegfried Pammer 10 years ago
parent
commit
bf990f8f41
  1. 3
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 17
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  3. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  4. 35
      ICSharpCode.Decompiler/IL/ILReader.cs
  5. 156
      ICSharpCode.Decompiler/IL/Instructions.cs
  6. 101
      ICSharpCode.Decompiler/IL/Instructions.tt
  7. 41
      ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs
  8. 325
      ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs
  9. 1
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  10. 9
      ICSharpCode.Decompiler/Tests/InitializerTests.cs
  11. 3
      ICSharpCode.Decompiler/TypeSystem/IDecompilerTypeSystem.cs
  12. 10
      ICSharpCode.Decompiler/TypeSystem/SpecializingDecompilerTypeSystem.cs

3
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -45,7 +45,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -45,7 +45,8 @@ namespace ICSharpCode.Decompiler.CSharp
new LoopDetection(),
new ControlFlowSimplification(),
new ILInlining(),
new TransformingVisitor()
new TransformingVisitor(),
new TransformArrayInitializers()
};
List<IAstTransform> astTransforms = new List<IAstTransform> {

17
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -135,15 +135,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -135,15 +135,12 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitNewArr(NewArr inst)
{
var arg = Translate(inst.Size);
return new ArrayCreateExpression {
Type = ConvertType(inst.Type),
Arguments = {
arg
}
}
.WithILInstruction(inst)
.WithRR(new ArrayCreateResolveResult(new ArrayType(compilation, inst.Type, 1), new [] { arg.ResolveResult }, new ResolveResult[0]));
var dimensions = inst.Indices.Count;
var args = inst.Indices.Select(arg => Translate(arg)).ToArray();
var expr = new ArrayCreateExpression { Type = ConvertType(inst.Type) };
expr.Arguments.AddRange(args.Select(arg => arg.Expression));
return expr.WithILInstruction(inst)
.WithRR(new ArrayCreateResolveResult(new ArrayType(compilation, inst.Type, dimensions), args.Select(a => a.ResolveResult).ToList(), new ResolveResult[0]));
}
protected internal override TranslatedExpression VisitLocAlloc(LocAlloc inst)
@ -696,7 +693,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -696,7 +693,7 @@ namespace ICSharpCode.Decompiler.CSharp
var arrayType = arrayExpr.Type as ArrayType;
// TODO: what if arrayExpr is not an array type?
// TODO: what if the type of the ldelema instruction does not match the array type?
TranslatedExpression expr = new IndexerExpression(arrayExpr, Translate(inst.Index))
TranslatedExpression expr = new IndexerExpression(arrayExpr, inst.Indices.Select(i => Translate(i).Expression))
.WithILInstruction(inst).WithRR(new ResolveResult(arrayType != null ? arrayType.ElementType : SpecialType.UnknownType));
return new DirectionExpression(FieldDirection.Ref, expr)
.WithoutILInstruction().WithRR(new ResolveResult(new ByReferenceType(expr.Type)));

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -111,6 +111,7 @@ @@ -111,6 +111,7 @@
<Compile Include="IL\Transforms\ILInlining.cs" />
<Compile Include="IL\Transforms\LoopDetection.cs" />
<Compile Include="IL\Transforms\OptimizingTransform.cs" />
<Compile Include="IL\Transforms\TransformArrayInitializers.cs" />
<Compile Include="IL\Transforms\TransformingVisitor.cs" />
<Compile Include="CecilExtensions.cs" />
<Compile Include="Disassembler\DisassemblerHelpers.cs" />

35
ICSharpCode.Decompiler/IL/ILReader.cs

@ -669,7 +669,7 @@ namespace ICSharpCode.Decompiler.IL @@ -669,7 +669,7 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Ldelem_Ref:
return LdElem(compilation.FindType(KnownTypeCode.Object));
case ILOpCode.Ldelema:
return Push(new LdElema(index: Pop(), array: Pop(), type: ReadAndDecodeTypeReference()));
return Push(new LdElema(indices: Pop(), array: Pop(), type: ReadAndDecodeTypeReference()));
case ILOpCode.Ldfld:
return Push(new LdFld(Pop(), ReadAndDecodeFieldReference()));
case ILOpCode.Ldflda:
@ -858,7 +858,7 @@ namespace ICSharpCode.Decompiler.IL @@ -858,7 +858,7 @@ namespace ICSharpCode.Decompiler.IL
private ILInstruction LdElem(IType type)
{
return Push(new LdObj(new LdElema(index: Pop(), array: Pop(), type: type), type));
return Push(new LdObj(new LdElema(indices: Pop(), array: Pop(), type: type), type));
}
private ILInstruction StElem(IType type)
@ -866,7 +866,7 @@ namespace ICSharpCode.Decompiler.IL @@ -866,7 +866,7 @@ namespace ICSharpCode.Decompiler.IL
var value = Pop();
var index = Pop();
var array = Pop();
return new StObj(new LdElema(array, index, type), value, type);
return new StObj(new LdElema(type, array, index), value, type);
}
ILInstruction InitObj(ILInstruction target, IType type)
@ -938,12 +938,31 @@ namespace ICSharpCode.Decompiler.IL @@ -938,12 +938,31 @@ namespace ICSharpCode.Decompiler.IL
for (int i = arguments.Length - 1; i >= 0; i--) {
arguments[i] = Pop();
}
var call = CallInstruction.Create(opCode, method);
call.Arguments.AddRange(arguments);
if (call.ResultType != StackType.Void)
return Push(call);
ILInstruction result;
if (method.DeclaringType.Kind == TypeKind.Array) {
var type = ((ICSharpCode.NRefactory.TypeSystem.ArrayType)method.DeclaringType).ElementType;
if (opCode == OpCode.NewObj) {
result = new NewArr(type, arguments);
} else if (method.Name == "Set") {
var target = arguments[0].Clone();
var value = arguments.Last().Clone();
var indices = arguments.Skip(1).Take(arguments.Length - 2).ToArray();
result = new StObj(new LdElema(type, target, indices), value, type);
} else if (method.Name == "Get") {
var target = arguments[0].Clone();
var indices = arguments.Skip(1).ToArray();
result = new LdObj(new LdElema(type, target, indices), type);
} else
throw new NotImplementedException();
} else {
var call = CallInstruction.Create(opCode, method);
call.Arguments.AddRange(arguments);
result = call;
}
if (result.ResultType != StackType.Void)
return Push(result);
else
return call;
return result;
}
static int GetPopCount(OpCode callCode, MethodReference methodReference)

156
ICSharpCode.Decompiler/IL/Instructions.cs

@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.Decompiler.IL
@ -366,6 +367,55 @@ namespace ICSharpCode.Decompiler.IL @@ -366,6 +367,55 @@ namespace ICSharpCode.Decompiler.IL
}
}
/// <summary>Instruction with a list of arguments.</summary>
public abstract partial class CallInstruction : ILInstruction
{
protected CallInstruction(OpCode opCode, params ILInstruction[] arguments) : base(opCode)
{
this.Arguments = new InstructionCollection<ILInstruction>(this, 0);
this.Arguments.AddRange(arguments);
}
public static readonly SlotInfo ArgumentsSlot = new SlotInfo("Arguments", canInlineInto: true);
public InstructionCollection<ILInstruction> Arguments { get; private set; }
protected sealed override int GetChildCount()
{
return Arguments.Count;
}
protected sealed override ILInstruction GetChild(int index)
{
switch (index) {
default:
return this.Arguments[index - 0];
}
}
protected sealed override void SetChild(int index, ILInstruction value)
{
switch (index) {
default:
this.Arguments[index - 0] = value;
break;
}
}
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
default:
return ArgumentsSlot;
}
}
public sealed override ILInstruction Clone()
{
var clone = (CallInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(this, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
return clone;
}
protected override InstructionFlags ComputeFlags()
{
return Arguments.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags) | InstructionFlags.MayThrow | InstructionFlags.SideEffect;
}
}
/// <summary>No operation. Takes 0 arguments and returns void.</summary>
public sealed partial class Nop : SimpleInstruction
{
@ -2508,65 +2558,54 @@ namespace ICSharpCode.Decompiler.IL @@ -2508,65 +2558,54 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>Creates an array instance.</summary>
public sealed partial class NewArr : ILInstruction
{
public NewArr(IType type, ILInstruction size) : base(OpCode.NewArr)
public NewArr(IType type, params ILInstruction[] indices) : base(OpCode.NewArr)
{
this.type = type;
this.Size = size;
this.Indices = new InstructionCollection<ILInstruction>(this, 0);
this.Indices.AddRange(indices);
}
readonly IType type;
/// <summary>Returns the type operand.</summary>
public IType Type { get { return type; } }
public static readonly SlotInfo SizeSlot = new SlotInfo("Size", canInlineInto: true);
ILInstruction size;
public ILInstruction Size {
get { return this.size; }
set {
ValidateChild(value);
SetChildInstruction(ref this.size, value, 0);
}
}
public static readonly SlotInfo IndicesSlot = new SlotInfo("Indices", canInlineInto: true);
public InstructionCollection<ILInstruction> Indices { get; private set; }
protected sealed override int GetChildCount()
{
return 1;
return Indices.Count;
}
protected sealed override ILInstruction GetChild(int index)
{
switch (index) {
case 0:
return this.size;
default:
throw new IndexOutOfRangeException();
return this.Indices[index - 0];
}
}
protected sealed override void SetChild(int index, ILInstruction value)
{
switch (index) {
case 0:
this.Size = value;
break;
default:
throw new IndexOutOfRangeException();
this.Indices[index - 0] = value;
break;
}
}
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return SizeSlot;
default:
throw new IndexOutOfRangeException();
return IndicesSlot;
}
}
public sealed override ILInstruction Clone()
{
var clone = (NewArr)ShallowClone();
clone.Size = this.size.Clone();
clone.Indices = new InstructionCollection<ILInstruction>(this, 0);
clone.Indices.AddRange(this.Indices.Select(arg => arg.Clone()));
return clone;
}
public override StackType ResultType { get { return StackType.O; } }
protected override InstructionFlags ComputeFlags()
{
return size.Flags | InstructionFlags.MayThrow;
return Indices.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags) | InstructionFlags.MayThrow;
}
public override void WriteTo(ITextOutput output)
{
@ -2574,7 +2613,11 @@ namespace ICSharpCode.Decompiler.IL @@ -2574,7 +2613,11 @@ namespace ICSharpCode.Decompiler.IL
output.Write(' ');
Disassembler.DisassemblerHelpers.WriteOperand(output, type);
output.Write('(');
this.size.WriteTo(output);
bool first = true;
foreach (var indices in Indices) {
if (!first) output.Write(", "); else first = false;
indices.WriteTo(output);
}
output.Write(')');
}
public override void AcceptVisitor(ILVisitor visitor)
@ -2762,12 +2805,16 @@ namespace ICSharpCode.Decompiler.IL @@ -2762,12 +2805,16 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>Load address of array element.</summary>
public sealed partial class LdElema : ILInstruction
{
public LdElema(ILInstruction array, ILInstruction index, IType type) : base(OpCode.LdElema)
public LdElema(IType type, ILInstruction array, params ILInstruction[] indices) : base(OpCode.LdElema)
{
this.Array = array;
this.Index = index;
this.type = type;
this.Array = array;
this.Indices = new InstructionCollection<ILInstruction>(this, 1);
this.Indices.AddRange(indices);
}
readonly IType type;
/// <summary>Returns the type operand.</summary>
public IType Type { get { return type; } }
public static readonly SlotInfo ArraySlot = new SlotInfo("Array", canInlineInto: true);
ILInstruction array;
public ILInstruction Array {
@ -2777,28 +2824,19 @@ namespace ICSharpCode.Decompiler.IL @@ -2777,28 +2824,19 @@ namespace ICSharpCode.Decompiler.IL
SetChildInstruction(ref this.array, value, 0);
}
}
public static readonly SlotInfo IndexSlot = new SlotInfo("Index", canInlineInto: true);
ILInstruction index;
public ILInstruction Index {
get { return this.index; }
set {
ValidateChild(value);
SetChildInstruction(ref this.index, value, 1);
}
}
public static readonly SlotInfo IndicesSlot = new SlotInfo("Indices", canInlineInto: true);
public InstructionCollection<ILInstruction> Indices { get; private set; }
protected sealed override int GetChildCount()
{
return 2;
return 1 + Indices.Count;
}
protected sealed override ILInstruction GetChild(int index)
{
switch (index) {
case 0:
return this.array;
case 1:
return this.index;
default:
throw new IndexOutOfRangeException();
return this.Indices[index - 1];
}
}
protected sealed override void SetChild(int index, ILInstruction value)
@ -2807,11 +2845,9 @@ namespace ICSharpCode.Decompiler.IL @@ -2807,11 +2845,9 @@ namespace ICSharpCode.Decompiler.IL
case 0:
this.Array = value;
break;
case 1:
this.Index = value;
break;
default:
throw new IndexOutOfRangeException();
this.Indices[index - 1] = value;
break;
}
}
protected sealed override SlotInfo GetChildSlot(int index)
@ -2819,28 +2855,24 @@ namespace ICSharpCode.Decompiler.IL @@ -2819,28 +2855,24 @@ namespace ICSharpCode.Decompiler.IL
switch (index) {
case 0:
return ArraySlot;
case 1:
return IndexSlot;
default:
throw new IndexOutOfRangeException();
return IndicesSlot;
}
}
public sealed override ILInstruction Clone()
{
var clone = (LdElema)ShallowClone();
clone.Array = this.array.Clone();
clone.Index = this.index.Clone();
clone.Indices = new InstructionCollection<ILInstruction>(this, 1);
clone.Indices.AddRange(this.Indices.Select(arg => arg.Clone()));
return clone;
}
readonly IType type;
/// <summary>Returns the type operand.</summary>
public IType Type { get { return type; } }
public override StackType ResultType { get { return StackType.Ref; } }
/// <summary>Gets whether the 'readonly' prefix was applied to this instruction.</summary>
public bool IsReadOnly { get; set; }
protected override InstructionFlags ComputeFlags()
{
return array.Flags | index.Flags | InstructionFlags.MayThrow;
return array.Flags | Indices.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags) | InstructionFlags.MayThrow;
}
public override void WriteTo(ITextOutput output)
{
@ -2851,8 +2883,10 @@ namespace ICSharpCode.Decompiler.IL @@ -2851,8 +2883,10 @@ namespace ICSharpCode.Decompiler.IL
Disassembler.DisassemblerHelpers.WriteOperand(output, type);
output.Write('(');
this.array.WriteTo(output);
output.Write(", ");
this.index.WriteTo(output);
foreach (var indices in Indices) {
output.Write(", ");
indices.WriteTo(output);
}
output.Write(')');
}
public override void AcceptVisitor(ILVisitor visitor)
@ -4140,16 +4174,14 @@ namespace ICSharpCode.Decompiler.IL @@ -4140,16 +4174,14 @@ namespace ICSharpCode.Decompiler.IL
type = default(IType);
return false;
}
public bool MatchNewArr(out IType type, out ILInstruction size)
public bool MatchNewArr(out IType type)
{
var inst = this as NewArr;
if (inst != null) {
type = inst.Type;
size = inst.Size;
return true;
}
type = default(IType);
size = default(ILInstruction);
return false;
}
public bool MatchDefaultValue(out IType type)
@ -4200,18 +4232,16 @@ namespace ICSharpCode.Decompiler.IL @@ -4200,18 +4232,16 @@ namespace ICSharpCode.Decompiler.IL
array = default(ILInstruction);
return false;
}
public bool MatchLdElema(out ILInstruction array, out ILInstruction index, out IType type)
public bool MatchLdElema(out IType type, out ILInstruction array)
{
var inst = this as LdElema;
if (inst != null) {
array = inst.Array;
index = inst.Index;
type = inst.Type;
array = inst.Array;
return true;
}
array = default(ILInstruction);
index = default(ILInstruction);
type = default(IType);
array = default(ILInstruction);
return false;
}
}

101
ICSharpCode.Decompiler/IL/Instructions.tt

@ -30,6 +30,9 @@ @@ -30,6 +30,9 @@
AbstractBaseClass, CustomArguments("argument")),
new OpCode("BinaryInstruction", "Instruction with two arguments: Left and Right",
AbstractBaseClass, CustomArguments("left", "right")),
new OpCode("CallInstruction", "Instruction with a list of arguments.",
AbstractBaseClass, CustomChildren(new []{ new ArgumentInfo("arguments") { IsCollection = true }}),
CustomWriteTo, MayThrow, SideEffect),
};
OpCode[] opCodes = {
@ -175,7 +178,7 @@ @@ -175,7 +178,7 @@
new OpCode("newobj", "Creates an object instance and calls the constructor.",
CustomClassName("NewObj"), Call, ResultType("Method.DeclaringType.GetStackType()")),
new OpCode("newarr", "Creates an array instance.",
CustomClassName("NewArr"), HasTypeOperand, CustomArguments("size"), MayThrow, ResultType("O")),
CustomClassName("NewArr"), HasTypeOperand, CustomChildren(new [] { new ArgumentInfo("indices") { IsCollection = true } }, true), MayThrow, ResultType("O")),
new OpCode("default.value", "Returns the default value for a type.",
NoArguments, HasTypeOperand, ResultType("type.GetStackType()")),
new OpCode("throw", "Throws an exception.",
@ -188,13 +191,14 @@ @@ -188,13 +191,14 @@
new OpCode("ldlen", "Returns the length of an array as 'native unsigned int'.",
CustomClassName("LdLen"), CustomArguments("array"), MayThrow, ResultType("I")),
new OpCode("ldelema", "Load address of array element.",
CustomClassName("LdElema"), CustomArguments("array", "index"), HasTypeOperand,
CustomClassName("LdElema"), HasTypeOperand, CustomChildren(new [] { new ArgumentInfo("array"), new ArgumentInfo("indices") { IsCollection = true } }, true),
MayThrow, ResultType("Ref"), SupportsReadonlyPrefix)
};
#>
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.Decompiler.IL
@ -571,6 +575,7 @@ namespace ICSharpCode.Decompiler.IL @@ -571,6 +575,7 @@ namespace ICSharpCode.Decompiler.IL
public readonly string Name;
public readonly string SlotName;
public bool IsCollection;
public bool CanInlineInto;
public ChildInfo(string name)
@ -605,32 +610,61 @@ namespace ICSharpCode.Decompiler.IL @@ -605,32 +610,61 @@ namespace ICSharpCode.Decompiler.IL
opCode.GenerateWriteTo = true;
opCode.WriteArguments.Add("output.Write('(');");
StringBuilder transformChildren = new StringBuilder();
ChildInfo collection = null;
int childCount = children.Length;
for (int i = 0; i < children.Length; i++) {
string arg = children[i].Name;
string argProp = children[i].PropertyName;
opCode.Flags.Add(arg + ".Flags");
opCode.ConstructorParameters.Add("ILInstruction " + arg);
opCode.ConstructorBody.Add("this." + argProp + " = " + arg + ";");
opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "ILInstruction", Name = arg, FieldName = argProp });
if (i > 0)
opCode.WriteArguments.Add("output.Write(\", \");");
opCode.WriteArguments.Add("this." + arg + ".WriteTo(output);");
opCode.Members.Add("public static readonly SlotInfo " + children[i].SlotName + " = " + children[i].GetSlotInit() + ";");
opCode.Members.Add("ILInstruction " + arg + ";");
opCode.Members.Add("public ILInstruction " + argProp + " {" + Environment.NewLine
+ "\tget { return this." + arg + "; }" + Environment.NewLine
+ "\tset {" + Environment.NewLine
+ "\t\tValidateChild(value);" + Environment.NewLine
+ "\t\tSetChildInstruction(ref this." + arg + ", value, " + i + ");" + Environment.NewLine
+ "\t}" + Environment.NewLine
+ "}");
if (children[i].IsCollection && i + 1 == children.Length) {
collection = children[i];
childCount = children.Length - 1;
opCode.Flags.Add(argProp + ".Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags)");
opCode.ConstructorParameters.Add("params ILInstruction[] " + arg);
opCode.ConstructorBody.Add("this." + argProp + " = new InstructionCollection<ILInstruction>(this, " + i + ");");
opCode.ConstructorBody.Add("this." + argProp + ".AddRange(" + arg + ");");
if (i == 0)
opCode.WriteArguments.Add("bool first = true;");
opCode.WriteArguments.Add("foreach (var " + arg + " in " + argProp + ") {");
if (i > 0)
opCode.WriteArguments.Add("\toutput.Write(\", \");");
else
opCode.WriteArguments.Add("\tif (!first) output.Write(\", \"); else first = false;");
opCode.WriteArguments.Add("\t" + arg + ".WriteTo(output);");
opCode.WriteArguments.Add("}");
opCode.Members.Add("public static readonly SlotInfo " + children[i].SlotName + " = " + children[i].GetSlotInit() + ";");
opCode.Members.Add("public InstructionCollection<ILInstruction> " + argProp + " { get; private set; }");
} else {
opCode.Flags.Add(arg + ".Flags");
opCode.ConstructorParameters.Add("ILInstruction " + arg);
opCode.ConstructorBody.Add("this." + argProp + " = " + arg + ";");
opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "ILInstruction", Name = arg, FieldName = argProp });
if (i > 0)
opCode.WriteArguments.Add("output.Write(\", \");");
opCode.WriteArguments.Add("this." + arg + ".WriteTo(output);");
opCode.Members.Add("public static readonly SlotInfo " + children[i].SlotName + " = " + children[i].GetSlotInit() + ";");
opCode.Members.Add("ILInstruction " + arg + ";");
opCode.Members.Add("public ILInstruction " + argProp + " {" + Environment.NewLine
+ "\tget { return this." + arg + "; }" + Environment.NewLine
+ "\tset {" + Environment.NewLine
+ "\t\tValidateChild(value);" + Environment.NewLine
+ "\t\tSetChildInstruction(ref this." + arg + ", value, " + i + ");" + Environment.NewLine
+ "\t}" + Environment.NewLine
+ "}");
}
}
opCode.WriteArguments.Add("output.Write(')');");
StringBuilder b;
b = new StringBuilder();
b.AppendLine("protected sealed override int GetChildCount()");
b.AppendLine("{");
b.AppendLine("\treturn " + children.Length + ";");
b.Append("\treturn ");
if (childCount > 0 || collection == null)
b.Append(childCount);
if (collection != null) {
if (childCount > 0) b.Append(" + ");
b.Append(collection.PropertyName + ".Count");
}
b.AppendLine(";");
b.Append("}");
opCode.Members.Add(b.ToString());
@ -638,12 +672,15 @@ namespace ICSharpCode.Decompiler.IL @@ -638,12 +672,15 @@ namespace ICSharpCode.Decompiler.IL
b.AppendLine("protected sealed override ILInstruction GetChild(int index)");
b.AppendLine("{");
b.AppendLine("\tswitch (index) {");
for (int i = 0; i < children.Length; i++) {
for (int i = 0; i < childCount; i++) {
b.AppendLine("\t\tcase " + i + ":");
b.AppendLine("\t\t\treturn this." + children[i].Name + ";");
}
b.AppendLine("\t\tdefault:");
b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();");
if (collection == null)
b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();");
else
b.AppendLine("\t\t\treturn this." + collection.PropertyName + "[index - " + childCount + "];");
b.AppendLine("\t}");
b.Append("}");
opCode.Members.Add(b.ToString());
@ -652,13 +689,18 @@ namespace ICSharpCode.Decompiler.IL @@ -652,13 +689,18 @@ namespace ICSharpCode.Decompiler.IL
b.AppendLine("protected sealed override void SetChild(int index, ILInstruction value)");
b.AppendLine("{");
b.AppendLine("\tswitch (index) {");
for (int i = 0; i < children.Length; i++) {
for (int i = 0; i < childCount; i++) {
b.AppendLine("\t\tcase " + i + ":");
b.AppendLine("\t\t\tthis." + children[i].PropertyName + " = value;");
b.AppendLine("\t\t\tbreak;");
}
b.AppendLine("\t\tdefault:");
b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();");
if (collection == null)
b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();");
else {
b.AppendLine("\t\t\tthis." + collection.PropertyName + "[index - " + childCount + "] = value;");
b.AppendLine("\t\t\tbreak;");
}
b.AppendLine("\t}");
b.Append("}");
opCode.Members.Add(b.ToString());
@ -667,12 +709,15 @@ namespace ICSharpCode.Decompiler.IL @@ -667,12 +709,15 @@ namespace ICSharpCode.Decompiler.IL
b.AppendLine("protected sealed override SlotInfo GetChildSlot(int index)");
b.AppendLine("{");
b.AppendLine("\tswitch (index) {");
for (int i = 0; i < children.Length; i++) {
for (int i = 0; i < childCount; i++) {
b.AppendLine("\t\tcase " + i + ":");
b.AppendLine("\t\t\treturn " + children[i].SlotName + ";");
}
b.AppendLine("\t\tdefault:");
b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();");
if (collection == null)
b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();");
else
b.AppendLine("\t\t\treturn " + collection.SlotName + ";");
b.AppendLine("\t}");
b.Append("}");
opCode.Members.Add(b.ToString());
@ -682,7 +727,11 @@ namespace ICSharpCode.Decompiler.IL @@ -682,7 +727,11 @@ namespace ICSharpCode.Decompiler.IL
b.AppendLine("{");
b.AppendLine("\tvar clone = (" + opCode.Name + ")ShallowClone();");
for (int i = 0; i < children.Length; i++) {
b.AppendLine("\tclone." + children[i].PropertyName + " = this." + children[i].Name + ".Clone();");
if (children[i].IsCollection) {
b.AppendLine("\tclone." + children[i].PropertyName + " = new InstructionCollection<ILInstruction>(this, " + i + ");");
b.AppendLine("\tclone." + children[i].PropertyName + ".AddRange(this." + children[i].PropertyName + ".Select(arg => arg.Clone()));");
} else
b.AppendLine("\tclone." + children[i].PropertyName + " = this." + children[i].Name + ".Clone();");
}
b.AppendLine("\treturn clone;");
b.Append("}");

41
ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs

@ -27,7 +27,7 @@ using ICSharpCode.Decompiler.Disassembler; @@ -27,7 +27,7 @@ using ICSharpCode.Decompiler.Disassembler;
namespace ICSharpCode.Decompiler.IL
{
public abstract class CallInstruction : ILInstruction
public abstract partial class CallInstruction : ILInstruction
{
public static readonly SlotInfo ArgumentSlot = new SlotInfo("Argument", canInlineInto: true, isCollection: true);
@ -45,7 +45,6 @@ namespace ICSharpCode.Decompiler.IL @@ -45,7 +45,6 @@ namespace ICSharpCode.Decompiler.IL
}
}
public readonly InstructionCollection<ILInstruction> Arguments;
public readonly IMethod Method;
/// <summary>
@ -66,16 +65,6 @@ namespace ICSharpCode.Decompiler.IL @@ -66,16 +65,6 @@ namespace ICSharpCode.Decompiler.IL
this.Arguments = new InstructionCollection<ILInstruction>(this, 0);
}
public sealed override ILInstruction Clone()
{
var clone = Create(this.OpCode, this.Method);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
clone.ILRange = this.ILRange;
clone.IsTail = this.IsTail;
clone.ConstrainedTo = this.ConstrainedTo;
return clone;
}
public override StackType ResultType {
get {
if (OpCode == OpCode.NewObj)
@ -85,34 +74,6 @@ namespace ICSharpCode.Decompiler.IL @@ -85,34 +74,6 @@ namespace ICSharpCode.Decompiler.IL
}
}
protected sealed override int GetChildCount()
{
return Arguments.Count;
}
protected sealed override ILInstruction GetChild(int index)
{
return Arguments[index];
}
protected sealed override void SetChild(int index, ILInstruction value)
{
Arguments[index] = value;
}
protected override SlotInfo GetChildSlot(int index)
{
return ArgumentSlot;
}
protected override InstructionFlags ComputeFlags()
{
var flags = InstructionFlags.MayThrow | InstructionFlags.SideEffect;
foreach (var op in Arguments)
flags |= op.Flags;
return flags;
}
public override void WriteTo(ITextOutput output)
{
if (ConstrainedTo != null) {

325
ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs

@ -0,0 +1,325 @@ @@ -0,0 +1,325 @@
// Copyright (c) 2015 Siegfried Pammer
//
// 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.
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
namespace ICSharpCode.Decompiler.IL
{
public class TransformArrayInitializers : IILTransform
{
ILTransformContext context;
void IILTransform.Run(ILFunction function, ILTransformContext context)
{
this.context = context;
foreach (var block in function.Descendants.OfType<Block>()) {
for (int i = block.Instructions.Count - 1; i >= 0; i--) {
try {
if (!DoTransform(block, i))
DoTransformMultiDim(block, i);
} catch (Exception ex) {
Console.WriteLine(ex);
throw;
}
}
}
}
bool DoTransform(Block body, int pos)
{
if (pos >= body.Instructions.Count - 2)
return false;
ILInstruction inst = body.Instructions[pos];
ILVariable v;
ILInstruction newarrExpr;
IType elementType;
int[] arrayLength;
if (inst.MatchStLoc(out v, out newarrExpr) && MatchNewArr(newarrExpr, out elementType, out arrayLength)) {
ILInstruction[] values;
int initArrayPos;
if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, elementType, arrayLength, out values, out initArrayPos)) {
var block = BlockFromInitializer(v, elementType, arrayLength, values);
body.Instructions[pos].ReplaceWith(new StLoc(v, block));
body.Instructions.RemoveAt(initArrayPos);
new ILInlining().InlineIfPossible(body, ref pos);
return true;
}
// Put in a limit so that we don't consume too much memory if the code allocates a huge array
// and populates it extremely sparsly. However, 255 "null" elements in a row actually occur in the Mono C# compiler!
// const int maxConsecutiveDefaultValueExpressions = 300;
// var operands = new List<ILInstruction>();
// int numberOfInstructionsToRemove = 0;
// for (int j = pos + 1; j < body.Instructions.Count; j++) {
// var nextExpr = body.Instructions[j] as Void;
// int arrayPos;
// if (nextExpr != null && nextExpr is a.IsStoreToArray() &&
// nextExpr.Arguments[0].Match(ILCode.Ldloc, out v3) &&
// v == v3 &&
// nextExpr.Arguments[1].Match(ILCode.Ldc_I4, out arrayPos) &&
// arrayPos >= operands.Count &&
// arrayPos <= operands.Count + maxConsecutiveDefaultValueExpressions &&
// !nextExpr.Arguments[2].ContainsReferenceTo(v3))
// {
// while (operands.Count < arrayPos)
// operands.Add(new ILExpression(ILCode.DefaultValue, elementType));
// operands.Add(nextExpr.Arguments[2]);
// numberOfInstructionsToRemove++;
// } else {
// break;
// }
// }
}
return false;
}
bool DoTransformMultiDim(Block body, int pos)
{
if (pos >= body.Instructions.Count - 2)
return false;
ILVariable v;
ILInstruction newarrExpr;
IType arrayType;
int[] length;
ILInstruction instr = body.Instructions[pos];
if (instr.MatchStLoc(out v, out newarrExpr) && MatchNewArr(newarrExpr, out arrayType, out length)) {
ILInstruction[] values;
int initArrayPos;
if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, arrayType, length, out values, out initArrayPos)) {
var block = BlockFromInitializer(v, arrayType, length, values);
body.Instructions[pos].ReplaceWith(new StLoc(v, block));
body.Instructions.RemoveAt(initArrayPos);
new ILInlining().InlineIfPossible(body, ref pos);
return true;
}
}
return false;
}
Block BlockFromInitializer(ILVariable v, IType elementType, int[] arrayLength, ILInstruction[] values)
{
var block = new Block();
block.Instructions.Add(new StLoc(v, new NewArr(elementType, arrayLength.Select(l => new LdcI4(l)).ToArray())));
int step = arrayLength.Length + 1;
for (int i = 0; i < values.Length / step; i++) {
// values array is filled backwards
var value = values[step * i];
var indices = new List<ILInstruction>();
for (int j = step - 1; j >= 1; j--) {
indices.Add(values[step * i + j]);
}
block.Instructions.Add(StElem(new LdLoc(v), indices.ToArray(), value, elementType));
}
block.FinalInstruction = new LdLoc(v);
return block;
}
static bool CompareTypes(IType a, IType b)
{
IType type1 = DummyTypeParameter.NormalizeAllTypeParameters(a);
IType type2 = DummyTypeParameter.NormalizeAllTypeParameters(b);
return type1.Equals(type2);
}
static bool CompareSignatures(IList<IParameter> parameters, IList<IParameter> otherParameters)
{
if (otherParameters.Count != parameters.Count)
return false;
for (int i = 0; i < otherParameters.Count; i++) {
if (!CompareTypes(otherParameters[i].Type, parameters[i].Type))
return false;
}
return true;
}
bool MatchNewArr(ILInstruction instruction, out IType arrayType, out int[] length)
{
NewArr newArr = instruction as NewArr;
length = null;
arrayType = null;
if (newArr == null)
return false;
arrayType = newArr.Type;
var args = newArr.Indices;
length = new int[args.Count];
for (int i = 0; i < args.Count; i++) {
int value;
if (!args[i].MatchLdcI4(out value) || value <= 0) return false;
length[i] = value;
}
return true;
}
bool MatchInitializeArrayCall(ILInstruction instruction, out IMethod method, out ILVariable array, out Mono.Cecil.FieldReference field)
{
method = null;
array = null;
field = null;
Call call = instruction as Call;
if (call == null || call.Arguments.Count != 2)
return false;
method = call.Method;
if (method.DeclaringTypeDefinition == null || method.DeclaringTypeDefinition.FullName != "System.Runtime.CompilerServices.RuntimeHelpers")
return false;
if (method.Name != "InitializeArray")
return false;
if (!call.Arguments[0].MatchLdLoc(out array))
return false;
IMember member;
if (!call.Arguments[1].MatchLdMemberToken(out member))
return false;
field = context.TypeSystem.GetCecil(member) as Mono.Cecil.FieldReference;
if (field == null)
return false;
return true;
}
bool ForwardScanInitializeArrayRuntimeHelper(Block body, int pos, ILVariable array, IType arrayType, int[] arrayLength, out ILInstruction[] values, out int foundPos)
{
ILVariable v2;
IMethod method;
Mono.Cecil.FieldReference field;
if (MatchInitializeArrayCall(body.Instructions[pos], out method, out v2, out field) && array == v2) {
var fieldDef = field.ResolveWithinSameModule();
if (fieldDef != null && fieldDef.InitialValue != null) {
var valuesList = new List<ILInstruction>();
if (DecodeArrayInitializer(arrayType, array, fieldDef.InitialValue, arrayLength, valuesList)) {
values = valuesList.ToArray();
foundPos = pos;
return true;
}
}
}
values = null;
foundPos = -1;
return false;
}
static bool DecodeArrayInitializer(IType type, ILVariable array, byte[] initialValue, int[] arrayLength, List<ILInstruction> output)
{
TypeCode typeCode = ReflectionHelper.GetTypeCode(type);
switch (typeCode) {
case TypeCode.Boolean:
case TypeCode.Byte:
return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (d, i) => (int)d[i]);
case TypeCode.SByte:
return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (d, i) => (int)unchecked((sbyte)d[i]));
case TypeCode.Int16:
return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (d, i) => (int)BitConverter.ToInt16(d, i));
case TypeCode.Char:
case TypeCode.UInt16:
return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (d, i) => (int)BitConverter.ToUInt16(d, i));
case TypeCode.Int32:
case TypeCode.UInt32:
return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, BitConverter.ToInt32);
case TypeCode.Int64:
case TypeCode.UInt64:
return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, BitConverter.ToInt64);
case TypeCode.Single:
return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, BitConverter.ToSingle);
case TypeCode.Double:
return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, BitConverter.ToDouble);
case TypeCode.Object:
var typeDef = type.GetDefinition();
if (typeDef != null && typeDef.Kind == TypeKind.Enum)
return DecodeArrayInitializer(typeDef.EnumUnderlyingType, array, initialValue, arrayLength, output);
return false;
default:
return false;
}
}
static bool DecodeArrayInitializer<T>(byte[] initialValue, ILVariable array, int[] arrayLength, List<ILInstruction> output, TypeCode elementType, IType type, Func<byte[], int, T> decoder)
{
int elementSize = ElementSizeOf(elementType);
var totalLength = arrayLength.Aggregate(1, (t, l) => t * l);
if (initialValue.Length < (totalLength * elementSize))
return false;
for (int i = 0; i < totalLength; i++) {
object value = (object)decoder(initialValue, i * elementSize);
if (!0.Equals(value)) {
output.Add(LoadInstructionFor(elementType, value));
int next = i;
for (int j = arrayLength.Length - 1; j >= 0; j--) {
output.Add(new LdcI4(next % arrayLength[j]));
next = next / arrayLength[j];
}
}
}
return true;
}
static ILInstruction LoadInstructionFor(TypeCode elementType, object value)
{
switch (elementType) {
case TypeCode.Boolean:
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.Char:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
return new LdcI4((int)value);
case TypeCode.Int64:
case TypeCode.UInt64:
return new LdcI8((long)value);
case TypeCode.Single:
case TypeCode.Double:
return new LdcF((double)value);
default:
throw new ArgumentOutOfRangeException("elementType");
}
}
static ILInstruction StElem(ILInstruction array, ILInstruction[] indices, ILInstruction value, IType type)
{
return new StObj(new LdElema(type, array, indices), value, type);
}
static int ElementSizeOf(TypeCode elementType)
{
switch (elementType) {
case TypeCode.Boolean:
case TypeCode.Byte:
case TypeCode.SByte:
return 1;
case TypeCode.Char:
case TypeCode.Int16:
case TypeCode.UInt16:
return 2;
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Single:
return 4;
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Double:
return 8;
default:
throw new ArgumentOutOfRangeException("elementType");
}
}
}
}

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

@ -101,6 +101,7 @@ @@ -101,6 +101,7 @@
<Compile Include="Helpers\Tester.cs" />
<Compile Include="Helpers\TypeSystemHelper.cs" />
<Compile Include="ILTransforms\InliningTests.cs" />
<Compile Include="InitializerTests.cs" />
<Compile Include="Loops.cs" />
<Compile Include="TestCases\CompoundAssignment.cs" />
<Compile Include="TestCases\ControlFlow.cs" />

9
ICSharpCode.Decompiler/Tests/InitializerTests.cs

@ -21,6 +21,15 @@ using System.Collections.Generic; @@ -21,6 +21,15 @@ using System.Collections.Generic;
public class InitializerTests
{
public static int Main()
{
int[,] test = new int[2,3];
test[0,0] = 0;
test[0,1] = 1;
test[0,2] = 2;
return test.Length + test[0, 0] + test[0, 2];
}
private enum MyEnum
{
a,

3
ICSharpCode.Decompiler/TypeSystem/IDecompilerTypeSystem.cs

@ -28,6 +28,9 @@ namespace ICSharpCode.Decompiler @@ -28,6 +28,9 @@ namespace ICSharpCode.Decompiler
{
ICompilation Compilation { get; }
TypeDefinition GetCecil(ITypeDefinition typeDefinition);
MemberReference GetCecil(IMember member);
IType Resolve(TypeReference typeReference);
IField Resolve(FieldReference fieldReference);
IMethod Resolve(MethodReference methodReference);

10
ICSharpCode.Decompiler/TypeSystem/SpecializingDecompilerTypeSystem.cs

@ -63,5 +63,15 @@ namespace ICSharpCode.Decompiler @@ -63,5 +63,15 @@ namespace ICSharpCode.Decompiler
method = (IMethod)method.Specialize(substitution);
return method;
}
public Mono.Cecil.TypeDefinition GetCecil(ITypeDefinition typeDefinition)
{
return context.GetCecil(typeDefinition);
}
public Mono.Cecil.MemberReference GetCecil(IMember member)
{
return context.GetCecil(member);
}
}
}

Loading…
Cancel
Save