| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| part of html; |
| |
| _serialize(var message) { |
| return new _JsSerializer().traverse(message); |
| } |
| |
| class _JsSerializer extends _Serializer { |
| |
| visitSendPortSync(SendPortSync x) { |
| if (x is _JsSendPortSync) return visitJsSendPortSync(x); |
| if (x is _LocalSendPortSync) return visitLocalSendPortSync(x); |
| if (x is _RemoteSendPortSync) return visitRemoteSendPortSync(x); |
| throw "Unknown port type $x"; |
| } |
| |
| visitJsSendPortSync(_JsSendPortSync x) { |
| return [ 'sendport', 'nativejs', x._id ]; |
| } |
| |
| visitLocalSendPortSync(_LocalSendPortSync x) { |
| return [ 'sendport', 'dart', |
| ReceivePortSync._isolateId, x._receivePort._portId ]; |
| } |
| |
| visitSendPort(SendPort x) { |
| throw new UnimplementedError('Asynchronous send port not yet implemented.'); |
| } |
| |
| visitRemoteSendPortSync(_RemoteSendPortSync x) { |
| return [ 'sendport', 'dart', x._isolateId, x._portId ]; |
| } |
| } |
| |
| _deserialize(var message) { |
| return new _JsDeserializer().deserialize(message); |
| } |
| |
| |
| class _JsDeserializer extends _Deserializer { |
| |
| static const _UNSPECIFIED = const Object(); |
| |
| deserializeSendPort(List x) { |
| String tag = x[1]; |
| switch (tag) { |
| case 'nativejs': |
| num id = x[2]; |
| return new _JsSendPortSync(id); |
| case 'dart': |
| num isolateId = x[2]; |
| num portId = x[3]; |
| return ReceivePortSync._lookup(isolateId, portId); |
| default: |
| throw 'Illegal SendPortSync type: $tag'; |
| } |
| } |
| } |
| |
| // The receiver is JS. |
| class _JsSendPortSync implements SendPortSync { |
| |
| final num _id; |
| _JsSendPortSync(this._id); |
| |
| callSync(var message) { |
| var serialized = _serialize(message); |
| var result = _callPortSync(_id, serialized); |
| return _deserialize(result); |
| } |
| |
| bool operator==(var other) { |
| return (other is _JsSendPortSync) && (_id == other._id); |
| } |
| |
| int get hashCode => _id; |
| } |
| |
| // TODO(vsm): Differentiate between Dart2Js and Dartium isolates. |
| // The receiver is a different Dart isolate, compiled to JS. |
| class _RemoteSendPortSync implements SendPortSync { |
| |
| int _isolateId; |
| int _portId; |
| _RemoteSendPortSync(this._isolateId, this._portId); |
| |
| callSync(var message) { |
| var serialized = _serialize(message); |
| var result = _call(_isolateId, _portId, serialized); |
| return _deserialize(result); |
| } |
| |
| static _call(int isolateId, int portId, var message) { |
| var target = 'dart-port-$isolateId-$portId'; |
| // TODO(vsm): Make this re-entrant. |
| // TODO(vsm): Set this up set once, on the first call. |
| var source = '$target-result'; |
| var result = null; |
| window.on[source].first.then((Event e) { |
| result = JSON.decode(_getPortSyncEventData(e)); |
| }); |
| _dispatchEvent(target, [source, message]); |
| return result; |
| } |
| |
| bool operator==(var other) { |
| return (other is _RemoteSendPortSync) && (_isolateId == other._isolateId) |
| && (_portId == other._portId); |
| } |
| |
| int get hashCode => _isolateId >> 16 + _portId; |
| } |
| |
| // The receiver is in the same Dart isolate, compiled to JS. |
| class _LocalSendPortSync implements SendPortSync { |
| |
| ReceivePortSync _receivePort; |
| |
| _LocalSendPortSync._internal(this._receivePort); |
| |
| callSync(var message) { |
| // TODO(vsm): Do a more efficient deep copy. |
| var copy = _deserialize(_serialize(message)); |
| var result = _receivePort._callback(copy); |
| return _deserialize(_serialize(result)); |
| } |
| |
| bool operator==(var other) { |
| return (other is _LocalSendPortSync) |
| && (_receivePort == other._receivePort); |
| } |
| |
| int get hashCode => _receivePort.hashCode; |
| } |
| |
| // TODO(vsm): Move this to dart:isolate. This will take some |
| // refactoring as there are dependences here on the DOM. Users |
| // interact with this class (or interface if we change it) directly - |
| // new ReceivePortSync. I think most of the DOM logic could be |
| // delayed until the corresponding SendPort is registered on the |
| // window. |
| |
| // A Dart ReceivePortSync (tagged 'dart' when serialized) is |
| // identifiable / resolvable by the combination of its isolateid and |
| // portid. When a corresponding SendPort is used within the same |
| // isolate, the _portMap below can be used to obtain the |
| // ReceivePortSync directly. Across isolates (or from JS), an |
| // EventListener can be used to communicate with the port indirectly. |
| class ReceivePortSync { |
| |
| static Map<int, ReceivePortSync> _portMap; |
| static int _portIdCount; |
| static int _cachedIsolateId; |
| |
| num _portId; |
| Function _callback; |
| StreamSubscription _portSubscription; |
| |
| ReceivePortSync() { |
| if (_portIdCount == null) { |
| _portIdCount = 0; |
| _portMap = new Map<int, ReceivePortSync>(); |
| } |
| _portId = _portIdCount++; |
| _portMap[_portId] = this; |
| } |
| |
| static int get _isolateId { |
| // TODO(vsm): Make this coherent with existing isolate code. |
| if (_cachedIsolateId == null) { |
| _cachedIsolateId = _getNewIsolateId(); |
| } |
| return _cachedIsolateId; |
| } |
| |
| static String _getListenerName(isolateId, portId) => |
| 'dart-port-$isolateId-$portId'; |
| String get _listenerName => _getListenerName(_isolateId, _portId); |
| |
| void receive(callback(var message)) { |
| _callback = callback; |
| if (_portSubscription == null) { |
| _portSubscription = window.on[_listenerName].listen((Event e) { |
| var data = JSON.decode(_getPortSyncEventData(e)); |
| var replyTo = data[0]; |
| var message = _deserialize(data[1]); |
| var result = _callback(message); |
| _dispatchEvent(replyTo, _serialize(result)); |
| }); |
| } |
| } |
| |
| void close() { |
| _portMap.remove(_portId); |
| if (_portSubscription != null) _portSubscription.cancel(); |
| } |
| |
| SendPortSync toSendPort() { |
| return new _LocalSendPortSync._internal(this); |
| } |
| |
| static SendPortSync _lookup(int isolateId, int portId) { |
| if (isolateId == _isolateId) { |
| return _portMap[portId].toSendPort(); |
| } else { |
| return new _RemoteSendPortSync(isolateId, portId); |
| } |
| } |
| } |
| |
| get _isolateId => ReceivePortSync._isolateId; |
| |
| void _dispatchEvent(String receiver, var message) { |
| var event = new CustomEvent(receiver, canBubble: false, cancelable:false, |
| detail: JSON.encode(message)); |
| window.dispatchEvent(event); |
| } |
| |
| String _getPortSyncEventData(CustomEvent event) => event.detail; |