2011-06-23 7 views
7

Nadal staram się, aby moja zasada FxCop działała.Jak uzyskać metodę faktycznie wywoływaną przez instrukcję IL callvirt w FxCop

W związku z tym muszę dowiedzieć się, jakie metody wywołuje metoda. Poprzednio używałem CallGraph.CallersFor() (robiąc to na odwrót, co jest moim ostatecznym celem), ale wydaje się, że ma ten sam problem, który opisałem poniżej.

Jako alternatywę dla użyciu klasy CallGraph Próbowałem odwiedzając wszystkie metody wzywa do budowania słownika, w oparciu o ten kod:

public override void VisitMethodCall(MethodCall call) 
{ 
    Method CalledMethod = (call.Callee as MemberBinding).BoundMember as Method; 
    // .... 
} 

Jednak okazuje się, że jeśli zwana metoda jest w klasie pochodnej nadpisuje metodę klasy bazowej, następnie BoundMember jest metodą klasy bazowej, a nie klasy potomnej (która jest faktycznie wywoływana).

Pytanie: Jak mogę uzyskać metodę, która zostanie wywołana w przypadku instrukcji IL callvirt w FxCop?

Odpowiedz

2

Okazało się w moim przypadku, że nie potrzebuję tego dokładnie (co jest dobre, ponieważ nie mam dokładnej odpowiedzi w tym przypadku).

Ponieważ FxCop jest statycznym sprawdzającym, nigdy nie może poznać typu instancji obiektu wskazywanego przez zmienną, która mogłaby zostać zadeklarowana jako typ podstawowy. Dlatego uważam, że to, o co proszę, jest niemożliwe.

Moje rozwiązanie polega na tym, że podczas budowania drzewa wywołań dodam dodatkowe odwołania do klasy bazowej "wywołującej" dowolne klasy pochodne. W ten sposób, jeśli wywołasz metodę na klasie bazowej, a metoda klasy pochodnej może zostać wywołana, to w ten sposób można podążać za drzewem wywołania.

Zobacz poniżej mojej klasy używanego przez FxCop klas zasada:

public class CallGraphBuilder : BinaryReadOnlyVisitor 
{ 
    public Dictionary<TypeNode, List<TypeNode>> ChildTypes; 

    public Dictionary<Method, List<Method>> CallersOfMethod; 

    private Method _CurrentMethod; 

    public CallGraphBuilder() 
     : base() 
    { 
     CallersOfMethod = new Dictionary<Method, List<Method>>(); 
     ChildTypes = new Dictionary<TypeNode, List<TypeNode>>(); 
    } 

    public override void VisitMethod(Method method) 
    { 
     _CurrentMethod = method; 

     base.VisitMethod(method); 
    } 

    public void CreateTypesTree(AssemblyNode Assy) 
    { 
     foreach (var Type in Assy.Types) 
     { 
      if (Type.FullName != "System.Object") 
      { 
       TypeNode BaseType = Type.BaseType; 

       if (BaseType != null && BaseType.FullName != "System.Object") 
       { 
        if (!ChildTypes.ContainsKey(BaseType)) 
         ChildTypes.Add(BaseType, new List<TypeNode>()); 

        if (!ChildTypes[BaseType].Contains(Type)) 
         ChildTypes[BaseType].Add(Type); 
       } 
      } 
     } 
    } 

    public override void VisitMethodCall(MethodCall call) 
    { 
     Method CalledMethod = (call.Callee as MemberBinding).BoundMember as Method; 

     AddCallerOfMethod(CalledMethod, _CurrentMethod); 

     Queue<Method> MethodsToCheck = new Queue<Method>(); 

     MethodsToCheck.Enqueue(CalledMethod); 

     while (MethodsToCheck.Count != 0) 
     { 
      Method CurrentMethod = MethodsToCheck.Dequeue(); 

      if (ChildTypes.ContainsKey(CurrentMethod.DeclaringType)) 
      { 
       foreach (var DerivedType in ChildTypes[CurrentMethod.DeclaringType]) 
       { 
        var DerivedCalledMethod = DerivedType.Members.OfType<Method>().Where(M => MethodHidesMethod(M, CurrentMethod)).SingleOrDefault(); 

        if (DerivedCalledMethod != null) 
        { 
         AddCallerOfMethod(DerivedCalledMethod, CurrentMethod); 

         MethodsToCheck.Enqueue(DerivedCalledMethod); 
        } 
       } 
      } 
     } 

     base.VisitMethodCall(call); 
    } 

    private void AddCallerOfMethod(Method CalledMethod, Method CallingMethod) 
    { 
     if (!CallersOfMethod.ContainsKey(CalledMethod)) 
      CallersOfMethod.Add(CalledMethod, new List<Method>()); 

     if (!CallersOfMethod[CalledMethod].Contains(CallingMethod)) 
      CallersOfMethod[CalledMethod].Add(CallingMethod); 
    } 

    private bool MethodHidesMethod(Method ChildMethod, Method BaseMethod) 
    { 
     while (ChildMethod != null) 
     { 
      if (ChildMethod == BaseMethod) 
       return true; 

      ChildMethod = ChildMethod.OverriddenMethod ?? ChildMethod.HiddenMethod; 
     } 

     return false; 
    } 
}