| 
1 |  | -import 'dart:async';  | 
2 |  | -import 'dart:collection';  | 
3 |  | -import 'dart:isolate';  | 
4 |  | - | 
5 |  | -abstract class PortClient {  | 
6 |  | -  Future<T> post<T>(Object message);  | 
7 |  | -  void fire(Object message);  | 
8 |  | - | 
9 |  | -  factory PortClient.parent() {  | 
10 |  | -    return ParentPortClient();  | 
11 |  | -  }  | 
12 |  | - | 
13 |  | -  factory PortClient.child(SendPort upstream) {  | 
14 |  | -    return ChildPortClient(upstream);  | 
15 |  | -  }  | 
16 |  | -}  | 
17 |  | - | 
18 |  | -class ParentPortClient implements PortClient {  | 
19 |  | -  late Future<SendPort> sendPortFuture;  | 
20 |  | -  SendPort? sendPort;  | 
21 |  | -  final ReceivePort _receivePort = ReceivePort();  | 
22 |  | -  final ReceivePort _errorPort = ReceivePort();  | 
23 |  | -  bool closed = false;  | 
24 |  | -  Object? _closeError;  | 
25 |  | -  String? _isolateDebugName;  | 
26 |  | -  int _nextId = 1;  | 
27 |  | - | 
28 |  | -  Map<int, Completer<Object?>> handlers = HashMap();  | 
29 |  | - | 
30 |  | -  ParentPortClient() {  | 
31 |  | -    final initCompleter = Completer<SendPort>.sync();  | 
32 |  | -    sendPortFuture = initCompleter.future;  | 
33 |  | -    sendPortFuture.then((value) {  | 
34 |  | -      sendPort = value;  | 
35 |  | -    });  | 
36 |  | -    _receivePort.listen((message) {  | 
37 |  | -      if (message is _InitMessage) {  | 
38 |  | -        assert(!initCompleter.isCompleted);  | 
39 |  | -        initCompleter.complete(message.port);  | 
40 |  | -      } else if (message is _PortChannelResult) {  | 
41 |  | -        final handler = handlers.remove(message.requestId);  | 
42 |  | -        assert(handler != null);  | 
43 |  | -        if (message.success) {  | 
44 |  | -          handler!.complete(message.result);  | 
45 |  | -        } else {  | 
46 |  | -          handler!.completeError(message.error, message.stackTrace);  | 
47 |  | -        }  | 
48 |  | -      } else if (message == _closeMessage) {  | 
49 |  | -        close();  | 
50 |  | -      }  | 
51 |  | -    }, onError: (e) {  | 
52 |  | -      if (!initCompleter.isCompleted) {  | 
53 |  | -        initCompleter.completeError(e);  | 
54 |  | -      }  | 
55 |  | - | 
56 |  | -      close();  | 
57 |  | -    }, onDone: () {  | 
58 |  | -      if (!initCompleter.isCompleted) {  | 
59 |  | -        initCompleter.completeError(ClosedException());  | 
60 |  | -      }  | 
61 |  | -      close();  | 
62 |  | -    });  | 
63 |  | -    _errorPort.listen((message) {  | 
64 |  | -      final [error, stackTraceString] = message;  | 
65 |  | -      final stackTrace = stackTraceString == null  | 
66 |  | -          ? null  | 
67 |  | -          : StackTrace.fromString(stackTraceString);  | 
68 |  | -      if (!initCompleter.isCompleted) {  | 
69 |  | -        initCompleter.completeError(error, stackTrace);  | 
70 |  | -      }  | 
71 |  | -      _close(IsolateError(cause: error, isolateDebugName: _isolateDebugName),  | 
72 |  | -          stackTrace);  | 
73 |  | -    });  | 
74 |  | -  }  | 
75 |  | - | 
76 |  | -  Future<void> get ready async {  | 
77 |  | -    await sendPortFuture;  | 
78 |  | -  }  | 
79 |  | - | 
80 |  | -  void _cancelAll(Object error, [StackTrace? stackTrace]) {  | 
81 |  | -    var handlers = this.handlers;  | 
82 |  | -    this.handlers = {};  | 
83 |  | -    for (var message in handlers.values) {  | 
84 |  | -      message.completeError(error, stackTrace);  | 
85 |  | -    }  | 
86 |  | -  }  | 
87 |  | - | 
88 |  | -  @override  | 
89 |  | -  Future<T> post<T>(Object message) async {  | 
90 |  | -    if (closed) {  | 
91 |  | -      throw _closeError ?? const ClosedException();  | 
92 |  | -    }  | 
93 |  | -    var completer = Completer<T>.sync();  | 
94 |  | -    var id = _nextId++;  | 
95 |  | -    handlers[id] = completer;  | 
96 |  | -    final port = sendPort ?? await sendPortFuture;  | 
97 |  | -    port.send(_RequestMessage(id, message, null));  | 
98 |  | -    return await completer.future;  | 
99 |  | -  }  | 
100 |  | - | 
101 |  | -  @override  | 
102 |  | -  void fire(Object message) async {  | 
103 |  | -    if (closed) {  | 
104 |  | -      throw _closeError ?? ClosedException();  | 
105 |  | -    }  | 
106 |  | -    final port = sendPort ?? await sendPortFuture;  | 
107 |  | -    port.send(_FireMessage(message));  | 
108 |  | -  }  | 
109 |  | - | 
110 |  | -  RequestPortServer server() {  | 
111 |  | -    return RequestPortServer(_receivePort.sendPort);  | 
112 |  | -  }  | 
113 |  | - | 
114 |  | -  void _close([Object? error, StackTrace? stackTrace]) {  | 
115 |  | -    if (!closed) {  | 
116 |  | -      closed = true;  | 
117 |  | - | 
118 |  | -      _receivePort.close();  | 
119 |  | -      _errorPort.close();  | 
120 |  | -      if (error == null) {  | 
121 |  | -        _cancelAll(const ClosedException());  | 
122 |  | -      } else {  | 
123 |  | -        _closeError = error;  | 
124 |  | -        _cancelAll(error, stackTrace);  | 
125 |  | -      }  | 
126 |  | -    }  | 
127 |  | -  }  | 
128 |  | - | 
129 |  | -  void close() {  | 
130 |  | -    _close();  | 
131 |  | -  }  | 
132 |  | - | 
133 |  | -  tieToIsolate(Isolate isolate) {  | 
134 |  | -    _isolateDebugName = isolate.debugName;  | 
135 |  | -    isolate.addErrorListener(_errorPort.sendPort);  | 
136 |  | -    isolate.addOnExitListener(_receivePort.sendPort, response: _closeMessage);  | 
137 |  | -  }  | 
138 |  | -}  | 
139 |  | - | 
140 |  | -class SerializedPortClient {  | 
141 |  | -  final SendPort sendPort;  | 
142 |  | - | 
143 |  | -  SerializedPortClient(this.sendPort);  | 
144 |  | - | 
145 |  | -  ChildPortClient open() {  | 
146 |  | -    return ChildPortClient(sendPort);  | 
147 |  | -  }  | 
148 |  | -}  | 
149 |  | - | 
150 |  | -class ChildPortClient implements PortClient {  | 
151 |  | -  final SendPort sendPort;  | 
152 |  | -  final ReceivePort receivePort = ReceivePort();  | 
153 |  | -  int _nextId = 1;  | 
154 |  | -  bool closed = false;  | 
155 |  | - | 
156 |  | -  final Map<int, Completer<Object?>> handlers = HashMap();  | 
157 |  | - | 
158 |  | -  ChildPortClient(this.sendPort) {  | 
159 |  | -    receivePort.listen((message) {  | 
160 |  | -      if (message is _PortChannelResult) {  | 
161 |  | -        final handler = handlers.remove(message.requestId);  | 
162 |  | -        assert(handler != null);  | 
163 |  | -        if (message.success) {  | 
164 |  | -          handler!.complete(message.result);  | 
165 |  | -        } else {  | 
166 |  | -          handler!.completeError(message.error, message.stackTrace);  | 
167 |  | -        }  | 
168 |  | -      }  | 
169 |  | -    });  | 
170 |  | -  }  | 
171 |  | - | 
172 |  | -  @override  | 
173 |  | -  Future<T> post<T>(Object message) async {  | 
174 |  | -    if (closed) {  | 
175 |  | -      throw const ClosedException();  | 
176 |  | -    }  | 
177 |  | -    var completer = Completer<T>.sync();  | 
178 |  | -    var id = _nextId++;  | 
179 |  | -    handlers[id] = completer;  | 
180 |  | -    sendPort.send(_RequestMessage(id, message, receivePort.sendPort));  | 
181 |  | -    return await completer.future;  | 
182 |  | -  }  | 
183 |  | - | 
184 |  | -  @override  | 
185 |  | -  void fire(Object message) {  | 
186 |  | -    if (closed) {  | 
187 |  | -      throw ClosedException();  | 
188 |  | -    }  | 
189 |  | -    sendPort.send(_FireMessage(message));  | 
190 |  | -  }  | 
191 |  | - | 
192 |  | -  void _cancelAll(Object error) {  | 
193 |  | -    var handlers = HashMap<int, Completer<Object?>>.from(this.handlers);  | 
194 |  | -    this.handlers.clear();  | 
195 |  | -    for (var message in handlers.values) {  | 
196 |  | -      message.completeError(error);  | 
197 |  | -    }  | 
198 |  | -  }  | 
199 |  | - | 
200 |  | -  void close() {  | 
201 |  | -    closed = true;  | 
202 |  | -    _cancelAll(const ClosedException());  | 
203 |  | -    receivePort.close();  | 
204 |  | -  }  | 
205 |  | -}  | 
206 |  | - | 
207 |  | -class RequestPortServer {  | 
208 |  | -  final SendPort port;  | 
209 |  | - | 
210 |  | -  RequestPortServer(this.port);  | 
211 |  | - | 
212 |  | -  open(Future<Object?> Function(Object? message) handle) {  | 
213 |  | -    return PortServer.forSendPort(port, handle);  | 
214 |  | -  }  | 
215 |  | -}  | 
216 |  | - | 
217 |  | -class PortServer {  | 
218 |  | -  final ReceivePort _receivePort = ReceivePort();  | 
219 |  | -  final Future<Object?> Function(Object? message) handle;  | 
220 |  | -  final SendPort? replyPort;  | 
221 |  | - | 
222 |  | -  PortServer(this.handle) : replyPort = null {  | 
223 |  | -    _init();  | 
224 |  | -  }  | 
225 |  | - | 
226 |  | -  PortServer.forSendPort(SendPort port, this.handle) : replyPort = port {  | 
227 |  | -    port.send(_InitMessage(_receivePort.sendPort));  | 
228 |  | -    _init();  | 
229 |  | -  }  | 
230 |  | - | 
231 |  | -  SendPort get sendPort {  | 
232 |  | -    return _receivePort.sendPort;  | 
233 |  | -  }  | 
234 |  | - | 
235 |  | -  SerializedPortClient client() {  | 
236 |  | -    return SerializedPortClient(sendPort);  | 
237 |  | -  }  | 
238 |  | - | 
239 |  | -  void close() {  | 
240 |  | -    _receivePort.close();  | 
241 |  | -  }  | 
242 |  | - | 
243 |  | -  void _init() {  | 
244 |  | -    _receivePort.listen((request) async {  | 
245 |  | -      if (request is _FireMessage) {  | 
246 |  | -        handle(request.message);  | 
247 |  | -      } else if (request is _RequestMessage) {  | 
248 |  | -        if (request.id == 0) {  | 
249 |  | -          // Fire and forget  | 
250 |  | -          handle(request.message);  | 
251 |  | -        } else {  | 
252 |  | -          final replyPort = request.reply ?? this.replyPort;  | 
253 |  | -          try {  | 
254 |  | -            var result = await handle(request.message);  | 
255 |  | -            replyPort!.send(_PortChannelResult.success(request.id, result));  | 
256 |  | -          } catch (e, stacktrace) {  | 
257 |  | -            replyPort!  | 
258 |  | -                .send(_PortChannelResult.error(request.id, e, stacktrace));  | 
259 |  | -          }  | 
260 |  | -        }  | 
261 |  | -      }  | 
262 |  | -    });  | 
263 |  | -  }  | 
264 |  | -}  | 
265 |  | - | 
266 |  | -const _closeMessage = '_Close';  | 
267 |  | - | 
268 |  | -class _InitMessage {  | 
269 |  | -  final SendPort port;  | 
270 |  | - | 
271 |  | -  _InitMessage(this.port);  | 
272 |  | -}  | 
273 |  | - | 
274 |  | -class _FireMessage {  | 
275 |  | -  final Object message;  | 
276 |  | - | 
277 |  | -  const _FireMessage(this.message);  | 
278 |  | -}  | 
279 |  | - | 
280 |  | -class _RequestMessage {  | 
281 |  | -  final int id;  | 
282 |  | -  final Object message;  | 
283 |  | -  final SendPort? reply;  | 
284 |  | - | 
285 |  | -  _RequestMessage(this.id, this.message, this.reply);  | 
286 |  | -}  | 
287 |  | - | 
288 |  | -class ClosedException implements Exception {  | 
289 |  | -  const ClosedException();  | 
290 |  | - | 
291 |  | -  @override  | 
292 |  | -  String toString() {  | 
293 |  | -    return 'ClosedException';  | 
294 |  | -  }  | 
295 |  | -}  | 
296 |  | - | 
297 |  | -class IsolateError extends Error {  | 
298 |  | -  final Object cause;  | 
299 |  | -  final String? isolateDebugName;  | 
300 |  | - | 
301 |  | -  IsolateError({required this.cause, this.isolateDebugName});  | 
302 |  | - | 
303 |  | -  @override  | 
304 |  | -  String toString() {  | 
305 |  | -    if (isolateDebugName != null) {  | 
306 |  | -      return 'IsolateError in $isolateDebugName: $cause';  | 
307 |  | -    } else {  | 
308 |  | -      return 'IsolateError: $cause';  | 
309 |  | -    }  | 
310 |  | -  }  | 
311 |  | -}  | 
312 |  | - | 
313 |  | -class _PortChannelResult<T> {  | 
314 |  | -  final int requestId;  | 
315 |  | -  final bool success;  | 
316 |  | -  final T? _result;  | 
317 |  | -  final Object? _error;  | 
318 |  | -  final StackTrace? stackTrace;  | 
319 |  | - | 
320 |  | -  const _PortChannelResult.success(this.requestId, T result)  | 
321 |  | -      : success = true,  | 
322 |  | -        _error = null,  | 
323 |  | -        stackTrace = null,  | 
324 |  | -        _result = result;  | 
325 |  | -  const _PortChannelResult.error(this.requestId, Object error,  | 
326 |  | -      [this.stackTrace])  | 
327 |  | -      : success = false,  | 
328 |  | -        _result = null,  | 
329 |  | -        _error = error;  | 
330 |  | - | 
331 |  | -  T get value {  | 
332 |  | -    if (success) {  | 
333 |  | -      return _result as T;  | 
334 |  | -    } else {  | 
335 |  | -      if (_error != null && stackTrace != null) {  | 
336 |  | -        Error.throwWithStackTrace(_error, stackTrace!);  | 
337 |  | -      } else {  | 
338 |  | -        throw _error!;  | 
339 |  | -      }  | 
340 |  | -    }  | 
341 |  | -  }  | 
342 |  | - | 
343 |  | -  T get result {  | 
344 |  | -    assert(success);  | 
345 |  | -    return _result as T;  | 
346 |  | -  }  | 
347 |  | - | 
348 |  | -  Object get error {  | 
349 |  | -    assert(!success);  | 
350 |  | -    return _error!;  | 
351 |  | -  }  | 
352 |  | -}  | 
 | 1 | +export 'port_channel_native.dart'  | 
 | 2 | +    if (dart.library.js_interop) 'port_channel_stub.dart';  | 
0 commit comments