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();
        }
Exemple #2
0
 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);
         }
     }
 }
Exemple #3
0
 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);
         }
     }
 }
Exemple #4
0
 /// <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>());
 }
Exemple #6
0
		/// <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);
			}
		}
Exemple #7
0
 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);
             }
         }
     }
 }
Exemple #8
0
 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);
                }
            }
        }
Exemple #10
0
        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()));
        }
Exemple #11
0
        /// <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);
            }
        }
Exemple #12
0
 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);
                }
            }
        }
Exemple #14
0
 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));
             }
         }
     }
 }
Exemple #15
0
 public static IEnumerable <ITypeDefinition> GetAllTypeDefinitions(this IAssembly assembly)
 {
     return(TreeTraversal.PreOrder(assembly.TopLevelTypeDefinitions, t => t.NestedTypes));
 }
Exemple #16
0
 /// <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));
 }
Exemple #17
0
        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);
                    }
                }
            }
        }
Exemple #18
0
		/// <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);
        }
Exemple #21
0
        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
                    });
                }
            }
        }
Exemple #22
0
        /// <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);
                    }
                }
            }
        }
Exemple #25
0
 /// <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));
Exemple #26
0
 /// <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));
 }