7 Stimmen

Wie erhält man die Methode, die tatsächlich von der IL-Anweisung callvirt innerhalb von FxCop aufgerufen wird?

Ich versuche immer noch, meine FxCop-Regel zum Laufen zu bringen.

In diesem Zusammenhang muss ich herausfinden, welche Methoden eine Methode aufruft. Bisher habe ich CallGraph.CallersFor() (in umgekehrter Reihenfolge, was sowieso mein Ziel ist), aber es scheint das gleiche Problem zu haben, das ich unten beschreibe.

Als Alternative zur Verwendung der CallGraph Klasse habe ich versucht, alle Methodenaufrufe zu besuchen, um ein Wörterbuch zu erstellen, basierend auf diesem Code:

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

Es stellt sich jedoch heraus, dass, wenn die aufgerufene Methode in einer abgeleiteten Klasse ist, die eine Methode der Basisklasse außer Kraft setzt, dann die BoundMember ist die Methode der Basisklasse, nicht die Methode der Kindklasse (die tatsächlich aufgerufen wird).

Frage: Wie kann ich die Methode erhalten, die im Falle einer callvirt IL-Anweisung in FxCop aufgerufen wird?

2voto

George Duckett Punkte 31040

In meinem Fall hat sich herausgestellt, dass ich das eigentlich nicht brauche (was gut ist, weil ich in diesem Fall keine genaue Antwort habe).

Da FxCop ein statischer Checker ist, kann er niemals den Typ der Instanz des Objekts kennen, auf das die Variable zeigt, die als Basistyp deklariert sein könnte. Daher glaube ich, dass das, wonach ich frage, unmöglich ist.

Meine Lösung hier ist es, beim Aufbau des Aufrufs Baum, füge ich in zusätzliche Referenzen für eine Basisklasse "Aufruf" alle abgeleiteten Klassen. Wenn ich auf diese Weise eine Methode der Basisklasse aufrufe und die Methode der abgeleiteten Klasse aufgerufen werden könnte, kann ich den Aufrufbaum auf diese Weise verfolgen.

Siehe unten für meine Klasse, die von den FxCop-Regelklassen verwendet wird:

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;
    }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X