private List <TypeDef> BuildIndexes(List <ModuleDefMD> modules, HashSet <string> convertingTypes) { var result = new List <TypeDef>(); var references = new Dictionary <string, List <string> >(modules.Count); foreach (var module in modules) { references.Add( module.Assembly.Name, module.GetAssemblyRefs().Select(x => x.Name.ToString()).ToList()); /* * var currentAssemblyName = module.Assembly.Name.ToString(); * var foundRefs = false; * foreach (var assemblyRef in module.GetAssemblyRefs()) * { * var name = assemblyRef.Name; * if (references.TryGetValue(name, out var list)) * { * list.Add(currentAssemblyName); * } * else * { * references.Add(name, new HashSet<string>{currentAssemblyName}); * } * * foundRefs = true; * } * * if (!foundRefs) * { * rootReferences.Add(currentAssemblyName); * } */ foreach (var typeDef in module.GetTypes()) { if (convertingTypes.Contains(typeDef.FullName /*typeDef.ReflectionFullName*/)) { result.Add(typeDef); } stat.TypeCount++; if (!typeDef.HasMethods) { continue; } foreach (var methodDef in typeDef.Methods) { stat.MethodCount++; if (!methodDef.HasBody) { continue; } if (!methodDef.Body.HasInstructions) { continue; } stat.InstructionCount += methodDef.Body.Instructions.Count; var cachedOverrides = methodDef.FindOverrides(); AddImplementations(methodDef, cachedOverrides); CallInfo cacheInfo = null; for (var index = 0; index < methodDef.Body.Instructions.Count; index++) { var instruction = methodDef.Body.Instructions[index]; if (instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld || instruction.OpCode == OpCodes.Ldsflda || instruction.OpCode == OpCodes.Ldflda || instruction.OpCode == OpCodes.Stsfld || instruction.OpCode == OpCodes.Stfld) { continue; } // TODO: check opcodes: // + 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) { if (methodOperand.DeclaringType.DefinitionAssembly == null) { // TODO: it is possible for an array of generic type, just skip it for now // FSharp.Core.dll // T[0...,0...] Microsoft.FSharp.Core.ExtraTopLevelOperators::array2D$cont@115<?,T>(?[],System.Int32,Microsoft.FSharp.Core.Unit) continue; } stat.MethodCallCount++; CallInfo newInfo; if (cacheInfo == null) { newInfo = new CallInfo(index, methodDef, cachedOverrides); cacheInfo = newInfo; } else { newInfo = cacheInfo.Copy(index); } // TODO PERF add cache for AssemblyInfo var assemblyInfo = new AssemblyInfo( methodOperand.DeclaringType.DefinitionAssembly.Name, methodOperand.DeclaringType.DefinitionAssembly.Version); MethodUniqueSignature key = null; if (instruction.OpCode.Code == Code.Callvirt) { if (TryGetConcreteSignature(methodDef, index, out key)) { stat.SpecifiedVirtualCalls++; newInfo.Opcode = OpCodes.Call; } } if (key == null) { key = methodOperand.CreateMethodUniqueSignature(); } 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 } } }); } } } } } } foreach (var refAssembly in references.Keys) { CacheReferences(refAssembly, references); } return(result); }
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); }
public CallInfoNode(CallInfo info) { this.info = info; InNodes = new List <ICallGraphNode>(); OutNodes = new List <ICallGraphNode>(); }