public void RemoveSameClasses() { var handledNodes = new HashSet <ICallGraphNode>(Nodes.Count); var processingNodes = new Queue <(ICallGraphNode, ICallGraphNode)>(); EntryNodes.Clear(); Nodes.Clear(); var roots = Roots.Values; Roots = new Dictionary <MethodUniqueSignature, ICallGraphNode>(); foreach (var rootNode in roots) { // we keep all parents of root(s) if (!handledNodes.Add(rootNode)) { continue; } var mappedRootNode = new CallInfoNode(rootNode.Info); Roots.Add(mappedRootNode.MethodSignature, mappedRootNode); Nodes.Add(mappedRootNode.MethodSignature, mappedRootNode); if (rootNode.InNodes.Count == 0 && !EntryNodes.ContainsKey(mappedRootNode.MethodSignature)) { EntryNodes.Add(mappedRootNode.MethodSignature, mappedRootNode); } foreach (var inNode in rootNode.InNodes) { var mappedNode = new CallInfoNode(inNode.Info); mappedRootNode.InNodes.Add(mappedNode); mappedNode.OutNodes.Add(mappedRootNode); if (!Nodes.ContainsKey(mappedNode.MethodSignature)) { Nodes.Add(mappedNode.MethodSignature, mappedNode); if (inNode.InNodes.Count == 0) { if (!EntryNodes.ContainsKey(mappedNode.MethodSignature)) { EntryNodes.Add(mappedNode.MethodSignature, mappedNode); } } else { processingNodes.Enqueue((inNode, mappedNode)); } } } } // and keep only different classes for other parents while (processingNodes.Count > 0) { var(node, mappedNode) = processingNodes.Dequeue(); if (!handledNodes.Add(node)) { continue; } var mappedInNodes = new HashSet <ICallGraphNode>(mappedNode.InNodes); foreach (var inNode in node.InNodes) { var classSignature = new MethodUniqueSignature(inNode.MethodSignature.ToClassName()); if (Nodes.TryGetValue(classSignature, out var mappedInNode)) { if (mappedInNode != mappedNode && mappedInNodes.Add(mappedInNode)) { mappedInNode.OutNodes.Add(mappedNode); } } else { mappedInNode = new CallInfoNode(new CallInfo( 0, null, inNode.AssemblyInfo, classSignature, inNode.IsPublic, null)); mappedInNodes.Add(mappedInNode); mappedInNode.OutNodes.Add(mappedNode); Nodes.Add(mappedInNode.MethodSignature, mappedInNode); } if (inNode.InNodes.Count == 0) { if (!EntryNodes.ContainsKey(mappedInNode.MethodSignature)) { EntryNodes.Add(mappedInNode.MethodSignature, mappedInNode); } } else { processingNodes.Enqueue((inNode, mappedInNode)); } } if (mappedNode.InNodes.Count != mappedInNodes.Count) { mappedNode.InNodes.Clear(); mappedNode.InNodes.AddRange(mappedInNodes); } } }
private CallGraph CreateGraphInternal(List <TemplateInfo> templates) { var graph = new CallGraph(); var processingEntities = new Queue <ProcessingEntity>(); foreach (var template in templates) { var calls = index.GetCalls(template); if (calls.Count > 0) { // HACK if we found call w/ version restriction // then we add it and find again w/o version restrictions // in practice the result should be the same (but not always) var info = CallInfo.CreateFake( new AssemblyInfo(UTF8String.Empty, template.RequiredOlderVersion), template.Method); var node = new CallInfoNode(info); graph.Nodes.Add(template.Method, node); graph.Roots.Add(template.Method, node); processingEntities.Enqueue(new ProcessingEntity(node.MethodSignature, node)); } } while (processingEntities.Count > 0) { var entity = processingEntities.Dequeue(); var calls = index.GetCalls(entity.Signature /*, entity.Node.AssemblyInfo*/); if (calls.Count != 0) { foreach (var callInfo in calls) { if (callInfo.Opcode.Code != Code.Call && callInfo.Opcode.Code != Code.Callvirt && callInfo.Opcode.Code != Code.Newobj && callInfo.Opcode.Code != Code.Ldtoken) // used into Expression Tree in KB, need to implement DFA //resume.Opcode.Code != Code.Ldvirtftn && // can use with calli //resume.Opcode.Code != Code.Ldftn) // https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.calli?view=netframework-4.7.2 { // just ignore it for now, need to implement DFA stat.IgnoredOpcodes.Add(callInfo.Opcode.Code); continue; } if (entity.VirtCallRequired && callInfo.Opcode.Code != Code.Callvirt) { continue; } if (graph.Nodes.TryGetValue(callInfo.Signature, out var callingNode)) { // TODO: need to add a new kind of cycled edge to remove recursive calls in RemoveNonPublicEntryNodes //callingNode.OutNodes.Add(entity.Node); //entity.Node.InNodes.Add(callingNode); } else { callingNode = new CallInfoNode(callInfo); callingNode.OutNodes.Add(entity.Node); entity.Node.InNodes.Add(callingNode); graph.Nodes.Add(callInfo.Signature, callingNode); processingEntities.Enqueue(new ProcessingEntity(callInfo.Signature, callingNode)); foreach (var overrideSignature in callInfo.OverrideSignatures) { processingEntities.Enqueue(new ProcessingEntity(overrideSignature, callingNode, true)); } } } } } foreach (var node in graph.Nodes.Values) { if (node.InNodes.Count != 0) { continue; } graph.EntryNodes.Add(node.MethodSignature, node); } return(graph); }