| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // Defines message visitors, serialization, and deserialization. | 5 // Defines message visitors, serialization, and deserialization. |
| 6 | 6 |
| 7 /** Serialize [message] (or simulate serialization). */ | 7 /** Serialize [message] (or simulate serialization). */ |
| 8 _serializeMessage(message) { | 8 _serializeMessage(message) { |
| 9 if (_globalState.needSerialization) { | 9 if (_globalState.needSerialization) { |
| 10 return new _Serializer().traverse(message); | 10 return new _JsSerializer().traverse(message); |
| 11 } else { | 11 } else { |
| 12 return new _Copier().traverse(message); | 12 return new _JsCopier().traverse(message); |
| 13 } | 13 } |
| 14 } | 14 } |
| 15 | 15 |
| 16 /** Deserialize [message] (or simulate deserialization). */ | 16 /** Deserialize [message] (or simulate deserialization). */ |
| 17 _deserializeMessage(message) { | 17 _deserializeMessage(message) { |
| 18 if (_globalState.needSerialization) { | 18 if (_globalState.needSerialization) { |
| 19 return new _Deserializer().deserialize(message); | 19 return new _JsDeserializer().deserialize(message); |
| 20 } else { | 20 } else { |
| 21 // Nothing more to do. | 21 // Nothing more to do. |
| 22 return message; | 22 return message; |
| 23 } | 23 } |
| 24 } | 24 } |
| 25 | 25 |
| 26 /** Abstract visitor for dart objects that can be sent as isolate messages. */ | 26 class _JsSerializer extends _Serializer { |
| 27 class _MessageTraverser { | |
| 28 | 27 |
| 29 List _taggedObjects; | 28 _JsSerializer() : super() { _visited = new _JsVisitedMap(); } |
| 30 | 29 |
| 31 _MessageTraverser(); | 30 visitSendPort(SendPort x) { |
| 32 | |
| 33 /** Visitor's entry point. */ | |
| 34 traverse(var x) { | |
| 35 if (isPrimitive(x)) return visitPrimitive(x); | |
| 36 _taggedObjects = new List(); | |
| 37 var result; | |
| 38 try { | |
| 39 result = _dispatch(x); | |
| 40 } finally { | |
| 41 _cleanup(); | |
| 42 } | |
| 43 return result; | |
| 44 } | |
| 45 | |
| 46 /** Remove all information injected in the native objects by this visitor. */ | |
| 47 void _cleanup() { | |
| 48 int len = _taggedObjects.length; | |
| 49 for (int i = 0; i < len; i++) { | |
| 50 _clearAttachedInfo(_taggedObjects[i]); | |
| 51 } | |
| 52 _taggedObjects = null; | |
| 53 } | |
| 54 | |
| 55 /** Injects into the native object some information used by the visitor. */ | |
| 56 void _attachInfo(var o, var info) { | |
| 57 _taggedObjects.add(o); | |
| 58 _setAttachedInfo(o, info); | |
| 59 } | |
| 60 | |
| 61 /** Retrieves any information stored in the native object [o]. */ | |
| 62 _getInfo(var o) { | |
| 63 return _getAttachedInfo(o); | |
| 64 } | |
| 65 | |
| 66 _dispatch(var x) { | |
| 67 if (isPrimitive(x)) return visitPrimitive(x); | |
| 68 if (x is List) return visitList(x); | |
| 69 if (x is Map) return visitMap(x); | |
| 70 if (x is _NativeJsSendPort) return visitNativeJsSendPort(x); | 31 if (x is _NativeJsSendPort) return visitNativeJsSendPort(x); |
| 71 if (x is _WorkerSendPort) return visitWorkerSendPort(x); | 32 if (x is _WorkerSendPort) return visitWorkerSendPort(x); |
| 72 if (x is _BufferingSendPort) return visitBufferingSendPort(x); | 33 if (x is _BufferingSendPort) return visitBufferingSendPort(x); |
| 73 // TODO(floitsch): make this a real exception. (which one)? | |
| 74 throw "Message serialization: Illegal value $x passed"; | |
| 75 } | |
| 76 | |
| 77 abstract visitPrimitive(x); | |
| 78 abstract visitList(List x); | |
| 79 abstract visitMap(Map x); | |
| 80 abstract visitNativeJsSendPort(_NativeJsSendPort x); | |
| 81 abstract visitWorkerSendPort(_WorkerSendPort x); | |
| 82 abstract visitBufferingSendPort(_BufferingSendPort x); | |
| 83 | |
| 84 _clearAttachedInfo(var o) native | |
| 85 "o['__MessageTraverser__attached_info__'] = (void 0);"; | |
| 86 | |
| 87 _setAttachedInfo(var o, var info) native | |
| 88 "o['__MessageTraverser__attached_info__'] = info;"; | |
| 89 | |
| 90 _getAttachedInfo(var o) native | |
| 91 "return o['__MessageTraverser__attached_info__'];"; | |
| 92 | |
| 93 _visitNativeOrWorkerPort(SendPort p) { | |
| 94 if (p is _NativeJsSendPort) return visitNativeJsSendPort(p); | |
| 95 if (p is _WorkerSendPort) return visitWorkerSendPort(p); | |
| 96 throw "Illegal underlying port $p"; | 34 throw "Illegal underlying port $p"; |
| 97 } | 35 } |
| 98 | 36 |
| 99 static bool isPrimitive(x) { | 37 visitNativeJsSendPort(_NativeJsSendPort port) { |
| 100 return (x === null) || (x is String) || (x is num) || (x is bool); | 38 return ['sendport', _globalState.currentManagerId, |
| 101 } | 39 port._isolateId, port._receivePort._id]; |
| 102 } | |
| 103 | |
| 104 | |
| 105 /** A visitor that recursively copies a message. */ | |
| 106 class _Copier extends _MessageTraverser { | |
| 107 _Copier() : super(); | |
| 108 | |
| 109 visitPrimitive(x) => x; | |
| 110 | |
| 111 List visitList(List list) { | |
| 112 List copy = _getInfo(list); | |
| 113 if (copy !== null) return copy; | |
| 114 | |
| 115 int len = list.length; | |
| 116 | |
| 117 // TODO(floitsch): we loose the generic type of the List. | |
| 118 copy = new List(len); | |
| 119 _attachInfo(list, copy); | |
| 120 for (int i = 0; i < len; i++) { | |
| 121 copy[i] = _dispatch(list[i]); | |
| 122 } | |
| 123 return copy; | |
| 124 } | 40 } |
| 125 | 41 |
| 126 Map visitMap(Map map) { | 42 visitWorkerSendPort(_WorkerSendPort port) { |
| 127 Map copy = _getInfo(map); | 43 return ['sendport', port._workerId, port._isolateId, port._receivePortId]; |
| 128 if (copy !== null) return copy; | |
| 129 | |
| 130 // TODO(floitsch): we loose the generic type of the map. | |
| 131 copy = new Map(); | |
| 132 _attachInfo(map, copy); | |
| 133 map.forEach((key, val) { | |
| 134 copy[_dispatch(key)] = _dispatch(val); | |
| 135 }); | |
| 136 return copy; | |
| 137 } | 44 } |
| 138 | 45 |
| 139 SendPort visitNativeJsSendPort(_NativeJsSendPort port) { | 46 visitBufferingSendPort(_BufferingSendPort port) { |
| 140 return new _NativeJsSendPort(port._receivePort, port._isolateId); | |
| 141 } | |
| 142 | |
| 143 SendPort visitWorkerSendPort(_WorkerSendPort port) { | |
| 144 return new _WorkerSendPort( | |
| 145 port._workerId, port._isolateId, port._receivePortId); | |
| 146 } | |
| 147 | |
| 148 SendPort visitBufferingSendPort(_BufferingSendPort port) { | |
| 149 if (port._port != null) { | 47 if (port._port != null) { |
| 150 return _visitNativeOrWorkerPort(port._port); | 48 return visitSendPort(port._port); |
| 151 } else { | 49 } else { |
| 152 // TODO(floitsch): Use real exception (which one?). | 50 // TODO(floitsch): Use real exception (which one?). |
| 153 throw | 51 throw |
| 154 "internal error: must call _waitForPendingPorts to ensure all" | 52 "internal error: must call _waitForPendingPorts to ensure all" |
| 155 " ports are resolved at this point."; | 53 " ports are resolved at this point."; |
| 156 } | 54 } |
| 157 } | 55 } |
| 56 |
| 158 } | 57 } |
| 159 | 58 |
| 160 /** Visitor that serializes a message as a JSON array. */ | |
| 161 class _Serializer extends _MessageTraverser { | |
| 162 int _nextFreeRefId = 0; | |
| 163 | 59 |
| 164 _Serializer() : super(); | 60 class _JsCopier extends _Copier { |
| 165 | 61 |
| 166 visitPrimitive(x) => x; | 62 _JsCopier() : super() { _visited = new _JsVisitedMap(); } |
| 167 | 63 |
| 168 visitList(List list) { | 64 visitSendPort(SendPort x) { |
| 169 int copyId = _getInfo(list); | 65 if (x is _NativeJsSendPort) return visitNativeJsSendPort(x); |
| 170 if (copyId !== null) return ['ref', copyId]; | 66 if (x is _WorkerSendPort) return visitWorkerSendPort(x); |
| 171 | 67 if (x is _BufferingSendPort) return visitBufferingSendPort(x); |
| 172 int id = _nextFreeRefId++; | 68 throw "Illegal underlying port $p"; |
| 173 _attachInfo(list, id); | |
| 174 var jsArray = _serializeList(list); | |
| 175 // TODO(floitsch): we are losing the generic type. | |
| 176 return ['list', id, jsArray]; | |
| 177 } | 69 } |
| 178 | 70 |
| 179 visitMap(Map map) { | 71 SendPort visitNativeJsSendPort(_NativeJsSendPort port) { |
| 180 int copyId = _getInfo(map); | 72 return new _NativeJsSendPort(port._receivePort, port._isolateId); |
| 181 if (copyId !== null) return ['ref', copyId]; | |
| 182 | |
| 183 int id = _nextFreeRefId++; | |
| 184 _attachInfo(map, id); | |
| 185 var keys = _serializeList(map.getKeys()); | |
| 186 var values = _serializeList(map.getValues()); | |
| 187 // TODO(floitsch): we are losing the generic type. | |
| 188 return ['map', id, keys, values]; | |
| 189 } | 73 } |
| 190 | 74 |
| 191 visitNativeJsSendPort(_NativeJsSendPort port) { | 75 SendPort visitWorkerSendPort(_WorkerSendPort port) { |
| 192 return ['sendport', _globalState.currentManagerId, | 76 return new _WorkerSendPort( |
| 193 port._isolateId, port._receivePort._id]; | 77 port._workerId, port._isolateId, port._receivePortId); |
| 194 } | 78 } |
| 195 | 79 |
| 196 visitWorkerSendPort(_WorkerSendPort port) { | 80 SendPort visitBufferingSendPort(_BufferingSendPort port) { |
| 197 return ['sendport', port._workerId, port._isolateId, port._receivePortId]; | |
| 198 } | |
| 199 | |
| 200 visitBufferingSendPort(_BufferingSendPort port) { | |
| 201 if (port._port != null) { | 81 if (port._port != null) { |
| 202 return _visitNativeOrWorkerPort(port._port); | 82 return visitSendPort(port._port); |
| 203 } else { | 83 } else { |
| 204 // TODO(floitsch): Use real exception (which one?). | 84 // TODO(floitsch): Use real exception (which one?). |
| 205 throw | 85 throw |
| 206 "internal error: must call _waitForPendingPorts to ensure all" | 86 "internal error: must call _waitForPendingPorts to ensure all" |
| 207 " ports are resolved at this point."; | 87 " ports are resolved at this point."; |
| 208 } | 88 } |
| 209 } | 89 } |
| 210 | 90 |
| 211 _serializeList(List list) { | |
| 212 int len = list.length; | |
| 213 var result = new List(len); | |
| 214 for (int i = 0; i < len; i++) { | |
| 215 result[i] = _dispatch(list[i]); | |
| 216 } | |
| 217 return result; | |
| 218 } | |
| 219 } | 91 } |
| 220 | 92 |
| 221 /** Deserializes arrays created with [_Serializer]. */ | 93 class _JsDeserializer extends _Deserializer { |
| 222 class _Deserializer { | |
| 223 Map<int, Dynamic> _deserialized; | |
| 224 | 94 |
| 225 _Deserializer(); | 95 SendPort deserializeSendPort(List x) { |
| 226 | |
| 227 static bool isPrimitive(x) { | |
| 228 return (x === null) || (x is String) || (x is num) || (x is bool); | |
| 229 } | |
| 230 | |
| 231 deserialize(x) { | |
| 232 if (isPrimitive(x)) return x; | |
| 233 // TODO(floitsch): this should be new HashMap<int, var|Dynamic>() | |
| 234 _deserialized = new HashMap(); | |
| 235 return _deserializeHelper(x); | |
| 236 } | |
| 237 | |
| 238 _deserializeHelper(x) { | |
| 239 if (isPrimitive(x)) return x; | |
| 240 assert(x is List); | |
| 241 switch (x[0]) { | |
| 242 case 'ref': return _deserializeRef(x); | |
| 243 case 'list': return _deserializeList(x); | |
| 244 case 'map': return _deserializeMap(x); | |
| 245 case 'sendport': return _deserializeSendPort(x); | |
| 246 // TODO(floitsch): Use real exception (which one?). | |
| 247 default: throw "Unexpected serialized object"; | |
| 248 } | |
| 249 } | |
| 250 | |
| 251 _deserializeRef(List x) { | |
| 252 int id = x[1]; | |
| 253 var result = _deserialized[id]; | |
| 254 assert(result !== null); | |
| 255 return result; | |
| 256 } | |
| 257 | |
| 258 List _deserializeList(List x) { | |
| 259 int id = x[1]; | |
| 260 // We rely on the fact that Dart-lists are directly mapped to Js-arrays. | |
| 261 List dartList = x[2]; | |
| 262 _deserialized[id] = dartList; | |
| 263 int len = dartList.length; | |
| 264 for (int i = 0; i < len; i++) { | |
| 265 dartList[i] = _deserializeHelper(dartList[i]); | |
| 266 } | |
| 267 return dartList; | |
| 268 } | |
| 269 | |
| 270 Map _deserializeMap(List x) { | |
| 271 Map result = new Map(); | |
| 272 int id = x[1]; | |
| 273 _deserialized[id] = result; | |
| 274 List keys = x[2]; | |
| 275 List values = x[3]; | |
| 276 int len = keys.length; | |
| 277 assert(len == values.length); | |
| 278 for (int i = 0; i < len; i++) { | |
| 279 var key = _deserializeHelper(keys[i]); | |
| 280 var value = _deserializeHelper(values[i]); | |
| 281 result[key] = value; | |
| 282 } | |
| 283 return result; | |
| 284 } | |
| 285 | |
| 286 SendPort _deserializeSendPort(List x) { | |
| 287 int managerId = x[1]; | 96 int managerId = x[1]; |
| 288 int isolateId = x[2]; | 97 int isolateId = x[2]; |
| 289 int receivePortId = x[3]; | 98 int receivePortId = x[3]; |
| 290 // If two isolates are in the same manager, we use NativeJsSendPorts to | 99 // If two isolates are in the same manager, we use NativeJsSendPorts to |
| 291 // deliver messages directly without using postMessage. | 100 // deliver messages directly without using postMessage. |
| 292 if (managerId == _globalState.currentManagerId) { | 101 if (managerId == _globalState.currentManagerId) { |
| 293 var isolate = _globalState.isolates[isolateId]; | 102 var isolate = _globalState.isolates[isolateId]; |
| 294 if (isolate == null) return null; // Isolate has been closed. | 103 if (isolate == null) return null; // Isolate has been closed. |
| 295 var receivePort = isolate.lookup(receivePortId); | 104 var receivePort = isolate.lookup(receivePortId); |
| 296 return new _NativeJsSendPort(receivePort, isolateId); | 105 return new _NativeJsSendPort(receivePort, isolateId); |
| 297 } else { | 106 } else { |
| 298 return new _WorkerSendPort(managerId, isolateId, receivePortId); | 107 return new _WorkerSendPort(managerId, isolateId, receivePortId); |
| 299 } | 108 } |
| 300 } | 109 } |
| 110 |
| 111 } |
| 112 |
| 113 class _JsVisitedMap implements _MessageTraverserVisitedMap { |
| 114 List tagged; |
| 115 |
| 116 /** Retrieves any information stored in the native object [object]. */ |
| 117 operator[](var object) { |
| 118 return _getAttachedInfo(object); |
| 119 } |
| 120 |
| 121 /** Injects some information into the native [object]. */ |
| 122 void operator[]=(var object, var info) { |
| 123 tagged.add(object); |
| 124 _setAttachedInfo(object, info); |
| 125 } |
| 126 |
| 127 /** Get ready to rumble. */ |
| 128 void reset() { |
| 129 assert(tagged == null); |
| 130 tagged = new List(); |
| 131 } |
| 132 |
| 133 /** Remove all information injected in the native objects. */ |
| 134 cleanup() { |
| 135 for (int i = 0, length = tagged.length; i < length; i++) { |
| 136 _clearAttachedInfo(tagged[i]); |
| 137 } |
| 138 tagged = null; |
| 139 } |
| 140 |
| 141 _clearAttachedInfo(var o) native |
| 142 "o['__MessageTraverser__attached_info__'] = (void 0);"; |
| 143 |
| 144 _setAttachedInfo(var o, var info) native |
| 145 "o['__MessageTraverser__attached_info__'] = info;"; |
| 146 |
| 147 _getAttachedInfo(var o) native |
| 148 "return o['__MessageTraverser__attached_info__'];"; |
| 301 } | 149 } |
| 302 | 150 |
| 303 // only visible for testing purposes | 151 // only visible for testing purposes |
| 304 // TODO(sigmund): remove once we can disable privacy for testing (bug #1882) | 152 // TODO(sigmund): remove once we can disable privacy for testing (bug #1882) |
| 305 class TestingOnly { | 153 class TestingOnly { |
| 306 static copy(x) { | 154 static copy(x) { |
| 307 return new _Copier().traverse(x); | 155 return new _JsCopier().traverse(x); |
| 308 } | 156 } |
| 309 | 157 |
| 310 // only visible for testing purposes | 158 // only visible for testing purposes |
| 311 static serialize(x) { | 159 static serialize(x) { |
| 312 _Serializer serializer = new _Serializer(); | 160 _Serializer serializer = new _JsSerializer(); |
| 313 _Deserializer deserializer = new _Deserializer(); | 161 _Deserializer deserializer = new _JsDeserializer(); |
| 314 return deserializer.deserialize(serializer.traverse(x)); | 162 return deserializer.deserialize(serializer.traverse(x)); |
| 315 } | 163 } |
| 316 } | 164 } |
| OLD | NEW |