Exemplo n.º 1
0
        void SimplifyBranchChains(ILFunction function, ILTransformContext context)
        {
            List <(BlockContainer, Block)> blocksToAdd = new List <(BlockContainer, Block)>();
            HashSet <Block> visitedBlocks = new HashSet <Block>();

            foreach (var branch in function.Descendants.OfType <Branch>())
            {
                // Resolve chained branches to the final target:
                var targetBlock = branch.TargetBlock;
                visitedBlocks.Clear();
                while (targetBlock.Instructions.Count == 1 && targetBlock.Instructions[0].OpCode == OpCode.Branch)
                {
                    if (!visitedBlocks.Add(targetBlock))
                    {
                        // prevent infinite loop when branch chain is cyclic
                        break;
                    }
                    context.Step("Simplify branch to branch", branch);
                    var nextBranch = (Branch)targetBlock.Instructions[0];
                    branch.TargetBlock = nextBranch.TargetBlock;
                    branch.AddILRange(nextBranch.ILRange);
                    if (targetBlock.IncomingEdgeCount == 0)
                    {
                        targetBlock.Instructions.Clear();                         // mark the block for deletion
                    }
                    targetBlock = branch.TargetBlock;
                }
                if (IsBranchToReturnBlock(branch))
                {
                    if (aggressivelyDuplicateReturnBlocks)
                    {
                        // Replace branches to 'return blocks' with the return instruction
                        context.Step("Replace branch to return with return", branch);
                        branch.ReplaceWith(targetBlock.Instructions[0].Clone());
                    }
                    else if (branch.TargetContainer != branch.Ancestors.OfType <BlockContainer>().First())
                    {
                        // We don't want to always inline the return directly, because this
                        // might force us to place the return within a loop, when it's better
                        // placed outside.
                        // But we do want to move the return block into the correct try-finally scope,
                        // so that loop detection at least has the option to put it inside
                        // the loop body.
                        context.Step("Copy return block into try block", branch);
                        Block          blockCopy      = (Block)branch.TargetBlock.Clone();
                        BlockContainer localContainer = branch.Ancestors.OfType <BlockContainer>().First();
                        blocksToAdd.Add((localContainer, blockCopy));
                        branch.TargetBlock = blockCopy;
                    }
                }
                else if (targetBlock.Instructions.Count == 1 && targetBlock.Instructions[0] is Leave leave && leave.Value.MatchNop())
                {
                    context.Step("Replace branch to leave with leave", branch);
                    // Replace branches to 'leave' instruction with the leave instruction
                    branch.ReplaceWith(leave.Clone());
                }
                if (targetBlock.IncomingEdgeCount == 0)
                {
                    targetBlock.Instructions.Clear();                     // mark the block for deletion
                }
            }
            foreach (var(container, block) in blocksToAdd)
            {
                container.Blocks.Add(block);
            }
        }
Exemplo n.º 2
0
        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));
                            }
                        }
                    }
                }
Exemplo n.º 3
0
        public void Run(ILFunction function, ILTransformContext context)
        {
            var insts = LivenessHelper.GetInstructions(function);

            if (insts.Count == 1)
            {
                // empty body, skip method
                return;
            }

            // skip marked methods
            //if (function.CecilMethod.CustomAttributes.Any(x => x.AttributeType.Name == "ManualRefCounting"))
            if (function.Method.GetAttributes().Any(x => x.AttributeType.Name == "ManualRefCounting"))
            {
                return;
            }

            IEnumerable <ILVariable> vars = function.Variables;

            var usedVars = new List <ILVariable>();

            foreach (var v in vars)
            {
                //var aliasSource = TypeInference.GetAliasSource(v);

                //// don't ref count parameters like this. Todo check for out and ref parameters (does O work already?)
                //if (aliasSource?.Kind == VariableKind.Parameter && aliasSource.StackType == StackType.O)
                //	continue;

                // no typeof intermediate variables
                if (v.StoreInstructions.Count == 1 && (v.StoreInstructions.First() as StLoc)?.Value is LdTypeToken)
                {
                    continue;
                }

                // skip variables that only contain NULL, etc
                if (TypeInference.GetVariableType(v) == null)
                {
                    continue;
                }

                // only reference types or valuetypes that require ref counting
                if (TypeInference.GetVariableType(v).Kind == TypeKind.Pointer)
                {
                    continue;

                    var pType    = TypeInference.GetVariableType(v) as ICSharpCode.Decompiler.TypeSystem.PointerType;
                    var elemType = pType.ElementType;

                    //if (!elemType.IsReferenceType.Value)
                    //	continue;
                    if (!elemType.IsReferenceType.Value && !elemType.GetMethods().Any(x => x.Name.EndsWith("_AddRef")))
                    {
                        continue;
                    }
                }
                else
                {
                    if (!TypeInference.GetVariableType(v).IsReferenceType.Value&& !TypeInference.GetVariableType(v).GetMethods().Any(x => x.Name.EndsWith("_AddRef")))
                    {
                        continue;
                    }
                }


                // references are not supported in C
                Debug.Assert(TypeInference.GetVariableType(v).Kind != TypeKind.ByReference);


                //todo: are the following two needed?
                //if (TypeInference.GetVariableType(v).Kind == TypeKind.Pointer)
                //	continue;

                //if (TypeInference.GetVariableType(v).Kind == TypeKind.ByReference)
                //	continue;

                usedVars.Add(v);
            }

            vars = usedVars.ToArray();

            var liveDebug = DebugLiveness.DebugLocalLiveness(function, vars);

            AddRefCountingLocalLiveness(insts, vars);

            ILAstDebugPrinter.DebugIlAst(function, "after_ref", liveDebug);

            // Add casts to make compiler happy
            // Todo: I would prefer to access base field insead. No easy representation at this stage though.
            // Todo: move to own optimization
            foreach (var c in function.Body.Descendants.OfType <CallInstruction>())
            {
                for (int idx = 0; idx < c.Method.Parameters.Count; idx++)
                {
                    var p     = c.Method.Parameters[idx];
                    var value = c.Arguments[idx];

                    var argType = TypeInference.GetInstType(value);
                    var pType   = p.Type;

                    if (argType != null && argType != pType)
                    {
                        value.ReplaceWith(new CastClass(value.Clone(), pType));
                    }
                }
            }
        }
Exemplo n.º 4
0
        public Dictionary <long, EventRegistration[]> DecompileEventMappings(string fullTypeName, CancellationToken cancellationToken)
        {
            var            result = new Dictionary <long, EventRegistration[]>();
            TypeDefinition type   = this.assembly.MainModule.GetType(fullTypeName);

            if (type == null)
            {
                return(result);
            }

            MethodDefinition method = null;

            foreach (var m in type.Methods)
            {
                if (m.Name == "System.Windows.Markup.IComponentConnector.Connect")
                {
                    method = m;
                    break;
                }
            }

            if (method == null)
            {
                return(result);
            }

            // decompile method and optimize the switch
            var typeSystem = new DecompilerTypeSystem(method.Module);
            var ilReader   = new ILReader(typeSystem);
            var function   = ilReader.ReadIL(method.Body, cancellationToken);

            var context = new ILTransformContext(function, typeSystem)
            {
                CancellationToken = cancellationToken
            };

            function.RunTransforms(CSharpDecompiler.GetILTransforms(), context);

            var block    = function.Body.Children.OfType <Block>().First();
            var ilSwitch = block.Children.OfType <SwitchInstruction>().FirstOrDefault();

            if (ilSwitch != null)
            {
                foreach (var section in ilSwitch.Sections)
                {
                    var events = FindEvents(section.Body);
                    foreach (long id in section.Labels.Values)
                    {
                        result.Add(id, events);
                    }
                }
            }
            else
            {
                foreach (var ifInst in function.Descendants.OfType <IfInstruction>())
                {
                    var comp = ifInst.Condition as Comp;
                    if (comp.Kind != ComparisonKind.Inequality && comp.Kind != ComparisonKind.Equality)
                    {
                        continue;
                    }
                    int id;
                    if (!comp.Right.MatchLdcI4(out id))
                    {
                        continue;
                    }
                    var events = FindEvents(comp.Kind == ComparisonKind.Inequality ? ifInst.FalseInst : ifInst.TrueInst);
                    result.Add(id, events);
                }
            }
            return(result);
        }
Exemplo n.º 5
0
        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;
                }
                //  } catch exceptionVariable : 02000078 System.Object when (ldc.i4 1) BlockContainer {
                //      Block IL_004a (incoming: 1) {
                //          stloc objectVariable(ldloc exceptionVariable)
                //          br finallyBlock
                //      }
                //
                //  }
                // }
                //
                // Block finallyBlock (incoming: 2) {
                //  if (comp.o(ldloc b == ldnull)) br afterFinallyBlock
                //  br finallyBlockContinuation
                // }
                //
                // Block finallyBlockContinuation (incoming: 1) {
                //  await(addressof System.Threading.Tasks.ValueTask(callvirt DisposeAsync(ldloc b)))
                //  br afterFinallyBlock
                // }
                //
                // Block afterFinallyBlock (incoming: 2) {
                //  stloc V_1(ldloc objectVariable)
                //  if (comp.o(ldloc V_1 == ldnull)) br IL_00ea
                //  br IL_00cf
                // }

                // await in finally uses a single catch block with catch-type object
                if (tryCatch.Handlers.Count != 1)
                {
                    continue;
                }
                var handler           = tryCatch.Handlers[0];
                var exceptionVariable = handler.Variable;
                if (handler.Body is not BlockContainer catchBlockContainer)
                {
                    continue;
                }
                if (!exceptionVariable.Type.IsKnownType(KnownTypeCode.Object))
                {
                    continue;
                }
                // Matches the await finally pattern:
                // [stloc V_3(ldloc E_100)	- copy exception variable to a temporary]
                // stloc V_6(ldloc V_3)	- store exception in 'global' object variable
                // br IL_0075				- jump out of catch block to the head of the finallyBlock
                var        catchBlockEntry = catchBlockContainer.EntryPoint;
                ILVariable objectVariable;
                switch (catchBlockEntry.Instructions.Count)
                {
                case 2:
                    if (!catchBlockEntry.Instructions[0].MatchStLoc(out objectVariable, out var value))
Exemplo n.º 6
0
 public void Run(ILFunction function, ILTransformContext context)
 {
     cancellationToken = context.CancellationToken;
     currentExit       = NoExit;
     function.AcceptVisitor(this);
 }
Exemplo n.º 7
0
        public static void Run(ILFunction function, ILTransformContext context)
        {
            if (!context.Settings.AwaitInCatchFinally)
            {
                return;
            }
            HashSet <BlockContainer> changedContainers = new HashSet <BlockContainer>();
            HashSet <Block>          removedBlocks     = new HashSet <Block>();

            // 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);
                }
                SwitchInstruction switchInstructionOpt = null;
                foreach (var result in transformableCatchBlocks)
                {
                    removedBlocks.Clear();
                    var node = cfg.GetNode(result.RealCatchBlockEntryPoint);

                    context.StepStartGroup($"Inline catch block with await (at {result.Handler.Variable.Name})", result.Handler);

                    // Remove the IfInstruction from the jump table and eliminate all branches to the block.
                    switch (result.JumpTableEntry)
                    {
                    case IfInstruction jumpTableEntry:
                        var jumpTableBlock = (Block)jumpTableEntry.Parent;
                        context.Step("Remove jump-table entry", result.JumpTableEntry);
                        jumpTableBlock.Instructions.RemoveAt(result.JumpTableEntry.ChildIndex);

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

                    case SwitchSection jumpTableEntry:
                        Debug.Assert(switchInstructionOpt == null || jumpTableEntry.Parent == switchInstructionOpt);
                        switchInstructionOpt = (SwitchInstruction)jumpTableEntry.Parent;
                        break;
                    }

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

                    TransformAsyncThrowToThrow(context, removedBlocks, result.RealCatchBlockEntryPoint);

                    // Inline all blocks that are dominated by the entrypoint of the real catch block
                    foreach (var n in cfg.cfg)
                    {
                        Block block = (Block)n.UserData;

                        if (node.Dominates(n))
                        {
                            TransformAsyncThrowToThrow(context, removedBlocks, block);

                            if (block.Parent == result.Handler.Body)
                            {
                                continue;
                            }

                            if (!removedBlocks.Contains(block))
                            {
                                context.Step("Move block", result.Handler.Body);
                                MoveBlock(block, (BlockContainer)result.Handler.Body);
                            }
                        }
                    }

                    // Remove unreachable pattern blocks
                    // TODO : sanity check
                    if (result.NextBlockOrExitContainer is Block nextBlock && nextBlock.IncomingEdgeCount == 0)
                    {
                        List <Block> dependentBlocks = new List <Block>();
                        Block        current         = nextBlock;

                        do
                        {
                            foreach (var branch in current.Descendants.OfType <Branch>())
                            {
                                dependentBlocks.Add(branch.TargetBlock);
                            }

                            current.Remove();
                            dependentBlocks.Remove(current);
                            current = dependentBlocks.FirstOrDefault(b => b.IncomingEdgeCount == 0);
                        } while (current != null);
                    }

                    // Remove all assignments to the common object variable that stores the exception object.
                    if (result.ObjectVariable != result.Handler.Variable)
                    {
                        foreach (var load in result.ObjectVariable.LoadInstructions.ToArray())
                        {
                            if (!load.IsDescendantOf(result.Handler))
                            {
                                continue;
                            }

                            if (load.Parent is CastClass cc && cc.Type.Equals(result.Handler.Variable.Type))
                            {
                                cc.ReplaceWith(new LdLoc(result.Handler.Variable).WithILRange(cc).WithILRange(load));
                            }
                            else
                            {
                                load.ReplaceWith(new LdLoc(result.Handler.Variable).WithILRange(load));
                            }
                        }
                    }

                    context.StepEndGroup(keepIfEmpty: true);
                }
Exemplo n.º 8
0
        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;
                }
                // await in finally uses a single catch block with catch-type object
                if (tryCatch.Handlers.Count != 1 || !(tryCatch.Handlers[0].Body is BlockContainer catchBlockContainer) || !tryCatch.Handlers[0].Variable.Type.IsKnownType(KnownTypeCode.Object))
                {
                    continue;
                }
                // and consists of an assignment to a temporary that is used outside the catch block
                // and a jump to the finally block
                var block = catchBlockContainer.EntryPoint;
                if (block.Instructions.Count < 2 || !block.Instructions[0].MatchStLoc(out var globalCopyVar, out var value) || !value.MatchLdLoc(tryCatch.Handlers[0].Variable))
                {
                    continue;
                }
                if (block.Instructions.Count == 3)
                {
                    if (!block.Instructions[1].MatchStLoc(out var globalCopyVarTemp, out value) || !value.MatchLdLoc(globalCopyVar))
                    {
                        continue;
                    }
                    globalCopyVar = globalCopyVarTemp;
                }
                if (!block.Instructions[block.Instructions.Count - 1].MatchBranch(out var entryPointOfFinally))
                {
                    continue;
                }
                // globalCopyVar should only be used once, at the end of the finally-block
                if (globalCopyVar.LoadCount != 1 || globalCopyVar.StoreCount > 2)
                {
                    continue;
                }
                var tempStore = globalCopyVar.LoadInstructions[0].Parent as StLoc;
                if (tempStore == null || !MatchExceptionCaptureBlock(tempStore, out var exitOfFinally, out var afterFinally, out var blocksToRemove))
                {
                    continue;
                }
                if (!MatchAfterFinallyBlock(ref afterFinally, blocksToRemove, out bool removeFirstInstructionInAfterFinally))
                {
                    continue;
                }
                var cfg = new ControlFlowGraph(container, context.CancellationToken);
                var exitOfFinallyNode       = cfg.GetNode(exitOfFinally);
                var entryPointOfFinallyNode = cfg.GetNode(entryPointOfFinally);

                var additionalBlocksInFinally = new HashSet <Block>();
                var invalidExits = new List <ControlFlowNode>();

                TraverseDominatorTree(entryPointOfFinallyNode);

                void TraverseDominatorTree(ControlFlowNode node)
                {
                    if (entryPointOfFinallyNode != node)
                    {
                        if (entryPointOfFinallyNode.Dominates(node))
                        {
                            additionalBlocksInFinally.Add((Block)node.UserData);
                        }
                        else
                        {
                            invalidExits.Add(node);
                        }
                    }

                    if (node == exitOfFinallyNode)
                    {
                        return;
                    }

                    foreach (var child in node.DominatorTreeChildren)
                    {
                        TraverseDominatorTree(child);
                    }
                }

                if (invalidExits.Any())
                {
                    continue;
                }

                context.Step("Inline finally block with await", tryCatch.Handlers[0]);

                foreach (var blockToRemove in blocksToRemove)
                {
                    blockToRemove.Remove();
                }
                var finallyContainer = new BlockContainer();
                entryPointOfFinally.Remove();
                if (removeFirstInstructionInAfterFinally)
                {
                    afterFinally.Instructions.RemoveAt(0);
                }
                changedContainers.Add(container);
                var outer = BlockContainer.FindClosestContainer(container.Parent);
                if (outer != null)
                {
                    changedContainers.Add(outer);
                }
                finallyContainer.Blocks.Add(entryPointOfFinally);
                finallyContainer.AddILRange(entryPointOfFinally);
                exitOfFinally.Instructions.RemoveRange(tempStore.ChildIndex, 3);
                exitOfFinally.Instructions.Add(new Leave(finallyContainer));
                foreach (var branchToFinally in container.Descendants.OfType <Branch>())
                {
                    if (branchToFinally.TargetBlock == entryPointOfFinally)
                    {
                        branchToFinally.ReplaceWith(new Branch(afterFinally));
                    }
                }
                foreach (var newBlock in additionalBlocksInFinally)
                {
                    newBlock.Remove();
                    finallyContainer.Blocks.Add(newBlock);
                    finallyContainer.AddILRange(newBlock);
                }
                tryCatch.ReplaceWith(new TryFinally(tryCatch.TryBlock, finallyContainer).WithILRange(tryCatch.TryBlock));
            }

            // clean up all modified containers
            foreach (var container in changedContainers)
            {
                container.SortBlocks(deleteUnreachableBlocks: true);
            }
        }
Exemplo n.º 9
0
 public void Run(ILFunction function, ILTransformContext context)
 {
     this.context = context;
     Visit((BlockContainer)function.Body, null);
 }