Example #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();
 }
Example #2
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);
 }
Example #3
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);
        }
Example #4
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);
        }
Example #5
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;
            }

            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);
            }
        }
Example #6
0
        public bool MatchLeave(BlockContainer targetContainer)
        {
            var inst = this as Leave;

            return(inst != null && inst.TargetContainer == targetContainer && inst.Value.MatchNop());
        }
Example #7
0
        /// <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);
            }

            // 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);
        }
Example #8
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.AddILRange(new Interval(eh.HandlerOffset, eh.HandlerOffset + eh.HandlerLength));
                handlerBlock.Blocks.Add(new Block());
                handlerContainers.Add(handlerBlock.StartILOffset, handlerBlock);

                if (eh.Kind == ExceptionRegionKind.Fault || eh.Kind == ExceptionRegionKind.Finally)
                {
                    var tryBlock = new BlockContainer();
                    tryBlock.AddILRange(tryRange);
                    if (eh.Kind == ExceptionRegionKind.Finally)
                    {
                        tryInstructionList.Add(new TryFinally(tryBlock, handlerBlock).WithILRange(tryRange));
                    }
                    else
                    {
                        tryInstructionList.Add(new TryFault(tryBlock, handlerBlock).WithILRange(tryRange));
                    }
                    continue;
                }
                //
                var tryCatch = tryCatchList.FirstOrDefault(tc => tc.TryBlock.ILRanges.SingleOrDefault() == tryRange);
                if (tryCatch == null)
                {
                    var tryBlock = new BlockContainer();
                    tryBlock.AddILRange(tryRange);
                    tryCatch = new TryCatch(tryBlock);
                    tryCatch.AddILRange(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.AddILRange(new Interval(eh.FilterOffset, eh.HandlerOffset));
                    filterBlock.Blocks.Add(new Block());
                    handlerContainers.Add(filterBlock.StartILOffset, filterBlock);
                    filter = filterBlock;
                }
                else
                {
                    filter = new LdcI4(1);
                }

                var handler = new TryCatchHandler(filter, handlerBlock, variableByExceptionHandler[eh]);
                handler.AddILRange(filter);
                handler.AddILRange(handlerBlock);
                tryCatch.Handlers.Add(handler);
                tryCatch.AddILRange(handler);
            }
            if (tryInstructionList.Count > 0)
            {
                tryInstructionList = tryInstructionList.OrderBy(tc => tc.TryBlock.StartILOffset).ThenByDescending(tc => tc.TryBlock.EndILOffset).ToList();
                nextTry            = tryInstructionList[0];
            }
        }
Example #9
0
        public void CreateBlocks(BlockContainer mainContainer, List <ILInstruction> instructions, BitArray incomingBranches, CancellationToken cancellationToken)
        {
            CreateContainerStructure();
            mainContainer.SetILRange(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.StartILOffset;
                if (currentBlock == null || (incomingBranches[start] && !IsStackAdjustment(inst)))
                {
                    // Finish up the previous block
                    FinalizeCurrentBlock(start, fallthrough: true);
                    // Leave nested containers if necessary
                    while (start >= currentContainer.EndILOffset)
                    {
                        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.EndILOffset)
                        {
                            Debug.Assert(currentBlock.HasILRange);
                            currentBlock.AddILRange(new Interval(currentBlock.StartILOffset, 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.SetILRange(new Interval(start, start));
                }
                while (nextTry != null && start == nextTry.TryBlock.StartILOffset)
                {
                    currentBlock.Instructions.Add(nextTry);
                    containerStack.Push(currentContainer);
                    currentContainer = (BlockContainer)nextTry.TryBlock;
                    currentBlock     = new Block();
                    currentContainer.Blocks.Add(currentBlock);
                    currentBlock.SetILRange(new Interval(start, start));

                    nextTry = tryInstructionList.ElementAtOrDefault(++currentTryIndex);
                }
                currentBlock.Instructions.Add(inst);
                if (inst.HasFlag(InstructionFlags.EndPointUnreachable))
                {
                    FinalizeCurrentBlock(inst.EndILOffset, fallthrough: false);
                }
                else if (!CreateExtendedBlocks && inst.HasFlag(InstructionFlags.MayBranch))
                {
                    FinalizeCurrentBlock(inst.EndILOffset, fallthrough: true);
                }
            }
            FinalizeCurrentBlock(mainContainer.EndILOffset, fallthrough: false);
            // Finish up all containers
            while (containerStack.Count > 0)
            {
                currentContainer = containerStack.Pop();
                currentBlock     = currentContainer.Blocks.Last();
                FinalizeCurrentBlock(mainContainer.EndILOffset, fallthrough: false);
            }
            ConnectBranches(mainContainer, cancellationToken);
        }