OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 part of html; | |
6 | |
7 _serialize(var message) { | |
8 return new _JsSerializer().traverse(message); | |
9 } | |
10 | |
11 class _JsSerializer extends _Serializer { | |
12 | |
13 visitSendPortSync(SendPortSync x) { | |
14 if (x is _JsSendPortSync) return visitJsSendPortSync(x); | |
15 if (x is _LocalSendPortSync) return visitLocalSendPortSync(x); | |
16 if (x is _RemoteSendPortSync) return visitRemoteSendPortSync(x); | |
17 throw "Unknown port type $x"; | |
18 } | |
19 | |
20 visitJsSendPortSync(_JsSendPortSync x) { | |
21 return [ 'sendport', 'nativejs', x._id ]; | |
22 } | |
23 | |
24 visitLocalSendPortSync(_LocalSendPortSync x) { | |
25 return [ 'sendport', 'dart', | |
26 ReceivePortSync._isolateId, x._receivePort._portId ]; | |
27 } | |
28 | |
29 visitSendPort(SendPort x) { | |
30 throw new UnimplementedError('Asynchronous send port not yet implemented.'); | |
31 } | |
32 | |
33 visitRemoteSendPortSync(_RemoteSendPortSync x) { | |
34 return [ 'sendport', 'dart', x._isolateId, x._portId ]; | |
35 } | |
36 } | |
37 | |
38 _deserialize(var message) { | |
39 return new _JsDeserializer().deserialize(message); | |
40 } | |
41 | |
42 | |
43 class _JsDeserializer extends _Deserializer { | |
44 | |
45 static const _UNSPECIFIED = const Object(); | |
46 | |
47 deserializeSendPort(List x) { | |
48 String tag = x[1]; | |
49 switch (tag) { | |
50 case 'nativejs': | |
51 num id = x[2]; | |
52 return new _JsSendPortSync(id); | |
53 case 'dart': | |
54 num isolateId = x[2]; | |
55 num portId = x[3]; | |
56 return ReceivePortSync._lookup(isolateId, portId); | |
57 default: | |
58 throw 'Illegal SendPortSync type: $tag'; | |
59 } | |
60 } | |
61 } | |
62 | |
63 // The receiver is JS. | |
64 class _JsSendPortSync implements SendPortSync { | |
65 | |
66 num _id; | |
67 _JsSendPortSync(this._id); | |
68 | |
69 callSync(var message) { | |
70 var serialized = _serialize(message); | |
71 var result = _callPortSync(_id, serialized); | |
72 return _deserialize(result); | |
73 } | |
74 | |
75 } | |
76 | |
77 // TODO(vsm): Differentiate between Dart2Js and Dartium isolates. | |
78 // The receiver is a different Dart isolate, compiled to JS. | |
79 class _RemoteSendPortSync implements SendPortSync { | |
80 | |
81 int _isolateId; | |
82 int _portId; | |
83 _RemoteSendPortSync(this._isolateId, this._portId); | |
84 | |
85 callSync(var message) { | |
86 var serialized = _serialize(message); | |
87 var result = _call(_isolateId, _portId, serialized); | |
88 return _deserialize(result); | |
89 } | |
90 | |
91 static _call(int isolateId, int portId, var message) { | |
92 var target = 'dart-port-$isolateId-$portId'; | |
93 // TODO(vsm): Make this re-entrant. | |
94 // TODO(vsm): Set this up set once, on the first call. | |
95 var source = '$target-result'; | |
96 var result = null; | |
97 var listener = (Event e) { | |
98 result = JSON.parse(_getPortSyncEventData(e)); | |
99 }; | |
100 window.on[source].add(listener); | |
101 _dispatchEvent(target, [source, message]); | |
102 window.on[source].remove(listener); | |
103 return result; | |
104 } | |
105 } | |
106 | |
107 // The receiver is in the same Dart isolate, compiled to JS. | |
108 class _LocalSendPortSync implements SendPortSync { | |
109 | |
110 ReceivePortSync _receivePort; | |
111 | |
112 _LocalSendPortSync._internal(this._receivePort); | |
113 | |
114 callSync(var message) { | |
115 // TODO(vsm): Do a more efficient deep copy. | |
116 var copy = _deserialize(_serialize(message)); | |
117 var result = _receivePort._callback(copy); | |
118 return _deserialize(_serialize(result)); | |
119 } | |
120 } | |
121 | |
122 // TODO(vsm): Move this to dart:isolate. This will take some | |
123 // refactoring as there are dependences here on the DOM. Users | |
124 // interact with this class (or interface if we change it) directly - | |
125 // new ReceivePortSync. I think most of the DOM logic could be | |
126 // delayed until the corresponding SendPort is registered on the | |
127 // window. | |
128 | |
129 // A Dart ReceivePortSync (tagged 'dart' when serialized) is | |
130 // identifiable / resolvable by the combination of its isolateid and | |
131 // portid. When a corresponding SendPort is used within the same | |
132 // isolate, the _portMap below can be used to obtain the | |
133 // ReceivePortSync directly. Across isolates (or from JS), an | |
134 // EventListener can be used to communicate with the port indirectly. | |
135 class ReceivePortSync { | |
136 | |
137 static Map<int, ReceivePortSync> _portMap; | |
138 static int _portIdCount; | |
139 static int _cachedIsolateId; | |
140 | |
141 num _portId; | |
142 Function _callback; | |
143 EventListener _listener; | |
144 | |
145 ReceivePortSync() { | |
146 if (_portIdCount == null) { | |
147 _portIdCount = 0; | |
148 _portMap = new Map<int, ReceivePortSync>(); | |
149 } | |
150 _portId = _portIdCount++; | |
151 _portMap[_portId] = this; | |
152 } | |
153 | |
154 static int get _isolateId { | |
155 // TODO(vsm): Make this coherent with existing isolate code. | |
156 if (_cachedIsolateId == null) { | |
157 _cachedIsolateId = _getNewIsolateId(); | |
158 } | |
159 return _cachedIsolateId; | |
160 } | |
161 | |
162 static String _getListenerName(isolateId, portId) => | |
163 'dart-port-$isolateId-$portId'; | |
164 String get _listenerName => _getListenerName(_isolateId, _portId); | |
165 | |
166 void receive(callback(var message)) { | |
167 _callback = callback; | |
168 if (_listener == null) { | |
169 _listener = (Event e) { | |
170 var data = JSON.parse(_getPortSyncEventData(e)); | |
171 var replyTo = data[0]; | |
172 var message = _deserialize(data[1]); | |
173 var result = _callback(message); | |
174 _dispatchEvent(replyTo, _serialize(result)); | |
175 }; | |
176 window.on[_listenerName].add(_listener); | |
177 } | |
178 } | |
179 | |
180 void close() { | |
181 _portMap.remove(_portId); | |
182 if (_listener != null) window.on[_listenerName].remove(_listener); | |
183 } | |
184 | |
185 SendPortSync toSendPort() { | |
186 return new _LocalSendPortSync._internal(this); | |
187 } | |
188 | |
189 static SendPortSync _lookup(int isolateId, int portId) { | |
190 if (isolateId == _isolateId) { | |
191 return _portMap[portId].toSendPort(); | |
192 } else { | |
193 return new _RemoteSendPortSync(isolateId, portId); | |
194 } | |
195 } | |
196 } | |
197 | |
198 get _isolateId => ReceivePortSync._isolateId; | |
199 | |
200 void _dispatchEvent(String receiver, var message) { | |
201 var event = new CustomEvent(receiver, false, false, JSON.stringify(message)); | |
202 window.$dom_dispatchEvent(event); | |
203 } | |
204 | |
205 String _getPortSyncEventData(CustomEvent event) => event.detail; | |
OLD | NEW |