| 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 |