你看,如果一个方法被调用的方法使用反射内 [英] Look if a method is called inside a method using reflection

查看:210
本文介绍了你看,如果一个方法被调用的方法使用反射内的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正与反思,目前有 MethodBody 。我该如何检查,如果一个特定的方法被称为MethodBody里面?

 装配总成=的Assembly.Load(模块1);
类型类型= assembly.GetType(Module1.ModuleInit);
MethodInfo的MI = type.GetMethod(初始化);
MethodBody MB = mi.GetMethodBody();
 

解决方案

使用 Mono.Cecil能做到 。这是一个独立的组件,将工作在Microsoft .NET和单声道。 (我想我用0.6版本或左右回来时,我写了$ C $低于C)

假设你有一些组件

 的IEnumerable< AssemblyDefinition>组件;
 

获取这些使用AssemblyFactory(加载一个?)

下面的代码片段会枚举的方法,所有使用各类这些组件

  methodUsages =组件
            .SelectMany(组装=> assembly.MainModule.Types.Cast<类型定义>())
            .SelectMany(类型=> type.Methods.Cast< MethodDefinition>())
            。凡(方法=>空= method.Body!)//允许摘要和仿制药
            .SelectMany(方法=> method.Body.Instructions.Cast<结构>())
            。选择(INSTR => instr.Operand)
            .OfType&其中; MethodReference>();
 

此将返回所有的方法的引用(因此包括在反射使用,或者构造可能会或可能不会被执行前pressions)。因此,这可能不是很实用,除了可以显示你有什么可以与塞西尔API没有太多的精力来完成:)

请注意,此示例假定塞西尔一个有些旧版本(一个在主流单声道版本)。新版本的

  • 更简洁(使用强类型泛型集合)
  • 更快

当然,你的情况,你也可以有一个方法引用作为出发点。假设你想检测时mytargetmethod可以的实际上的直接'起点'里面叫:

  MethodReference起点; //获取它的地方使用塞西尔
MethodReference mytargetmethod; //你在找什么

布尔isCalled =起点
    .GetOriginalMethod()//跳转到原来的(仿制药如)
    .Resolve()//从IL图像的清晰度
    .Body.Instructions.Cast<结构>()
    。任何(I => i.Op code ==运算codes.Callvirt和放大器;&安培; i.Operand ==(mytargetmethod));
 

调用树搜索

下面是一个工作片段,让你递归搜索到(选择)方法相互调用(间接)。

 使用系统;
System.Collections中使用;
使用System.Collections.Generic;
使用System.Linq的;
使用Mono.Cecil能做到;
使用Mono.Cecil.Cil;

命名空间计算器
{
    / *
     *广度优先跨调用树的一个子集懒搜索的起点生根
     *
     * methodSelect选择递归到的方法
     * resultGen产生由枚举返回的结果对象
     *
     * /
    类CallTreeSearch< T> :基本codeVisitor,IEnumerable的< T>其中T:类
    {
        私人只读Func键< MethodReference,布尔> _methodSelect;
        私人只读Func键<指令,堆栈< MethodReference>中T> _转换;

        私人只读的IEnumerable< MethodDefinition> _startingPoints;
        私人只读IDictionary的< MethodDefinition,堆叠式和LT; MethodReference>> _chain =新字典< MethodDefinition,堆叠式和LT; MethodReference>>();
        私人只读的ICollection< MethodDefinition> _seen =新的HashSet&其中; MethodDefinition>(新CompareMembers&其中; MethodDefinition>());
        私人只读的ICollection< T> _results =新的HashSet< T>();
        私人堆叠式和LT; MethodReference> _currentStack;

        私人const int的InfiniteRecursion = -1;
        私人只读INT _maxrecursiondepth;
        私人布尔_busy;

        公共CallTreeSearch(IEnumerable的< MethodDefinition> startingPoints,
                              FUNC< MethodReference,布尔> methodSelect,
                              FUNC<指令,堆栈< MethodReference>中T> resultGen)
            :这个(startingPoints,methodSelect,resultGen,InfiniteRecursion)
        {

        }

        公共CallTreeSearch(IEnumerable的< MethodDefinition> startingPoints,
                              FUNC< MethodReference,布尔> methodSelect,
                              FUNC<指令,堆栈< MethodReference>中T> resultGen,
                              INT maxrecursiondepth)
        {
            _startingPoints = startingPoints.ToList();

            _methodSelect = methodSelect;
            _maxrecursiondepth = maxrecursiondepth;
            _transform = resultGen;
        }

        公众覆盖无效VisitMethodBody(MethodBody体)
        {
            _seen.Add(body.Method); //避免无限递归
            base.VisitMethodBody(体);
        }

        公众覆盖无效VisitInstructionCollection(InstructionCollection说明)
        {
            的foreach(在指令指令指令输入)
                VisitInstruction(INSTR);

            base.VisitInstructionCollection(说明​​);
        }

        公众覆盖无效VisitInstruction(指令INSTR)
        {
            牛逼的结果= _transform(INSTR,_currentStack);
            如果(结果!= NULL)
                _results.Add(结果);

            VAR methodRef = instr.Operand为MethodReference; // TODO只能选择电话?
            如果(methodRef = NULL和放大器;!&安培; _methodSelect(methodRef))
            {
                VAR决心= methodRef.Resolve();
                如果(空=化解和放大器;!&安培;!(_ chain.ContainsKey(解析)|| _seen.Contains(解析)))
                    _chain.Add(解决,新的堆叠式和LT; MethodReference>(_ currentStack.Reverse()));
            }

            base.VisitInstruction(INSTR);
        }

        公众的IEnumerator< T>的GetEnumerator()
        {
            锁(本)//不是多线程安全
            {
                如果(_busy)
                    抛出新的InvalidOperationException异常(CallTreeSearch枚举器不能重返进入);
                _busy = TRUE;

                尝试
                {
                    INT recursionLevel = 0;
                    ResetToStartingPoints();

                    而(_chain.Count大于0&安培;&安培;
                           ((InfiniteRecursion == _maxrecursiondepth)|| recursionLevel ++< = _maxrecursiondepth))
                    {

                        //换出的集合,因为游客将修改
                        VAR克隆=新字典< MethodDefinition,堆叠式和LT; MethodReference>>(_链);
                        _chain.Clear();

                        的foreach(在clone.Where(VAR打电话呼叫=> HasBody(call.Key)))
                        {
// Console.Error.Write(\ rCallTreeSearch:一级#{0},{扫描1,-20} \ R,recursionLevel,call.Key.Name +新的字符串('',21));
                            _currentStack = call.Value;
                            _currentStack.Push(call.Key);
                            尝试
                            {
                                _results.Clear();
                                call.Key.Body.Accept(本); //增长_chain和_results
                            }
                            最后
                            {
                                _currentStack.Pop();
                            }
                            _currentStack = NULL;

                            的foreach(VAR结果_results)
                                得到返回结果;
                        }
                    }
                }
                最后
                {
                    _busy = FALSE;
                }
            }
        }

        私人无效ResetToStartingPoints()
        {
            _chain.Clear();
            _seen.Clear();
            的foreach(在_startingPoints VAR起点)
            {
                _chain.Add(起点,新的堆叠式和LT; MethodReference>());
                _seen.Add(起点);
            }
        }

        私有静态布尔HasBody(MethodDefinition methodDefinition)
        {
            返回(methodDefinition.IsAbstract || methodDefinition.Body == NULL)!;
        }

        IEnumerator的IEnumerable.GetEnumerator()
        {
            返回的GetEnumerator();
        }
    }

    内部类CompareMembers< T> :的IComparer< T&GT ;,的IEqualityComparer< T>
        其中T:类,IMemberReference
    {
        公众诠释比较(T X,T Y)
        {返回StringComparer.InvariantCultureIgnoreCase.Compare(keyFor继续作用(X),keyFor继续作用(Y)); }

        公共布尔等于(T X,T Y)
        {返回keyFor继续作用(X).Equals(keyFor继续作用(Y)); }

        私人静态字符串keyFor继续作用(T MR)
        {返回null ==先生? :的String.Format({0} :: {1},mr.DeclaringType.FullName,mr.Name); }

        公众诠释GetHash code(T obj)以
        {返回keyFor继续作用(OBJ).GetHash code(); }
    }
}
 

  • 在做一些错误处理解决()(我有一个扩展方法 TryResolve()为目的)
  • 根据需要选择 MethodReferences 的用法在调用操作(打电话,愈伤组织,callvirt ...)只( // TODO 的)

典型用途:

 公共静态的IEnumerable< T> SearchCallTree< T>(此类型定义startingClass,
                                               FUNC< MethodReference,布尔> methodSelect,
                                               FUNC<指令,堆栈< MethodReference>中T> resultFunc,
                                               INT MAXDEPTH)
    其中T:类
{
    返回新CallTreeSearch< T>(startingClass.Methods.Cast< MethodDefinition>(),methodSelect,resultFunc,MAXDEPTH);
}

公共静态的IEnumerable< T> SearchCallTree< T>(这MethodDefinition startingMethod,
                                               FUNC< MethodReference,布尔> methodSelect,
                                               FUNC<指令,堆栈< MethodReference>中T> resultFunc,
                                               INT MAXDEPTH)
    其中T:类
{
    返回新CallTreeSearch< T>(新[] {} startingMethod,methodSelect,resultFunc,MAXDEPTH);
}

//实际使用量:
私有静态的IEnumerable< TypeUsage> SearchMessages(类型定义uiType,布尔onlyConstructions)
{
    返回uiType.SearchCallTree(IsBusinessCall,
           (指令,堆栈)=> DetectRequestUsage(指令,堆栈,onlyConstructions));
}
 

注意像DetectRequestUsage功能的completiion适合您的需要是完全的,完全由你(编辑: <一个href="http://stackoverflow.com/questions/5667816/get-types-used-inside-c-method-body/5741926#5741926">but看到这里 的)。 你可以做你想做的,不要忘记:你将有完整的静态分析调用堆栈在您的处置,所以你其实可以做pretty的巧妙的事情与所有的信息的!

I'm working with reflection and currently have a MethodBody. How do I check if a specific method is called inside the MethodBody?

Assembly assembly = Assembly.Load("Module1");
Type type = assembly.GetType("Module1.ModuleInit");
MethodInfo mi = type.GetMethod("Initialize");
MethodBody mb = mi.GetMethodBody();

解决方案

Use Mono.Cecil. It is a single standalone assembly that will work on Microsoft .NET as well as Mono. (I think I used version 0.6 or thereabouts back when I wrote the code below)

Say you have a number of assemblies

IEnumerable<AssemblyDefinition> assemblies;

Get these using AssemblyFactory (load one?)

The following snippet would enumerate all usages of methods in all types of these assemblies

methodUsages = assemblies
            .SelectMany(assembly => assembly.MainModule.Types.Cast<TypeDefinition>())
            .SelectMany(type => type.Methods.Cast<MethodDefinition>())
            .Where(method => null != method.Body) // allow abstracts and generics
            .SelectMany(method => method.Body.Instructions.Cast<Instruction>())
            .Select(instr => instr.Operand)
            .OfType<MethodReference>();

This will return all references to methods (so including use in reflection, or to construct expressions which may or may not be executed). As such, this is probably not very useful, except to show you what can be done with the Cecil API without too much of an effort :)

Note that this sample assumes a somewhat older version of Cecil (the one in mainstream mono versions). Newer versions are

  • more succinct (by using strong typed generic collections)
  • faster

Of course in your case you could have a single method reference as starting point. Say you want to detect when 'mytargetmethod' can actually be called directly inside 'startingpoint':

MethodReference startingpoint; // get it somewhere using Cecil
MethodReference mytargetmethod; // what you are looking for

bool isCalled = startingpoint    
    .GetOriginalMethod() // jump to original (for generics e.g.)
    .Resolve()           // get the definition from the IL image
    .Body.Instructions.Cast<Instruction>()
    .Any(i => i.OpCode == OpCodes.Callvirt && i.Operand == (mytargetmethod));

Call Tree Search

Here is a working snippet that allows you to recursively search to (selected) methods that call each other (indirectly).

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace StackOverflow
{
    /*
     * breadth-first lazy search across a subset of the call tree rooting in startingPoint
     * 
     * methodSelect selects the methods to recurse into
     * resultGen generates the result objects to be returned by the enumerator
     * 
     */
    class CallTreeSearch<T> : BaseCodeVisitor, IEnumerable<T> where T : class
    {
        private readonly Func<MethodReference, bool> _methodSelect;
        private readonly Func<Instruction, Stack<MethodReference>, T> _transform;

        private readonly IEnumerable<MethodDefinition> _startingPoints;
        private readonly IDictionary<MethodDefinition, Stack<MethodReference>> _chain = new Dictionary<MethodDefinition, Stack<MethodReference>>();
        private readonly ICollection<MethodDefinition> _seen = new HashSet<MethodDefinition>(new CompareMembers<MethodDefinition>());
        private readonly ICollection<T> _results = new HashSet<T>();
        private Stack<MethodReference> _currentStack;

        private const int InfiniteRecursion = -1;
        private readonly int _maxrecursiondepth;
        private bool _busy;

        public CallTreeSearch(IEnumerable<MethodDefinition> startingPoints,
                              Func<MethodReference, bool> methodSelect,
                              Func<Instruction, Stack<MethodReference>, T> resultGen)
            : this(startingPoints, methodSelect, resultGen, InfiniteRecursion)
        {

        }

        public CallTreeSearch(IEnumerable<MethodDefinition> startingPoints,
                              Func<MethodReference, bool> methodSelect,
                              Func<Instruction, Stack<MethodReference>, T> resultGen,
                              int maxrecursiondepth)
        {
            _startingPoints = startingPoints.ToList();

            _methodSelect = methodSelect;
            _maxrecursiondepth = maxrecursiondepth;
            _transform = resultGen;
        }

        public override void VisitMethodBody(MethodBody body)
        {
            _seen.Add(body.Method); // avoid infinite recursion
            base.VisitMethodBody(body);
        }

        public override void VisitInstructionCollection(InstructionCollection instructions)
        {
            foreach (Instruction instr in instructions)
                VisitInstruction(instr);

            base.VisitInstructionCollection(instructions);
        }

        public override void VisitInstruction(Instruction instr)
        {
            T result = _transform(instr, _currentStack);
            if (result != null)
                _results.Add(result);

            var methodRef = instr.Operand as MethodReference; // TODO select calls only?
            if (methodRef != null && _methodSelect(methodRef))
            {
                var resolve = methodRef.Resolve();
                if (null != resolve && !(_chain.ContainsKey(resolve) || _seen.Contains(resolve)))
                    _chain.Add(resolve, new Stack<MethodReference>(_currentStack.Reverse()));
            }

            base.VisitInstruction(instr);
        }

        public IEnumerator<T> GetEnumerator()
        {
            lock (this) // not multithread safe
            {
                if (_busy)
                    throw new InvalidOperationException("CallTreeSearch enumerator is not reentrant");
                _busy = true;

                try
                {
                    int recursionLevel = 0;
                    ResetToStartingPoints();

                    while (_chain.Count > 0 &&
                           ((InfiniteRecursion == _maxrecursiondepth) || recursionLevel++ <= _maxrecursiondepth))
                    {

                        // swapout the collection because Visitor will modify
                        var clone = new Dictionary<MethodDefinition, Stack<MethodReference>>(_chain);
                        _chain.Clear();

                        foreach (var call in clone.Where(call => HasBody(call.Key)))
                        {
//                          Console.Error.Write("\rCallTreeSearch: level #{0}, scanning {1,-20}\r", recursionLevel, call.Key.Name + new string(' ',21));
                            _currentStack = call.Value;
                            _currentStack.Push(call.Key);
                            try
                            {
                                _results.Clear();
                                call.Key.Body.Accept(this); // grows _chain and _results
                            }
                            finally
                            {
                                _currentStack.Pop();
                            }
                            _currentStack = null;

                            foreach (var result in _results)
                                yield return result;
                        }
                    }
                }
                finally
                {
                    _busy = false;
                }
            }
        }

        private void ResetToStartingPoints()
        {
            _chain.Clear();
            _seen.Clear();
            foreach (var startingPoint in _startingPoints)
            {
                _chain.Add(startingPoint, new Stack<MethodReference>());
                _seen.Add(startingPoint);
            }
        }

        private static bool HasBody(MethodDefinition methodDefinition)
        {
            return !(methodDefinition.IsAbstract || methodDefinition.Body == null);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

    internal class CompareMembers<T> : IComparer<T>, IEqualityComparer<T>
        where T: class, IMemberReference
    {
        public int Compare(T x, T y)
        { return StringComparer.InvariantCultureIgnoreCase.Compare(KeyFor(x), KeyFor(y)); }

        public bool Equals(T x, T y)
        { return KeyFor(x).Equals(KeyFor(y)); }

        private static string KeyFor(T mr)
        { return null == mr ? "" : String.Format("{0}::{1}", mr.DeclaringType.FullName, mr.Name); }

        public int GetHashCode(T obj)
        { return KeyFor(obj).GetHashCode(); }
    }
}

Notes

  • do some error handling a Resolve() (I have an extension method TryResolve() for the purpose)
  • optionally select usages of MethodReferences in a call operation (call, calli, callvirt ...) only (see //TODO)

Typical usage:

public static IEnumerable<T> SearchCallTree<T>(this TypeDefinition startingClass,
                                               Func<MethodReference, bool> methodSelect,
                                               Func<Instruction, Stack<MethodReference>, T> resultFunc,
                                               int maxdepth)
    where T : class
{
    return new CallTreeSearch<T>(startingClass.Methods.Cast<MethodDefinition>(), methodSelect, resultFunc, maxdepth);
}

public static IEnumerable<T> SearchCallTree<T>(this MethodDefinition startingMethod,
                                               Func<MethodReference, bool> methodSelect,
                                               Func<Instruction, Stack<MethodReference>, T> resultFunc,
                                               int maxdepth)
    where T : class
{
    return new CallTreeSearch<T>(new[] { startingMethod }, methodSelect, resultFunc, maxdepth); 
}

// Actual usage:
private static IEnumerable<TypeUsage> SearchMessages(TypeDefinition uiType, bool onlyConstructions)
{
    return uiType.SearchCallTree(IsBusinessCall,
           (instruction, stack) => DetectRequestUsage(instruction, stack, onlyConstructions));
}

Note the completiion of a function like DetectRequestUsage to suite your needs is completely and entirely up to you (edit: but see here). You can do whatever you want, and don't forget: you'll have the complete statically analyzed call stack at your disposal, so you actually can do pretty neat things with all that information!

这篇关于你看,如果一个方法被调用的方法使用反射内的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆