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