| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 part of bindings; | 5 part of bindings; |
| 6 | 6 |
| 7 /// The object that [ProxyMessageHandler.errorFuture] completes with when there | 7 /// The object that [ProxyMessageHandler.errorFuture] completes with when there |
| 8 /// is an error. | 8 /// is an error. |
| 9 class ProxyError { | 9 class ProxyError { |
| 10 final String message; | 10 final String message; |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 62 /// bound Proxy. For example, every class extending the Application base class | 62 /// bound Proxy. For example, every class extending the Application base class |
| 63 /// in package:mojo/application.dart inherits an implementation of the | 63 /// in package:mojo/application.dart inherits an implementation of the |
| 64 /// ServiceConnector interface. | 64 /// ServiceConnector interface. |
| 65 abstract class ServiceConnector { | 65 abstract class ServiceConnector { |
| 66 /// Connects [proxy] to the service called [serviceName] that lives at [url]. | 66 /// Connects [proxy] to the service called [serviceName] that lives at [url]. |
| 67 void connectToService(String url, Proxy proxy, [String serviceName]); | 67 void connectToService(String url, Proxy proxy, [String serviceName]); |
| 68 } | 68 } |
| 69 | 69 |
| 70 abstract class ProxyMessageHandler extends core.MojoEventHandler | 70 abstract class ProxyMessageHandler extends core.MojoEventHandler |
| 71 implements MojoInterfaceControl { | 71 implements MojoInterfaceControl { |
| 72 HashMap<int, Completer> _completerMap = new HashMap<int, Completer>(); | 72 HashMap<int, Function> _callbackMap = new HashMap<int, Function>(); |
| 73 Completer _errorCompleter = new Completer(); | 73 Completer _errorCompleter = new Completer(); |
| 74 Set<Completer> _errorCompleters; | 74 Set<Completer> _errorCompleters; |
| 75 int _nextId = 0; | 75 int _nextId = 0; |
| 76 int _version = 0; | 76 int _version = 0; |
| 77 int _pendingCount = 0; | 77 int _pendingCount = 0; |
| 78 | 78 |
| 79 ProxyMessageHandler.fromEndpoint(core.MojoMessagePipeEndpoint endpoint) | 79 ProxyMessageHandler.fromEndpoint(core.MojoMessagePipeEndpoint endpoint) |
| 80 : super.fromEndpoint(endpoint); | 80 : super.fromEndpoint(endpoint); |
| 81 | 81 |
| 82 ProxyMessageHandler.fromHandle(core.MojoHandle handle) | 82 ProxyMessageHandler.fromHandle(core.MojoHandle handle) |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 124 } | 124 } |
| 125 } | 125 } |
| 126 | 126 |
| 127 @override | 127 @override |
| 128 void handleWrite() { | 128 void handleWrite() { |
| 129 proxyError("Unexpected writable signal"); | 129 proxyError("Unexpected writable signal"); |
| 130 } | 130 } |
| 131 | 131 |
| 132 @override | 132 @override |
| 133 Future close({bool immediate: false}) { | 133 Future close({bool immediate: false}) { |
| 134 // Drop the completers for outstanding calls. The Futures will never | 134 // Drop the callbacks for outstanding calls. They will never be called. |
| 135 // complete. | 135 _callbackMap.clear(); |
| 136 _completerMap.clear(); | |
| 137 | 136 |
| 138 // Signal to any pending calls that the ProxyMessageHandler is closed. | 137 // Signal to any pending calls that the ProxyMessageHandler is closed. |
| 139 if (_pendingCount > 0) { | 138 if (_pendingCount > 0) { |
| 140 proxyError("The ProxyMessageHandler is closed."); | 139 proxyError("The ProxyMessageHandler is closed."); |
| 141 } | 140 } |
| 142 | 141 |
| 143 return super.close(immediate: immediate); | 142 return super.close(immediate: immediate); |
| 144 } | 143 } |
| 145 | 144 |
| 146 void sendMessage(Struct message, int name) { | 145 void sendMessage(Struct message, int name) { |
| 147 if (!isBound) { | 146 if (!isBound) { |
| 148 proxyError("The ProxyMessageHandler is closed."); | 147 proxyError("The ProxyMessageHandler is closed."); |
| 149 return; | 148 return; |
| 150 } | 149 } |
| 151 if (!isOpen) { | 150 if (!isOpen) { |
| 152 beginHandlingEvents(); | 151 beginHandlingEvents(); |
| 153 } | 152 } |
| 154 var header = new MessageHeader(name); | 153 var header = new MessageHeader(name); |
| 155 var serviceMessage = message.serializeWithHeader(header); | 154 var serviceMessage = message.serializeWithHeader(header); |
| 156 endpoint.write(serviceMessage.buffer, serviceMessage.buffer.lengthInBytes, | 155 endpoint.write(serviceMessage.buffer, serviceMessage.buffer.lengthInBytes, |
| 157 serviceMessage.handles); | 156 serviceMessage.handles); |
| 158 if (endpoint.status != core.MojoResult.kOk) { | 157 if (endpoint.status != core.MojoResult.kOk) { |
| 159 proxyError("Write to message pipe endpoint failed."); | 158 proxyError("Write to message pipe endpoint failed."); |
| 160 } | 159 } |
| 161 } | 160 } |
| 162 | 161 |
| 163 Future sendMessageWithRequestId(Struct message, int name, int id, int flags) { | 162 void sendMessageWithRequestId( |
| 164 var completer = new Completer(); | 163 Struct message, int name, int id, int flags, Function callback) { |
| 165 if (!isBound) { | 164 if (!isBound) { |
| 166 proxyError("The ProxyMessageHandler is closed."); | 165 proxyError("The Proxy is closed."); |
| 167 return completer.future; | 166 return; |
| 168 } | 167 } |
| 169 if (!isOpen) { | 168 if (!isOpen) { |
| 170 beginHandlingEvents(); | 169 beginHandlingEvents(); |
| 171 } | 170 } |
| 172 if (id == -1) { | 171 if (id == -1) { |
| 173 id = _nextId++; | 172 id = _nextId++; |
| 174 } | 173 } |
| 175 | 174 |
| 176 var header = new MessageHeader.withRequestId(name, flags, id); | 175 var header = new MessageHeader.withRequestId(name, flags, id); |
| 177 var serviceMessage = message.serializeWithHeader(header); | 176 var serviceMessage = message.serializeWithHeader(header); |
| 178 endpoint.write(serviceMessage.buffer, serviceMessage.buffer.lengthInBytes, | 177 endpoint.write(serviceMessage.buffer, serviceMessage.buffer.lengthInBytes, |
| 179 serviceMessage.handles); | 178 serviceMessage.handles); |
| 180 | 179 |
| 181 if (endpoint.status == core.MojoResult.kOk) { | 180 if (endpoint.status == core.MojoResult.kOk) { |
| 182 _completerMap[id] = completer; | 181 _callbackMap[id] = callback; |
| 183 _pendingCount++; | 182 _pendingCount++; |
| 184 } else { | 183 } else { |
| 185 proxyError("Write to message pipe endpoint failed: ${endpoint}"); | 184 proxyError("Write to message pipe endpoint failed: ${endpoint}"); |
| 186 } | 185 } |
| 187 return completer.future; | |
| 188 } | 186 } |
| 189 | 187 |
| 190 // Need a getter for this for access in subclasses. | 188 // Need a getter for this for access in subclasses. |
| 191 HashMap<int, Completer> get completerMap => _completerMap; | 189 HashMap<int, Function> get callbackMap => _callbackMap; |
| 192 | 190 |
| 193 @override | 191 @override |
| 194 String toString() { | 192 String toString() { |
| 195 var superString = super.toString(); | 193 var superString = super.toString(); |
| 196 return "ProxyMessageHandler(${superString})"; | 194 return "ProxyMessageHandler(${superString})"; |
| 197 } | 195 } |
| 198 | 196 |
| 199 /// Queries the max version that the remote side supports. | 197 /// Queries the max version that the remote side supports. |
| 200 /// Updates [version]. | 198 /// Updates [version]. |
| 201 Future<int> queryVersion() async { | 199 Future<int> queryVersion() { |
| 200 Completer<int> completer = new Completer<int>(); |
| 202 var params = new icm.RunMessageParams(); | 201 var params = new icm.RunMessageParams(); |
| 203 params.reserved0 = 16; | 202 params.reserved0 = 16; |
| 204 params.reserved1 = 0; | 203 params.reserved1 = 0; |
| 205 params.queryVersion = new icm.QueryVersion(); | 204 params.queryVersion = new icm.QueryVersion(); |
| 206 var response = await sendMessageWithRequestId( | 205 sendMessageWithRequestId( |
| 207 params, icm.kRunMessageId, -1, MessageHeader.kMessageExpectsResponse); | 206 params, icm.kRunMessageId, -1, MessageHeader.kMessageExpectsResponse, |
| 208 _version = response.queryVersionResult.version; | 207 (r0, r1, queryResult) { |
| 209 return _version; | 208 _version = queryResult.version; |
| 209 completer.complete(_version); |
| 210 }); |
| 211 return completer.future; |
| 210 } | 212 } |
| 211 | 213 |
| 212 /// If the remote side doesn't support the [requiredVersion], it will close | 214 /// If the remote side doesn't support the [requiredVersion], it will close |
| 213 /// its end of the message pipe asynchronously. This does nothing if it's | 215 /// its end of the message pipe asynchronously. This does nothing if it's |
| 214 /// already known that the remote side supports [requiredVersion]. | 216 /// already known that the remote side supports [requiredVersion]. |
| 215 /// Updates [version]. | 217 /// Updates [version]. |
| 216 void requireVersion(int requiredVersion) { | 218 void requireVersion(int requiredVersion) { |
| 217 if (requiredVersion <= _version) { | 219 if (requiredVersion <= _version) { |
| 218 // Already supported. | 220 // Already supported. |
| 219 return; | 221 return; |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 280 return callCompleter.future; | 282 return callCompleter.future; |
| 281 } | 283 } |
| 282 | 284 |
| 283 _handleControlMessageResponse(ServiceMessage message) { | 285 _handleControlMessageResponse(ServiceMessage message) { |
| 284 // We only expect to see Run messages. | 286 // We only expect to see Run messages. |
| 285 if (message.header.type != icm.kRunMessageId) { | 287 if (message.header.type != icm.kRunMessageId) { |
| 286 proxyError("Unexpected header type in control message response: " | 288 proxyError("Unexpected header type in control message response: " |
| 287 "${message.header.type}"); | 289 "${message.header.type}"); |
| 288 return; | 290 return; |
| 289 } | 291 } |
| 290 | |
| 291 var response = icm.RunResponseMessageParams.deserialize(message.payload); | 292 var response = icm.RunResponseMessageParams.deserialize(message.payload); |
| 292 if (!message.header.hasRequestId) { | 293 if (!message.header.hasRequestId) { |
| 293 proxyError("Expected a message with a valid request Id."); | 294 proxyError("Expected a message with a valid request Id."); |
| 294 return; | 295 return; |
| 295 } | 296 } |
| 296 Completer c = completerMap[message.header.requestId]; | 297 Function callback = callbackMap[message.header.requestId]; |
| 297 if (c == null) { | 298 if (callback == null) { |
| 298 proxyError("Message had unknown request Id: ${message.header.requestId}"); | 299 proxyError("Message had unknown request Id: ${message.header.requestId}"); |
| 299 return; | 300 return; |
| 300 } | 301 } |
| 301 completerMap.remove(message.header.requestId); | 302 callbackMap.remove(message.header.requestId); |
| 302 if (c.isCompleted) { | 303 callback( |
| 303 proxyError("Control message response completer already completed"); | 304 response.reserved0, response.reserved1, response.queryVersionResult); |
| 304 return; | 305 return; |
| 305 } | |
| 306 c.complete(response); | |
| 307 } | 306 } |
| 308 } | 307 } |
| 308 |
| 309 // A class that acts like a function, but which completes a completer with the |
| 310 // the result of the function rather than returning the result. E.g.: |
| 311 // |
| 312 // Completer c = new Completer(); |
| 313 // var completerator = new Completerator._(c, f); |
| 314 // completerator(a, b); |
| 315 // await c.future; |
| 316 // |
| 317 // This completes the future c with the result of passing a and b to f. |
| 318 // |
| 319 // More usefully for Mojo, e.g.: |
| 320 // await _Completerator.completerate( |
| 321 // proxy.method, argList, MethodResponseParams#init); |
| 322 class _Completerator implements Function { |
| 323 final Completer _c; |
| 324 final Function _toComplete; |
| 325 |
| 326 _Completerator._(this._c, this._toComplete); |
| 327 |
| 328 static Future completerate(Function f, List args, Function ctor) { |
| 329 Completer c = new Completer(); |
| 330 var newArgs = new List.from(args); |
| 331 newArgs.add(new _Completerator._(c, ctor)); |
| 332 Function.apply(f, newArgs); |
| 333 return c.future; |
| 334 } |
| 335 |
| 336 // Work-around to avoid checked-mode only having grudging support for |
| 337 // Function implemented with noSuchMethod. See: |
| 338 // https://github.com/dart-lang/sdk/issues/26528 |
| 339 dynamic call([ |
| 340 dynamic a1, dynamic a2, dynamic a3, dynamic a4, dynamic a5, |
| 341 dynamic a6, dynamic a7, dynamic a8, dynamic a9, dynamic a10, |
| 342 dynamic a11, dynamic a12, dynamic a13, dynamic a14, dynamic a15, |
| 343 dynamic a16, dynamic a17, dynamic a18, dynamic a19, dynamic a20]); |
| 344 |
| 345 @override |
| 346 dynamic noSuchMethod(Invocation invocation) => |
| 347 (invocation.memberName == #call) |
| 348 ? _c.complete(Function.apply(_toComplete, invocation.positionalArguments)) |
| 349 : super.noSuchMethod(invocation); |
| 350 } |
| 351 |
| 352 /// Base class for Proxy class Futurizing wrappers. It turns callback-based |
| 353 /// methods on the Proxy into Future based methods in derived classes. E.g.: |
| 354 /// |
| 355 /// class FuturizedHostResolverProxy extends FuturizedProxy<HostResolverProxy> { |
| 356 /// Map<Symbol, Function> _mojoMethods; |
| 357 /// |
| 358 /// FuturizedHostResolverProxy(HostResolverProxy proxy) : super(proxy) { |
| 359 /// _mojoMethods = <Symbol, Function>{ |
| 360 /// #getHostAddresses: proxy.getHostAddresses, |
| 361 /// }; |
| 362 /// } |
| 363 /// Map<Symbol, Function> get mojoMethods => _mojoMethods; |
| 364 /// |
| 365 /// FuturizedHostResolverProxy.unbound() : |
| 366 /// this(new HostResolverProxy.unbound()); |
| 367 /// |
| 368 /// static final Map<Symbol, Function> _mojoResponses = { |
| 369 /// #getHostAddresses: new HostResolverGetHostAddressesResponseParams#init, |
| 370 /// }; |
| 371 /// Map<Symbol, Function> get mojoResponses => _mojoResponses; |
| 372 /// } |
| 373 /// |
| 374 /// Then: |
| 375 /// |
| 376 /// HostResolveProxy proxy = ... |
| 377 /// var futurizedProxy = new FuturizedHostResolverProxy(proxy); |
| 378 /// var response = await futurizedProxy.getHostAddresses(host, family); |
| 379 /// // etc. |
| 380 /// |
| 381 /// Warning 1: The list of methods and return object constructors in |
| 382 /// FuturizedHostResolverProxy has to be kept up-do-date by hand with changes |
| 383 /// to the Mojo interface. |
| 384 /// |
| 385 /// Warning 2: The recommended API to use is the generated callback-based API. |
| 386 /// This wrapper class is exposed only for convenience during development, |
| 387 /// and has no guarantee of optimal performance. |
| 388 abstract class FuturizedProxy<T extends Proxy> { |
| 389 final T _proxy; |
| 390 Map<Symbol, Function> get mojoMethods; |
| 391 Map<Symbol, Function> get mojoResponses; |
| 392 |
| 393 FuturizedProxy(T this._proxy); |
| 394 |
| 395 T get proxy => _proxy; |
| 396 Future responseOrError(Future f) => _proxy.responseOrError(f); |
| 397 Future close({immediate: false}) => _proxy.close(immediate: immediate); |
| 398 |
| 399 @override |
| 400 dynamic noSuchMethod(Invocation invocation) => |
| 401 mojoMethods.containsKey(invocation.memberName) |
| 402 ? _Completerator.completerate( |
| 403 mojoMethods[invocation.memberName], |
| 404 invocation.positionalArguments, |
| 405 mojoResponses[invocation.memberName]) |
| 406 : super.noSuchMethod(invocation); |
| 407 } |
| OLD | NEW |