Chromium Code Reviews| 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 id = serializedObjectIds[x]; | |
| 39 if (id != null) return makeRef(id); | |
| 40 | |
| 41 id = serializedObjectIds.length; | |
| 42 serializedObjectIds[x] = id; | |
| 43 | |
| 44 if (x is JSIndexable) return serializeJSIndexable(x, id); | |
| 45 if (x is InternalMap) return serializeMap(x, id); | |
| 46 | |
| 47 if (x is JSObject) return serializeJSObject(x, id); | |
| 48 | |
| 49 // We should not have any interceptors any more. | |
| 50 if (x is Interceptor) unsupported(x); | |
| 51 | |
| 52 if (x is RawReceivePort) { | |
| 53 unsupported(x, "RawReceivePorts can't be transmitted:"); | |
| 54 } | |
| 55 | |
| 56 // SendPorts need their workerIds adjusted (either during serialization or | |
| 57 // deserialization). | |
| 58 if (x is _NativeJsSendPort) return serializeJsSendPort(x, id); | |
| 59 if (x is _WorkerSendPort) return serializeWorkerSendPort(x, id); | |
| 60 | |
| 61 if (x is Closure) return serializeClosure(x, id); | |
| 62 | |
| 63 return serializeAny(x, id); | |
| 64 } | |
| 65 | |
| 66 void unsupported(x, [String message]) { | |
| 67 if (message == null) message = "Can't transmit:"; | |
| 68 throw new UnsupportedError("$message $x"); | |
| 69 } | |
| 70 | |
| 71 makeRef(int id) => ["ref", id]; | |
| 72 | |
| 73 bool isPrimitive(x) => x == null || x is String || x is num || x is bool; | |
| 74 serializePrimitive(primitive) => primitive; | |
| 75 | |
| 76 serializeJSIndexable(JSIndexable indexable, int id) { | |
| 77 // Strings are JSIndexable but should have been treated earlier. | |
| 78 assert(indexable is! String); | |
| 79 if (indexable is TypedData) { | |
| 80 return ["typed", id, indexable]; | |
| 81 } | |
| 82 List serialized = serializeArray(indexable); | |
| 83 if (indexable is JSFixedArray) return ["fixed", id, serialized]; | |
| 84 if (indexable is JSExtendableArray) return ["extendable", id, serialized]; | |
| 85 // MutableArray check must be last, since JSFixedArray and JSExtendableArray | |
| 86 // extend JSMutableArray. | |
| 87 if (indexable is JSMutableArray) return ["mutable", id, serialized]; | |
| 88 if (indexable is JSArray) return ["const", id, serialized]; | |
|
sigurdm
2014/11/24 09:06:09
Maybe use a different keyword. 'const' has a certa
Lasse Reichstein Nielsen
2014/11/24 12:55:41
This is a const array (created using `const[...]`)
floitsch
2014/11/24 15:06:57
This is the const List (as in const [1, 2]). Since
floitsch
2014/11/24 15:06:57
Yes. Since it is not a JSMutableArray it must be c
| |
| 89 unsupported(indexable, "Can't serialize indexable: "); | |
| 90 return null; | |
| 91 } | |
| 92 | |
| 93 serializeArray(JSArray x) { | |
| 94 List serialized = []; | |
| 95 serialized.length = x.length; | |
| 96 for (int i = 0; i < x.length; i++) { | |
| 97 serialized[i] = serialize(x[i]); | |
| 98 } | |
| 99 return serialized; | |
| 100 } | |
| 101 | |
| 102 serializeArrayInPlace(JSArray x) { | |
| 103 for (int i = 0; i < x.length; i++) { | |
| 104 x[i] = serialize(x[i]); | |
| 105 } | |
| 106 return x; | |
| 107 } | |
| 108 | |
| 109 serializeMap(Map x, int id) { | |
| 110 return ['map', id, | |
|
Lasse Reichstein Nielsen
2014/11/24 12:55:42
Do you have to send the id with the object - can't
floitsch
2014/11/24 15:06:57
I thought it would be less brittle with the ids, b
| |
| 111 x.keys.map(serialize).toList(), | |
| 112 x.values.map(serialize).toList()]; | |
| 113 } | |
| 114 | |
| 115 serializeJSObject(JSObject x, int id) { | |
| 116 // TODO(floitsch): weed out more JS objects. | |
|
Lasse Reichstein Nielsen
2014/11/24 12:55:41
Document what you are trying to guard against. The
floitsch
2014/11/24 15:06:57
Changed to checking the constructor, which is what
| |
| 117 if (JS('bool', '(typeof #.__proto__) !== "undefined"', x) && | |
| 118 JS('bool', '(typeof #.__proto__) !== "object"', x)) { | |
|
Lasse Reichstein Nielsen
2014/11/24 12:55:41
Are there any prototypes that don't have typeof "o
floitsch
2014/11/24 15:06:56
Functions have "function" as type for the prototyp
| |
| 119 unsupported(x, "Only JS Objects without custom prototype are supported:"); | |
| 120 } | |
| 121 List keys = JS('JSArray', 'Object.keys(#)', x); | |
| 122 List values = []; | |
| 123 values.length = keys.length; | |
| 124 for (int i = 0; i < keys.length; i++) { | |
| 125 values[i] = serialize(JS('', '#[#]', x, keys[i])); | |
| 126 } | |
| 127 return ['js-object', id, keys, values]; | |
| 128 } | |
| 129 | |
| 130 serializeWorkerSendPort(_WorkerSendPort x, int id) { | |
| 131 if (_serializeSendPorts) { | |
| 132 return ['sendport', id, x._workerId, x._isolateId, x._receivePortId]; | |
| 133 } | |
| 134 return ['raw sendport', x]; | |
| 135 } | |
| 136 | |
| 137 serializeJsSendPort(_NativeJsSendPort x, int id) { | |
| 138 if (_serializeSendPorts) { | |
| 139 int workerId = _globalState.currentManagerId; | |
| 140 return ['sendport', id, workerId, x._isolateId, x._receivePort._id]; | |
| 141 } | |
| 142 return ['raw sendport', x]; | |
| 143 } | |
| 144 | |
| 145 serializeCapability(CapabilityImpl x, int id) => ['capability', id, x._id]; | |
| 146 | |
| 147 serializeClosure(Closure x, int id) { | |
| 148 final name = IsolateNatives._getJSFunctionName(x); | |
| 149 if (name == null) { | |
| 150 unsupported(x, "Closures can't be transmitted:"); | |
| 151 } | |
| 152 return ['function', id, name]; | |
| 153 } | |
| 154 | |
| 155 serializeAny(x, int id) { | |
| 156 var classExtractor = JS_EMBEDDED_GLOBAL('', CLASS_ID_EXTRACTOR); | |
| 157 var fieldsExtractor = JS_EMBEDDED_GLOBAL('', CLASS_FIELDS_EXTRACTOR); | |
| 158 String classId = JS('String', '#(#)', classExtractor, x); | |
| 159 List fields = JS('JSArray', '#(#)', fieldsExtractor, x); | |
| 160 return ['any', id, classId, serializeArrayInPlace(fields)]; | |
| 161 } | |
| 162 } | |
| 163 | |
| 164 class _Deserializer { | |
| 165 /// When `true`, encodes sendports specially so that they can be adjusted on | |
| 166 /// the receiving end. | |
| 167 /// | |
| 168 /// When `false`, sendports are cloned like any other object. | |
| 169 final bool _adjustSendPorts; | |
| 170 | |
| 171 Map<int, dynamic> deserializedObjects = new Map<int, dynamic>(); | |
| 172 | |
| 173 _Deserializer({adjustSendPorts: true}) : _adjustSendPorts = adjustSendPorts; | |
| 174 | |
| 175 /// Returns a message that can be transmitted through web-worker channels. | |
| 176 deserialize(x) { | |
| 177 if (isPrimitive(x)) return deserializePrimitive(x); | |
| 178 | |
| 179 assert(x is List); | |
| 180 switch (x.first) { | |
| 181 case "ref": return deserializeRef(x); | |
| 182 case "typed": return deserializeTyped(x); | |
| 183 case "fixed": return deserializeFixed(x); | |
| 184 case "extendable": return deserializeExtendable(x); | |
| 185 case "mutable": return deserializeMutable(x); | |
| 186 case "const": return deserializeConst(x); | |
| 187 case "map": return deserializeMap(x); | |
| 188 case "sendport": return deserializeSendPort(x); | |
| 189 case "raw sendport": return deserializeRawSendPort(x); | |
| 190 case "js-object": return deserializeJSObject(x); | |
| 191 case "function": return deserializeClosure(x); | |
| 192 case "any": return deserializeAny(x); | |
| 193 default: throw "couldn't deserialize: $x"; | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 bool isPrimitive(x) => x == null || x is String || x is num || x is bool; | |
| 198 deserializePrimitive(x) => x; | |
| 199 | |
| 200 // ['ref', id]. | |
| 201 deserializeRef(x) { | |
| 202 assert(x[0] == 'ref'); | |
| 203 int id = x[1]; | |
| 204 return deserializedObjects[id]; | |
| 205 } | |
| 206 | |
| 207 // ['typed', id, <typed array>]. | |
| 208 TypedData deserializeTyped(x) { | |
| 209 assert(x[0] == 'typed'); | |
| 210 int id = x[1]; | |
| 211 TypedData result = x[2]; | |
| 212 deserializedObjects[id] = result; | |
| 213 return result; | |
| 214 } | |
| 215 | |
| 216 // Updates the given array in place with its deserialized content. | |
| 217 List deserializeArrayInPlace(JSArray x) { | |
| 218 for (int i = 0; i < x.length; i++) { | |
| 219 x[i] = deserialize(x[i]); | |
| 220 } | |
| 221 return x; | |
| 222 } | |
| 223 | |
| 224 // ['fixed', id, <array>]. | |
| 225 List deserializeFixed(x) { | |
| 226 assert(x[0] == 'fixed'); | |
| 227 int id = x[1]; | |
| 228 List result = x[2]; | |
| 229 deserializedObjects[id] = result; | |
| 230 return new JSArray.markFixed(deserializeArrayInPlace(result)); | |
| 231 } | |
| 232 | |
| 233 // ['extendable', id, <array>]. | |
| 234 List deserializeExtendable(x) { | |
| 235 assert(x[0] == 'extendable'); | |
| 236 int id = x[1]; | |
| 237 List result = x[2]; | |
| 238 deserializedObjects[id] = result; | |
| 239 return new JSArray.markGrowable(deserializeArrayInPlace(result)); | |
| 240 } | |
| 241 | |
| 242 // ['mutable', id, <array>]. | |
| 243 List deserializeMutable(x) { | |
| 244 assert(x[0] == 'mutable'); | |
| 245 int id = x[1]; | |
| 246 List result = x[2]; | |
| 247 deserializedObjects[id] = result; | |
| 248 return deserializeArrayInPlace(result); | |
| 249 } | |
| 250 | |
| 251 // ['const', id, <array>]. | |
| 252 List deserializeConst(x) { | |
| 253 assert(x[0] == 'const'); | |
| 254 int id = x[1]; | |
| 255 List result = x[2]; | |
| 256 deserializedObjects[id] = result; | |
| 257 // TODO(floitsch): need to mark list as non-changeable. | |
| 258 return new JSArray.markFixed(deserializeArrayInPlace(result)); | |
| 259 } | |
| 260 | |
| 261 // ['map', id, <key-list>, <value-list>]. | |
| 262 Map deserializeMap(x) { | |
| 263 assert(x[0] == 'map'); | |
| 264 int id = x[1]; | |
| 265 List keys = x[2]; | |
| 266 List values = x[3]; | |
| 267 Map result = {}; | |
| 268 deserializedObjects[x[1]] = result; | |
| 269 for (int i = 0; i < keys.length; i++) { | |
| 270 result[deserialize(keys[i])] = deserialize(values[i]); | |
| 271 } | |
| 272 return result; | |
|
Lasse Reichstein Nielsen
2014/11/24 12:55:41
This special casing of just a single Map implement
floitsch
2014/11/24 15:06:56
This is just a special case for the extremely comm
| |
| 273 } | |
| 274 | |
| 275 // ['sendport', id, <managerId>, <isolateId>, <receivePortId>]. | |
| 276 SendPort deserializeSendPort(x) { | |
| 277 assert(x[0] == 'sendport'); | |
| 278 int id = x[1]; | |
| 279 int managerId = x[2]; | |
| 280 int isolateId = x[3]; | |
| 281 int receivePortId = x[4]; | |
| 282 SendPort result; | |
| 283 // If two isolates are in the same manager, we use NativeJsSendPorts to | |
| 284 // deliver messages directly without using postMessage. | |
| 285 if (managerId == _globalState.currentManagerId) { | |
| 286 var isolate = _globalState.isolates[isolateId]; | |
| 287 if (isolate == null) return null; // Isolate has been closed. | |
| 288 var receivePort = isolate.lookup(receivePortId); | |
| 289 if (receivePort == null) return null; // Port has been closed. | |
| 290 result = new _NativeJsSendPort(receivePort, isolateId); | |
| 291 } else { | |
| 292 result = new _WorkerSendPort(managerId, isolateId, receivePortId); | |
| 293 } | |
| 294 deserializedObjects[id] = result; | |
| 295 return result; | |
| 296 } | |
| 297 | |
| 298 // ['raw sendport', <sendport>]. | |
| 299 SendPort deserializeRawSendPort(x) { | |
| 300 assert(x[0] == 'raw sendport'); | |
| 301 return x[1]; | |
|
Lasse Reichstein Nielsen
2014/11/24 12:55:41
This means the SendPort preserves its identity whe
floitsch
2014/11/24 15:06:57
Yes. But anything else would have required big ref
| |
| 302 } | |
| 303 | |
| 304 // ['js-object', id, <key-list>, <value-list>]. | |
| 305 deserializeJSObject(x) { | |
| 306 assert(x[0] == 'js-object'); | |
| 307 int id = x[1]; | |
| 308 List keys = x[2]; | |
| 309 List values = x[3]; | |
| 310 var o = JS('', '{}'); | |
| 311 deserializedObjects[id] = o; | |
| 312 for (int i = 0; i < keys.length; i++) { | |
| 313 JS('', '#[#]=#', o, keys[i], deserialize(values[i])); | |
| 314 } | |
| 315 return o; | |
| 316 } | |
| 317 | |
| 318 // ['function', id, <name>]. | |
| 319 Function deserializeClosure(x) { | |
| 320 assert(x[0] == 'function'); | |
| 321 int id = x[1]; | |
| 322 String name = x[2]; | |
| 323 Function result = IsolateNatives._getJSFunctionFromName(name); | |
| 324 deserializedObjects[id] = result; | |
| 325 return result; | |
| 326 } | |
| 327 | |
| 328 // ['any', id, <class-id>, <field-list>]. | |
| 329 deserializeAny(x) { | |
|
sigurdm
2014/11/24 09:06:09
Maybe name this 'deserializeDartObject'
floitsch
2014/11/24 15:06:56
Done.
| |
| 330 assert(x[0] == 'any'); | |
| 331 int id = x[1]; | |
| 332 String classId = x[2]; | |
| 333 List fields = x[3]; | |
| 334 var instanceFromClassId = JS_EMBEDDED_GLOBAL('', INSTANCE_FROM_CLASS_ID); | |
| 335 var initializeObject = JS_EMBEDDED_GLOBAL('', INITIALIZE_EMPTY_INSTANCE); | |
| 336 | |
| 337 var emptyInstance = JS('', '#(#)', instanceFromClassId, classId); | |
| 338 deserializedObjects[id] = emptyInstance; | |
| 339 deserializeArrayInPlace(fields); | |
| 340 return JS('', '#(#, #, #)', | |
| 341 initializeObject, classId, emptyInstance, fields); | |
| 342 } | |
| 343 } | |
| OLD | NEW |