diff options
Diffstat (limited to 'ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs')
-rw-r--r-- | ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs | 535 |
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); - } - } - } - } -} |