private void DriveRemoveHalfNodes() { var classRef = new RemHalfNodes<int>(); var treeBuilder = new TreeBuilder(); var treeTraversal = new TreeTraversal<int>(); var root = treeBuilder.BootStrapTree5(); var newRoot = classRef.RemoveNodes(ref root); treeTraversal.PreOrder(newRoot); Console.WriteLine(); root = treeBuilder.BootStrapTree1(); newRoot = classRef.RemoveNodes(ref root); treeTraversal.PreOrder(newRoot); Console.WriteLine(); root = treeBuilder.BootStrapTree2(); newRoot = classRef.RemoveNodes(ref root); treeTraversal.PreOrder(newRoot); Console.WriteLine(); root = treeBuilder.BootStrapTree3(); newRoot = classRef.RemoveNodes(ref root); treeTraversal.PreOrder(newRoot); Console.WriteLine(); }
private IEnumerable <T> FindReferencesInTypeScope(CancellationToken ct) { foreach (TypeDef type in TreeTraversal.PreOrder(typeScope, t => t.NestedTypes)) { ct.ThrowIfCancellationRequested(); foreach (var result in typeAnalysisFunction(type)) { ct.ThrowIfCancellationRequested(); yield return(result); } } }
private IEnumerable <T> FindReferencesInAssembly(AssemblyDef asm, CancellationToken ct) { foreach (TypeDef type in TreeTraversal.PreOrder(asm.Modules.SelectMany(m => m.Types), t => t.NestedTypes)) { ct.ThrowIfCancellationRequested(); foreach (var result in typeAnalysisFunction(type)) { ct.ThrowIfCancellationRequested(); yield return(result); } } }
/// <summary> /// Given a natural loop, add additional CFG nodes to the loop in order /// to reduce the number of exit points out of the loop. /// We do this because C# only allows reaching a single exit point (with 'break' /// statements or when the loop condition evaluates to false), so we'd have /// to introduce 'goto' statements for any additional exit points. /// </summary> /// <remarks> /// Definition: /// A "reachable exit" is a branch/leave target that is reachable from the loop, /// but not dominated by the loop head. A reachable exit may or may not have a /// corresponding CFG node (depending on whether it is a block in the current block container). /// -> reachable exits are leaving the code region dominated by the loop /// /// Definition: /// A loop "exit point" is a CFG node that is not itself part of the loop, /// but has at least one predecessor which is part of the loop. /// -> exit points are leaving the loop itself /// /// Nodes can only be added to the loop if they are dominated by the loop head. /// When adding a node to the loop, we must also add all of that node's predecessors /// to the loop. (this ensures that the loop keeps its single entry point) /// /// Goal: If possible, find a set of nodes that can be added to the loop so that there /// remains only a single exit point. /// Add as little code as possible to the loop to reach this goal. /// /// This means we need to partition the set of nodes dominated by the loop entry point /// into two sets (in-loop and out-of-loop). /// Constraints: /// * the loop head itself is in-loop /// * there must not be any edge from an out-of-loop node to an in-loop node /// -> all predecessors of in-loop nodes are also in-loop /// -> all nodes in a cycle are part of the same partition /// Optimize: /// * use only a single exit point if at all possible /// * minimize the amount of code in the in-loop partition /// (thus: maximize the amount of code in the out-of-loop partition) /// "amount of code" could be measured as: /// * number of basic blocks /// * number of instructions directly in those basic blocks (~= number of statements) /// * number of instructions in those basic blocks (~= number of expressions) /// (we currently use the number of statements) /// /// Observations: /// * If a node is in-loop, so are all its ancestors in the dominator tree (up to the loop entry point) /// * If there are no exits reachable from a node (i.e. all paths from that node lead to a return/throw instruction), /// it is valid to put the group of nodes dominated by that node into either partition independently of /// any other nodes except for the ancestors in the dominator tree. /// (exception: the loop head itself must always be in-loop) /// /// There are two different cases we need to consider: /// 1) There are no exits reachable at all from the loop head. /// -> it is possible to create a loop with zero exit points by adding all nodes /// dominated by the loop to the loop. /// -> the only way to exit the loop is by "return;" or "throw;" /// 2) There are some exits reachable from the loop head. /// /// In case 1, we can pick a single exit point freely by picking any node that has no reachable exits /// (other than the loop head). /// All nodes dominated by the exit point are out-of-loop, all other nodes are in-loop. /// See PickExitPoint() for the heuristic that picks the exit point in this case. /// /// In case 2, we need to pick our exit point so that all paths from the loop head /// to the reachable exits run through that exit point. /// /// This is a form of postdominance where the reachable exits are considered exit nodes, /// while "return;" or "throw;" instructions are not considered exit nodes. /// /// Using this form of postdominance, we are looking for an exit point that post-dominates all nodes in the natural loop. /// --> a common ancestor in post-dominator tree. /// To minimize the amount of code in-loop, we pick the lowest common ancestor. /// All nodes dominated by the exit point are out-of-loop, all other nodes are in-loop. /// (using normal dominance as in case 1, not post-dominance!) /// /// If it is impossible to use a single exit point for the loop, the lowest common ancestor will be the fake "exit node" /// used by the post-dominance analysis. In this case, we fall back to the old heuristic algorithm. /// /// Requires and maintains the invariant that a node is marked as visited iff it is contained in the loop. /// </remarks> void ExtendLoop(ControlFlowNode loopHead, List <ControlFlowNode> loop, out ControlFlowNode exitPoint) { exitPoint = FindExitPoint(loopHead, loop); Debug.Assert(!loop.Contains(exitPoint), "Cannot pick an exit point that is part of the natural loop"); if (exitPoint != null) { // Either we are in case 1 and just picked an exit that maximizes the amount of code // outside the loop, or we are in case 2 and found an exit point via post-dominance. // Note that if exitPoint == NoExitPoint, we end up adding all dominated blocks to the loop. var ep = exitPoint; foreach (var node in TreeTraversal.PreOrder(loopHead, n => DominatorTreeChildren(n, ep))) { if (!node.Visited) { node.Visited = true; loop.Add(node); } } // The loop/switch can only be entered through the entry point. if (isSwitch) { // In the case of a switch, false positives in the "continue;" detection logic // can lead to falsely excludes some blocks from the body. // Fix that by including all predecessors of included blocks. Debug.Assert(loop[0] == loopHead); for (int i = 1; i < loop.Count; i++) { foreach (var p in loop[i].Predecessors) { if (!p.Visited) { p.Visited = true; loop.Add(p); } } } } Debug.Assert(loop.All(n => n == loopHead || n.Predecessors.All(p => p.Visited))); } else { // We are in case 2, but could not find a suitable exit point. // Heuristically try to minimize the number of exit points // (but we'll always end up with more than 1 exit and will require goto statements). ExtendLoopHeuristic(loopHead, loop, loopHead); } }
private IEnumerable <BonsaiNode> LowerPriorityAbortables(BonsaiNode aborter) { GetCompositeParent(aborter, out BonsaiNode parent, out BonsaiNode directChild); if (parent != null) { parent.SortChildren(); int abortIndex = parent.IndexOf(directChild); if (abortIndex >= 0) { return(Enumerable .Range(0, parent.ChildCount()) .Where(i => i > abortIndex) .SelectMany(i => TreeTraversal.PreOrder(parent.GetChildAt(i)))); } } return(Enumerable.Empty <BonsaiNode>()); }
/// <summary> /// Gets all base type definitions. /// </summary> /// <remarks> /// This is equivalent to type.GetAllBaseTypes().Select(t => t.GetDefinition()).Where(d => d != null).Distinct(). /// </remarks> public static IEnumerable<ITypeDefinition> GetAllBaseTypeDefinitions(this IType type, ITypeResolveContext context) { if (type == null) throw new ArgumentNullException("type"); if (context == null) throw new ArgumentNullException("context"); HashSet<ITypeDefinition> typeDefinitions = new HashSet<ITypeDefinition>(); Func<ITypeDefinition, IEnumerable<ITypeDefinition>> recursion = t => t.GetBaseTypes(context).Select(b => b.GetDefinition()).Where(d => d != null && typeDefinitions.Add(d)); ITypeDefinition typeDef = type as ITypeDefinition; if (typeDef != null) { typeDefinitions.Add(typeDef); return TreeTraversal.PreOrder(typeDef, recursion); } else { return TreeTraversal.PreOrder( type.GetBaseTypes(context).Select(b => b.GetDefinition()).Where(d => d != null && typeDefinitions.Add(d)), recursion); } }
public IEnumerable <ITypeDefinition> GetTypesInScope(CancellationToken ct) { if (IsLocal) { foreach (var type in TreeTraversal.PreOrder(typeScope, t => t.NestedTypes)) { yield return(type); } } else { foreach (var module in GetModulesInScope(ct)) { var typeSystem = ConstructTypeSystem(module); foreach (var type in typeSystem.MainModule.TypeDefinitions) { yield return(type); } } } }
public IEnumerable <ITypeDefinition> GetTypesInScope(CancellationToken ct) { if (IsLocal) { var typeSystem = new DecompilerTypeSystem(TypeScope.ParentModule.PEFile, TypeScope.ParentModule.PEFile.GetAssemblyResolver()); foreach (var type in TreeTraversal.PreOrder(typeScope, t => t.NestedTypes)) { yield return(type); } } else { foreach (var module in GetModulesInScope(ct)) { var typeSystem = new DecompilerTypeSystem(module, module.GetAssemblyResolver()); foreach (var type in typeSystem.MainModule.TypeDefinitions) { yield return(type); } } } }
IEnumerable <SharpTreeNode> FindReferences(LoadedAssembly asm, CancellationToken ct) { string asmName = asm.AssemblyDefinition.Name.Name; string name = analyzedProperty.Name; string declTypeName = analyzedProperty.DeclaringType.FullName; foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.AssemblyDefinition.MainModule.Types, t => t.NestedTypes)) { ct.ThrowIfCancellationRequested(); SharpTreeNode newNode = null; try { if (!TypesHierarchyHelpers.IsBaseType(analyzedProperty.DeclaringType, type, resolveTypeArguments: false)) { continue; } foreach (PropertyDefinition property in type.Properties) { ct.ThrowIfCancellationRequested(); if (TypesHierarchyHelpers.IsBaseProperty(analyzedProperty, property)) { MethodDefinition anyAccessor = property.GetMethod ?? property.SetMethod; bool hidesParent = !anyAccessor.IsVirtual ^ anyAccessor.IsNewSlot; newNode = new AnalyzedPropertyTreeNode(property, hidesParent ? "(hides) " : ""); } } } catch (ReferenceResolvingException) { // ignore this type definition. } if (newNode != null) { yield return(newNode); } } }
public IEnumerable <ICompletionItem> CreateListForXmlnsCompletion(ICompilation compilation) { this.compilation = compilation; List <XmlnsCompletionItem> list = new List <XmlnsCompletionItem>(); IType xmlnsAttrType = compilation.FindType(typeof(System.Windows.Markup.XmlnsDefinitionAttribute)); foreach (IAssembly asm in compilation.ReferencedAssemblies) { foreach (IAttribute att in asm.AssemblyAttributes) { if (att.PositionalArguments.Count == 2 && att.AttributeType.Equals(xmlnsAttrType)) { ResolveResult arg1 = att.PositionalArguments[0]; if (arg1.IsCompileTimeConstant && arg1.ConstantValue is string) { list.Add(new XmlnsCompletionItem((string)arg1.ConstantValue, true)); } } } foreach (INamespace @namespace in TreeTraversal.PreOrder(asm.RootNamespace, ns => ns.ChildNamespaces)) { list.Add(new XmlnsCompletionItem(@namespace.FullName, asm.AssemblyName)); } } foreach (INamespace @namespace in TreeTraversal.PreOrder(compilation.MainAssembly.RootNamespace, ns => ns.ChildNamespaces)) { list.Add(new XmlnsCompletionItem(@namespace.FullName, false)); } list.Add(new XmlnsCompletionItem(XamlConst.MarkupCompatibilityNamespace, true)); return(list .Distinct(new XmlnsEqualityComparer()) .OrderBy(item => item, new XmlnsComparer())); }
/// <summary> /// Sets the position of the subtree at an offset. /// </summary> /// <param name="dragPosition">The drag position for the subtree. </param> /// <param name="offset">Additional offset.</param> /// <param name="root">The subtree root.</param> public static void SetSubtreePosition(BonsaiNode root, Vector2 dragPosition, Vector2 offset) { float min = float.MinValue; if (!root.IsOrphan()) { float nodeTop = root.RectPositon.yMin; float parentBottom = root.Parent.RectPositon.yMax; // The root cannot be above its parent. if (nodeTop < parentBottom) { min = parentBottom; } } // Record the old position to later determine the translation delta to move children. Vector2 oldPosition = root.Center; // Clamp the position so it does not go above the parent. Vector2 newPosition = dragPosition - offset; newPosition.y = Mathf.Clamp(newPosition.y, min, float.MaxValue); float snap = BonsaiPreferences.Instance.snapStep; root.Center = Utility.MathExtensions.SnapPosition(newPosition, snap); // Calculate the change of position of the root. Vector2 pan = root.Center - oldPosition; // Move the entire subtree of the root. // For all children, pan by the same amount that the parent changed by. foreach (BonsaiNode node in TreeTraversal.PreOrder(root).Skip(1)) { node.Center = Utility.MathExtensions.SnapPosition(node.Center + pan, snap); } }
private static IEnumerable <TypeDefinition> FindDerivedTypes(TypeDefinition type, IEnumerable <ModuleDefinition> assemblies) { foreach (ModuleDefinition module in assemblies) { foreach (TypeDefinition td in TreeTraversal.PreOrder(module.Types, t => t.NestedTypes)) { if (type.IsInterface && td.HasInterfaces) { foreach (TypeReference typeRef in td.Interfaces) { if (IsSameType(typeRef, type)) { yield return(td); } } } else if (!type.IsInterface && td.BaseType != null && IsSameType(td.BaseType, type)) { yield return(td); } } } }
private IEnumerable <SharpTreeNode> FindReferences(LoadedAssembly asm, CancellationToken ct) { string asmName = asm.AssemblyDefinition.Name.Name; string name = analyzedMethod.Name; string declTypeName = analyzedMethod.DeclaringType.FullName; foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.AssemblyDefinition.MainModule.Types, t => t.NestedTypes)) { ct.ThrowIfCancellationRequested(); SharpTreeNode newNode = null; try { if (!TypesHierarchyHelpers.IsBaseType(analyzedMethod.DeclaringType, type, resolveTypeArguments: false)) { continue; } foreach (MethodDefinition method in type.Methods) { ct.ThrowIfCancellationRequested(); if (TypesHierarchyHelpers.IsBaseMethod(analyzedMethod, method)) { bool hidesParent = !method.IsVirtual ^ method.IsNewSlot; newNode = new AnalyzedMethodTreeNode(method, hidesParent ? "(hides) " : ""); } } } catch (ReferenceResolvingException) { // ignore this type definition. maybe add a notification about such cases. } if (newNode != null) { yield return(newNode); } } }
internal static IEnumerable <DerivedTypesEntryNode> FindDerivedTypes(TypeDefinition type, ModuleDefinition[] assemblies, CancellationToken cancellationToken) { foreach (ModuleDefinition module in assemblies) { foreach (TypeDefinition td in TreeTraversal.PreOrder(module.Types, t => t.NestedTypes)) { cancellationToken.ThrowIfCancellationRequested(); if (type.IsInterface && td.HasInterfaces) { foreach (var iface in td.Interfaces) { if (IsSameType(iface.InterfaceType, type)) { yield return(new DerivedTypesEntryNode(td, assemblies)); } } } else if (!type.IsInterface && td.BaseType != null && IsSameType(td.BaseType, type)) { yield return(new DerivedTypesEntryNode(td, assemblies)); } } } }
public static IEnumerable <ITypeDefinition> GetAllTypeDefinitions(this IAssembly assembly) { return(TreeTraversal.PreOrder(assembly.TopLevelTypeDefinitions, t => t.NestedTypes)); }
/// <summary> /// Gets all unresolved type definitions from the file. /// For partial classes, each part is returned. /// </summary> public static IEnumerable <IUnresolvedTypeDefinition> GetAllTypeDefinitions(this IUnresolvedFile file) { return(TreeTraversal.PreOrder(file.TopLevelTypeDefinitions, t => t.NestedTypes)); }
private IEnumerable <AnalyzerTreeNode> FindReferencesInModule(IEnumerable <ModuleDef> modules, ITypeDefOrRef tr, CancellationToken ct) { foreach (var module in modules) { //since we do not display modules as separate entities, coalesce the assembly and module searches bool foundInAssyOrModule = false; if ((usage & AttributeTargets.Assembly) != 0) { AssemblyDef asm = module.Assembly; if (asm != null && asm.HasCustomAttributes) { foreach (var attribute in asm.CustomAttributes) { if (new SigComparer().Equals(attribute.AttributeType, tr)) { foundInAssyOrModule = true; break; } } } } if (!foundInAssyOrModule) { ct.ThrowIfCancellationRequested(); //search module if ((usage & AttributeTargets.Module) != 0) { if (module.HasCustomAttributes) { foreach (var attribute in module.CustomAttributes) { if (new SigComparer().Equals(attribute.AttributeType, tr)) { foundInAssyOrModule = true; break; } } } } } if (foundInAssyOrModule) { yield return(new AnalyzedAssemblyTreeNode(module)); } ct.ThrowIfCancellationRequested(); foreach (TypeDef type in TreeTraversal.PreOrder(module.Types, t => t.NestedTypes).OrderBy(t => t.FullName)) { ct.ThrowIfCancellationRequested(); foreach (var result in FindReferencesWithinInType(type, tr)) { ct.ThrowIfCancellationRequested(); yield return(result); } } } }
/// <summary> /// Gets all classes, including nested classes. /// </summary> public static IEnumerable<ITypeDefinition> GetAllClasses(this ITypeResolveContext context) { return TreeTraversal.PreOrder(context.GetClasses(), t => t.InnerClasses); }
private IEnumerable <BonsaiNode> SelfAbortables(BonsaiNode aborter) { return(TreeTraversal.PreOrder(aborter).Skip(1)); }
/// <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); }
private void DetectSwitchBody(Block block, SwitchInstruction switchInst) { Debug.Assert(block.Instructions.Last() == switchInst); ControlFlowNode h = context.ControlFlowNode; // CFG node for our switch head Debug.Assert(h.UserData == block); Debug.Assert(!TreeTraversal.PreOrder(h, n => n.DominatorTreeChildren).Any(n => n.Visited)); var nodesInSwitch = new List <ControlFlowNode>(); nodesInSwitch.Add(h); h.Visited = true; ExtendLoop(h, nodesInSwitch, out var exitPoint, isSwitch: true); if (exitPoint != null && exitPoint.Predecessors.Count == 1 && !context.ControlFlowGraph.HasReachableExit(exitPoint)) { // If the exit point is reachable from just one single "break;", // it's better to move the code into the switch. // (unlike loops which should not be nested unless necessary, // nesting switches makes it clearer in which cases a piece of code is reachable) nodesInSwitch.AddRange(TreeTraversal.PreOrder(exitPoint, p => p.DominatorTreeChildren)); exitPoint = null; } context.Step("Create BlockContainer for switch", switchInst); // Sort blocks in the loop in reverse post-order to make the output look a bit nicer. // (if the loop doesn't contain nested loops, this is a topological sort) nodesInSwitch.Sort((a, b) => b.PostOrderNumber.CompareTo(a.PostOrderNumber)); Debug.Assert(nodesInSwitch[0] == h); foreach (var node in nodesInSwitch) { node.Visited = false; // reset visited flag so that we can find outer loops Debug.Assert(h.Dominates(node) || !node.IsReachable, "The switch body must be dominated by the switch head"); } BlockContainer switchContainer = new BlockContainer(); Block newEntryPoint = new Block(); newEntryPoint.ILRange = switchInst.ILRange; switchContainer.Blocks.Add(newEntryPoint); newEntryPoint.Instructions.Add(switchInst); block.Instructions[block.Instructions.Count - 1] = switchContainer; Block exitTargetBlock = (Block)exitPoint?.UserData; if (exitTargetBlock != null) { block.Instructions.Add(new Branch(exitTargetBlock)); } MoveBlocksIntoContainer(nodesInSwitch, switchContainer); // Rewrite branches within the loop from oldEntryPoint to newEntryPoint: foreach (var branch in switchContainer.Descendants.OfType <Branch>()) { if (branch.TargetBlock == exitTargetBlock) { branch.ReplaceWith(new Leave(switchContainer) { ILRange = branch.ILRange }); } } }
/// <summary> /// Check whether 'block' is a loop head; and construct a loop instruction /// (nested BlockContainer) if it is. /// </summary> public void Run(Block block, BlockTransformContext context) { this.context = context; // LoopDetection runs early enough so that block should still // be in the original container at this point. Debug.Assert(block.Parent == context.ControlFlowGraph.Container); this.currentBlockContainer = context.ControlFlowGraph.Container; // Because this is a post-order block transform, we can assume that // any nested loops within this loop have already been constructed. if (block.Instructions.Last() is SwitchInstruction switchInst) { // Switch instructions support "break;" just like loops DetectSwitchBody(block, switchInst); } ControlFlowNode h = context.ControlFlowNode; // CFG node for our potential loop head Debug.Assert(h.UserData == block); Debug.Assert(!TreeTraversal.PreOrder(h, n => n.DominatorTreeChildren).Any(n => n.Visited)); List <ControlFlowNode> loop = null; foreach (var t in h.Predecessors) { if (h.Dominates(t)) { // h->t is a back edge, and h is a loop header // Add the natural loop of t->h to the loop. // Definitions: // * A back edge is an edge t->h so that h dominates t. // * The natural loop of the back edge is the smallest set of nodes // that includes the back edge and has no predecessors outside the set // except for the predecessor of the header. if (loop == null) { loop = new List <ControlFlowNode>(); loop.Add(h); // Mark loop header as visited so that the pre-order traversal // stops at the loop header. h.Visited = true; } t.TraversePreOrder(n => n.Predecessors, loop.Add); } } if (loop != null) { var headBlock = (Block)h.UserData; context.Step($"Construct loop with head {headBlock.Label}", headBlock); // loop now is the union of all natural loops with loop head h. // Try to extend the loop to reduce the number of exit points: ExtendLoop(h, loop, out var exitPoint); // Sort blocks in the loop in reverse post-order to make the output look a bit nicer. // (if the loop doesn't contain nested loops, this is a topological sort) loop.Sort((a, b) => b.PostOrderNumber.CompareTo(a.PostOrderNumber)); Debug.Assert(loop[0] == h); foreach (var node in loop) { node.Visited = false; // reset visited flag so that we can find outer loops Debug.Assert(h.Dominates(node) || !node.IsReachable, "The loop body must be dominated by the loop head"); } ConstructLoop(loop, exitPoint); } }
IEnumerable <AnalyzerTreeNodeData> FindReferencesInModule(IEnumerable <ModuleDef> modules, ITypeDefOrRef tr, CancellationToken ct) { var trScopeType = tr.GetScopeType(); var checkedAsms = new HashSet <AssemblyDef>(); foreach (var module in modules) { if ((usage & AttributeTargets.Assembly) != 0) { AssemblyDef asm = module.Assembly; if (!(asm is null) && checkedAsms.Add(asm)) { foreach (var attribute in asm.GetCustomAttributes()) { if (new SigComparer().Equals(attribute.AttributeType?.GetScopeType(), trScopeType)) { yield return(new AssemblyNode(asm) { Context = Context }); break; } } } } ct.ThrowIfCancellationRequested(); if ((usage & AttributeTargets.Module) != 0) { foreach (var attribute in module.GetCustomAttributes()) { if (new SigComparer().Equals(attribute.AttributeType?.GetScopeType(), trScopeType)) { yield return(new ModuleNode(module) { Context = Context }); break; } } } ct.ThrowIfCancellationRequested(); if ((usage & (AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.ReturnValue | AttributeTargets.Parameter)) != 0) { foreach (TypeDef type in TreeTraversal.PreOrder(module.Types, t => t.NestedTypes)) { ct.ThrowIfCancellationRequested(); foreach (var result in FindReferencesWithinInType(type, tr)) { ct.ThrowIfCancellationRequested(); yield return(result); } } } } }
IEnumerable <IAnalyzerTreeNodeData> FindReferencesInModule(IEnumerable <ModuleDef> modules, ITypeDefOrRef tr, CancellationToken ct) { var checkedAsms = new HashSet <AssemblyDef>(); foreach (var module in modules) { if ((usage & AttributeTargets.Assembly) != 0) { AssemblyDef asm = module.Assembly; if (asm != null && !checkedAsms.Contains(asm) && asm.HasCustomAttributes) { checkedAsms.Add(asm); foreach (var attribute in asm.CustomAttributes) { if (new SigComparer().Equals(attribute.AttributeType, tr)) { yield return(new AssemblyNode(asm) { Context = Context }); break; } } } } ct.ThrowIfCancellationRequested(); if ((usage & AttributeTargets.Module) != 0) { if (module.HasCustomAttributes) { foreach (var attribute in module.CustomAttributes) { if (new SigComparer().Equals(attribute.AttributeType, tr)) { yield return(new ModuleNode(module) { Context = Context }); break; } } } } ct.ThrowIfCancellationRequested(); foreach (TypeDef type in TreeTraversal.PreOrder(module.Types, t => t.NestedTypes)) { ct.ThrowIfCancellationRequested(); foreach (var result in FindReferencesWithinInType(type, tr)) { ct.ThrowIfCancellationRequested(); yield return(result); } } } }
/// <summary> /// Lists all potential targets for break; statements from a domination tree, /// assuming the domination tree must be exited via either break; or continue; /// /// First list all nodes in the dominator tree (excluding continue nodes) /// Then return the all successors not contained within said tree. /// /// Note that node will be returned once for each outgoing edge. /// Labelled continue statements (depth > 1) are counted as break targets /// </summary> internal IEnumerable <ControlFlowNode> GetBreakTargets(ControlFlowNode dominator) => TreeTraversal.PreOrder(dominator, n => n.DominatorTreeChildren.Where(c => !MatchContinue(c))) .SelectMany(n => n.Successors) .Where(n => !dominator.Dominates(n) && !MatchContinue(n, 1));
/// <summary> /// Gets all type definitions, including nested types. /// </summary> public static IEnumerable <ITypeDefinition> GetAllTypes(this ITypeResolveContext context) { return(TreeTraversal.PreOrder(context.GetTypes(), t => t.NestedTypes)); }