Skip to content

Commit 6de15f9

Browse files
committed
documentation _ plugin explorer SystemJS option does work again
SQUASHED: AUTO-COMMIT-demos-tom-assets-ablauf.png,AUTO-COMMIT-demos-tom-assets-Klassendiagramm.png,AUTO-COMMIT-demos-tom-assignment-plugin.js,AUTO-COMMIT-demos-tom-babel-plugin-tracer.js,AUTO-COMMIT-demos-tom-condition-plugin.js,AUTO-COMMIT-demos-tom-index.md,AUTO-COMMIT-demos-tom-playground.js,AUTO-COMMIT-demos-tom-plugin-explorer-worker.js,AUTO-COMMIT-demos-tom-Sections.js,AUTO-COMMIT-demos-tom-trace.js,AUTO-COMMIT-demos-tom-TraceLogParser.js,AUTO-COMMIT-demos-tom-wrapAST.js,AUTO-COMMIT-src-client-reactive-babel-plugin-active-expression-rewriting-index.js,AUTO-COMMIT-src-components-tools-lively-plugin-explorer.js,AUTO-COMMIT-src-components-tools-lively-plugin-explorer-playground.workspace,AUTO-COMMIT-src-components-tools-plugin-selector-list.json,AUTO-COMMIT-src-components-tools-trace-visualization.html,
1 parent f8bf473 commit 6de15f9

File tree

16 files changed

+217
-153
lines changed

16 files changed

+217
-153
lines changed

demos/tom/Sections.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,6 @@ export class TraceSection {
6464
}
6565
}
6666

67-
export class PluginSection extends TraceSection {
68-
69-
}
70-
7167
export class FunctionSection extends TraceSection {
7268
constructor() {
7369
super(...arguments);

demos/tom/TraceLogParser.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Event, eventTypes } from 'demos/tom/Events.js';
2-
import { FunctionSection, PluginSection, TraceSection } from 'demos/tom/Sections.js';
2+
import { FunctionSection, TraceSection } from 'demos/tom/Sections.js';
33

44
class EarlyReturn {
55
constructor(type) {
@@ -179,7 +179,7 @@ export default class TraceLogParser {
179179
}
180180

181181
parsePlugin(sections) {
182-
const plugin = new PluginSection(this.consume().data);
182+
const plugin = new TraceSection(this.consume().data);
183183
sections.push(plugin);
184184
this.defaultParse(plugin, []);
185185
return plugin;
37.3 KB
Loading

demos/tom/assets/ablauf.png

117 KB
Loading

demos/tom/assignment-plugin.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export default function({types: t}) {
2+
return {
3+
name: 'assignment-plugin',
4+
visitor: {
5+
AssignmentExpression(path) {
6+
console.log(path.node.loc.start);
7+
}
8+
}
9+
}
10+
}

demos/tom/babel-plugin-tracer.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,6 @@ export default function({ types: t }) {
101101
name = t.stringLiteral(callee.node.name || 'anonymous function');
102102
}
103103

104-
105-
106104
const loc = location(callee.node, this);
107105

108106
const aboutToEnter = callOnTrace('aboutToEnter',

demos/tom/condition-plugin.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export default function({types: t}) {
2+
return {
3+
name: 'condition-plugin',
4+
visitor: {
5+
Conditional(path) {
6+
debugger
7+
const string = t.stringLiteral('afterTest');
8+
path.get('test')
9+
.insertAfter(string);
10+
}
11+
}
12+
}
13+
}

demos/tom/index.md

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,78 @@
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+
![](assets/ablauf.png){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+
135

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).
436

537
## 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.
739

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).
941

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.
1143

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.
1345

1446
## 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)
1554

1655
## 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+
![](assets/Klassendiagramm.png){height=600px}
71+
1872

1973

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.
2276

2377
**Sollte die Kopie zur Weiterentwicklung verwendet werden, nicht vergessen die gewünschten Änderungen am Ende in das originale Plugin zu kopieren.**
2478

demos/tom/playground.js

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
export default function({types: t}) {
2-
return {
3-
name: 'test',
4-
visitor: {
5-
Conditional(path) {
6-
debugger
7-
const string = t.stringLiteral('afterTest');
8-
path.get('test')
9-
.insertAfter(string);
10-
},
1+
export default function({types: t, template}) {
2+
return {
3+
name: 'demo',
4+
visitor: {
5+
Conditional(path) {
6+
const log = template('console.log("afterTest")')
7+
path.get('test')
8+
.insertAfter(log());
9+
},
1110

12-
AssignmentExpression(path) {
13-
console.log(path.node.loc.start);
14-
}
15-
}
11+
AssignmentExpression(path) {
12+
console.log(path.node.loc.start);
13+
}
1614
}
15+
}
1716
}

0 commit comments

Comments
 (0)