| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 part of html; | |
| 6 | |
| 7 _serialize(var message) { | |
| 8 return new _JsSerializer().traverse(message); | |
| 9 } | |
| 10 | |
| 11 class _JsSerializer extends _Serializer { | |
| 12 | |
| 13 visitSendPortSync(SendPortSync x) { | |
| 14 if (x is _JsSendPortSync) return visitJsSendPortSync(x); | |
| 15 if (x is _LocalSendPortSync) return visitLocalSendPortSync(x); | |
| 16 if (x is _RemoteSendPortSync) return visitRemoteSendPortSync(x); | |
| 17 throw "Unknown port type $x"; | |
| 18 } | |
| 19 | |
| 20 visitJsSendPortSync(_JsSendPortSync x) { | |
| 21 return [ 'sendport', 'nativejs', x._id ]; | |
| 22 } | |
| 23 | |
| 24 visitLocalSendPortSync(_LocalSendPortSync x) { | |
| 25 return [ 'sendport', 'dart', | |
| 26 ReceivePortSync._isolateId, x._receivePort._portId ]; | |
| 27 } | |
| 28 | |
| 29 visitSendPort(SendPort x) { | |
| 30 throw new UnimplementedError('Asynchronous send port not yet implemented.'); | |
| 31 } | |
| 32 | |
| 33 visitRemoteSendPortSync(_RemoteSendPortSync x) { | |
| 34 return [ 'sendport', 'dart', x._isolateId, x._portId ]; | |
| 35 } | |
| 36 } | |
| 37 | |
| 38 _deserialize(var message) { | |
| 39 return new _JsDeserializer().deserialize(message); | |
| 40 } | |
| 41 | |
| 42 | |
| 43 class _JsDeserializer extends _Deserializer { | |
| 44 | |
| 45 static const _UNSPECIFIED = const Object(); | |
| 46 | |
| 47 deserializeSendPort(List x) { | |
| 48 String tag = x[1]; | |
| 49 switch (tag) { | |
| 50 case 'nativejs': | |
| 51 num id = x[2]; | |
| 52 return new _JsSendPortSync(id); | |
| 53 case 'dart': | |
| 54 num isolateId = x[2]; | |
| 55 num portId = x[3]; | |
| 56 return ReceivePortSync._lookup(isolateId, portId); | |
| 57 default: | |
| 58 throw 'Illegal SendPortSync type: $tag'; | |
| 59 } | |
| 60 } | |
| 61 } | |
| 62 | |
| 63 // The receiver is JS. | |
| 64 class _JsSendPortSync implements SendPortSync { | |
| 65 | |
| 66 num _id; | |
| 67 _JsSendPortSync(this._id); | |
| 68 | |
| 69 callSync(var message) { | |
| 70 var serialized = _serialize(message); | |
| 71 var result = _callPortSync(_id, serialized); | |
| 72 return _deserialize(result); | |
| 73 } | |
| 74 | |
| 75 } | |
| 76 | |
| 77 // TODO(vsm): Differentiate between Dart2Js and Dartium isolates. | |
| 78 // The receiver is a different Dart isolate, compiled to JS. | |
| 79 class _RemoteSendPortSync implements SendPortSync { | |
| 80 | |
| 81 int _isolateId; | |
| 82 int _portId; | |
| 83 _RemoteSendPortSync(this._isolateId, this._portId); | |
| 84 | |
| 85 callSync(var message) { | |
| 86 var serialized = _serialize(message); | |
| 87 var result = _call(_isolateId, _portId, serialized); | |
| 88 return _deserialize(result); | |
| 89 } | |
| 90 | |
| 91 static _call(int isolateId, int portId, var message) { | |
| 92 var target = 'dart-port-$isolateId-$portId'; | |
| 93 // TODO(vsm): Make this re-entrant. | |
| 94 // TODO(vsm): Set this up set once, on the first call. | |
| 95 var source = '$target-result'; | |
| 96 var result = null; | |
| 97 var listener = (Event e) { | |
| 98 result = JSON.parse(_getPortSyncEventData(e)); | |
| 99 }; | |
| 100 window.on[source].add(listener); | |
| 101 _dispatchEvent(target, [source, message]); | |
| 102 window.on[source].remove(listener); | |
| 103 return result; | |
| 104 } | |
| 105 } | |
| 106 | |
| 107 // The receiver is in the same Dart isolate, compiled to JS. | |
| 108 class _LocalSendPortSync implements SendPortSync { | |
| 109 | |
| 110 ReceivePortSync _receivePort; | |
| 111 | |
| 112 _LocalSendPortSync._internal(this._receivePort); | |
| 113 | |
| 114 callSync(var message) { | |
| 115 // TODO(vsm): Do a more efficient deep copy. | |
| 116 var copy = _deserialize(_serialize(message)); | |
| 117 var result = _receivePort._callback(copy); | |
| 118 return _deserialize(_serialize(result)); | |
| 119 } | |
| 120 } | |
| 121 | |
| 122 // TODO(vsm): Move this to dart:isolate. This will take some | |
| 123 // refactoring as there are dependences here on the DOM. Users | |
| 124 // interact with this class (or interface if we change it) directly - | |
| 125 // new ReceivePortSync. I think most of the DOM logic could be | |
| 126 // delayed until the corresponding SendPort is registered on the | |
| 127 // window. | |
| 128 | |
| 129 // A Dart ReceivePortSync (tagged 'dart' when serialized) is | |
| 130 // identifiable / resolvable by the combination of its isolateid and | |
| 131 // portid. When a corresponding SendPort is used within the same | |
| 132 // isolate, the _portMap below can be used to obtain the | |
| 133 // ReceivePortSync directly. Across isolates (or from JS), an | |
| 134 // EventListener can be used to communicate with the port indirectly. | |
| 135 class ReceivePortSync { | |
| 136 | |
| 137 static Map<int, ReceivePortSync> _portMap; | |
| 138 static int _portIdCount; | |
| 139 static int _cachedIsolateId; | |
| 140 | |
| 141 num _portId; | |
| 142 Function _callback; | |
| 143 EventListener _listener; | |
| 144 | |
| 145 ReceivePortSync() { | |
| 146 if (_portIdCount == null) { | |
| 147 _portIdCount = 0; | |
| 148 _portMap = new Map<int, ReceivePortSync>(); | |
| 149 } | |
| 150 _portId = _portIdCount++; | |
| 151 _portMap[_portId] = this; | |
| 152 } | |
| 153 | |
| 154 static int get _isolateId { | |
| 155 // TODO(vsm): Make this coherent with existing isolate code. | |
| 156 if (_cachedIsolateId == null) { | |
| 157 _cachedIsolateId = _getNewIsolateId(); | |
| 158 } | |
| 159 return _cachedIsolateId; | |
| 160 } | |
| 161 | |
| 162 static String _getListenerName(isolateId, portId) => | |
| 163 'dart-port-$isolateId-$portId'; | |
| 164 String get _listenerName => _getListenerName(_isolateId, _portId); | |
| 165 | |
| 166 void receive(callback(var message)) { | |
| 167 _callback = callback; | |
| 168 if (_listener == null) { | |
| 169 _listener = (Event e) { | |
| 170 var data = JSON.parse(_getPortSyncEventData(e)); | |
| 171 var replyTo = data[0]; | |
| 172 var message = _deserialize(data[1]); | |
| 173 var result = _callback(message); | |
| 174 _dispatchEvent(replyTo, _serialize(result)); | |
| 175 }; | |
| 176 window.on[_listenerName].add(_listener); | |
| 177 } | |
| 178 } | |
| 179 | |
| 180 void close() { | |
| 181 _portMap.remove(_portId); | |
| 182 if (_listener != null) window.on[_listenerName].remove(_listener); | |
| 183 } | |
| 184 | |
| 185 SendPortSync toSendPort() { | |
| 186 return new _LocalSendPortSync._internal(this); | |
| 187 } | |
| 188 | |
| 189 static SendPortSync _lookup(int isolateId, int portId) { | |
| 190 if (isolateId == _isolateId) { | |
| 191 return _portMap[portId].toSendPort(); | |
| 192 } else { | |
| 193 return new _RemoteSendPortSync(isolateId, portId); | |
| 194 } | |
| 195 } | |
| 196 } | |
| 197 | |
| 198 get _isolateId => ReceivePortSync._isolateId; | |
| 199 | |
| 200 void _dispatchEvent(String receiver, var message) { | |
| 201 var event = new CustomEvent(receiver, false, false, JSON.stringify(message)); | |
| 202 window.$dom_dispatchEvent(event); | |
| 203 } | |
| 204 | |
| 205 String _getPortSyncEventData(CustomEvent event) => event.detail; | |
| OLD | NEW |