Dart: Adds optional named arguments for creating bindings.

Also:
- Ensures that Application and _ApplicationImpl clean up state when the other end goes down.
- Renames generated Stubs' "delegate" field to "impl".
- Plumbs doClosed callback through ApplicationConnection
- Plumbs resolvedUrl through Application.

BUG=
R=hansmuller@google.com

Review URL: https://codereview.chromium.org/968243003
diff --git a/examples/dart/hello_world/hello/main.dart b/examples/dart/hello_world/hello/main.dart
index 4dcbd4c..fda1741 100644
--- a/examples/dart/hello_world/hello/main.dart
+++ b/examples/dart/hello_world/hello/main.dart
@@ -14,10 +14,16 @@
 
   void initialize(List<String> args, String url) {
     print("$url Hello");
+
     // We expect to find the world mojo application at the same
     // path as this application.
-    connectToApplication(url.replaceAll("hello", "world"));
-    close();
+    var c = connectToApplication(url.replaceAll("hello", "world"));
+
+    // A call to close() here would cause this app to go down before the "world"
+    // app has a chance to come up. Instead, we wait to close this app until
+    // the "world" app comes up, does its print, and closes its end of the
+    // connection.
+    c.listen(onClosed: close);
   }
 }
 
diff --git a/mojo/dart/embedder/test/dart_to_cpp_tests.dart b/mojo/dart/embedder/test/dart_to_cpp_tests.dart
index c93d82d..f9621ae 100644
--- a/mojo/dart/embedder/test/dart_to_cpp_tests.dart
+++ b/mojo/dart/embedder/test/dart_to_cpp_tests.dart
@@ -22,9 +22,7 @@
   Completer _completer;
 
   DartSideImpl(core.MojoMessagePipeEndpoint endpoint) {
-    _stub = new DartSideStub.fromEndpoint(endpoint)
-            ..delegate = this
-            ..listen();
+    _stub = new DartSideStub.fromEndpoint(endpoint, impl: this);
     _sampleData = new Uint8List(CAPACITY_BYTES);
     for (int i = 0; i < _sampleData.length; ++i) {
       _sampleData[i] = i;
diff --git a/mojo/dart/test/bindings_generation_test.dart b/mojo/dart/test/bindings_generation_test.dart
index e1bddef..5943fef 100644
--- a/mojo/dart/test/bindings_generation_test.dart
+++ b/mojo/dart/test/bindings_generation_test.dart
@@ -9,17 +9,17 @@
 import 'mojo:core' as core;
 
 import 'package:mojo/dart/testing/expect.dart';
-import 'package:mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.dart' as sample;
-import 'package:mojo/public/interfaces/bindings/tests/test_structs.mojom.dart' as structs;
+import 'package:mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.dart'
+    as sample;
+import 'package:mojo/public/interfaces/bindings/tests/test_structs.mojom.dart'
+    as structs;
 import 'package:mojo/public/interfaces/bindings/tests/rect.mojom.dart' as rect;
 
 class ProviderImpl implements sample.Provider {
   sample.ProviderStub _stub;
 
   ProviderImpl(core.MojoMessagePipeEndpoint endpoint) {
-    _stub = new sample.ProviderStub.fromEndpoint(endpoint)
-            ..delegate = this
-            ..listen();
+    _stub = new sample.ProviderStub.fromEndpoint(endpoint, impl: this);
   }
 
   echoString(String a, Function responseFactory) =>
@@ -69,8 +69,7 @@
   var echoStringResponse = await client.ptr.echoString("hello!");
   Expect.equals("hello!", echoStringResponse.a);
 
-  var echoStringsResponse =
-      await client.ptr.echoStrings("hello", "mojo!");
+  var echoStringsResponse = await client.ptr.echoStrings("hello", "mojo!");
   Expect.equals("hello", echoStringsResponse.a);
   Expect.equals("mojo!", echoStringsResponse.b);
 
@@ -79,18 +78,18 @@
 
 
 bindings.ServiceMessage messageOfStruct(bindings.Struct s) =>
-  s.serializeWithHeader(new bindings.MessageHeader(0));
+    s.serializeWithHeader(new bindings.MessageHeader(0));
 
 
 testSerializeNamedRegion() {
   var r = new rect.Rect()
-          ..x = 1
-          ..y = 2
-          ..width = 3
-          ..height = 4;
+      ..x = 1
+      ..y = 2
+      ..width = 3
+      ..height = 4;
   var namedRegion = new structs.NamedRegion()
-                    ..name = "name"
-                    ..rects = [r];
+      ..name = "name"
+      ..rects = [r];
   var message = messageOfStruct(namedRegion);
   var namedRegion2 = structs.NamedRegion.deserialize(message.payload);
   Expect.equals(namedRegion.name, namedRegion2.name);
@@ -99,12 +98,12 @@
 
 testSerializeArrayValueTypes() {
   var arrayValues = new structs.ArrayValueTypes()
-                    ..f0 = [0, 1, -1, 0x7f, -0x10]
-                    ..f1 = [0, 1, -1, 0x7fff, -0x1000]
-                    ..f2 = [0, 1, -1, 0x7fffffff, -0x10000000]
-                    ..f3 = [0, 1, -1, 0x7fffffffffffffff, -0x1000000000000000]
-                    ..f4 = [0.0, 1.0, -1.0, 4.0e9, -4.0e9]
-                    ..f5 = [0.0, 1.0, -1.0, 4.0e9, -4.0e9];
+      ..f0 = [0, 1, -1, 0x7f, -0x10]
+      ..f1 = [0, 1, -1, 0x7fff, -0x1000]
+      ..f2 = [0, 1, -1, 0x7fffffff, -0x10000000]
+      ..f3 = [0, 1, -1, 0x7fffffffffffffff, -0x1000000000000000]
+      ..f4 = [0.0, 1.0, -1.0, 4.0e9, -4.0e9]
+      ..f5 = [0.0, 1.0, -1.0, 4.0e9, -4.0e9];
   var message = messageOfStruct(arrayValues);
   var arrayValues2 = structs.ArrayValueTypes.deserialize(message.payload);
   Expect.listEquals(arrayValues.f0, arrayValues2.f0);
diff --git a/mojo/dart/test/interface_test.dart b/mojo/dart/test/interface_test.dart
index ca34dcc..18c4733 100644
--- a/mojo/dart/test/interface_test.dart
+++ b/mojo/dart/test/interface_test.dart
@@ -78,7 +78,8 @@
 const int kEchoStringResponse_name = 1;
 
 class EchoStub extends bindings.Stub {
-  EchoStub(core.MojoMessagePipeEndpoint endpoint) : super(endpoint);
+  EchoStub(core.MojoMessagePipeEndpoint endpoint)
+      : super.fromEndpoint(endpoint);
 
   Future<EchoStringResponse> echoString(EchoString es) {
     var response = new EchoStringResponse();
@@ -110,7 +111,9 @@
 
 
 class EchoProxy extends bindings.Proxy {
-  EchoProxy(core.MojoMessagePipeEndpoint endpoint) : super(endpoint);
+  EchoProxy(core.MojoMessagePipeEndpoint endpoint, {bool doListen: true,
+      Function onClosed})
+      : super.fromEndpoint(endpoint, doListen: doListen, onClosed: onClosed);
 
   Future<EchoStringResponse> echoString(String a) {
     // compose message.
@@ -140,8 +143,7 @@
 
 
 void providerIsolate(core.MojoMessagePipeEndpoint endpoint) {
-  var provider = new EchoStub(endpoint);
-  provider.listen();
+  new EchoStub(endpoint);
 }
 
 
@@ -193,12 +195,10 @@
   var testCompleter = new Completer();
 
   var pipe = new core.MojoMessagePipe();
-  var proxy = new EchoProxy(pipe.endpoints[0]);
-  await Isolate.spawn(closingProviderIsolate, pipe.endpoints[1]);
-  proxy.listen(onClosed: () {
+  var proxy = new EchoProxy(pipe.endpoints[0], onClosed: () {
     testCompleter.complete(true);
   });
-
+  await Isolate.spawn(closingProviderIsolate, pipe.endpoints[1]);
   return testCompleter.future.timeout(
       new Duration(seconds: 1),
       onTimeout: () => false);
diff --git a/mojo/dart/test/validation_test.dart b/mojo/dart/test/validation_test.dart
index 978a5bb..0c2ff4b 100644
--- a/mojo/dart/test/validation_test.dart
+++ b/mojo/dart/test/validation_test.dart
@@ -18,10 +18,8 @@
   Completer _completer;
 
   ConformanceTestInterfaceImpl(this._completer,
-                              MojoMessagePipeEndpoint endpoint) {
-    _stub = new ConformanceTestInterfaceStub.fromEndpoint(endpoint)
-            ..delegate = this
-            ..listen();
+      MojoMessagePipeEndpoint endpoint) {
+    _stub = new ConformanceTestInterfaceStub.fromEndpoint(endpoint, impl: this);
   }
 
   void _complete() => _completer.complete(null);
@@ -38,7 +36,7 @@
   method9(List<List<MojoHandle>> param0) => _complete();
   method10(Map<String, int> param0) => _complete();
 
-  void close({bool nodefer : false}) => _stub.close(nodefer: nodefer);
+  void close({bool nodefer: false}) => _stub.close(nodefer: nodefer);
 }
 
 parser.ValidationParseResult readAndParseTest(String test) {
@@ -53,17 +51,17 @@
 }
 
 runTest(String name, parser.ValidationParseResult test, String expected) {
-  var handles = new List.generate(test.numHandles,
+  var handles = new List.generate(
+      test.numHandles,
       (_) => new MojoSharedBuffer.create(10).handle);
   var pipe = new MojoMessagePipe();
   var completer = new Completer();
   var conformanceImpl;
 
   runZoned(() {
-    conformanceImpl = new ConformanceTestInterfaceImpl(
-        completer, pipe.endpoints[0]);
-  },
-  onError: (e, stackTrace) {
+    conformanceImpl =
+        new ConformanceTestInterfaceImpl(completer, pipe.endpoints[0]);
+  }, onError: (e, stackTrace) {
     assert(e is MojoCodecError);
     // TODO(zra): Make the error messages conform?
     // assert(e == expected);
@@ -87,10 +85,12 @@
 // TODO(zra, yzshen): Some struct versioning tests (with "mthd11" in their
 // names) are skipped.
 Iterable<String> getTestFiles(String path, String prefix) =>
-    builtin.enumerateFiles(path)
-           .where((s) => s.startsWith(prefix) && s.endsWith(".data") &&
-                         !s.contains("mthd11"))
-           .map((s) => s.replaceFirst('.data', ''));
+    builtin.enumerateFiles(
+        path).where(
+            (s) =>
+                s.startsWith(prefix) &&
+                    s.endsWith(".data") &&
+                    !s.contains("mthd11")).map((s) => s.replaceFirst('.data', ''));
 
 main(List args) {
   int handle = args[0];
diff --git a/mojo/public/dart/src/application.dart b/mojo/public/dart/src/application.dart
index 6e61df6..7588209 100644
--- a/mojo/public/dart/src/application.dart
+++ b/mojo/public/dart/src/application.dart
@@ -10,18 +10,23 @@
   Application _application;
 
   _ApplicationImpl(Application application,
-      core.MojoMessagePipeEndpoint endpoint) {
+      core.MojoMessagePipeEndpoint endpoint, {Function onClosed}) {
     _application = application;
-    _stub = new application_mojom.ApplicationStub.fromEndpoint(endpoint)
-        ..delegate = this
-        ..listen();
+    // We wrap the onClosed callback in a closure to ensure that all
+    // necessary cleanup is performed on a PEER_CLOSED signal.
+    _stub = new application_mojom.ApplicationStub.fromEndpoint(
+        endpoint,
+        impl: this,
+        onClosed: _closer(onClosed));
   }
 
-  _ApplicationImpl.fromHandle(Application application, core.MojoHandle handle) {
+  _ApplicationImpl.fromHandle(Application application, core.MojoHandle handle,
+      {Function onClosed}) {
     _application = application;
-    _stub = new application_mojom.ApplicationStub.fromHandle(handle)
-        ..delegate = this
-        ..listen();
+    _stub = new application_mojom.ApplicationStub.fromHandle(
+        handle,
+        impl: this,
+        onClosed: _closer(onClosed));
   }
 
   void initialize(bindings.ProxyBase shellProxy, List<String> args,
@@ -34,13 +39,28 @@
   @override
   void acceptConnection(String requestorUrl, ServiceProviderStub services,
       bindings.ProxyBase exposedServices, String resolvedUrl) =>
-      _application._acceptConnection(requestorUrl, services, exposedServices,
-                                     resolvedUrl);
+      _application._acceptConnection(
+          requestorUrl,
+          services,
+          exposedServices,
+          resolvedUrl);
 
   @override
   void requestQuit() => _application._requestQuitAndClose();
 
-  void close({bool nodefer: false}) => shell.close();
+  Function _closer(Function onClosed) {
+    return (() {
+      if (onClosed != null) {
+        onClosed();
+      }
+      close();
+    });
+  }
+
+  void close({bool nodefer: false}) {
+    shell.close();
+    _stub.close();
+  }
 }
 
 // TODO(zra): Better documentation and examples.
@@ -52,14 +72,18 @@
   _ApplicationImpl _applicationImpl;
   List<ApplicationConnection> _applicationConnections;
 
-  Application(core.MojoMessagePipeEndpoint endpoint) {
+  Application(core.MojoMessagePipeEndpoint endpoint, {Function onClosed}) {
     _applicationConnections = [];
-    _applicationImpl = new _ApplicationImpl(this, endpoint);
+    // We wrap the onClosed callback in a closure to ensure that all
+    // necessary cleanup is performed on a PEER_CLOSED signal.
+    _applicationImpl =
+        new _ApplicationImpl(this, endpoint, onClosed: _closer(onClosed));
   }
 
-  Application.fromHandle(core.MojoHandle appHandle) {
+  Application.fromHandle(core.MojoHandle appHandle, {Function onClosed}) {
     _applicationConnections = [];
-    _applicationImpl = new _ApplicationImpl.fromHandle(this, appHandle);
+    _applicationImpl =
+        new _ApplicationImpl.fromHandle(this, appHandle, onClosed: _closer(onClosed));
   }
 
   void initialize(List<String> args, String url) {}
@@ -91,6 +115,15 @@
     close();
   }
 
+  Function _closer(Function onClose) {
+    return (() {
+      if (onClose != null) {
+        onClose();
+      }
+      close();
+    });
+  }
+
   void close() {
     assert(_applicationImpl != null);
     _applicationConnections.forEach((c) => c.close());
@@ -109,6 +142,6 @@
   // If you provide at least one service or set fallbackServiceProvider,
   // then you must invoke connection.listen().
   void acceptConnection(String requestorUrl, String resolvedUrl,
-                        ApplicationConnection connection) {
+      ApplicationConnection connection) {
   }
 }
diff --git a/mojo/public/dart/src/application_connection.dart b/mojo/public/dart/src/application_connection.dart
index 4f0b6c8..5d98896 100644
--- a/mojo/public/dart/src/application_connection.dart
+++ b/mojo/public/dart/src/application_connection.dart
@@ -14,12 +14,21 @@
   ServiceProviderStub _stub;
 
   LocalServiceProvider(this.connection, this._stub) {
-    _stub.delegate = this;
+    _stub.impl = this;
   }
 
-  listen() => _stub.listen();
+  listen({Function onClosed}) => _stub.listen(onClosed: _closer(onClosed));
 
-  void close({bool nodefer : false}) => _stub.close(nodefer: nodefer);
+  Function _closer(Function onClosed) {
+    return (() {
+      if (onClosed != null) {
+        onClosed();
+      }
+      close();
+    });
+  }
+
+  void close({bool nodefer: false}) => _stub.close(nodefer: nodefer);
 
   void connectToService(String interfaceName,
       core.MojoMessagePipeEndpoint pipe) {
@@ -71,7 +80,7 @@
   }
 
   FallbackServiceFactory get fallbackServiceFactory => _fallbackServiceFactory;
-                         set fallbackServiceFactory(FallbackServiceFactory f) {
+  set fallbackServiceFactory(FallbackServiceFactory f) {
     assert(_localServiceProvider != null);
     _fallbackServiceFactory = f;
   }
@@ -82,8 +91,7 @@
         remoteServiceProvider.impl.isBound);
     var pipe = new core.MojoMessagePipe();
     proxy.impl.bind(pipe.endpoints[0]);
-    remoteServiceProvider.ptr.connectToService(
-        proxy.name, pipe.endpoints[1]);
+    remoteServiceProvider.ptr.connectToService(proxy.name, pipe.endpoints[1]);
     return proxy;
   }
 
@@ -92,9 +100,18 @@
     _nameToServiceFactory[interfaceName] = factory;
   }
 
-  void listen() {
+  void listen({Function onClosed}) {
     assert(_localServiceProvider != null);
-    _localServiceProvider.listen();
+    _localServiceProvider.listen(onClosed: _closer(onClosed));
+  }
+
+  Function _closer(Function onClosed) {
+    return (() {
+      if (onClosed != null) {
+        onClosed();
+      }
+      close();
+    });
   }
 
   void close({bool nodefer: false}) {
diff --git a/mojo/public/dart/src/event_stream.dart b/mojo/public/dart/src/event_stream.dart
index 8f022ac..9c83217 100644
--- a/mojo/public/dart/src/event_stream.dart
+++ b/mojo/public/dart/src/event_stream.dart
@@ -132,16 +132,26 @@
   MojoEventStream _eventStream;
   bool _isOpen = false;
   bool _isInHandler = false;
+  StreamSubscription subscription;
 
-  MojoEventStreamListener(MojoMessagePipeEndpoint endpoint)
+  MojoEventStreamListener.fromEndpoint(MojoMessagePipeEndpoint endpoint,
+      {bool doListen: true, Function onClosed})
       : _endpoint = endpoint,
         _eventStream = new MojoEventStream(endpoint.handle),
-        _isOpen = false;
+        _isOpen = false {
+    if (doListen) {
+      listen(onClosed: onClosed);
+    }
+  }
 
-  MojoEventStreamListener.fromHandle(MojoHandle handle) {
+  MojoEventStreamListener.fromHandle(MojoHandle handle, {bool doListen: true,
+      Function onClosed}) {
     _endpoint = new MojoMessagePipeEndpoint(handle);
     _eventStream = new MojoEventStream(handle);
     _isOpen = false;
+    if (doListen) {
+      listen(onClosed: onClosed);
+    }
   }
 
   MojoEventStreamListener.unbound()
@@ -164,8 +174,9 @@
   }
 
   StreamSubscription<List<int>> listen({Function onClosed}) {
+    assert(isBound && (subscription == null));
     _isOpen = true;
-    return _eventStream.listen((List<int> event) {
+    subscription = _eventStream.listen((List<int> event) {
       var signalsWatched = new MojoHandleSignals(event[0]);
       var signalsReceived = new MojoHandleSignals(event[1]);
       if (signalsReceived.isPeerClosed) {
@@ -190,11 +201,13 @@
       }
       _isInHandler = false;
     }, onDone: close);
+    return subscription;
   }
 
   void close() {
     _isOpen = false;
     _endpoint = null;
+    subscription = null;
     if (_eventStream != null) {
       _eventStream.close();
       _eventStream = null;
diff --git a/mojo/public/dart/src/proxy.dart b/mojo/public/dart/src/proxy.dart
index f7c770f..6b00cd7 100644
--- a/mojo/public/dart/src/proxy.dart
+++ b/mojo/public/dart/src/proxy.dart
@@ -8,17 +8,19 @@
   Map<int, Completer> _completerMap;
   int _nextId = 0;
 
-  Proxy(core.MojoMessagePipeEndpoint endpoint) :
-      _completerMap = {},
-      super(endpoint);
+  Proxy.fromEndpoint(core.MojoMessagePipeEndpoint endpoint, {bool doListen:
+      true, Function onClosed})
+      : _completerMap = {},
+        super.fromEndpoint(endpoint, doListen: doListen, onClosed: onClosed);
 
-  Proxy.fromHandle(core.MojoHandle handle) :
-      _completerMap = {},
-      super.fromHandle(handle);
+  Proxy.fromHandle(core.MojoHandle handle, {bool doListen: true,
+      Function onClosed})
+      : _completerMap = {},
+        super.fromHandle(handle, doListen: doListen, onClosed: onClosed);
 
-  Proxy.unbound() :
-      _completerMap = {},
-      super.unbound();
+  Proxy.unbound()
+      : _completerMap = {},
+        super.unbound();
 
   void handleResponse(ServiceMessage reader);
 
@@ -46,16 +48,16 @@
     }
     var header = new MessageHeader(name);
     var serviceMessage = message.serializeWithHeader(header);
-    endpoint.write(serviceMessage.buffer,
-                   serviceMessage.buffer.lengthInBytes,
-                   serviceMessage.handles);
+    endpoint.write(
+        serviceMessage.buffer,
+        serviceMessage.buffer.lengthInBytes,
+        serviceMessage.handles);
     if (!endpoint.status.isOk) {
       throw "message pipe write failed - ${endpoint.status}";
     }
   }
 
-  Future sendMessageWithRequestId(
-      Struct message, int name, int id, int flags) {
+  Future sendMessageWithRequestId(Struct message, int name, int id, int flags) {
     if (!isOpen) {
       listen();
     }
@@ -65,9 +67,10 @@
 
     var header = new MessageHeader.withRequestId(name, flags, id);
     var serviceMessage = message.serializeWithHeader(header);
-    endpoint.write(serviceMessage.buffer,
-                   serviceMessage.buffer.lengthInBytes,
-                   serviceMessage.handles);
+    endpoint.write(
+        serviceMessage.buffer,
+        serviceMessage.buffer.lengthInBytes,
+        serviceMessage.handles);
     if (!endpoint.status.isOk) {
       throw "message pipe write failed - ${endpoint.status}";
     }
diff --git a/mojo/public/dart/src/stub.dart b/mojo/public/dart/src/stub.dart
index 1ce0982..f11747a 100644
--- a/mojo/public/dart/src/stub.dart
+++ b/mojo/public/dart/src/stub.dart
@@ -8,9 +8,13 @@
   int _outstandingResponseFutures = 0;
   bool _isClosing = false;
 
-  Stub(core.MojoMessagePipeEndpoint endpoint) : super(endpoint);
+  Stub.fromEndpoint(core.MojoMessagePipeEndpoint endpoint, {bool doListen: true,
+      Function onClosed})
+      : super.fromEndpoint(endpoint, doListen: doListen, onClosed: onClosed);
 
-  Stub.fromHandle(core.MojoHandle handle) : super.fromHandle(handle);
+  Stub.fromHandle(core.MojoHandle handle, {bool doListen: true,
+      Function onClosed})
+      : super.fromHandle(handle, doListen: doListen, onClosed: onClosed);
 
   Stub.unbound() : super.unbound();
 
diff --git a/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl
index cbd39ef..16004f1 100644
--- a/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl
@@ -38,10 +38,13 @@
 
 class {{interface|name}}ProxyImpl extends bindings.Proxy {
   {{interface|name}}ProxyImpl.fromEndpoint(
-      core.MojoMessagePipeEndpoint endpoint) : super(endpoint);
+      core.MojoMessagePipeEndpoint endpoint,
+      {bool doListen: true, Function onClosed}) :
+      super.fromEndpoint(endpoint, doListen: doListen, onClosed: onClosed);
 
-  {{interface|name}}ProxyImpl.fromHandle(core.MojoHandle handle) :
-      super.fromHandle(handle);
+  {{interface|name}}ProxyImpl.fromHandle(core.MojoHandle handle,
+      {bool doListen: true, Function onClosed}) :
+      super.fromHandle(handle, doListen: doListen, onClosed: onClosed);
 
   {{interface|name}}ProxyImpl.unbound() : super.unbound();
 
@@ -89,6 +92,7 @@
   {%- endfor -%}
   {%- set request_struct = method|struct_from_method -%}
     ) {
+      assert(_proxyImpl.isBound);
       var params = new {{request_struct|name}}();
   {%- for parameter in method.parameters %}
       params.{{parameter|name}} = {{parameter|name}};
@@ -103,6 +107,7 @@
       {{parameter.kind|dart_type}} {{parameter|name}},
   {%- endfor -%}
       [Function responseFactory = null]) {
+      assert(_proxyImpl.isBound);
       var params = new {{request_struct|name}}();
   {%- for parameter in method.parameters %}
       params.{{parameter|name}} = {{parameter|name}};
@@ -128,13 +133,17 @@
       ptr = new _{{interface|name}}ProxyCalls(proxyImpl);
 
   {{interface|name}}Proxy.fromEndpoint(
-      core.MojoMessagePipeEndpoint endpoint) :
-      impl = new {{interface|name}}ProxyImpl.fromEndpoint(endpoint) {
+      core.MojoMessagePipeEndpoint endpoint,
+      {bool doListen: true, Function onClosed}) :
+      impl = new {{interface|name}}ProxyImpl.fromEndpoint(
+          endpoint, doListen: doListen, onClosed: onClosed) {
     ptr = new _{{interface|name}}ProxyCalls(impl);
   }
 
-  {{interface|name}}Proxy.fromHandle(core.MojoHandle handle) :
-      impl = new {{interface|name}}ProxyImpl.fromHandle(handle) {
+  {{interface|name}}Proxy.fromHandle(core.MojoHandle handle,
+      {bool doListen: true, Function onClosed}) :
+      impl = new {{interface|name}}ProxyImpl.fromHandle(
+          handle, doListen: doListen, onClosed: onClosed) {
     ptr = new _{{interface|name}}ProxyCalls(impl);
   }
 
@@ -152,19 +161,37 @@
 
 
 class {{interface|name}}Stub extends bindings.Stub {
-  {{interface|name}} _delegate = null;
+  {{interface|name}} _impl = null;
 
-  {{interface|name}}Stub.fromEndpoint(core.MojoMessagePipeEndpoint endpoint) :
-      super(endpoint);
+  {{interface|name}}Stub.fromEndpoint(core.MojoMessagePipeEndpoint endpoint,
+      { {{interface|name}} impl, bool doListen: true, Function onClosed}) :
+      super.fromEndpoint(endpoint, doListen: false) {
+    assert(!doListen || (impl != null));
+    if (impl != null) {
+      this._impl = impl;
+    }
+    if (doListen) {
+      listen(onClosed: onClosed);
+    }
+  }
 
-  {{interface|name}}Stub.fromHandle(core.MojoHandle handle) :
-      super.fromHandle(handle);
+  {{interface|name}}Stub.fromHandle(core.MojoHandle handle,
+      { {{interface|name}} impl, bool doListen: true, Function onClosed}) :
+      super.fromHandle(handle, doListen: false) {
+    assert(!doListen || (impl != null));
+    if (impl != null) {
+      this._impl = impl;
+    }
+    if (doListen) {
+      listen(onClosed: onClosed);
+    }
+  }
 
   {{interface|name}}Stub.unbound() : super.unbound();
 
   static {{interface|name}}Stub newFromEndpoint(
       core.MojoMessagePipeEndpoint endpoint) =>
-      new {{interface|name}}Stub.fromEndpoint(endpoint);
+      new {{interface|name}}Stub.fromEndpoint(endpoint, doListen: false);
 
   static const String name = {{interface|name}}Name;
 
@@ -186,7 +213,7 @@
 {%- endfor %}
 
   Future<bindings.Message> handleMessage(bindings.ServiceMessage message) {
-    assert(_delegate != null);
+    assert(_impl != null);
     switch (message.header.type) {
 {%- for method in interface.methods %}
 {%- set request_struct = method|struct_from_method %}
@@ -194,14 +221,14 @@
         var params = {{request_struct|name}}.deserialize(
             message.payload);
 {%- if method.response_parameters == None %}
-        _delegate.{{method|name}}(
+        _impl.{{method|name}}(
           {%- for parameter in method.parameters -%}
               params.{{parameter|name}}{% if not loop.last %}, {% endif %}
           {%- endfor -%}
         );
 {%- else %}
 {%- set response_struct = method|response_struct_from_method %}
-        return _delegate.{{method|name}}(
+        return _impl.{{method|name}}(
           {%- for parameter in method.parameters -%}
               params.{{parameter|name}},
           {%- endfor -%}
@@ -224,9 +251,9 @@
     return null;
   }
 
-  {{interface|name}} get delegate => _delegate;
-      set delegate({{interface|name}} d) {
-    assert(_delegate == null);
-    _delegate = d;
+  {{interface|name}} get impl => _impl;
+      set impl({{interface|name}} d) {
+    assert(_impl == null);
+    _impl = d;
   }
 }
diff --git a/services/dart/test/echo/main.dart b/services/dart/test/echo/main.dart
index 1059221..5d32fb2 100644
--- a/services/dart/test/echo/main.dart
+++ b/services/dart/test/echo/main.dart
@@ -14,16 +14,13 @@
   Application _application;
 
   EchoServiceImpl(Application application, MojoMessagePipeEndpoint endpoint)
-    : _application = application {
-    _stub = new EchoServiceStub.fromEndpoint(endpoint)
-            ..delegate = this
-            ..listen();
+      : _application = application {
+    _stub = new EchoServiceStub.fromEndpoint(endpoint, impl: this);
   }
 
   echoString(String value, Function responseFactory) {
     if (value == "quit") {
       _stub.close();
-      _application.close();
     }
     return new Future.value(responseFactory(value));
   }
@@ -34,9 +31,10 @@
 
   @override
   void acceptConnection(String requestorUrl, String resolvedUrl,
-                        ApplicationConnection connection) {
-    connection.provideService(EchoServiceName, (endpoint) =>
-        new EchoServiceImpl(this, endpoint));
+      ApplicationConnection connection) {
+    connection.provideService(
+        EchoServiceName,
+        (endpoint) => new EchoServiceImpl(this, endpoint));
     connection.listen();
   }
 }
diff --git a/services/dart/test/pingpong/main.dart b/services/dart/test/pingpong/main.dart
index 9b04761..9f7485d 100644
--- a/services/dart/test/pingpong/main.dart
+++ b/services/dart/test/pingpong/main.dart
@@ -16,7 +16,7 @@
 
   PingPongClientImpl.unbound(this._count, this._completer)
       : stub = new PingPongClientStub.unbound() {
-    stub.delegate = this;
+    stub.impl = this;
   }
 
   void pong(int pongValue) {
@@ -34,19 +34,17 @@
 
   PingPongServiceImpl(Application application, MojoMessagePipeEndpoint endpoint)
       : _application = application {
-    _stub = new PingPongServiceStub.fromEndpoint(endpoint)
-            ..delegate = this
-            ..listen();
+    _stub = new PingPongServiceStub.fromEndpoint(endpoint, impl: this);
   }
 
   PingPongServiceImpl.fromStub(this._stub) {
-    _stub.delegate = this;
+    _stub.impl = this;
   }
 
   listen() => _stub.listen();
 
   void setClient(ProxyBase proxyBase) {
-    assert(_pingPongClient== null);
+    assert(_pingPongClient == null);
     _pingPongClient = proxyBase;
   }
 
@@ -61,7 +59,7 @@
       return responseFactory(false);
     }
     var completer = new Completer();
-    var pingPongService= new PingPongServiceProxy.unbound();
+    var pingPongService = new PingPongServiceProxy.unbound();
     _application.connectToService(url, pingPongService);
 
     var pingPongClient = new PingPongClientImpl.unbound(count, completer);
@@ -73,13 +71,13 @@
     }
     await completer.future;
     pingPongService.ptr.quit();
+    pingPongService.close();
 
     return responseFactory(true);
   }
 
-  Future pingTargetService(ProxyBase proxyBase,
-                           int count,
-                           Function responseFactory) async {
+  Future pingTargetService(ProxyBase proxyBase, int count,
+      Function responseFactory) async {
     var pingPongService = proxyBase;
     var completer = new Completer();
     var client = new PingPongClientImpl.unbound(count, completer);
@@ -91,6 +89,7 @@
     }
     await completer.future;
     pingPongService.ptr.quit();
+    pingPongService.close();
 
     return responseFactory(true);
   }
@@ -105,9 +104,7 @@
       _pingPongClient = null;
     }
     _stub.close();
-    if (_application != null) {
-      _application.close();
-    }
+    _stub = null;
   }
 }
 
@@ -116,8 +113,9 @@
 
   @override
   void acceptConnection(String requestorUrl, String resolvedUrl,
-                        ApplicationConnection connection) {
-    connection.provideService(PingPongServiceName,
+      ApplicationConnection connection) {
+    connection.provideService(
+        PingPongServiceName,
         (endpoint) => new PingPongServiceImpl(this, endpoint));
     connection.listen();
   }
diff --git a/services/dart/test/pingpong_target/main.dart b/services/dart/test/pingpong_target/main.dart
index 96bc0ed..a85e4a7 100644
--- a/services/dart/test/pingpong_target/main.dart
+++ b/services/dart/test/pingpong_target/main.dart
@@ -16,9 +16,7 @@
 
   PingPongServiceImpl(Application application, MojoMessagePipeEndpoint endpoint)
       : _application = application {
-    _stub = new PingPongServiceStub.fromEndpoint(endpoint)
-            ..delegate = this
-            ..listen();
+    _stub = new PingPongServiceStub.fromEndpoint(endpoint, impl: this);
   }
 
   void setClient(ProxyBase proxyBase) {
@@ -34,9 +32,6 @@
       _pingPongClient = null;
     }
     _stub.close();
-    if (_application != null) {
-      _application.close();
-    }
   }
 }
 
@@ -45,10 +40,12 @@
 
   @override
   void acceptConnection(String requestorUrl, String resolvedUrl,
-                        ApplicationConnection connection) {
-    connection.provideService(PingPongServiceName,
+      ApplicationConnection connection) {
+    connection.provideService(
+        PingPongServiceName,
         (endpoint) => new PingPongServiceImpl(this, endpoint));
-    connection.listen();
+    // Close the application when the first connection goes down.
+    connection.listen(onClosed: close);
   }
 }