Beispiel #1
0
        /// <summary>
        /// Builds the control flow graph for the current container (if necessary), establishes loopContext
        /// and returns the ControlFlowNodes corresponding to the inner flow and case blocks of the potential switch
        /// </summary>
        private (List <ControlFlowNode> flowNodes, List <ControlFlowNode> caseNodes) AnalyzeControlFlow()
        {
            if (controlFlowGraph == null)
            {
                controlFlowGraph = new ControlFlowGraph(currentContainer, context.CancellationToken);
            }

            var switchHead = controlFlowGraph.GetNode(analysis.RootBlock);

            loopContext = new LoopContext(controlFlowGraph, switchHead);

            var flowNodes = new List <ControlFlowNode> {
                switchHead
            };

            flowNodes.AddRange(analysis.InnerBlocks.Select(controlFlowGraph.GetNode));

            // grab the control flow nodes for blocks targetted by each section
            var caseNodes = new List <ControlFlowNode>();

            foreach (var s in analysis.Sections)
            {
                if (!s.Value.MatchBranch(out var block))
                {
                    continue;
                }

                if (block.Parent == currentContainer)
                {
                    var node = controlFlowGraph.GetNode(block);
                    if (!loopContext.MatchContinue(node))
                    {
                        caseNodes.Add(node);
                    }
                }
            }

            AddNullCase(flowNodes, caseNodes);

            Debug.Assert(flowNodes.SelectMany(n => n.Successors)
                         .All(n => flowNodes.Contains(n) || caseNodes.Contains(n) || loopContext.MatchContinue(n)));

            return(flowNodes, caseNodes);
        }
        public static void Run(ILFunction function, ILTransformContext context)
        {
            if (!context.Settings.AwaitInCatchFinally)
            {
                return;
            }
            HashSet <BlockContainer> changedContainers = new HashSet <BlockContainer>();

            // analyze all try-catch statements in the function
            foreach (var tryCatch in function.Descendants.OfType <TryCatch>().ToArray())
            {
                if (!(tryCatch.Parent?.Parent is BlockContainer container))
                {
                    continue;
                }
                // Detect all handlers that contain an await expression
                AnalyzeHandlers(tryCatch.Handlers, out var catchHandlerIdentifier, out var transformableCatchBlocks);
                var cfg = new ControlFlowGraph(container, context.CancellationToken);
                if (transformableCatchBlocks.Count > 0)
                {
                    changedContainers.Add(container);
                }
                foreach (var result in transformableCatchBlocks)
                {
                    var node = cfg.GetNode(result.RealCatchBlockEntryPoint);

                    context.Step("Inline catch block with await", result.Handler);

                    // Remove the IfInstruction from the jump table and eliminate all branches to the block.
                    var jumpTableBlock = (Block)result.JumpTableEntry.Parent;
                    jumpTableBlock.Instructions.RemoveAt(result.JumpTableEntry.ChildIndex);

                    foreach (var branch in tryCatch.Descendants.OfType <Branch>())
                    {
                        if (branch.TargetBlock == jumpTableBlock)
                        {
                            if (result.NextBlockOrExitContainer is BlockContainer exitContainer)
                            {
                                branch.ReplaceWith(new Leave(exitContainer));
                            }
                            else
                            {
                                branch.ReplaceWith(new Branch((Block)result.NextBlockOrExitContainer));
                            }
                        }
                    }

                    // Add the real catch block entry-point to the block container
                    var catchBlockHead = ((BlockContainer)result.Handler.Body).Blocks.Last();

                    result.RealCatchBlockEntryPoint.Remove();
                    ((BlockContainer)result.Handler.Body).Blocks.Insert(0, result.RealCatchBlockEntryPoint);

                    // Remove the generated catch block
                    catchBlockHead.Remove();

                    // Inline all blocks that are dominated by the entrypoint of the real catch block
                    foreach (var n in cfg.cfg)
                    {
                        if (((Block)n.UserData).Parent == result.Handler.Body)
                        {
                            continue;
                        }
                        if (node.Dominates(n))
                        {
                            MoveBlock((Block)n.UserData, (BlockContainer)result.Handler.Body);
                        }
                    }

                    // Remove all assignments to the common object variable that stores the exception object.
                    if (result.ObjectVariableStore != null)
                    {
                        foreach (var load in result.ObjectVariableStore.Variable.LoadInstructions.ToArray())
                        {
                            if (load.Parent is CastClass cc && cc.Type == result.Handler.Variable.Type)
                            {
                                cc.ReplaceWith(new LdLoc(result.Handler.Variable));
                            }
                            else
                            {
                                load.ReplaceWith(new LdLoc(result.Handler.Variable));
                            }
                        }
                    }
                }