OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2014, 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 _isolate_helper; |
| 6 |
| 7 /// Serialize [message]. |
| 8 _serializeMessage(message) { |
| 9 return new _Serializer().serialize(message); |
| 10 } |
| 11 |
| 12 /// Deserialize [message]. |
| 13 _deserializeMessage(message) { |
| 14 return new _Deserializer().deserialize(message); |
| 15 } |
| 16 |
| 17 /// Clones the message. |
| 18 /// |
| 19 /// Contrary to a `_deserializeMessage(_serializeMessage(x))` the `_clone` |
| 20 /// function will not try to adjust SendPort values and pass them through. |
| 21 _clone(message) { |
| 22 _Serializer serializer = new _Serializer(serializeSendPorts: false); |
| 23 _Deserializer deserializer = new _Deserializer(); |
| 24 return deserializer.deserialize(serializer.serialize(message)); |
| 25 } |
| 26 |
| 27 class _Serializer { |
| 28 final bool _serializeSendPorts; |
| 29 Map<dynamic, int> serializedObjectIds = new Map<dynamic, int>.identity(); |
| 30 |
| 31 _Serializer({serializeSendPorts: true}) |
| 32 : _serializeSendPorts = serializeSendPorts; |
| 33 |
| 34 /// Returns a message that can be transmitted through web-worker channels. |
| 35 serialize(x) { |
| 36 if (isPrimitive(x)) return serializePrimitive(x); |
| 37 |
| 38 int serializationId = serializedObjectIds[x]; |
| 39 if (serializationId != null) return makeRef(serializationId); |
| 40 |
| 41 serializationId = serializedObjectIds.length; |
| 42 serializedObjectIds[x] = serializationId; |
| 43 |
| 44 if (x is NativeByteBuffer) return serializeByteBuffer(x); |
| 45 if (x is NativeTypedData) return serializeTypedData(x); |
| 46 if (x is JSIndexable) return serializeJSIndexable(x); |
| 47 if (x is InternalMap) return serializeMap(x); |
| 48 |
| 49 if (x is JSObject) return serializeJSObject(x); |
| 50 |
| 51 // We should not have any interceptors any more. |
| 52 if (x is Interceptor) unsupported(x); |
| 53 |
| 54 if (x is RawReceivePort) { |
| 55 unsupported(x, "RawReceivePorts can't be transmitted:"); |
| 56 } |
| 57 |
| 58 // SendPorts need their workerIds adjusted (either during serialization or |
| 59 // deserialization). |
| 60 if (x is _NativeJsSendPort) return serializeJsSendPort(x); |
| 61 if (x is _WorkerSendPort) return serializeWorkerSendPort(x); |
| 62 |
| 63 if (x is Closure) return serializeClosure(x); |
| 64 |
| 65 return serializeDartObject(x); |
| 66 } |
| 67 |
| 68 void unsupported(x, [String message]) { |
| 69 if (message == null) message = "Can't transmit:"; |
| 70 throw new UnsupportedError("$message $x"); |
| 71 } |
| 72 |
| 73 makeRef(int serializationId) => ["ref", serializationId]; |
| 74 |
| 75 bool isPrimitive(x) => x == null || x is String || x is num || x is bool; |
| 76 serializePrimitive(primitive) => primitive; |
| 77 |
| 78 serializeByteBuffer(NativeByteBuffer buffer) { |
| 79 return ["buffer", buffer]; |
| 80 } |
| 81 |
| 82 serializeTypedData(NativeTypedData data) { |
| 83 return ["typed", data]; |
| 84 } |
| 85 |
| 86 serializeJSIndexable(JSIndexable indexable) { |
| 87 // Strings are JSIndexable but should have been treated earlier. |
| 88 assert(indexable is! String); |
| 89 List serialized = serializeArray(indexable); |
| 90 if (indexable is JSFixedArray) return ["fixed", serialized]; |
| 91 if (indexable is JSExtendableArray) return ["extendable", serialized]; |
| 92 // MutableArray check must be last, since JSFixedArray and JSExtendableArray |
| 93 // extend JSMutableArray. |
| 94 if (indexable is JSMutableArray) return ["mutable", serialized]; |
| 95 // The only JSArrays left are the const Lists (as in `const [1, 2]`). |
| 96 if (indexable is JSArray) return ["const", serialized]; |
| 97 unsupported(indexable, "Can't serialize indexable: "); |
| 98 return null; |
| 99 } |
| 100 |
| 101 serializeArray(JSArray x) { |
| 102 List serialized = []; |
| 103 serialized.length = x.length; |
| 104 for (int i = 0; i < x.length; i++) { |
| 105 serialized[i] = serialize(x[i]); |
| 106 } |
| 107 return serialized; |
| 108 } |
| 109 |
| 110 serializeArrayInPlace(JSArray x) { |
| 111 for (int i = 0; i < x.length; i++) { |
| 112 x[i] = serialize(x[i]); |
| 113 } |
| 114 return x; |
| 115 } |
| 116 |
| 117 serializeMap(Map x) { |
| 118 Function serializeTearOff = serialize; |
| 119 return ['map', |
| 120 x.keys.map(serializeTearOff).toList(), |
| 121 x.values.map(serializeTearOff).toList()]; |
| 122 } |
| 123 |
| 124 serializeJSObject(JSObject x) { |
| 125 // Don't serialize objects if their `constructor` property isn't `Object` |
| 126 // or undefined/null. |
| 127 // A different constructor is taken as a sign that the object has complex |
| 128 // internal state, or that it is a function, and won't be serialized. |
| 129 if (JS('bool', '!!(#.constructor)', x) && |
| 130 JS('bool', 'x.constructor !== Object')) { |
| 131 unsupported(x, "Only plain JS Objects are supported:"); |
| 132 } |
| 133 List keys = JS('JSArray', 'Object.keys(#)', x); |
| 134 List values = []; |
| 135 values.length = keys.length; |
| 136 for (int i = 0; i < keys.length; i++) { |
| 137 values[i] = serialize(JS('', '#[#]', x, keys[i])); |
| 138 } |
| 139 return ['js-object', keys, values]; |
| 140 } |
| 141 |
| 142 serializeWorkerSendPort(_WorkerSendPort x) { |
| 143 if (_serializeSendPorts) { |
| 144 return ['sendport', x._workerId, x._isolateId, x._receivePortId]; |
| 145 } |
| 146 return ['raw sendport', x]; |
| 147 } |
| 148 |
| 149 serializeJsSendPort(_NativeJsSendPort x) { |
| 150 if (_serializeSendPorts) { |
| 151 int workerId = _globalState.currentManagerId; |
| 152 return ['sendport', workerId, x._isolateId, x._receivePort._id]; |
| 153 } |
| 154 return ['raw sendport', x]; |
| 155 } |
| 156 |
| 157 serializeCapability(CapabilityImpl x) => ['capability', x._id]; |
| 158 |
| 159 serializeClosure(Closure x) { |
| 160 final name = IsolateNatives._getJSFunctionName(x); |
| 161 if (name == null) { |
| 162 unsupported(x, "Closures can't be transmitted:"); |
| 163 } |
| 164 return ['function', name]; |
| 165 } |
| 166 |
| 167 serializeDartObject(x) { |
| 168 var classExtractor = JS_EMBEDDED_GLOBAL('', CLASS_ID_EXTRACTOR); |
| 169 var fieldsExtractor = JS_EMBEDDED_GLOBAL('', CLASS_FIELDS_EXTRACTOR); |
| 170 String classId = JS('String', '#(#)', classExtractor, x); |
| 171 List fields = JS('JSArray', '#(#)', fieldsExtractor, x); |
| 172 return ['dart', classId, serializeArrayInPlace(fields)]; |
| 173 } |
| 174 } |
| 175 |
| 176 class _Deserializer { |
| 177 /// When `true`, encodes sendports specially so that they can be adjusted on |
| 178 /// the receiving end. |
| 179 /// |
| 180 /// When `false`, sendports are cloned like any other object. |
| 181 final bool _adjustSendPorts; |
| 182 |
| 183 List<dynamic> deserializedObjects = new List<dynamic>(); |
| 184 |
| 185 _Deserializer({adjustSendPorts: true}) : _adjustSendPorts = adjustSendPorts; |
| 186 |
| 187 /// Returns a message that can be transmitted through web-worker channels. |
| 188 deserialize(x) { |
| 189 if (isPrimitive(x)) return deserializePrimitive(x); |
| 190 |
| 191 if (x is! JSArray) throw new ArgumentError("Bad serialized message: $x"); |
| 192 |
| 193 switch (x.first) { |
| 194 case "ref": return deserializeRef(x); |
| 195 case "buffer": return deserializeByteBuffer(x); |
| 196 case "typed": return deserializeTypedData(x); |
| 197 case "fixed": return deserializeFixed(x); |
| 198 case "extendable": return deserializeExtendable(x); |
| 199 case "mutable": return deserializeMutable(x); |
| 200 case "const": return deserializeConst(x); |
| 201 case "map": return deserializeMap(x); |
| 202 case "sendport": return deserializeSendPort(x); |
| 203 case "raw sendport": return deserializeRawSendPort(x); |
| 204 case "js-object": return deserializeJSObject(x); |
| 205 case "function": return deserializeClosure(x); |
| 206 case "dart": return deserializeDartObject(x); |
| 207 default: throw "couldn't deserialize: $x"; |
| 208 } |
| 209 } |
| 210 |
| 211 bool isPrimitive(x) => x == null || x is String || x is num || x is bool; |
| 212 deserializePrimitive(x) => x; |
| 213 |
| 214 // ['ref', id]. |
| 215 deserializeRef(x) { |
| 216 assert(x[0] == 'ref'); |
| 217 int serializationId = x[1]; |
| 218 return deserializedObjects[serializationId]; |
| 219 } |
| 220 |
| 221 // ['buffer', <byte buffer>]. |
| 222 NativeByteBuffer deserializeByteBuffer(x) { |
| 223 assert(x[0] == 'buffer'); |
| 224 NativeByteBuffer result = x[1]; |
| 225 deserializedObjects.add(result); |
| 226 return result; |
| 227 } |
| 228 |
| 229 // ['typed', <typed array>]. |
| 230 NativeTypedData deserializeTypedData(x) { |
| 231 assert(x[0] == 'typed'); |
| 232 NativeTypedData result = x[1]; |
| 233 deserializedObjects.add(result); |
| 234 return result; |
| 235 } |
| 236 |
| 237 // Updates the given array in place with its deserialized content. |
| 238 List deserializeArrayInPlace(JSArray x) { |
| 239 for (int i = 0; i < x.length; i++) { |
| 240 x[i] = deserialize(x[i]); |
| 241 } |
| 242 return x; |
| 243 } |
| 244 |
| 245 // ['fixed', <array>]. |
| 246 List deserializeFixed(x) { |
| 247 assert(x[0] == 'fixed'); |
| 248 List result = x[1]; |
| 249 deserializedObjects.add(result); |
| 250 return new JSArray.markFixed(deserializeArrayInPlace(result)); |
| 251 } |
| 252 |
| 253 // ['extendable', <array>]. |
| 254 List deserializeExtendable(x) { |
| 255 assert(x[0] == 'extendable'); |
| 256 List result = x[1]; |
| 257 deserializedObjects.add(result); |
| 258 return new JSArray.markGrowable(deserializeArrayInPlace(result)); |
| 259 } |
| 260 |
| 261 // ['mutable', <array>]. |
| 262 List deserializeMutable(x) { |
| 263 assert(x[0] == 'mutable'); |
| 264 List result = x[1]; |
| 265 deserializedObjects.add(result); |
| 266 return deserializeArrayInPlace(result); |
| 267 } |
| 268 |
| 269 // ['const', <array>]. |
| 270 List deserializeConst(x) { |
| 271 assert(x[0] == 'const'); |
| 272 List result = x[1]; |
| 273 deserializedObjects.add(result); |
| 274 // TODO(floitsch): need to mark list as non-changeable. |
| 275 return new JSArray.markFixed(deserializeArrayInPlace(result)); |
| 276 } |
| 277 |
| 278 // ['map', <key-list>, <value-list>]. |
| 279 Map deserializeMap(x) { |
| 280 assert(x[0] == 'map'); |
| 281 List keys = x[1]; |
| 282 List values = x[2]; |
| 283 Map result = {}; |
| 284 deserializedObjects.add(result); |
| 285 // We need to keep the order of how objects were serialized. |
| 286 // First deserialize all keys, and then only deserialize the values. |
| 287 keys = keys.map(deserialize).toList(); |
| 288 |
| 289 for (int i = 0; i < keys.length; i++) { |
| 290 result[keys[i]] = deserialize(values[i]); |
| 291 } |
| 292 return result; |
| 293 } |
| 294 |
| 295 // ['sendport', <managerId>, <isolateId>, <receivePortId>]. |
| 296 SendPort deserializeSendPort(x) { |
| 297 assert(x[0] == 'sendport'); |
| 298 int managerId = x[1]; |
| 299 int isolateId = x[2]; |
| 300 int receivePortId = x[3]; |
| 301 SendPort result; |
| 302 // If two isolates are in the same manager, we use NativeJsSendPorts to |
| 303 // deliver messages directly without using postMessage. |
| 304 if (managerId == _globalState.currentManagerId) { |
| 305 var isolate = _globalState.isolates[isolateId]; |
| 306 if (isolate == null) return null; // Isolate has been closed. |
| 307 var receivePort = isolate.lookup(receivePortId); |
| 308 if (receivePort == null) return null; // Port has been closed. |
| 309 result = new _NativeJsSendPort(receivePort, isolateId); |
| 310 } else { |
| 311 result = new _WorkerSendPort(managerId, isolateId, receivePortId); |
| 312 } |
| 313 deserializedObjects.add(result); |
| 314 return result; |
| 315 } |
| 316 |
| 317 // ['raw sendport', <sendport>]. |
| 318 SendPort deserializeRawSendPort(x) { |
| 319 assert(x[0] == 'raw sendport'); |
| 320 SendPort result = x[1]; |
| 321 deserializedObjects.add(result); |
| 322 return result; |
| 323 } |
| 324 |
| 325 // ['js-object', <key-list>, <value-list>]. |
| 326 deserializeJSObject(x) { |
| 327 assert(x[0] == 'js-object'); |
| 328 List keys = x[1]; |
| 329 List values = x[2]; |
| 330 var o = JS('', '{}'); |
| 331 deserializedObjects.add(o); |
| 332 for (int i = 0; i < keys.length; i++) { |
| 333 JS('', '#[#]=#', o, keys[i], deserialize(values[i])); |
| 334 } |
| 335 return o; |
| 336 } |
| 337 |
| 338 // ['function', <name>]. |
| 339 Function deserializeClosure(x) { |
| 340 assert(x[0] == 'function'); |
| 341 String name = x[1]; |
| 342 Function result = IsolateNatives._getJSFunctionFromName(name); |
| 343 deserializedObjects.add(result); |
| 344 return result; |
| 345 } |
| 346 |
| 347 // ['dart', <class-id>, <field-list>]. |
| 348 deserializeDartObject(x) { |
| 349 assert(x[0] == 'dart'); |
| 350 String classId = x[1]; |
| 351 List fields = x[2]; |
| 352 var instanceFromClassId = JS_EMBEDDED_GLOBAL('', INSTANCE_FROM_CLASS_ID); |
| 353 var initializeObject = JS_EMBEDDED_GLOBAL('', INITIALIZE_EMPTY_INSTANCE); |
| 354 |
| 355 var emptyInstance = JS('', '#(#)', instanceFromClassId, classId); |
| 356 deserializedObjects.add(emptyInstance); |
| 357 deserializeArrayInPlace(fields); |
| 358 return JS('', '#(#, #, #)', |
| 359 initializeObject, classId, emptyInstance, fields); |
| 360 } |
| 361 } |
OLD | NEW |