You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@thrift.apache.org by je...@apache.org on 2015/10/03 01:47:02 UTC

[1/5] thrift git commit: THRIFT-3299 Create an Apache Thrift language binding for Dart (dartlang.org). Client: Dart Patch: Mark Erickson

Repository: thrift
Updated Branches:
  refs/heads/master 123258ba6 -> a8d5a7841


http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/dart/console_client/bin/main.dart
----------------------------------------------------------------------
diff --git a/tutorial/dart/console_client/bin/main.dart b/tutorial/dart/console_client/bin/main.dart
new file mode 100644
index 0000000..fda206a
--- /dev/null
+++ b/tutorial/dart/console_client/bin/main.dart
@@ -0,0 +1,149 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:args/args.dart';
+import 'package:logging/logging.dart';
+import 'package:thrift/thrift.dart';
+import 'package:thrift/thrift_console.dart';
+import 'package:tutorial/tutorial.dart';
+
+TTransport _transport;
+Calculator _calculator;
+int logid = 0;
+
+const Map<String, int> operationLookup = const {
+  '+': Operation.ADD,
+  '-': Operation.SUBTRACT,
+  '*': Operation.MULTIPLY,
+  '/': Operation.DIVIDE
+};
+
+main(List<String> args) {
+  Logger.root.level = Level.ALL;
+  Logger.root.onRecord.listen((LogRecord rec) {
+    print('${rec.level.name}: ${rec.time}: ${rec.message}');
+  });
+
+  var parser = new ArgParser();
+  parser.addOption('port', defaultsTo: '9090', help: 'The port to connect to');
+
+  ArgResults results;
+  try {
+    results = parser.parse(args);
+  } catch (e) {
+    results = null;
+  }
+
+  if (results == null) {
+    print(parser.usage);
+    exit(0);
+  }
+
+  int port = int.parse(results['port']);
+
+  _initConnection(port).then((_) => _run());
+}
+
+Future _initConnection(int port) async {
+  var socket = await Socket.connect('127.0.0.1', port);
+  _transport = new TAsyncClientSocketTransport(
+      new TTcpSocket(socket), new TMessageReader(new TBinaryProtocolFactory()));
+  TProtocol protocol = new TBinaryProtocol(_transport);
+  await _transport.open();
+
+  _calculator = new CalculatorClient(protocol);
+}
+
+Future _run() async {
+  _help();
+
+  while (true) {
+    stdout.write("> ");
+    var input = stdin.readLineSync();
+    var parts = input.split(' ');
+    var command = parts[0];
+    var args = parts.length > 1 ? parts.sublist(1) : [];
+
+    switch (command) {
+      case 'ping':
+        await _ping();
+        break;
+
+      case 'add':
+        await _add(int.parse(args[0]), int.parse(args[1]));
+        break;
+
+      case 'calc':
+        int op = operationLookup[args[1]];
+        if (!Operation.VALID_VALUES.contains(op)) {
+          stdout.writeln('Unknown operator ${args[1]}');
+          break;
+        }
+
+        var work = new Work()
+          ..num1 = int.parse(args[0])
+          ..op = op
+          ..num2 = int.parse(args[2])
+          ..comment = args.length > 3 ? args[3] : '';
+
+        await _calc(work);
+        break;
+
+      case 'struct':
+        await _struct(int.parse(args[0]));
+        break;
+
+      case 'help':
+      default:
+        _help();
+        break;
+    }
+  }
+}
+
+void _help() {
+  stdout.writeln('Commands:');
+  stdout.writeln('  help');
+  stdout.writeln('  ping');
+  stdout.writeln('  add x y');
+  stdout.writeln('  calc x op y [comment]');
+  stdout.writeln('  struct id');
+  stdout.writeln('');
+}
+
+Future _ping() async {
+  await _calculator.ping();
+  stdout.writeln('ping succeeded');
+}
+
+Future _add(int x, int y) async {
+  int result = await _calculator.add(x, y);
+  stdout.writeln('= $result');
+}
+
+Future _calc(Work work) async {
+  int result = await _calculator.calculate(logid++, work);
+  stdout.writeln('= $result');
+}
+
+Future _struct(int key) async {
+  var struct = await _calculator.getStruct(key);
+  stdout.writeln(struct.toString());
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/dart/console_client/pubspec.yaml
----------------------------------------------------------------------
diff --git a/tutorial/dart/console_client/pubspec.yaml b/tutorial/dart/console_client/pubspec.yaml
new file mode 100644
index 0000000..3db9fb9
--- /dev/null
+++ b/tutorial/dart/console_client/pubspec.yaml
@@ -0,0 +1,34 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# 'License'); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: tutorial_console_client
+version: 1.0.0-dev
+description: >
+  A Dart console client to implementation of the Apache Thrift tutorial
+author: Mark Erickson <ma...@workiva.com>
+homepage: https://github.com/apache/thrift
+environment:
+  sdk: ^1.12.0
+dependencies:
+  args: ^0.13.0
+  collection: ^1.1.0
+  shared:
+    path: ../gen-dart/shared
+  thrift:
+    path: ../../../lib/dart
+  tutorial:
+    path: ../gen-dart/tutorial

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/dart/server/bin/main.dart
----------------------------------------------------------------------
diff --git a/tutorial/dart/server/bin/main.dart b/tutorial/dart/server/bin/main.dart
new file mode 100644
index 0000000..b8ac30d
--- /dev/null
+++ b/tutorial/dart/server/bin/main.dart
@@ -0,0 +1,163 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:args/args.dart';
+import 'package:logging/logging.dart';
+import 'package:thrift/thrift.dart';
+import 'package:thrift/thrift_console.dart';
+import 'package:tutorial/tutorial.dart';
+import 'package:shared/shared.dart';
+
+TProtocol _protocol;
+TProcessor _processor;
+WebSocket _webSocket;
+
+main(List<String> args) {
+  Logger.root.level = Level.ALL;
+  Logger.root.onRecord.listen((LogRecord rec) {
+    print('${rec.level.name}: ${rec.time}: ${rec.message}');
+  });
+
+  var parser = new ArgParser();
+  parser.addOption('port', defaultsTo: '9090', help: 'The port to listen on');
+  parser.addOption('type',
+      defaultsTo: 'ws',
+      allowed: ['ws', 'tcp'],
+      help: 'The type of socket',
+      allowedHelp: {'ws': 'WebSocket', 'tcp': 'TCP Socket'});
+
+  ArgResults results;
+  try {
+    results = parser.parse(args);
+  } catch (e) {
+    results = null;
+  }
+
+  if (results == null) {
+    print(parser.usage);
+    exit(0);
+  }
+
+  int port = int.parse(results['port']);
+  String socketType = results['type'];
+
+  if (socketType == 'tcp') {
+    _runTcpServer(port);
+  } else if (socketType == 'ws') {
+    _runWebSocketServer(port);
+  }
+}
+
+Future _runWebSocketServer(int port) async {
+  var httpServer = await HttpServer.bind('127.0.0.1', port);
+  print('listening for WebSocket connections on $port');
+
+  httpServer.listen((HttpRequest request) async {
+    if (request.uri.path == '/ws') {
+      _webSocket = await WebSocketTransformer.upgrade(request);
+      await _initProcessor(new TWebSocket(_webSocket));
+    } else {
+      print('Invalid path: ${request.uri.path}');
+    }
+  });
+}
+
+Future _runTcpServer(int port) async {
+  var serverSocket = await ServerSocket.bind('127.0.0.1', port);
+  print('listening for TCP connections on $port');
+
+  Socket socket = await serverSocket.first;
+  await _initProcessor(new TTcpSocket(socket));
+}
+
+Future _initProcessor(TSocket socket) async {
+  TServerSocketTransport transport = new TServerSocketTransport(socket);
+  transport.onIncomingMessage.listen(_processMessage);
+  _processor = new CalculatorProcessor(new CalculatorServer());
+  _protocol = new TBinaryProtocol(transport);
+  await _protocol.transport.open();
+
+  print('connected');
+}
+
+Future _processMessage(_) async {
+  _processor.process(_protocol, _protocol);
+}
+
+class CalculatorServer implements Calculator {
+  final Map<int, SharedStruct> _log = {};
+
+  Future ping() async {
+    print('ping()');
+  }
+
+  Future<int> add(int num1, int num2) async {
+    print('add($num1, $num2)');
+
+    return num1 + num2;
+  }
+
+  Future<int> calculate(int logid, Work work) async {
+    print('calulate($logid, ${work.toString()})');
+
+    int val;
+
+    switch (work.op) {
+      case Operation.ADD:
+        val = work.num1 + work.num2;
+        break;
+
+      case Operation.SUBTRACT:
+        val = work.num1 - work.num2;
+        break;
+
+      case Operation.MULTIPLY:
+        val = work.num1 * work.num2;
+        break;
+
+      case Operation.DIVIDE:
+        if (work.num2 == 0) {
+          var x = new InvalidOperation();
+          x.whatOp = work.op;
+          x.why = 'Cannot divide by 0';
+          throw x;
+        }
+        val = (work.num1 / work.num2).floor();
+        break;
+    }
+
+    var log = new SharedStruct();
+    log.key = logid;
+    log.value = '$val "${work.comment}"';
+    this._log[logid] = log;
+
+    return val;
+  }
+
+  Future zip() async {
+    print('zip()');
+  }
+
+  Future<SharedStruct> getStruct(int key) async {
+    print('getStruct($key)');
+
+    return _log[key];
+  }
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/dart/server/pubspec.yaml
----------------------------------------------------------------------
diff --git a/tutorial/dart/server/pubspec.yaml b/tutorial/dart/server/pubspec.yaml
new file mode 100644
index 0000000..f502974
--- /dev/null
+++ b/tutorial/dart/server/pubspec.yaml
@@ -0,0 +1,32 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# 'License'); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: tutorial_server
+version: 1.0.0-dev
+description: A Dart server to support the Apache Thrift tutorial
+author: Mark Erickson <ma...@workiva.com>
+homepage: https://github.com/apache/thrift
+environment:
+  sdk: ^1.12.0
+dependencies:
+  args: ^0.13.0
+  shared:
+    path: ../gen-dart/shared
+  thrift:
+    path: ../../../lib/dart
+  tutorial:
+    path: ../gen-dart/tutorial

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/shared.thrift
----------------------------------------------------------------------
diff --git a/tutorial/shared.thrift b/tutorial/shared.thrift
index 60e8e7a..386000b 100644
--- a/tutorial/shared.thrift
+++ b/tutorial/shared.thrift
@@ -24,6 +24,7 @@
 
 namespace cpp shared
 namespace d share // "shared" would collide with the eponymous D keyword.
+namespace dart shared
 namespace java shared
 namespace perl shared
 namespace php shared

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/tutorial.thrift
----------------------------------------------------------------------
diff --git a/tutorial/tutorial.thrift b/tutorial/tutorial.thrift
index 571ab8e..1149edf 100644
--- a/tutorial/tutorial.thrift
+++ b/tutorial/tutorial.thrift
@@ -64,6 +64,7 @@ include "shared.thrift"
  */
 namespace cpp tutorial
 namespace d tutorial
+namespace dart tutorial
 namespace java tutorial
 namespace php tutorial
 namespace perl tutorial


[3/5] thrift git commit: THRIFT-3299 Create an Apache Thrift language binding for Dart (dartlang.org). Client: Dart Patch: Mark Erickson

Posted by je...@apache.org.
http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 5066a00..44bb9ff 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -65,6 +65,10 @@ if WITH_PHP
 SUBDIRS += php
 endif
 
+if WITH_DART
+SUBDIRS += dart
+endif
+
 if WITH_GO
 SUBDIRS += go
 endif
@@ -88,6 +92,7 @@ EXTRA_DIST = \
 	as3 \
 	cocoa \
 	d \
+	dart \
 	delphi \
 	haxe \
 	javame \

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/LICENSE_HEADER
----------------------------------------------------------------------
diff --git a/lib/dart/LICENSE_HEADER b/lib/dart/LICENSE_HEADER
new file mode 100644
index 0000000..4eacb64
--- /dev/null
+++ b/lib/dart/LICENSE_HEADER
@@ -0,0 +1,16 @@
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/dart/Makefile.am b/lib/dart/Makefile.am
new file mode 100644
index 0000000..6dfff40
--- /dev/null
+++ b/lib/dart/Makefile.am
@@ -0,0 +1,26 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+all-local:
+	$(DARTPUB) get
+
+clean-local:
+	$(RM) -r .pub
+
+check-local: all

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/README.md
----------------------------------------------------------------------
diff --git a/lib/dart/README.md b/lib/dart/README.md
new file mode 100644
index 0000000..2be168b
--- /dev/null
+++ b/lib/dart/README.md
@@ -0,0 +1,26 @@
+Thrift Dart Library
+
+License
+=======
+
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+
+Using Thrift with Dart
+====================
+
+Dart 1.12.0 or newer is required

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/coding_standards.md
----------------------------------------------------------------------
diff --git a/lib/dart/coding_standards.md b/lib/dart/coding_standards.md
new file mode 100644
index 0000000..62f6003
--- /dev/null
+++ b/lib/dart/coding_standards.md
@@ -0,0 +1,6 @@
+# Dart Coding Standards
+
+### Please follow:
+ * [Thrift General Coding Standards](/doc/coding_standards.md)
+ * [Use dartfmt](https://www.dartlang.org/tools/dartfmt/) and follow the 
+   [Dart Style Guide](https://www.dartlang.org/articles/style-guide/)

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/browser/t_web_socket.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/browser/t_web_socket.dart b/lib/dart/lib/src/browser/t_web_socket.dart
new file mode 100644
index 0000000..dfcee83
--- /dev/null
+++ b/lib/dart/lib/src/browser/t_web_socket.dart
@@ -0,0 +1,130 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+library thrift.src.browser;
+
+import 'dart:async';
+import 'dart:html' show CloseEvent;
+import 'dart:html' show Event;
+import 'dart:html' show MessageEvent;
+import 'dart:html' show WebSocket;
+import 'dart:typed_data' show Uint8List;
+
+import 'package:crypto/crypto.dart' show CryptoUtils;
+import 'package:thrift/thrift.dart';
+
+/// A [TSocket] backed by a [WebSocket] from dart:html
+class TWebSocket implements TSocket {
+  final Uri url;
+
+  final StreamController<TSocketState> _onStateController;
+  Stream<TSocketState> get onState => _onStateController.stream;
+
+  final StreamController<Object> _onErrorController;
+  Stream<Object> get onError => _onErrorController.stream;
+
+  final StreamController<Uint8List> _onMessageController;
+  Stream<Uint8List> get onMessage => _onMessageController.stream;
+
+  final List<Uint8List> _requests = [];
+
+  TWebSocket(this.url)
+      : _onStateController = new StreamController.broadcast(),
+        _onErrorController = new StreamController.broadcast(),
+        _onMessageController = new StreamController.broadcast() {
+    if (url == null || !url.hasAuthority || !url.hasPort) {
+      throw new ArgumentError('Invalid url');
+    }
+  }
+
+  WebSocket _socket;
+
+  bool get isOpen => _socket != null && _socket.readyState == WebSocket.OPEN;
+
+  bool get isClosed =>
+      _socket == null || _socket.readyState == WebSocket.CLOSED;
+
+  Future open() {
+    if (!isClosed) {
+      throw new TTransportError(
+          TTransportErrorType.ALREADY_OPEN, 'Socket already connected');
+    }
+
+    _socket = new WebSocket(url.toString());
+    _socket.onError.listen(_onError);
+    _socket.onOpen.listen(_onOpen);
+    _socket.onClose.listen(_onClose);
+    _socket.onMessage.listen(_onMessage);
+
+    return _socket.onOpen.first;
+  }
+
+  Future close() {
+    if (_socket != null) {
+      _socket.close();
+      return _socket.onClose.first;
+    } else {
+      return new Future.value();
+    }
+  }
+
+  void send(Uint8List data) {
+    _requests.add(data);
+    _sendRequests();
+  }
+
+  void _sendRequests() {
+    while (isOpen && _requests.isNotEmpty) {
+      Uint8List data = _requests.removeAt(0);
+      _socket.sendString(CryptoUtils.bytesToBase64(data));
+    }
+  }
+
+  void _onOpen(Event event) {
+    _onStateController.add(TSocketState.OPEN);
+    _sendRequests();
+  }
+
+  void _onClose(CloseEvent event) {
+    _socket = null;
+
+    if (_requests.isNotEmpty) {
+      _onErrorController
+          .add(new StateError('Socket was closed with pending requests'));
+    }
+    _requests.clear();
+
+    _onStateController.add(TSocketState.CLOSED);
+  }
+
+  void _onMessage(MessageEvent message) {
+    try {
+      Uint8List data =
+          new Uint8List.fromList(CryptoUtils.base64StringToBytes(message.data));
+      _onMessageController.add(data);
+    } on FormatException catch (_) {
+      var error = new TProtocolError(TProtocolErrorType.INVALID_DATA,
+          "Expected a Base 64 encoded string.");
+      _onErrorController.add(error);
+    }
+  }
+
+  void _onError(Event event) {
+    close();
+    _onErrorController.add(event.toString());
+  }
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/console/t_tcp_socket.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/console/t_tcp_socket.dart b/lib/dart/lib/src/console/t_tcp_socket.dart
new file mode 100644
index 0000000..b714803
--- /dev/null
+++ b/lib/dart/lib/src/console/t_tcp_socket.dart
@@ -0,0 +1,81 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+library thrift.src.console.t_tcp_socket;
+
+import 'dart:async';
+import 'dart:io';
+import 'dart:typed_data' show Uint8List;
+
+import 'package:thrift/thrift.dart';
+
+/// A [TSocket] backed by a [Socket] from dart:io
+class TTcpSocket implements TSocket {
+  final StreamController<TSocketState> _onStateController;
+  Stream<TSocketState> get onState => _onStateController.stream;
+
+  final StreamController<Object> _onErrorController;
+  Stream<Object> get onError => _onErrorController.stream;
+
+  final StreamController<Uint8List> _onMessageController;
+  Stream<Uint8List> get onMessage => _onMessageController.stream;
+
+  TTcpSocket(Socket socket)
+      : _onStateController = new StreamController.broadcast(),
+        _onErrorController = new StreamController.broadcast(),
+        _onMessageController = new StreamController.broadcast() {
+    if (socket == null) {
+      throw new ArgumentError.notNull('socket');
+    }
+
+    _socket = socket;
+    _socket.listen(_onMessage, onError: _onError, onDone: close);
+  }
+
+  Socket _socket;
+
+  bool get isOpen => _socket != null;
+
+  bool get isClosed => _socket == null;
+
+  Future open() async {
+    _onStateController.add(TSocketState.OPEN);
+  }
+
+  Future close() async {
+    if (_socket != null) {
+      await _socket.close();
+      _socket = null;
+    }
+
+    _onStateController.add(TSocketState.CLOSED);
+  }
+
+  void send(Uint8List data) {
+    _socket.add(data);
+  }
+
+  void _onMessage(List<int> message) {
+    Uint8List data = new Uint8List.fromList(message);
+    _onMessageController.add(data);
+  }
+
+  void _onError(Object error) {
+    close();
+    _onErrorController.add('$error');
+  }
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/console/t_web_socket.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/console/t_web_socket.dart b/lib/dart/lib/src/console/t_web_socket.dart
new file mode 100644
index 0000000..5a549be
--- /dev/null
+++ b/lib/dart/lib/src/console/t_web_socket.dart
@@ -0,0 +1,89 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+library thrift.src.console.t_web_socket;
+
+import 'dart:async';
+import 'dart:io';
+import 'dart:typed_data' show Uint8List;
+
+import 'package:crypto/crypto.dart' show CryptoUtils;
+import 'package:thrift/thrift.dart';
+
+/// A [TSocket] backed by a [WebSocket] from dart:io
+class TWebSocket implements TSocket {
+  final StreamController<TSocketState> _onStateController;
+  Stream<TSocketState> get onState => _onStateController.stream;
+
+  final StreamController<Object> _onErrorController;
+  Stream<Object> get onError => _onErrorController.stream;
+
+  final StreamController<Uint8List> _onMessageController;
+  Stream<Uint8List> get onMessage => _onMessageController.stream;
+
+  TWebSocket(WebSocket socket)
+      : _onStateController = new StreamController.broadcast(),
+        _onErrorController = new StreamController.broadcast(),
+        _onMessageController = new StreamController.broadcast() {
+    if (socket == null) {
+      throw new ArgumentError.notNull('socket');
+    }
+
+    _socket = socket;
+    _socket.listen(_onMessage, onError: _onError, onDone: close);
+  }
+
+  WebSocket _socket;
+
+  bool get isOpen => _socket != null;
+
+  bool get isClosed => _socket == null;
+
+  Future open() async {
+    _onStateController.add(TSocketState.OPEN);
+  }
+
+  Future close() async {
+    if (_socket != null) {
+      await _socket.close();
+      _socket = null;
+    }
+
+    _onStateController.add(TSocketState.CLOSED);
+  }
+
+  void send(Uint8List data) {
+    _socket.add(CryptoUtils.bytesToBase64(data));
+  }
+
+  void _onMessage(String message) {
+    try {
+      Uint8List data =
+          new Uint8List.fromList(CryptoUtils.base64StringToBytes(message));
+      _onMessageController.add(data);
+    } on FormatException catch (_) {
+      var error = new TProtocolError(TProtocolErrorType.INVALID_DATA,
+          "Expected a Base 64 encoded string.");
+      _onErrorController.add(error);
+    }
+  }
+
+  void _onError(Object error) {
+    close();
+    _onErrorController.add('$error');
+  }
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/protocol/t_binary_protocol.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/protocol/t_binary_protocol.dart b/lib/dart/lib/src/protocol/t_binary_protocol.dart
new file mode 100644
index 0000000..f73223c
--- /dev/null
+++ b/lib/dart/lib/src/protocol/t_binary_protocol.dart
@@ -0,0 +1,276 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TBinaryProtocolFactory implements TProtocolFactory<TBinaryProtocol> {
+  TBinaryProtocolFactory({this.strictRead: false, this.strictWrite: true});
+
+  final bool strictRead;
+  final bool strictWrite;
+
+  TBinaryProtocol getProtocol(TTransport transport) {
+    return new TBinaryProtocol(transport,
+        strictRead: strictRead, strictWrite: strictWrite);
+  }
+}
+
+/// Binary protocol implementation for Thrift.
+///
+/// Adapted from the C# version.
+class TBinaryProtocol extends TProtocol {
+  static const int VERSION_MASK = 0xffff0000;
+  static const int VERSION_1 = 0x80010000;
+
+  static const Utf8Codec _utf8Codec = const Utf8Codec();
+
+  final bool strictRead;
+  final bool strictWrite;
+
+  TBinaryProtocol(TTransport transport,
+      {this.strictRead: false, this.strictWrite: true})
+      : super(transport);
+
+  /// write
+  void writeMessageBegin(TMessage message) {
+    if (strictWrite) {
+      int version = VERSION_1 | message.type;
+      writeI32(version);
+      writeString(message.name);
+      writeI32(message.seqid);
+    } else {
+      writeString(message.name);
+      writeByte(message.type);
+      writeI32(message.seqid);
+    }
+  }
+
+  void writeMessageEnd() {}
+
+  void writeStructBegin(TStruct struct) {}
+
+  void writeStructEnd() {}
+
+  void writeFieldBegin(TField field) {
+    writeByte(field.type);
+    writeI16(field.id);
+  }
+
+  void writeFieldEnd() {}
+
+  void writeFieldStop() {
+    writeByte(TType.STOP);
+  }
+
+  void writeMapBegin(TMap map) {
+    writeByte(map.keyType);
+    writeByte(map.valueType);
+    writeI32(map.length);
+  }
+
+  void writeMapEnd() {}
+
+  void writeListBegin(TList list) {
+    writeByte(list.elementType);
+    writeI32(list.length);
+  }
+
+  void writeListEnd() {}
+
+  void writeSetBegin(TSet set) {
+    writeByte(set.elementType);
+    writeI32(set.length);
+  }
+
+  void writeSetEnd() {}
+
+  void writeBool(bool b) {
+    if (b == null) b = false;
+    writeByte(b ? 1 : 0);
+  }
+
+  final ByteData _byteOut = new ByteData(1);
+  void writeByte(int byte) {
+    if (byte == null) byte = 0;
+    _byteOut.setUint8(0, byte);
+    transport.write(_byteOut.buffer.asUint8List(), 0, 1);
+  }
+
+  final ByteData _i16Out = new ByteData(2);
+  void writeI16(int i16) {
+    if (i16 == null) i16 = 0;
+    _i16Out.setInt16(0, i16);
+    transport.write(_i16Out.buffer.asUint8List(), 0, 2);
+  }
+
+  final ByteData _i32Out = new ByteData(4);
+  void writeI32(int i32) {
+    if (i32 == null) i32 = 0;
+    _i32Out.setInt32(0, i32);
+    transport.write(_i32Out.buffer.asUint8List(), 0, 4);
+  }
+
+  final ByteData _i64Out = new ByteData(8);
+  void writeI64(int i64) {
+    if (i64 == null) i64 = 0;
+    _i64Out.setInt64(0, i64);
+    transport.write(_i64Out.buffer.asUint8List(), 0, 8);
+  }
+
+  void writeString(String s) {
+    var bytes = s != null ? _utf8Codec.encode(s) : new Uint8List.fromList([]);
+    writeI32(bytes.length);
+    transport.write(bytes, 0, bytes.length);
+  }
+
+  final ByteData _doubleOut = new ByteData(8);
+  void writeDouble(double d) {
+    if (d == null) d = 0.0;
+    _doubleOut.setFloat64(0, d);
+    transport.write(_doubleOut.buffer.asUint8List(), 0, 8);
+  }
+
+  void writeBinary(Uint8List bytes) {
+    var length = bytes.length;
+    writeI32(length);
+    transport.write(bytes, 0, length);
+  }
+
+  /// read
+  TMessage readMessageBegin() {
+    String name;
+    int type;
+    int seqid;
+
+    int size = readI32();
+    if (size < 0) {
+      int version = size & VERSION_MASK;
+      if (version != VERSION_1) {
+        throw new TProtocolError(TProtocolErrorType.BAD_VERSION,
+            "Bad version in readMessageBegin: $version");
+      }
+      type = size & 0x000000ff;
+      name = readString();
+      seqid = readI32();
+    } else {
+      if (strictRead) {
+        throw new TProtocolError(TProtocolErrorType.BAD_VERSION,
+            "Missing version in readMessageBegin");
+      }
+      name = _readString(size);
+      type = readByte();
+      seqid = readI32();
+    }
+    return new TMessage(name, type, seqid);
+  }
+
+  void readMessageEnd() {}
+
+  TStruct readStructBegin() {
+    return new TStruct();
+  }
+
+  void readStructEnd() {}
+
+  TField readFieldBegin() {
+    String name = "";
+    int type = readByte();
+    int id = type != TType.STOP ? readI16() : 0;
+
+    return new TField(name, type, id);
+  }
+
+  void readFieldEnd() {}
+
+  TMap readMapBegin() {
+    int keyType = readByte();
+    int valueType = readByte();
+    int length = readI32();
+
+    return new TMap(keyType, valueType, length);
+  }
+
+  void readMapEnd() {}
+
+  TList readListBegin() {
+    int elementType = readByte();
+    int length = readI32();
+
+    return new TList(elementType, length);
+  }
+
+  void readListEnd() {}
+
+  TSet readSetBegin() {
+    int elementType = readByte();
+    int length = readI32();
+
+    return new TSet(elementType, length);
+  }
+
+  void readSetEnd() {}
+
+  bool readBool() => readByte() == 1;
+
+  final Uint8List _byteIn = new Uint8List(1);
+  int readByte() {
+    transport.readAll(_byteIn, 0, 1);
+    return _byteIn.buffer.asByteData().getUint8(0);
+  }
+
+  final Uint8List _i16In = new Uint8List(2);
+  int readI16() {
+    transport.readAll(_i16In, 0, 2);
+    return _i16In.buffer.asByteData().getInt16(0);
+  }
+
+  final Uint8List _i32In = new Uint8List(4);
+  int readI32() {
+    transport.readAll(_i32In, 0, 4);
+    return _i32In.buffer.asByteData().getInt32(0);
+  }
+
+  final Uint8List _i64In = new Uint8List(8);
+  int readI64() {
+    transport.readAll(_i64In, 0, 8);
+    return _i64In.buffer.asByteData().getInt64(0);
+  }
+
+  final Uint8List _doubleIn = new Uint8List(8);
+  double readDouble() {
+    transport.readAll(_doubleIn, 0, 8);
+    return _doubleIn.buffer.asByteData().getFloat64(0);
+  }
+
+  String readString() {
+    int size = readI32();
+    return _readString(size);
+  }
+
+  String _readString(int size) {
+    Uint8List stringIn = new Uint8List(size);
+    transport.readAll(stringIn, 0, size);
+    return _utf8Codec.decode(stringIn);
+  }
+
+  Uint8List readBinary() {
+    int length = readI32();
+    Uint8List binaryIn = new Uint8List(length);
+    transport.readAll(binaryIn, 0, length);
+    return binaryIn;
+  }
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/protocol/t_field.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/protocol/t_field.dart b/lib/dart/lib/src/protocol/t_field.dart
new file mode 100644
index 0000000..444b4e5
--- /dev/null
+++ b/lib/dart/lib/src/protocol/t_field.dart
@@ -0,0 +1,26 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TField {
+  final String name;
+  final int type;
+  final int id;
+
+  TField(this.name, this.type, this.id);
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/protocol/t_json_protocol.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/protocol/t_json_protocol.dart b/lib/dart/lib/src/protocol/t_json_protocol.dart
new file mode 100644
index 0000000..4fa6499
--- /dev/null
+++ b/lib/dart/lib/src/protocol/t_json_protocol.dart
@@ -0,0 +1,754 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TJsonProtocolFactory implements TProtocolFactory<TJsonProtocol> {
+  TJsonProtocol getProtocol(TTransport transport) {
+    return new TJsonProtocol(transport);
+  }
+}
+
+/// JSON protocol implementation for Thrift.
+///
+/// Adapted from the C# version.
+class TJsonProtocol extends TProtocol {
+  static const int VERSION_1 = 1;
+
+  static const Utf8Codec utf8Codec = const Utf8Codec();
+
+  _BaseContext _context;
+  _BaseContext _rootContext;
+  _LookaheadReader _reader;
+
+  final List<_BaseContext> _contextStack = [];
+  final Uint8List _tempBuffer = new Uint8List(4);
+
+  TJsonProtocol(TTransport transport) : super(transport) {
+    _rootContext = new _BaseContext(this);
+    _reader = new _LookaheadReader(this);
+  }
+
+  void _pushContext(_BaseContext c) {
+    _contextStack.add(c);
+    _context = c;
+  }
+
+  void _popContext() {
+    _contextStack.removeLast();
+    _context = _contextStack.isEmpty ? _rootContext : _contextStack.last;
+  }
+
+  void _resetContext() {
+    _contextStack.clear();
+    _context = _rootContext;
+  }
+
+  /// Read a byte that must match [char]; otherwise throw a [TProtocolError].
+  void _readJsonSyntaxChar(int charByte) {
+    int byte = _reader.read();
+    if (byte != charByte) {
+      throw new TProtocolError(TProtocolErrorType.INVALID_DATA,
+          "Expected character ${new String.fromCharCode(charByte)} but found: ${new String.fromCharCode(byte)}");
+    }
+  }
+
+  int _hexVal(int byte) {
+    if (byte >= _Constants.HEX_0_BYTES[0] &&
+        byte <= _Constants.HEX_9_BYTES[0]) {
+      return byte - _Constants.HEX_0_BYTES[0];
+    } else if (byte >= _Constants.HEX_A_BYTES[0] &&
+        byte <= _Constants.HEX_F_BYTES[0]) {
+      byte += 10;
+      return byte - _Constants.HEX_A_BYTES[0];
+    } else {
+      throw new TProtocolError(
+          TProtocolErrorType.INVALID_DATA, "Expected hex character");
+    }
+  }
+
+  int _hexChar(int byte) => byte.toRadixString(16).codeUnitAt(0);
+
+  /// write
+
+  /// Write the [bytes] as JSON characters, escaping as needed.
+  void _writeJsonString(Uint8List bytes) {
+    _context.write();
+    transport.writeAll(_Constants.QUOTE_BYTES);
+
+    int length = bytes.length;
+    for (int i = 0; i < length; i++) {
+      int byte = bytes[i];
+      if ((byte & 0x00FF) >= 0x30) {
+        if (byte == _Constants.BACKSLASH_BYTES[0]) {
+          transport.writeAll(_Constants.BACKSLASH_BYTES);
+          transport.writeAll(_Constants.BACKSLASH_BYTES);
+        } else {
+          transport.write(bytes, i, 1);
+        }
+      } else {
+        _tempBuffer[0] = _Constants.JSON_CHAR_TABLE[byte];
+        if (_tempBuffer[0] == 1) {
+          transport.write(bytes, i, 1);
+        } else if (_tempBuffer[0] > 1) {
+          transport.writeAll(_Constants.BACKSLASH_BYTES);
+          transport.write(_tempBuffer, 0, 1);
+        } else {
+          transport.writeAll(_Constants.ESCSEQ_BYTES);
+          _tempBuffer[0] = _hexChar(byte >> 4);
+          _tempBuffer[1] = _hexChar(byte);
+          transport.write(_tempBuffer, 0, 2);
+        }
+      }
+    }
+
+    transport.writeAll(_Constants.QUOTE_BYTES);
+  }
+
+  void _writeJsonInteger(int i) {
+    if (i == null) i = 0;
+
+    _context.write();
+    String str = i.toString();
+
+    if (_context.escapeNumbers) {
+      transport.writeAll(_Constants.QUOTE_BYTES);
+    }
+    transport.writeAll(utf8Codec.encode(str));
+    if (_context.escapeNumbers) {
+      transport.writeAll(_Constants.QUOTE_BYTES);
+    }
+  }
+
+  void _writeJsonDouble(double d) {
+    if (d == null) d = 0.0;
+
+    _context.write();
+    String str = d.toString();
+    bool escapeNumbers = d.isNaN || d.isInfinite || _context.escapeNumbers;
+
+    if (escapeNumbers) {
+      transport.writeAll(_Constants.QUOTE_BYTES);
+    }
+    transport.writeAll(utf8Codec.encode(str));
+    if (escapeNumbers) {
+      transport.writeAll(_Constants.QUOTE_BYTES);
+    }
+  }
+
+  void _writeJsonBase64(Uint8List bytes) {
+    _context.write();
+    transport.writeAll(_Constants.QUOTE_BYTES);
+
+    String base64 = CryptoUtils.bytesToBase64(bytes);
+    transport.writeAll(utf8Codec.encode(base64));
+
+    transport.writeAll(_Constants.QUOTE_BYTES);
+  }
+
+  void _writeJsonObjectStart() {
+    _context.write();
+    transport.writeAll(_Constants.LBRACE_BYTES);
+    _pushContext(new _PairContext(this));
+  }
+
+  void _writeJsonObjectEnd() {
+    _popContext();
+    transport.writeAll(_Constants.RBRACE_BYTES);
+  }
+
+  void _writeJsonArrayStart() {
+    _context.write();
+    transport.writeAll(_Constants.LBRACKET_BYTES);
+    _pushContext(new _ListContext(this));
+  }
+
+  void _writeJsonArrayEnd() {
+    _popContext();
+    transport.writeAll(_Constants.RBRACKET_BYTES);
+  }
+
+  void writeMessageBegin(TMessage message) {
+    _resetContext();
+
+    _writeJsonArrayStart();
+    _writeJsonInteger(VERSION_1);
+
+    _writeJsonString(utf8Codec.encode(message.name));
+    _writeJsonInteger(message.type);
+    _writeJsonInteger(message.seqid);
+  }
+
+  void writeMessageEnd() {
+    _writeJsonArrayEnd();
+  }
+
+  void writeStructBegin(TStruct struct) {
+    _writeJsonObjectStart();
+  }
+
+  void writeStructEnd() {
+    _writeJsonObjectEnd();
+  }
+
+  void writeFieldBegin(TField field) {
+    _writeJsonInteger(field.id);
+    _writeJsonObjectStart();
+    _writeJsonString(_Constants.getTypeNameBytesForTypeId(field.type));
+  }
+
+  void writeFieldEnd() {
+    _writeJsonObjectEnd();
+  }
+
+  void writeFieldStop() {}
+
+  void writeMapBegin(TMap map) {
+    _writeJsonArrayStart();
+    _writeJsonString(_Constants.getTypeNameBytesForTypeId(map.keyType));
+    _writeJsonString(_Constants.getTypeNameBytesForTypeId(map.valueType));
+    _writeJsonInteger(map.length);
+    _writeJsonObjectStart();
+  }
+
+  void writeMapEnd() {
+    _writeJsonObjectEnd();
+    _writeJsonArrayEnd();
+  }
+
+  void writeListBegin(TList list) {
+    _writeJsonArrayStart();
+    _writeJsonString(_Constants.getTypeNameBytesForTypeId(list.elementType));
+    _writeJsonInteger(list.length);
+  }
+
+  void writeListEnd() {
+    _writeJsonArrayEnd();
+  }
+
+  void writeSetBegin(TSet set) {
+    _writeJsonArrayStart();
+    _writeJsonString(_Constants.getTypeNameBytesForTypeId(set.elementType));
+    _writeJsonInteger(set.length);
+  }
+
+  void writeSetEnd() {
+    _writeJsonArrayEnd();
+  }
+
+  void writeBool(bool b) {
+    if (b == null) b = false;
+    _writeJsonInteger(b ? 1 : 0);
+  }
+
+  void writeByte(int b) {
+    _writeJsonInteger(b);
+  }
+
+  void writeI16(int i16) {
+    _writeJsonInteger(i16);
+  }
+
+  void writeI32(int i32) {
+    _writeJsonInteger(i32);
+  }
+
+  void writeI64(int i64) {
+    _writeJsonInteger(i64);
+  }
+
+  void writeDouble(double d) {
+    _writeJsonDouble(d);
+  }
+
+  void writeString(String s) {
+    var bytes = s != null ? utf8Codec.encode(s) : new Uint8List.fromList([]);
+    _writeJsonString(bytes);
+  }
+
+  void writeBinary(Uint8List bytes) {
+    _writeJsonBase64(bytes);
+  }
+
+  /// read
+
+  Uint8List _readJsonString({bool skipContext: false}) {
+    List<int> bytes = [];
+
+    if (!skipContext) {
+      _context.read();
+    }
+
+    _readJsonSyntaxChar(_Constants.QUOTE_BYTES[0]);
+    while (true) {
+      int byte = _reader.read();
+      if (byte == _Constants.QUOTE_BYTES[0]) {
+        break;
+      }
+
+      // escaped?
+      if (byte != _Constants.ESCSEQ_BYTES[0]) {
+        bytes.add(byte);
+        continue;
+      }
+
+      byte = _reader.read();
+
+      // distinguish between \u00XX and control chars like \n
+      if (byte != _Constants.ESCSEQ_BYTES[1]) {
+        String char = new String.fromCharCode(byte);
+        int offset = _Constants.ESCAPE_CHARS.indexOf(char);
+        if (offset == -1) {
+          throw new TProtocolError(
+              TProtocolErrorType.INVALID_DATA, "Expected control char");
+        }
+        byte = _Constants.ESCAPE_CHAR_VALS.codeUnitAt(offset);
+        bytes.add(byte);
+        continue;
+      }
+
+      // it's \u00XX
+      _readJsonSyntaxChar(_Constants.HEX_0_BYTES[0]);
+      _readJsonSyntaxChar(_Constants.HEX_0_BYTES[0]);
+      transport.readAll(_tempBuffer, 0, 2);
+      byte = _hexVal(_tempBuffer[0]) << 4 + _hexVal(_tempBuffer[1]);
+      bytes.add(byte);
+    }
+
+    return new Uint8List.fromList(bytes);
+  }
+
+  String _readJsonNumericChars() {
+    StringBuffer buffer = new StringBuffer();
+    while (true) {
+      if (!_Constants.isJsonNumeric(_reader.peek())) {
+        break;
+      }
+      buffer.write(new String.fromCharCode(_reader.read()));
+    }
+    return buffer.toString();
+  }
+
+  int _readJsonInteger() {
+    _context.read();
+
+    if (_context.escapeNumbers) {
+      _readJsonSyntaxChar(_Constants.QUOTE_BYTES[0]);
+    }
+    String str = _readJsonNumericChars();
+    if (_context.escapeNumbers) {
+      _readJsonSyntaxChar(_Constants.QUOTE_BYTES[0]);
+    }
+
+    try {
+      return int.parse(str);
+    } on FormatException catch (_) {
+      throw new TProtocolError(TProtocolErrorType.INVALID_DATA,
+          "Bad data encounted in numeric data");
+    }
+  }
+
+  double _readJsonDouble() {
+    _context.read();
+
+    if (_reader.peek() == _Constants.QUOTE_BYTES[0]) {
+      Uint8List bytes = _readJsonString(skipContext: true);
+      double d = double.parse(utf8Codec.decode(bytes), (_) {
+        throw new TProtocolError(TProtocolErrorType.INVALID_DATA,
+            "Bad data encounted in numeric data");
+      });
+      if (!_context.escapeNumbers && !d.isNaN && !d.isInfinite) {
+        throw new TProtocolError(TProtocolErrorType.INVALID_DATA,
+            "Numeric data unexpectedly quoted");
+      }
+      return d;
+    } else {
+      if (_context.escapeNumbers) {
+        // This will throw - we should have had a quote if escapeNumbers == true
+        _readJsonSyntaxChar(_Constants.QUOTE_BYTES[0]);
+      }
+      return double.parse(_readJsonNumericChars(), (_) {
+        throw new TProtocolError(TProtocolErrorType.INVALID_DATA,
+            "Bad data encounted in numeric data");
+      });
+    }
+  }
+
+  Uint8List _readJsonBase64() {
+    // convert UTF-8 bytes of a Base 64 encoded string to binary bytes
+    Uint8List base64Bytes = _readJsonString();
+    String base64 = utf8Codec.decode(base64Bytes);
+
+    return new Uint8List.fromList(CryptoUtils.base64StringToBytes(base64));
+  }
+
+  void _readJsonObjectStart() {
+    _context.read();
+    _readJsonSyntaxChar(_Constants.LBRACE_BYTES[0]);
+    _pushContext(new _PairContext(this));
+  }
+
+  void _readJsonObjectEnd() {
+    _readJsonSyntaxChar(_Constants.RBRACE_BYTES[0]);
+    _popContext();
+  }
+
+  void _readJsonArrayStart() {
+    _context.read();
+    _readJsonSyntaxChar(_Constants.LBRACKET_BYTES[0]);
+    _pushContext(new _ListContext(this));
+  }
+
+  void _readJsonArrayEnd() {
+    _readJsonSyntaxChar(_Constants.RBRACKET_BYTES[0]);
+    _popContext();
+  }
+
+  TMessage readMessageBegin() {
+    _resetContext();
+
+    _readJsonArrayStart();
+    if (_readJsonInteger() != VERSION_1) {
+      throw new TProtocolError(
+          TProtocolErrorType.BAD_VERSION, "Message contained bad version.");
+    }
+
+    Uint8List buffer = _readJsonString();
+    String name = utf8Codec.decode(buffer);
+    int type = _readJsonInteger();
+    int seqid = _readJsonInteger();
+
+    return new TMessage(name, type, seqid);
+  }
+
+  void readMessageEnd() {
+    _readJsonArrayEnd();
+  }
+
+  TStruct readStructBegin() {
+    _readJsonObjectStart();
+    return new TStruct();
+  }
+
+  void readStructEnd() {
+    _readJsonObjectEnd();
+  }
+
+  TField readFieldBegin() {
+    String name = "";
+    int type = TType.STOP;
+    int id = 0;
+
+    if (_reader.peek() != _Constants.RBRACE_BYTES[0]) {
+      id = _readJsonInteger();
+      _readJsonObjectStart();
+      type = _Constants.getTypeIdForTypeName(_readJsonString());
+    }
+
+    return new TField(name, type, id);
+  }
+
+  void readFieldEnd() {
+    _readJsonObjectEnd();
+  }
+
+  TMap readMapBegin() {
+    _readJsonArrayStart();
+    int keyType = _Constants.getTypeIdForTypeName(_readJsonString());
+    int valueType = _Constants.getTypeIdForTypeName(_readJsonString());
+    int length = _readJsonInteger();
+    _readJsonObjectStart();
+
+    return new TMap(keyType, valueType, length);
+  }
+
+  void readMapEnd() {
+    _readJsonObjectEnd();
+    _readJsonArrayEnd();
+  }
+
+  TList readListBegin() {
+    _readJsonArrayStart();
+    int elementType = _Constants.getTypeIdForTypeName(_readJsonString());
+    int length = _readJsonInteger();
+
+    return new TList(elementType, length);
+  }
+
+  void readListEnd() {
+    _readJsonArrayEnd();
+  }
+
+  TSet readSetBegin() {
+    _readJsonArrayStart();
+    int elementType = _Constants.getTypeIdForTypeName(_readJsonString());
+    int length = _readJsonInteger();
+
+    return new TSet(elementType, length);
+  }
+
+  void readSetEnd() {
+    _readJsonArrayEnd();
+  }
+
+  bool readBool() {
+    return _readJsonInteger() == 0 ? false : true;
+  }
+
+  int readByte() {
+    return _readJsonInteger();
+  }
+
+  int readI16() {
+    return _readJsonInteger();
+  }
+
+  int readI32() {
+    return _readJsonInteger();
+  }
+
+  int readI64() {
+    return _readJsonInteger();
+  }
+
+  double readDouble() {
+    return _readJsonDouble();
+  }
+
+  String readString() {
+    return utf8Codec.decode(_readJsonString());
+  }
+
+  Uint8List readBinary() {
+    return new Uint8List.fromList(_readJsonBase64());
+  }
+}
+
+class _Constants {
+  static const utf8codec = const Utf8Codec();
+
+  static final Uint8List HEX_0_BYTES = new Uint8List.fromList('0'.codeUnits);
+  static final Uint8List HEX_9_BYTES = new Uint8List.fromList('9'.codeUnits);
+  static final Uint8List HEX_A_BYTES = new Uint8List.fromList('a'.codeUnits);
+  static final Uint8List HEX_F_BYTES = new Uint8List.fromList('f'.codeUnits);
+  static final Uint8List COMMA_BYTES = new Uint8List.fromList(','.codeUnits);
+  static final Uint8List COLON_BYTES = new Uint8List.fromList(':'.codeUnits);
+  static final Uint8List LBRACE_BYTES = new Uint8List.fromList('{'.codeUnits);
+  static final Uint8List RBRACE_BYTES = new Uint8List.fromList('}'.codeUnits);
+  static final Uint8List LBRACKET_BYTES = new Uint8List.fromList('['.codeUnits);
+  static final Uint8List RBRACKET_BYTES = new Uint8List.fromList(']'.codeUnits);
+  static final Uint8List QUOTE_BYTES = new Uint8List.fromList('"'.codeUnits);
+  static final Uint8List BACKSLASH_BYTES =
+      new Uint8List.fromList(r'\'.codeUnits);
+
+  static final ESCSEQ_BYTES = new Uint8List.fromList(r'\u00'.codeUnits);
+
+  static final Uint8List JSON_CHAR_TABLE = new Uint8List.fromList([
+    0, 0, 0, 0, 0, 0, 0, 0, // 8 bytes
+    'b'.codeUnitAt(0), 't'.codeUnitAt(0), 'n'.codeUnitAt(0), 0, // 4 bytes
+    'f'.codeUnitAt(0), 'r'.codeUnitAt(0), 0, 0, // 4 bytes
+    0, 0, 0, 0, 0, 0, 0, 0, // 8 bytes
+    0, 0, 0, 0, 0, 0, 0, 0, // 8 bytes
+    1, 1, '"'.codeUnitAt(0), 1, 1, 1, 1, 1, // 8 bytes
+    1, 1, 1, 1, 1, 1, 1, 1 // 8 bytes
+  ]);
+
+  static const String ESCAPE_CHARS = r'"\/bfnrt';
+  static const String ESCAPE_CHAR_VALS = '"\\/\b\f\n\r\t';
+
+  static const String NAME_BOOL = 'tf';
+  static const String NAME_BYTE = 'i8';
+  static const String NAME_I16 = 'i16';
+  static const String NAME_I32 = 'i32';
+  static const String NAME_I64 = 'i64';
+  static const String NAME_DOUBLE = 'dbl';
+  static const String NAME_STRUCT = 'rec';
+  static const String NAME_STRING = 'str';
+  static const String NAME_MAP = 'map';
+  static const String NAME_LIST = 'lst';
+  static const String NAME_SET = 'set';
+
+  static final Map<int, Uint8List> _TYPE_ID_TO_NAME_BYTES =
+      new Map.unmodifiable({
+    TType.BOOL: new Uint8List.fromList(NAME_BOOL.codeUnits),
+    TType.BYTE: new Uint8List.fromList(NAME_BYTE.codeUnits),
+    TType.I16: new Uint8List.fromList(NAME_I16.codeUnits),
+    TType.I32: new Uint8List.fromList(NAME_I32.codeUnits),
+    TType.I64: new Uint8List.fromList(NAME_I64.codeUnits),
+    TType.DOUBLE: new Uint8List.fromList(NAME_DOUBLE.codeUnits),
+    TType.STRING: new Uint8List.fromList(NAME_STRING.codeUnits),
+    TType.STRUCT: new Uint8List.fromList(NAME_STRUCT.codeUnits),
+    TType.MAP: new Uint8List.fromList(NAME_MAP.codeUnits),
+    TType.SET: new Uint8List.fromList(NAME_SET.codeUnits),
+    TType.LIST: new Uint8List.fromList(NAME_LIST.codeUnits)
+  });
+
+  static Uint8List getTypeNameBytesForTypeId(int typeId) {
+    if (!_TYPE_ID_TO_NAME_BYTES.containsKey(typeId)) {
+      throw new TProtocolError(
+          TProtocolErrorType.NOT_IMPLEMENTED, "Unrecognized type");
+    }
+
+    return _TYPE_ID_TO_NAME_BYTES[typeId];
+  }
+
+  static final Map<String, int> _NAME_TO_TYPE_ID = new Map.unmodifiable({
+    NAME_BOOL: TType.BOOL,
+    NAME_BYTE: TType.BYTE,
+    NAME_I16: TType.I16,
+    NAME_I32: TType.I32,
+    NAME_I64: TType.I64,
+    NAME_DOUBLE: TType.DOUBLE,
+    NAME_STRING: TType.STRING,
+    NAME_STRUCT: TType.STRUCT,
+    NAME_MAP: TType.MAP,
+    NAME_SET: TType.SET,
+    NAME_LIST: TType.LIST
+  });
+
+  static int getTypeIdForTypeName(Uint8List bytes) {
+    String name = utf8codec.decode(bytes);
+    if (!_NAME_TO_TYPE_ID.containsKey(name)) {
+      throw new TProtocolError(
+          TProtocolErrorType.NOT_IMPLEMENTED, "Unrecognized type");
+    }
+
+    return _NAME_TO_TYPE_ID[name];
+  }
+
+  static final Set<int> _JSON_NUMERICS = new Set.from([
+    '+'.codeUnitAt(0),
+    '-'.codeUnitAt(0),
+    '.'.codeUnitAt(0),
+    '0'.codeUnitAt(0),
+    '1'.codeUnitAt(0),
+    '2'.codeUnitAt(0),
+    '3'.codeUnitAt(0),
+    '4'.codeUnitAt(0),
+    '5'.codeUnitAt(0),
+    '6'.codeUnitAt(0),
+    '7'.codeUnitAt(0),
+    '8'.codeUnitAt(0),
+    '9'.codeUnitAt(0),
+    'E'.codeUnitAt(0),
+    'e'.codeUnitAt(0)
+  ]);
+
+  static bool isJsonNumeric(int byte) {
+    return _JSON_NUMERICS.contains(byte);
+  }
+}
+
+class _LookaheadReader {
+  final TJsonProtocol protocol;
+
+  _LookaheadReader(this.protocol);
+
+  bool _hasData = false;
+  final Uint8List _data = new Uint8List(1);
+
+  int read() {
+    if (_hasData) {
+      _hasData = false;
+    } else {
+      protocol.transport.readAll(_data, 0, 1);
+    }
+
+    return _data[0];
+  }
+
+  int peek() {
+    if (!_hasData) {
+      protocol.transport.readAll(_data, 0, 1);
+    }
+    _hasData = true;
+
+    return _data[0];
+  }
+}
+
+class _BaseContext {
+  final TJsonProtocol protocol;
+
+  _BaseContext(this.protocol);
+
+  void write() {}
+
+  void read() {}
+
+  bool get escapeNumbers => false;
+
+  String toString() => 'BaseContext';
+}
+
+class _ListContext extends _BaseContext {
+  _ListContext(TJsonProtocol protocol) : super(protocol);
+
+  bool _first = true;
+
+  void write() {
+    if (_first) {
+      _first = false;
+    } else {
+      protocol.transport.writeAll(_Constants.COMMA_BYTES);
+    }
+  }
+
+  void read() {
+    if (_first) {
+      _first = false;
+    } else {
+      protocol._readJsonSyntaxChar(_Constants.COMMA_BYTES[0]);
+    }
+  }
+
+  String toString() => 'ListContext';
+}
+
+class _PairContext extends _BaseContext {
+  _PairContext(TJsonProtocol protocol) : super(protocol);
+
+  bool _first = true;
+  bool _colon = true;
+
+  Uint8List get symbolBytes =>
+      _colon ? _Constants.COLON_BYTES : _Constants.COMMA_BYTES;
+
+  void write() {
+    if (_first) {
+      _first = false;
+      _colon = true;
+    } else {
+      protocol.transport.writeAll(symbolBytes);
+      _colon = !_colon;
+    }
+  }
+
+  void read() {
+    if (_first) {
+      _first = false;
+      _colon = true;
+    } else {
+      protocol._readJsonSyntaxChar(symbolBytes[0]);
+      _colon = !_colon;
+    }
+  }
+
+  bool get escapeNumbers => _colon;
+
+  String toString() => 'PairContext';
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/protocol/t_list.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/protocol/t_list.dart b/lib/dart/lib/src/protocol/t_list.dart
new file mode 100644
index 0000000..49f4673
--- /dev/null
+++ b/lib/dart/lib/src/protocol/t_list.dart
@@ -0,0 +1,25 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TList {
+  final int elementType;
+  final int length;
+
+  TList(this.elementType, this.length);
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/protocol/t_map.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/protocol/t_map.dart b/lib/dart/lib/src/protocol/t_map.dart
new file mode 100644
index 0000000..efdf681
--- /dev/null
+++ b/lib/dart/lib/src/protocol/t_map.dart
@@ -0,0 +1,26 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TMap {
+  final int keyType;
+  final int valueType;
+  final int length;
+
+  TMap(this.keyType, this.valueType, this.length);
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/protocol/t_message.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/protocol/t_message.dart b/lib/dart/lib/src/protocol/t_message.dart
new file mode 100644
index 0000000..cc7b886
--- /dev/null
+++ b/lib/dart/lib/src/protocol/t_message.dart
@@ -0,0 +1,35 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TMessageType {
+  static const int CALL = 1;
+  static const int REPLY = 2;
+  static const int EXCEPTION = 3;
+  static const int ONEWAY = 4;
+}
+
+class TMessage {
+  final String name;
+  final int type;
+  final int seqid;
+
+  TMessage(this.name, this.type, this.seqid);
+
+  String toString() => "<TMessage name: '$name' type: $type seqid: $seqid>";
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/protocol/t_multiplexed_protocol.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/protocol/t_multiplexed_protocol.dart b/lib/dart/lib/src/protocol/t_multiplexed_protocol.dart
new file mode 100644
index 0000000..078a6d7
--- /dev/null
+++ b/lib/dart/lib/src/protocol/t_multiplexed_protocol.dart
@@ -0,0 +1,43 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+/// Adapted from the C# version.
+class TMultiplexedProtocol extends TProtocolDecorator {
+  static const SEPARATOR = ':';
+
+  final String _serviceName;
+
+  TMultiplexedProtocol(TProtocol protocol, String serviceName)
+      : _serviceName = serviceName,
+        super(protocol) {
+    if (serviceName == null) {
+      throw new ArgumentError.notNull("serviceName");
+    }
+  }
+
+  void writeMessageBegin(TMessage message) {
+    if (message.type == TMessageType.CALL ||
+        message.type == TMessageType.ONEWAY) {
+      String name = _serviceName + SEPARATOR + message.name;
+      message = new TMessage(name, message.type, message.seqid);
+    }
+
+    super.writeMessageBegin(message);
+  }
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/protocol/t_protocol.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/protocol/t_protocol.dart b/lib/dart/lib/src/protocol/t_protocol.dart
new file mode 100644
index 0000000..f49c032
--- /dev/null
+++ b/lib/dart/lib/src/protocol/t_protocol.dart
@@ -0,0 +1,95 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+abstract class TProtocol {
+  final TTransport transport;
+
+  TProtocol(this.transport);
+
+  /// Write
+  void writeMessageBegin(TMessage message);
+  void writeMessageEnd();
+
+  void writeStructBegin(TStruct struct);
+  void writeStructEnd();
+
+  void writeFieldBegin(TField field);
+  void writeFieldEnd();
+  void writeFieldStop();
+
+  void writeMapBegin(TMap map);
+  void writeMapEnd();
+
+  void writeListBegin(TList list);
+  void writeListEnd();
+
+  void writeSetBegin(TSet set);
+  void writeSetEnd();
+
+  void writeBool(bool b);
+
+  void writeByte(int b);
+
+  void writeI16(int i16);
+
+  void writeI32(int i32);
+
+  void writeI64(int i64);
+
+  void writeDouble(double d);
+
+  void writeString(String str);
+
+  void writeBinary(Uint8List bytes);
+
+  /// Read
+  TMessage readMessageBegin();
+  void readMessageEnd();
+
+  TStruct readStructBegin();
+  void readStructEnd();
+
+  TField readFieldBegin();
+  void readFieldEnd();
+
+  TMap readMapBegin();
+  void readMapEnd();
+
+  TList readListBegin();
+  void readListEnd();
+
+  TSet readSetBegin();
+  void readSetEnd();
+
+  bool readBool();
+
+  int readByte();
+
+  int readI16();
+
+  int readI32();
+
+  int readI64();
+
+  double readDouble();
+
+  String readString();
+
+  Uint8List readBinary();
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/protocol/t_protocol_decorator.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/protocol/t_protocol_decorator.dart b/lib/dart/lib/src/protocol/t_protocol_decorator.dart
new file mode 100644
index 0000000..9cd02f6
--- /dev/null
+++ b/lib/dart/lib/src/protocol/t_protocol_decorator.dart
@@ -0,0 +1,150 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+/// Forward all operations to the wrapped protocol.  Used as a base class.
+///
+/// Adapted from the C# version.
+class TProtocolDecorator extends TProtocol {
+  final TProtocol _protocol;
+
+  TProtocolDecorator(TProtocol protocol)
+      : _protocol = protocol,
+        super(protocol.transport);
+
+  /// Write
+
+  void writeMessageBegin(TMessage message) {
+    _protocol.writeMessageBegin(message);
+  }
+
+  void writeMessageEnd() {
+    _protocol.writeMessageEnd();
+  }
+
+  void writeStructBegin(TStruct struct) {
+    _protocol.writeStructBegin(struct);
+  }
+
+  void writeStructEnd() {
+    _protocol.writeStructEnd();
+  }
+
+  void writeFieldBegin(TField field) {
+    _protocol.writeFieldBegin(field);
+  }
+
+  void writeFieldEnd() {
+    _protocol.writeFieldEnd();
+  }
+
+  void writeFieldStop() {
+    _protocol.writeFieldStop();
+  }
+
+  void writeMapBegin(TMap map) {
+    _protocol.writeMapBegin(map);
+  }
+
+  void writeMapEnd() {
+    _protocol.writeMapEnd();
+  }
+
+  void writeListBegin(TList list) {
+    _protocol.writeListBegin(list);
+  }
+
+  void writeListEnd() {
+    _protocol.writeListEnd();
+  }
+
+  void writeSetBegin(TSet set) {
+    _protocol.writeSetBegin(set);
+  }
+
+  void writeSetEnd() {
+    _protocol.writeSetEnd();
+  }
+
+  void writeBool(bool b) {
+    _protocol.writeBool(b);
+  }
+
+  void writeByte(int b) {
+    _protocol.writeByte(b);
+  }
+
+  void writeI16(int i16) {
+    _protocol.writeI16(i16);
+  }
+
+  void writeI32(int i32) {
+    _protocol.writeI32(i32);
+  }
+
+  void writeI64(int i64) {
+    _protocol.writeI64(i64);
+  }
+
+  void writeDouble(double d) {
+    _protocol.writeDouble(d);
+  }
+
+  void writeString(String str) {
+    _protocol.writeString(str);
+  }
+
+  void writeBinary(Uint8List bytes) {
+    _protocol.writeBinary(bytes);
+  }
+
+  /// Read
+  TMessage readMessageBegin() => _protocol.readMessageBegin();
+  void readMessageEnd() => _protocol.readMessageEnd();
+
+  TStruct readStructBegin() => _protocol.readStructBegin();
+  void readStructEnd() => _protocol.readStructEnd();
+
+  TField readFieldBegin() => _protocol.readFieldBegin();
+  void readFieldEnd() => _protocol.readFieldEnd();
+
+  TMap readMapBegin() => _protocol.readMapBegin();
+  void readMapEnd() => _protocol.readMapEnd();
+
+  TList readListBegin() => _protocol.readListBegin();
+  void readListEnd() => _protocol.readListEnd();
+
+  TSet readSetBegin() => _protocol.readSetBegin();
+  void readSetEnd() => _protocol.readSetEnd();
+
+  bool readBool() => _protocol.readBool();
+
+  int readByte() => _protocol.readByte();
+
+  int readI16() => _protocol.readI16();
+
+  int readI32() => _protocol.readI32();
+
+  int readI64() => _protocol.readI64();
+
+  double readDouble() => _protocol.readDouble();
+
+  String readString() => _protocol.readString();
+
+  Uint8List readBinary() => _protocol.readBinary();
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/protocol/t_protocol_error.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/protocol/t_protocol_error.dart b/lib/dart/lib/src/protocol/t_protocol_error.dart
new file mode 100644
index 0000000..456baeb
--- /dev/null
+++ b/lib/dart/lib/src/protocol/t_protocol_error.dart
@@ -0,0 +1,33 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TProtocolErrorType {
+  static const int UNKNOWN = 0;
+  static const int INVALID_DATA = 1;
+  static const int NEGATIVE_SIZE = 2;
+  static const int SIZE_LIMIT = 3;
+  static const int BAD_VERSION = 4;
+  static const int NOT_IMPLEMENTED = 5;
+  static const int DEPTH_LIMIT = 6;
+}
+
+class TProtocolError extends TError {
+  TProtocolError([int type = TProtocolErrorType.UNKNOWN, String message = ""])
+      : super(type, message);
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/protocol/t_protocol_factory.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/protocol/t_protocol_factory.dart b/lib/dart/lib/src/protocol/t_protocol_factory.dart
new file mode 100644
index 0000000..922c6cb
--- /dev/null
+++ b/lib/dart/lib/src/protocol/t_protocol_factory.dart
@@ -0,0 +1,22 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+abstract class TProtocolFactory<T extends TProtocol> {
+  T getProtocol(TTransport transport);
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/protocol/t_protocol_util.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/protocol/t_protocol_util.dart b/lib/dart/lib/src/protocol/t_protocol_util.dart
new file mode 100644
index 0000000..ad20068
--- /dev/null
+++ b/lib/dart/lib/src/protocol/t_protocol_util.dart
@@ -0,0 +1,107 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TProtocolUtil {
+  // equal to JavaScript Number.MAX_SAFE_INTEGER, 2^53 - 1
+  static const int defaultRecursionLimit = 9007199254740991;
+
+  static int maxRecursionLimit = defaultRecursionLimit;
+
+  static skip(TProtocol prot, int type) {
+    _skip(prot, type, maxRecursionLimit);
+  }
+
+  static _skip(TProtocol prot, int type, int recursionLimit) {
+    if (recursionLimit <= 0) {
+      throw new TProtocolError(
+          TProtocolErrorType.DEPTH_LIMIT, "Depth limit exceeded");
+    }
+
+    switch (type) {
+      case TType.BOOL:
+        prot.readBool();
+        break;
+
+      case TType.BYTE:
+        prot.readByte();
+        break;
+
+      case TType.I16:
+        prot.readI16();
+        break;
+
+      case TType.I32:
+        prot.readI32();
+        break;
+
+      case TType.I64:
+        prot.readI64();
+        break;
+
+      case TType.DOUBLE:
+        prot.readDouble();
+        break;
+
+      case TType.STRING:
+        prot.readBinary();
+        break;
+
+      case TType.STRUCT:
+        prot.readStructBegin();
+        while (true) {
+          TField field = prot.readFieldBegin();
+          if (field.type == TType.STOP) {
+            break;
+          }
+          _skip(prot, field.type, recursionLimit - 1);
+          prot.readFieldEnd();
+        }
+        prot.readStructEnd();
+        break;
+
+      case TType.MAP:
+        TMap map = prot.readMapBegin();
+        for (int i = 0; i < map.length; i++) {
+          _skip(prot, map.keyType, recursionLimit - 1);
+          _skip(prot, map.valueType, recursionLimit - 1);
+        }
+        prot.readMapEnd();
+        break;
+
+      case TType.SET:
+        TSet set = prot.readSetBegin();
+        for (int i = 0; i < set.length; i++) {
+          _skip(prot, set.elementType, recursionLimit - 1);
+        }
+        prot.readSetEnd();
+        break;
+
+      case TType.LIST:
+        TList list = prot.readListBegin();
+        for (int i = 0; i < list.length; i++) {
+          _skip(prot, list.elementType, recursionLimit - 1);
+        }
+        prot.readListEnd();
+        break;
+
+      default:
+        break;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/protocol/t_set.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/protocol/t_set.dart b/lib/dart/lib/src/protocol/t_set.dart
new file mode 100644
index 0000000..b342537
--- /dev/null
+++ b/lib/dart/lib/src/protocol/t_set.dart
@@ -0,0 +1,25 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TSet {
+  final int elementType;
+  final int length;
+
+  TSet(this.elementType, this.length);
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/protocol/t_struct.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/protocol/t_struct.dart b/lib/dart/lib/src/protocol/t_struct.dart
new file mode 100644
index 0000000..0303f63
--- /dev/null
+++ b/lib/dart/lib/src/protocol/t_struct.dart
@@ -0,0 +1,24 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TStruct {
+  final String name;
+
+  TStruct([this.name = ""]);
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/protocol/t_type.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/protocol/t_type.dart b/lib/dart/lib/src/protocol/t_type.dart
new file mode 100644
index 0000000..3919d96
--- /dev/null
+++ b/lib/dart/lib/src/protocol/t_type.dart
@@ -0,0 +1,34 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TType {
+  static const int STOP = 0;
+  static const int VOID = 1;
+  static const int BOOL = 2;
+  static const int BYTE = 3;
+  static const int DOUBLE = 4;
+  static const int I16 = 6;
+  static const int I32 = 8;
+  static const int I64 = 10;
+  static const int STRING = 11;
+  static const int STRUCT = 12;
+  static const int MAP = 13;
+  static const int SET = 14;
+  static const int LIST = 15;
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/t_application_error.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/t_application_error.dart b/lib/dart/lib/src/t_application_error.dart
new file mode 100644
index 0000000..6f8abd4
--- /dev/null
+++ b/lib/dart/lib/src/t_application_error.dart
@@ -0,0 +1,104 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TApplicationErrorType {
+  static const int UNKNOWN = 0;
+  static const int UNKNOWN_METHOD = 1;
+  static const int INVALID_MESSAGE_TYPE = 2;
+  static const int WRONG_METHOD_NAME = 3;
+  static const int BAD_SEQUENCE_ID = 4;
+  static const int MISSING_RESULT = 5;
+  static const int INTERNAL_ERROR = 6;
+  static const int PROTOCOL_ERROR = 7;
+  static const int INVALID_TRANSFORM = 8;
+  static const int INVALID_PROTOCOL = 9;
+  static const int UNSUPPORTED_CLIENT_TYPE = 10;
+}
+
+class TApplicationError extends TError {
+  static final TStruct _struct = new TStruct("TApplicationError");
+  static const int MESSAGE = 1;
+  static final TField _messageField =
+      new TField("message", TType.STRING, MESSAGE);
+  static const int TYPE = 2;
+  static final TField _typeField = new TField("type", TType.I32, TYPE);
+
+  TApplicationError(
+      [int type = TApplicationErrorType.UNKNOWN, String message = ""])
+      : super(type, message);
+
+  static TApplicationError read(TProtocol iprot) {
+    TField field;
+
+    String message = null;
+    int type = TApplicationErrorType.UNKNOWN;
+
+    iprot.readStructBegin();
+    while (true) {
+      field = iprot.readFieldBegin();
+
+      if (field.type == TType.STOP) {
+        break;
+      }
+
+      switch (field.id) {
+        case MESSAGE:
+          if (field.type == TType.STRING) {
+            message = iprot.readString();
+          } else {
+            TProtocolUtil.skip(iprot, field.type);
+          }
+          break;
+
+        case TYPE:
+          if (field.type == TType.I32) {
+            type = iprot.readI32();
+          } else {
+            TProtocolUtil.skip(iprot, field.type);
+          }
+          break;
+
+        default:
+          TProtocolUtil.skip(iprot, field.type);
+          break;
+      }
+      iprot.readFieldEnd();
+    }
+    iprot.readStructEnd();
+
+    return new TApplicationError(type, message);
+  }
+
+  write(TProtocol oprot) {
+    oprot.writeStructBegin(_struct);
+
+    if (message != null && !message.isEmpty) {
+      oprot.writeFieldBegin(_messageField);
+      oprot.writeString(message);
+      oprot.writeFieldEnd();
+    }
+
+    oprot.writeFieldBegin(_typeField);
+    oprot.writeI32(type);
+    oprot.writeFieldEnd();
+
+    oprot.writeFieldStop();
+    oprot.writeStructEnd();
+  }
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/t_base.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/t_base.dart b/lib/dart/lib/src/t_base.dart
new file mode 100644
index 0000000..d5571b6
--- /dev/null
+++ b/lib/dart/lib/src/t_base.dart
@@ -0,0 +1,37 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+abstract class TBase {
+  /// Reads the TObject from the given input protocol.
+  void read(TProtocol iprot);
+
+  /// Writes the objects out to the [oprot] protocol.
+  void write(TProtocol oprot);
+
+  /// Check if a field is currently set or unset, using the [fieldId].
+  bool isSet(int fieldId);
+
+  /// Get a field's value by [fieldId]. Primitive types will be wrapped in the
+  /// appropriate "boxed" types.
+  getFieldValue(int fieldId);
+
+  /// Set a field's value by [fieldId]. Primitive types must be "boxed" in the
+  /// appropriate object wrapper type.
+  setFieldValue(int fieldId, Object value);
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/t_error.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/t_error.dart b/lib/dart/lib/src/t_error.dart
new file mode 100644
index 0000000..93ab732
--- /dev/null
+++ b/lib/dart/lib/src/t_error.dart
@@ -0,0 +1,27 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TError extends Error {
+  final String message;
+  final int type;
+
+  TError(this.type, this.message);
+
+  String toString() => "<TError type: $type message: '$message'>";
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/t_processor.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/t_processor.dart b/lib/dart/lib/src/t_processor.dart
new file mode 100644
index 0000000..dcf20fb
--- /dev/null
+++ b/lib/dart/lib/src/t_processor.dart
@@ -0,0 +1,24 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+/// A processor is a generic object which operates upon an input stream and
+/// writes to some output stream.
+abstract class TProcessor {
+  bool process(TProtocol input, TProtocol output);
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/transport/t_buffered_transport.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/transport/t_buffered_transport.dart b/lib/dart/lib/src/transport/t_buffered_transport.dart
new file mode 100644
index 0000000..1d44c62
--- /dev/null
+++ b/lib/dart/lib/src/transport/t_buffered_transport.dart
@@ -0,0 +1,98 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+/// Buffered implementation of [TTransport].
+class TBufferedTransport extends TTransport {
+  final List<int> _writeBuffer = [];
+  Iterator<int> _readIterator;
+
+  Uint8List _consumeWriteBuffer() {
+    Uint8List buffer = new Uint8List.fromList(_writeBuffer);
+    _writeBuffer.clear();
+    return buffer;
+  }
+
+  void _setReadBuffer(Uint8List readBuffer) {
+    _readIterator = readBuffer != null ? readBuffer.iterator : null;
+  }
+
+  void _reset({bool isOpen: false}) {
+    _isOpen = isOpen;
+    _writeBuffer.clear();
+    _readIterator = null;
+  }
+
+  bool get hasReadData => _readIterator != null;
+
+  bool _isOpen;
+  bool get isOpen => _isOpen;
+
+  Future open() async {
+    _reset(isOpen: true);
+  }
+
+  Future close() async {
+    _reset(isOpen: false);
+  }
+
+  int read(Uint8List buffer, int offset, int length) {
+    if (buffer == null) {
+      throw new ArgumentError.notNull("buffer");
+    }
+
+    if (offset + length > buffer.length) {
+      throw new ArgumentError("The range exceeds the buffer length");
+    }
+
+    if (_readIterator == null || length <= 0) {
+      return 0;
+    }
+
+    int i = 0;
+    while (i < length && _readIterator.moveNext()) {
+      buffer[offset + i] = _readIterator.current;
+      i++;
+    }
+
+    // cleanup iterator when we've reached the end
+    if (_readIterator.current == null) {
+      _readIterator = null;
+    }
+
+    return i;
+  }
+
+  void write(Uint8List buffer, int offset, int length) {
+    if (buffer == null) {
+      throw new ArgumentError.notNull("buffer");
+    }
+
+    if (offset + length > buffer.length) {
+      throw new ArgumentError("The range exceeds the buffer length");
+    }
+
+    _writeBuffer.addAll(buffer.sublist(offset, offset + length));
+  }
+
+  Future flush() {
+    _readIterator = _consumeWriteBuffer().iterator;
+
+    return new Future.value();
+  }
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/transport/t_framed_transport.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/transport/t_framed_transport.dart b/lib/dart/lib/src/transport/t_framed_transport.dart
new file mode 100644
index 0000000..baf8f32
--- /dev/null
+++ b/lib/dart/lib/src/transport/t_framed_transport.dart
@@ -0,0 +1,83 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+/// Framed [TTransport].
+///
+/// Adapted from the Java Framed transport.
+class TFramedTransport extends TBufferedTransport {
+  static const int headerByteCount = 4;
+
+  final TTransport _transport;
+
+  final Uint8List headerBytes = new Uint8List(headerByteCount);
+
+  TFramedTransport(TTransport transport) : _transport = transport {
+    if (transport == null) {
+      throw new ArgumentError.notNull("transport");
+    }
+  }
+
+  bool get isOpen => _transport.isOpen;
+
+  Future open() {
+    _reset(isOpen: true);
+    return _transport.open();
+  }
+
+  Future close() {
+    _reset(isOpen: false);
+    return _transport.close();
+  }
+
+  int read(Uint8List buffer, int offset, int length) {
+    if (hasReadData) {
+      int got = super.read(buffer, offset, length);
+      if (got > 0) return got;
+    }
+
+    _readFrame();
+
+    return super.read(buffer, offset, length);
+  }
+
+  void _readFrame() {
+    _transport.readAll(headerBytes, 0, headerByteCount);
+    int size = headerBytes.buffer.asByteData().getUint32(0);
+
+    if (size < 0) {
+      throw new TTransportError(
+          TTransportErrorType.UNKNOWN, "Read a negative frame size: $size");
+    }
+
+    Uint8List buffer = new Uint8List(size);
+    _transport.readAll(buffer, 0, size);
+    _setReadBuffer(buffer);
+  }
+
+  Future flush() {
+    Uint8List buffer = _consumeWriteBuffer();
+    int length = buffer.length;
+
+    headerBytes.buffer.asByteData().setUint32(0, length);
+    _transport.write(headerBytes, 0, headerByteCount);
+    _transport.write(buffer, 0, length);
+
+    return _transport.flush();
+  }
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/transport/t_http_transport.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/transport/t_http_transport.dart b/lib/dart/lib/src/transport/t_http_transport.dart
new file mode 100644
index 0000000..0e99a1d
--- /dev/null
+++ b/lib/dart/lib/src/transport/t_http_transport.dart
@@ -0,0 +1,100 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+/// HTTP implementation of [TTransport].
+///
+/// For example:
+///
+///     var transport = new THttpClientTransport(new BrowserClient(),
+///         new THttpConfig(url, {'X-My-Custom-Header': 'my value'}));
+///     var protocol = new TJsonProtocol(transport);
+///     var client = new MyThriftServiceClient(protocol);
+///     var result = client.myMethod();
+///
+/// Adapted from the JS XHR HTTP transport.
+class THttpClientTransport extends TBufferedTransport {
+  final Client httpClient;
+  final THttpConfig config;
+
+  THttpClientTransport(this.httpClient, this.config) {
+    if (httpClient == null) {
+      throw new ArgumentError.notNull("httpClient");
+    }
+  }
+
+  Future close() async {
+    _reset(isOpen: false);
+    httpClient.close();
+  }
+
+  Future flush() {
+    var requestBody = CryptoUtils.bytesToBase64(_consumeWriteBuffer());
+
+    // Use a sync completer to ensure that the buffer can be read immediately
+    // after the read buffer is set, and avoid a race condition where another
+    // response could overwrite the read buffer.
+    var completer = new Completer.sync();
+
+    httpClient
+        .post(config.url, headers: config.headers, body: requestBody)
+        .then((response) {
+      Uint8List data;
+      try {
+        data = new Uint8List.fromList(
+            CryptoUtils.base64StringToBytes(response.body));
+      } on FormatException catch (_) {
+        throw new TProtocolError(TProtocolErrorType.INVALID_DATA,
+            "Expected a Base 64 encoded string.");
+      }
+
+      _setReadBuffer(data);
+      completer.complete();
+    });
+
+    return completer.future;
+  }
+}
+
+class THttpConfig {
+  final Uri url;
+
+  Map<String, String> _headers;
+  get headers => _headers;
+
+  THttpConfig(this.url, Map<String, String> headers) {
+    if (url == null || !url.hasAuthority) {
+      throw new ArgumentError("Invalid url");
+    }
+
+    _initHeaders(headers);
+  }
+
+  void _initHeaders(Map<String, String> initial) {
+    var h = {};
+
+    if (initial != null) {
+      h.addAll(initial);
+    }
+
+    h['Content-Type'] = 'application/x-thrift';
+    h['Accept'] = 'application/x-thrift';
+
+    _headers = new Map.unmodifiable(h);
+  }
+}


[5/5] thrift git commit: THRIFT-3299 Apache Thrift language bindings for Dart Client: Dart Patch: Jens Geyer

Posted by je...@apache.org.
THRIFT-3299 Apache Thrift language bindings for Dart
Client: Dart
Patch: Jens Geyer

Adjusted dart package description files (pubspec.yaml)


Project: http://git-wip-us.apache.org/repos/asf/thrift/repo
Commit: http://git-wip-us.apache.org/repos/asf/thrift/commit/a8d5a784
Tree: http://git-wip-us.apache.org/repos/asf/thrift/tree/a8d5a784
Diff: http://git-wip-us.apache.org/repos/asf/thrift/diff/a8d5a784

Branch: refs/heads/master
Commit: a8d5a7841d42a9d663bc7fb2379500597a7d5020
Parents: 932c470
Author: Jens Geyer <je...@apache.org>
Authored: Sat Oct 3 01:34:59 2015 +0200
Committer: Jens Geyer <je...@apache.org>
Committed: Sat Oct 3 01:44:44 2015 +0200

----------------------------------------------------------------------
 lib/dart/pubspec.yaml                     | 6 +++---
 test/dart/test_client/pubspec.yaml        | 4 ++--
 tutorial/dart/client/pubspec.yaml         | 4 ++--
 tutorial/dart/console_client/pubspec.yaml | 4 ++--
 tutorial/dart/server/pubspec.yaml         | 4 ++--
 5 files changed, 11 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/thrift/blob/a8d5a784/lib/dart/pubspec.yaml
----------------------------------------------------------------------
diff --git a/lib/dart/pubspec.yaml b/lib/dart/pubspec.yaml
index f64d980..99d490c 100644
--- a/lib/dart/pubspec.yaml
+++ b/lib/dart/pubspec.yaml
@@ -19,9 +19,9 @@ name: thrift
 version: 1.0.0-dev
 description: >
   A Dart library for Apache Thrift
-author: Mark Erickson <ma...@workiva.com>
-homepage: https://github.com/apache/thrift
-documentation: https://github.com/apache/thrift
+author: Apache Thrift Developers <de...@thrift.apache.org>
+homepage: http://thrift.apache.org
+documentation: http://thrift.apache.org
 environment:
   sdk: ">=1.12.0 <2.0.0"
 dependencies:

http://git-wip-us.apache.org/repos/asf/thrift/blob/a8d5a784/test/dart/test_client/pubspec.yaml
----------------------------------------------------------------------
diff --git a/test/dart/test_client/pubspec.yaml b/test/dart/test_client/pubspec.yaml
index 54d8b0f..b76fb5d 100644
--- a/test/dart/test_client/pubspec.yaml
+++ b/test/dart/test_client/pubspec.yaml
@@ -18,8 +18,8 @@
 name: thrift_test_client
 version: 1.0.0-dev
 description: A client integration test for the Dart Thrift library
-author: Mark Erickson <ma...@workiva.com>
-homepage: https://github.com/apache/thrift
+author: Apache Thrift Developers <de...@thrift.apache.org>
+homepage: http://thrift.apache.org
 environment:
   sdk: ^1.12.0
 dependencies:

http://git-wip-us.apache.org/repos/asf/thrift/blob/a8d5a784/tutorial/dart/client/pubspec.yaml
----------------------------------------------------------------------
diff --git a/tutorial/dart/client/pubspec.yaml b/tutorial/dart/client/pubspec.yaml
index 97c625b..4bd3394 100644
--- a/tutorial/dart/client/pubspec.yaml
+++ b/tutorial/dart/client/pubspec.yaml
@@ -18,8 +18,8 @@
 name: tutorial_client
 version: 1.0.0-dev
 description: A Dart client implementation of the Apache Thrift tutorial
-author: Mark Erickson <ma...@workiva.com>
-homepage: https://github.com/apache/thrift
+author: Apache Thrift Developers <de...@thrift.apache.org>
+homepage: http://thrift.apache.org
 
 environment:
   sdk: ^1.12.0

http://git-wip-us.apache.org/repos/asf/thrift/blob/a8d5a784/tutorial/dart/console_client/pubspec.yaml
----------------------------------------------------------------------
diff --git a/tutorial/dart/console_client/pubspec.yaml b/tutorial/dart/console_client/pubspec.yaml
index 3db9fb9..1423ed2 100644
--- a/tutorial/dart/console_client/pubspec.yaml
+++ b/tutorial/dart/console_client/pubspec.yaml
@@ -19,8 +19,8 @@ name: tutorial_console_client
 version: 1.0.0-dev
 description: >
   A Dart console client to implementation of the Apache Thrift tutorial
-author: Mark Erickson <ma...@workiva.com>
-homepage: https://github.com/apache/thrift
+author: Apache Thrift Developers <de...@thrift.apache.org>
+homepage: http://thrift.apache.org
 environment:
   sdk: ^1.12.0
 dependencies:

http://git-wip-us.apache.org/repos/asf/thrift/blob/a8d5a784/tutorial/dart/server/pubspec.yaml
----------------------------------------------------------------------
diff --git a/tutorial/dart/server/pubspec.yaml b/tutorial/dart/server/pubspec.yaml
index f502974..cdb2dc7 100644
--- a/tutorial/dart/server/pubspec.yaml
+++ b/tutorial/dart/server/pubspec.yaml
@@ -18,8 +18,8 @@
 name: tutorial_server
 version: 1.0.0-dev
 description: A Dart server to support the Apache Thrift tutorial
-author: Mark Erickson <ma...@workiva.com>
-homepage: https://github.com/apache/thrift
+author: Apache Thrift Developers <de...@thrift.apache.org>
+homepage: http://thrift.apache.org
 environment:
   sdk: ^1.12.0
 dependencies:


[2/5] thrift git commit: THRIFT-3299 Create an Apache Thrift language binding for Dart (dartlang.org). Client: Dart Patch: Mark Erickson

Posted by je...@apache.org.
http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/transport/t_message_reader.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/transport/t_message_reader.dart b/lib/dart/lib/src/transport/t_message_reader.dart
new file mode 100644
index 0000000..8ca0708
--- /dev/null
+++ b/lib/dart/lib/src/transport/t_message_reader.dart
@@ -0,0 +1,99 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+/// [TMessageReader] extracts a [TMessage] from bytes.  This is used to allow a
+/// transport to inspect the message seqid and map responses to requests.
+class TMessageReader {
+  final TProtocolFactory protocolFactory;
+
+  final int byteOffset;
+  final _TMessageReaderTransport _transport;
+
+  /// Construct a [MessageReader].  The optional [byteOffset] specifies the
+  /// number of bytes to skip before reading the [TMessage].
+  TMessageReader(this.protocolFactory, {int byteOffset: 0})
+      : _transport = new _TMessageReaderTransport(),
+        this.byteOffset = byteOffset;
+
+  TMessage readMessage(Uint8List bytes) {
+    _transport.reset(bytes, byteOffset);
+    TProtocol protocol = protocolFactory.getProtocol(_transport);
+    TMessage message = protocol.readMessageBegin();
+    _transport.reset(null);
+
+    return message;
+  }
+}
+
+/// An internal class used to support [TMessageReader].
+class _TMessageReaderTransport extends TTransport {
+  _TMessageReaderTransport();
+
+  Iterator<int> _readIterator;
+
+  void reset(Uint8List bytes, [int offset = 0]) {
+    if (bytes == null) {
+      _readIterator = null;
+      return;
+    }
+
+    if (offset > bytes.length) {
+      throw new ArgumentError("The offset exceeds the bytes length");
+    }
+
+    _readIterator = bytes.iterator;
+
+    for (var i = 0; i < offset; i++) {
+      _readIterator.moveNext();
+    }
+  }
+
+  get isOpen => true;
+
+  Future open() => throw new UnsupportedError("Unsupported in MessageReader");
+
+  Future close() => throw new UnsupportedError("Unsupported in MessageReader");
+
+  int read(Uint8List buffer, int offset, int length) {
+    if (buffer == null) {
+      throw new ArgumentError.notNull("buffer");
+    }
+
+    if (offset + length > buffer.length) {
+      throw new ArgumentError("The range exceeds the buffer length");
+    }
+
+    if (_readIterator == null || length <= 0) {
+      return 0;
+    }
+
+    int i = 0;
+    while (i < length && _readIterator.moveNext()) {
+      buffer[offset + i] = _readIterator.current;
+      i++;
+    }
+
+    return i;
+  }
+
+  void write(Uint8List buffer, int offset, int length) =>
+      throw new UnsupportedError("Unsupported in MessageReader");
+
+  Future flush() => throw new UnsupportedError("Unsupported in MessageReader");
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/transport/t_socket.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/transport/t_socket.dart b/lib/dart/lib/src/transport/t_socket.dart
new file mode 100644
index 0000000..74618b6
--- /dev/null
+++ b/lib/dart/lib/src/transport/t_socket.dart
@@ -0,0 +1,38 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+enum TSocketState { CLOSED, OPEN }
+
+abstract class TSocket {
+  Stream<TSocketState> get onState;
+
+  Stream<String> get onError;
+
+  Stream<Uint8List> get onMessage;
+
+  bool get isOpen;
+
+  bool get isClosed;
+
+  Future open();
+
+  Future close();
+
+  void send(Uint8List data);
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/transport/t_socket_transport.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/transport/t_socket_transport.dart b/lib/dart/lib/src/transport/t_socket_transport.dart
new file mode 100644
index 0000000..ad7e48e
--- /dev/null
+++ b/lib/dart/lib/src/transport/t_socket_transport.dart
@@ -0,0 +1,175 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+/// Socket implementation of [TTransport].
+///
+/// For example:
+///
+///     var transport = new TClientSocketTransport(new TWebSocket(url));
+///     var protocol = new TBinaryProtocol(transport);
+///     var client = new MyThriftServiceClient(protocol);
+///     var result = client.myMethod();
+///
+/// Adapted from the JS WebSocket transport.
+abstract class TSocketTransport extends TBufferedTransport {
+  final Logger log = new Logger('thrift.TSocketTransport');
+
+  final TSocket socket;
+
+  /// A transport using the provided [socket].
+  TSocketTransport(this.socket) {
+    if (socket == null) {
+      throw new ArgumentError.notNull('socket');
+    }
+
+    socket.onError.listen((String e) => log.warning(e));
+    socket.onMessage.listen(handleIncomingMessage);
+  }
+
+  bool get isOpen => socket.isOpen;
+
+  Future open() {
+    _reset(isOpen: true);
+    return socket.open();
+  }
+
+  Future close() {
+    _reset(isOpen: false);
+    return socket.close();
+  }
+
+  /// Make an incoming message available to read from the transport.
+  void handleIncomingMessage(Uint8List messageBytes) {
+    _setReadBuffer(messageBytes);
+  }
+}
+
+/// [TClientSocketTransport] is a basic client socket transport.  It sends
+/// outgoing messages and expects a response.
+///
+/// NOTE: This transport expects a single threaded server, as it will process
+/// responses in FIFO order.
+class TClientSocketTransport extends TSocketTransport {
+  final List<Completer<Uint8List>> _completers = [];
+
+  TClientSocketTransport(TSocket socket) : super(socket);
+
+  Future flush() {
+    Uint8List bytes = _consumeWriteBuffer();
+
+    // Use a sync completer to ensure that the buffer can be read immediately
+    // after the read buffer is set, and avoid a race condition where another
+    // response could overwrite the read buffer.
+    Completer completer = new Completer.sync();
+    _completers.add(completer);
+
+    socket.send(bytes);
+
+    return completer.future;
+  }
+
+  void handleIncomingMessage(Uint8List messageBytes) {
+    super.handleIncomingMessage(messageBytes);
+
+    if (_completers.isNotEmpty) {
+      var completer = _completers.removeAt(0);
+      completer.complete();
+    }
+  }
+}
+
+/// [TAsyncClientSocketTransport] sends outgoing messages and expects an
+/// asynchronous response.
+///
+/// NOTE: This transport uses a [MessageReader] to read a [TMessage] when an
+/// incoming message arrives to correlate a response to a request, using the
+/// seqid.
+class TAsyncClientSocketTransport extends TSocketTransport {
+  static const defaultTimeout = const Duration(seconds: 30);
+
+  final Map<int, Completer<Uint8List>> _completers = {};
+
+  final TMessageReader messageReader;
+
+  final Duration responseTimeout;
+
+  TAsyncClientSocketTransport(TSocket socket, TMessageReader messageReader,
+      {Duration responseTimeout: defaultTimeout})
+      : this.messageReader = messageReader,
+        this.responseTimeout = responseTimeout,
+        super(socket);
+
+  Future flush() {
+    Uint8List bytes = _consumeWriteBuffer();
+    TMessage message = messageReader.readMessage(bytes);
+    int seqid = message.seqid;
+
+    // Use a sync completer to ensure that the buffer can be read immediately
+    // after the read buffer is set, and avoid a race condition where another
+    // response could overwrite the read buffer.
+    Completer completer = new Completer.sync();
+    _completers[seqid] = completer;
+
+    if (responseTimeout != null) {
+      new Future.delayed(responseTimeout, () {
+        var completer = _completers.remove(seqid);
+        if (completer != null) {
+          completer.completeError(
+              new TimeoutException("Response timed out.", responseTimeout));
+        }
+      });
+    }
+
+    socket.send(bytes);
+
+    return completer.future;
+  }
+
+  void handleIncomingMessage(Uint8List messageBytes) {
+    super.handleIncomingMessage(messageBytes);
+
+    TMessage message = messageReader.readMessage(messageBytes);
+    var completer = _completers.remove(message.seqid);
+    if (completer != null) {
+      completer.complete();
+    }
+  }
+}
+
+/// [TServerSocketTransport] listens for incoming messages.  When it sends a
+/// response, it does not expect an acknowledgement.
+class TServerSocketTransport extends TSocketTransport {
+  final StreamController _onIncomingMessageController;
+  Stream get onIncomingMessage => _onIncomingMessageController.stream;
+
+  TServerSocketTransport(TSocket socket)
+      : _onIncomingMessageController = new StreamController.broadcast(),
+        super(socket);
+
+  Future flush() async {
+    Uint8List message = _consumeWriteBuffer();
+    socket.send(message);
+  }
+
+  void handleIncomingMessage(Uint8List messageBytes) {
+    super.handleIncomingMessage(messageBytes);
+
+    _onIncomingMessageController.add(null);
+  }
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/transport/t_transport.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/transport/t_transport.dart b/lib/dart/lib/src/transport/t_transport.dart
new file mode 100644
index 0000000..563d5eb
--- /dev/null
+++ b/lib/dart/lib/src/transport/t_transport.dart
@@ -0,0 +1,70 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+abstract class TTransport {
+  /// Queries whether the transport is open.
+  /// Returns [true] if the transport is open.
+  bool get isOpen;
+
+  /// Opens the transport for reading/writing.
+  /// Throws [TTransportError] if the transport could not be opened.
+  Future open();
+
+  /// Closes the transport.
+  Future close();
+
+  /// Reads up to [length] bytes into [buffer], starting at [offset].
+  /// Returns the number of bytes actually read.
+  /// Throws [TTransportError] if there was an error reading data
+  int read(Uint8List buffer, int offset, int length);
+
+  /// Guarantees that all of [length] bytes are actually read off the transport.
+  /// Returns the number of bytes actually read, which must be equal to
+  /// [length].
+  /// Throws [TTransportError] if there was an error reading data
+  int readAll(Uint8List buffer, int offset, int length) {
+    int got = 0;
+    int ret = 0;
+    while (got < length) {
+      ret = read(buffer, offset + got, length - got);
+      if (ret <= 0) {
+        throw new TTransportError(
+            TTransportErrorType.UNKNOWN,
+            "Cannot read. Remote side has closed. Tried to read $length "
+            "bytes, but only got $got bytes.");
+      }
+      got += ret;
+    }
+    return got;
+  }
+
+  /// Writes up to [len] bytes from the buffer.
+  /// Throws [TTransportError] if there was an error writing data
+  void write(Uint8List buffer, int offset, int length);
+
+  /// Writes the [bytes] to the output.
+  /// Throws [TTransportError] if there was an error writing data
+  void writeAll(Uint8List buffer) {
+    write(buffer, 0, buffer.length);
+  }
+
+  /// Flush any pending data out of a transport buffer.
+  /// Throws [TTransportError] if there was an error writing out data.
+  Future flush();
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/transport/t_transport_error.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/transport/t_transport_error.dart b/lib/dart/lib/src/transport/t_transport_error.dart
new file mode 100644
index 0000000..d3508e0
--- /dev/null
+++ b/lib/dart/lib/src/transport/t_transport_error.dart
@@ -0,0 +1,31 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+class TTransportErrorType {
+  static const int UNKNOWN = 0;
+  static const int NOT_OPEN = 1;
+  static const int ALREADY_OPEN = 2;
+  static const int TIMED_OUT = 3;
+  static const int END_OF_FILE = 4;
+}
+
+class TTransportError extends TError {
+  TTransportError([int type = TTransportErrorType.UNKNOWN, String message = ""])
+      : super(type, message);
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/transport/t_transport_factory.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/src/transport/t_transport_factory.dart b/lib/dart/lib/src/transport/t_transport_factory.dart
new file mode 100644
index 0000000..7a10461
--- /dev/null
+++ b/lib/dart/lib/src/transport/t_transport_factory.dart
@@ -0,0 +1,27 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+part of thrift;
+
+/// Factory class used to create wrapped instance of a [TTransport]. This is
+/// used primarily in servers.
+///
+/// Adapted from the Java version.
+class TTransportFactory {
+  Future<TTransport> getTransport(TTransport transport) =>
+      new Future.value(transport);
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/thrift.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/thrift.dart b/lib/dart/lib/thrift.dart
new file mode 100644
index 0000000..2483726
--- /dev/null
+++ b/lib/dart/lib/thrift.dart
@@ -0,0 +1,58 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+library thrift;
+
+import 'dart:async';
+import 'dart:convert' show Utf8Codec;
+import 'dart:typed_data' show ByteData;
+import 'dart:typed_data' show Uint8List;
+
+import 'package:crypto/crypto.dart' show CryptoUtils;
+import 'package:http/http.dart' show Client;
+import 'package:logging/logging.dart';
+
+part 'src/t_application_error.dart';
+part 'src/t_base.dart';
+part 'src/t_error.dart';
+part 'src/t_processor.dart';
+
+part 'src/protocol/t_binary_protocol.dart';
+part 'src/protocol/t_field.dart';
+part 'src/protocol/t_json_protocol.dart';
+part 'src/protocol/t_list.dart';
+part 'src/protocol/t_map.dart';
+part 'src/protocol/t_message.dart';
+part 'src/protocol/t_multiplexed_protocol.dart';
+part 'src/protocol/t_protocol.dart';
+part 'src/protocol/t_protocol_decorator.dart';
+part 'src/protocol/t_protocol_error.dart';
+part 'src/protocol/t_protocol_factory.dart';
+part 'src/protocol/t_protocol_util.dart';
+part 'src/protocol/t_set.dart';
+part 'src/protocol/t_struct.dart';
+part 'src/protocol/t_type.dart';
+
+part 'src/transport/t_buffered_transport.dart';
+part 'src/transport/t_framed_transport.dart';
+part 'src/transport/t_http_transport.dart';
+part 'src/transport/t_message_reader.dart';
+part 'src/transport/t_socket.dart';
+part 'src/transport/t_transport.dart';
+part 'src/transport/t_transport_error.dart';
+part 'src/transport/t_transport_factory.dart';
+part 'src/transport/t_socket_transport.dart';

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/thrift_browser.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/thrift_browser.dart b/lib/dart/lib/thrift_browser.dart
new file mode 100644
index 0000000..2ebc257
--- /dev/null
+++ b/lib/dart/lib/thrift_browser.dart
@@ -0,0 +1,22 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+library thrift_browser;
+
+/// Classes that are only supported in browser applications go here
+
+export 'src/browser/t_web_socket.dart' show TWebSocket;

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/thrift_console.dart
----------------------------------------------------------------------
diff --git a/lib/dart/lib/thrift_console.dart b/lib/dart/lib/thrift_console.dart
new file mode 100644
index 0000000..48a83d1
--- /dev/null
+++ b/lib/dart/lib/thrift_console.dart
@@ -0,0 +1,23 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+library thrift_console;
+
+/// Classes that are only supported in console applications go here
+
+export 'src/console/t_tcp_socket.dart' show TTcpSocket;
+export 'src/console/t_web_socket.dart' show TWebSocket;

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/pubspec.yaml
----------------------------------------------------------------------
diff --git a/lib/dart/pubspec.yaml b/lib/dart/pubspec.yaml
new file mode 100644
index 0000000..f64d980
--- /dev/null
+++ b/lib/dart/pubspec.yaml
@@ -0,0 +1,36 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# 'License'); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: thrift
+version: 1.0.0-dev
+description: >
+  A Dart library for Apache Thrift
+author: Mark Erickson <ma...@workiva.com>
+homepage: https://github.com/apache/thrift
+documentation: https://github.com/apache/thrift
+environment:
+  sdk: ">=1.12.0 <2.0.0"
+dependencies:
+  crypto: "^0.9.0"
+  http: "^0.11.3"
+  logging: "^0.11.0"
+dev_dependencies:
+  coverage: "^0.7.2"
+  dart_dev: "^1.0.1"
+  dart_style: "^0.2.0"
+  mockito: "^0.11.0"
+  test: "^0.12.0"

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/test/protocol/t_protocol_test.dart
----------------------------------------------------------------------
diff --git a/lib/dart/test/protocol/t_protocol_test.dart b/lib/dart/test/protocol/t_protocol_test.dart
new file mode 100644
index 0000000..88ddd4f
--- /dev/null
+++ b/lib/dart/test/protocol/t_protocol_test.dart
@@ -0,0 +1,374 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+library thrift.test.transport.t_json_protocol_test;
+
+import 'dart:async';
+import 'dart:typed_data' show Uint8List;
+
+import 'package:test/test.dart';
+import 'package:thrift/thrift.dart';
+
+void main() {
+  final message = new TMessage('my message', TMessageType.ONEWAY, 123);
+
+  TProtocol protocol;
+
+  Primitive getPrimitive(int tType) {
+    switch (tType) {
+      case TType.BOOL:
+        return new Primitive(protocol.readBool, protocol.writeBool, false);
+
+      case TType.BYTE:
+        return new Primitive(protocol.readByte, protocol.writeByte, 0);
+
+      case TType.I16:
+        return new Primitive(protocol.readI16, protocol.writeI16, 0);
+
+      case TType.I32:
+        return new Primitive(protocol.readI32, protocol.writeI32, 0);
+
+      case TType.I64:
+        return new Primitive(protocol.readI64, protocol.writeI64, 0);
+
+      case TType.DOUBLE:
+        return new Primitive(protocol.readDouble, protocol.writeDouble, 0);
+
+      case TType.STRING:
+        return new Primitive(protocol.readString, protocol.writeString, '');
+
+      default:
+        throw new UnsupportedError("Unsupported TType $tType");
+    }
+  }
+
+  Future primitiveTest(Primitive primitive, input) async {
+    primitive.write(input);
+    protocol.writeMessageEnd();
+
+    await protocol.transport.flush();
+
+    protocol.readMessageBegin();
+    var output = primitive.read();
+
+    expect(output, input);
+  }
+
+  Future primitiveNullTest(Primitive primitive) async {
+    primitive.write(null);
+    protocol.writeMessageEnd();
+
+    await protocol.transport.flush();
+
+    protocol.readMessageBegin();
+    var output = primitive.read();
+
+    expect(output, primitive.defaultValue);
+  }
+
+  var sharedTests = () {
+    test('Test message', () async {
+      protocol.writeMessageEnd();
+
+      await protocol.transport.flush();
+
+      var subject = protocol.readMessageBegin();
+
+      expect(subject.name, message.name);
+      expect(subject.type, message.type);
+      expect(subject.seqid, message.seqid);
+    });
+
+    test('Test struct', () async {
+      var input = new TStruct();
+
+      protocol.writeStructBegin(input);
+      protocol.writeStructEnd();
+      protocol.writeMessageEnd();
+
+      await protocol.transport.flush();
+
+      protocol.readMessageBegin();
+      var output = protocol.readStructBegin();
+
+      // name is not serialized, see C# version for reference
+      expect(output, isNotNull);
+    });
+
+    test('Test field', () async {
+      var input = new TField('my field', TType.MAP, 123);
+
+      protocol.writeFieldBegin(input);
+      protocol.writeFieldEnd();
+      protocol.writeMessageEnd();
+
+      await protocol.transport.flush();
+
+      protocol.readMessageBegin();
+      var output = protocol.readFieldBegin();
+
+      // name is not serialized, see C# version for reference
+      expect(output.type, input.type);
+      expect(output.id, input.id);
+    });
+
+    test('Test map', () async {
+      var input = new TMap(TType.STRING, TType.STRUCT, 123);
+
+      protocol.writeMapBegin(input);
+      protocol.writeMapEnd();
+      protocol.writeMessageEnd();
+
+      await protocol.transport.flush();
+
+      protocol.readMessageBegin();
+      var output = protocol.readMapBegin();
+
+      expect(output.keyType, input.keyType);
+      expect(output.valueType, input.valueType);
+      expect(output.length, input.length);
+    });
+
+    test('Test list', () async {
+      var input = new TList(TType.STRING, 123);
+
+      protocol.writeListBegin(input);
+      protocol.writeListEnd();
+      protocol.writeMessageEnd();
+
+      await protocol.transport.flush();
+
+      protocol.readMessageBegin();
+      var output = protocol.readListBegin();
+
+      expect(output.elementType, input.elementType);
+      expect(output.length, input.length);
+    });
+
+    test('Test set', () async {
+      var input = new TSet(TType.STRING, 123);
+
+      protocol.writeSetBegin(input);
+      protocol.writeSetEnd();
+      protocol.writeMessageEnd();
+
+      await protocol.transport.flush();
+
+      protocol.readMessageBegin();
+      var output = protocol.readListBegin();
+
+      expect(output.elementType, input.elementType);
+      expect(output.length, input.length);
+    });
+
+    test('Test bool', () async {
+      await primitiveTest(getPrimitive(TType.BOOL), true);
+    });
+
+    test('Test bool null', () async {
+      await primitiveNullTest(getPrimitive(TType.BOOL));
+    });
+
+    test('Test byte', () async {
+      await primitiveTest(getPrimitive(TType.BYTE), 64);
+    });
+
+    test('Test byte null', () async {
+      await primitiveNullTest(getPrimitive(TType.BYTE));
+    });
+
+    test('Test I16', () async {
+      await primitiveTest(getPrimitive(TType.I16), 32767);
+    });
+
+    test('Test I16 null', () async {
+      await primitiveNullTest(getPrimitive(TType.I16));
+    });
+
+    test('Test I32', () async {
+      await primitiveTest(getPrimitive(TType.I32), 2147483647);
+    });
+
+    test('Test I32 null', () async {
+      await primitiveNullTest(getPrimitive(TType.I32));
+    });
+
+    test('Test I64', () async {
+      await primitiveTest(getPrimitive(TType.I64), 9223372036854775807);
+    });
+
+    test('Test I64 null', () async {
+      await primitiveNullTest(getPrimitive(TType.I64));
+    });
+
+    test('Test double', () async {
+      await primitiveTest(getPrimitive(TType.DOUBLE), 3.1415926);
+    });
+
+    test('Test double null', () async {
+      await primitiveNullTest(getPrimitive(TType.DOUBLE));
+    });
+
+    test('Test string', () async {
+      var input = 'There are only two hard things in computer science: '
+          'cache invalidation, naming things, and off-by-one errors.';
+      await primitiveTest(getPrimitive(TType.STRING), input);
+    });
+
+    test('Test string null', () async {
+      await primitiveNullTest(getPrimitive(TType.STRING));
+    });
+
+    test('Test binary', () async {
+      var input = new Uint8List.fromList(new List.filled(100, 123));
+
+      protocol.writeBinary(input);
+      protocol.writeMessageEnd();
+
+      await protocol.transport.flush();
+
+      protocol.readMessageBegin();
+      var output = protocol.readBinary();
+
+      expect(output.length, input.length);
+      expect(output.every((i) => i == 123), isTrue);
+    });
+
+    test('Test complex struct', () async {
+      // {1: {10: 20}, 2: {30: 40}}
+      protocol.writeStructBegin(new TStruct());
+      protocol.writeFieldBegin(new TField('success', TType.MAP, 0));
+      protocol.writeMapBegin(new TMap(TType.I32, TType.MAP, 2));
+
+      protocol.writeI32(1); // key
+      protocol.writeMapBegin(new TMap(TType.I32, TType.I32, 1));
+      protocol.writeI32(10); // key
+      protocol.writeI32(20); // value
+      protocol.writeMapEnd();
+
+      protocol.writeI32(2); // key
+      protocol.writeMapBegin(new TMap(TType.I32, TType.I32, 1));
+      protocol.writeI32(30); // key
+      protocol.writeI32(40); // value
+      protocol.writeMapEnd();
+
+      protocol.writeMapEnd();
+      protocol.writeFieldEnd();
+      protocol.writeFieldStop();
+      protocol.writeStructEnd();
+      protocol.writeMessageEnd();
+
+      await protocol.transport.flush();
+
+      protocol.readMessageBegin();
+      protocol.readStructBegin();
+      expect(protocol.readFieldBegin().type, TType.MAP);
+      expect(protocol.readMapBegin().length, 2);
+
+      expect(protocol.readI32(), 1); // key
+      expect(protocol.readMapBegin().length, 1);
+      expect(protocol.readI32(), 10); // key
+      expect(protocol.readI32(), 20); // value
+      protocol.readMapEnd();
+
+      expect(protocol.readI32(), 2); // key
+      expect(protocol.readMapBegin().length, 1);
+      expect(protocol.readI32(), 30); // key
+      expect(protocol.readI32(), 40); // value
+      protocol.readMapEnd();
+
+      protocol.readMapEnd();
+      protocol.readFieldEnd();
+      protocol.readStructEnd();
+      protocol.readMessageEnd();
+    });
+
+    test('Test nested maps and lists', () async {
+      // {1: [{10: 20}], 2: [{30: 40}]}
+      protocol.writeMapBegin(new TMap(TType.I32, TType.LIST, 2));
+
+      protocol.writeI32(1); // key
+      protocol.writeListBegin(new TList(TType.MAP, 1));
+      protocol.writeMapBegin(new TMap(TType.I32, TType.I32, 1));
+      protocol.writeI32(10); // key
+      protocol.writeI32(20); // value
+      protocol.writeMapEnd();
+      protocol.writeListEnd();
+
+      protocol.writeI32(2); // key
+      protocol.writeListBegin(new TList(TType.MAP, 1));
+      protocol.writeMapBegin(new TMap(TType.I32, TType.I32, 1));
+      protocol.writeI32(30); // key
+      protocol.writeI32(40); // value
+      protocol.writeMapEnd();
+      protocol.writeListEnd();
+
+      protocol.writeMapEnd();
+      protocol.writeMessageEnd();
+
+      await protocol.transport.flush();
+
+      protocol.readMessageBegin();
+      expect(protocol.readMapBegin().length, 2);
+
+      expect(protocol.readI32(), 1); // key
+      expect(protocol.readListBegin().length, 1);
+      expect(protocol.readMapBegin().length, 1);
+      expect(protocol.readI32(), 10); // key
+      expect(protocol.readI32(), 20); // value
+      protocol.readMapEnd();
+      protocol.readListEnd();
+
+      expect(protocol.readI32(), 2); // key
+      expect(protocol.readListBegin().length, 1);
+      expect(protocol.readMapBegin().length, 1);
+      expect(protocol.readI32(), 30); // key
+      expect(protocol.readI32(), 40); // value
+      protocol.readMapEnd();
+      protocol.readListEnd();
+
+      protocol.readMapEnd();
+      protocol.readMessageEnd();
+    });
+  };
+
+  group('JSON', () {
+    setUp(() {
+      protocol = new TJsonProtocol(new TBufferedTransport());
+      protocol.writeMessageBegin(message);
+    });
+
+    group('shared tests', sharedTests);
+  });
+
+  group('binary', () {
+    setUp(() {
+      protocol = new TBinaryProtocol(new TBufferedTransport());
+      protocol.writeMessageBegin(message);
+    });
+
+    group('shared tests', sharedTests);
+  });
+}
+
+class Primitive {
+  final Function read;
+  final Function write;
+  final defaultValue;
+
+  Primitive(this.read, this.write, this.defaultValue);
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/test/t_application_error_test.dart
----------------------------------------------------------------------
diff --git a/lib/dart/test/t_application_error_test.dart b/lib/dart/test/t_application_error_test.dart
new file mode 100644
index 0000000..511d8d6
--- /dev/null
+++ b/lib/dart/test/t_application_error_test.dart
@@ -0,0 +1,46 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+library thrift.test.t_application_error_test;
+
+import 'package:test/test.dart';
+import 'package:thrift/thrift.dart';
+
+void main() {
+  TProtocol protocol;
+
+  setUp(() {
+    protocol = new TBinaryProtocol(new TBufferedTransport());
+  });
+
+  test('Write and read an application error', () {
+    var expectedType = TApplicationErrorType.INTERNAL_ERROR;
+    var expectedMessage = 'test error message';
+
+    TApplicationError error =
+        new TApplicationError(expectedType, expectedMessage);
+    error.write(protocol);
+
+    protocol.transport.flush();
+
+    TApplicationError subject = TApplicationError.read(protocol);
+
+    expect(subject, isNotNull);
+    expect(subject.type, expectedType);
+    expect(subject.message, expectedMessage);
+  });
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/test/transport/t_http_transport_test.dart
----------------------------------------------------------------------
diff --git a/lib/dart/test/transport/t_http_transport_test.dart b/lib/dart/test/transport/t_http_transport_test.dart
new file mode 100644
index 0000000..7fcab3e
--- /dev/null
+++ b/lib/dart/test/transport/t_http_transport_test.dart
@@ -0,0 +1,165 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+library thrift.test.transport.t_socket_transport_test;
+
+import 'dart:async';
+import 'dart:convert' show Encoding;
+import 'dart:convert' show Utf8Codec;
+import 'dart:typed_data' show Uint8List;
+
+import 'package:crypto/crypto.dart' show CryptoUtils;
+import 'package:http/http.dart' show BaseRequest;
+import 'package:http/http.dart' show Client;
+import 'package:http/http.dart' show Response;
+import 'package:http/http.dart' show StreamedResponse;
+import 'package:test/test.dart';
+import 'package:thrift/thrift.dart';
+
+void main() {
+  const utf8Codec = const Utf8Codec();
+
+  group('THttpClientTransport', () {
+    FakeHttpClient client;
+    THttpClientTransport transport;
+
+    setUp(() {
+      client = new FakeHttpClient(sync: false);
+      var config = new THttpConfig(Uri.parse('http://localhost'), {});
+      transport = new THttpClientTransport(client, config);
+    });
+
+    test('Test transport sends body', () async {
+      var expectedText = 'my request';
+      transport.writeAll(utf8Codec.encode(expectedText));
+
+      expect(client.postRequest, isEmpty);
+
+      await transport.flush();
+
+      expect(client.postRequest, isNotEmpty);
+
+      var requestText =
+          utf8Codec.decode(CryptoUtils.base64StringToBytes(client.postRequest));
+      expect(requestText, expectedText);
+    });
+
+    test('Test transport receives response', () async {
+      var expectedText = 'my response';
+      var expectedBytes = utf8Codec.encode(expectedText);
+      client.postResponse = CryptoUtils.bytesToBase64(expectedBytes);
+
+      transport.writeAll(utf8Codec.encode('my request'));
+      expect(transport.hasReadData, isFalse);
+
+      await transport.flush();
+
+      expect(transport.hasReadData, isTrue);
+
+      var buffer = new Uint8List(expectedBytes.length);
+      transport.readAll(buffer, 0, expectedBytes.length);
+
+      var bufferText = utf8Codec.decode(buffer);
+      expect(bufferText, expectedText);
+    });
+  });
+
+  group('THttpClientTransport with multiple messages', () {
+    FakeHttpClient client;
+    THttpClientTransport transport;
+
+    setUp(() {
+      client = new FakeHttpClient(sync: true);
+      var config = new THttpConfig(Uri.parse('http://localhost'), {});
+      transport = new THttpClientTransport(client, config);
+    });
+
+    test('Test read correct buffer after flush', () async {
+      String bufferText;
+      var expectedText = 'response 1';
+      var expectedBytes = utf8Codec.encode(expectedText);
+
+      // prepare a response
+      transport.writeAll(utf8Codec.encode('request 1'));
+      client.postResponse = CryptoUtils.bytesToBase64(expectedBytes);
+
+      Future responseReady = transport.flush().then((_) {
+        var buffer = new Uint8List(expectedBytes.length);
+        transport.readAll(buffer, 0, expectedBytes.length);
+        bufferText = utf8Codec.decode(buffer);
+      });
+
+      // prepare a second response
+      transport.writeAll(utf8Codec.encode('request 2'));
+      var response2Bytes = utf8Codec.encode('response 2');
+      client.postResponse = CryptoUtils.bytesToBase64(response2Bytes);
+      await transport.flush();
+
+      await responseReady;
+      expect(bufferText, expectedText);
+    });
+  });
+}
+
+class FakeHttpClient implements Client {
+  String postResponse = '';
+  String postRequest = '';
+
+  final bool sync;
+
+  FakeHttpClient({this.sync: false});
+
+  Future<Response> post(url,
+      {Map<String, String> headers, body, Encoding encoding}) {
+    postRequest = body;
+    var response = new Response(postResponse, 200);
+
+    if (sync) {
+      return new Future.sync(() => response);
+    } else {
+      return new Future.value(response);
+    }
+  }
+
+  Future<Response> head(url, {Map<String, String> headers}) =>
+      throw new UnimplementedError();
+
+  Future<Response> get(url, {Map<String, String> headers}) =>
+      throw new UnimplementedError();
+
+  Future<Response> put(url,
+          {Map<String, String> headers, body, Encoding encoding}) =>
+      throw new UnimplementedError();
+
+  Future<Response> patch(url,
+          {Map<String, String> headers, body, Encoding encoding}) =>
+      throw new UnimplementedError();
+
+  Future<Response> delete(url, {Map<String, String> headers}) =>
+      throw new UnimplementedError();
+
+  Future<String> read(url, {Map<String, String> headers}) =>
+      throw new UnimplementedError();
+
+  Future<Uint8List> readBytes(url, {Map<String, String> headers}) =>
+      throw new UnimplementedError();
+
+  Future<StreamedResponse> send(BaseRequest request) =>
+      throw new UnimplementedError();
+
+  void close() => throw new UnimplementedError();
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/test/transport/t_socket_transport_test.dart
----------------------------------------------------------------------
diff --git a/lib/dart/test/transport/t_socket_transport_test.dart b/lib/dart/test/transport/t_socket_transport_test.dart
new file mode 100644
index 0000000..997df0d
--- /dev/null
+++ b/lib/dart/test/transport/t_socket_transport_test.dart
@@ -0,0 +1,312 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+library thrift.test.transport.t_socket_transport_test;
+
+import 'dart:async';
+import 'dart:convert' show Utf8Codec;
+import 'dart:typed_data' show Uint8List;
+
+import 'package:crypto/crypto.dart' show CryptoUtils;
+import 'package:mockito/mockito.dart';
+import 'package:test/test.dart';
+import 'package:thrift/thrift.dart';
+
+void main() {
+  const utf8Codec = const Utf8Codec();
+
+  final requestText = 'my test request';
+  final requestBytes = new Uint8List.fromList(utf8Codec.encode(requestText));
+  final requestBase64 = CryptoUtils.bytesToBase64(requestBytes);
+
+  final responseText = 'response 1';
+  final responseBytes = new Uint8List.fromList(utf8Codec.encode(responseText));
+  final responseBase64 = CryptoUtils.bytesToBase64(responseBytes);
+
+  final framedResponseBase64 =
+      CryptoUtils.bytesToBase64(_getFramedResponse(responseBytes));
+
+  group('TClientSocketTransport', () {
+    FakeSocket socket;
+    TTransport transport;
+
+    setUp(() async {
+      socket = new FakeSocket(sync: false);
+      await socket.open();
+      transport = new TClientSocketTransport(socket);
+      await transport.open();
+      transport.writeAll(requestBytes);
+    });
+
+    test('Test client sending data over transport', () async {
+      expect(socket.sendPayload, isNull);
+
+      Future responseReady = transport.flush();
+
+      // allow microtask events to finish
+      await new Future.value();
+
+      expect(socket.sendPayload, isNotNull);
+      expect(socket.sendPayload, requestBytes);
+
+      // simulate a response
+      socket.receiveFakeMessage(responseBase64);
+
+      await responseReady;
+      var buffer = new Uint8List(responseBytes.length);
+      transport.readAll(buffer, 0, responseBytes.length);
+      var bufferText = utf8Codec.decode(buffer);
+
+      expect(bufferText, responseText);
+    });
+  }, timeout: new Timeout(new Duration(seconds: 1)));
+
+  group('TClientSocketTransport with FramedTransport', () {
+    FakeSocket socket;
+    TTransport transport;
+
+    setUp(() async {
+      socket = new FakeSocket(sync: true);
+      await socket.open();
+
+      transport = new TFramedTransport(new TClientSocketTransport(socket));
+      await transport.open();
+      transport.writeAll(requestBytes);
+    });
+
+    test('Test client sending data over framed transport', () async {
+      String bufferText;
+
+      Future responseReady = transport.flush().then((_) {
+        var buffer = new Uint8List(responseBytes.length);
+        transport.readAll(buffer, 0, responseBytes.length);
+        bufferText = utf8Codec.decode(buffer);
+      });
+
+      // simulate a response
+      socket.receiveFakeMessage(framedResponseBase64);
+
+      await responseReady;
+      expect(bufferText, responseText);
+    });
+  }, timeout: new Timeout(new Duration(seconds: 1)));
+
+  group('TAsyncClientSocketTransport', () {
+    FakeSocket socket;
+    FakeProtocolFactory protocolFactory;
+    TTransport transport;
+
+    setUp(() async {
+      socket = new FakeSocket(sync: true);
+      await socket.open();
+
+      protocolFactory = new FakeProtocolFactory();
+      protocolFactory.message = new TMessage('foo', TMessageType.CALL, 123);
+      transport = new TAsyncClientSocketTransport(
+          socket, new TMessageReader(protocolFactory),
+          responseTimeout: Duration.ZERO);
+      await transport.open();
+      transport.writeAll(requestBytes);
+    });
+
+    test('Test response correlates to correct request', () async {
+      String bufferText;
+
+      Future responseReady = transport.flush().then((_) {
+        var buffer = new Uint8List(responseBytes.length);
+        transport.readAll(buffer, 0, responseBytes.length);
+        bufferText = utf8Codec.decode(buffer);
+      });
+
+      // simulate a response
+      protocolFactory.message = new TMessage('foo', TMessageType.REPLY, 123);
+      socket.receiveFakeMessage(responseBase64);
+
+      // simulate a second response
+      var response2Text = 'response 2';
+      var response2Bytes =
+          new Uint8List.fromList(utf8Codec.encode(response2Text));
+      var response2Base64 = CryptoUtils.bytesToBase64(response2Bytes);
+      protocolFactory.message = new TMessage('foo2', TMessageType.REPLY, 124);
+      socket.receiveFakeMessage(response2Base64);
+
+      await responseReady;
+      expect(bufferText, responseText);
+    });
+
+    test('Test response timeout', () async {
+      Future responseReady = transport.flush();
+      expect(responseReady, throwsA(new isInstanceOf<TimeoutException>()));
+    });
+  }, timeout: new Timeout(new Duration(seconds: 1)));
+
+  group('TAsyncClientSocketTransport with TFramedTransport', () {
+    FakeSocket socket;
+    FakeProtocolFactory protocolFactory;
+    TTransport transport;
+
+    setUp(() async {
+      socket = new FakeSocket(sync: true);
+      await socket.open();
+
+      protocolFactory = new FakeProtocolFactory();
+      protocolFactory.message = new TMessage('foo', TMessageType.CALL, 123);
+      var messageReader = new TMessageReader(protocolFactory,
+          byteOffset: TFramedTransport.headerByteCount);
+
+      transport = new TFramedTransport(new TAsyncClientSocketTransport(
+          socket, messageReader,
+          responseTimeout: Duration.ZERO));
+      await transport.open();
+      transport.writeAll(requestBytes);
+    });
+
+    test('Test async client sending data over framed transport', () async {
+      String bufferText;
+
+      Future responseReady = transport.flush().then((_) {
+        var buffer = new Uint8List(responseBytes.length);
+        transport.readAll(buffer, 0, responseBytes.length);
+        bufferText = utf8Codec.decode(buffer);
+      });
+
+      // simulate a response
+      protocolFactory.message = new TMessage('foo', TMessageType.REPLY, 123);
+      socket.receiveFakeMessage(framedResponseBase64);
+
+      await responseReady;
+      expect(bufferText, responseText);
+    });
+  }, timeout: new Timeout(new Duration(seconds: 1)));
+
+  group('TServerTransport', () {
+    test('Test server transport listens to socket', () async {
+      var socket = new FakeSocket();
+      await socket.open();
+      expect(socket.isOpen, isTrue);
+
+      var transport = new TServerSocketTransport(socket);
+      expect(transport.hasReadData, isFalse);
+
+      socket.receiveFakeMessage(requestBase64);
+
+      // allow microtask events to finish
+      await new Future.value();
+
+      expect(transport.hasReadData, isTrue);
+
+      var buffer = new Uint8List(requestBytes.length);
+      transport.readAll(buffer, 0, requestBytes.length);
+
+      var bufferText = utf8Codec.decode(buffer);
+      expect(bufferText, requestText);
+    });
+
+    test('Test server sending data over transport', () async {
+      var socket = new FakeSocket();
+      await socket.open();
+
+      var transport = new TServerSocketTransport(socket);
+
+      transport.writeAll(responseBytes);
+      expect(socket.sendPayload, isNull);
+
+      transport.flush();
+
+      // allow microtask events to finish
+      await new Future.value();
+
+      expect(socket.sendPayload, isNotNull);
+      expect(socket.sendPayload, responseBytes);
+    });
+  }, timeout: new Timeout(new Duration(seconds: 1)));
+}
+
+class FakeSocket extends TSocket {
+  final StreamController<TSocketState> _onStateController;
+  Stream<TSocketState> get onState => _onStateController.stream;
+
+  final StreamController<Object> _onErrorController;
+  Stream<Object> get onError => _onErrorController.stream;
+
+  final StreamController<Uint8List> _onMessageController;
+  Stream<Uint8List> get onMessage => _onMessageController.stream;
+
+  FakeSocket({bool sync: false})
+      : _onStateController = new StreamController.broadcast(sync: sync),
+        _onErrorController = new StreamController.broadcast(sync: sync),
+        _onMessageController = new StreamController.broadcast(sync: sync);
+
+  bool _isOpen;
+
+  bool get isOpen => _isOpen;
+
+  bool get isClosed => !isOpen;
+
+  Future open() async {
+    _isOpen = true;
+    _onStateController.add(TSocketState.OPEN);
+  }
+
+  Future close() async {
+    _isOpen = false;
+    _onStateController.add(TSocketState.CLOSED);
+  }
+
+  Uint8List _sendPayload;
+  Uint8List get sendPayload => _sendPayload;
+
+  void send(Uint8List data) {
+    if (!isOpen) throw new StateError('The socket is not open');
+
+    _sendPayload = data;
+  }
+
+  void receiveFakeMessage(String base64) {
+    if (!isOpen) throw new StateError('The socket is not open');
+
+    var message =
+        new Uint8List.fromList(CryptoUtils.base64StringToBytes(base64));
+    _onMessageController.add(message);
+  }
+}
+
+class FakeProtocolFactory implements TProtocolFactory {
+  FakeProtocolFactory();
+
+  TMessage message;
+
+  getProtocol(TTransport transport) => new FakeProtocol(message);
+}
+
+class FakeProtocol extends Mock implements TProtocol {
+  FakeProtocol(this._message);
+
+  TMessage _message;
+
+  readMessageBegin() => _message;
+}
+
+Uint8List _getFramedResponse(Uint8List responseBytes) {
+  var byteOffset = TFramedTransport.headerByteCount;
+  var response = new Uint8List(byteOffset + responseBytes.length);
+
+  response.buffer.asByteData().setInt32(0, responseBytes.length);
+  response.setAll(byteOffset, responseBytes);
+
+  return response;
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/test/transport/t_transport_test.dart
----------------------------------------------------------------------
diff --git a/lib/dart/test/transport/t_transport_test.dart b/lib/dart/test/transport/t_transport_test.dart
new file mode 100644
index 0000000..0bb381a
--- /dev/null
+++ b/lib/dart/test/transport/t_transport_test.dart
@@ -0,0 +1,41 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+library thrift.test.transport.t_socket_transport_test;
+
+import 'package:test/test.dart';
+import 'package:thrift/thrift.dart';
+
+/// Common transport tests
+void main() {
+  group('TTransportFactory', () {
+    test('transport is returned from base factory', () async {
+      TTransport result;
+      TTransport transport = null;
+
+      var factory = new TTransportFactory();
+
+      result = await factory.getTransport(transport);
+      expect(result, isNull);
+
+      transport = new TBufferedTransport();
+      result = await factory.getTransport(transport);
+
+      expect(result, transport);
+    });
+  });
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/tool/dev.dart
----------------------------------------------------------------------
diff --git a/lib/dart/tool/dev.dart b/lib/dart/tool/dev.dart
new file mode 100644
index 0000000..27f8b8f
--- /dev/null
+++ b/lib/dart/tool/dev.dart
@@ -0,0 +1,33 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+library tool.dev;
+
+import 'package:dart_dev/dart_dev.dart' show dev, config;
+
+main(List<String> args) async {
+  // https://github.com/Workiva/dart_dev
+
+  var directories = ['lib/', 'test/', 'tool/'];
+  config.analyze.entryPoints = directories;
+  config.format.directories = directories;
+  config.copyLicense
+    ..licensePath = 'LICENSE_HEADER'
+    ..directories = directories;
+
+  await dev(args);
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/test/Makefile.am
----------------------------------------------------------------------
diff --git a/test/Makefile.am b/test/Makefile.am
index 6ebcd27..7590921 100755
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -40,6 +40,11 @@ SUBDIRS += php
 PRECROSS_TARGET += precross-php
 endif
 
+if WITH_DART
+SUBDIRS += dart
+PRECROSS_TARGET += precross-dart
+endif
+
 if WITH_PYTHON
 SUBDIRS += py
 PRECROSS_TARGET += precross-py

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/test/dart/Makefile.am
----------------------------------------------------------------------
diff --git a/test/dart/Makefile.am b/test/dart/Makefile.am
new file mode 100644
index 0000000..59b3b7d
--- /dev/null
+++ b/test/dart/Makefile.am
@@ -0,0 +1,41 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+gen-dart/thrift_test/lib/thrift_test.dart: ../ThriftTest.thrift
+	$(THRIFT) --gen dart ../ThriftTest.thrift
+
+pub-get-gen: gen-dart/thrift_test/lib/thrift_test.dart
+	cd gen-dart/thrift_test; ${DARTPUB} get
+
+pub-get: pub-get-gen
+	cd test_client; ${DARTPUB} get
+
+stubs: gen-dart/thrift_test/lib/thrift_test.dart pub-get
+
+precross: stubs
+
+check: stubs
+
+clean-local:
+	$(RM) -r gen-dart test_client/.pub
+
+client: stubs
+	${DART} test_client/bin/main.dart

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/test/dart/test_client/bin/main.dart
----------------------------------------------------------------------
diff --git a/test/dart/test_client/bin/main.dart b/test/dart/test_client/bin/main.dart
new file mode 100644
index 0000000..3733a08
--- /dev/null
+++ b/test/dart/test_client/bin/main.dart
@@ -0,0 +1,288 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// 'License'); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:args/args.dart';
+import 'package:collection/equality.dart';
+import 'package:thrift/thrift.dart';
+import 'package:thrift/thrift_console.dart';
+import 'package:thrift_test/thrift_test.dart';
+
+ThriftTestClient client;
+bool verbose;
+
+/// Adapted from TestClient.php
+main(List<String> args) async {
+  var parser = new ArgParser();
+  parser.addOption('host', defaultsTo: 'localhost', help: 'The server host');
+  parser.addOption('port', defaultsTo: '9090', help: 'The port to connect to');
+  parser.addOption('transport',
+      defaultsTo: 'buffered',
+      allowed: ['buffered', 'framed'],
+      help: 'The transport name',
+      allowedHelp: {
+        'buffered': 'TBufferedTransport',
+        'framed': 'TFramedTransport'
+      });
+  parser.addOption('protocol',
+      defaultsTo: 'binary',
+      allowed: ['binary', 'json'],
+      help: 'The protocol name',
+      allowedHelp: {'binary': 'TBinaryProtocol', 'json': 'TJsonProtocol'});
+  parser.addFlag('verbose', defaultsTo: false);
+
+  ArgResults results;
+  try {
+    results = parser.parse(args);
+  } catch (e) {
+    stdout.writeln('$e\n');
+    results = null;
+  }
+
+  if (results == null) {
+    print(parser.usage);
+    exit(0);
+  }
+
+  verbose = results['verbose'] == true;
+
+  await init(
+      host: results['host'],
+      port: int.parse(results['port']),
+      transportType: results['transport'],
+      protocolType: results['protocol']).then((_) {
+    exit(0);
+  }).catchError((e) {
+    stdout.writeln('Error:');
+    stdout.writeln('$e');
+    if (e is Error) {
+      stdout.writeln('${e.stackTrace}');
+    }
+    exit(1);
+  });
+  exit(0);
+}
+
+TProtocolFactory getProtocolFactory(String protocolType) {
+  if (protocolType == 'binary') {
+    return new TBinaryProtocolFactory();
+  } else if (protocolType == 'json') {
+    return new TJsonProtocolFactory();
+  }
+
+  throw new ArgumentError.value(protocolType);
+}
+
+Future init(
+    {String host, int port, String transportType, String protocolType}) async {
+  TTransport transport;
+  var protocolFactory = getProtocolFactory(protocolType);
+
+  if (transportType == 'http') {
+    var httpClient = new HttpClient();
+    var config = new THttpConfig(Uri.parse('http://localhost'), {});
+    transport = new THttpClientTransport(httpClient, config);
+  } else {
+    var socket = await Socket.connect(host, port);
+    transport = new TClientSocketTransport(new TTcpSocket(socket));
+    if (transportType == 'framed') {
+      transport = new TFramedTransport(transport);
+    }
+  }
+
+  var protocol = protocolFactory.getProtocol(transport);
+  client = new ThriftTestClient(protocol);
+
+  await transport.open();
+
+  await runTests();
+}
+
+Future _test(String name, Function testFunc) async {
+  if (verbose) stdout.write('$name... ');
+  await testFunc();
+  if (verbose) stdout.writeln('success!');
+}
+
+Future runTests() async {
+  var xtruct = new Xtruct()
+    ..string_thing = 'Zero'
+    ..byte_thing = 1
+    ..i32_thing = -3
+    ..i64_thing = -5;
+
+  await _test('testVoid', () async {
+    await client.testVoid();
+  });
+
+  await _test('testString', () async {
+    var input = 'Test';
+    var result = await client.testString(input);
+    if (result != input) throw new StateError('$result != $input');
+  });
+
+  await _test('testBool', () async {
+    var input = true;
+    var result = await client.testBool(input);
+    if (result != input) throw new StateError('$result != $input');
+  });
+
+  await _test('testByte', () async {
+    var input = 64;
+    var result = await client.testByte(input);
+    if (result != input) throw new StateError('$result != $input');
+  });
+
+  await _test('testI32', () async {
+    var input = 2147483647;
+    var result = await client.testI32(input);
+    if (result != input) throw new StateError('$result != $input');
+  });
+
+  await _test('testI64', () async {
+    var input = 9223372036854775807;
+    var result = await client.testI64(input);
+    if (result != input) throw new StateError('$result != $input');
+  });
+
+  await _test('testDouble', () async {
+    var input = 3.1415926;
+    var result = await client.testDouble(input);
+    if (result != input) throw new StateError('$result != $input');
+  });
+
+  await _test('testBinary', () async {
+    var utf8Codec = const Utf8Codec();
+    var input = utf8Codec.encode('foo');
+    var result = await client.testBinary(input);
+    var equality = const ListEquality();
+    if (!equality.equals(
+        result, input)) throw new StateError('$result != $input');
+  });
+
+  await _test('testStruct', () async {
+    var result = await client.testStruct(xtruct);
+    if ('$result' != '$xtruct') throw new StateError('$result != $xtruct');
+  });
+
+  await _test('testNest', () async {
+    var input = new Xtruct2()
+      ..byte_thing = 1
+      ..struct_thing = xtruct
+      ..i32_thing = -3;
+
+    var result = await client.testNest(input);
+    if ('$result' != '$input') throw new StateError('$result != $input');
+  });
+
+  await _test('testMap', () async {
+    Map<int, int> input = {1: -10, 2: -9, 3: -8, 4: -7, 5: -6};
+
+    var result = await client.testMap(input);
+    var equality = const MapEquality();
+    if (!equality.equals(
+        result, input)) throw new StateError('$result != $input');
+  });
+
+  await _test('testSet', () async {
+    var input = new Set.from([-2, -1, 0, 1, 2]);
+    var result = await client.testSet(input);
+    var equality = const SetEquality();
+    if (!equality.equals(
+        result, input)) throw new StateError('$result != $input');
+  });
+
+  await _test('testList', () async {
+    var input = [-2, -1, 0, 1, 2];
+    var result = await client.testList(input);
+    var equality = const ListEquality();
+    if (!equality.equals(
+        result, input)) throw new StateError('$result != $input');
+  });
+
+  await _test('testEnum', () async {
+    await _testEnum(Numberz.ONE);
+    await _testEnum(Numberz.TWO);
+    await _testEnum(Numberz.THREE);
+    await _testEnum(Numberz.FIVE);
+    await _testEnum(Numberz.EIGHT);
+  });
+
+  await _test('testTypedef', () async {
+    var input = 309858235082523;
+    var result = await client.testTypedef(input);
+    if (result != input) throw new StateError('$result != $input');
+  });
+
+  await _test('testMapMap', () async {
+    Map<int, Map<int, int>> result = await client.testMapMap(1);
+    if (result.isEmpty ||
+        result[result.keys.first].isEmpty) throw new StateError(
+        'expected Map<int, Map<int, int>> got $result');
+  });
+
+  await _test('testInsanity', () async {
+    var input = new Insanity();
+    input.userMap = {Numberz.FIVE: 5000};
+    input.xtructs = [xtruct];
+
+    Map<int, Map<int, Insanity>> result = await client.testInsanity(input);
+    if (result.isEmpty ||
+        result[result.keys.first].isEmpty) throw new StateError(
+        'expected Map<int, Map<int, Insanity>> got $result');
+  });
+
+  await _test('testMulti', () async {
+    var input = new Xtruct()
+      ..string_thing = 'Hello2'
+      ..byte_thing = 123
+      ..i32_thing = 456
+      ..i64_thing = 789;
+
+    var result = await client.testMulti(input.byte_thing, input.i32_thing,
+        input.i64_thing, {1: 'one'}, Numberz.EIGHT, 5678);
+    if ('$result' != '$input') throw new StateError('$result != $input');
+  });
+
+  await _test('testException', () async {
+    try {
+      await client.testException('Xception');
+    } on Xception catch (x) {
+      return;
+    }
+
+    throw new StateError('expected an Xception');
+  });
+
+  await _test('testMultiException', () async {
+    try {
+      await client.testMultiException('Xception2', 'foo');
+    } on Xception2 catch (x) {
+      return;
+    }
+
+    throw new StateError('expected an Xception2');
+  });
+}
+
+Future _testEnum(int input) async {
+  var result = await client.testEnum(input);
+  if (result != input) throw new StateError('$result != $input');
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/test/dart/test_client/pubspec.yaml
----------------------------------------------------------------------
diff --git a/test/dart/test_client/pubspec.yaml b/test/dart/test_client/pubspec.yaml
new file mode 100644
index 0000000..54d8b0f
--- /dev/null
+++ b/test/dart/test_client/pubspec.yaml
@@ -0,0 +1,32 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# 'License'); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: thrift_test_client
+version: 1.0.0-dev
+description: A client integration test for the Dart Thrift library
+author: Mark Erickson <ma...@workiva.com>
+homepage: https://github.com/apache/thrift
+environment:
+  sdk: ^1.12.0
+dependencies:
+  args: ^0.13.0
+  thrift:
+    path: ../../../lib/dart
+  thrift_test:
+    path: ../gen-dart/thrift_test
+dev_dependencies:
+  test: "^0.12.0"

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/test/tests.json
----------------------------------------------------------------------
diff --git a/test/tests.json b/test/tests.json
index c902cd4..8c152fc 100644
--- a/test/tests.json
+++ b/test/tests.json
@@ -354,5 +354,27 @@
       ]
     },
     "workdir": "php"
+  },
+  {
+    "name": "dart",
+    "client": {
+      "transports": [
+        "buffered",
+        "framed",
+        "http"
+      ],
+      "sockets": [
+        "ip"
+      ],
+      "protocols": [
+        "binary",
+        "json"
+      ],
+      "command": [
+        "dart",
+        "test_client/bin/main.dart"
+      ]
+    },
+    "workdir": "dart"
   }
 ]

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/Makefile.am
----------------------------------------------------------------------
diff --git a/tutorial/Makefile.am b/tutorial/Makefile.am
index 47711a9..37addda 100755
--- a/tutorial/Makefile.am
+++ b/tutorial/Makefile.am
@@ -62,6 +62,10 @@ if WITH_NODEJS
 SUBDIRS += nodejs
 endif
 
+if WITH_DART
+SUBDIRS += dart
+endif
+
 #
 # generate html for ThriftTest.thrift
 #

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/dart/Makefile.am
----------------------------------------------------------------------
diff --git a/tutorial/dart/Makefile.am b/tutorial/dart/Makefile.am
new file mode 100644
index 0000000..2bb6bf2
--- /dev/null
+++ b/tutorial/dart/Makefile.am
@@ -0,0 +1,56 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+gen-dart/tutorial/lib/tutorial.dart gen-dart/shared/lib/shared.dart: $(top_srcdir)/tutorial/tutorial.thrift
+	$(THRIFT) --gen dart -r $<
+
+all-local: gen-dart/tutorial/lib/tutorial.dart pub-get
+
+clean-local:
+	$(RM) -r gen-*
+
+pub-get: pub-get-gen pub-get-client pub-get-console-client pub-get-server
+
+pub-get-gen: pub-get-tutorial pub-get-shared
+
+pub-get-tutorial: gen-dart/tutorial/lib/tutorial.dart
+	cd gen-dart/tutorial; ${DARTPUB} get
+
+pub-get-shared: gen-dart/shared/lib/shared.dart
+	cd gen-dart/shared; ${DARTPUB} get
+
+pub-get-client:
+	cd client; ${DARTPUB} get
+
+pub-get-console-client:
+	cd console_client; ${DARTPUB} get
+
+pub-get-server:
+	cd client; ${DARTPUB} get
+
+tutorialserver: pub-get-gen pub-get-server
+	${DART} server/bin/main.dart
+
+tutorialclient: pub-get-gen pub-get-client
+	cd client; ${DARTPUB} serve
+
+tutorialconsoleclient: pub-get-console-client
+	${DART} console-client/bin/main.dart

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/dart/build.sh
----------------------------------------------------------------------
diff --git a/tutorial/dart/build.sh b/tutorial/dart/build.sh
new file mode 100644
index 0000000..eabe04a
--- /dev/null
+++ b/tutorial/dart/build.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# 'License'); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+set -e;
+rm -r gen-dart || true;
+
+thrift --gen dart ../shared.thrift;
+cd gen-dart/shared;
+pub get;
+cd ../..;
+
+thrift --gen dart ../tutorial.thrift;
+cd gen-dart/tutorial;
+pub get;
+cd ../..;
+
+cd client;
+pub get;
+cd ..;
+
+cd console_client;
+pub get;
+cd ..;
+
+cd server;
+pub get;
+cd ..;
+
+dartfmt -w gen-dart;
+
+echo "\nEnjoy the Dart tutorial!";
+echo "\nTo run the server:";
+echo "> dart server/bin/main.dart";
+echo "\nTo run the client:";
+echo "# Serve the app from the client directory and view in a browser";
+echo "> cd client;";
+echo "> pub serve;";
+echo "\nTo run the console client:";
+echo "> dart console_client/bin/main.dart";
+echo "";

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/dart/client/pubspec.yaml
----------------------------------------------------------------------
diff --git a/tutorial/dart/client/pubspec.yaml b/tutorial/dart/client/pubspec.yaml
new file mode 100644
index 0000000..97c625b
--- /dev/null
+++ b/tutorial/dart/client/pubspec.yaml
@@ -0,0 +1,34 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# 'License'); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: tutorial_client
+version: 1.0.0-dev
+description: A Dart client implementation of the Apache Thrift tutorial
+author: Mark Erickson <ma...@workiva.com>
+homepage: https://github.com/apache/thrift
+
+environment:
+  sdk: ^1.12.0
+
+dependencies:
+  browser: ^0.10.0
+  shared:
+    path: ../gen-dart/shared
+  thrift:
+    path: ../../../lib/dart
+  tutorial:
+    path: ../gen-dart/tutorial

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/dart/client/web/client.dart
----------------------------------------------------------------------
diff --git a/tutorial/dart/client/web/client.dart b/tutorial/dart/client/web/client.dart
new file mode 100644
index 0000000..4f02d0d
--- /dev/null
+++ b/tutorial/dart/client/web/client.dart
@@ -0,0 +1,278 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+import 'dart:html';
+
+import 'package:thrift/thrift.dart';
+import 'package:thrift/thrift_browser.dart';
+import 'package:shared/shared.dart';
+import 'package:tutorial/tutorial.dart';
+
+/// Adapted from the AS3 tutorial
+void main() {
+  new CalculatorUI(querySelector('#output')).start();
+}
+
+class CalculatorUI {
+  final DivElement output;
+
+  CalculatorUI(this.output);
+
+  TTransport _transport;
+  Calculator _calculatorClient;
+
+  void start() {
+    _buildInterface();
+    _initConnection();
+  }
+
+  void _validate() {
+    if (!_transport.isOpen) {
+      window.alert("The transport is not open!");
+    }
+  }
+
+  void _initConnection() {
+    _transport = new TAsyncClientSocketTransport(
+        new TWebSocket(Uri.parse('ws://127.0.0.1:9090/ws')),
+        new TMessageReader(new TBinaryProtocolFactory()));
+    TProtocol protocol = new TBinaryProtocol(_transport);
+    _transport.open();
+
+    _calculatorClient = new CalculatorClient(protocol);
+  }
+
+  void _buildInterface() {
+    output.children.forEach((e) {
+      e.remove();
+    });
+
+    _buildPingComponent();
+
+    _buildAddComponent();
+
+    _buildCalculatorComponent();
+
+    _buildGetStructComponent();
+  }
+
+  void _buildPingComponent() {
+    output.append(new HeadingElement.h3()..text = "Ping");
+    ButtonElement pingButton = new ButtonElement()
+      ..text = "PING"
+      ..onClick.listen(_onPingClick);
+    output.append(pingButton);
+  }
+
+  void _onPingClick(MouseEvent e) {
+    _validate();
+
+    _calculatorClient.ping();
+  }
+
+  void _buildAddComponent() {
+    output.append(new HeadingElement.h3()..text = "Add");
+    InputElement num1 = new InputElement()
+      ..id = "add1"
+      ..type = "number"
+      ..style.fontSize = "14px"
+      ..style.width = "50px";
+    output.append(num1);
+    SpanElement op = new SpanElement()
+      ..text = "+"
+      ..style.fontSize = "14px"
+      ..style.marginLeft = "10px";
+    output.append(op);
+    InputElement num2 = new InputElement()
+      ..id = "add2"
+      ..type = "number"
+      ..style.fontSize = "14px"
+      ..style.width = "50px"
+      ..style.marginLeft = "10px";
+    output.append(num2);
+    ButtonElement addButton = new ButtonElement()
+      ..text = "="
+      ..style.fontSize = "14px"
+      ..style.marginLeft = "10px"
+      ..onClick.listen(_onAddClick);
+    output.append(addButton);
+    SpanElement result = new SpanElement()
+      ..id = "addResult"
+      ..style.fontSize = "14px"
+      ..style.marginLeft = "10px";
+    output.append(result);
+  }
+
+  void _onAddClick(MouseEvent e) {
+    _validate();
+
+    InputElement num1 = querySelector("#add1");
+    InputElement num2 = querySelector("#add2");
+    SpanElement result = querySelector("#addResult");
+
+    _calculatorClient
+        .add(int.parse(num1.value), int.parse(num2.value))
+        .then((int n) {
+      result.text = "$n";
+    });
+  }
+
+  void _buildCalculatorComponent() {
+    output.append(new HeadingElement.h3()..text = "Calculator");
+    InputElement num1 = new InputElement()
+      ..id = "calc1"
+      ..type = "number"
+      ..style.fontSize = "14px"
+      ..style.width = "50px";
+    output.append(num1);
+    SelectElement op = new SelectElement()
+      ..id = "calcOp"
+      ..multiple = false
+      ..selectedIndex = 0
+      ..style.fontSize = "16px"
+      ..style.marginLeft = "10px"
+      ..style.width = "50px";
+    OptionElement addOp = new OptionElement()
+      ..text = "+"
+      ..value = Operation.ADD.toString();
+    op.add(addOp, 0);
+    OptionElement subtractOp = new OptionElement()
+      ..text = "-"
+      ..value = Operation.SUBTRACT.toString();
+    op.add(subtractOp, 1);
+    OptionElement multiplyOp = new OptionElement()
+      ..text = "*"
+      ..value = Operation.MULTIPLY.toString();
+    op.add(multiplyOp, 2);
+    OptionElement divideOp = new OptionElement()
+      ..text = "/"
+      ..value = Operation.DIVIDE.toString();
+    op.add(divideOp, 3);
+    output.append(op);
+    InputElement num2 = new InputElement()
+      ..id = "calc2"
+      ..type = "number"
+      ..style.fontSize = "14px"
+      ..style.width = "50px"
+      ..style.marginLeft = "10px";
+    output.append(num2);
+    ButtonElement calcButton = new ButtonElement()
+      ..text = "="
+      ..style.fontSize = "14px"
+      ..style.marginLeft = "10px"
+      ..onClick.listen(_onCalcClick);
+    output.append(calcButton);
+    SpanElement result = new SpanElement()
+      ..id = "calcResult"
+      ..style.fontSize = "14px"
+      ..style.marginLeft = "10px";
+    output.append(result);
+    output.append(new BRElement());
+    output.append(new BRElement());
+    LabelElement logIdLabel = new LabelElement()
+      ..text = "Log ID:"
+      ..style.fontSize = "14px";
+    output.append(logIdLabel);
+    InputElement logId = new InputElement()
+      ..id = "logId"
+      ..type = "number"
+      ..value = "1"
+      ..style.fontSize = "14px"
+      ..style.width = "50px"
+      ..style.marginLeft = "10px";
+    output.append(logId);
+    LabelElement commentLabel = new LabelElement()
+      ..text = "Comment:"
+      ..style.fontSize = "14px"
+      ..style.marginLeft = "10px";
+    output.append(commentLabel);
+    InputElement comment = new InputElement()
+      ..id = "comment"
+      ..style.fontSize = "14px"
+      ..style.width = "100px"
+      ..style.marginLeft = "10px";
+    output.append(comment);
+  }
+
+  void _onCalcClick(MouseEvent e) {
+    _validate();
+
+    InputElement num1 = querySelector("#calc1");
+    InputElement num2 = querySelector("#calc2");
+    SelectElement op = querySelector("#calcOp");
+    SpanElement result = querySelector("#calcResult");
+    InputElement logId = querySelector("#logId");
+    InputElement comment = querySelector("#comment");
+
+    int logIdValue = int.parse(logId.value);
+    logId.value = (logIdValue + 1).toString();
+
+    Work work = new Work();
+    work.num1 = int.parse(num1.value);
+    work.num2 = int.parse(num2.value);
+    work.op = int.parse(op.options[op.selectedIndex].value);
+    work.comment = comment.value;
+
+    _calculatorClient.calculate(logIdValue, work).then((int n) {
+      result.text = "$n";
+    });
+  }
+
+  void _buildGetStructComponent() {
+    output.append(new HeadingElement.h3()..text = "Get Struct");
+    LabelElement logIdLabel = new LabelElement()
+      ..text = "Struct Key:"
+      ..style.fontSize = "14px";
+    output.append(logIdLabel);
+    InputElement logId = new InputElement()
+      ..id = "structKey"
+      ..type = "number"
+      ..value = "1"
+      ..style.fontSize = "14px"
+      ..style.width = "50px"
+      ..style.marginLeft = "10px";
+    output.append(logId);
+    ButtonElement getStructButton = new ButtonElement()
+      ..text = "GET"
+      ..style.fontSize = "14px"
+      ..style.marginLeft = "10px"
+      ..onClick.listen(_onGetStructClick);
+    output.append(getStructButton);
+    output.append(new BRElement());
+    output.append(new BRElement());
+    TextAreaElement result = new TextAreaElement()
+      ..id = "getStructResult"
+      ..style.fontSize = "14px"
+      ..style.width = "300px"
+      ..style.height = "50px"
+      ..style.marginLeft = "10px";
+    output.append(result);
+  }
+
+  void _onGetStructClick(MouseEvent e) {
+    _validate();
+
+    InputElement structKey = querySelector("#structKey");
+    TextAreaElement result = querySelector("#getStructResult");
+
+    _calculatorClient
+        .getStruct(int.parse(structKey.value))
+        .then((SharedStruct s) {
+      result.text = "${s.toString()}";
+    });
+  }
+}

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/dart/client/web/index.html
----------------------------------------------------------------------
diff --git a/tutorial/dart/client/web/index.html b/tutorial/dart/client/web/index.html
new file mode 100644
index 0000000..64b184e
--- /dev/null
+++ b/tutorial/dart/client/web/index.html
@@ -0,0 +1,36 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Thrift Tutorial</title>
+    <link rel="stylesheet" href="styles.css">
+    <script async src="client.dart" type="application/dart"></script>
+    <script async src="packages/browser/dart.js"></script>
+</head>
+
+<body>
+
+  <div id="output"></div>
+
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/dart/client/web/styles.css
----------------------------------------------------------------------
diff --git a/tutorial/dart/client/web/styles.css b/tutorial/dart/client/web/styles.css
new file mode 100644
index 0000000..c031502
--- /dev/null
+++ b/tutorial/dart/client/web/styles.css
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+@import url(https://fonts.googleapis.com/css?family=Roboto);
+
+html, body {
+    width: 100%;
+    height: 100%;
+    margin: 0;
+    padding: 10px;
+    font-family: 'Roboto', sans-serif;
+}
+
+h3 {
+    border-bottom: solid;
+    border-width: thin;
+    padding-top: 20px;
+}


[4/5] thrift git commit: THRIFT-3299 Create an Apache Thrift language binding for Dart (dartlang.org). Client: Dart Patch: Mark Erickson

Posted by je...@apache.org.
THRIFT-3299 Create an Apache Thrift language binding for Dart (dartlang.org).
Client: Dart
Patch: Mark Erickson <ma...@workiva.com>

This closes #608


Project: http://git-wip-us.apache.org/repos/asf/thrift/repo
Commit: http://git-wip-us.apache.org/repos/asf/thrift/commit/932c4700
Tree: http://git-wip-us.apache.org/repos/asf/thrift/tree/932c4700
Diff: http://git-wip-us.apache.org/repos/asf/thrift/diff/932c4700

Branch: refs/heads/master
Commit: 932c4700c6e3aee20b74d5e2f562326029e6b9c8
Parents: 123258b
Author: Mark Erickson <ma...@workiva.com>
Authored: Sat Aug 29 10:46:51 2015 -0500
Committer: Jens Geyer <je...@apache.org>
Committed: Sat Oct 3 01:43:59 2015 +0200

----------------------------------------------------------------------
 .gitignore                                      |   12 +
 compiler/cpp/CMakeLists.txt                     |    3 +-
 compiler/cpp/Makefile.am                        |    1 +
 compiler/cpp/src/generate/t_dart_generator.cc   | 2361 ++++++++++++++++++
 configure.ac                                    |   23 +
 lib/Makefile.am                                 |    5 +
 lib/dart/LICENSE_HEADER                         |   16 +
 lib/dart/Makefile.am                            |   26 +
 lib/dart/README.md                              |   26 +
 lib/dart/coding_standards.md                    |    6 +
 lib/dart/lib/src/browser/t_web_socket.dart      |  130 +
 lib/dart/lib/src/console/t_tcp_socket.dart      |   81 +
 lib/dart/lib/src/console/t_web_socket.dart      |   89 +
 .../lib/src/protocol/t_binary_protocol.dart     |  276 ++
 lib/dart/lib/src/protocol/t_field.dart          |   26 +
 lib/dart/lib/src/protocol/t_json_protocol.dart  |  754 ++++++
 lib/dart/lib/src/protocol/t_list.dart           |   25 +
 lib/dart/lib/src/protocol/t_map.dart            |   26 +
 lib/dart/lib/src/protocol/t_message.dart        |   35 +
 .../src/protocol/t_multiplexed_protocol.dart    |   43 +
 lib/dart/lib/src/protocol/t_protocol.dart       |   95 +
 .../lib/src/protocol/t_protocol_decorator.dart  |  150 ++
 lib/dart/lib/src/protocol/t_protocol_error.dart |   33 +
 .../lib/src/protocol/t_protocol_factory.dart    |   22 +
 lib/dart/lib/src/protocol/t_protocol_util.dart  |  107 +
 lib/dart/lib/src/protocol/t_set.dart            |   25 +
 lib/dart/lib/src/protocol/t_struct.dart         |   24 +
 lib/dart/lib/src/protocol/t_type.dart           |   34 +
 lib/dart/lib/src/t_application_error.dart       |  104 +
 lib/dart/lib/src/t_base.dart                    |   37 +
 lib/dart/lib/src/t_error.dart                   |   27 +
 lib/dart/lib/src/t_processor.dart               |   24 +
 .../lib/src/transport/t_buffered_transport.dart |   98 +
 .../lib/src/transport/t_framed_transport.dart   |   83 +
 .../lib/src/transport/t_http_transport.dart     |  100 +
 .../lib/src/transport/t_message_reader.dart     |   99 +
 lib/dart/lib/src/transport/t_socket.dart        |   38 +
 .../lib/src/transport/t_socket_transport.dart   |  175 ++
 lib/dart/lib/src/transport/t_transport.dart     |   70 +
 .../lib/src/transport/t_transport_error.dart    |   31 +
 .../lib/src/transport/t_transport_factory.dart  |   27 +
 lib/dart/lib/thrift.dart                        |   58 +
 lib/dart/lib/thrift_browser.dart                |   22 +
 lib/dart/lib/thrift_console.dart                |   23 +
 lib/dart/pubspec.yaml                           |   36 +
 lib/dart/test/protocol/t_protocol_test.dart     |  374 +++
 lib/dart/test/t_application_error_test.dart     |   46 +
 .../test/transport/t_http_transport_test.dart   |  165 ++
 .../test/transport/t_socket_transport_test.dart |  312 +++
 lib/dart/test/transport/t_transport_test.dart   |   41 +
 lib/dart/tool/dev.dart                          |   33 +
 test/Makefile.am                                |    5 +
 test/dart/Makefile.am                           |   41 +
 test/dart/test_client/bin/main.dart             |  288 +++
 test/dart/test_client/pubspec.yaml              |   32 +
 test/tests.json                                 |   22 +
 tutorial/Makefile.am                            |    4 +
 tutorial/dart/Makefile.am                       |   56 +
 tutorial/dart/build.sh                          |   56 +
 tutorial/dart/client/pubspec.yaml               |   34 +
 tutorial/dart/client/web/client.dart            |  278 +++
 tutorial/dart/client/web/index.html             |   36 +
 tutorial/dart/client/web/styles.css             |   33 +
 tutorial/dart/console_client/bin/main.dart      |  149 ++
 tutorial/dart/console_client/pubspec.yaml       |   34 +
 tutorial/dart/server/bin/main.dart              |  163 ++
 tutorial/dart/server/pubspec.yaml               |   32 +
 tutorial/shared.thrift                          |    1 +
 tutorial/tutorial.thrift                        |    1 +
 69 files changed, 7741 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index e0c7a26..44e630a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -145,6 +145,10 @@ test-driver
 /lib/d/test/serialization_benchmark
 /lib/d/test/transport_test
 /lib/d/unittest/
+/lib/dart/**/.packages
+/lib/dart/**/packages
+/lib/dart/**/.pub/
+/lib/dart/**/pubspec.lock
 /lib/delphi/src/*.dcu
 /lib/delphi/test/*.identcache
 /lib/delphi/test/*.local
@@ -235,6 +239,10 @@ test-driver
 /test/cpp/StressTestNonBlocking
 /test/cpp/TestClient
 /test/cpp/TestServer
+/test/dart/**/.packages
+/test/dart/**/packages
+/test/dart/**/.pub/
+/test/dart/**/pubspec.lock
 /test/log/
 /test/test.log
 /test/erl/.eunit/
@@ -260,6 +268,10 @@ test-driver
 /tutorial/csharp/CsharpServer/bin
 /tutorial/csharp/CsharpClient/obj
 /tutorial/csharp/CsharpClient/bin
+/tutorial/dart/**/.packages
+/tutorial/dart/**/packages
+/tutorial/dart/**/.pub/
+/tutorial/dart/**/pubspec.lock
 /tutorial/delphi/*.dsk
 /tutorial/delphi/*.local
 /tutorial/delphi/*.tvsconfig

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/compiler/cpp/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/compiler/cpp/CMakeLists.txt b/compiler/cpp/CMakeLists.txt
index bc6591c..2d0c3f0 100644
--- a/compiler/cpp/CMakeLists.txt
+++ b/compiler/cpp/CMakeLists.txt
@@ -47,7 +47,7 @@ set(libparse_SOURCES
 add_library(libparse STATIC ${libparse_SOURCES})
 
 # Create the thrift compiler
-set( thrift_SOURCES 
+set( thrift_SOURCES
     src/main.cc
     src/md5.c
     src/generate/t_generator.cc
@@ -100,6 +100,7 @@ THRIFT_ADD_COMPILER(c_glib  "Enable compiler for C with Glib" ON)
 THRIFT_ADD_COMPILER(cpp     "Enable compiler for C++" ON)
 THRIFT_ADD_COMPILER(java    "Enable compiler for Java"   ON)
 THRIFT_ADD_COMPILER(as3     "Enable compiler for ActionScript 3" ON)
+THRIFT_ADD_COMPILER(dart    "Enable compiler for Dart" ON)
 THRIFT_ADD_COMPILER(haxe    "Enable compiler for Haxe" ON)
 THRIFT_ADD_COMPILER(csharp  "Enable compiler for C#" ON)
 THRIFT_ADD_COMPILER(py      "Enable compiler for Python 2.0" ON)

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/compiler/cpp/Makefile.am
----------------------------------------------------------------------
diff --git a/compiler/cpp/Makefile.am b/compiler/cpp/Makefile.am
index 38f9f9d..2aa6913 100644
--- a/compiler/cpp/Makefile.am
+++ b/compiler/cpp/Makefile.am
@@ -74,6 +74,7 @@ thrift_SOURCES += src/generate/t_c_glib_generator.cc \
                   src/generate/t_java_generator.cc \
                   src/generate/t_json_generator.cc \
                   src/generate/t_as3_generator.cc \
+                  src/generate/t_dart_generator.cc \
                   src/generate/t_haxe_generator.cc \
                   src/generate/t_csharp_generator.cc \
                   src/generate/t_py_generator.cc \

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/compiler/cpp/src/generate/t_dart_generator.cc
----------------------------------------------------------------------
diff --git a/compiler/cpp/src/generate/t_dart_generator.cc b/compiler/cpp/src/generate/t_dart_generator.cc
new file mode 100644
index 0000000..147ca13
--- /dev/null
+++ b/compiler/cpp/src/generate/t_dart_generator.cc
@@ -0,0 +1,2361 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <sstream>
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <cctype>
+
+#include <sys/stat.h>
+#include <stdexcept>
+
+#include "platform.h"
+#include "t_oop_generator.h"
+
+using std::map;
+using std::ofstream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+static const string endl2 = "\n\n";
+
+/**
+ * Use the current Thrift version for static libraries.  When releasing, update
+ * the version in these files.
+ * - lib/dart/pubspec.yaml
+ * - test/dart/test_client/pubspec.yaml
+ * - tutorial/dart/client/pubspec.yaml
+ * - tutorial/dart/console_client/pubspec.yaml
+ * - tutorial/dart/server/pubspec.yaml
+ * See https://thrift.apache.org/docs/committers/HowToVersion
+ */
+static const string dart_thrift_version = THRIFT_VERSION;
+
+/* forward declarations */
+string initial_caps_to_underscores(string name);
+
+/**
+ * Dart code generator
+ *
+ */
+class t_dart_generator : public t_oop_generator {
+public:
+  t_dart_generator(t_program* program,
+                  const std::map<std::string, std::string>& parsed_options,
+                  const std::string& option_string)
+    : t_oop_generator(program) {
+    (void)option_string;
+    std::map<std::string, std::string>::const_iterator iter;
+
+    iter = parsed_options.find("library_name");
+    if (iter != parsed_options.end()) {
+      library_name_ = (iter->second);
+    } else {
+      library_name_ = "";
+    }
+
+    out_dir_base_ = "gen-dart";
+  }
+
+  void scope_up(std::ostream& out, std::string prefix=" ") {
+    out << prefix << "{" << endl;
+    indent_up();
+  }
+
+  void scope_down(std::ostream& out, std::string postfix=endl) {
+    indent_down();
+    indent(out) << "}" << postfix;
+  }
+
+  string replace_all(string contents, string search, string repl) {
+    string str(contents);
+
+    size_t slen = search.length();
+    size_t rlen = repl.length();
+    size_t incr = (rlen > 0) ? rlen : 1;
+
+    if (slen > 0) {
+      size_t found = str.find(search);
+      while ((found != string::npos) && (found < str.length())) {
+        str.replace(found, slen, repl);
+        found = str.find(search, found + incr);
+      }
+    }
+
+    return str;
+  }
+
+
+  /**
+   * Init and close methods
+   */
+
+  void init_generator();
+  void close_generator();
+
+  void export_class_to_library(string file_name, string class_name);
+
+  void generate_dart_library();
+  void generate_dart_pubspec();
+
+  void generate_consts(std::vector<t_const*> consts);
+
+  /**
+   * Program-level generation functions
+   */
+
+  void generate_typedef(t_typedef* ttypedef);
+  void generate_enum(t_enum* tenum);
+  void generate_struct(t_struct* tstruct);
+  void generate_xception(t_struct* txception);
+  void generate_service(t_service* tservice);
+
+  void print_const_value(std::ofstream& out,
+                         std::string name,
+                         t_type* type,
+                         t_const_value* value,
+                         bool in_static,
+                         bool defval = false);
+  std::string render_const_value(ofstream& out,
+                                 std::string name,
+                                 t_type* type,
+                                 t_const_value* value);
+
+  /**
+   * Service-level generation functions
+   */
+
+  void generate_dart_struct(t_struct* tstruct, bool is_exception);
+
+  void generate_dart_struct_definition(std::ofstream& out,
+                                       t_struct* tstruct,
+                                       bool is_xception = false,
+                                       bool is_result = false,
+                                       string export_file_name = "");
+  void generate_dart_struct_reader(std::ofstream& out, t_struct* tstruct);
+  void generate_dart_validator(std::ofstream& out, t_struct* tstruct);
+  void generate_dart_struct_result_writer(std::ofstream& out, t_struct* tstruct);
+  void generate_dart_struct_writer(std::ofstream& out, t_struct* tstruct);
+  void generate_dart_struct_tostring(std::ofstream& out, t_struct* tstruct);
+  std::string get_dart_type_string(t_type* type);
+  void generate_generic_field_getters(std::ofstream& out, t_struct* tstruct);
+  void generate_generic_field_setters(std::ofstream& out, t_struct* tstruct);
+  void generate_generic_isset_method(std::ofstream& out, t_struct* tstruct);
+  void generate_dart_bean_boilerplate(std::ofstream& out, t_struct* tstruct);
+
+  void generate_function_helpers(t_function* tfunction);
+  std::string get_cap_name(std::string name);
+  std::string get_field_name(std::string name);
+  std::string get_args_class_name(std::string name);
+  std::string get_result_class_name(std::string name);
+  std::string get_file_name(std::string name);
+  std::string generate_isset_check(t_field* field);
+  std::string generate_isset_check(std::string field);
+  void generate_isset_set(ofstream& out, t_field* field);
+
+  void generate_service_interface(t_service* tservice);
+  void generate_service_helpers(t_service* tservice);
+  void generate_service_client(t_service* tservice);
+  void generate_service_server(t_service* tservice);
+  void generate_process_function(t_service* tservice, t_function* tfunction);
+
+  /**
+   * Serialization constructs
+   */
+
+  void generate_deserialize_field(std::ofstream& out, t_field* tfield, std::string prefix = "");
+
+  void generate_deserialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = "");
+
+  void generate_deserialize_container(std::ofstream& out, t_type* ttype, std::string prefix = "");
+
+  void generate_deserialize_set_element(std::ofstream& out, t_set* tset, std::string prefix = "");
+
+  void generate_deserialize_map_element(std::ofstream& out, t_map* tmap, std::string prefix = "");
+
+  void generate_deserialize_list_element(std::ofstream& out,
+                                         t_list* tlist,
+                                         std::string prefix = "");
+
+  void generate_serialize_field(std::ofstream& out, t_field* tfield, std::string prefix = "");
+
+  void generate_serialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = "");
+
+  void generate_serialize_container(std::ofstream& out, t_type* ttype, std::string prefix = "");
+
+  void generate_serialize_map_element(std::ofstream& out,
+                                      t_map* tmap,
+                                      std::string iter,
+                                      std::string map);
+
+  void generate_serialize_set_element(std::ofstream& out, t_set* tmap, std::string iter);
+
+  void generate_serialize_list_element(std::ofstream& out, t_list* tlist, std::string iter);
+
+  void generate_dart_doc(std::ofstream& out, t_doc* tdoc);
+
+  void generate_dart_doc(std::ofstream& out, t_function* tdoc);
+
+  /**
+   * Helper rendering functions
+   */
+
+  std::string dart_library(string file_name);
+  std::string service_imports();
+  std::string dart_thrift_imports();
+  std::string type_name(t_type* ttype);
+  std::string base_type_name(t_base_type* tbase);
+  std::string declare_field(t_field* tfield, bool init = false);
+  std::string function_signature(t_function* tfunction);
+  std::string argument_list(t_struct* tstruct);
+  std::string type_to_enum(t_type* ttype);
+  std::string get_enum_class_name(t_type* type);
+
+  bool type_can_be_null(t_type* ttype) {
+    ttype = get_true_type(ttype);
+
+    return ttype->is_container() || ttype->is_struct() || ttype->is_xception()
+           || ttype->is_string();
+  }
+
+  std::string constant_name(std::string name);
+
+private:
+  std::ofstream f_service_;
+
+  std::string library_name_;
+
+  std::string base_dir_;
+  std::string src_dir_;
+  std::string library_exports_;
+};
+
+/**
+ * Prepares for file generation by opening up the necessary file output
+ * streams.
+ *
+ * @param tprogram The program to generate
+ */
+void t_dart_generator::init_generator() {
+  MKDIR(get_out_dir().c_str());
+
+  if (library_name_.empty()) {
+    library_name_ = program_->get_namespace("dart");
+  }
+  if (library_name_.empty()) {
+    library_name_ = program_->get_name();
+  }
+  library_name_ = replace_all(library_name_, ".", "_");
+
+  string subdir = get_out_dir() + "/" + library_name_;
+  MKDIR(subdir.c_str());
+
+  base_dir_ = subdir;
+  subdir += "/lib";
+  MKDIR(subdir.c_str());
+
+  subdir += "/src";
+  MKDIR(subdir.c_str());
+  src_dir_ = subdir;
+}
+
+/**
+ * The Dart library
+ *
+ * @return String of the library, e.g. "library myservice;"
+ */
+string t_dart_generator::dart_library(string file_name) {
+  string out = "library ";
+  if (!library_name_.empty()) {
+    out += library_name_;
+  }
+  if (!file_name.empty()) {
+    out += ".src." + file_name;
+  }
+  return out + ";\n";
+}
+
+/**
+ * Prints imports for services
+ *
+ * @return List of imports for services
+ */
+string t_dart_generator::service_imports() {
+  return "import 'dart:async';" + endl;
+}
+
+/**
+ * Prints standard dart imports
+ *
+ * @return List of imports necessary for thrift
+ */
+string t_dart_generator::dart_thrift_imports() {
+  string imports = "import 'dart:typed_data' show Uint8List;" + endl +
+    "import 'package:thrift/thrift.dart';" + endl +
+    "import 'package:" + library_name_ + "/" + library_name_ + ".dart';" + endl;
+
+  // add imports for included thrift files
+  const vector<t_program*>& includes = program_->get_includes();
+  for (size_t i = 0; i < includes.size(); ++i) {
+    string include_name = includes[i]->get_namespace("dart");
+    imports += "import 'package:" + include_name + "/" + include_name + ".dart';" + endl;
+  }
+
+  return imports;
+}
+
+/**
+ * Not used
+ */
+void t_dart_generator::close_generator() {
+  generate_dart_library();
+  generate_dart_pubspec();
+}
+
+void t_dart_generator::generate_dart_library() {
+  string f_library_name = base_dir_ + "/lib/" + library_name_ + ".dart";
+  ofstream f_library;
+  f_library.open(f_library_name.c_str());
+
+  f_library << autogen_comment() << endl;
+  f_library << "library " << library_name_ << ";" << endl2;
+  f_library << library_exports_;
+
+  f_library.close();
+}
+
+void t_dart_generator::export_class_to_library(string file_name, string class_name) {
+  library_exports_ += "export 'src/" + file_name + ".dart' show " + class_name + ";" + endl;
+}
+
+void t_dart_generator::generate_dart_pubspec() {
+  string f_pubspec_name = base_dir_ + "/pubspec.yaml";
+  ofstream f_pubspec;
+  f_pubspec.open(f_pubspec_name.c_str());
+
+  indent(f_pubspec) << "name: " << library_name_ << endl;
+  indent(f_pubspec) << "version: 0.0.1" << endl;
+  indent(f_pubspec) << "description: Autogenerated by Thrift Compiler" << endl;
+  f_pubspec << endl;
+
+  indent(f_pubspec) << "environment:" << endl;
+  indent_up();
+  indent(f_pubspec) << "sdk: ^1.12.0" << endl;
+  indent_down();
+  f_pubspec << endl;
+
+  indent(f_pubspec) << "dependencies:" << endl;
+  indent_up();
+  indent(f_pubspec) << "thrift:  # ^" << dart_thrift_version << endl;
+  indent_up();
+  indent(f_pubspec) << "path: ../../../../lib/dart" << endl;
+  indent_down();
+
+  // add included thrift files as dependencies
+  const vector<t_program*>& includes = program_->get_includes();
+  for (size_t i = 0; i < includes.size(); ++i) {
+    string include_name = includes[i]->get_namespace("dart");
+    indent(f_pubspec) << include_name << ":" << endl;
+    indent_up();
+    indent(f_pubspec) << "path: ../" << include_name << endl;
+    indent_down();
+  }
+
+  indent_down();
+  f_pubspec << endl;
+
+  f_pubspec.close();
+}
+
+/**
+ * Not used
+ *
+ * @param ttypedef The type definition
+ */
+void t_dart_generator::generate_typedef(t_typedef* ttypedef) {
+  (void)ttypedef;
+}
+
+/**
+ * Enums are a class with a set of static constants.
+ *
+ * @param tenum The enumeration
+ */
+void t_dart_generator::generate_enum(t_enum* tenum) {
+  // Make output file
+  string file_name = get_file_name(tenum->get_name());
+
+  string f_enum_name = src_dir_ + "/" + file_name + ".dart";
+  ofstream f_enum;
+  f_enum.open(f_enum_name.c_str());
+
+  // Comment and add library
+  f_enum << autogen_comment() << dart_library(file_name) << endl;
+
+  string class_name = tenum->get_name();
+  export_class_to_library(file_name, class_name);
+  f_enum << "class " << class_name;
+  scope_up(f_enum);
+
+  vector<t_enum_value*> constants = tenum->get_constants();
+  vector<t_enum_value*>::iterator c_iter;
+  for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+    int value = (*c_iter)->get_value();
+    indent(f_enum) << "static const int " << (*c_iter)->get_name() << " = " << value << ";"
+                   << endl;
+  }
+
+  // Create a static Set with all valid values for this enum
+  f_enum << endl;
+
+  indent(f_enum) << "static final Set<int> VALID_VALUES = new Set.from([" << endl;
+  indent_up();
+  bool firstValue = true;
+  for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+    // populate set
+    indent(f_enum) << (firstValue ? "" : ", ");
+    f_enum << (*c_iter)->get_name() << endl;
+    firstValue = false;
+  }
+  indent_down();
+  indent(f_enum) << "]);" << endl;
+
+  indent(f_enum) << "static final Map<int, String> VALUES_TO_NAMES = {" << endl;
+  indent_up();
+  firstValue = true;
+  for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+    indent(f_enum) << (firstValue ? "" : ", ");
+    f_enum  << (*c_iter)->get_name() << ": '" << (*c_iter)->get_name() << "'" << endl;
+    firstValue = false;
+  }
+  indent_down();
+  indent(f_enum) << "};" << endl;
+
+  scope_down(f_enum); // end class
+
+  f_enum.close();
+}
+
+/**
+ * Generates a class that holds all the constants.
+ */
+void t_dart_generator::generate_consts(std::vector<t_const*> consts) {
+  if (consts.empty()) {
+    return;
+  }
+
+  string class_name = get_cap_name(program_name_) + "Constants";
+  string file_name = get_file_name(class_name);
+
+  string f_consts_name = src_dir_ + "/" + file_name + ".dart";
+  ofstream f_consts;
+  f_consts.open(f_consts_name.c_str());
+
+  // Print header
+  f_consts << autogen_comment() << dart_library(file_name) << endl;
+
+  export_class_to_library(file_name, class_name);
+  indent(f_consts) << "class " << class_name;
+  scope_up(f_consts);
+
+  vector<t_const*>::iterator c_iter;
+  for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+    print_const_value(f_consts,
+                      (*c_iter)->get_name(),
+                      (*c_iter)->get_type(),
+                      (*c_iter)->get_value(),
+                      false);
+    f_consts << endl;
+  }
+
+  scope_down(f_consts);
+
+  f_consts.close();
+}
+
+void t_dart_generator::print_const_value(std::ofstream& out,
+                                        string name,
+                                        t_type* type,
+                                        t_const_value* value,
+                                        bool in_static,
+                                        bool defval) {
+  type = get_true_type(type);
+
+  indent(out);
+  if (!defval) {
+    out << (in_static ? "var " : "static final ");
+  }
+  if (type->is_base_type()) {
+    if (!defval) {
+      out << type_name(type) << " ";
+    }
+    string v2 = render_const_value(out, name, type, value);
+    out << name;
+    out << " = " << v2 << ";" << endl << endl;
+  } else if (type->is_enum()) {
+    if (!defval) {
+      out << type_name(type) << " ";
+    }
+    out << name;
+    out << " = " << value->get_integer() << ";" << endl << endl;
+  } else if (type->is_struct() || type->is_xception()) {
+    const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+    vector<t_field*>::const_iterator f_iter;
+    const map<t_const_value*, t_const_value*>& val = value->get_map();
+    map<t_const_value*, t_const_value*>::const_iterator v_iter;
+    out << type_name(type) << " " << name << " = new " << type_name(type) << "();"
+        << endl;
+    for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+      t_type* field_type = NULL;
+      for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+        if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+          field_type = (*f_iter)->get_type();
+        }
+      }
+      if (field_type == NULL) {
+        throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+      }
+      string val = render_const_value(out, name, field_type, v_iter->second);
+      indent(out) << name << ".";
+      out << v_iter->first->get_string() << " = " << val << ";" << endl;
+    }
+    out << endl;
+  } else if (type->is_map()) {
+    if (!defval) {
+      out << type_name(type) << " ";
+    }
+    out << name << " =";
+    scope_up(out);
+
+    t_type* ktype = ((t_map*)type)->get_key_type();
+    t_type* vtype = ((t_map*)type)->get_val_type();
+    const map<t_const_value*, t_const_value*>& val = value->get_map();
+    map<t_const_value*, t_const_value*>::const_iterator v_iter;
+
+    for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+      string key = render_const_value(out, name, ktype, v_iter->first);
+      string val = render_const_value(out, name, vtype, v_iter->second);
+      indent(out) << key << ": " << val << "," << endl;
+    }
+    scope_down(out, ";" + endl);
+
+    out << endl;
+  } else if (type->is_list() || type->is_set()) {
+    if (!defval) {
+      out << type_name(type) << " ";
+    }
+    out << name << " = ";
+    t_type* etype;
+    if (type->is_list()) {
+      out << "[" << endl;
+      etype = ((t_list*)type)->get_elem_type();
+    } else {
+      out << "new " << type_name(type) << ".from([" << endl;
+      etype = ((t_set*)type)->get_elem_type();
+    }
+    const vector<t_const_value*>& val = value->get_list();
+    vector<t_const_value*>::const_iterator v_iter;
+
+    indent_up();
+    for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+      string val = render_const_value(out, name, etype, *v_iter);
+      indent(out) << val << "," << endl;
+    }
+    indent_down();
+
+    if (type->is_list()) {
+      indent(out) << "];" << endl;
+    } else {
+      indent(out) << "]);" << endl;
+    }
+
+  } else {
+    throw "compiler error: no const of type " + type->get_name();
+  }
+}
+
+string t_dart_generator::render_const_value(ofstream& out,
+                                           string name,
+                                           t_type* type,
+                                           t_const_value* value) {
+  (void)name;
+  type = get_true_type(type);
+  std::ostringstream render;
+
+  if (type->is_base_type()) {
+    t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+    switch (tbase) {
+    case t_base_type::TYPE_STRING:
+      render << "'" << get_escaped_string(value) << "'";
+      break;
+    case t_base_type::TYPE_BOOL:
+      render << ((value->get_integer() > 0) ? "true" : "false");
+      break;
+    case t_base_type::TYPE_BYTE:
+    case t_base_type::TYPE_I16:
+    case t_base_type::TYPE_I32:
+    case t_base_type::TYPE_I64:
+      render << value->get_integer();
+      break;
+    case t_base_type::TYPE_DOUBLE:
+      if (value->get_type() == t_const_value::CV_INTEGER) {
+        render << value->get_integer();
+      } else {
+        render << value->get_double();
+      }
+      break;
+    default:
+      throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+    }
+  } else if (type->is_enum()) {
+    render << value->get_integer();
+  } else {
+    string t = tmp("tmp");
+    print_const_value(out, t, type, value, true);
+    out << endl;
+    render << t;
+  }
+
+  return render.str();
+}
+
+/**
+ * Generates a struct definition for a thrift data type. This is a class
+ * with data members, read(), write(), and an inner Isset class.
+ *
+ * @param tstruct The struct definition
+ */
+void t_dart_generator::generate_struct(t_struct* tstruct) {
+  generate_dart_struct(tstruct, false);
+}
+
+/**
+ * Exceptions are structs, but they inherit from Exception
+ *
+ * @param tstruct The struct definition
+ */
+void t_dart_generator::generate_xception(t_struct* txception) {
+  generate_dart_struct(txception, true);
+}
+
+/**
+ * Dart struct definition.
+ *
+ * @param tstruct The struct definition
+ */
+void t_dart_generator::generate_dart_struct(t_struct* tstruct, bool is_exception) {
+  string file_name = get_file_name(tstruct->get_name());
+  string f_struct_name = src_dir_ + "/" + file_name + ".dart";
+  ofstream f_struct;
+  f_struct.open(f_struct_name.c_str());
+
+  f_struct << autogen_comment() << dart_library(file_name) << endl;
+
+  string imports;
+
+  f_struct << dart_thrift_imports() << endl;
+
+  generate_dart_struct_definition(f_struct, tstruct, is_exception, false, file_name);
+
+  f_struct.close();
+}
+
+/**
+ * Dart struct definition. This has various parameters, as it could be
+ * generated standalone or inside another class as a helper. If it
+ * is a helper than it is a static class.
+ *
+ * @param tstruct      The struct definition
+ * @param is_exception Is this an exception?
+ * @param in_class     If inside a class, needs to be static class
+ * @param is_result    If this is a result it needs a different writer
+ */
+void t_dart_generator::generate_dart_struct_definition(ofstream& out,
+                                                       t_struct* tstruct,
+                                                       bool is_exception,
+                                                       bool is_result,
+                                                       string export_file_name) {
+  generate_dart_doc(out, tstruct);
+
+  string class_name = tstruct->get_name();
+  if (!export_file_name.empty()) {
+    export_class_to_library(export_file_name, class_name);
+  }
+  indent(out) << "class " << class_name << " ";
+
+  if (is_exception) {
+    out << "extends Error ";
+  }
+  out << "implements TBase";
+  scope_up(out);
+
+  indent(out) << "static final TStruct _STRUCT_DESC = new TStruct(\"" << class_name
+              << "\");" << endl;
+
+  // Members are public for -dart, private for -dartbean
+  const vector<t_field*>& members = tstruct->get_members();
+  vector<t_field*>::const_iterator m_iter;
+
+  for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+    indent(out) << "static final TField _" << constant_name((*m_iter)->get_name())
+                << "_FIELD_DESC = new TField(\"" << (*m_iter)->get_name() << "\", "
+                << type_to_enum((*m_iter)->get_type()) << ", " << (*m_iter)->get_key() << ");"
+                << endl;
+  }
+
+  out << endl;
+
+  for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+    generate_dart_doc(out, *m_iter);
+    indent(out) << type_name((*m_iter)->get_type()) + " _"
+                << get_field_name((*m_iter)->get_name()) << ";" << endl;
+
+    indent(out) << "static const int " << upcase_string((*m_iter)->get_name())
+                << " = " << (*m_iter)->get_key() << ";" << endl;
+  }
+
+  out << endl;
+
+  // Inner Isset class
+  if (members.size() > 0) {
+    for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+      if (!type_can_be_null((*m_iter)->get_type())) {
+        string field_name = get_field_name((*m_iter)->get_name());
+        indent(out) << "bool __isset_" << field_name << " = false;" << endl;
+      }
+    }
+  }
+
+  out << endl;
+
+  // Default constructor
+  indent(out) << tstruct->get_name() << "()";
+  scope_up(out);
+  for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+    t_type* t = get_true_type((*m_iter)->get_type());
+    if ((*m_iter)->get_value() != NULL) {
+      print_const_value(out,
+                        "this." + (*m_iter)->get_name(),
+                        t,
+                        (*m_iter)->get_value(),
+                        true,
+                        true);
+    }
+  }
+  scope_down(out);
+  out << endl;
+
+  generate_dart_bean_boilerplate(out, tstruct);
+  generate_generic_field_getters(out, tstruct);
+  generate_generic_field_setters(out, tstruct);
+  generate_generic_isset_method(out, tstruct);
+
+  generate_dart_struct_reader(out, tstruct);
+  if (is_result) {
+    generate_dart_struct_result_writer(out, tstruct);
+  } else {
+    generate_dart_struct_writer(out, tstruct);
+  }
+  generate_dart_struct_tostring(out, tstruct);
+  generate_dart_validator(out, tstruct);
+  scope_down(out);
+  out << endl;
+}
+
+/**
+ * Generates a function to read all the fields of the struct.
+ *
+ * @param tstruct The struct definition
+ */
+void t_dart_generator::generate_dart_struct_reader(ofstream& out, t_struct* tstruct) {
+  indent(out) << "read(TProtocol iprot)";
+  scope_up(out);
+
+  const vector<t_field*>& fields = tstruct->get_members();
+  vector<t_field*>::const_iterator f_iter;
+
+  // Declare stack tmp variables and read struct header
+  indent(out) << "TField field;" << endl;
+  indent(out) << "iprot.readStructBegin();" << endl;
+
+  // Loop over reading in fields
+  indent(out) << "while (true)";
+  scope_up(out);
+
+  // Read beginning field marker
+  indent(out) << "field = iprot.readFieldBegin();" << endl;
+
+  // Check for field STOP marker and break
+  indent(out) << "if (field.type == TType.STOP)";
+  scope_up(out);
+  indent(out) << "break;" << endl;
+  scope_down(out);
+
+  // Switch statement on the field we are reading
+  indent(out) << "switch (field.id)";
+  scope_up(out);
+
+  // Generate deserialization code for known cases
+  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+    indent(out) << "case " << upcase_string((*f_iter)->get_name()) << ":" << endl;
+    indent_up();
+
+    indent(out) << "if (field.type == " << type_to_enum((*f_iter)->get_type()) << ")";
+    scope_up(out);
+
+    generate_deserialize_field(out, *f_iter, "this.");
+    generate_isset_set(out, *f_iter);
+
+    scope_down(out, " else");
+    scope_up(out);
+    indent(out) << "TProtocolUtil.skip(iprot, field.type);" << endl;
+    scope_down(out);
+
+    indent(out) << "break;" << endl;
+    indent_down();
+  }
+
+  // In the default case we skip the field
+  indent(out) << "default:" << endl;
+  indent_up();
+  indent(out) << "TProtocolUtil.skip(iprot, field.type);" << endl;
+  indent(out) << "break;" << endl;
+  indent_down();
+
+  scope_down(out);
+
+  // Read field end marker
+  indent(out) << "iprot.readFieldEnd();" << endl;
+
+  scope_down(out);
+
+  indent(out) << "iprot.readStructEnd();" << endl2;
+
+  // in non-beans style, check for required fields of primitive type
+  // (which can be checked here but not in the general validate method)
+  indent(out) << "// check for required fields of primitive type, which can't be "
+                 "checked in the validate method" << endl;
+  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+    if ((*f_iter)->get_req() == t_field::T_REQUIRED && !type_can_be_null((*f_iter)->get_type())) {
+      string field_name = get_field_name((*f_iter)->get_name());
+      indent(out) << "if (!__isset_" << field_name << ")";
+      scope_up(out);
+      indent(out) << "  throw new TProtocolError(TProtocolErrorType.UNKNOWN, \"Required field '"
+          << field_name
+          << "' was not found in serialized data! Struct: \" + toString());" << endl;
+      scope_down(out, endl2);
+    }
+  }
+
+  // performs various checks (e.g. check that all required fields are set)
+  indent(out) << "validate();" << endl;
+
+  scope_down(out, endl2);
+}
+
+// generates dart method to perform various checks
+// (e.g. check that all required fields are set)
+void t_dart_generator::generate_dart_validator(ofstream& out, t_struct* tstruct) {
+  indent(out) << "validate()";
+  scope_up(out);
+
+  const vector<t_field*>& fields = tstruct->get_members();
+  vector<t_field*>::const_iterator f_iter;
+
+  indent(out) << "// check for required fields" << endl;
+  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+    if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
+      string field_name = get_field_name((*f_iter)->get_name());
+      if (type_can_be_null((*f_iter)->get_type())) {
+        indent(out) << "if (" << field_name << " == null)";
+        scope_up(out);
+        indent(out) << "throw new TProtocolError(TProtocolErrorType.UNKNOWN, \"Required field '"
+                    << field_name << "' was not present! Struct: \" + toString());"
+                    << endl;
+        scope_down(out);
+      } else {
+        indent(out) << "// alas, we cannot check '" << field_name
+                    << "' because it's a primitive and you chose the non-beans generator." << endl;
+      }
+    }
+  }
+
+  // check that fields of type enum have valid values
+  indent(out) << "// check that fields of type enum have valid values" << endl;
+  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+    t_field* field = (*f_iter);
+    t_type* type = field->get_type();
+    // if field is an enum, check that its value is valid
+    if (type->is_enum()) {
+      string field_name = get_field_name(field->get_name());
+      indent(out) << "if (" << generate_isset_check(field) << " && !" << get_enum_class_name(type)
+                  << ".VALID_VALUES.contains(" << field_name << "))";
+      scope_up(out);
+      indent(out) << "throw new TProtocolError(TProtocolErrorType.UNKNOWN, \"The field '"
+                  << field_name << "' has been assigned the invalid value "
+                  << "$" << field_name << "\");" << endl;
+      scope_down(out);
+    }
+  }
+
+  scope_down(out, endl2);
+}
+
+/**
+ * Generates a function to write all the fields of the struct
+ *
+ * @param tstruct The struct definition
+ */
+void t_dart_generator::generate_dart_struct_writer(ofstream& out, t_struct* tstruct) {
+  out << indent() << "write(TProtocol oprot)";
+  scope_up(out);
+
+  string name = tstruct->get_name();
+  const vector<t_field*>& fields = tstruct->get_sorted_members();
+  vector<t_field*>::const_iterator f_iter;
+
+  // performs various checks (e.g. check that all required fields are set)
+  indent(out) << "validate();" << endl2;
+
+  indent(out) << "oprot.writeStructBegin(_STRUCT_DESC);" << endl;
+
+  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+    string field_name = get_field_name((*f_iter)->get_name());
+    bool null_allowed = type_can_be_null((*f_iter)->get_type());
+    if (null_allowed) {
+      indent(out) << "if (this." << field_name << " != null)";
+      scope_up(out);
+    }
+
+    indent(out) << "oprot.writeFieldBegin(_" << constant_name((*f_iter)->get_name())
+                << "_FIELD_DESC);" << endl;
+
+    // Write field contents
+    generate_serialize_field(out, *f_iter, "this.");
+
+    // Write field closer
+    indent(out) << "oprot.writeFieldEnd();" << endl;
+
+    if (null_allowed) {
+      scope_down(out);
+    }
+  }
+  // Write the struct map
+  indent(out) << "oprot.writeFieldStop();" << endl << indent() << "oprot.writeStructEnd();"
+      << endl;
+
+  scope_down(out, endl2);
+}
+
+/**
+ * Generates a function to write all the fields of the struct,
+ * which is a function result. These fields are only written
+ * if they are set in the Isset array, and only one of them
+ * can be set at a time.
+ *
+ * @param tstruct The struct definition
+ */
+void t_dart_generator::generate_dart_struct_result_writer(ofstream& out, t_struct* tstruct) {
+  indent(out) << "write(TProtocol oprot)";
+  scope_up(out);
+
+  string name = tstruct->get_name();
+  const vector<t_field*>& fields = tstruct->get_sorted_members();
+  vector<t_field*>::const_iterator f_iter;
+
+  indent(out) << "oprot.writeStructBegin(_STRUCT_DESC);" << endl2;
+
+  bool first = true;
+  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+    if (first) {
+      first = false;
+      indent(out) << "if ";
+    } else {
+      out << " else if ";
+    }
+
+    out << "(this." << generate_isset_check(*f_iter) << ")";
+    scope_up(out);
+
+    indent(out) << "oprot.writeFieldBegin(_" << constant_name((*f_iter)->get_name())
+                << "_FIELD_DESC);" << endl;
+
+    // Write field contents
+    generate_serialize_field(out, *f_iter, "this.");
+
+    // Write field closer
+    indent(out) << "oprot.writeFieldEnd();" << endl;
+
+    scope_down(out, "");
+  }
+  out << endl;
+
+  // Write the struct map
+  indent(out) << "oprot.writeFieldStop();" << endl << indent()
+      << "oprot.writeStructEnd();" << endl;
+
+  scope_down(out, endl2);
+}
+
+void t_dart_generator::generate_generic_field_getters(std::ofstream& out,
+                                                      t_struct* tstruct) {
+  // create the getter
+  indent(out) << "getFieldValue(int fieldID)";
+  scope_up(out);
+
+  indent(out) << "switch (fieldID)";
+  scope_up(out);
+
+  const vector<t_field*>& fields = tstruct->get_members();
+  vector<t_field*>::const_iterator f_iter;
+  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+    t_field* field = *f_iter;
+    std::string field_name = get_field_name(field->get_name());
+
+    indent(out) << "case " << upcase_string(field_name) << ":" << endl;
+    indent_up();
+    indent(out) << "return this." << field_name << ";" << endl;
+    indent_down();
+  }
+
+  indent(out) << "default:" << endl;
+  indent_up();
+  indent(out) << "throw new ArgumentError(\"Field $fieldID doesn't exist!\");" << endl;
+  indent_down();
+
+  scope_down(out);  // switch
+  scope_down(out, endl2);  // method
+}
+
+void t_dart_generator::generate_generic_field_setters(std::ofstream& out,
+                                                      t_struct* tstruct) {
+
+  // create the setter
+  indent(out) << "setFieldValue(int fieldID, Object value)";
+  scope_up(out);
+
+  indent(out) << "switch (fieldID)";
+  scope_up(out);
+
+  // build up the bodies of both the getter and setter at once
+  const vector<t_field*>& fields = tstruct->get_members();
+  vector<t_field*>::const_iterator f_iter;
+  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+    t_field* field = *f_iter;
+    std::string field_name = get_field_name(field->get_name());
+
+    indent(out) << "case " << upcase_string(field_name) << ":" << endl;
+    indent_up();
+
+    indent(out) << "if (value == null)";
+    scope_up(out);
+    indent(out) << "unset" << get_cap_name(field_name) << "();" << endl;
+
+    scope_down(out, " else");
+    scope_up(out);
+    indent(out) << "this." << field_name << " = value;" << endl;
+    scope_down(out);
+
+    indent(out) << "break;" << endl;
+
+    indent_down();
+    out << endl;
+  }
+
+  indent(out) << "default:" << endl;
+  indent_up();
+  indent(out) << "throw new ArgumentError(\"Field $fieldID doesn't exist!\");" << endl;
+  indent_down();
+
+  scope_down(out);  // switch
+  scope_down(out, endl2);  // method
+}
+
+// Creates a generic isSet method that takes the field number as argument
+void t_dart_generator::generate_generic_isset_method(std::ofstream& out, t_struct* tstruct) {
+  const vector<t_field*>& fields = tstruct->get_members();
+  vector<t_field*>::const_iterator f_iter;
+
+  // create the isSet method
+  indent(out) << "// Returns true if field corresponding to fieldID is set (has been assigned a "
+                 "value) and false otherwise" << endl;
+  indent(out) << "bool isSet(int fieldID)";
+  scope_up(out);
+
+  indent(out) << "switch (fieldID)";
+  scope_up(out);
+
+  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+    t_field* field = *f_iter;
+    indent(out) << "case " << upcase_string(field->get_name()) << ":" << endl;
+    indent_up();
+    indent(out) << "return " << generate_isset_check(field) << ";" << endl;
+    indent_down();
+  }
+
+  indent(out) << "default:" << endl;
+  indent_up();
+  indent(out) << "throw new ArgumentError(\"Field $fieldID doesn't exist!\");" << endl;
+  indent_down();
+
+  scope_down(out);  // switch
+  scope_down(out, endl2);  // method
+}
+
+/**
+ * Generates a set of Dart Bean boilerplate functions (setters, getters, etc.)
+ * for the given struct.
+ *
+ * @param tstruct The struct definition
+ */
+void t_dart_generator::generate_dart_bean_boilerplate(ofstream& out,
+                                                    t_struct* tstruct) {
+  const vector<t_field*>& fields = tstruct->get_members();
+  vector<t_field*>::const_iterator f_iter;
+  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+    t_field* field = *f_iter;
+    t_type* type = get_true_type(field->get_type());
+    std::string field_name = get_field_name(field->get_name());
+    std::string cap_name = get_cap_name(field_name);
+
+    indent(out) << "// " << field_name << endl;
+
+    // Simple getter
+    generate_dart_doc(out, field);
+    indent(out) << type_name(type) << " get " << field_name << " => this._" << field_name << ";" << endl2;
+
+    // Simple setter
+    generate_dart_doc(out, field);
+    indent(out) << "set " << field_name << "(" << type_name(type) << " " << field_name << ")";
+    scope_up(out);
+    indent(out) << "this._" << field_name << " = " << field_name << ";" << endl;
+    generate_isset_set(out, field);
+    scope_down(out, endl2);
+
+    // isSet method
+    indent(out) << "bool is" << get_cap_name("set") << cap_name << "()";
+    if (type_can_be_null(type)) {
+      out << " => this." << field_name << " != null;" << endl2;
+    } else {
+      out << " => this.__isset_" << field_name << ";" << endl2;
+    }
+
+    // Unsetter
+    indent(out) << "unset" << cap_name << "()";
+    scope_up(out);
+    if (type_can_be_null(type)) {
+      indent(out) << "this." << field_name << " = null;" << endl;
+    } else {
+      indent(out) << "this.__isset_" << field_name << " = false;" << endl;
+    }
+    scope_down(out, endl2);
+  }
+}
+
+/**
+ * Generates a toString() method for the given struct
+ *
+ * @param tstruct The struct definition
+ */
+void t_dart_generator::generate_dart_struct_tostring(ofstream& out,
+                                                   t_struct* tstruct) {
+  indent(out) << "String toString()";
+  scope_up(out);
+
+  indent(out) << "StringBuffer ret = new StringBuffer(\""
+              << tstruct->get_name() << "(\");" << endl2;
+
+  const vector<t_field*>& fields = tstruct->get_members();
+  vector<t_field*>::const_iterator f_iter;
+
+  bool first = true;
+  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+    bool could_be_unset = (*f_iter)->get_req() == t_field::T_OPTIONAL;
+    if (could_be_unset) {
+      indent(out) << "if (" << generate_isset_check(*f_iter) << ")";
+      scope_up(out);
+    }
+
+    t_field* field = (*f_iter);
+    std::string field_name = get_field_name(field->get_name());
+
+    if (!first) {
+      indent(out) << "ret.write(\", \");" << endl;
+    }
+    indent(out) << "ret.write(\"" << field_name << ":\");" << endl;
+    bool can_be_null = type_can_be_null(field->get_type());
+    if (can_be_null) {
+      indent(out) << "if (this." << field_name << " == null)";
+      scope_up(out);
+      indent(out) << "ret.write(\"null\");" << endl;
+      scope_down(out, " else");
+      scope_up(out);
+    }
+
+    if (field->get_type()->is_base_type() && ((t_base_type*)(field->get_type()))->is_binary()) {
+      indent(out) << "ret.write(\"BINARY\");" << endl;
+    } else if (field->get_type()->is_enum()) {
+      indent(out) << "String " << field_name << "_name = "
+                  << get_enum_class_name(field->get_type())
+                  << ".VALUES_TO_NAMES[this." << field_name << "];" << endl;
+      indent(out) << "if (" << field_name << "_name != null)";
+      scope_up(out);
+      indent(out) << "ret.write(" << field_name << "_name);" << endl;
+      indent(out) << "ret.write(\" (\");" << endl;
+      scope_down(out);
+      indent(out) << "ret.write(this." << field_name << ");" << endl;
+      indent(out) << "if (" << field_name << "_name != null)";
+      scope_up(out);
+      indent(out) << "ret.write(\")\");" << endl;
+      scope_down(out);
+    } else {
+      indent(out) << "ret.write(this." << field_name << ");" << endl;
+    }
+
+    if (can_be_null) {
+      scope_down(out);
+    }
+    if (could_be_unset) {
+      scope_down(out);
+    }
+
+    out << endl;
+    first = false;
+  }
+
+  indent(out) << "ret.write(\")\");" << endl2;
+
+  indent(out) << "return ret.toString();" << endl;
+
+  scope_down(out, endl2);
+}
+
+/**
+ * Returns a string with the dart representation of the given thrift type
+ * (e.g. for the type struct it returns "TType.STRUCT")
+ */
+std::string t_dart_generator::get_dart_type_string(t_type* type) {
+  if (type->is_list()) {
+    return "TType.LIST";
+  } else if (type->is_map()) {
+    return "TType.MAP";
+  } else if (type->is_set()) {
+    return "TType.SET";
+  } else if (type->is_struct() || type->is_xception()) {
+    return "TType.STRUCT";
+  } else if (type->is_enum()) {
+    return "TType.I32";
+  } else if (type->is_typedef()) {
+    return get_dart_type_string(((t_typedef*)type)->get_type());
+  } else if (type->is_base_type()) {
+    switch (((t_base_type*)type)->get_base()) {
+    case t_base_type::TYPE_VOID:
+      return "TType.VOID";
+      break;
+    case t_base_type::TYPE_STRING:
+      return "TType.STRING";
+      break;
+    case t_base_type::TYPE_BOOL:
+      return "TType.BOOL";
+      break;
+    case t_base_type::TYPE_BYTE:
+      return "TType.BYTE";
+      break;
+    case t_base_type::TYPE_I16:
+      return "TType.I16";
+      break;
+    case t_base_type::TYPE_I32:
+      return "TType.I32";
+      break;
+    case t_base_type::TYPE_I64:
+      return "TType.I64";
+      break;
+    case t_base_type::TYPE_DOUBLE:
+      return "TType.DOUBLE";
+      break;
+    default:
+      throw std::runtime_error("Unknown thrift type \"" + type->get_name()
+                               + "\" passed to t_dart_generator::get_dart_type_string!");
+      break; // This should never happen!
+    }
+  } else {
+    throw std::runtime_error(
+        "Unknown thrift type \"" + type->get_name()
+        + "\" passed to t_dart_generator::get_dart_type_string!"); // This should never happen!
+  }
+}
+
+void t_dart_generator::generate_service(t_service* tservice) {
+  string file_name = get_file_name(service_name_);
+  string f_service_name = src_dir_ + "/" + file_name + ".dart";
+  f_service_.open(f_service_name.c_str());
+
+  f_service_ << autogen_comment() << dart_library(file_name) << endl;
+  f_service_ << service_imports() << dart_thrift_imports() << endl;
+  f_service_ << endl;
+
+  generate_service_interface(tservice);
+  generate_service_client(tservice);
+  generate_service_server(tservice);
+  generate_service_helpers(tservice);
+
+  f_service_.close();
+}
+
+/**
+ * Generates a service interface definition.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_dart_generator::generate_service_interface(t_service* tservice) {
+  string extends_iface = "";
+  if (tservice->get_extends() != NULL) {
+    extends_iface = " extends " + tservice->get_extends()->get_name();
+  }
+
+  generate_dart_doc(f_service_, tservice);
+
+  string class_name = service_name_;
+  export_class_to_library(get_file_name(service_name_), class_name);
+  indent(f_service_) << "abstract class " << class_name << extends_iface;
+  scope_up(f_service_);
+
+  vector<t_function*> functions = tservice->get_functions();
+  vector<t_function*>::iterator f_iter;
+  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+    f_service_ << endl;
+    generate_dart_doc(f_service_, *f_iter);
+    indent(f_service_) << function_signature(*f_iter) << ";" << endl;
+  }
+
+  scope_down(f_service_, endl2);
+}
+
+/**
+ * Generates structs for all the service args and return types
+ *
+ * @param tservice The service
+ */
+void t_dart_generator::generate_service_helpers(t_service* tservice) {
+  vector<t_function*> functions = tservice->get_functions();
+  vector<t_function*>::iterator f_iter;
+  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+    t_struct* ts = (*f_iter)->get_arglist();
+    generate_dart_struct_definition(f_service_, ts, false, false);
+    generate_function_helpers(*f_iter);
+  }
+}
+
+/**
+ * Generates a service client definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_dart_generator::generate_service_client(t_service* tservice) {
+  string extends = "";
+  string extends_client = "";
+  if (tservice->get_extends() != NULL) {
+    extends = tservice->get_extends()->get_name();
+    extends_client = " extends " + extends + "Client";
+  }
+
+  string class_name = service_name_ + "Client";
+  export_class_to_library(get_file_name(service_name_), class_name);
+  indent(f_service_) << "class " << class_name << extends_client
+                     << " implements " << service_name_;
+  scope_up(f_service_);
+  f_service_ << endl;
+
+  indent(f_service_) << class_name << "(TProtocol iprot, [TProtocol oprot = null])";
+
+  if (!extends.empty()) {
+    indent_up();
+    f_service_ << endl;
+    indent(f_service_) << ": super(iprot, oprot);" << endl;
+    indent_down();
+  } else {
+    scope_up(f_service_);
+    indent(f_service_) << "_iprot = iprot;" << endl;
+    indent(f_service_) << "_oprot = (oprot == null) ? iprot : oprot;" << endl;
+    scope_down(f_service_);
+  }
+  f_service_ << endl;
+
+  if (extends.empty()) {
+    indent(f_service_) << "TProtocol _iprot;" << endl2;
+    indent(f_service_) << "TProtocol get iprot => _iprot;" << endl2;
+    indent(f_service_) << "TProtocol _oprot;" << endl2;
+    indent(f_service_) << "TProtocol get oprot => _oprot;" << endl2;
+    indent(f_service_) << "int _seqid = 0;" << endl2;
+    indent(f_service_) << "int get seqid => _seqid;" << endl2;
+    indent(f_service_) << "int nextSeqid() => ++_seqid;" << endl2;
+  }
+
+  // Generate client method implementations
+  vector<t_function*> functions = tservice->get_functions();
+  vector<t_function*>::const_iterator f_iter;
+  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+    string funname = (*f_iter)->get_name();
+
+    // Open function
+    indent(f_service_) << function_signature(*f_iter) << " async";
+    scope_up(f_service_);
+
+    // Get the struct of function call params
+    t_struct* arg_struct = (*f_iter)->get_arglist();
+
+    string argsname = get_args_class_name((*f_iter)->get_name());
+    vector<t_field*>::const_iterator fld_iter;
+    const vector<t_field*>& fields = arg_struct->get_members();
+
+    // Serialize the request
+    indent(f_service_) << "oprot.writeMessageBegin(new TMessage(\"" << funname << "\", "
+               << ((*f_iter)->is_oneway() ? "TMessageType.ONEWAY" : "TMessageType.CALL")
+               << ", nextSeqid()));" << endl;
+    indent(f_service_) << argsname << " args = new " << argsname << "();" << endl;
+
+    for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+      indent(f_service_) << "args." << (*fld_iter)->get_name() << " = "
+                 << (*fld_iter)->get_name() << ";" << endl;
+    }
+
+    indent(f_service_) << "args.write(oprot);" << endl;
+    indent(f_service_) << "oprot.writeMessageEnd();" << endl2;
+
+    indent(f_service_) << "await oprot.transport.flush();" << endl2;
+
+    if (!(*f_iter)->is_oneway()) {
+      indent(f_service_) << "TMessage msg = iprot.readMessageBegin();" << endl;
+      indent(f_service_) << "if (msg.type == TMessageType.EXCEPTION)";
+      scope_up(f_service_);
+      indent(f_service_) << "TApplicationError error = TApplicationError.read(iprot);" << endl;
+      indent(f_service_) << "iprot.readMessageEnd();" << endl;
+      indent(f_service_) << "throw error;" << endl;
+      scope_down(f_service_, endl2);
+
+      string result_class = get_result_class_name((*f_iter)->get_name());
+      indent(f_service_) << result_class << " result = new " << result_class << "();" << endl;
+      indent(f_service_) << "result.read(iprot);" << endl;
+      indent(f_service_) << "iprot.readMessageEnd();" << endl;
+
+      // Careful, only return _result if not a void function
+      if (!(*f_iter)->get_returntype()->is_void()) {
+        indent(f_service_) << "if (result." << generate_isset_check("success") << ")";
+        scope_up(f_service_);
+        indent(f_service_) << "return result.success;" << endl;
+        scope_down(f_service_, endl2);
+      }
+
+      t_struct* xs = (*f_iter)->get_xceptions();
+      const std::vector<t_field*>& xceptions = xs->get_members();
+      vector<t_field*>::const_iterator x_iter;
+      for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+        string result_field_name = get_field_name((*x_iter)->get_name());
+        indent(f_service_) << "if (result." << result_field_name << " != null)";
+        scope_up(f_service_);
+        indent(f_service_) << "throw result." << result_field_name << ";" << endl;
+        scope_down(f_service_);
+      }
+
+      // If you get here it's an exception, unless a void function
+      if ((*f_iter)->get_returntype()->is_void()) {
+        indent(f_service_) << "return;" << endl;
+      } else {
+        indent(f_service_) << "throw new TApplicationError(TApplicationErrorType.MISSING_RESULT, \""
+                   << (*f_iter)->get_name() << " failed: unknown result\");" << endl;
+      }
+    }
+
+    scope_down(f_service_, endl2);
+  }
+
+  scope_down(f_service_, endl2);
+}
+
+/**
+ * Generates a service server definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_dart_generator::generate_service_server(t_service* tservice) {
+  // Generate the dispatch methods
+  vector<t_function*> functions = tservice->get_functions();
+  vector<t_function*>::iterator f_iter;
+
+  // typedef
+  indent(f_service_) << "typedef void ProcessFunction(int seqid, TProtocol iprot, TProtocol oprot);" << endl2;
+
+  // Extends stuff
+  string extends = "";
+  string extends_processor = "";
+  if (tservice->get_extends() != NULL) {
+    extends = type_name(tservice->get_extends());
+    extends_processor = " extends " + extends + "Processor";
+  }
+
+  // Generate the header portion
+  string class_name =  service_name_ + "Processor";
+  export_class_to_library(get_file_name(service_name_), class_name);
+  indent(f_service_) << "class " << class_name << extends_processor << " implements TProcessor";
+  scope_up(f_service_);
+
+  indent(f_service_) << class_name << "(" << service_name_ << " iface)";
+  if (!extends.empty()) {
+    indent_up();
+    f_service_ << endl;
+    indent(f_service_) << ": super(iface)";
+    indent_down();
+  }
+  scope_up(f_service_);
+
+  if (extends.empty()) {
+    indent(f_service_) << "iface_ = iface;" << endl;
+  }
+
+  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+    indent(f_service_) << "PROCESS_MAP[\"" << (*f_iter)->get_name()
+               << "\"] = " << (*f_iter)->get_name() << ";" << endl;
+  }
+  scope_down(f_service_, endl2);
+
+  indent(f_service_) << service_name_ << " iface_;" << endl;
+
+  if (extends.empty()) {
+    indent(f_service_) << "final Map<String, ProcessFunction> PROCESS_MAP = {};" << endl;
+  }
+
+  f_service_ << endl;
+
+  // Generate the server implementation
+  indent(f_service_) << "bool process(TProtocol iprot, TProtocol oprot)";
+  scope_up(f_service_);
+  indent(f_service_) << "TMessage msg = iprot.readMessageBegin();" << endl;
+  indent(f_service_) << "ProcessFunction fn = PROCESS_MAP[msg.name];" << endl;
+  indent(f_service_) << "if (fn == null)";
+  scope_up(f_service_);
+  indent(f_service_) << "TProtocolUtil.skip(iprot, TType.STRUCT);" << endl;
+  indent(f_service_) << "iprot.readMessageEnd();" << endl;
+  indent(f_service_) << "TApplicationError x = new TApplicationError(TApplicationErrorType.UNKNOWN_METHOD, "
+         "\"Invalid method name: '\"+msg.name+\"'\");" << endl;
+  indent(f_service_) << "oprot.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION, msg.seqid));" << endl;
+  indent(f_service_) << "x.write(oprot);" << endl;
+  indent(f_service_) << "oprot.writeMessageEnd();" << endl;
+  indent(f_service_) << "oprot.transport.flush();" << endl;
+  indent(f_service_) << "return true;" << endl;
+  scope_down(f_service_);
+  indent(f_service_) << "fn(msg.seqid, iprot, oprot);" << endl;
+  indent(f_service_) << "return true;" << endl;
+  scope_down(f_service_, endl2); // process function
+
+  // Generate the process subfunctions
+  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+    generate_process_function(tservice, *f_iter);
+  }
+
+  scope_down(f_service_, endl2); // class
+}
+
+/**
+ * Generates a struct and helpers for a function.
+ *
+ * @param tfunction The function
+ */
+void t_dart_generator::generate_function_helpers(t_function* tfunction) {
+  if (tfunction->is_oneway()) {
+    return;
+  }
+
+  t_struct result(program_, get_result_class_name(tfunction->get_name()));
+  t_field success(tfunction->get_returntype(), "success", 0);
+  if (!tfunction->get_returntype()->is_void()) {
+    result.append(&success);
+  }
+
+  t_struct* xs = tfunction->get_xceptions();
+  const vector<t_field*>& fields = xs->get_members();
+  vector<t_field*>::const_iterator f_iter;
+  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+    result.append(*f_iter);
+  }
+
+  generate_dart_struct_definition(f_service_, &result, false, true);
+}
+
+/**
+ * Generates a process function definition.
+ *
+ * @param tfunction The function to write a dispatcher for
+ */
+void t_dart_generator::generate_process_function(t_service* tservice, t_function* tfunction) {
+  (void)tservice;
+
+  bool await_result = (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void());
+
+  indent(f_service_) << tfunction->get_name() << "(int seqid, TProtocol iprot, TProtocol oprot)";
+  if (await_result) {
+    f_service_ << " async";
+  }
+  scope_up(f_service_);
+
+  string argsname = get_args_class_name(tfunction->get_name());
+  string resultname = get_result_class_name(tfunction->get_name());
+
+  indent(f_service_) << argsname << " args = new " << argsname << "();" << endl;
+  indent(f_service_) << "args.read(iprot);" << endl;
+  indent(f_service_) << "iprot.readMessageEnd();" << endl;
+
+  t_struct* xs = tfunction->get_xceptions();
+  const std::vector<t_field*>& xceptions = xs->get_members();
+  vector<t_field*>::const_iterator x_iter;
+
+  if (!tfunction->is_oneway()) {
+    indent(f_service_) << resultname << " result = new " << resultname << "();" << endl;
+  }
+
+  if (!tfunction->is_oneway() && xceptions.size() > 0) {
+    indent(f_service_) << "try";
+    scope_up(f_service_);
+  }
+
+  // Generate the function call
+  t_struct* arg_struct = tfunction->get_arglist();
+  const std::vector<t_field*>& fields = arg_struct->get_members();
+  vector<t_field*>::const_iterator f_iter;
+
+  f_service_ << indent();
+  if (await_result) {
+    f_service_ << "result.success = await ";
+  }
+  f_service_ << "iface_." << tfunction->get_name() << "(";
+  bool first = true;
+  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+    if (first) {
+      first = false;
+    } else {
+      f_service_ << ", ";
+    }
+    f_service_ << "args." << get_field_name((*f_iter)->get_name());
+  }
+  f_service_ << ");" << endl;
+
+  if (!tfunction->is_oneway() && xceptions.size() > 0) {
+    for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+      string result_field_name = get_field_name((*x_iter)->get_name());
+      scope_down(f_service_, "");
+      f_service_ << " on " << type_name((*x_iter)->get_type())
+              << " catch(" << result_field_name << ")";
+      scope_up(f_service_);
+      if (!tfunction->is_oneway()) {
+        indent(f_service_) << "result." << result_field_name << " = "
+                   << result_field_name << ";" << endl;
+      }
+    }
+    scope_down(f_service_, " ");
+    f_service_ << "catch (th)";
+    scope_up(f_service_);
+    indent(f_service_) << "// Internal error" << endl;
+    indent(f_service_) << "TApplicationError x = new "
+               "TApplicationError(TApplicationErrorType.INTERNAL_ERROR, \"Internal error processing "
+               << tfunction->get_name() << "\");" << endl;
+    indent(f_service_) << "oprot.writeMessageBegin(new TMessage(\"" << tfunction->get_name()
+               << "\", TMessageType.EXCEPTION, seqid));" << endl;
+    indent(f_service_) << "x.write(oprot);" << endl;
+    indent(f_service_) << "oprot.writeMessageEnd();" << endl;
+    indent(f_service_) << "oprot.transport.flush();" << endl;
+    indent(f_service_) << "return;" << endl;
+    scope_down(f_service_);
+  }
+
+  if (tfunction->is_oneway()) {
+    indent(f_service_) << "return;" << endl;
+  } else {
+    indent(f_service_) << "oprot.writeMessageBegin(new TMessage(\"" << tfunction->get_name()
+               << "\", TMessageType.REPLY, seqid));" << endl;
+    indent(f_service_) << "result.write(oprot);" << endl;
+    indent(f_service_) << "oprot.writeMessageEnd();" << endl;
+    indent(f_service_) << "oprot.transport.flush();" << endl;
+  }
+
+  scope_down(f_service_, endl2);
+}
+
+/**
+ * Deserializes a field of any type.
+ *
+ * @param tfield The field
+ * @param prefix The variable name or container for this field
+ */
+void t_dart_generator::generate_deserialize_field(ofstream& out, t_field* tfield, string prefix) {
+  t_type* type = get_true_type(tfield->get_type());
+  string field_name = get_field_name(tfield->get_name());
+
+  if (type->is_void()) {
+    throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + field_name;
+  }
+
+  string name = prefix + field_name;
+
+  if (type->is_struct() || type->is_xception()) {
+    generate_deserialize_struct(out, (t_struct*)type, name);
+  } else if (type->is_container()) {
+    generate_deserialize_container(out, type, name);
+  } else if (type->is_base_type() || type->is_enum()) {
+
+    indent(out) << name << " = iprot.";
+
+    if (type->is_base_type()) {
+      t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+      switch (tbase) {
+      case t_base_type::TYPE_VOID:
+        throw "compiler error: cannot serialize void field in a struct: " + name;
+        break;
+      case t_base_type::TYPE_STRING:
+        if (((t_base_type*)type)->is_binary()) {
+          out << "readBinary();";
+        } else {
+          out << "readString();";
+        }
+        break;
+      case t_base_type::TYPE_BOOL:
+        out << "readBool();";
+        break;
+      case t_base_type::TYPE_BYTE:
+        out << "readByte();";
+        break;
+      case t_base_type::TYPE_I16:
+        out << "readI16();";
+        break;
+      case t_base_type::TYPE_I32:
+        out << "readI32();";
+        break;
+      case t_base_type::TYPE_I64:
+        out << "readI64();";
+        break;
+      case t_base_type::TYPE_DOUBLE:
+        out << "readDouble();";
+        break;
+      default:
+        throw "compiler error: no Dart name for base type " + t_base_type::t_base_name(tbase);
+      }
+    } else if (type->is_enum()) {
+      out << "readI32();";
+    }
+    out << endl;
+  } else {
+    printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
+           field_name.c_str(),
+           type_name(type).c_str());
+  }
+}
+
+/**
+ * Generates an unserializer for a struct, invokes read()
+ */
+void t_dart_generator::generate_deserialize_struct(ofstream& out, t_struct* tstruct, string prefix) {
+  indent(out) << prefix << " = new " << type_name(tstruct) << "();" << endl;
+  indent(out) << prefix << ".read(iprot);" << endl;
+}
+
+/**
+ * Deserializes a container by reading its size and then iterating
+ */
+void t_dart_generator::generate_deserialize_container(ofstream& out, t_type* ttype, string prefix) {
+  indent(out);
+  scope_up(out, "");
+
+  string obj;
+
+  if (ttype->is_map()) {
+    obj = tmp("_map");
+  } else if (ttype->is_set()) {
+    obj = tmp("_set");
+  } else if (ttype->is_list()) {
+    obj = tmp("_list");
+  }
+
+  // Declare variables, read header
+  if (ttype->is_map()) {
+    indent(out) << "TMap " << obj << " = iprot.readMapBegin();" << endl;
+  } else if (ttype->is_set()) {
+    indent(out) << "TSet " << obj << " = iprot.readSetBegin();" << endl;
+  } else if (ttype->is_list()) {
+    indent(out) << "TList " << obj << " = iprot.readListBegin();" << endl;
+  }
+
+  indent(out) << prefix << " = new " << type_name(ttype) << "();" << endl;
+
+  // For loop iterates over elements
+  string i = tmp("_i");
+  indent(out) << "for (int " << i << " = 0; " << i << " < " << obj << ".length"
+              << "; "
+              << "++" << i << ")";
+  scope_up(out);
+
+  if (ttype->is_map()) {
+    generate_deserialize_map_element(out, (t_map*)ttype, prefix);
+  } else if (ttype->is_set()) {
+    generate_deserialize_set_element(out, (t_set*)ttype, prefix);
+  } else if (ttype->is_list()) {
+    generate_deserialize_list_element(out, (t_list*)ttype, prefix);
+  }
+
+  scope_down(out);
+
+  // Read container end
+  if (ttype->is_map()) {
+    indent(out) << "iprot.readMapEnd();" << endl;
+  } else if (ttype->is_set()) {
+    indent(out) << "iprot.readSetEnd();" << endl;
+  } else if (ttype->is_list()) {
+    indent(out) << "iprot.readListEnd();" << endl;
+  }
+
+  scope_down(out);
+}
+
+/**
+ * Generates code to deserialize a map
+ */
+void t_dart_generator::generate_deserialize_map_element(ofstream& out, t_map* tmap, string prefix) {
+  string key = tmp("_key");
+  string val = tmp("_val");
+  t_field fkey(tmap->get_key_type(), key);
+  t_field fval(tmap->get_val_type(), val);
+
+  indent(out) << declare_field(&fkey) << endl;
+  indent(out) << declare_field(&fval) << endl;
+
+  generate_deserialize_field(out, &fkey);
+  generate_deserialize_field(out, &fval);
+
+  indent(out) << prefix << "[" << key << "] = " << val << ";" << endl;
+}
+
+/**
+ * Deserializes a set element
+ */
+void t_dart_generator::generate_deserialize_set_element(ofstream& out, t_set* tset, string prefix) {
+  string elem = tmp("_elem");
+  t_field felem(tset->get_elem_type(), elem);
+
+  indent(out) << declare_field(&felem) << endl;
+
+  generate_deserialize_field(out, &felem);
+
+  indent(out) << prefix << ".add(" << elem << ");" << endl;
+}
+
+/**
+ * Deserializes a list element
+ */
+void t_dart_generator::generate_deserialize_list_element(ofstream& out,
+                                                        t_list* tlist,
+                                                        string prefix) {
+  string elem = tmp("_elem");
+  t_field felem(tlist->get_elem_type(), elem);
+
+  indent(out) << declare_field(&felem) << endl;
+
+  generate_deserialize_field(out, &felem);
+
+  indent(out) << prefix << ".add(" << elem << ");" << endl;
+}
+
+/**
+ * Serializes a field of any type.
+ *
+ * @param tfield The field to serialize
+ * @param prefix Name to prepend to field name
+ */
+void t_dart_generator::generate_serialize_field(ofstream& out, t_field* tfield, string prefix) {
+  t_type* type = get_true_type(tfield->get_type());
+  string field_name = get_field_name(tfield->get_name());
+
+  // Do nothing for void types
+  if (type->is_void()) {
+    throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + field_name;
+  }
+
+  if (type->is_struct() || type->is_xception()) {
+    generate_serialize_struct(out, (t_struct*)type, prefix + field_name);
+  } else if (type->is_container()) {
+    generate_serialize_container(out, type, prefix + field_name);
+  } else if (type->is_base_type() || type->is_enum()) {
+
+    string name = prefix + field_name;
+    indent(out) << "oprot.";
+
+    if (type->is_base_type()) {
+      t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+      switch (tbase) {
+      case t_base_type::TYPE_VOID:
+        throw "compiler error: cannot serialize void field in a struct: " + name;
+        break;
+      case t_base_type::TYPE_STRING:
+        if (((t_base_type*)type)->is_binary()) {
+          out << "writeBinary(" << name << ");";
+        } else {
+          out << "writeString(" << name << ");";
+        }
+        break;
+      case t_base_type::TYPE_BOOL:
+        out << "writeBool(" << name << ");";
+        break;
+      case t_base_type::TYPE_BYTE:
+        out << "writeByte(" << name << ");";
+        break;
+      case t_base_type::TYPE_I16:
+        out << "writeI16(" << name << ");";
+        break;
+      case t_base_type::TYPE_I32:
+        out << "writeI32(" << name << ");";
+        break;
+      case t_base_type::TYPE_I64:
+        out << "writeI64(" << name << ");";
+        break;
+      case t_base_type::TYPE_DOUBLE:
+        out << "writeDouble(" << name << ");";
+        break;
+      default:
+        throw "compiler error: no Dart name for base type " + t_base_type::t_base_name(tbase);
+      }
+    } else if (type->is_enum()) {
+      out << "writeI32(" << name << ");";
+    }
+    out << endl;
+  } else {
+    printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n",
+           prefix.c_str(),
+           field_name.c_str(),
+           type_name(type).c_str());
+  }
+}
+
+/**
+ * Serializes all the members of a struct.
+ *
+ * @param tstruct The struct to serialize
+ * @param prefix  String prefix to attach to all fields
+ */
+void t_dart_generator::generate_serialize_struct(ofstream& out, t_struct* tstruct, string prefix) {
+  (void)tstruct;
+  indent(out) << prefix << ".write(oprot);" << endl;
+}
+
+/**
+ * Serializes a container by writing its size then the elements.
+ *
+ * @param ttype  The type of container
+ * @param prefix String prefix for fields
+ */
+void t_dart_generator::generate_serialize_container(ofstream& out, t_type* ttype, string prefix) {
+  indent(out);
+  scope_up(out, "");
+
+  if (ttype->is_map()) {
+    string iter = tmp("_key");
+    indent(out) << "oprot.writeMapBegin(new TMap(" << type_to_enum(((t_map*)ttype)->get_key_type())
+                << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << prefix << ".length));"
+                << endl;
+  } else if (ttype->is_set()) {
+    indent(out) << "oprot.writeSetBegin(new TSet(" << type_to_enum(((t_set*)ttype)->get_elem_type())
+                << ", " << prefix << ".length));" << endl;
+  } else if (ttype->is_list()) {
+    indent(out) << "oprot.writeListBegin(new TList("
+                << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix << ".length));"
+                << endl;
+  }
+
+  string iter = tmp("elem");
+  if (ttype->is_map()) {
+    indent(out) << "for (var " << iter << " in " << prefix << ".keys)";
+  } else if (ttype->is_set() || ttype->is_list()) {
+    indent(out) << "for (var " << iter << " in " << prefix << ")";
+  }
+
+  scope_up(out);
+
+  if (ttype->is_map()) {
+    generate_serialize_map_element(out, (t_map*)ttype, iter, prefix);
+  } else if (ttype->is_set()) {
+    generate_serialize_set_element(out, (t_set*)ttype, iter);
+  } else if (ttype->is_list()) {
+    generate_serialize_list_element(out, (t_list*)ttype, iter);
+  }
+
+  scope_down(out);
+
+  if (ttype->is_map()) {
+    indent(out) << "oprot.writeMapEnd();" << endl;
+  } else if (ttype->is_set()) {
+    indent(out) << "oprot.writeSetEnd();" << endl;
+  } else if (ttype->is_list()) {
+    indent(out) << "oprot.writeListEnd();" << endl;
+  }
+
+  scope_down(out);
+}
+
+/**
+ * Serializes the members of a map.
+ */
+void t_dart_generator::generate_serialize_map_element(ofstream& out,
+                                                     t_map* tmap,
+                                                     string iter,
+                                                     string map) {
+  t_field kfield(tmap->get_key_type(), iter);
+  generate_serialize_field(out, &kfield, "");
+  t_field vfield(tmap->get_val_type(), map + "[" + iter + "]");
+  generate_serialize_field(out, &vfield, "");
+}
+
+/**
+ * Serializes the members of a set.
+ */
+void t_dart_generator::generate_serialize_set_element(ofstream& out, t_set* tset, string iter) {
+  t_field efield(tset->get_elem_type(), iter);
+  generate_serialize_field(out, &efield, "");
+}
+
+/**
+ * Serializes the members of a list.
+ */
+void t_dart_generator::generate_serialize_list_element(ofstream& out, t_list* tlist, string iter) {
+  t_field efield(tlist->get_elem_type(), iter);
+  generate_serialize_field(out, &efield, "");
+}
+
+/**
+ * Returns a Dart type name
+ *
+ * @param ttype The type
+ * @return Dart type name, i.e. Map<Key, Value>
+ */
+string t_dart_generator::type_name(t_type* ttype) {
+  ttype = get_true_type(ttype);
+
+  if (ttype->is_base_type()) {
+    return base_type_name((t_base_type*)ttype);
+  } else if (ttype->is_enum()) {
+    return "int";
+  } else if (ttype->is_map()) {
+    t_map* tmap = (t_map*)ttype;
+    return "Map<" + type_name(tmap->get_key_type()) + ", "
+                  + type_name(tmap->get_val_type()) + ">";
+  } else if (ttype->is_set()) {
+    t_set* tset = (t_set*)ttype;
+    return "Set<" + type_name(tset->get_elem_type()) + ">";
+  } else if (ttype->is_list()) {
+    t_list* tlist = (t_list*)ttype;
+    return "List<" + type_name(tlist->get_elem_type()) + ">";
+  }
+
+  return ttype->get_name();
+}
+
+/**
+ * Returns the Dart type that corresponds to the thrift type.
+ *
+ * @param tbase The base type
+ */
+string t_dart_generator::base_type_name(t_base_type* type) {
+  t_base_type::t_base tbase = type->get_base();
+
+  switch (tbase) {
+  case t_base_type::TYPE_VOID:
+    return "void";
+  case t_base_type::TYPE_STRING:
+    if (type->is_binary()) {
+      return "Uint8List";
+    } else {
+      return "String";
+    }
+  case t_base_type::TYPE_BOOL:
+    return "bool";
+  case t_base_type::TYPE_BYTE:
+  case t_base_type::TYPE_I16:
+  case t_base_type::TYPE_I32:
+  case t_base_type::TYPE_I64:
+    return "int";
+  case t_base_type::TYPE_DOUBLE:
+    return "double";
+  default:
+    throw "compiler error: no Dart name for base type " + t_base_type::t_base_name(tbase);
+  }
+}
+
+/**
+ * Declares a field, which may include initialization as necessary.
+ *
+ * @param ttype The type
+ */
+string t_dart_generator::declare_field(t_field* tfield, bool init) {
+  string field_name = get_field_name(tfield->get_name());
+  string result = type_name(tfield->get_type()) + " " + field_name;
+  if (init) {
+    t_type* ttype = get_true_type(tfield->get_type());
+    if (ttype->is_base_type() && tfield->get_value() != NULL) {
+      ofstream dummy;
+      result += " = " + render_const_value(dummy, field_name, ttype, tfield->get_value());
+    } else if (ttype->is_base_type()) {
+      t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
+      switch (tbase) {
+      case t_base_type::TYPE_VOID:
+        throw "NO T_VOID CONSTRUCT";
+      case t_base_type::TYPE_STRING:
+        result += " = null";
+        break;
+      case t_base_type::TYPE_BOOL:
+        result += " = false";
+        break;
+      case t_base_type::TYPE_BYTE:
+      case t_base_type::TYPE_I16:
+      case t_base_type::TYPE_I32:
+      case t_base_type::TYPE_I64:
+        result += " = 0";
+        break;
+      case t_base_type::TYPE_DOUBLE:
+        result += " = 0.0";
+        break;
+      }
+
+    } else if (ttype->is_enum()) {
+      result += " = 0";
+    } else if (ttype->is_container()) {
+      result += " = new " + type_name(ttype) + "()";
+    } else {
+      result += " = new " + type_name(ttype) + "()";
+      ;
+    }
+  }
+  return result + ";";
+}
+
+/**
+ * Renders a function signature of the form 'type name(args)'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_dart_generator::function_signature(t_function* tfunction) {
+  std::string arguments = argument_list(tfunction->get_arglist());
+
+  std::string returntype;
+  if (tfunction->get_returntype()->is_void()) {
+    returntype = "Future";
+  } else {
+    returntype = "Future<" + type_name(tfunction->get_returntype()) + ">";
+  }
+
+  std::string result = returntype + " "  + tfunction->get_name() + "(" + arguments + ")";
+  return result;
+}
+
+/**
+ * Renders a comma separated field list, with type names
+ */
+string t_dart_generator::argument_list(t_struct* tstruct) {
+  string result = "";
+
+  const vector<t_field*>& fields = tstruct->get_members();
+  vector<t_field*>::const_iterator f_iter;
+  bool first = true;
+  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+    if (first) {
+      first = false;
+    } else {
+      result += ", ";
+    }
+    string field_name = get_field_name((*f_iter)->get_name());
+    result += type_name((*f_iter)->get_type()) + " " + field_name;
+  }
+  return result;
+}
+
+/**
+ * Converts the parse type to a C++ enum string for the given type.
+ */
+string t_dart_generator::type_to_enum(t_type* type) {
+  type = get_true_type(type);
+
+  if (type->is_base_type()) {
+    t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+    switch (tbase) {
+    case t_base_type::TYPE_VOID:
+      throw "NO T_VOID CONSTRUCT";
+    case t_base_type::TYPE_STRING:
+      return "TType.STRING";
+    case t_base_type::TYPE_BOOL:
+      return "TType.BOOL";
+    case t_base_type::TYPE_BYTE:
+      return "TType.BYTE";
+    case t_base_type::TYPE_I16:
+      return "TType.I16";
+    case t_base_type::TYPE_I32:
+      return "TType.I32";
+    case t_base_type::TYPE_I64:
+      return "TType.I64";
+    case t_base_type::TYPE_DOUBLE:
+      return "TType.DOUBLE";
+    }
+  } else if (type->is_enum()) {
+    return "TType.I32";
+  } else if (type->is_struct() || type->is_xception()) {
+    return "TType.STRUCT";
+  } else if (type->is_map()) {
+    return "TType.MAP";
+  } else if (type->is_set()) {
+    return "TType.SET";
+  } else if (type->is_list()) {
+    return "TType.LIST";
+  }
+
+  throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+std::string t_dart_generator::get_cap_name(std::string name) {
+  name[0] = toupper(name[0]);
+  return name;
+}
+
+std::string t_dart_generator::get_field_name(std::string name) {
+  name[0] = tolower(name[0]);
+  return name;
+}
+
+std::string t_dart_generator::get_args_class_name(std::string name) {
+  return name + "_args";
+}
+
+std::string t_dart_generator::get_result_class_name(std::string name) {
+  return name + "_result";
+}
+
+std::string t_dart_generator::get_file_name(std::string name) {
+  // e.g. change APIForFileIO to api_for_file_io
+
+  string ret;
+  const char* tmp = name.c_str();
+  bool is_prev_lc = true;
+  bool is_current_lc = tmp[0] == tolower(tmp[0]);
+  bool is_next_lc = false;
+
+  for (unsigned int i = 0; i < name.length(); i++) {
+    char lc = tolower(tmp[i]);
+
+    if (i == name.length() - 1) {
+      is_next_lc = false;
+    } else {
+      is_next_lc = (tmp[i+1] == tolower(tmp[i+1]));
+    }
+
+    if (i != 0 && !is_current_lc && (is_prev_lc || is_next_lc)) {
+      ret += "_";
+    }
+    ret += lc;
+
+    is_prev_lc = is_current_lc;
+    is_current_lc = is_next_lc;
+  }
+
+  return ret;
+}
+
+string t_dart_generator::constant_name(string name) {
+  string constant_name;
+
+  bool is_first = true;
+  bool was_previous_char_upper = false;
+  for (string::iterator iter = name.begin(); iter != name.end(); ++iter) {
+    string::value_type character = (*iter);
+
+    bool is_upper = isupper(character);
+
+    if (is_upper && !is_first && !was_previous_char_upper) {
+      constant_name += '_';
+    }
+    constant_name += toupper(character);
+
+    is_first = false;
+    was_previous_char_upper = is_upper;
+  }
+
+  return constant_name;
+}
+
+/**
+ * Emits a doc comment if the provided object has a doc in Thrift
+ */
+void t_dart_generator::generate_dart_doc(ofstream& out, t_doc* tdoc) {
+  if (tdoc->has_doc()) {
+    generate_docstring_comment(out, "", "/// ", tdoc->get_doc(), "");
+  }
+}
+
+/**
+ * Emits a doc comment if the provided function object has a doc in Thrift
+ */
+void t_dart_generator::generate_dart_doc(ofstream& out, t_function* tfunction) {
+  if (tfunction->has_doc()) {
+    stringstream ss;
+    ss << tfunction->get_doc();
+    const vector<t_field*>& fields = tfunction->get_arglist()->get_members();
+    vector<t_field*>::const_iterator p_iter;
+    for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) {
+      t_field* p = *p_iter;
+      string field_name = get_field_name(p->get_name());
+      ss << "\n@param " << field_name;
+      if (p->has_doc()) {
+        ss << " " << p->get_doc();
+      }
+    }
+    generate_docstring_comment(out, "", "/// ", ss.str(), "");
+  }
+}
+
+std::string t_dart_generator::generate_isset_check(t_field* field) {
+  string field_name = get_field_name(field->get_name());
+  return generate_isset_check(field_name);
+}
+
+std::string t_dart_generator::generate_isset_check(std::string field_name) {
+  return "is" + get_cap_name("set") + get_cap_name(field_name) + "()";
+}
+
+void t_dart_generator::generate_isset_set(ofstream& out, t_field* field) {
+  if (!type_can_be_null(field->get_type())) {
+    string field_name = get_field_name(field->get_name());
+    indent(out) << "this.__isset_" << field_name << " = true;" << endl;
+  }
+}
+
+std::string t_dart_generator::get_enum_class_name(t_type* type) {
+  return type->get_name();
+}
+
+THRIFT_REGISTER_GENERATOR(
+    dart,
+    "Dart",
+    "    library_name=my_library    Optional override for library name.\n"
+)

http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 880cc32..e6b3be0 100755
--- a/configure.ac
+++ b/configure.ac
@@ -122,6 +122,7 @@ if test "$enable_libs" = "no"; then
   with_perl="no"
   with_php="no"
   with_php_extension="no"
+  with_dart="no"
   with_erlang="no"
   with_go="no"
   with_d="no"
@@ -325,6 +326,16 @@ AM_CONDITIONAL(WITH_PHP_EXTENSION, [test "$have_php_extension" = "yes"])
 AC_PATH_PROG([PHPUNIT], [phpunit])
 AM_CONDITIONAL(HAVE_PHPUNIT, [test "x$PHPUNIT" != "x"])
 
+AX_THRIFT_LIB(dart, [DART], yes)
+if test "$with_dart" = "yes"; then
+  AC_PATH_PROG([DART], [dart])
+  AC_PATH_PROG([DARTPUB], [pub])
+  if test "x$DART" != "x" -a "x$DARTPUB" != "x"; then
+    have_dart="yes"
+  fi
+fi
+AM_CONDITIONAL(WITH_DART, [test "$have_dart" = "yes"])
+
 AX_THRIFT_LIB(ruby, [Ruby], yes)
 have_ruby=no
 if test "$with_ruby" = "yes"; then
@@ -727,6 +738,7 @@ AC_CONFIG_FILES([
   lib/perl/test/Makefile
   lib/php/Makefile
   lib/php/test/Makefile
+  lib/dart/Makefile
   lib/py/Makefile
   lib/rb/Makefile
   lib/lua/Makefile
@@ -738,6 +750,7 @@ AC_CONFIG_FILES([
   test/haxe/Makefile
   test/hs/Makefile
   test/php/Makefile
+  test/dart/Makefile
   test/perl/Makefile
   test/py/Makefile
   test/py.twisted/Makefile
@@ -752,6 +765,7 @@ AC_CONFIG_FILES([
   tutorial/java/Makefile
   tutorial/js/Makefile
   tutorial/nodejs/Makefile
+  tutorial/dart/Makefile
   tutorial/py/Makefile
   tutorial/py.twisted/Makefile
   tutorial/py.tornado/Makefile
@@ -776,6 +790,8 @@ if test "$have_perl" = "yes" ; then MAYBE_PERL="perl" ; else MAYBE_PERL="" ; fi
 AC_SUBST([MAYBE_PERL])
 if test "$have_php" = "yes" ; then MAYBE_PHP="php" ; else MAYBE_PHP="" ; fi
 AC_SUBST([MAYBE_PHP])
+if test "$have_dart" = "yes" ; then MAYBE_DART="dart" ; else MAYBE_DART="" ; fi
+AC_SUBST([MAYBE_DART])
 if test "$have_go" = "yes" ; then MAYBE_GO="go" ; else MAYBE_GO="" ; fi
 AC_SUBST([MAYBE_GO])
 if test "$have_nodejs" = "yes" ; then MAYBE_NODEJS="nodejs" ; else MAYBE_NODEJS="" ; fi
@@ -797,6 +813,7 @@ echo "Building Haxe Library ........ : $have_haxe"
 echo "Building Haskell Library ..... : $have_haskell"
 echo "Building Perl Library ........ : $have_perl"
 echo "Building PHP Library ......... : $have_php"
+echo "Building Dart Library ........ : $have_dart"
 echo "Building Erlang Library ...... : $have_erlang"
 echo "Building Go Library .......... : $have_go"
 echo "Building D Library ........... : $have_d"
@@ -834,6 +851,12 @@ if test "$have_php" = "yes" ; then
   echo "PHP Library:"
   echo "   Using php-config .......... : $PHP_CONFIG"
 fi
+if test "$have_dart" = "yes" ; then
+  echo
+  echo "Dart Library:"
+  echo "   Using Dart ................ : $DART"
+  echo "   Using Pub ................. : $DARTPUB"
+fi
 if test "$have_ruby" = "yes" ; then
   echo
   echo "Ruby Library:"