Statement TransformForeachOnArray(ForStatement forStatement)
        {
            if (!context.Settings.ForEachStatement)
            {
                return(null);
            }
            Match m = forOnArrayPattern.Match(forStatement);

            if (!m.Success)
            {
                return(null);
            }
            var itemVariable  = m.Get <IdentifierExpression>("itemVariable").Single().GetILVariable();
            var indexVariable = m.Get <IdentifierExpression>("indexVariable").Single().GetILVariable();
            var arrayVariable = m.Get <IdentifierExpression>("arrayVariable").Single().GetILVariable();
            var loopContainer = forStatement.Annotation <IL.BlockContainer>();

            if (itemVariable == null || indexVariable == null || arrayVariable == null)
            {
                return(null);
            }
            if (!itemVariable.IsSingleDefinition || (itemVariable.CaptureScope != null && itemVariable.CaptureScope != loopContainer))
            {
                return(null);
            }
            if (indexVariable.StoreCount != 2 || indexVariable.LoadCount != 3 || indexVariable.AddressCount != 0)
            {
                return(null);
            }
            var body = new BlockStatement();

            foreach (var statement in m.Get <Statement>("statements"))
            {
                body.Statements.Add(statement.Detach());
            }
            var foreachStmt = new ForeachStatement {
                VariableType      = context.Settings.AnonymousTypes && itemVariable.Type.ContainsAnonymousType() ? new SimpleType("var") : context.TypeSystemAstBuilder.ConvertType(itemVariable.Type),
                VariableName      = itemVariable.Name,
                InExpression      = m.Get <IdentifierExpression>("arrayVariable").Single().Detach(),
                EmbeddedStatement = body
            };

            foreachStmt.CopyAnnotationsFrom(forStatement);
            itemVariable.Kind = IL.VariableKind.ForeachLocal;
            // Add the variable annotation for highlighting (TokenTextWriter expects it directly on the ForeachStatement).
            foreachStmt.AddAnnotation(new ILVariableResolveResult(itemVariable, itemVariable.Type));
            // TODO : add ForeachAnnotation
            forStatement.ReplaceWith(foreachStmt);
            return(foreachStmt);
        }
Exemplo n.º 2
0
 public static ForeachStatement WithILVariable(this ForeachStatement loop, ILVariable v)
 {
     loop.AddAnnotation(v);
     return(loop);
 }
Exemplo n.º 3
0
        Statement TransformToForeach(UsingInstruction inst, out Expression resource)
        {
            // Check if the using resource matches the GetEnumerator pattern.
            resource = exprBuilder.Translate(inst.ResourceExpression);
            var m = getEnumeratorPattern.Match(resource);

            // The using body must be a BlockContainer.
            if (!(inst.Body is BlockContainer container) || !m.Success)
            {
                return(null);
            }
            // The using-variable is the enumerator.
            var enumeratorVar = inst.Variable;
            // If there's another BlockContainer nested in this container and it only has one child block, unwrap it.
            // If there's an extra leave inside the block, extract it into optionalReturnAfterLoop.
            var loopContainer = UnwrapNestedContainerIfPossible(container, out var optionalReturnAfterLoop);
            // Detect whether we're dealing with a while loop with multiple embedded statements.
            var loop = DetectedLoop.DetectLoop(loopContainer);

            if (loop.Kind != LoopKind.While || !(loop.Body is Block body))
            {
                return(null);
            }
            // The loop condition must be a call to enumerator.MoveNext()
            var condition = exprBuilder.TranslateCondition(loop.Conditions.Single());
            var m2        = moveNextConditionPattern.Match(condition.Expression);

            if (!m2.Success)
            {
                return(null);
            }
            // Check enumerator variable references.
            var enumeratorVar2 = m2.Get <IdentifierExpression>("enumerator").Single().GetILVariable();

            if (enumeratorVar2 != enumeratorVar)
            {
                return(null);
            }
            // Detect which foreach-variable transformation is necessary/possible.
            var transformation = DetectGetCurrentTransformation(container, body, enumeratorVar, condition.ILInstructions.Single(),
                                                                out var singleGetter, out var foreachVariable);

            if (transformation == RequiredGetCurrentTransformation.NoForeach)
            {
                return(null);
            }
            // The existing foreach variable, if found, can only be used in the loop container.
            if (foreachVariable != null && !(foreachVariable.CaptureScope == null || foreachVariable.CaptureScope == loopContainer))
            {
                return(null);
            }
            // Extract in-expression
            var collectionExpr = m.Get <Expression>("collection").Single();

            // Special case: foreach (var item in this) is decompiled as foreach (var item in base)
            // but a base reference is not valid in this context.
            if (collectionExpr is BaseReferenceExpression)
            {
                collectionExpr = new ThisReferenceExpression().CopyAnnotationsFrom(collectionExpr);
            }
            // Handle explicit casts:
            // This is the case if an explicit type different from the collection-item-type was used.
            // For example: foreach (ClassA item in nonGenericEnumerable)
            var           type          = singleGetter.Method.ReturnType;
            ILInstruction instToReplace = singleGetter;

            switch (instToReplace.Parent)
            {
            case CastClass cc:
                type          = cc.Type;
                instToReplace = cc;
                break;

            case UnboxAny ua:
                type          = ua.Type;
                instToReplace = ua;
                break;
            }
            // Handle the required foreach-variable transformation:
            switch (transformation)
            {
            case RequiredGetCurrentTransformation.UseExistingVariable:
                foreachVariable.Type = type;
                foreachVariable.Kind = VariableKind.ForeachLocal;
                foreachVariable.Name = AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation <ILInstruction>(), foreachVariable);
                break;

            case RequiredGetCurrentTransformation.UninlineAndUseExistingVariable:
                // Unwrap stloc chain.
                var nestedStores = new Stack <ILVariable>();
                var currentInst  = instToReplace;                        // instToReplace is the innermost value of the stloc chain.
                while (currentInst.Parent is StLoc stloc)
                {
                    // Exclude nested stores to foreachVariable
                    // we'll insert one store at the beginning of the block.
                    if (stloc.Variable != foreachVariable && stloc.Parent is StLoc)
                    {
                        nestedStores.Push(stloc.Variable);
                    }
                    currentInst = stloc;
                }
                // Rebuild the nested store instructions:
                ILInstruction reorderedStores = new LdLoc(foreachVariable);
                while (nestedStores.Count > 0)
                {
                    reorderedStores = new StLoc(nestedStores.Pop(), reorderedStores);
                }
                currentInst.ReplaceWith(reorderedStores);
                body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace));
                // Adjust variable type, kind and name.
                goto case RequiredGetCurrentTransformation.UseExistingVariable;

            case RequiredGetCurrentTransformation.IntroduceNewVariable:
                foreachVariable = currentFunction.RegisterVariable(
                    VariableKind.ForeachLocal, type,
                    AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation <ILInstruction>())
                    );
                instToReplace.ReplaceWith(new LdLoc(foreachVariable));
                body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace));
                break;
            }
            // Convert the modified body to C# AST:
            var            whileLoop   = (WhileStatement)ConvertAsBlock(container).First();
            BlockStatement foreachBody = (BlockStatement)whileLoop.EmbeddedStatement.Detach();

            // Remove the first statement, as it is the foreachVariable = enumerator.Current; statement.
            foreachBody.Statements.First().Detach();
            // Construct the foreach loop.
            var foreachStmt = new ForeachStatement {
                VariableType      = settings.AnonymousTypes && foreachVariable.Type.ContainsAnonymousType() ? new SimpleType("var") : exprBuilder.ConvertType(foreachVariable.Type),
                VariableName      = foreachVariable.Name,
                InExpression      = collectionExpr.Detach(),
                EmbeddedStatement = foreachBody
            };

            // Add the variable annotation for highlighting (TokenTextWriter expects it directly on the ForeachStatement).
            foreachStmt.AddAnnotation(new ILVariableResolveResult(foreachVariable, foreachVariable.Type));
            // If there was an optional return statement, return it as well.
            if (optionalReturnAfterLoop != null)
            {
                return(new BlockStatement {
                    Statements =
                    {
                        foreachStmt,
                        optionalReturnAfterLoop.AcceptVisitor(this)
                    }
                });
            }
            return(foreachStmt);
        }
Exemplo n.º 4
0
 /// <summary>
 /// Adds an <see cref="ILVariable"/> to this foreach statement.
 /// </summary>
 public static ForeachStatement WithILVariable(this ForeachStatement loop, ILVariable v)
 {
     loop.AddAnnotation(new ILVariableResolveResult(v, v.Type));
     return(loop);
 }
        Statement TransformForeachOnMultiDimArray(ExpressionStatement expressionStatement)
        {
            if (!context.Settings.ForEachStatement)
            {
                return(null);
            }
            Match     m;
            Statement stmt = expressionStatement;

            IL.ILVariable    collection         = null;
            IL.ILVariable[]  upperBounds        = null;
            List <Statement> statementsToDelete = new List <Statement>();
            int i = 0;

            // first we look for all the upper bound initializations
            do
            {
                m = variableAssignUpperBoundPattern.Match(stmt);
                if (!m.Success)
                {
                    break;
                }
                if (upperBounds == null)
                {
                    collection = m.Get <IdentifierExpression>("collection").Single().GetILVariable();
                    if (!(collection?.Type is Decompiler.TypeSystem.ArrayType arrayType))
                    {
                        break;
                    }
                    upperBounds = new IL.ILVariable[arrayType.Dimensions];
                }
                else
                {
                    statementsToDelete.Add(stmt);
                }
                var nextCollection = m.Get <IdentifierExpression>("collection").Single().GetILVariable();
                if (nextCollection != collection)
                {
                    break;
                }
                if (!int.TryParse(m.Get <PrimitiveExpression>("index").Single().Value?.ToString() ?? "", out int index) || index != i)
                {
                    break;
                }
                upperBounds[i] = m.Get <IdentifierExpression>("variable").Single().GetILVariable();
                stmt           = stmt.GetNextStatement();
                i++;
            } while (stmt != null && i < upperBounds.Length);

            if (upperBounds?.LastOrDefault() == null || collection == null)
            {
                return(null);
            }
            if (!MatchForeachOnMultiDimArray(upperBounds, collection, stmt, out var foreachVariable, out var statements, out var lowerBounds))
            {
                return(null);
            }
            statementsToDelete.Add(stmt);
            statementsToDelete.Add(stmt.GetNextStatement());
            var itemVariable = foreachVariable.GetILVariable();

            if (itemVariable == null || !itemVariable.IsSingleDefinition ||
                !upperBounds.All(ub => ub.IsSingleDefinition && ub.LoadCount == 1) ||
                !lowerBounds.All(lb => lb.StoreCount == 2 && lb.LoadCount == 3 && lb.AddressCount == 0))
            {
                return(null);
            }
            var body = new BlockStatement();

            foreach (var statement in statements)
            {
                body.Statements.Add(statement.Detach());
            }
            var foreachStmt = new ForeachStatement {
                VariableType      = context.Settings.AnonymousTypes && itemVariable.Type.ContainsAnonymousType() ? new SimpleType("var") : context.TypeSystemAstBuilder.ConvertType(itemVariable.Type),
                VariableName      = itemVariable.Name,
                InExpression      = m.Get <IdentifierExpression>("collection").Single().Detach(),
                EmbeddedStatement = body
            };

            foreach (var statement in statementsToDelete)
            {
                statement.Detach();
            }
            //foreachStmt.CopyAnnotationsFrom(forStatement);
            itemVariable.Kind = IL.VariableKind.ForeachLocal;
            // Add the variable annotation for highlighting (TokenTextWriter expects it directly on the ForeachStatement).
            foreachStmt.AddAnnotation(new ILVariableResolveResult(itemVariable, itemVariable.Type));
            // TODO : add ForeachAnnotation
            expressionStatement.ReplaceWith(foreachStmt);
            return(foreachStmt);
        }
Exemplo n.º 6
0
        Statement TransformToForeach(UsingInstruction inst, out Expression resource)
        {
            resource = exprBuilder.Translate(inst.ResourceExpression);
            var m = getEnumeratorPattern.Match(resource);

            if (!(inst.Body is BlockContainer container) || !m.Success)
            {
                return(null);
            }
            var enumeratorVar = inst.Variable;
            var loopContainer = UnwrapNestedContainerIfPossible(container, out var optionalReturnAfterLoop);
            var loop          = DetectedLoop.DetectLoop(loopContainer);

            if (loop.Kind != LoopKind.While || !(loop.Body is Block body))
            {
                return(null);
            }
            var condition = exprBuilder.TranslateCondition(loop.Conditions.Single());
            var m2        = moveNextConditionPattern.Match(condition.Expression);

            if (!m2.Success)
            {
                return(null);
            }
            var enumeratorVar2 = m2.Get <IdentifierExpression>("enumerator").Single().GetILVariable();

            if (enumeratorVar2 != enumeratorVar || !BodyHasSingleGetCurrent(body, enumeratorVar, condition.ILInstructions.Single(),
                                                                            out var singleGetter, out var needsUninlining, out var itemVariable))
            {
                return(null);
            }
            if (itemVariable != null && !(itemVariable.CaptureScope == null || itemVariable.CaptureScope == loopContainer))
            {
                return(null);
            }
            var collectionExpr = m.Get <Expression>("collection").Single();

            if (collectionExpr is BaseReferenceExpression)
            {
                collectionExpr = new ThisReferenceExpression().CopyAnnotationsFrom(collectionExpr);
            }
            var           type          = singleGetter.Method.ReturnType;
            ILInstruction instToReplace = singleGetter;

            switch (instToReplace.Parent)
            {
            case CastClass cc:
                type          = cc.Type;
                instToReplace = cc;
                break;

            case UnboxAny ua:
                type          = ua.Type;
                instToReplace = ua;
                break;
            }
            if (needsUninlining)
            {
                itemVariable = currentFunction.RegisterVariable(
                    VariableKind.ForeachLocal, type,
                    AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation <ILInstruction>())
                    );
                instToReplace.ReplaceWith(new LdLoc(itemVariable));
                body.Instructions.Insert(0, new StLoc(itemVariable, instToReplace));
            }
            else
            {
                if (itemVariable.StoreCount != 1)
                {
                    return(null);
                }
                itemVariable.Type = type;
                itemVariable.Kind = VariableKind.ForeachLocal;
                itemVariable.Name = AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation <ILInstruction>(), itemVariable);
            }
            var            whileLoop   = (WhileStatement)ConvertAsBlock(container).First();
            BlockStatement foreachBody = (BlockStatement)whileLoop.EmbeddedStatement.Detach();

            foreachBody.Statements.First().Detach();
            var foreachStmt = new ForeachStatement {
                VariableType      = settings.AnonymousTypes && itemVariable.Type.ContainsAnonymousType() ? new SimpleType("var") : exprBuilder.ConvertType(itemVariable.Type),
                VariableName      = itemVariable.Name,
                InExpression      = collectionExpr.Detach(),
                EmbeddedStatement = foreachBody
            };

            foreachStmt.AddAnnotation(new ILVariableResolveResult(itemVariable, itemVariable.Type));
            if (optionalReturnAfterLoop != null)
            {
                return(new BlockStatement {
                    Statements =
                    {
                        foreachStmt,
                        optionalReturnAfterLoop.AcceptVisitor(this)
                    }
                });
            }
            return(foreachStmt);
        }