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