Beispiel #1
0
        private static bool Step2(List <ILNode> body, ref int pos)
        {
            // stloc(CS$0$0001, callvirt(class System.Threading.Tasks.Task`1<bool>::GetAwaiter, awaiterExpr)
            // brtrue(IL_7C, call(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::get_IsCompleted, ldloca(CS$0$0001)))
            // await(ldloca(CS$0$0001))
            // ...
            // IL_7C:
            // arg_8B_0 = call(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::GetResult, ldloca(CS$0$0001))
            // initobj(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>, ldloca(CS$0$0001))

            ILExpression loadAwaiter;
            ILVariable   awaiterVar;

            if (!body[pos].Match(ILCode.Await, out loadAwaiter))
            {
                return(false);
            }
            if (!loadAwaiter.Match(ILCode.Ldloca, out awaiterVar))
            {
                return(false);
            }

            ILVariable   stackVar;
            ILExpression stackExpr;

            while (pos >= 1 && body[pos - 1].Match(ILCode.Stloc, out stackVar, out stackExpr))
            {
                pos--;
            }

            // stloc(CS$0$0001, callvirt(class System.Threading.Tasks.Task`1<bool>::GetAwaiter, awaiterExpr)
            ILExpression getAwaiterCall;

            if (!(pos >= 2 && body[pos - 2].MatchStloc(awaiterVar, out getAwaiterCall)))
            {
                return(false);
            }
            MethodReference getAwaiterMethod;
            ILExpression    awaitedExpr;

            if (!(getAwaiterCall.Match(ILCode.Call, out getAwaiterMethod, out awaitedExpr) || getAwaiterCall.Match(ILCode.Callvirt, out getAwaiterMethod, out awaitedExpr)))
            {
                return(false);
            }

            if (awaitedExpr.Code == ILCode.AddressOf)
            {
                // remove 'AddressOf()' when calling GetAwaiter() on a value type
                awaitedExpr = awaitedExpr.Arguments[0];
            }

            // brtrue(IL_7C, call(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::get_IsCompleted, ldloca(CS$0$0001)))
            ILLabel      label;
            ILExpression getIsCompletedCall;

            if (!(pos >= 1 && body[pos - 1].Match(ILCode.Brtrue, out label, out getIsCompletedCall)))
            {
                return(false);
            }

            int labelPos = body.IndexOf(label);

            if (labelPos < pos)
            {
                return(false);
            }
            for (int i = pos + 1; i < labelPos; i++)
            {
                // validate that we aren't deleting any unexpected instructions -
                // between the await and the label, there should only be the stack, awaiter and state logic
                ILExpression expr = body[i] as ILExpression;
                if (expr == null)
                {
                    return(false);
                }
                switch (expr.Code)
                {
                case ILCode.Stloc:
                case ILCode.Initobj:
                case ILCode.Stfld:
                case ILCode.Await:
                    // e.g.
                    // stloc(CS$0$0001, ldfld(StateMachine::<>u__$awaitere, ldloc(this)))
                    // initobj(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>, ldloca(CS$0$0002_66))
                    // stfld('<AwaitInLoopCondition>d__d'::<>u__$awaitere, ldloc(this), ldloc(CS$0$0002_66))
                    // stfld('<AwaitInLoopCondition>d__d'::<>1__state, ldloc(this), ldc.i4(-1))
                    break;

                default:
                    return(false);
                }
            }
            if (labelPos + 1 >= body.Count)
            {
                return(false);
            }
            ILExpression resultAssignment = body[labelPos + 1] as ILExpression;
            ILVariable   resultVar;
            ILExpression getResultCall;
            bool         isResultAssignment = resultAssignment.Match(ILCode.Stloc, out resultVar, out getResultCall);

            if (!isResultAssignment)
            {
                getResultCall = resultAssignment;
            }
            if (!(getResultCall.Operand is MethodReference && ((MethodReference)getResultCall.Operand).Name == "GetResult"))
            {
                return(false);
            }

            pos -= 2;             // also delete 'stloc', 'brtrue' and 'await'
            body.RemoveRange(pos, labelPos - pos);
            Debug.Assert(body[pos] == label);

            pos++;
            if (isResultAssignment)
            {
                Debug.Assert(body[pos] == resultAssignment);
                resultAssignment.Arguments[0] = new ILExpression(ILCode.Await, null, awaitedExpr);
            }
            else
            {
                body[pos] = new ILExpression(ILCode.Await, null, awaitedExpr);
            }

            // if the awaiter variable is cleared out in the next instruction, remove that instruction
            if (IsVariableReset(body.ElementAtOrDefault(pos + 1), awaiterVar))
            {
                body.RemoveAt(pos + 1);
            }

            return(true);
        }
Beispiel #2
0
        private static bool MatchStFld(ILNode stfld, ILVariable stateMachineVar, out FieldDefinition field, out ILExpression expr)
        {
            field = null;
            FieldReference fieldRef;
            ILExpression   ldloca;

            if (!stfld.Match(ILCode.Stfld, out fieldRef, out ldloca, out expr))
            {
                return(false);
            }
            field = fieldRef.ResolveWithinSameModule();
            return(field != null && ldloca.MatchLdloca(stateMachineVar));
        }
Beispiel #3
0
        private void HandleAwait(List <ILNode> newBody, out ILVariable awaiterVar, out FieldDefinition awaiterField, out int targetStateID)
        {
            // Handle the instructions prior to the exit out of the method to detect what is being awaited.
            // (analyses the last instructions in newBody and removes the analyzed instructions from newBody)

            if (doFinallyBodies != null)
            {
                // stloc(<>t__doFinallyBodies, ldc.i4(0))
                ILExpression dfbInitExpr;
                if (!newBody.LastOrDefault().MatchStloc(doFinallyBodies, out dfbInitExpr))
                {
                    throw new SymbolicAnalysisFailedException();
                }
                int val;
                if (!(dfbInitExpr.Match(ILCode.Ldc_I4, out val) && val == 0))
                {
                    throw new SymbolicAnalysisFailedException();
                }
                newBody.RemoveAt(newBody.Count - 1);                 // remove doFinallyBodies assignment
            }

            // call(AsyncTaskMethodBuilder::AwaitUnsafeOnCompleted, ldflda(StateMachine::<>t__builder, ldloc(this)), ldloca(CS$0$0001), ldloc(this))
            ILExpression callAwaitUnsafeOnCompleted = newBody.LastOrDefault() as ILExpression;

            newBody.RemoveAt(newBody.Count - 1);             // remove AwaitUnsafeOnCompleted call
            if (callAwaitUnsafeOnCompleted == null || callAwaitUnsafeOnCompleted.Code != ILCode.Call)
            {
                throw new SymbolicAnalysisFailedException();
            }
            string methodName = ((MethodReference)callAwaitUnsafeOnCompleted.Operand).Name;

            if (methodName != "AwaitUnsafeOnCompleted" && methodName != "AwaitOnCompleted")
            {
                throw new SymbolicAnalysisFailedException();
            }
            if (callAwaitUnsafeOnCompleted.Arguments.Count != 3)
            {
                throw new SymbolicAnalysisFailedException();
            }
            if (!callAwaitUnsafeOnCompleted.Arguments[1].Match(ILCode.Ldloca, out awaiterVar))
            {
                throw new SymbolicAnalysisFailedException();
            }

            // stfld(StateMachine::<>u__$awaiter6, ldloc(this), ldloc(CS$0$0001))
            FieldReference awaiterFieldRef;
            ILExpression   loadThis, loadAwaiterVar;

            if (!newBody.LastOrDefault().Match(ILCode.Stfld, out awaiterFieldRef, out loadThis, out loadAwaiterVar))
            {
                throw new SymbolicAnalysisFailedException();
            }
            newBody.RemoveAt(newBody.Count - 1);             // remove awaiter field assignment
            awaiterField = awaiterFieldRef.ResolveWithinSameModule();
            if (!(awaiterField != null && loadThis.MatchThis() && loadAwaiterVar.MatchLdloc(awaiterVar)))
            {
                throw new SymbolicAnalysisFailedException();
            }

            // stfld(StateMachine::<>1__state, ldloc(this), ldc.i4(0))
            if (MatchStateAssignment(newBody.LastOrDefault(), out targetStateID))
            {
                newBody.RemoveAt(newBody.Count - 1);                 // remove awaiter field assignment
            }
            else if (MatchRoslynStateAssignment(newBody, newBody.Count - 3, out targetStateID))
            {
                newBody.RemoveRange(newBody.Count - 3, 3);                 // remove awaiter field assignment
            }
        }
        private List <ILNode> FindConditions(HashSet <ControlFlowNode> scope, ControlFlowNode entryNode)
        {
            List <ILNode> result = new List <ILNode>();

            // Do not modify entry data
            scope = new HashSet <ControlFlowNode>(scope);

            Stack <ControlFlowNode> agenda = new Stack <ControlFlowNode>();

            agenda.Push(entryNode);
            while (agenda.Count > 0)
            {
                ControlFlowNode node = agenda.Pop();

                // Find a block that represents a simple condition
                if (scope.Contains(node))
                {
                    ILBasicBlock block = (ILBasicBlock)node.UserData;

                    {
                        // Switch
                        ILLabel[]    caseLabels;
                        ILExpression switchArg;
                        ILLabel      fallLabel;
                        if (block.MatchLastAndBr(ILCode.Switch, out caseLabels, out switchArg, out fallLabel))
                        {
                            // Replace the switch code with ILSwitch
                            ILSwitch ilSwitch = new ILSwitch()
                            {
                                Condition = switchArg
                            };
                            block.Body.RemoveTail(ILCode.Switch, ILCode.Br);
                            block.Body.Add(ilSwitch);
                            block.Body.Add(new ILExpression(ILCode.Br, fallLabel));
                            result.Add(block);

                            // Remove the item so that it is not picked up as content
                            scope.RemoveOrThrow(node);

                            // Find the switch offset
                            int addValue = 0;
                            List <ILExpression> subArgs;
                            if (ilSwitch.Condition.Match(ILCode.Sub, out subArgs) && subArgs[1].Match(ILCode.Ldc_I4, out addValue))
                            {
                                ilSwitch.Condition = subArgs[0];
                            }

                            // Pull in code of cases
                            ControlFlowNode fallTarget = null;
                            labelToCfNode.TryGetValue(fallLabel, out fallTarget);

                            HashSet <ControlFlowNode> frontiers = new HashSet <ControlFlowNode>();
                            if (fallTarget != null)
                            {
                                frontiers.UnionWith(fallTarget.DominanceFrontier.Except(new[] { fallTarget }));
                            }

                            foreach (ILLabel condLabel in caseLabels)
                            {
                                ControlFlowNode condTarget = null;
                                labelToCfNode.TryGetValue(condLabel, out condTarget);
                                if (condTarget != null)
                                {
                                    frontiers.UnionWith(condTarget.DominanceFrontier.Except(new[] { condTarget }));
                                }
                            }

                            for (int i = 0; i < caseLabels.Length; i++)
                            {
                                ILLabel condLabel = caseLabels[i];

                                // Find or create new case block
                                ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.FirstOrDefault(b => b.EntryGoto.Operand == condLabel);
                                if (caseBlock == null)
                                {
                                    caseBlock = new ILSwitch.CaseBlock()
                                    {
                                        Values    = new List <int>(),
                                        EntryGoto = new ILExpression(ILCode.Br, condLabel)
                                    };
                                    ilSwitch.CaseBlocks.Add(caseBlock);

                                    ControlFlowNode condTarget = null;
                                    labelToCfNode.TryGetValue(condLabel, out condTarget);
                                    if (condTarget != null && !frontiers.Contains(condTarget))
                                    {
                                        HashSet <ControlFlowNode> content = FindDominatedNodes(scope, condTarget);
                                        scope.ExceptWith(content);
                                        caseBlock.Body.AddRange(FindConditions(content, condTarget));
                                        // Add explicit break which should not be used by default, but the goto removal might decide to use it
                                        caseBlock.Body.Add(new ILBasicBlock()
                                        {
                                            Body =
                                            {
                                                new ILLabel()
                                                {
                                                    Name = "SwitchBreak_" + (nextLabelIndex++)
                                                },
                                                new ILExpression(ILCode.LoopOrSwitchBreak, null)
                                            }
                                        });
                                    }
                                }
                                caseBlock.Values.Add(i + addValue);
                            }

                            // Heuristis to determine if we want to use fallthough as default case
                            if (fallTarget != null && !frontiers.Contains(fallTarget))
                            {
                                HashSet <ControlFlowNode> content = FindDominatedNodes(scope, fallTarget);
                                if (content.Any())
                                {
                                    var caseBlock = new ILSwitch.CaseBlock()
                                    {
                                        EntryGoto = new ILExpression(ILCode.Br, fallLabel)
                                    };
                                    ilSwitch.CaseBlocks.Add(caseBlock);
                                    block.Body.RemoveTail(ILCode.Br);

                                    scope.ExceptWith(content);
                                    caseBlock.Body.AddRange(FindConditions(content, fallTarget));
                                    // Add explicit break which should not be used by default, but the goto removal might decide to use it
                                    caseBlock.Body.Add(new ILBasicBlock()
                                    {
                                        Body =
                                        {
                                            new ILLabel()
                                            {
                                                Name = "SwitchBreak_" + (nextLabelIndex++)
                                            },
                                            new ILExpression(ILCode.LoopOrSwitchBreak, null)
                                        }
                                    });
                                }
                            }
                        }

                        // Two-way branch
                        ILExpression condExpr;
                        ILLabel      trueLabel;
                        ILLabel      falseLabel;
                        if (block.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel))
                        {
                            // Swap bodies since that seems to be the usual C# order
                            ILLabel temp = trueLabel;
                            trueLabel  = falseLabel;
                            falseLabel = temp;
                            condExpr   = new ILExpression(ILCode.LogicNot, null, condExpr);

                            // Convert the brtrue to ILCondition
                            ILCondition ilCond = new ILCondition()
                            {
                                Condition = condExpr,
                                TrueBlock = new ILBlock()
                                {
                                    EntryGoto = new ILExpression(ILCode.Br, trueLabel)
                                },
                                FalseBlock = new ILBlock()
                                {
                                    EntryGoto = new ILExpression(ILCode.Br, falseLabel)
                                }
                            };
                            block.Body.RemoveTail(ILCode.Brtrue, ILCode.Br);
                            block.Body.Add(ilCond);
                            result.Add(block);

                            // Remove the item immediately so that it is not picked up as content
                            scope.RemoveOrThrow(node);

                            ControlFlowNode trueTarget = null;
                            labelToCfNode.TryGetValue(trueLabel, out trueTarget);
                            ControlFlowNode falseTarget = null;
                            labelToCfNode.TryGetValue(falseLabel, out falseTarget);

                            // Pull in the conditional code
                            if (trueTarget != null && HasSingleEdgeEnteringBlock(trueTarget))
                            {
                                HashSet <ControlFlowNode> content = FindDominatedNodes(scope, trueTarget);
                                scope.ExceptWith(content);
                                ilCond.TrueBlock.Body.AddRange(FindConditions(content, trueTarget));
                            }
                            if (falseTarget != null && HasSingleEdgeEnteringBlock(falseTarget))
                            {
                                HashSet <ControlFlowNode> content = FindDominatedNodes(scope, falseTarget);
                                scope.ExceptWith(content);
                                ilCond.FalseBlock.Body.AddRange(FindConditions(content, falseTarget));
                            }
                        }
                    }

                    // Add the node now so that we have good ordering
                    if (scope.Contains(node))
                    {
                        result.Add((ILNode)node.UserData);
                        scope.Remove(node);
                    }
                }

                // depth-first traversal of dominator tree
                for (int i = node.DominatorTreeChildren.Count - 1; i >= 0; i--)
                {
                    agenda.Push(node.DominatorTreeChildren[i]);
                }
            }

            // Add whatever is left
            foreach (var node in scope)
            {
                result.Add((ILNode)node.UserData);
            }

            return(result);
        }
Beispiel #5
0
        List <ILNode> ConvertToAst(List <ByteCode> body)
        {
            List <ILNode> ast = new List <ILNode>();

            // Convert stack-based IL code to ILAst tree
            foreach (ByteCode byteCode in body)
            {
                ILRange ilRange = new ILRange()
                {
                    From = byteCode.Offset, To = byteCode.EndOffset
                };

                if (byteCode.StackBefore == null)
                {
                    // Unreachable code
                    continue;
                }

                ILExpression expr = new ILExpression(byteCode.Code, byteCode.Operand);
                expr.ILRanges.Add(ilRange);
                expr.Prefixes = byteCode.Prefixes;

                // Label for this instruction
                if (byteCode.Label != null)
                {
                    ast.Add(byteCode.Label);
                }

                // Reference arguments using temporary variables
                int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count;
                for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++)
                {
                    StackSlot slot = byteCode.StackBefore[i];
                    expr.Arguments.Add(new ILExpression(ILCode.Ldloc, slot.LoadFrom));
                }

                // Store the result to temporary variable(s) if needed
                if (byteCode.StoreTo == null || byteCode.StoreTo.Count == 0)
                {
                    ast.Add(expr);
                }
                else if (byteCode.StoreTo.Count == 1)
                {
                    ast.Add(new ILExpression(ILCode.Stloc, byteCode.StoreTo[0], expr));
                }
                else
                {
                    ILVariable tmpVar = new ILVariable()
                    {
                        Name = "expr_" + byteCode.Offset.ToString("X2"), IsGenerated = true
                    };
                    ast.Add(new ILExpression(ILCode.Stloc, tmpVar, expr));
                    foreach (ILVariable storeTo in byteCode.StoreTo.AsEnumerable().Reverse())
                    {
                        ast.Add(new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, tmpVar)));
                    }
                }
            }

            return(ast);
        }
Beispiel #6
0
 public override bool Match(PatternMatcher pm, ILExpression e)
 {
     return(e.Code == ILCode.Ldc_I4 && e.InferredType.GetElementType() == ElementType.Boolean && object.Equals(e.Operand, value));
 }
Beispiel #7
0
        /// <summary>
        /// Finds the position to inline to.
        /// </summary>
        /// <returns>true = found; false = cannot continue search; null = not found</returns>
        private bool?FindLoadInNext(ILExpression expr, ILVariable v, ILExpression expressionBeingMoved, out ILExpression parent, out int pos)
        {
            parent = null;
            pos    = 0;
            if (expr == null)
            {
                return(false);
            }
            for (int i = 0; i < expr.Arguments.Count; i++)
            {
                // Stop when seeing an opcode that does not guarantee that its operands will be evaluated.
                // Inlining in that case might result in the inlined expresion not being evaluted.
                if (i == 1 && (expr.Code == ILCode.LogicAnd || expr.Code == ILCode.LogicOr || expr.Code == ILCode.TernaryOp || expr.Code == ILCode.NullCoalescing))
                {
                    return(false);
                }

                ILExpression arg = expr.Arguments[i];

                if ((arg.Code == ILCode.Ldloc || arg.Code == ILCode.Ldloca) && arg.Operand == v)
                {
                    parent = expr;
                    pos    = i;
                    return(true);
                }
                bool?r = FindLoadInNext(arg, v, expressionBeingMoved, out parent, out pos);
                if (r != null)
                {
                    return(r);
                }
            }
            if (IsSafeForInlineOver(expr, expressionBeingMoved))
            {
                return(null);                // continue searching
            }
            else
            {
                return(false);                // abort, inlining not possible
            }
        }
        public static bool Match <T>(this ILNode node, ILCode code, out T operand, out ILExpression arg1, out ILExpression arg2)
        {
            List <ILExpression> args;

            if (node.Match(code, out operand, out args) && args.Count == 2)
            {
                arg1 = args[0];
                arg2 = args[1];
                return(true);
            }
            arg1 = null;
            arg2 = null;
            return(false);
        }
 public static bool MatchSingle <T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg)
 {
     if (bb.Body.Count == 2 &&
         bb.Body[0] is ILLabel &&
         bb.Body[1].Match(code, out operand, out arg))
     {
         return(true);
     }
     operand = default(T);
     arg     = null;
     return(false);
 }
        /// <summary>
        /// Parses an object initializer.
        /// </summary>
        /// <param name="body">ILAst block</param>
        /// <param name="pos">
        /// Input: position of the instruction assigning to 'v'.
        /// Output: first position after the object initializer
        /// </param>
        /// <param name="v">The variable that holds the object being initialized</param>
        /// <param name="newObjExpr">The newobj instruction</param>
        /// <returns>InitObject instruction</returns>
        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);
                }
            }
        }
        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)
                    {
                        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);
        }
        /// <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);
        }
        static bool DecodeArrayInitializer(TypeCode elementType, byte[] initialValue, ILExpression[] output)
        {
            switch (elementType)
            {
            case TypeCode.Boolean:
            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.SByte:
                if (initialValue.Length == output.Length)
                {
                    for (int j = 0; j < output.Length; j++)
                    {
                        output[j] = new ILExpression(ILCode.Ldc_I4, (int)unchecked ((sbyte)initialValue[j]));
                    }
                    return(true);
                }
                return(false);

            case TypeCode.Int16:
                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.Char:
            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.ToUInt16(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);
            }
        }
Beispiel #15
0
 public override bool Match(PatternMatcher pm, ILExpression e)
 {
     return(e.Code == this.code && base.Match(pm, e));
 }
 public static bool MatchSingleAndBr <T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel brLabel)
 {
     if (bb.Body.Count == 3 &&
         bb.Body[0] is ILLabel &&
         bb.Body[1].Match(code, out operand, out arg) &&
         bb.Body[2].Match(ILCode.Br, out brLabel))
     {
         return(true);
     }
     operand = default(T);
     arg     = null;
     brLabel = null;
     return(false);
 }
Beispiel #17
0
                public override bool Match(PatternMatcher pm, ILExpression e)
                {
                    switch (e.Code)
                    {
                    case ILCode.Ceq:
                        if (type != OperatorType.Equality)
                        {
                            return(false);
                        }
                        break;

                    case ILCode.Cne:
                        if (type != OperatorType.InEquality)
                        {
                            return(false);
                        }
                        break;

                    case ILCode.Cgt:
                    case ILCode.Cgt_Un:
                    case ILCode.Cge:
                    case ILCode.Cge_Un:
                    case ILCode.Clt:
                    case ILCode.Clt_Un:
                    case ILCode.Cle:
                    case ILCode.Cle_Un:
                        if (type != OperatorType.Comparison)
                        {
                            return(false);
                        }
                        break;

                    case ILCode.Add:
                    case ILCode.Add_Ovf:
                    case ILCode.Add_Ovf_Un:
                    case ILCode.Sub:
                    case ILCode.Sub_Ovf:
                    case ILCode.Sub_Ovf_Un:
                    case ILCode.Mul:
                    case ILCode.Mul_Ovf:
                    case ILCode.Mul_Ovf_Un:
                    case ILCode.Div:
                    case ILCode.Div_Un:
                    case ILCode.Rem:
                    case ILCode.Rem_Un:
                    case ILCode.And:
                    case ILCode.Or:
                    case ILCode.Xor:
                    case ILCode.Shl:
                    case ILCode.Shr:
                    case ILCode.Shr_Un:
                    case ILCode.Not:
                    case ILCode.Neg:
                    case ILCode.LogicNot:
                        if (type != OperatorType.Other)
                        {
                            return(false);
                        }
                        break;

                    case ILCode.Call:
                        var m = e.Operand as IMethod;
                        if (m == null || m.MethodSig == null || m.MethodSig.HasThis || m.MethodSig.Params.Count == 0 || e.Arguments.Count > 2 || !IsCustomOperator(m.Name))
                        {
                            return(false);
                        }
                        break;

                    default: return(false);
                    }
                    if (pm.Operator != null)
                    {
                        throw new InvalidOperationException();
                    }
                    pm.Operator = e;

                    var a0 = e.Arguments[0];

                    if (!simple)
                    {
                        return(VariableAGetValueOrDefault.Match(pm, a0) && VariableBGetValueOrDefault.Match(pm, e.Arguments[1]));
                    }
                    if (e.Arguments.Count == 1)
                    {
                        return(VariableAGetValueOrDefault.Match(pm, a0));
                    }
                    if (VariableAGetValueOrDefault.Match(pm, a0))
                    {
                        pm.SimpleOperand     = e.Arguments[1];
                        pm.SimpleLeftOperand = false;
                        return(true);
                    }
                    if (VariableAGetValueOrDefault.Match(pm, e.Arguments[1]))
                    {
                        pm.SimpleOperand     = a0;
                        pm.SimpleLeftOperand = true;
                        return(true);
                    }
                    return(false);
                }
 public static bool MatchLastAndBr <T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel brLabel)
 {
     if (bb.Body.ElementAtOrDefault(bb.Body.Count - 2).Match(code, out operand, out arg) &&
         bb.Body.LastOrDefault().Match(ILCode.Br, out brLabel))
     {
         return(true);
     }
     operand = default(T);
     arg     = null;
     brLabel = null;
     return(false);
 }
Beispiel #19
0
        /// <summary>
        /// Is this a temporary variable generated by the C# compiler for instance method calls on value type values
        /// </summary>
        /// <param name="next">The next top-level expression</param>
        /// <param name="parent">The direct parent of the load within 'next'</param>
        /// <param name="pos">Index of the load within 'parent'</param>
        /// <param name="v">The variable being inlined.</param>
        /// <param name="inlinedExpression">The expression being inlined</param>
        private bool IsGeneratedValueTypeTemporary(ILExpression next, ILExpression parent, int pos, ILVariable v, ILExpression inlinedExpression)
        {
            if (pos == 0 && v.Type != null && v.Type.IsValueType)
            {
                // Inlining a value type variable is allowed only if the resulting code will maintain the semantics
                // that the method is operating on a copy.
                // Thus, we have to disallow inlining of other locals, fields, array elements, dereferenced pointers
                switch (inlinedExpression.Code)
                {
                case ILCode.Ldloc:
                case ILCode.Stloc:
                case ILCode.CompoundAssignment:
                case ILCode.Ldelem_Any:
                case ILCode.Ldelem_I:
                case ILCode.Ldelem_I1:
                case ILCode.Ldelem_I2:
                case ILCode.Ldelem_I4:
                case ILCode.Ldelem_I8:
                case ILCode.Ldelem_R4:
                case ILCode.Ldelem_R8:
                case ILCode.Ldelem_Ref:
                case ILCode.Ldelem_U1:
                case ILCode.Ldelem_U2:
                case ILCode.Ldelem_U4:
                case ILCode.Ldobj:
                case ILCode.Ldind_Ref:
                    return(false);

                case ILCode.Ldfld:
                case ILCode.Stfld:
                case ILCode.Ldsfld:
                case ILCode.Stsfld:
                    // allow inlining field access only if it's a readonly field
                    FieldDefinition f = ((FieldReference)inlinedExpression.Operand).Resolve();
                    if (!(f != null && f.IsInitOnly))
                    {
                        return(false);
                    }
                    break;

                case ILCode.Call:
                case ILCode.CallGetter:
                    // inlining runs both before and after IntroducePropertyAccessInstructions,
                    // so we have to handle both 'call' and 'callgetter'
                    MethodReference mr = (MethodReference)inlinedExpression.Operand;
                    // ensure that it's not an multi-dimensional array getter
                    if (mr.DeclaringType is ArrayType)
                    {
                        return(false);
                    }
                    goto case ILCode.Callvirt;

                case ILCode.Callvirt:
                case ILCode.CallvirtGetter:
                    // don't inline foreach loop variables:
                    mr = (MethodReference)inlinedExpression.Operand;
                    if (mr.Name == "get_Current" && mr.HasThis)
                    {
                        return(false);
                    }
                    break;

                case ILCode.Castclass:
                case ILCode.Unbox_Any:
                    // These are valid, but might occur as part of a foreach loop variable.
                    ILExpression arg = inlinedExpression.Arguments[0];
                    if (arg.Code == ILCode.CallGetter || arg.Code == ILCode.CallvirtGetter || arg.Code == ILCode.Call || arg.Code == ILCode.Callvirt)
                    {
                        mr = (MethodReference)arg.Operand;
                        if (mr.Name == "get_Current" && mr.HasThis)
                        {
                            return(false);                                    // looks like a foreach loop variable, so don't inline it
                        }
                    }
                    break;
                }

                // inline the compiler-generated variable that are used when accessing a member on a value type:
                switch (parent.Code)
                {
                case ILCode.Call:
                case ILCode.CallGetter:
                case ILCode.CallSetter:
                case ILCode.Callvirt:
                case ILCode.CallvirtGetter:
                case ILCode.CallvirtSetter:
                    MethodReference mr = (MethodReference)parent.Operand;
                    return(mr.HasThis);

                case ILCode.Stfld:
                case ILCode.Ldfld:
                case ILCode.Ldflda:
                case ILCode.Await:
                    return(true);
                }
            }
            return(false);
        }
        public static bool MatchStloc(this ILNode node, ILVariable expectedVar, out ILExpression expr)
        {
            ILVariable v;

            return(node.Match(ILCode.Stloc, out v, out expr) && v == expectedVar);
        }
        private List <ILNode> FindLoops(HashSet <ControlFlowNode> scope, ControlFlowNode entryPoint, bool excludeEntryPoint)
        {
            List <ILNode> result = new List <ILNode>();

            // Do not modify entry data
            scope = new HashSet <ControlFlowNode>(scope);

            Queue <ControlFlowNode> agenda = new Queue <ControlFlowNode>();

            agenda.Enqueue(entryPoint);
            while (agenda.Count > 0)
            {
                ControlFlowNode node = agenda.Dequeue();

                // If the node is a loop header
                if (scope.Contains(node) &&
                    node.DominanceFrontier.Contains(node) &&
                    (node != entryPoint || !excludeEntryPoint))
                {
                    HashSet <ControlFlowNode> loopContents = FindLoopContent(scope, node);

                    // If the first expression is a loop condition
                    ILBasicBlock basicBlock = (ILBasicBlock)node.UserData;
                    ILExpression condExpr;
                    ILLabel      trueLabel;
                    ILLabel      falseLabel;
                    // It has to be just brtrue - any preceding code would introduce goto
                    if (basicBlock.MatchSingleAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel))
                    {
                        ControlFlowNode trueTarget;
                        labelToCfNode.TryGetValue(trueLabel, out trueTarget);
                        ControlFlowNode falseTarget;
                        labelToCfNode.TryGetValue(falseLabel, out falseTarget);

                        // If one point inside the loop and the other outside
                        if ((!loopContents.Contains(trueTarget) && loopContents.Contains(falseTarget)) ||
                            (loopContents.Contains(trueTarget) && !loopContents.Contains(falseTarget)))
                        {
                            loopContents.RemoveOrThrow(node);
                            scope.RemoveOrThrow(node);

                            // If false means enter the loop
                            if (loopContents.Contains(falseTarget) || falseTarget == node)
                            {
                                // Negate the condition
                                condExpr = new ILExpression(ILCode.LogicNot, null, condExpr);
                                ILLabel tmp = trueLabel;
                                trueLabel  = falseLabel;
                                falseLabel = tmp;
                            }

                            ControlFlowNode postLoopTarget;
                            labelToCfNode.TryGetValue(falseLabel, out postLoopTarget);
                            if (postLoopTarget != null)
                            {
                                // Pull more nodes into the loop
                                HashSet <ControlFlowNode> postLoopContents = FindDominatedNodes(scope, postLoopTarget);
                                var pullIn = scope.Except(postLoopContents).Where(n => node.Dominates(n));
                                loopContents.UnionWith(pullIn);
                            }

                            // Use loop to implement the brtrue
                            basicBlock.Body.RemoveTail(ILCode.Brtrue, ILCode.Br);
                            basicBlock.Body.Add(new ILWhileLoop()
                            {
                                Condition = condExpr,
                                BodyBlock = new ILBlock()
                                {
                                    EntryGoto = new ILExpression(ILCode.Br, trueLabel),
                                    Body      = FindLoops(loopContents, node, false)
                                }
                            });
                            basicBlock.Body.Add(new ILExpression(ILCode.Br, falseLabel));
                            result.Add(basicBlock);

                            scope.ExceptWith(loopContents);
                        }
                    }

                    // Fallback method: while(true)
                    if (scope.Contains(node))
                    {
                        result.Add(new ILBasicBlock()
                        {
                            Body = new List <ILNode>()
                            {
                                new ILLabel()
                                {
                                    Name = "Loop_" + (nextLabelIndex++)
                                },
                                new ILWhileLoop()
                                {
                                    BodyBlock = new ILBlock()
                                    {
                                        EntryGoto = new ILExpression(ILCode.Br, (ILLabel)basicBlock.Body.First()),
                                        Body      = FindLoops(loopContents, node, true)
                                    }
                                },
                            },
                        });

                        scope.ExceptWith(loopContents);
                    }
                }

                // Using the dominator tree should ensure we find the the widest loop first
                foreach (var child in node.DominatorTreeChildren)
                {
                    agenda.Enqueue(child);
                }
            }

            // Add whatever is left
            foreach (var node in scope)
            {
                result.Add((ILNode)node.UserData);
            }
            scope.Clear();

            return(result);
        }
        public static bool Match(this ILNode node, ILCode code)
        {
            ILExpression expr = node as ILExpression;

            return(expr != null && expr.Prefixes == null && expr.Code == code);
        }
        /// <summary>
        /// Get the first expression to be excecuted if the instruction pointer is at the start of the given node.
        /// Try blocks may not be entered in any way.  If possible, the try block is returned as the node to be executed.
        /// </summary>
        ILNode Enter(ILNode node, HashSet <ILNode> visitedNodes)
        {
            if (node == null)
            {
                throw new ArgumentNullException();
            }

            if (!visitedNodes.Add(node))
            {
                return(null);                 // Infinite loop
            }
            ILLabel label = node as ILLabel;

            if (label != null)
            {
                return(Exit(label, visitedNodes));
            }

            ILExpression expr = node as ILExpression;

            if (expr != null)
            {
                if (expr.Code == ILCode.Br || expr.Code == ILCode.Leave)
                {
                    ILLabel target = (ILLabel)expr.Operand;
                    // Early exit - same try-block
                    if (GetParents(expr).OfType <ILTryCatchBlock>().FirstOrDefault() == GetParents(target).OfType <ILTryCatchBlock>().FirstOrDefault())
                    {
                        return(Enter(target, visitedNodes));
                    }
                    // Make sure we are not entering any try-block
                    var srcTryBlocks = GetParents(expr).OfType <ILTryCatchBlock>().Reverse().ToList();
                    var dstTryBlocks = GetParents(target).OfType <ILTryCatchBlock>().Reverse().ToList();
                    // Skip blocks that we are already in
                    int i = 0;
                    while (i < srcTryBlocks.Count && i < dstTryBlocks.Count && srcTryBlocks[i] == dstTryBlocks[i])
                    {
                        i++;
                    }
                    if (i == dstTryBlocks.Count)
                    {
                        return(Enter(target, visitedNodes));
                    }
                    else
                    {
                        ILTryCatchBlock dstTryBlock = dstTryBlocks[i];
                        // Check that the goto points to the start
                        ILTryCatchBlock current = dstTryBlock;
                        while (current != null)
                        {
                            foreach (ILNode n in current.TryBlock.Body)
                            {
                                if (n is ILLabel)
                                {
                                    if (n == target)
                                    {
                                        return(dstTryBlock);
                                    }
                                }
                                else if (!n.Match(ILCode.Nop))
                                {
                                    current = n as ILTryCatchBlock;
                                    break;
                                }
                            }
                        }
                        return(null);
                    }
                }
                else if (expr.Code == ILCode.Nop)
                {
                    return(Exit(expr, visitedNodes));
                }
                else if (expr.Code == ILCode.LoopOrSwitchBreak)
                {
                    ILNode breakBlock = GetParents(expr).First(n => n is ILWhileLoop || n is ILSwitch);
                    return(Exit(breakBlock, new HashSet <ILNode>()
                    {
                        expr
                    }));
                }
                else if (expr.Code == ILCode.LoopContinue)
                {
                    ILNode continueBlock = GetParents(expr).First(n => n is ILWhileLoop);
                    return(Enter(continueBlock, new HashSet <ILNode>()
                    {
                        expr
                    }));
                }
                else
                {
                    return(expr);
                }
            }

            ILBlock block = node as ILBlock;

            if (block != null)
            {
                if (block.EntryGoto != null)
                {
                    return(Enter(block.EntryGoto, visitedNodes));
                }
                else if (block.Body.Count > 0)
                {
                    return(Enter(block.Body[0], visitedNodes));
                }
                else
                {
                    return(Exit(block, visitedNodes));
                }
            }

            ILCondition cond = node as ILCondition;

            if (cond != null)
            {
                return(cond.Condition);
            }

            ILWhileLoop loop = node as ILWhileLoop;

            if (loop != null)
            {
                if (loop.Condition != null)
                {
                    return(loop.Condition);
                }
                else
                {
                    return(Enter(loop.BodyBlock, visitedNodes));
                }
            }

            ILTryCatchBlock tryCatch = node as ILTryCatchBlock;

            if (tryCatch != null)
            {
                return(tryCatch);
            }

            ILSwitch ilSwitch = node as ILSwitch;

            if (ilSwitch != null)
            {
                return(ilSwitch.Condition);
            }

            throw new NotSupportedException(node.GetType().ToString());
        }
Beispiel #24
0
        List <ILNode> ConvertToAst(List <ByteCode> body, HashSet <ExceptionHandler> ehs)
        {
            List <ILNode> ast = new List <ILNode>();

            while (ehs.Any())
            {
                ILTryCatchBlock tryCatchBlock = new ILTryCatchBlock();

                // Find the first and widest scope
                int tryStart = ehs.Min(eh => eh.TryStart.Offset);
                int tryEnd   = ehs.Where(eh => eh.TryStart.Offset == tryStart).Max(eh => eh.TryEnd.Offset);
                var handlers = ehs.Where(eh => eh.TryStart.Offset == tryStart && eh.TryEnd.Offset == tryEnd).ToList();

                // Cut all instructions up to the try block
                {
                    int tryStartIdx;
                    for (tryStartIdx = 0; body[tryStartIdx].Offset != tryStart; tryStartIdx++)
                    {
                        ;
                    }
                    ast.AddRange(ConvertToAst(body.CutRange(0, tryStartIdx)));
                }

                // Cut the try block
                {
                    HashSet <ExceptionHandler> nestedEHs = new HashSet <ExceptionHandler>(ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd)));
                    ehs.ExceptWith(nestedEHs);
                    int tryEndIdx;
                    for (tryEndIdx = 0; tryEndIdx < body.Count && body[tryEndIdx].Offset != tryEnd; tryEndIdx++)
                    {
                        ;
                    }
                    tryCatchBlock.TryBlock = new ILBlock(ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs));
                }

                // Cut all handlers
                tryCatchBlock.CatchBlocks = new List <ILTryCatchBlock.CatchBlock>();
                foreach (ExceptionHandler eh in handlers)
                {
                    int startIndex;
                    for (startIndex = 0; body[startIndex].Offset != eh.HandlerStart.Offset; startIndex++)
                    {
                        ;
                    }
                    int endInclusiveIndex;
                    if (eh.HandlerEnd == null)
                    {
                        endInclusiveIndex = body.Count - 1;
                    }
                    // Note that the end(exclusive) instruction may not necessarly be in our body
                    else
                    {
                        for (endInclusiveIndex = 0; body[endInclusiveIndex].Next.Offset != eh.HandlerEnd.Offset; endInclusiveIndex++)
                        {
                            ;
                        }
                    }
                    int count = 1 + endInclusiveIndex - startIndex;
                    HashSet <ExceptionHandler> nestedEHs = new HashSet <ExceptionHandler>(ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < eh.HandlerEnd.Offset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= eh.HandlerEnd.Offset)));
                    ehs.ExceptWith(nestedEHs);
                    List <ILNode> handlerAst = ConvertToAst(body.CutRange(startIndex, count), nestedEHs);
                    if (eh.HandlerType == ExceptionHandlerType.Catch)
                    {
                        ILTryCatchBlock.CatchBlock catchBlock = new ILTryCatchBlock.CatchBlock()
                        {
                            ExceptionType = eh.CatchType,
                            Body          = handlerAst
                        };
                        // Handle the automatically pushed exception on the stack
                        ByteCode ldexception = ldexceptions[eh];
                        if (ldexception.StoreTo.Count == 0)
                        {
                            throw new Exception("Exception should be consumed by something");
                        }
                        else if (ldexception.StoreTo.Count == 1)
                        {
                            ILExpression first = catchBlock.Body[0] as ILExpression;
                            if (first != null &&
                                first.Code == ILCode.Pop &&
                                first.Arguments[0].Code == ILCode.Ldloc &&
                                first.Arguments[0].Operand == ldexception.StoreTo[0])
                            {
                                // The exception is just poped - optimize it all away;
                                catchBlock.ExceptionVariable = null;
                                catchBlock.Body.RemoveAt(0);
                            }
                            else
                            {
                                catchBlock.ExceptionVariable = ldexception.StoreTo[0];
                            }
                        }
                        else
                        {
                            ILVariable exTemp = new ILVariable()
                            {
                                Name = "ex_" + eh.HandlerStart.Offset.ToString("X2"), IsGenerated = true
                            };
                            catchBlock.ExceptionVariable = exTemp;
                            foreach (ILVariable storeTo in ldexception.StoreTo)
                            {
                                catchBlock.Body.Insert(0, new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, exTemp)));
                            }
                        }
                        tryCatchBlock.CatchBlocks.Add(catchBlock);
                    }
                    else if (eh.HandlerType == ExceptionHandlerType.Finally)
                    {
                        tryCatchBlock.FinallyBlock = new ILBlock(handlerAst);
                    }
                    else if (eh.HandlerType == ExceptionHandlerType.Fault)
                    {
                        tryCatchBlock.FaultBlock = new ILBlock(handlerAst);
                    }
                    else
                    {
                        // TODO: ExceptionHandlerType.Filter
                    }
                }

                ehs.ExceptWith(handlers);

                ast.Add(tryCatchBlock);
            }

            // Add whatever is left
            ast.AddRange(ConvertToAst(body));

            return(ast);
        }