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(); }
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); }
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); }
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); }
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); } }
public bool MatchLeave(BlockContainer targetContainer) { var inst = this as Leave; return(inst != null && inst.TargetContainer == targetContainer && inst.Value.MatchNop()); }
/// <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); }
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]; } }
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); }