Wie erkennt man, welche Statusvariablen in einer bestimmten Methode in C # gelesen / geschrieben werden?

8

Was ist der einfachste Weg zu erkennen, ob eine bestimmte Methode eine Membervariable oder -eigenschaft liest oder schreibt? Ich schreibe ein Tool, um in einem RPC-System zu helfen, in dem der Zugriff auf Remote-Objekte teuer ist. Wenn festgestellt werden kann, ob ein bestimmtes Objekt in einer Methode nicht verwendet wird, können wir möglicherweise vermeiden, seinen Status zu serialisieren. Es ist vollkommen vernünftig, es im Quellcode zu tun (aber in kompiliertem Code zu arbeiten wäre toll)

Ich denke, ich kann entweder meinen eigenen einfachen Parser schreiben, ich kann versuchen, einen der existierenden C # Parser zu benutzen und mit dem AST zu arbeiten. Ich bin nicht sicher, ob dies mit Assemblys mit Reflection möglich ist. Gibt es noch andere Möglichkeiten? Was wäre am einfachsten?

EDIT: Danke für all die schnellen Antworten. Lassen Sie mich einige weitere Informationen geben, um die Frage klarer zu machen. Ich bevorzuge definitiv korrekt, aber es sollte definitiv nicht sehr komplex sein. Was ich meine ist, dass wir nicht zu weit gehen können, um nach Extremen oder Unmöglichem zu suchen (wie die erwähnten Delegierten, die erwähnt wurden, was ein großer Punkt ist). Es würde reichen, diese Fälle zu erkennen und davon auszugehen, dass alles genutzt und nicht optimiert werden kann. Ich würde annehmen, dass diese Fälle relativ selten sind. Die Idee ist, dieses Tool an Entwickler außerhalb unseres Teams zu übergeben, die sich nicht um diese Optimierung kümmern sollten. Das Tool nimmt ihren Code und generiert Proxies für unser eigenes RPC-Protokoll. (Wir verwenden nur das Probobuf-Netz für die Serialisierung, aber weder für den W-LAN noch für den REM). Aus diesem Grund muss alles, was wir verwenden, frei sein oder wir könnten das Tool nicht für Lizenzprobleme bereitstellen.

    
cloudraven 14.09.2010, 18:56
quelle

8 Antworten

0

Eric hat recht: Um das gut zu machen, brauchen Sie ein Compiler-Frontend. Was er nicht genug betont hat, ist die Notwendigkeit starker Flussanalysefähigkeiten (oder die Bereitschaft, sehr konservative Antworten zu akzeptieren, die möglicherweise durch Benutzeranmerkungen gemildert werden). Vielleicht meinte er das im Ausdruck "semantische Analyse", obwohl sein Beispiel von "go to definition" nur eine Symboltabelle braucht, keine Flussanalyse.

Ein einfacher C # -Parser konnte nur verwendet werden, um sehr konservative Antworten zu erhalten (z. B. wenn Methode A in Klasse C Bezeichner X enthält, angenommen, dass sie -Klasse liest member X; wenn A no Aufrufe enthält, wissen Sie, dass es Member X nicht lesen kann.

Der erste Schritt darüber hinaus besteht darin, die Symboltabelle und die Typinformationen eines Compilers zu haben (wenn Methode A direkt auf Klassenmitglied X verweist, dann wird angenommen, dass Element X gelesen wird; wenn A ** keine * Aufrufe enthält und erwähnt identifier X nur im Kontext von Zugriffen auf Objekte, die nicht von dieser Klasse sind, dann wissen Sie, dass sie Member X nicht lesen können. Sie müssen sich auch um qualifizierte Referenzen sorgen; Q.X kann Member X lesen, wenn Q mit C kompatibel ist.

Der Klebepunkt sind Aufrufe, die beliebige Aktionen verbergen können. Eine Analyse, die nur auf Analyse- und Symboltabellen basiert, könnte feststellen, dass im Falle von Aufrufen die Argumente nur auf Konstanten oder auf Objekte beziehen, die nicht zu der Klasse gehören, die A repräsentieren könnte (möglicherweise geerbt).

Wenn Sie ein Argument finden, das einen C-kompatiblen Klassentyp hat, müssen Sie nun bestimmen, ob dieses Argument an das gebunden werden kann, was eine Kontrolle und Datenflussanalyse erfordert:

%Vor%

foo könnte einen Zugriff auf X verbergen. Sie brauchen also zwei Dinge: eine Flow-Analyse, um zu bestimmen, ob die anfängliche Zuweisung zu q den Call foo erreichen kann (nicht; q = das könnte alle Aufrufe von foo dominieren) und call Graph-Analyse, um zu ermitteln, welche Methoden foo tatsächlich aufruft, so dass Sie diese für Zugriffe auf Member X analysieren können.

Sie können entscheiden, wie weit Sie gehen wollen, indem Sie einfach die konservative Annahme "A liest X" machen, wenn Sie nicht genug Informationen haben, um das Gegenteil zu beweisen. Dadurch erhalten Sie eine "sichere" Antwort (wenn nicht "richtig" oder was ich lieber "präzise" nennen würde).

Von Frameworks, die hilfreich sein könnten, könnten Sie Mono betrachten, das sicher Symboltabellen analysiert und aufbaut. Ich weiß nicht, welche Unterstützung es für die Flussanalyse oder die Extraktion von Aufrufgraphen bietet. Ich würde nicht erwarten, dass der Mono-zu-IL-Front-End-Compiler viel tun wird, da die Leute diese Maschinen normalerweise im JIT-Teil von JIT-basierten Systemen verbergen. Ein Nachteil ist, dass Mono hinter der "modernen C #" - Kurve stehen könnte; Das letzte Mal hörte ich, dass es nur C # 2.0 behandelte, aber meine Informationen könnten abgestanden sein.

Eine Alternative ist unser DMS Software Reengineering Toolkit und seine C # Front End . (Kein Open-Source-Produkt).

DMS bietet allgemeine Quellcode-Analyse, Baumerstellung / Inspektion / Analyse, allgemeine Symboltabellenunterstützung und eingebaute Maschinerie zur Implementierung von Kontrollflussanalyse, Datenflussanalyse, Punkteanalyse (benötigt für "Was bedeutet Objekt O Punkt zu? "), und rufen Graph Konstruktion. Diese Maschinerie wurde allesamt mit den Java- und C-Frontends von DMS getestet, und die Symboltabellenunterstützung wurde verwendet, um die vollständige C ++ - Namens- und Typauflösung zu implementieren, so dass sie ziemlich effektiv ist. (Sie wollen die Arbeit nicht unterschätzen, die es braucht, um all diese Maschinen zu bauen; wir arbeiten seit 1995 an DMS).

Das C # -Front-End ermöglicht vollständiges C # 4.0-Parsing und vollständigen Baumaufbau. Derzeit werden keine Symboltabellen für C # erstellt (wir arbeiten daran), und das ist ein Nachteil im Vergleich zu Mono. Mit einer solchen Symboltabelle hätten Sie jedoch Zugriff auf alle Flussanalysemaschinen (die mit den Java- und C-Frontends von DMS getestet wurden), und das könnte ein großer Schritt von Mono sein, wenn dies nicht vorgesehen ist.

Wenn Sie das gut machen wollen, haben Sie eine Menge Arbeit vor sich. Wenn Sie bei "einfach" bleiben möchten, müssen Sie nur den Baum analysieren und OK mit sehr konservativ sein.

Sie haben nicht viel darüber gesagt, ob eine Methode an ein Mitglied geschrieben wurde . Wenn Sie den Datenverkehr auf die beschriebene Weise minimieren möchten, möchten Sie die Fälle "Lesen", "Schreiben" und "Aktualisieren" unterscheiden und Nachrichten in beide Richtungen optimieren. Die Analyse ist offensichtlich für die verschiedenen Fälle ziemlich ähnlich.

Schließlich könnten Sie MSIL direkt verarbeiten, um die benötigten Informationen zu erhalten. Sie werden immer noch die Probleme der Flussanalyse und der konservativen Analyse haben.Vielleicht finden Sie das folgende technische Paper interessant; Es beschreibt ein vollständig verteiltes Java-Objektsystem, das die gleiche grundlegende Analyse durchführen muss, die Sie durchführen möchten. und tut dies, IIRC, durch Analysieren von Klassendateien und Durchführen von massivem Bytecode-Umschreiben. Java Orchestra System

    
Ira Baxter 15.09.2010, 09:51
quelle
6

Sie können einfach haben oder korrigieren - was bevorzugen Sie?

Am einfachsten wäre es, die Klasse und den Methodenkörper zu analysieren. Identifizieren Sie dann die Menge der Token, die Eigenschaften und Feldnamen der Klasse sind. Die Teilmenge dieser Token, die im Methodenkörper angezeigt werden, sind die Eigenschaften und Feldnamen, die Sie interessieren.

Diese triviale Analyse ist natürlich nicht korrekt . Wenn du

hättest %Vor%

Dann würden Sie fälschlicherweise schließen, dass M C.Length referenziert. Das ist ein falsches Positiv.

Der richtige Weg, dies zu tun, besteht darin, einen vollständigen C # -Compiler zu schreiben und die Ausgabe seines semantischen Analysators zu verwenden, um Ihre Frage zu beantworten. So implementiert die IDE Funktionen wie "Gehe zur Definition".

    
Eric Lippert 14.09.2010 20:04
quelle
2

Bevor Sie versuchen, diese Art von Logik selbst zu schreiben, würde ich prüfen, ob Sie NDepend nutzen können, um Ihre Anforderungen zu erfüllen .

NDepend ist ein Codeabhängigkeitsanalyse-Tool ... und vieles mehr. Es implementiert einen ausgefeilten Analysator zur Untersuchung von Beziehungen zwischen Code-Konstrukten und sollte in der Lage sein, diese Frage zu beantworten. Es funktioniert auch auf Quelle und IL, wenn ich mich nicht irre.

NDepend macht CQL-Code Query Language verfügbar - mit dem Sie SQL-ähnliche Abfragen gegen die Beziehungen zwischen Strukturen in Ihrem Code schreiben können. NDepend unterstützt Skripts und kann in Ihren Build-Prozess integriert werden.

    
LBushkin 15.09.2010 00:28
quelle
2

Um die LBushkin-Antwort auf NDepend abzuschließen ( Haftungsausschluss: Ich bin einer der Entwickler dieses Tools ), kann NDepend Ihnen dabei helfen. Die Code LINQ Query (CQLinq) stimmt mit Methoden überein, die ...

  • sollte keine RPC-Aufrufe provozieren, aber
  • das Lesen / Schreiben von Feldern von RPC-Typen ,
  • oder das Lesen / Schreiben von -Eigenschaften von RPC-Typen ,

Beachten Sie, wie wir zuerst die 4 Sets definieren: typesRPC , fieldsRPC , propertiesRPC , Methoden, die \ URPC nicht verwenden - und dann Wir passen Methoden an, die gegen die Regel verstoßen. Natürlich muss diese CQLinq-Regel so angepasst werden, dass sie zu Ihren eigenen typesRPC und Methoden ThatShouldntntUseRPC :

passt %Vor%     
quelle
1

Meine Intuition besteht darin, dass das Erkennen, auf welche Elementvariablen zugegriffen wird, falsch ist. Meine erste Vermutung über eine Möglichkeit, dies zu tun, wäre, einfach serialisierte Objekte nach Bedarf anzufordern (vorzugsweise am Anfang jeder Funktion, die sie benötigt, nicht stückweise). Beachten Sie, dass TCP / IP (d. H. Der Nagle-Algorithmus) diese Anforderungen zusammenfassen sollte, wenn sie in schneller Folge erstellt werden und klein sind

    
Brian 14.09.2010 19:03
quelle
0

Mit RPC meinen Sie .NET Remoting? Oder DCOM? Oder WCF?

All dies bietet die Möglichkeit, die prozessübergreifende Kommunikation und Serialisierung über Senken und andere Konstrukte zu überwachen, aber sie sind alle plattformspezifisch, daher müssen Sie die Plattform angeben ...

    
Jeff 14.09.2010 19:06
quelle
0

Sie könnten auf das Ereignis, bei dem eine Eigenschaft gelesen / geschrieben wird, mit einer ähnlichen Schnittstelle wie INotifyPropertyChanged (obwohl Sie offensichtlich nicht wissen, welche Methode das Lesen / Schreiben bewirkt hat.)

    
Pierreten 14.09.2010 19:07
quelle
-2

Ich denke, das Beste, was Sie tun können, ist explizit eine schmutzige Flagge beizubehalten.

    
Steven Sudit 14.09.2010 19:05
quelle