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 |