Ejemplo n.º 1
0
 public Leave(BlockContainer targetContainer, ILInstruction value = null) : base(OpCode.Leave)
 {
     // Note: ILReader will create Leave instructions with targetContainer==null to represent 'endfinally',
     // the targetContainer will then be filled in by BlockBuilder
     this.targetContainer = targetContainer;
     this.Value           = value ?? new Nop();
 }
Ejemplo n.º 2
0
        void CreateContainerStructure()
        {
            List <TryCatch> tryCatchList = new List <TryCatch>();

            foreach (var eh in body.ExceptionHandlers)
            {
                var tryRange     = new Interval(eh.TryStart.Offset, eh.TryEnd != null ? eh.TryEnd.Offset : body.CodeSize);
                var handlerBlock = new BlockContainer();
                handlerBlock.ILRange = new Interval(eh.HandlerStart.Offset, eh.HandlerEnd != null ? eh.HandlerEnd.Offset : body.CodeSize);
                handlerBlock.Blocks.Add(new Block());
                handlerContainers.Add(handlerBlock.ILRange.Start, handlerBlock);

                if (eh.HandlerType == Mono.Cecil.Cil.ExceptionHandlerType.Fault || eh.HandlerType == Mono.Cecil.Cil.ExceptionHandlerType.Finally)
                {
                    var tryBlock = new BlockContainer();
                    tryBlock.ILRange = tryRange;
                    if (eh.HandlerType == Mono.Cecil.Cil.ExceptionHandlerType.Finally)
                    {
                        tryInstructionList.Add(new TryFinally(tryBlock, handlerBlock));
                    }
                    else
                    {
                        tryInstructionList.Add(new TryFault(tryBlock, handlerBlock));
                    }
                    continue;
                }
                //
                var tryCatch = tryCatchList.FirstOrDefault(tc => tc.TryBlock.ILRange == tryRange);
                if (tryCatch == null)
                {
                    var tryBlock = new BlockContainer();
                    tryBlock.ILRange = tryRange;
                    tryCatch         = new TryCatch(tryBlock);
                    tryCatchList.Add(tryCatch);
                    tryInstructionList.Add(tryCatch);
                }

                ILInstruction filter;
                if (eh.HandlerType == Mono.Cecil.Cil.ExceptionHandlerType.Filter)
                {
                    var filterBlock = new BlockContainer(expectedResultType: StackType.I4);
                    filterBlock.ILRange = new Interval(eh.FilterStart.Offset, eh.HandlerStart.Offset);
                    filterBlock.Blocks.Add(new Block());
                    handlerContainers.Add(filterBlock.ILRange.Start, filterBlock);
                    filter = filterBlock;
                }
                else
                {
                    filter = new LdcI4(1);
                }

                tryCatch.Handlers.Add(new TryCatchHandler(filter, handlerBlock, variableByExceptionHandler[eh]));
            }
            if (tryInstructionList.Count > 0)
            {
                tryInstructionList = tryInstructionList.OrderBy(tc => tc.TryBlock.ILRange.Start).ThenByDescending(tc => tc.TryBlock.ILRange.End).ToList();
                nextTry            = tryInstructionList[0];
            }
        }
Ejemplo n.º 3
0
 internal static bool GetExecutesFinallyBlock(ILInstruction inst, BlockContainer container)
 {
     for (; inst != container; inst = inst.Parent)
     {
         if (inst.Parent is TryFinally && inst.SlotInfo == TryFinally.TryBlockSlot)
         {
             return(true);
         }
     }
     return(false);
 }
Ejemplo n.º 4
0
        public bool MatchLeave(out BlockContainer targetContainer)
        {
            var inst = this as Leave;

            if (inst != null && inst.Value.MatchNop())
            {
                targetContainer = inst.TargetContainer;
                return(true);
            }
            targetContainer = null;
            return(false);
        }
Ejemplo n.º 5
0
        public bool MatchLeave(BlockContainer targetContainer, out ILInstruction value)
        {
            var inst = this as Leave;

            if (inst != null && targetContainer == inst.TargetContainer)
            {
                value = inst.Value;
                return(true);
            }
            value = null;
            return(false);
        }
Ejemplo n.º 6
0
        private void Visit(BlockContainer container, Block continueTarget)
        {
            switch (container.Kind)
            {
            case ContainerKind.Loop:
            case ContainerKind.While:
                continueTarget = container.EntryPoint;
                break;

            case ContainerKind.DoWhile:
            case ContainerKind.For:
                continueTarget = container.Blocks.Last();
                break;
            }

            foreach (var block in container.Blocks)
            {
                Visit(block, continueTarget);
            }
        }
Ejemplo n.º 7
0
 private static Block FindDoWhileConditionBlock(BlockContainer container, List <ILInstruction> conditions)
 {
     foreach (var b in container.Blocks)
     {
         if (b.Instructions.Last().MatchBranch(container.EntryPoint))
         {
             // potentially the do-while-condition block
             int i = b.Instructions.Count - 2;
             while (i >= 0 && b.Instructions[i] is IfInstruction ifInst &&
                    ifInst.TrueInst.MatchLeave(container) && ifInst.FalseInst.MatchNop())
             {
                 conditions.Add(ifInst.Condition);
                 i--;
             }
             if (i == -1 && conditions.Any())
             {
                 return(b);
             }
         }
     }
     return(null);
 }
Ejemplo n.º 8
0
        public override ILInstruction Clone()
        {
            BlockContainer clone = new BlockContainer();

            clone.ILRange = this.ILRange;
            clone.Blocks.AddRange(this.Blocks.Select(block => (Block)block.Clone()));
            // Adjust branch instructions to point to the new container
            foreach (var branch in clone.Descendants.OfType <Branch>())
            {
                if (branch.TargetBlock != null && branch.TargetBlock.Parent == this)
                {
                    branch.TargetBlock = clone.Blocks[branch.TargetBlock.ChildIndex];
                }
            }
            foreach (var leave in clone.Descendants.OfType <Leave>())
            {
                if (leave.TargetContainer == this)
                {
                    leave.TargetContainer = clone;
                }
            }
            return(clone);
        }
        private void Visit(BlockContainer container, Block continueTarget)
        {
            switch (container.Kind)
            {
            case ContainerKind.Loop:
            case ContainerKind.While:
                continueTarget = container.EntryPoint;
                break;

            case ContainerKind.DoWhile:
            case ContainerKind.For:
                continueTarget = container.Blocks.Last();
                break;
            }

            for (int i = 0; i < container.Blocks.Count; i++)
            {
                var block = container.Blocks[i];
                // Note: it's possible for additional blocks to be appended to the container
                // by the Visit() call; but there should be no other changes to the Blocks collection.
                Visit(block, continueTarget);
                Debug.Assert(container.Blocks[i] == block);
            }
        }
Ejemplo n.º 10
0
        void CreateContainerStructure()
        {
            List <TryCatch> tryCatchList = new List <TryCatch>();

            foreach (var eh in body.ExceptionRegions)
            {
                var tryRange     = new Interval(eh.TryOffset, eh.TryOffset + eh.TryLength);
                var handlerBlock = new BlockContainer();
                handlerBlock.ILRange = new Interval(eh.HandlerOffset, eh.HandlerOffset + eh.HandlerLength);
                handlerBlock.Blocks.Add(new Block());
                handlerContainers.Add(handlerBlock.ILRange.Start, handlerBlock);

                if (eh.Kind == ExceptionRegionKind.Fault || eh.Kind == ExceptionRegionKind.Finally)
                {
                    var tryBlock = new BlockContainer();
                    tryBlock.ILRange = tryRange;
                    if (eh.Kind == ExceptionRegionKind.Finally)
                    {
                        tryInstructionList.Add(new TryFinally(tryBlock, handlerBlock)
                        {
                            ILRange = tryRange
                        });
                    }
                    else
                    {
                        tryInstructionList.Add(new TryFault(tryBlock, handlerBlock)
                        {
                            ILRange = tryRange
                        });
                    }
                    continue;
                }
                //
                var tryCatch = tryCatchList.FirstOrDefault(tc => tc.TryBlock.ILRange == tryRange);
                if (tryCatch == null)
                {
                    var tryBlock = new BlockContainer();
                    tryBlock.ILRange = tryRange;
                    tryCatch         = new TryCatch(tryBlock);
                    tryCatch.ILRange = tryRange;
                    tryCatchList.Add(tryCatch);
                    tryInstructionList.Add(tryCatch);
                }

                ILInstruction filter;
                if (eh.Kind == System.Reflection.Metadata.ExceptionRegionKind.Filter)
                {
                    var filterBlock = new BlockContainer(expectedResultType: StackType.I4);
                    filterBlock.ILRange = new Interval(eh.FilterOffset, eh.HandlerOffset);
                    filterBlock.Blocks.Add(new Block());
                    handlerContainers.Add(filterBlock.ILRange.Start, filterBlock);
                    filter = filterBlock;
                }
                else
                {
                    filter = new LdcI4(1);
                }

                var handler = new TryCatchHandler(filter, handlerBlock, variableByExceptionHandler[eh]);
                handler.AddILRange(filter.ILRange);
                handler.AddILRange(handlerBlock.ILRange);
                tryCatch.Handlers.Add(handler);
                tryCatch.AddILRange(handler.ILRange);
            }
            if (tryInstructionList.Count > 0)
            {
                tryInstructionList = tryInstructionList.OrderBy(tc => tc.TryBlock.ILRange.Start).ThenByDescending(tc => tc.TryBlock.ILRange.End).ToList();
                nextTry            = tryInstructionList[0];
            }
        }
Ejemplo n.º 11
0
        public void CreateBlocks(BlockContainer mainContainer, List <ILInstruction> instructions, BitArray incomingBranches, CancellationToken cancellationToken)
        {
            CreateContainerStructure();
            mainContainer.ILRange = new Interval(0, body.GetCodeSize());
            currentContainer      = mainContainer;
            if (instructions.Count == 0)
            {
                currentContainer.Blocks.Add(new Block {
                    Instructions =
                    {
                        new InvalidBranch("Empty body found. Decompiled assembly might be a reference assembly.")
                    }
                });
                return;
            }

            foreach (var inst in instructions)
            {
                cancellationToken.ThrowIfCancellationRequested();
                int start = inst.ILRange.Start;
                if (currentBlock == null || (incomingBranches[start] && !IsStackAdjustment(inst)))
                {
                    // Finish up the previous block
                    FinalizeCurrentBlock(start, fallthrough: true);
                    // Leave nested containers if necessary
                    while (start >= currentContainer.ILRange.End)
                    {
                        currentContainer = containerStack.Pop();
                        currentBlock     = currentContainer.Blocks.Last();
                        // this container is skipped (i.e. the loop will execute again)
                        // set ILRange to the last instruction offset inside the block.
                        if (start >= currentContainer.ILRange.End)
                        {
                            Debug.Assert(currentBlock.ILRange.IsEmpty);
                            currentBlock.ILRange = new Interval(currentBlock.ILRange.Start, start);
                        }
                    }
                    // Enter a handler if necessary
                    BlockContainer handlerContainer;
                    if (handlerContainers.TryGetValue(start, out handlerContainer))
                    {
                        containerStack.Push(currentContainer);
                        currentContainer = handlerContainer;
                        currentBlock     = handlerContainer.EntryPoint;
                    }
                    else
                    {
                        FinalizeCurrentBlock(start, fallthrough: false);
                        // Create the new block
                        currentBlock = new Block();
                        currentContainer.Blocks.Add(currentBlock);
                    }
                    currentBlock.ILRange = new Interval(start, start);
                }
                while (nextTry != null && start == nextTry.TryBlock.ILRange.Start)
                {
                    currentBlock.Instructions.Add(nextTry);
                    containerStack.Push(currentContainer);
                    currentContainer = (BlockContainer)nextTry.TryBlock;
                    currentBlock     = new Block();
                    currentContainer.Blocks.Add(currentBlock);
                    currentBlock.ILRange = new Interval(start, start);

                    nextTry = tryInstructionList.ElementAtOrDefault(++currentTryIndex);
                }
                currentBlock.Instructions.Add(inst);
                if (inst.HasFlag(InstructionFlags.EndPointUnreachable))
                {
                    FinalizeCurrentBlock(inst.ILRange.End, fallthrough: false);
                }
                else if (!CreateExtendedBlocks && inst.HasFlag(InstructionFlags.MayBranch))
                {
                    FinalizeCurrentBlock(inst.ILRange.End, fallthrough: true);
                }
            }
            FinalizeCurrentBlock(mainContainer.ILRange.End, fallthrough: false);
            // Finish up all containers
            while (containerStack.Count > 0)
            {
                currentContainer = containerStack.Pop();
                currentBlock     = currentContainer.Blocks.Last();
                FinalizeCurrentBlock(mainContainer.ILRange.End, fallthrough: false);
            }
            ConnectBranches(mainContainer, cancellationToken);
        }
        /// <summary>
        /// Reduce Nesting in switch statements by replacing break; in cases with the block exit, and extracting the default case
        /// Does not affect IL order
        /// </summary>
        private bool ReduceSwitchNesting(Block parentBlock, BlockContainer switchContainer, ILInstruction exitInst)
        {
            // break; from outer container cannot be brought inside the switch as the meaning would change
            if (exitInst is Leave leave && !leave.IsLeavingFunction)
            {
                return(false);
            }

            // find the default section, and ensure it has only one incoming edge
            var switchInst     = (SwitchInstruction)switchContainer.EntryPoint.Instructions.Single();
            var defaultSection = switchInst.Sections.MaxBy(s => s.Labels.Count());

            if (!defaultSection.Body.MatchBranch(out var defaultBlock) || defaultBlock.IncomingEdgeCount != 1)
            {
                return(false);
            }
            if (defaultBlock.Parent != switchContainer)
            {
                return(false);
            }

            // tally stats for heuristic from each case block
            int maxStatements = 0, maxDepth = 0;

            foreach (var section in switchInst.Sections)
            {
                if (section != defaultSection && section.Body.MatchBranch(out var caseBlock) && caseBlock.Parent == switchContainer)
                {
                    UpdateStats(caseBlock, ref maxStatements, ref maxDepth);
                }
            }

            if (!ShouldReduceNesting(defaultBlock, maxStatements, maxDepth))
            {
                return(false);
            }

            Debug.Assert(defaultBlock.HasFlag(InstructionFlags.EndPointUnreachable));

            // ensure the default case dominator tree has no exits (branches to other cases)
            var cfg         = new ControlFlowGraph(switchContainer, context.CancellationToken);
            var defaultNode = cfg.GetNode(defaultBlock);
            var defaultTree = TreeTraversal.PreOrder(defaultNode, n => n.DominatorTreeChildren).ToList();

            if (defaultTree.SelectMany(n => n.Successors).Any(n => !defaultNode.Dominates(n)))
            {
                return(false);
            }

            if (defaultTree.Count > 1 && !(parentBlock.Parent is BlockContainer))
            {
                return(false);
            }

            context.Step("Extract default case of switch", switchContainer);

            // replace all break; statements with the exitInst
            var leaveInstructions = switchContainer.Descendants.Where(inst => inst.MatchLeave(switchContainer));

            foreach (var leaveInst in leaveInstructions.ToArray())
            {
                leaveInst.ReplaceWith(exitInst.Clone());
            }

            // replace the default section branch with a break;
            defaultSection.Body.ReplaceWith(new Leave(switchContainer));

            // remove all default blocks from the switch container
            var defaultBlocks = defaultTree.Select(c => (Block)c.UserData).ToList();

            foreach (var block in defaultBlocks)
            {
                switchContainer.Blocks.Remove(block);
            }

            // replace the parent block exit with the default case instructions
            if (parentBlock.Instructions.Last() == exitInst)
            {
                parentBlock.Instructions.RemoveLast();
            }
            // Note: even though we don't check that the switchContainer is near the end of the block,
            // we know this must be the case because we know "exitInst" is a leave/branch and directly
            // follows the switchContainer.
            Debug.Assert(parentBlock.Instructions.Last() == switchContainer);
            parentBlock.Instructions.AddRange(defaultBlock.Instructions);

            // add any additional blocks from the default case to the parent container
            Debug.Assert(defaultBlocks[0] == defaultBlock);
            if (defaultBlocks.Count > 1)
            {
                var parentContainer = (BlockContainer)parentBlock.Parent;
                int insertAt        = parentContainer.Blocks.IndexOf(parentBlock) + 1;
                foreach (var block in defaultBlocks.Skip(1))
                {
                    parentContainer.Blocks.Insert(insertAt++, block);
                }
            }

            return(true);
        }
Ejemplo n.º 13
0
        public bool MatchLeave(BlockContainer targetContainer)
        {
            var inst = this as Leave;

            return(inst != null && inst.TargetContainer == targetContainer && inst.Value.MatchNop());
        }
Ejemplo n.º 14
0
 public static DetectedLoop DetectLoop(BlockContainer container)
 {
     return(new DetectedLoop(container).DetectLoopInternal());
 }
Ejemplo n.º 15
0
        }                                                               // null, except in case of For

        private DetectedLoop(BlockContainer container)
        {
            this.Container = container;
        }