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 _serialize(var message) { | 5 _serialize(var message) { |
6 return new _JsSerializer().traverse(message); | 6 return new _JsSerializer().traverse(message); |
7 } | 7 } |
8 | 8 |
9 class JsProxy { | |
10 SendPortSync _port; | |
11 final _id; | |
12 | |
13 JsProxy._internal(this._port, this._id); | |
14 | |
15 noSuchMethod(method, args) { | |
16 var result = _port.callSync([_id, method, args]); | |
17 switch (result[0]) { | |
18 case 'return': return result[1]; | |
19 case 'exception': throw result[1]; | |
20 case 'none': throw new NoSuchMethodException(this, method, args); | |
21 default: throw 'Invalid return value'; | |
22 } | |
23 } | |
24 } | |
25 | |
26 int _localNextElementId = 0; | |
27 | |
28 const _DART_ID = 'data-dart_id'; | |
29 | |
30 _elementId(Element e) { | |
31 if (e.attributes.containsKey(_DART_ID)) return e.attributes[_DART_ID]; | |
32 var id = '$_isolateId-${_localNextElementId++}'; | |
33 e.attributes[_DART_ID] = id; | |
34 return id; | |
35 } | |
36 | |
37 Element _getElement(var id) { | |
38 var list = queryAll('[$_DART_ID="$id"]'); | |
39 if (list.length > 1) throw 'Non unique ID: $id'; | |
40 if (list.length == 0) { | |
41 throw 'Only elements attached to document can be serialized: $id'; | |
42 } | |
43 return list[0]; | |
44 } | |
45 | |
46 class _JsSerializer extends _Serializer { | 9 class _JsSerializer extends _Serializer { |
47 | 10 |
48 visitSendPortSync(SendPortSync x) { | 11 visitSendPortSync(SendPortSync x) { |
49 if (x is _JsSendPortSync) return visitJsSendPortSync(x); | 12 if (x is _JsSendPortSync) return visitJsSendPortSync(x); |
50 if (x is _LocalSendPortSync) return visitLocalSendPortSync(x); | 13 if (x is _LocalSendPortSync) return visitLocalSendPortSync(x); |
51 if (x is _RemoteSendPortSync) return visitRemoteSendPortSync(x); | 14 if (x is _RemoteSendPortSync) return visitRemoteSendPortSync(x); |
52 throw "Unknown port type $x"; | 15 throw "Unknown port type $x"; |
53 } | 16 } |
54 | 17 |
55 visitJsSendPortSync(_JsSendPortSync x) { | 18 visitJsSendPortSync(_JsSendPortSync x) { |
56 return [ 'sendport', 'nativejs', x._id ]; | 19 return [ 'sendport', 'nativejs', x._id ]; |
57 } | 20 } |
58 | 21 |
59 visitLocalSendPortSync(_LocalSendPortSync x) { | 22 visitLocalSendPortSync(_LocalSendPortSync x) { |
60 return [ 'sendport', 'dart', | 23 return [ 'sendport', 'dart', |
61 ReceivePortSync._isolateId, x._receivePort._portId ]; | 24 ReceivePortSync._isolateId, x._receivePort._portId ]; |
62 } | 25 } |
63 | 26 |
64 visitRemoteSendPortSync(_RemoteSendPortSync x) { | 27 visitRemoteSendPortSync(_RemoteSendPortSync x) { |
65 return [ 'sendport', 'dart', | 28 return [ 'sendport', 'dart', |
66 x._receivePort._isolateId, x._receivePort._portId ]; | 29 x._receivePort._isolateId, x._receivePort._portId ]; |
67 } | 30 } |
68 | |
69 visitObject(Object x) { | |
70 if (x is Function) return visitFunction(x); | |
71 if (x is JsProxy) return visitJsProxy(x); | |
72 if (x is Element) return visitElement(x); | |
73 | |
74 // TODO: Handle DOM elements and proxy other objects. | |
75 var proxyId = _dartProxyRegistry._add(x); | |
76 return [ 'objref', proxyId, | |
77 visitSendPortSync(_dartProxyRegistry._sendPort) ]; | |
78 } | |
79 | |
80 visitFunction(Function func) { | |
81 // Look for a cached serialization first. The cached version | |
82 // should point to the original port. | |
83 var serialized = _deserializedFunctionTable.find(func); | |
84 if (serialized != null) return serialized; | |
85 // Create a new serialization forwarding to this port. | |
86 return [ 'funcref', | |
87 _functionRegistry._add(func), | |
88 visitSendPortSync(_functionRegistry._sendPort), null ]; | |
89 } | |
90 | |
91 visitJsProxy(JsProxy proxy) { | |
92 return [ 'objref', proxy._id, visitSendPortSync(proxy._port) ]; | |
93 } | |
94 | |
95 visitElement(Element element) { | |
96 var id = _elementId(element); | |
97 // Verify that the element is connected to the document. | |
98 // Otherwise, we will not be able to find it on the other side. | |
99 _getElement(id); | |
100 return [ 'element', id ]; | |
101 } | |
102 } | 31 } |
103 | 32 |
104 // Leaking implementation. Later will be backend specific and hopefully | |
105 // not leaking (at least in most of the cases.) | |
106 // TODO: provide better, backend specific implementation. | |
107 class _Registry<T> { | |
108 final String _name; | |
109 int _nextId; | |
110 final Map<String, T> _registry; | |
111 final ReceivePortSync _port; | |
112 | |
113 _Registry(this._name) : | |
114 _nextId = 0, | |
115 _registry = <T>{}, | |
116 _port = new ReceivePortSync(); | |
117 | |
118 String _add(T x) { | |
119 // TODO(vsm): Cache x and reuse id. | |
120 final id = '$_name-${_nextId++}'; | |
121 _registry[id] = x; | |
122 return id; | |
123 } | |
124 | |
125 T _get(String id) { | |
126 return _registry[id]; | |
127 } | |
128 | |
129 get _sendPort => _port.toSendPort(); | |
130 } | |
131 | |
132 class _FunctionRegistry extends _Registry<Function> { | |
133 _FunctionRegistry() : super('func-ref') { | |
134 _port.receive((msg) { | |
135 final id = msg[0]; | |
136 final args = msg[1]; | |
137 final f = _registry[id]; | |
138 switch (args.length) { | |
139 case 0: return f(); | |
140 case 1: return f(args[0]); | |
141 case 2: return f(args[0], args[1]); | |
142 case 3: return f(args[0], args[1], args[2]); | |
143 case 4: return f(args[0], args[1], args[2], args[3]); | |
144 default: throw 'Unsupported number of arguments.'; | |
145 } | |
146 }); | |
147 } | |
148 } | |
149 | |
150 _FunctionRegistry __functionRegistry; | |
151 get _functionRegistry { | |
152 if (__functionRegistry === null) __functionRegistry = new _FunctionRegistry(); | |
153 return __functionRegistry; | |
154 } | |
155 /// End of function serialization implementation. | |
156 | |
157 /// Object proxy implementation. | |
158 | |
159 class _DartProxyRegistry extends _Registry<Object> { | |
160 _DartProxyRegistry() : super('dart-ref') { | |
161 _port.receive((msg) { | |
162 // TODO(vsm): Support a mechanism to register a handler here. | |
163 throw 'Invocation unsupported on Dart proxies'; | |
164 }); | |
165 } | |
166 } | |
167 | |
168 _DartProxyRegistry __dartProxyRegistry; | |
169 get _dartProxyRegistry { | |
170 if (__dartProxyRegistry === null) { | |
171 __dartProxyRegistry = new _DartProxyRegistry(); | |
172 } | |
173 return __dartProxyRegistry; | |
174 } | |
175 | |
176 /// End of object proxy implementation. | |
177 | |
178 _deserialize(var message) { | 33 _deserialize(var message) { |
179 return new _JsDeserializer().deserialize(message); | 34 return new _JsDeserializer().deserialize(message); |
180 } | 35 } |
181 | 36 |
182 // TODO(vsm): Replace this with a hash map once functions are | |
183 // hashable. | |
184 class _DeserializedFunctionTable { | |
185 List data; | |
186 _DeserializedFunctionTable() { | |
187 data = []; | |
188 } | |
189 | |
190 find(Function f) { | |
191 for (var item in data) { | |
192 if (f == item[0]) return item[1]; | |
193 } | |
194 return null; | |
195 } | |
196 | |
197 add(Function f, x) { | |
198 data.add([f, x]); | |
199 } | |
200 } | |
201 | |
202 _DeserializedFunctionTable __deserializedFunctionTable = null; | |
203 get _deserializedFunctionTable { | |
204 if (__deserializedFunctionTable == null) { | |
205 __deserializedFunctionTable = new _DeserializedFunctionTable(); | |
206 } | |
207 return __deserializedFunctionTable; | |
208 } | |
209 | 37 |
210 class _JsDeserializer extends _Deserializer { | 38 class _JsDeserializer extends _Deserializer { |
211 | 39 |
212 static const _UNSPECIFIED = const Object(); | 40 static const _UNSPECIFIED = const Object(); |
213 | 41 |
214 deserializeSendPort(List x) { | 42 deserializeSendPort(List x) { |
215 String tag = x[1]; | 43 String tag = x[1]; |
216 switch (tag) { | 44 switch (tag) { |
217 case 'nativejs': | 45 case 'nativejs': |
218 num id = x[2]; | 46 num id = x[2]; |
219 return new _JsSendPortSync(id); | 47 return new _JsSendPortSync(id); |
220 case 'dart': | 48 case 'dart': |
221 num isolateId = x[2]; | 49 num isolateId = x[2]; |
222 num portId = x[3]; | 50 num portId = x[3]; |
223 return ReceivePortSync._lookup(isolateId, portId); | 51 return ReceivePortSync._lookup(isolateId, portId); |
224 default: | 52 default: |
225 throw 'Illegal SendPortSync type: $tag'; | 53 throw 'Illegal SendPortSync type: $tag'; |
226 } | 54 } |
227 } | 55 } |
228 | |
229 deserializeObject(List x) { | |
230 String tag = x[0]; | |
231 switch (tag) { | |
232 case 'funcref': return deserializeFunction(x); | |
233 case 'objref': return deserializeProxy(x); | |
234 case 'element': return deserializeElement(x); | |
235 default: throw 'Illegal object type: $x'; | |
236 } | |
237 } | |
238 | |
239 deserializeFunction(List x) { | |
240 var id = x[1]; | |
241 // If the sendPort is local, just return the underlying function. | |
242 // Otherwise, create a new function that forwards to the remote | |
243 // port. | |
244 SendPortSync port = deserializeSendPort(x[2]); | |
245 if (port is _LocalSendPortSync) { | |
246 return _functionRegistry._get(id); | |
247 } | |
248 // TODO: Support varargs when there is support in the language. | |
249 var f = ([arg0 = _UNSPECIFIED, arg1 = _UNSPECIFIED, | |
250 arg2 = _UNSPECIFIED, arg3 = _UNSPECIFIED]) { | |
251 var args = [arg0, arg1, arg2, arg3]; | |
252 var last = args.indexOf(_UNSPECIFIED); | |
253 if (last >= 0) args = args.getRange(0, last); | |
254 var message = [id, args]; | |
255 return port.callSync(message); | |
256 }; | |
257 _deserializedFunctionTable.add(f, x); | |
258 return f; | |
259 } | |
260 | |
261 deserializeProxy(x) { | |
262 var id = x[1]; | |
263 var port = deserializeSendPort(x[2]); | |
264 if (port is _JsSendPortSync) return new JsProxy._internal(port, id); | |
265 if (port is _LocalSendPortSync) return _dartProxyRegistry._get(id); | |
266 // TODO(vsm): Support this case. | |
267 if (port is _RemoteSendPortSync) throw 'Remote Dart proxies unsupported'; | |
268 throw 'Illegal proxy: $port'; | |
269 } | |
270 | |
271 deserializeElement(x) { | |
272 var id = x[1]; | |
273 return _getElement(id); | |
274 } | |
275 } | 56 } |
276 | 57 |
277 // The receiver is JS. | 58 // The receiver is JS. |
278 class _JsSendPortSync implements SendPortSync { | 59 class _JsSendPortSync implements SendPortSync { |
279 | 60 |
280 num _id; | 61 num _id; |
281 _JsSendPortSync(this._id); | 62 _JsSendPortSync(this._id); |
282 | 63 |
283 callSync(var message) { | 64 callSync(var message) { |
284 var serialized = _serialize(message); | 65 var serialized = _serialize(message); |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
409 } | 190 } |
410 } | 191 } |
411 | 192 |
412 get _isolateId => ReceivePortSync._isolateId; | 193 get _isolateId => ReceivePortSync._isolateId; |
413 | 194 |
414 void _dispatchEvent(String receiver, var message) { | 195 void _dispatchEvent(String receiver, var message) { |
415 var event = document.$dom_createEvent('TextEvent'); | 196 var event = document.$dom_createEvent('TextEvent'); |
416 event.initTextEvent(receiver, false, false, window, JSON.stringify(message)); | 197 event.initTextEvent(receiver, false, false, window, JSON.stringify(message)); |
417 window.$dom_dispatchEvent(event); | 198 window.$dom_dispatchEvent(event); |
418 } | 199 } |
OLD | NEW |