private void BuildIndexes(List <ModuleDefMD> modules) { foreach (var module in modules) { foreach (var typeDef in module.GetTypes()) { stat.TypeCount++; if (!typeDef.HasMethods) { continue; } foreach (var methodDef in typeDef.Methods) { stat.MethodCount++; if (!methodDef.HasBody) { continue; } if (!methodDef.Body.HasInstructions) { continue; } CallInfo cacheInfo = null; foreach (var instruction in methodDef.Body.Instructions) { // TODO: check opcode // call: 802655 // callvirt: 918700 // newobj: 255686 // ldsfld: 27607 // ldfld: 57948 // stfld: 23493 // ldftn: 41261 // ldflda: 4556 // ldvirtftn: 1453 // stsfld: 2023 // ldtoken: 622 // ldsflda: 24 if (instruction.Operand is IMethod methodOperand) { CallInfo newInfo; if (cacheInfo == null) { newInfo = new CallInfo( new AssemblyInfo(module.Assembly.Name, module.Assembly.Version), methodDef.CreateMethodUniqueName(), instruction.OpCode, methodDef.IsPublicGlobalVisibility(), methodDef.FindOverrides().Select(md => md.CreateMethodUniqueName()).ToList()); cacheInfo = newInfo; } else { newInfo = new CallInfo( cacheInfo.AssemblyInfo, cacheInfo.Signature, instruction.OpCode, cacheInfo.IsPublic, cacheInfo.OverrideSignatures); } // TODO PERF add cache for AssemblyInfo var assemblyInfo = new AssemblyInfo( methodOperand.DeclaringType.DefinitionAssembly.Name, methodOperand.DeclaringType.DefinitionAssembly.Version); var key = methodOperand.CreateMethodUniqueName(); if (callers.TryGetValue(key, out var callInfoMap)) { if (callInfoMap.TryGetValue(assemblyInfo, out var list)) { list.Add(newInfo); } else { callInfoMap.Add(assemblyInfo, new List <CallInfo>() { newInfo }); } } else { callers.Add(key, new Dictionary <AssemblyInfo, List <CallInfo> >() { { assemblyInfo, new List <CallInfo>() { newInfo } } }); } } } } } } }
private CallGraph CreateGraphInternal(List <PatternInfo> patterns) { var graph = new CallGraph(); var processingEntities = new Queue <ProcessingEntity>(); foreach (var pattern in patterns) { var calls = index.GetCalls(pattern); 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 = new CallInfo( new AssemblyInfo(UTF8String.Empty, pattern.RequiredOlderVersion), pattern.Method, OpCodes.Call, true, new List <MethodUniqueSignature>(0)); var node = new CallGraphNode(info); graph.Nodes.Add(pattern.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 (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 CallGraphNode(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)); } } } } } foreach (var node in graph.Nodes.Values) { if (node.InNodes.Count != 0) { continue; } graph.EntryNodes.Add(node.MethodSignature, node); } return(graph); }