Ein Jahr JavaScript-Konkurrent Dart

Es ist etwa ein Jahr her, dass Google mit Dart eine zweite Programmiersprache vorgestellt hatte. Seitdem haben sich Entwickler und Community um die Weiterentwicklung der Sprache, der Bibliotheken und der Werkzeuge gekümmert. Mittlerweile sind sie auf einem guten Weg zum ersten Meilenstein.

In Pocket speichern vorlesen Druckansicht 2 Kommentare lesen
Lesezeit: 22 Min.
Von
  • Frank Müller
Inhaltsverzeichnis

Es ist etwa ein Jahr her, dass Google mit Dart eine zweite Programmiersprache vorgestellt hatte. Seitdem haben sich sowohl das Team um Gilad Bracha und Lars Bak als auch die Community um die Weiterentwicklung der Sprache, der Bibliotheken und der Werkzeuge gekümmert. Mittlerweile ist man auf einem guten Weg zum ersten Meilenstein.

Anfang des Jahres stellte die iX die Sprache Dart, die ihre Wurzeln in JavaScript nicht verleugnen kann, erstmals vor [1]. Seinerzeit war das Fazit, dass noch einige Zeit bis zur Reife der Sprache benötigt werde. Mittlerweile haben ihre Entwickler diverse Ecken und Kanten abgeschliffen und fehlende Konzepte hinzugefügt. Damit hat sich Dart mehr von JavaScript emanzipiert und fühlt sich nun reifer an. Hierzu passen der Ausbau der Bibliotheken und deren streckenweise Reorganisation sowie die Ergänzung des sich in Eclipse integrierenden Editors um zahlreiche hilfreiche Features.

Im Großen und Ganzen hat sich Dart wenig verändert, der Kern ist in wichtigen Konstrukten stabil geblieben. Wichtiger sind hingegen die kleineren Korrekturen sowohl in den Features als auch im Komfort. Eine der Änderungen betrifft das Typsystem. Es betrachtet Typen selbst als Objekte, sie lassen sich über die Getter-Methode type ermitteln. Ebenso können Entwickler die Namen von Typen direkt in Ausdrücken verwenden. Das ermöglicht einfache Vergleiche wie myObject == MyType. Die Definition von Gettern wie dem obigen wurde in der Notation ebenfalls vereinfacht, sodass sich auf die nicht benötigten Klammern verzichten lässt. Es genügt ein einfaches

num get answer => 40 + 2; 

zur Definition eines Getters, für den entsprechende Setter lautet die Notation passenderweise

set answer(num value) => ...;

Eine weitere Vereinfachung ist der Verzicht auf die explizite Definition von Interfaces. Vielmehr leitet sich durch die Definition einer konkreten oder abstrakten Klasse automatisch ein gleichnamiges Interface ab. Es sei also eine Klasse Duck wie im Folgenden definiert:

class Duck {
final String name;
Duck(this.name);

quack(other) => print("Hi, ${other}, I'm ${this.name}.");
}

In dem Fall kann eine Klasse Frog das Interface nutzen, ohne gleich die ganze Klasse zu erben.

class Frog implements Duck { 
quack(other) => print("I'm a quacking frog, ${other}.");
}

Aus Sicht der Go-Programmierung ist die Entscheidung nicht glücklich. Der Ansatz dort ist genau entgegengesetzt. Mit der Definition eines Interfaces beschreibt man die an eine Instanz gestellte Erwartungshaltung. Für die Typen ist dabei nicht explizit anzugeben, ob sie ein Interface erfüllen. Das ergibt sich implizit aus den implementierten Methoden. So sagt

type Quackable interface { 
Quack(p Pitch)
}

aus, dass etwas in einer Tonhöhe quaken kann. Ein eigener Typ, beispielsweise Duck, kann das implementieren.

type Duck struct { ... } 

func (d *Duck) Quack(p Pitch) { ... }

Man beachte, dass kein explizites implements benötigt wird. Deshalb funktioniert ein Konzert der Form

func Concert(q Quackable) { ... } 

mit Enten und Fröschen. Erfüllt ein Typ nicht das von ihm geforderte Interface, reklamiert der Compiler es bei der Übersetzung. Diese Denkweise scheint dem Autor für Schnittstellen natürlicher. Dafür haben die Entwickler eine andere Schwäche bezüglich abstrakter Klassen getilgt. Sie lassen sich nun im Gegensatz zu älteren Versionen nicht mehr instanziieren. Das wird, neben der Warnung im Editor, ab sofort zur Laufzeit entdeckt, in dessen Folge ein AbstractClassInstantiationError geworfen wird.

Ein weiterer Block mit mehreren Veränderungen betrifft das Exception Handling. Zur Vermeidung von Fehlern ist es beispielsweise nicht mehr gestattet, eine null zu werfen. Sie geben dem Fänger einer Exception keine gute Hilfestellung zur Fehlerbehebung. Das Werfen haben die Entwickler dafür vereinfacht. Nun ist throw ein Ausdruck und lässt sich daher in Einzeilern nutzen. Das zeigt sich zum Beispiel in

throw const NotImplementedException();

für noch in Arbeit befindliche Methoden, sodass sich frühzeitig Interfaces erfüllen lassen (siehe oben). Gleichzeitig hat sich für das Fangen spezieller Exceptions die Syntax verändert. Sie lautet nun

try { 
...
} on NotImplementedException catch (ex) { ... }

Die Spezialisierung, was gefangen werden soll, wird somit besser von der Bindung an eine Variable getrennt. Das vermeidet die in der Vergangenheit durch die optionale Typisierung aufgetretene Unklarheit, ob ein catch (foo) jede gefangene Exception an die Variable foo binden oder eine Exception vom Typ foo fangen soll.

Eine Eigenschaft der Sprache, die Anhängern von Smalltalk gefallen dürfte, ist die Nutzung der benannten Parameter als Ergänzung zu positionierten Parametern. Beide ließen sich unter Verwendung einer gemeinsamen Syntax bisher optional angeben. So erlaubt die Definition

setRange([num min, num max]) { ... } 

den Aufruf mit beiden, einem oder keinem Parameter. Durch die gemeinsame Syntax für benannte und positionierte Parameter ist

setRange(4711); 

erlaubt und bedeutet, dass min den Wert 4711 und max den Wert null hat. Der erste Schritt zur Verbesserung ist die Differenzierung zwischen benannten und positionierten Parameter, erstere in geschweiften Klammern, letztere wie bisher in eckigen Klammern. Die obige Definition lässt sich in

setRange({num min, num max}) { ... } 

ändern, und der Aufruf lautet nun zum Beispiel

setRange(max: 4711); 

Eine weitere Verbesserung bringen Standardwerte mit sich, die bei der Definition mit einem Doppelpunkt an die Variable gehängt werden.

setRange({num min: 0, num max: 10000}) { ... } 

Nun hätte min bei einem Aufruf wie oben nicht den Wert null, sondern 0. Doch was ist, wenn der Standardwert nicht definiert ist? Wie ist nun zu unterscheiden, ob ein optionaler Wert erlaubterweise mit null gesetzt oder einfach nicht angegeben wurde? Hierfür haben die Entwickler das Fragezeichen als Testoperator eingeführt. Im folgenden Listing lässt sich etwa die Übergabe eines Ports prüfen.

Database open(String addr, [num port]) {
if (?port) {
...
} else {
...
}
}

Eine weitere für Smalltalk-Entwickler bekannte Neuerung ist das Cascading von Methodenaufrufen. Eine Methode ist nicht mehr gezwungen, die eigene Instanz zurückzugeben, um eine Verkettung herzustellen. Vielmehr lassen sich mit dem einfachen Konstrukt zweier Punkte mehrere Methodenaufrufe auf der gleichen Instanz ausführen.

list..add('the')..add('quick')..add('brown')..add('fox'); 

Eine interessante Verhaltensänderung ist das Lazy Init von Variablen mit dem Modifier final. So lässt sich auf oberster Ebene

final cache = new Cache(); 
var addressFile = new AddressFile(cache);

definieren. Erst beim ersten Zugriff auf addressFile, beispielsweise mit addressFile.add(anAddress), werden die beiden Variablen initialisiert. Das Gleiche gilt für Felder mit dem Modifier static final. Auch sie werden erst beim Zugriff initialisiert und sind dann final.

class Transaction { 
static final startTime = new DateTime.now()
}

Nun bedeutet final nicht, dass die Werte konstant sind, nur die Variablen beziehungsweise Felder sind es. Was also tun, wenn man einen konstanten Wert benötigt? Hier erlaubt Dart die Verwendung des Modifiers const, sodass nun

class MyClass {
static const myConstValue = 42;
static const myConstValueList = const [myConstValue];
}

erlaubt ist. Ebenso lassen sich Funktionen der obersten Ebene und statische Methoden in konstanten Ausdrücken nutzen. Bisher wurde darauf verzichtet, weil Closures oder Methoden auf einem externen und nicht konstanten Zustand beruhen. Das trifft jedoch nicht auf Funktionen und statische Methoden zu:

pong() => print('pong');

class Pinger {
static const defaultPinger = const Pinger(pong);

final pinger;
const Pinger(this.pinger);
}

Als weitere Änderung rund um Variablen und Felder im ersten Meilenstein der Sprache lassen sich Instanz-Felder nun auch im Gegensatz zu den bisherigen Versionen mit nicht konstanten Werten initialisieren. So ist die Definition

class Order { 
List<Item> items = <Item>[]
}

erlaubt, und items wird automatisch bei der Instanziierung des Objekts initialisiert. Das geschieht vor der Ausführung des Konstruktors. Die Verwendung von this innerhalb der Feld-Initialisierungen ist nicht gestattet, da eventuell weitere Initialisierungen noch nicht stattgefunden haben.

Mit diesem Milestone implementiert die Klasse Object das Interface Hashable. Damit verfügt jedes Objekt über die Methode hashCode(), auch ohne sie explizit zu implementieren. Als Folge lassen sich alle Objekte als Schlüssel in Maps verwenden. Einzig Map-Literale im Quelltext erwarten einen String als Schlüssel.

Strings haben auch ein paar Veränderungen erfahren. Eine auffallende ist die nun nicht mehr erlaubte Konkatenation mit dem Operator +, da das in gemischten Ausdrücken zu unerwarteten Ergebnissen führen konnte. Das bevorzugte Mittel der Wahl ist jetzt die Interpolation, beispielsweise "Name: ${foo}" oder "Summe: ${1 + 1}". Die Darstellung von Objekten als String lässt sich über die Implementierung der Methode toString() definieren, was dann in der Interpolation zum Einsatz kommt. Werden im Code sogenannte Raw Strings benötigt, greift die letzte Änderung bezüglich der Strings. Bisher wurden sie mit @ eingeleitet. Das Zeichen ist aber nun der Definition von Metadaten vorbehalten. Dafür werden Raw Strings jetzt mit dem kleinen Buchstaben r gekennzeichnet. So führt

var raw = r'Raw String mit \n und ${1 + 1}.'; 

den Zeilenumbruch und die Interpolation nicht aus. Das auf diesem Wege frei gewordene @ kommt dann für Metadaten zum Einsatz, die aus dem Zeichen und einem konstanten Ausdruck bestehen. Sie lassen sich vor der Deklaration von Klassen, Typedefs, Konstruktoren, Fabrikmethoden, Funktionen, Feldern, Parametern oder Variablen aufführen. Die Ausdrücke selbst sind in den Formen @native und @42, aber auch @const <int>[1, 2, 3, 4] möglich. Das Auslesen erfolgt über die noch zu definierende Reflection API. Erste Definitionen wie deprecated und override finden sich in der meta-Bibliothek.

Neben den größeren Blöcken umfasst der erste Milestone eine Reihe kleinerer Sprachänderungen. Durften bisher benannte Konstruktoren und Methoden den gleichen Namen haben, ist das nun nicht mehr gestattet. Ebenso wurde as als ein Infix-Operator für Castings eingeführt. Sollte bisher Wissen über den erwarteten Typ als Ergebnis eines query()-Aufrufs dokumentiert werden, war die Deklaration einer typisierten Variable notwendig.

InputElement firstName = query('#firstname'); 
firstName.value = 'Frank';

Mit dem neuen Operator kann das auch direkt erfolgen.

(query('#firstname') as InputElement).value = 'Frank'; 

Auch die Ausführung des switch-Statements hat Veränderungen erfahren. Bisher hat es sich mehr wie JavaScript verhalten, also eine andere Schreibweise für eine Reihe von if-else-Statements. Die hierin
durchgeführten Vergleiche, die eventuell auf benutzerdefinierten Methoden beruhen, lassen sich nicht vom Compiler optimieren. Dafür wurde switch auf Zahlen, Strings und konstante Objekte beschränkt. Hierdurch kann der Compiler bessere Verfahren zur Steuerung der Verzweigungen nutzen.

Weniger wichtig für die Programmausführung als für die Arbeit im Team und die spätere Wartung ist eine gute Kommentierung des Codes. Zum Erzeugen extern lesbarer Dokumentation verwendet Dart mit dartdoc ein Werkzeug ähnlich zu Javadoc. Auch die Kommentarform ist entsprechend. Das kann für einen Einzeiler schon etwas unverhältnismäßig wirken. Für diese Fälle hat das Dart-Team den einzeiligen Dokumentations-Kommentar eingeführt.

/// Connects to the backend with the configuration [cfg]. 
connect(Configuration cfg) { ... }

Diese Kommentare dürfen Entwickler auch mehrzeilig einsetzen, wenn sie ihnen besser als die regulären /** ... */ gefallen. Schön ist zudem, dass anstelle von HTML Markdown als Auszeichnungssprache in den Kommentaren genutzt wird. Das wirkt sich in der Regel weniger negativ auf die Lesbarkeit im Quelltext aus.

Neben sprachlichen Änderungen haben sich die Bibliotheken weiterentwickelt. Basis für ihre Nutzung ist der Import mit der Direktive #import. Bisher wirkte sie in der Form, dass sie die referenzierte Bibliothek komplett importierte. Bei gleichen Namen von Bezeichnern in zwei Bibliotheken konnte das zu Konflikten führen, die sich nur durch einen Präfix beim Import beheben ließen. Nun gibt es in der Direktive zusätzlich eine optionale Erweiterung für die selektive Aus- oder Abwahl von Namen.

#import('firstlib.dart', show: ['foo', 'bar']); 
#import('secondlib.dart', hide: ['foo', 'bar']);

In dem Fall werden von firstlib.dart nur die Namen foo und bar importiert, während von secondlib.dart alles außer diesen Namen herangezogen wird. Ebenso kann man mehrere einzelne Bibliotheken zu einer großen zusammenfassen. Hierfür kennt #import den export:-Parameter.

// mylib.dart // mylib.dart 
#import('foo.dart', export: true);
#import('bar.dart', export: true);

So werden von mylib.dart neben den eigenen Exporten alle von foo.dart und bar.dart exportiert. Auf dem Weg lassen sich auch große Bibliotheken in kleinere refaktorisieren, ohne dass importierende Programme etwas ändern müssen. Eine Kombination mit show: oder hide: ist ebenfalls möglich. So lassen sich Importe bei Bedarf präzise steuern. Jedoch bestand noch das Problem der Namenskonflikte zwischen Importen und lokalen Definitionen, beispielsweise nach Updates importierter Bibliotheken. Das wurde inzwischen zugunsten der lokalen Definitionen spezifiziert, die immer gegenüber Importen gewinnen.

Nun wollen Entwickler nicht alles von Grund auf neu programmieren, sondern gerne auf eine leistungsstarke Bibliothek zurückgreifen. Jav SE/EE und .NET haben gezeigt, wie wichtig das ist. Daher statten die Dart-Entwickler ihr System mit der Dart API aus. Sie hat inzwischen um einiges zugelegt. Basis ist weiterhin dart:core, das immer automatisch geladen wird. Wichtigste Bestandteile sind die Basis-Datentypen inklusive Date und Duration sowie einige Collection-Klassen wie List, Set, Queue oder Map.

Auch Future für Funktionsaufrufe mit einem Ergebnis in der Zukunft ist in der Bibliothek enthalten. Eine ähnliche Bedeutung hat die Bibliothek dart:io. Neben Klassen rund um lokale Verzeichnisse und Dateien enthält sie Typen für die Netzwerkprogrammierung. Das reicht von einfachen Sockets über HTTP als Client und Server bis hin zu WebSockets. Abgerundet wird es von Klassen zum Start externer Prozesse. Sollen hingegen Aufgaben intern nebenläufig verarbeitet werden, kommt die dart:isolate-Bibliothek zum Einsatz.

Der Name dart:crypto lässt schon vermuten, was Inhalt dieser Library ist. Sie bietet Typen wie HMAC, MD5, SHA1 und SHA256. Auch dart:math erschwert es einem mit seinen Funktionen und Konstanten nicht. Ein Zufallsgenerator ist hierin ebenfalls enthalten. Der Name dart:scalarlist ist weniger eindeutig. Die Bibliothek bietet dynamische Arrays für skalare Werte vom Byte bis zum Unsigned Integer mit 64 Bit. Sie steht jedoch nur für Server-Applikationen in der eigenständigen Dart VM zur Verfügung. Universeller für die Arbeit mit unterschiedlichen Unicode Encodings ist hingegen dart:utf. Sie bietet Decoder für UTF-8, UTF-16 und UTF-32.

Die für die Entwicklung von Internet-Anwendungen wichtigste Bibliothek ist dart:html. Sie bietet eine immense Anzahl an Klassen, die die Elemente des DOM und CSS repräsentieren. Hinzu kommen Schnittstellen für Mediadaten, SVG und WebGL, in das Dateisystem, zu integrierten Datenbanken, dem Clipboard und dem Batterieladezustand. Außerdem gibt es Klassen für XML-Serialisierung, XPath und XSLT. Die sich zunehmender Beliebtheit erfreuende Alternative JSON ist Inhalt der Bibliothek dart:json. Leider unterscheidet sich das Interface der Serialisierung von Objekten in XML und JSON. Hier würde eine Nachbesserung gut tun. Dafür bietet die Bibliothek das Parsing eines XML-Strings in eine Dart-Datenstruktur. Mit der Bibliothek dart:uri lassen sich URIs kodieren, dekodieren und analysieren.

Eine Zeit lang kämpften die Entwickler noch mit dem Problem unterschiedlicher Implementierungen von dart:core und der hiermit verbundenen dart:coreimpl für den dart2js-Übersetzer und die Dart VM. Inzwischen wurde das jedoch angeglichen.

Neben der Sprache und ihrer Bibliotheken gehören weitere Werkzeuge zu einer Entwicklungsumgebung. Das wichtigste Tool ist der sich in Eclipse integrierende Editor. Er bietet eine Projektverwaltung inklusive der Generierung eines Gerüsts und auf Wunsch für den Package Manager pub. Der Editor bietet Syntax-Highlighting für die Sprache selbst sowie für HTML und CSS. Daneben beherrscht er Code Completion und unterstützt die schnelle Navigation in den Quellen durch eine Suche von Referenzen, Aufrufern oder der Definition. Für die Übersetzung bietet der Editor zudem einen Build Hook. Das ist ein während des Kompiliervorgangs ausgeführtes Dart-Skript, mit dem sich weitere Ressourcen generieren, manipulieren oder die vom Editor unterstützten SCSS-Dateien in CSS-Dateien übersetzen lassen. Aus dem Code lässt sich für eine Nutzung im Browser, wie oben angedeutet, JavaScript generieren. Alternativ steht die Ausführung direkt in der Dart VM zur Verfügung. Auch eine Code-Dokumentation in HTML lässt sich aus dem Editor heraus oder auf der Kommandozeile mit dartdoc erzeugen. Daneben ist ein Debugger im Editor enthalten. Er hilft beim Nachvollziehen des Programmablaufs und der Analyse des derzeitigen Zustands.

Für spätere Erweiterungen und die Softwarewartung verfügt der Editor zudem über ein Refactoring. Hiermit lässt sich im Rahmen der Säuberung der Code nach auswählbaren Kriterien analysieren und umstellen, ebenso sind Anpassungen am Code-Stil möglich, die jedoch noch nicht an gofmt von Googles Go herankommen. Besonders am Clean-up ist auch die automatische Umstellung bisher gültigen Codes auf aktuelle Standards. Individuell sind weitere Umstellungen wie Umbenennungen, Umwandlungen in Getter, Inlining und dem Extrakt von Funktionen oder lokalen Variablen machbar.

Mit dem Editor, der in eingeschränktem Umfang auch als Plug-in für Eclipse verfügbar ist, kommt der Dart Package Manager pub. Er erlaubt die Bündelung von Ressourcen und die Definition von Abhängigkeiten zu externen Packages. Diese lassen sich installieren und aktualisieren, sowohl auf der Kommandozeile als auch vom Editor aus. Über ein definiertes Namensschema werden die externen Packages in den Code importiert.

Mit Dartium hat das Dart-Team noch einen Chromium-Browser mit einer integrierten Dart VM beigelegt. Er vereinfacht das Testen von Client-Anwendungen und entspricht sicherlich dem, was sich die Entwickler für Dart von der Browser-Welt wünschen: den nativen Einsatz von Dart. Das letzte wichtige Tool wird nicht lokal installiert, es befindet sich auf dem Server. Denn inzwischen ist die API gut dokumentiert.

Seit der Vorstellung hat sich Dart deutlich weiterentwickelt. Der Milestone 1 ist sicherlich noch nicht produktionsreif, das ist auch allen Beteiligten bewusst. Aber sowohl die Sprache als auch die gut in den Editor integrierten Werkzeuge zeigen die Marschrichtung und erleichtern so den Einstieg. Das ermöglicht so auch den Vorlauf, der notwendig ist, um Dart später in eigene Entwicklungen zu integrieren. Insbesondere die Entwicklung von Client-Anwendungen im Browser kann stark von Dart profitieren.

Die Sprache selbst hat sich zunehmend vom Ahnen JavaScript emanzipiert, weitere Schritte sind absehbar. So werden aktuell beispielsweise Mixins diskutiert. Einige kleinere Inkonsistenzen und Umständlichkeiten müssten nicht sein, was letztlich jedoch Geschmacksache ist. Andere Konzepte sind hingegen erfrischend aus Sprachen wie Smalltalk übernommen worden.

Für den an Dart interessierten Entwickler stehen neben der Projektseite inzwischen weitere Ressourcen zur Verfügung. So gibt es eine News-Seite, die zudem auf Google+ gespiegelt ist. Hier und bei Twitter hat sich zudem inzwischen das Hashtag #dartlang etabliert, und die im Web lesbare Mailing-Liste erleichtert umfangreichere Diskussionen.

Schloss der erste Test seinerzeit noch mit dem Hinweis, dass Dart noch einige Zeit bis zur Reife benötige, so ist dieser Zeitpunkt der Reife auch jetzt noch nicht, aber schon bald erreicht. Daher sei es jedem an der Entwicklung von Webanwendungen Interessierten für einen tieferen Blick ans Herz gelegt.

Frank Müller
arbeitet als Software Engineer bei Canonical.

  1. Frank Müller; Volltreffer; Dart – Googles zweite Programmiersprache; iX 1/2012, S. 126

Darts Väter Bracha und Bak haben die Sprache von vornherein als Sprache für Webanwendungen sowohl auf dem Client als auch auf dem Server konzipiert. Zuvor wirkten die Entwickler an Newspeak und Strongtalk, der JVM HotSpot, Self und der JavaScript Engine V8 mit. Der Erfahrungsschatz findet sich in der an JavaScript angelehnten Sprache wie auch der Laufzeitumgebung wieder.

Aus Sicht Bracha und Baks sind die unzureichenden Strukturierungsmöglichkeiten und die langen Startzeiten große Nachteile von JavaScript. Ein Bibliothekssystem, Klassen statt Prototypen und eine optionale Typisierung setzen hier an. Letzteres mag verwirren, aber es handelt sich prinzipiell um eine dynamische Typisierung, der sich im Quelltext Typen mitgeben lassen. Diese kann der Compiler bereits während des Übersetzungsvorgangs erkennen. Auf Wunsch lässt sich aber auch vollständig darauf verzichten.

Optisch kommen JavaScript-Entwickler am schnellsten mit der Sprache zurecht. Die Struktur, geschweifte Klammern, Kontrollkonstrukte, alles erinnert an diese Wurzeln. Aber auch Smalltalker fühlen sich wohl. Die Objektorientierung mit Einfachvererbung, Collection Classes, benannte Argumente oder die optionale Methode noSuchMethod() als Entsprechung zu Smalltalks doesNotUnderstand:. Für die Attribute der Klassen lassen sich Getter und Setter definieren, Operator Overloading ist ebenfalls möglich. Dazu kommen noch Funktionen und Closures.

Zu Darts Lieferungumfang gehören eine Reihe von Bibliotheken. Neben einer umfangreichen zur Erzeugung und Manipulation von HTML gehören auch die Nebenläufigkeit über Isolates, Futures, JSON oder Netzwerk- und Crypto-Bibliotheken dazu. So oder zudem in Templates kodierte Programme lassen sich anschließend nach JavaScript übersetzen. Sie können in Chrome, Safari ab Version 5 und Firefox ab Version 4 ausgeführt werden. Alternativ ist die direkte Ausführung im zum Lieferumfang gehörenden Chromium mit integrierter Dart VM oder als Server direkt in der Dart VM möglich. Hier lässt sich der Code entweder stets neu zur Ladezeit übersetzen oder alternativ durch einen Snapshot beschleunigt starten. Neben dem Übersetzer von Dart nach JavaScript sowie der Dart VM und Chromium gehört auch ein Programm zur Doku-Generierung aus dem Quelltext zum Lieferumfang. Abgerundet wird das Paket durch den Dart Editor. (ane)