summaryrefslogtreecommitdiff
path: root/ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs
diff options
context:
space:
mode:
Diffstat (limited to 'ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs')
-rw-r--r--ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs535
1 files changed, 0 insertions, 535 deletions
diff --git a/ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs b/ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs
deleted file mode 100644
index 4fb248de..00000000
--- a/ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs
+++ /dev/null
@@ -1,535 +0,0 @@
-// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
-//
-// 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.Diagnostics;
-using System.Linq;
-using Mono.Cecil;
-
-namespace ICSharpCode.Decompiler.ILAst
-{
- /// <summary>
- /// IL AST transformation that introduces array, object and collection initializers.
- /// </summary>
- partial class ILAstOptimizer
- {
- #region Array Initializers
- bool TransformArrayInitializers(List<ILNode> body, ILExpression expr, int pos)
- {
- ILVariable v, v3;
- ILExpression newarrExpr;
- TypeReference elementType;
- ILExpression lengthExpr;
- int arrayLength;
- if (expr.Match(ILCode.Stloc, out v, out newarrExpr) &&
- newarrExpr.Match(ILCode.Newarr, out elementType, out lengthExpr) &&
- lengthExpr.Match(ILCode.Ldc_I4, out arrayLength) &&
- arrayLength > 0) {
- ILExpression[] newArr;
- int initArrayPos;
- if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, elementType, arrayLength, out newArr, out initArrayPos)) {
- var arrayType = new ArrayType(elementType, 1);
- arrayType.Dimensions[0] = new ArrayDimension(0, arrayLength);
- body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr));
- body.RemoveAt(initArrayPos);
- }
- // 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;
- List<ILExpression> operands = new List<ILExpression>();
- 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 &&
- !nextExpr.Arguments[2].ContainsReferenceTo(v3))
- {
- while (operands.Count < arrayPos)
- operands.Add(new ILExpression(ILCode.DefaultValue, elementType));
- operands.Add(nextExpr.Arguments[2]);
- numberOfInstructionsToRemove++;
- } else {
- break;
- }
- }
- if (operands.Count == arrayLength) {
- var arrayType = new ArrayType(elementType, 1);
- arrayType.Dimensions[0] = new ArrayDimension(0, arrayLength);
- expr.Arguments[0] = new ILExpression(ILCode.InitArray, arrayType, operands);
- body.RemoveRange(pos + 1, numberOfInstructionsToRemove);
-
- new ILInlining(method).InlineIfPossible(body, ref pos);
- return true;
- }
- }
- return false;
- }
-
- bool TransformMultidimensionalArrayInitializers(List<ILNode> body, ILExpression expr, int pos)
- {
- ILVariable v;
- ILExpression newarrExpr;
- MethodReference ctor;
- List<ILExpression> ctorArgs;
- ArrayType arrayType;
- if (expr.Match(ILCode.Stloc, out v, out newarrExpr) &&
- newarrExpr.Match(ILCode.Newobj, out ctor, out ctorArgs) &&
- (arrayType = (ctor.DeclaringType as ArrayType)) != null &&
- arrayType.Rank == ctorArgs.Count) {
- // Clone the type, so we can muck about with the Dimensions
- arrayType = new ArrayType(arrayType.ElementType, arrayType.Rank);
- var arrayLengths = new int[arrayType.Rank];
- for (int i = 0; i < arrayType.Rank; i++) {
- if (!ctorArgs[i].Match(ILCode.Ldc_I4, out arrayLengths[i])) return false;
- if (arrayLengths[i] <= 0) return false;
- arrayType.Dimensions[i] = new ArrayDimension(0, arrayLengths[i]);
- }
-
- var totalElements = arrayLengths.Aggregate(1, (t, l) => t * l);
- ILExpression[] newArr;
- int initArrayPos;
- if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, arrayType, totalElements, out newArr, out initArrayPos)) {
- body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr));
- body.RemoveAt(initArrayPos);
- return true;
- }
- }
- return false;
- }
-
- bool ForwardScanInitializeArrayRuntimeHelper(List<ILNode> body, int pos, ILVariable array, TypeReference arrayType, int arrayLength, out ILExpression[] values, out int foundPos)
- {
- ILVariable v2;
- MethodReference methodRef;
- ILExpression methodArg1;
- ILExpression methodArg2;
- FieldReference fieldRef;
- if (body.ElementAtOrDefault(pos).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) &&
- array == v2 &&
- methodArg2.Match(ILCode.Ldtoken, out fieldRef))
- {
- FieldDefinition fieldDef = fieldRef.ResolveWithinSameModule();
- if (fieldDef != null && fieldDef.InitialValue != null) {
- ILExpression[] newArr = new ILExpression[arrayLength];
- if (DecodeArrayInitializer(arrayType.GetElementType(), fieldDef.InitialValue, newArr))
- {
- values = newArr;
- foundPos = pos;
- return true;
- }
- }
- }
- values = null;
- foundPos = -1;
- return false;
- }
-
- static bool DecodeArrayInitializer(TypeReference elementTypeRef, byte[] initialValue, ILExpression[] output)
- {
- TypeCode elementType = TypeAnalysis.GetTypeCode(elementTypeRef);
- switch (elementType) {
- case TypeCode.Boolean:
- case TypeCode.Byte:
- return DecodeArrayInitializer(initialValue, output, elementType, (d, i) => (int)d[i]);
- case TypeCode.SByte:
- return DecodeArrayInitializer(initialValue, output, elementType, (d, i) => (int)unchecked((sbyte)d[i]));
- case TypeCode.Int16:
- return DecodeArrayInitializer(initialValue, output, elementType, (d, i) => (int)BitConverter.ToInt16(d, i));
- case TypeCode.Char:
- case TypeCode.UInt16:
- return DecodeArrayInitializer(initialValue, output, elementType, (d, i) => (int)BitConverter.ToUInt16(d, i));
- case TypeCode.Int32:
- case TypeCode.UInt32:
- return DecodeArrayInitializer(initialValue, output, elementType, BitConverter.ToInt32);
- case TypeCode.Int64:
- case TypeCode.UInt64:
- return DecodeArrayInitializer(initialValue, output, elementType, BitConverter.ToInt64);
- case TypeCode.Single:
- return DecodeArrayInitializer(initialValue, output, elementType, BitConverter.ToSingle);
- case TypeCode.Double:
- return DecodeArrayInitializer(initialValue, output, elementType, BitConverter.ToDouble);
- case TypeCode.Object:
- var typeDef = elementTypeRef.ResolveWithinSameModule();
- if (typeDef != null && typeDef.IsEnum)
- return DecodeArrayInitializer(typeDef.GetEnumUnderlyingType(), initialValue, output);
-
- return false;
- default:
- return false;
- }
- }
-
- static bool DecodeArrayInitializer<T>(byte[] initialValue, ILExpression[] output, TypeCode elementType, Func<byte[], int, T> decoder)
- {
- int elementSize = ElementSizeOf(elementType);
- if (initialValue.Length < (output.Length * elementSize))
- return false;
-
- ILCode code = LoadCodeFor(elementType);
- for (int i = 0; i < output.Length; i++)
- output[i] = new ILExpression(code, decoder(initialValue, i * elementSize));
-
- return true;
- }
-
- static ILCode LoadCodeFor(TypeCode elementType)
- {
- 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 ILCode.Ldc_I4;
- case TypeCode.Int64:
- case TypeCode.UInt64:
- return ILCode.Ldc_I8;
- case TypeCode.Single:
- return ILCode.Ldc_R4;
- case TypeCode.Double:
- return ILCode.Ldc_R8;
- default:
- throw new ArgumentOutOfRangeException("elementType");
- }
- }
-
- 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");
- }
- }
- #endregion
-
- /// <summary>
- /// Handles both object and collection initializers.
- /// </summary>
- bool TransformObjectInitializers(List<ILNode> body, ILExpression expr, int pos)
- {
- if (!context.Settings.ObjectOrCollectionInitializers)
- return false;
-
- Debug.Assert(body[pos] == expr); // should be called for top-level expressions only
- ILVariable v;
- ILExpression newObjExpr;
- TypeReference newObjType;
- bool isValueType;
- MethodReference ctor;
- List<ILExpression> ctorArgs;
- if (expr.Match(ILCode.Stloc, out v, out newObjExpr)) {
- if (newObjExpr.Match(ILCode.Newobj, out ctor, out ctorArgs)) {
- // v = newObj(ctor, ctorArgs)
- newObjType = ctor.DeclaringType;
- isValueType = false;
- } else if (newObjExpr.Match(ILCode.DefaultValue, out newObjType)) {
- // v = defaultvalue(type)
- isValueType = true;
- } else {
- return false;
- }
- } else if (expr.Match(ILCode.Call, out ctor, out ctorArgs)) {
- // call(SomeStruct::.ctor, ldloca(v), remainingArgs)
- if (ctorArgs.Count > 0 && ctorArgs[0].Match(ILCode.Ldloca, out v)) {
- isValueType = true;
- newObjType = ctor.DeclaringType;
- ctorArgs = new List<ILExpression>(ctorArgs);
- ctorArgs.RemoveAt(0);
- newObjExpr = new ILExpression(ILCode.Newobj, ctor, ctorArgs);
- } else {
- return false;
- }
- } else {
- return false;
- }
- if (newObjType.IsValueType != isValueType)
- return false;
-
- int originalPos = pos;
-
- // don't use object initializer syntax for closures
- if (Ast.Transforms.DelegateConstruction.IsPotentialClosure(context, newObjType.ResolveWithinSameModule()))
- return false;
-
- ILExpression initializer = ParseObjectInitializer(body, ref pos, v, newObjExpr, IsCollectionType(newObjType), isValueType);
-
- if (initializer.Arguments.Count == 1) // only newobj argument, no initializer elements
- return false;
- int totalElementCount = pos - originalPos - 1; // totalElementCount: includes elements from nested collections
- Debug.Assert(totalElementCount >= initializer.Arguments.Count - 1);
-
- // Verify that we can inline 'v' into the next instruction:
-
- if (pos >= body.Count)
- return false; // reached end of block, but there should be another instruction which consumes the initialized object
-
- ILInlining inlining = new ILInlining(method);
- if (isValueType) {
- // one ldloc for the use of the initialized object
- if (inlining.numLdloc.GetOrDefault(v) != 1)
- return false;
- // one ldloca for each initializer argument, and also for the ctor call (if it exists)
- if (inlining.numLdloca.GetOrDefault(v) != totalElementCount + (expr.Code == ILCode.Call ? 1 : 0))
- return false;
- // one stloc for the initial store (if no ctor call was used)
- if (inlining.numStloc.GetOrDefault(v) != (expr.Code == ILCode.Call ? 0 : 1))
- return false;
- } else {
- // one ldloc for each initializer argument, and another ldloc for the use of the initialized object
- if (inlining.numLdloc.GetOrDefault(v) != totalElementCount + 1)
- return false;
- if (!(inlining.numStloc.GetOrDefault(v) == 1 && inlining.numLdloca.GetOrDefault(v) == 0))
- return false;
- }
- ILExpression nextExpr = body[pos] as ILExpression;
- if (!inlining.CanInlineInto(nextExpr, v, initializer))
- return false;
-
- if (expr.Code == ILCode.Stloc) {
- expr.Arguments[0] = initializer;
- } else {
- Debug.Assert(expr.Code == ILCode.Call);
- expr.Code = ILCode.Stloc;
- expr.Operand = v;
- expr.Arguments.Clear();
- expr.Arguments.Add(initializer);
- }
- // remove all the instructions that were pulled into the initializer
- body.RemoveRange(originalPos + 1, pos - originalPos - 1);
-
- // now that we know that it's an object initializer, change all the first arguments to 'InitializedObject'
- ChangeFirstArgumentToInitializedObject(initializer);
-
- inlining = new ILInlining(method);
- inlining.InlineIfPossible(body, ref originalPos);
-
- return true;
- }
-
- /// <summary>
- /// Gets whether the type supports collection initializers.
- /// </summary>
- static bool IsCollectionType(TypeReference tr)
- {
- if (tr == null)
- return false;
- TypeDefinition td = tr.Resolve();
- while (td != null) {
- if (td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections"))
- return true;
- td = td.BaseType != null ? td.BaseType.Resolve() : null;
- }
- return false;
- }
-
- /// <summary>
- /// Gets whether 'expr' represents a setter in an object initializer.
- /// ('CallvirtSetter(Property, v, value)')
- /// </summary>
- static bool IsSetterInObjectInitializer(ILExpression expr)
- {
- if (expr == null)
- return false;
- if (expr.Code == ILCode.CallvirtSetter || expr.Code == ILCode.CallSetter || expr.Code == ILCode.Stfld) {
- return expr.Arguments.Count == 2;
- }
- return false;
- }
-
- /// <summary>
- /// Gets whether 'expr' represents the invocation of an 'Add' method in a collection initializer.
- /// </summary>
- static bool IsAddMethodCall(ILExpression expr)
- {
- MethodReference addMethod;
- List<ILExpression> args;
- if (expr.Match(ILCode.Callvirt, out addMethod, out args) || expr.Match(ILCode.Call, out addMethod, out args)) {
- if (addMethod.Name == "Add" && addMethod.HasThis) {
- return args.Count >= 2;
- }
- }
- return false;
- }
-
- /// <summary>
- /// Parses an object initializer.
- /// </summary>
- ILExpression ParseObjectInitializer(List<ILNode> body, ref int pos, ILVariable v, ILExpression newObjExpr, bool isCollection, bool isValueType)
- {
- // Take care not to modify any existing ILExpressions in here.
- // We just construct new ones around the old ones, any modifications must wait until the whole
- // object/collection initializer was analyzed.
- ILExpression objectInitializer = new ILExpression(isCollection ? ILCode.InitCollection : ILCode.InitObject, null, newObjExpr);
- List<ILExpression> initializerStack = new List<ILExpression>();
- initializerStack.Add(objectInitializer);
- while (++pos < body.Count) {
- ILExpression nextExpr = body[pos] as ILExpression;
- if (IsSetterInObjectInitializer(nextExpr)) {
- if (!AdjustInitializerStack(initializerStack, nextExpr.Arguments[0], v, false, isValueType)) {
- CleanupInitializerStackAfterFailedAdjustment(initializerStack);
- break;
- }
- initializerStack[initializerStack.Count - 1].Arguments.Add(nextExpr);
- } else if (IsAddMethodCall(nextExpr)) {
- if (!AdjustInitializerStack(initializerStack, nextExpr.Arguments[0], v, true, isValueType)) {
- CleanupInitializerStackAfterFailedAdjustment(initializerStack);
- break;
- }
- initializerStack[initializerStack.Count - 1].Arguments.Add(nextExpr);
- } else {
- // can't match any more initializers: end of object initializer
- break;
- }
- }
- return objectInitializer;
- }
-
- static bool AdjustInitializerStack(List<ILExpression> initializerStack, ILExpression argument, ILVariable v, bool isCollection, bool isValueType)
- {
- // Argument is of the form 'getter(getter(...(v)))'
- // Unpack it into a list of getters:
- List<ILExpression> getters = new List<ILExpression>();
- while (argument.Code == ILCode.CallvirtGetter || argument.Code == ILCode.CallGetter || argument.Code == ILCode.Ldfld) {
- getters.Add(argument);
- if (argument.Arguments.Count != 1)
- return false;
- argument = argument.Arguments[0];
- }
- // Ensure that the final argument is 'v'
- if (isValueType) {
- ILVariable loadedVar;
- if (!(argument.Match(ILCode.Ldloca, out loadedVar) && loadedVar == v))
- return false;
- } else {
- if (!argument.MatchLdloc(v))
- return false;
- }
- // Now compare the getters with those that are currently active on the initializer stack:
- int i;
- for (i = 1; i <= Math.Min(getters.Count, initializerStack.Count - 1); i++) {
- ILExpression g1 = initializerStack[i].Arguments[0]; // getter stored in initializer
- ILExpression g2 = getters[getters.Count - i]; // matching getter from argument
- if (g1.Operand != g2.Operand) {
- // operands differ, so we abort the comparison
- break;
- }
- }
- // Remove all initializers from the stack that were not matched with one from the argument:
- initializerStack.RemoveRange(i, initializerStack.Count - i);
- // Now create new initializers for the remaining arguments:
- for (; i <= getters.Count; i++) {
- ILExpression g = getters[getters.Count - i];
- MemberReference mr = (MemberReference)g.Operand;
- TypeReference returnType;
- if (mr is FieldReference)
- returnType = TypeAnalysis.GetFieldType((FieldReference)mr);
- else
- returnType = TypeAnalysis.SubstituteTypeArgs(((MethodReference)mr).ReturnType, mr);
-
- ILExpression nestedInitializer = new ILExpression(
- IsCollectionType(returnType) ? ILCode.InitCollection : ILCode.InitObject,
- null, g);
- // add new initializer to its parent:
- ILExpression parentInitializer = initializerStack[initializerStack.Count - 1];
- if (parentInitializer.Code == ILCode.InitCollection) {
- // can't add children to collection initializer
- if (parentInitializer.Arguments.Count == 1) {
- // convert empty collection initializer to object initializer
- parentInitializer.Code = ILCode.InitObject;
- } else {
- return false;
- }
- }
- parentInitializer.Arguments.Add(nestedInitializer);
- initializerStack.Add(nestedInitializer);
- }
- ILExpression lastInitializer = initializerStack[initializerStack.Count - 1];
- if (isCollection) {
- return lastInitializer.Code == ILCode.InitCollection;
- } else {
- if (lastInitializer.Code == ILCode.InitCollection) {
- if (lastInitializer.Arguments.Count == 1) {
- // convert empty collection initializer to object initializer
- lastInitializer.Code = ILCode.InitObject;
- return true;
- } else {
- return false;
- }
- } else {
- return true;
- }
- }
- }
-
- static void CleanupInitializerStackAfterFailedAdjustment(List<ILExpression> initializerStack)
- {
- // There might be empty nested initializers left over; so we'll remove those:
- while (initializerStack.Count > 1 && initializerStack[initializerStack.Count - 1].Arguments.Count == 1) {
- ILExpression parent = initializerStack[initializerStack.Count - 2];
- Debug.Assert(parent.Arguments.Last() == initializerStack[initializerStack.Count - 1]);
- parent.Arguments.RemoveAt(parent.Arguments.Count - 1);
- initializerStack.RemoveAt(initializerStack.Count - 1);
- }
- }
-
- static void ChangeFirstArgumentToInitializedObject(ILExpression initializer)
- {
- // Go through all elements in the initializer (so skip the newobj-instr. at the start)
- for (int i = 1; i < initializer.Arguments.Count; i++) {
- ILExpression element = initializer.Arguments[i];
- if (element.Code == ILCode.InitCollection || element.Code == ILCode.InitObject) {
- // nested collection/object initializer
- ILExpression getCollection = element.Arguments[0];
- getCollection.Arguments[0] = new ILExpression(ILCode.InitializedObject, null);
- ChangeFirstArgumentToInitializedObject(element); // handle the collection elements
- } else {
- element.Arguments[0] = new ILExpression(ILCode.InitializedObject, null);
- }
- }
- }
- }
-}