Beispiel #1
0
        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);
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
 public CallInfoNode(CallInfo info)
 {
     this.info = info;
     InNodes   = new List <ICallGraphNode>();
     OutNodes  = new List <ICallGraphNode>();
 }