// 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 { /// /// IL AST transformation that introduces array, object and collection initializers. /// public class Initializers { public static bool TransformArrayInitializers(List body, ILExpression expr, int pos) { ILVariable v, v2, v3; ILExpression newarrExpr; TypeReference arrayType; ILExpression lengthExpr; int arrayLength; if (expr.Match(ILCode.Stloc, out v, out newarrExpr) && newarrExpr.Match(ILCode.Newarr, out arrayType, out lengthExpr) && lengthExpr.Match(ILCode.Ldc_I4, out arrayLength) && arrayLength > 0) { MethodReference methodRef; ILExpression methodArg1; ILExpression methodArg2; FieldDefinition field; if (body.ElementAtOrDefault(pos + 1).Match(ILCode.Call, out methodRef, out methodArg1, out methodArg2) && methodRef.DeclaringType.FullName == "System.Runtime.CompilerServices.RuntimeHelpers" && methodRef.Name == "InitializeArray" && methodArg1.Match(ILCode.Ldloc, out v2) && v == v2 && methodArg2.Match(ILCode.Ldtoken, out field) && field != null && field.InitialValue != null) { ILExpression[] newArr = new ILExpression[arrayLength]; if (DecodeArrayInitializer(TypeAnalysis.GetTypeCode(arrayType), field.InitialValue, newArr)) { body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr)); body.RemoveAt(pos + 1); return true; } } const int maxConsecutiveDefaultValueExpressions = 10; List operands = new List(); int numberOfInstructionsToRemove = 0; for (int j = pos + 1; j < body.Count; j++) { ILExpression nextExpr = body[j] as ILExpression; int arrayPos; if (nextExpr != null && nextExpr.Code.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) { while (operands.Count < arrayPos) operands.Add(new ILExpression(ILCode.DefaultValue, arrayType)); operands.Add(nextExpr.Arguments[2]); numberOfInstructionsToRemove++; } else { break; } } if (operands.Count == arrayLength) { expr.Arguments[0] = new ILExpression(ILCode.InitArray, arrayType, operands); body.RemoveRange(pos + 1, numberOfInstructionsToRemove); 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; } } public static bool TransformCollectionInitializers(List body, ILExpression expr, int pos) { ILVariable v, v2; ILExpression newObjExpr; MethodReference ctor; List ctorArgs; if (expr.Match(ILCode.Stloc, out v, out newObjExpr) && newObjExpr.Match(ILCode.Newobj, out ctor, out ctorArgs)) { TypeDefinition td = ctor.DeclaringType.Resolve(); if (td == null || !td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections")) return false; // This is a collection: we can convert Add() calls into a collection initializer ILExpression collectionInitializer = new ILExpression(ILCode.InitCollection, null, newObjExpr); bool anyAdded = false; while(pos + 1 < body.Count) { ILExpression nextExpr = body[pos + 1] as ILExpression; MethodReference addMethod; List args; if (nextExpr.Match(ILCode.Callvirt, out addMethod, out args) && addMethod.Name == "Add" && addMethod.HasThis && args.Count >= 2 && args[0].Match(ILCode.Ldloc, out v2) && v == v2) { nextExpr.Code = ILCode.InitCollectionAddMethod; nextExpr.Arguments.RemoveAt(0); collectionInitializer.Arguments.Add(nextExpr); body.RemoveAt(pos + 1); anyAdded = true; } else { break; } } // ensure we added at least one additional arg to the collection initializer: if (anyAdded) { expr.Arguments[0] = collectionInitializer; return true; } } return false; } } }