| 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 _serialize(var message) { | |
| 6 return new _JsSerializer().traverse(message); | |
| 7 } | |
| 8 | |
| 9 class _JsSerializer extends _Serializer { | |
| 10 | |
| 11 visitSendPortSync(SendPortSync x) { | |
| 12 if (x is _JsSendPortSync) return visitJsSendPortSync(x); | |
| 13 if (x is _LocalSendPortSync) return visitLocalSendPortSync(x); | |
| 14 if (x is _RemoteSendPortSync) return visitRemoteSendPortSync(x); | |
| 15 throw "Illegal underlying port $x"; | |
| 16 } | |
| 17 | |
| 18 visitJsSendPortSync(_JsSendPortSync x) { | |
| 19 return [ 'sendport', 'nativejs', x._id ]; | |
| 20 } | |
| 21 | |
| 22 visitLocalSendPortSync(_LocalSendPortSync x) { | |
| 23 return [ 'sendport', 'dart', | |
| 24 ReceivePortSync._isolateId, x._receivePort._portId ]; | |
| 25 } | |
| 26 | |
| 27 visitRemoteSendPortSync(_RemoteSendPortSync x) { | |
| 28 return [ 'sendport', 'dart', | |
| 29 x._receivePort._isolateId, x._receivePort._portId ]; | |
| 30 } | |
| 31 | |
| 32 visitObject(Object x) { | |
| 33 if (x is Function) return visitFunction(x); | |
| 34 // TODO: Handle DOM elements and proxy other objects. | |
| 35 throw "Unserializable object $x"; | |
| 36 } | |
| 37 | |
| 38 visitFunction(Function func) { | |
| 39 return [ 'funcref', | |
| 40 _makeFunctionRef(func), visitSendPortSync(_sendPort()), null ]; | |
| 41 } | |
| 42 } | |
| 43 | |
| 44 // Leaking implementation. Later will be backend specific and hopefully | |
| 45 // not leaking (at least in most of the cases.) | |
| 46 // TODO: provide better, backend specific implementation. | |
| 47 class _FunctionRegistry { | |
| 48 final ReceivePortSync _port; | |
| 49 int _nextId; | |
| 50 final Map<String, Function> _registry; | |
| 51 | |
| 52 _FunctionRegistry() : | |
| 53 _port = new ReceivePortSync(), | |
| 54 _nextId = 0, | |
| 55 _registry = <Function>{} { | |
| 56 _port.receive((msg) { | |
| 57 final id = msg[0]; | |
| 58 final args = msg[1]; | |
| 59 final f = _registry[id]; | |
| 60 switch (args.length) { | |
| 61 case 0: return f(); | |
| 62 case 1: return f(args[0]); | |
| 63 case 2: return f(args[0], args[1]); | |
| 64 case 3: return f(args[0], args[1], args[2]); | |
| 65 case 4: return f(args[0], args[1], args[2], args[3]); | |
| 66 default: throw 'Unsupported number of arguments.'; | |
| 67 } | |
| 68 }); | |
| 69 } | |
| 70 | |
| 71 String _add(Function f) { | |
| 72 final id = 'func-ref-${_nextId++}'; | |
| 73 _registry[id] = f; | |
| 74 return id; | |
| 75 } | |
| 76 | |
| 77 get _sendPort() => _port.toSendPort(); | |
| 78 } | |
| 79 | |
| 80 _FunctionRegistry __functionRegistry; | |
| 81 get _functionRegistry() { | |
| 82 if (__functionRegistry === null) __functionRegistry = new _FunctionRegistry(); | |
| 83 return __functionRegistry; | |
| 84 } | |
| 85 | |
| 86 _makeFunctionRef(f) => _functionRegistry._add(f); | |
| 87 _sendPort() => _functionRegistry._sendPort; | |
| 88 /// End of function serialization implementation. | |
| 89 | |
| 90 _deserialize(var message) { | |
| 91 return new _JsDeserializer().deserialize(message); | |
| 92 } | |
| 93 | |
| 94 class _JsDeserializer extends _Deserializer { | |
| 95 | |
| 96 static final _UNSPECIFIED = const Object(); | |
| 97 | |
| 98 deserializeSendPort(List x) { | |
| 99 String tag = x[1]; | |
| 100 switch (tag) { | |
| 101 case 'nativejs': | |
| 102 num id = x[2]; | |
| 103 return new _JsSendPortSync(id); | |
| 104 case 'dart': | |
| 105 num isolateId = x[2]; | |
| 106 num portId = x[3]; | |
| 107 return ReceivePortSync._lookup(isolateId, portId); | |
| 108 default: | |
| 109 throw 'Illegal SendPortSync type: $tag'; | |
| 110 } | |
| 111 } | |
| 112 | |
| 113 deserializeObject(List x) { | |
| 114 String tag = x[0]; | |
| 115 switch (tag) { | |
| 116 case 'funcref': return deserializeFunction(x); | |
| 117 default: throw 'Illegal object type: $x'; | |
| 118 } | |
| 119 } | |
| 120 | |
| 121 deserializeFunction(List x) { | |
| 122 var id = x[1]; | |
| 123 SendPortSync port = deserializeSendPort(x[2]); | |
| 124 // TODO: Support varargs when there is support in the language. | |
| 125 return ([arg0 = _UNSPECIFIED, arg1 = _UNSPECIFIED, | |
| 126 arg2 = _UNSPECIFIED, arg3 = _UNSPECIFIED]) { | |
| 127 var args = [arg0, arg1, arg2, arg3]; | |
| 128 var last = args.indexOf(_UNSPECIFIED); | |
| 129 if (last >= 0) args = args.getRange(0, last); | |
| 130 var message = [id, args]; | |
| 131 return port.callSync(message); | |
| 132 }; | |
| 133 } | |
| 134 } | |
| 135 | |
| 136 // The receiver is JS. | |
| 137 class _JsSendPortSync implements SendPortSync { | |
| 138 | |
| 139 num _id; | |
| 140 _JsSendPortSync(this._id); | |
| 141 | |
| 142 callSync(var message) { | |
| 143 var serialized = _serialize(message); | |
| 144 var result = _callPortSync(_id, serialized); | |
| 145 return _deserialize(result); | |
| 146 } | |
| 147 | |
| 148 } | |
| 149 | |
| 150 // TODO(vsm): Differentiate between Dart2Js and Dartium isolates. | |
| 151 // The receiver is a different Dart isolate, compiled to JS. | |
| 152 class _RemoteSendPortSync implements SendPortSync { | |
| 153 | |
| 154 int _isolateId; | |
| 155 int _portId; | |
| 156 _RemoteSendPortSync(this._isolateId, this._portId); | |
| 157 | |
| 158 callSync(var message) { | |
| 159 var serialized = _serialize(message); | |
| 160 var result = _call(_isolateId, _portId, serialized); | |
| 161 return _deserialize(result); | |
| 162 } | |
| 163 | |
| 164 static _call(int isolateId, int portId, var message) { | |
| 165 var target = 'dart-port-$isolateId-$portId'; | |
| 166 // TODO(vsm): Make this re-entrant. | |
| 167 // TODO(vsm): Set this up set once, on the first call. | |
| 168 var source = '$target-result'; | |
| 169 var result = null; | |
| 170 var listener = (TextEvent e) { | |
| 171 result = JSON.parse(e.data); | |
| 172 }; | |
| 173 window.on[source].add(listener); | |
| 174 _dispatchEvent(target, [source, message]); | |
| 175 window.on[source].remove(listener); | |
| 176 return result; | |
| 177 } | |
| 178 } | |
| 179 | |
| 180 // The receiver is in the same Dart isolate, compiled to JS. | |
| 181 class _LocalSendPortSync implements SendPortSync { | |
| 182 | |
| 183 ReceivePortSync _receivePort; | |
| 184 | |
| 185 _LocalSendPortSync._internal(this._receivePort); | |
| 186 | |
| 187 callSync(var message) { | |
| 188 // TODO(vsm): Do a more efficient deep copy. | |
| 189 var copy = _deserialize(_serialize(message)); | |
| 190 var result = _receivePort._callback(copy); | |
| 191 return _deserialize(_serialize(result)); | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 // TODO(vsm): Move this to dart:isolate. This will take some | |
| 196 // refactoring as there are dependences here on the DOM. Users | |
| 197 // interact with this class (or interface if we change it) directly - | |
| 198 // new ReceivePortSync. I think most of the DOM logic could be | |
| 199 // delayed until the corresponding SendPort is registered on the | |
| 200 // window. | |
| 201 | |
| 202 // A Dart ReceivePortSync (tagged 'dart' when serialized) is | |
| 203 // identifiable / resolvable by the combination of its isolateid and | |
| 204 // portid. When a corresponding SendPort is used within the same | |
| 205 // isolate, the _portMap below can be used to obtain the | |
| 206 // ReceivePortSync directly. Across isolates (or from JS), an | |
| 207 // EventListener can be used to communicate with the port indirectly. | |
| 208 class ReceivePortSync { | |
| 209 | |
| 210 static Map<int, ReceivePortSync> _portMap; | |
| 211 static int _portIdCount; | |
| 212 static int _cachedIsolateId; | |
| 213 | |
| 214 num _portId; | |
| 215 Function _callback; | |
| 216 EventListener _listener; | |
| 217 | |
| 218 ReceivePortSync() { | |
| 219 if (_portIdCount == null) { | |
| 220 _portIdCount = 0; | |
| 221 _portMap = new Map<int, ReceivePortSync>(); | |
| 222 } | |
| 223 _portId = _portIdCount++; | |
| 224 _portMap[_portId] = this; | |
| 225 } | |
| 226 | |
| 227 static int get _isolateId() { | |
| 228 // TODO(vsm): Make this coherent with existing isolate code. | |
| 229 if (_cachedIsolateId == null) { | |
| 230 _cachedIsolateId = _getNewIsolateId(); | |
| 231 } | |
| 232 return _cachedIsolateId; | |
| 233 } | |
| 234 | |
| 235 static String _getListenerName(isolateId, portId) => | |
| 236 'dart-port-$isolateId-$portId'; | |
| 237 String get _listenerName() => _getListenerName(_isolateId, _portId); | |
| 238 | |
| 239 void receive(callback(var message)) { | |
| 240 _callback = callback; | |
| 241 if (_listener === null) { | |
| 242 _listener = (TextEvent e) { | |
| 243 var data = JSON.parse(e.data); | |
| 244 var replyTo = data[0]; | |
| 245 var message = _deserialize(data[1]); | |
| 246 var result = _callback(message); | |
| 247 _dispatchEvent(replyTo, _serialize(result)); | |
| 248 }; | |
| 249 window.on[_listenerName].add(_listener); | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 void close() { | |
| 254 _portMap.remove(_portId); | |
| 255 if (_listener !== null) window.on[_listenerName].remove(_listener); | |
| 256 } | |
| 257 | |
| 258 SendPortSync toSendPort() { | |
| 259 return new _LocalSendPortSync._internal(this); | |
| 260 } | |
| 261 | |
| 262 static SendPortSync _lookup(int isolateId, int portId) { | |
| 263 if (isolateId == _isolateId) { | |
| 264 return _portMap[portId].toSendPort(); | |
| 265 } else { | |
| 266 return new _RemoteSendPortSync(isolateId, portId); | |
| 267 } | |
| 268 } | |
| 269 } | |
| 270 | |
| 271 void _dispatchEvent(String receiver, var message) { | |
| 272 var event = document.$dom_createEvent('TextEvent'); | |
| 273 event.initTextEvent(receiver, false, false, window, JSON.stringify(message)); | |
| 274 window.$dom_dispatchEvent(event); | |
| 275 } | |
| OLD | NEW |