/// <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)); } } } }