|
| 1 | +## Projektidee |
| 2 | +Das Projekt soll dabei helfen Babel plugins zu debuggen. Dafür bedient es sich dynamischer Analyse und zeichnet zum einen ausgeführte Instruktionen aus (über das trace plugin), zum anderen werden Veränderungen am AST aufgezeichnet und den Instruktionen zugeordnet. Diese Aufzeichnung werden dann visualisiert. Dadurch liegt die gesamte Ausführungshistorie offen und kann beliebig exploriert vorwärts und rückwärts exploriert werden. Die Aufzeichnung der Veränderungen ermöglicht es auch die Zwischenschritte der AST Transformation zu betrachten. Es wird also für die gesamte Aufzeichnung aufgelöst, welche Instruktionen ausgeführt wurden und welche Änderungen am AST diese vorgenommen haben. |
| 3 | + |
| 4 | +Das aktuelle Dokument versucht einen Überblick über das Projekt zu verschaffen. |
| 5 | + |
| 6 | + |
| 7 | +## Babel plugin tracer (trace plugin) |
| 8 | +Ein Babel Plugin, das auf andere Plugins angewandt wird. Es geht davon aus, dass es als einziges Plugin auf diese angewandt wird! Es behandelt noch nicht alle Fälle die in source code vorkommen können, aber die grundlegenden wie Funktionsaufrufe sind abgedeckt. |
| 9 | + |
| 10 | +Das Plugin umgibt die meisten Instruktionen mit einem log, dass diese gleich beginnen und wenn sie vorbei sind (vereinfachtes Beispiel: ```if(true) {}``` wird zu ```trace.startCondition(); if(true) {}; trace.endCondition()```). Die logs bekommen eine Nummer als Parameter, die die originale Position der Anweisung kodieren. Soll ein Wert für den log gespeichert werden, nimmt die log Funktion diesen entgegen und gibt ihn auch wieder zurück, um eine korrekte Ausführung zu erhalten (vereinfachtes Beispiel: ```if(true) {}``` wird zu ```if(trace.test(true)) {}```). |
| 11 | + |
| 12 | +## AST wrapping |
| 13 | +Um Veränderungen am AST mitzuschreiben wird dieser gewrapt. Dazu wird der gesamte AST traversiert und für alle AST Knoten werden properties durch getter und setter ersetzt. Die setter können dann immer mitschneiden welche Veränderung vorgenommen wurde (da getter/setter zusätzliches Verhalten einführen, aber sich syntaktisch nicht von normalen public properties unterscheiden arbeiten sie ohne Probleme mit dem bestehenden Babel code). Veränderungen an Arrays können auf diesem Weg nicht getrackt werden, weswegen diese in Proxies gepackt werden, die dann Veränderungen melden können. |
| 14 | + |
| 15 | +## Copy AST |
| 16 | +Das mitschneiden der Veränderungen erfordert es Teile des ASTs zu kopieren (sonst würden die kopierten Objekte immer noch durch AST-Transformationen verändert werden wenn nur eine Referenz gespeichert wird). Da sich in der Praxis gezeigt hat, dass es zu unperformant ist des gesamten AST zu kopieren, sollte es auch wirklich nicht viel mehr als nötig sein. |
| 17 | + |
| 18 | +Um dies umzusetzen werden die Knoten des ursprüngliche AST des zu transformiernden codes mit IDs versehen. Diese Identifizieren die Knoten zum einen eindeutig, geben aber auch an welcher Knoten bereits existiert hat. Wird eine Änderung vorgenommen kann über die ID eindeutig bestimmt werden welchem Knoten diese zuzuordnen ist (auch über serialisierungsgrenzen hinweg, da alles vom WebWorker serialisiert werden muss; siehe nächster Abschnitt) Wird ein neuer Knoten eingefügt kann dieser daran erkannt werden, dass er keine ID besitzt. Dieser muss dann eine ID erhalten, komplett kopiert werden und ein gewrapt werden. Kopiert werden nur neue AST Knoten. Für bereits bekannte AST Knoten wird ein Objekt mit deren ID abgelegt. Dieses kann dann unter Kenntnis des gesamten ASTs vor dieser Veränderung aufgelöst werden (da der Knoten ja zu dem Zeitpunkt bereits bekannt war, und somit auch sein muss wenn alle Veränderungen vor der aktuellen angewandt wurden). Alles andere wird mit einer normalen deep Copy kopiert (in der Annahme, das dies nur kleine Objekte sind und nicht so teuer). |
| 19 | + |
| 20 | + |
| 21 | +## Plugin explorer worker |
| 22 | +Der Plugin explorer worker nimmt source code und ein Array von Plugin Urls entgegen und generiert aus diesen einen Trace der Ausführung. Dieser Prozess wurde in eine WebWorker ausgelagert, um eine frische JavaScript Umgebung zu erhalten. Normalerweise hat lively ein vorkonfiguriertes SystemJS, welches sich um imports von Files kümmert. Im WebWorker kann SystemJS so konfiguriert werden, dass nur das Trace Plugin auf allen importierten Code angewandt wird (wodurch auch imports von Plugins mit dem nötigen Trace code versehen werden). |
| 23 | + |
| 24 | +Der grobe Ablauf ist wie folgt: |
| 25 | + |
| 26 | + |
| 27 | +{height=400px} |
| 28 | + |
| 29 | + |
| 30 | +Die gesendeten Daten müssen serialisiert werden um diese vom WebWorker in den normalen Kontext zu schicken, wobei Objektidentität verloren geht. |
| 31 | + |
| 32 | +## Plugin load promise |
| 33 | +Eine einfache Art den Plugin explorer worker zu nutzen. Aktuelle wird jedes mal ein komplett neuer Worker erstellt. Dies bedeutet auch, dass potenziell mehrmals nutzbare imports, wie zB von SystemJS, nicht ausgenutzt werden. Die Worker werden jedes mal neu erstellt, da ich bisher noch keine Weg fand imports von plugins Zuverkässig wieder zu unloaden. Diese müssen auch jedes mal neu geladen werden, damit sie richtig indiziert werden. Werden sie nicht richtig unloaded kann das nicht passieren und spätere Schritte (zB auflösen von Positionen) wird fehlerhaft, da Daten nicht erstellt/gesammelt wurden. |
| 34 | + |
1 | 35 |
|
2 | | -## AST worker |
3 | | -Der AST worker nimmt source code und ein Array von Plugin Urls entgegen und generiert aus diesen einen Trace der Ausführung. Dieser Prozess wurde in eine WebWorker ausgelagert, um eine frische JavaScript Umgebung zu erhalten. Normalerweise hat lively ein vorkonfiguriertes SystemJS, welches sich um imports von Files kümmert. Im WebWorker kann SystemJS so konfiguriert werden, dass nur das Trace Plugin auf allen importierten Code angewandt wird (wodurch auch imports von Plugins mit dem nötigen Trace code versehen werden). |
4 | 36 |
|
5 | 37 | ## Trace |
6 | | -Trace ist die zentrale Klasse, für welche während der gesamten Aufzeichnung der Plugins sichergestellt ist, dass sie global über ```window[Trace.traceIdentifierName]``` verfügbar ist. |
| 38 | +```Trace``` ist die zentrale Klasse, für welche während der gesamten Aufzeichnung der Plugins sichergestellt ist, dass eine Instanz global über ```window[Trace.traceIdentifierName]``` verfügbar ist. |
7 | 39 |
|
8 | | -Ihre Aufgabe ist es zum einen den Tracelog zu generieren und zu speichern. Dazu stellt die Klasse Methode bereit, die die nötigen Instanzen von ```Event```, ```ErrorEvent``` oder ```ASTChangeEvent``` mit den nötigen Daten erstellen. |
| 40 | +Ihre Aufgabe ist es zum einen den Tracelog zu generieren und zu speichern. Dazu stellt die Klasse Methode bereit, die die nötigen Instanzen von ```Event```, ```ErrorEvent``` oder ```ASTChangeEvent``` mit den nötigen Daten erstellen. Diese Methoden werden aus dem vom trace plugin generierten code aufgerufen (also den transformierten plugins). |
9 | 41 |
|
10 | | -Auch erstellt der Trace die IDs für AST Knoten, die aus der Nummer der aktuell ausgeführten Pluginmethode (leider nur der aus den Babelplugins und nicht den in ```NodePath.traverse``` aufgerufenden Methoden) und einer für den gesamten AST globalen Nummer. |
| 42 | +Auch erstellt der Trace die IDs für AST Knoten, die aus der Nummer der aktuell ausgeführten Pluginmethode und einer für den gesamten AST globalen Nummer. |
11 | 43 |
|
12 | | -Trace registriert weiterhin die Positionen von AST Knoten im Quelltext und speichert diese mit einigen Optimierungen. Die dadurch vergebenen Positions-IDs kann Trace auh wieder auflösen. |
| 44 | +```Trace``` registriert weiterhin die Positionen von AST Knoten im Quelltext und speichert diese mit einigen Speicheroptimierungen. Die dadurch vergebenen Positions-IDs kann Trace auch wieder auflösen. |
13 | 45 |
|
14 | 46 | ## Events |
| 47 | +Es gibt normale ```Event```s, ```ErrorEvent```s und ```ASTChangeEvent```s (diese erben nicht von Event, da sie nicht genug Gemeinsamkeiten habe, obwohl das der Name implizieren würde). |
| 48 | + |
| 49 | +```Event```s sind für Instruktionene, ```ErrorEvent```s für Fehler, die die Ausführung des Tracings unterbrechen (aktuell wird es also maximal eins im log geben), und ```ASTChangeEvent```s sind für Veränderungen des ASTs. |
| 50 | + |
| 51 | +```Event```s können ```ASTChangeEvent```s enthalten. Dies ist der Fall wenn eine Instruktion ein Veränderung am AST vornimmt. Dann wird der TraceLogParser das ```ASTChangeEvent```s dem zugehörigen ```Event``` zuordnen. |
| 52 | + |
| 53 | +```ASTChangeEvent```s enthalten alle Informationen über einen Veränderung des ASTs und können diese auf einen AST anwenden (gegeben diese ist im richtigen Zustand, d.h. alle AST Veränderungen, die vor der aktuellen Veränderung passieren, wurden auf den AST angewandt. Dies ist der Fall, da das ```ASTChangeEvent``` eventuell auf einen AST knoten zugreigen will, der erst durch eine Änderung am AST erstellt wurde) |
15 | 54 |
|
16 | 55 | ## TraceLogParser |
17 | | -Der ```TraceLogParser``` ist ein recursive Descent Parser, der dafür verantwortlich ist den generierten Log zu säubern und zusammengehörige Events zusammenzufassen. Es erzeugt aus den ```Event```s eine Composite aus ```TraceSection``` und ```Event```s. |
| 56 | +Der ```TraceLogParser``` ist ein recursive Descent Parser, der dafür verantwortlich ist den generierten Log zu säubern und zusammengehörige Events zusammenzufassen. Es erzeugt aus den ```Event```s, ```ErrorEvent```s und ```ASTChangeEvent```s ein Composite aus ```TraceSection``` und ```Event```s,```ErrorEvent```s und ```ASTChangeEvent```s. |
| 57 | + |
| 58 | +Um ihn besser zu verstehen sollten zunächst die Struktur der geparsten Events (sie übernehmen die Rolle der Tokens beim sonstigen parsen) verstanden werden. Die meisten treten in Paaren auf, manche in speziellen Fällen aber nicht (zB kein conditionEnd wenn innerhalb der Bedingung ein return ist). |
| 59 | + |
| 60 | +## TraceVisualization |
| 61 | +Die vom ```TraceLogParser``` zurückgegeben Liste von Composites wird von der ```TraceVisualization``` visualisiert. Sie zeigt das Composite in einer Liste verschachtelt (TraceSection beinhalten wieder andere und Events) an. Über diese kann gehovert werden. Dann wird gezeigt an welcher Stelle in welchem Plugin sich die Instruktion über die gehovert wurde befindet und wie der AST des zu transformierenden Quelltextes vor und nach dieser Instruktion aussieht. |
| 62 | + |
| 63 | +Instruktionen die eine Veränderung am AST herbeiführen und alle in der Composite Struktur über ihr stehenden werden orange eingefärbt (zur besseren Navigation). Fehler werden rot eingefärbt (da dies Fehler sind die die Ausführung abgebrochen haben sind sie immer die letzte Instruktion). |
| 64 | + |
| 65 | +Eine Visualiserung kann im Plugin explorer angefordert werden indem auf das Lupensymbol bei den Buttons oben rechts gedrückt wird (dies stößt natürlich den gesamten Prozess an und visualisiert dann die im Plugin explorer aktuell selektierten Plugins und den aktuell eingegeben Quelltext zusammen). |
| 66 | + |
| 67 | +## Gesamteinordnung |
| 68 | +Das folgende Klassendiagramm dient dazu einen Überblick über den Zusammenhang der Klassen nach Generierung der Event (also mit den Ergebnissen des workers) zu erhalten. |
| 69 | + |
| 70 | +{height=600px} |
| 71 | + |
18 | 72 |
|
19 | 73 |
|
20 | | -## Debug Trace-Plugin |
21 | | -Es ist möglich das tracer-plugin mit sich selbst zu debuggen. Dazu wird einfach eine Kopie des Plugins (aktuell in demos/tom/plugin-backup.js) an den WebWorker übergeben als Plugin übergeben. Die Kopie ist nötig, da SystemJS das tracer-plugin ganz am Anfang laden muss und dies nun gecached vorhält. Würde man versuchen das originale tracer-plugin noch einmal zu laden würde einfach auf diese Version zugegriffen werden, anstatt das Plugin zu transformieren. Dies wird mit diesem Workaround eines zweiten Files umgangen. |
| 74 | +## How to Debug the trace plugin |
| 75 | +Es ist möglich das trace plugin mit sich selbst zu debuggen. Dazu wird einfach eine Kopie des Plugins (aktuell in demos/tom/plugin-backup.js) an den WebWorker übergeben als Plugin übergeben. Die Kopie ist nötig, da SystemJS das trace plugin ganz am Anfang laden muss und dies nun gecached vorhält. Würde man versuchen das originale trace plugin noch einmal zu laden würde einfach auf diese Version zugegriffen werden, anstatt das Plugin zu transformieren. Dies wird mit diesem Workaround eines zweiten Files umgangen. |
22 | 76 |
|
23 | 77 | **Sollte die Kopie zur Weiterentwicklung verwendet werden, nicht vergessen die gewünschten Änderungen am Ende in das originale Plugin zu kopieren.** |
24 | 78 |
|
|
0 commit comments