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 final 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 bool operator==(var other) { | |
76 return (other is _JsSendPortSync) && (_id == other._id); | |
77 } | |
78 | |
79 int get hashCode => _id; | |
80 } | |
81 | |
82 // TODO(vsm): Differentiate between Dart2Js and Dartium isolates. | |
83 // The receiver is a different Dart isolate, compiled to JS. | |
84 class _RemoteSendPortSync implements SendPortSync { | |
85 | |
86 int _isolateId; | |
87 int _portId; | |
88 _RemoteSendPortSync(this._isolateId, this._portId); | |
89 | |
90 callSync(var message) { | |
91 var serialized = _serialize(message); | |
92 var result = _call(_isolateId, _portId, serialized); | |
93 return _deserialize(result); | |
94 } | |
95 | |
96 static _call(int isolateId, int portId, var message) { | |
97 var target = 'dart-port-$isolateId-$portId'; | |
98 // TODO(vsm): Make this re-entrant. | |
99 // TODO(vsm): Set this up set once, on the first call. | |
100 var source = '$target-result'; | |
101 var result = null; | |
102 window.on[source].first.then((Event e) { | |
103 result = JSON.decode(_getPortSyncEventData(e)); | |
104 }); | |
105 _dispatchEvent(target, [source, message]); | |
106 return result; | |
107 } | |
108 | |
109 bool operator==(var other) { | |
110 return (other is _RemoteSendPortSync) && (_isolateId == other._isolateId) | |
111 && (_portId == other._portId); | |
112 } | |
113 | |
114 int get hashCode => _isolateId >> 16 + _portId; | |
115 } | |
116 | |
117 // The receiver is in the same Dart isolate, compiled to JS. | |
118 class _LocalSendPortSync implements SendPortSync { | |
119 | |
120 ReceivePortSync _receivePort; | |
121 | |
122 _LocalSendPortSync._internal(this._receivePort); | |
123 | |
124 callSync(var message) { | |
125 // TODO(vsm): Do a more efficient deep copy. | |
126 var copy = _deserialize(_serialize(message)); | |
127 var result = _receivePort._callback(copy); | |
128 return _deserialize(_serialize(result)); | |
129 } | |
130 | |
131 bool operator==(var other) { | |
132 return (other is _LocalSendPortSync) | |
133 && (_receivePort == other._receivePort); | |
134 } | |
135 | |
136 int get hashCode => _receivePort.hashCode; | |
137 } | |
138 | |
139 // TODO(vsm): Move this to dart:isolate. This will take some | |
140 // refactoring as there are dependences here on the DOM. Users | |
141 // interact with this class (or interface if we change it) directly - | |
142 // new ReceivePortSync. I think most of the DOM logic could be | |
143 // delayed until the corresponding SendPort is registered on the | |
144 // window. | |
145 | |
146 // A Dart ReceivePortSync (tagged 'dart' when serialized) is | |
147 // identifiable / resolvable by the combination of its isolateid and | |
148 // portid. When a corresponding SendPort is used within the same | |
149 // isolate, the _portMap below can be used to obtain the | |
150 // ReceivePortSync directly. Across isolates (or from JS), an | |
151 // EventListener can be used to communicate with the port indirectly. | |
152 class ReceivePortSync { | |
153 | |
154 static Map<int, ReceivePortSync> _portMap; | |
155 static int _portIdCount; | |
156 static int _cachedIsolateId; | |
157 | |
158 num _portId; | |
159 Function _callback; | |
160 StreamSubscription _portSubscription; | |
161 | |
162 ReceivePortSync() { | |
163 if (_portIdCount == null) { | |
164 _portIdCount = 0; | |
165 _portMap = new Map<int, ReceivePortSync>(); | |
166 } | |
167 _portId = _portIdCount++; | |
168 _portMap[_portId] = this; | |
169 } | |
170 | |
171 static int get _isolateId { | |
172 // TODO(vsm): Make this coherent with existing isolate code. | |
173 if (_cachedIsolateId == null) { | |
174 _cachedIsolateId = _getNewIsolateId(); | |
175 } | |
176 return _cachedIsolateId; | |
177 } | |
178 | |
179 static String _getListenerName(isolateId, portId) => | |
180 'dart-port-$isolateId-$portId'; | |
181 String get _listenerName => _getListenerName(_isolateId, _portId); | |
182 | |
183 void receive(callback(var message)) { | |
184 _callback = callback; | |
185 if (_portSubscription == null) { | |
186 _portSubscription = window.on[_listenerName].listen((Event e) { | |
187 var data = JSON.decode(_getPortSyncEventData(e)); | |
188 var replyTo = data[0]; | |
189 var message = _deserialize(data[1]); | |
190 var result = _callback(message); | |
191 _dispatchEvent(replyTo, _serialize(result)); | |
192 }); | |
193 } | |
194 } | |
195 | |
196 void close() { | |
197 _portMap.remove(_portId); | |
198 if (_portSubscription != null) _portSubscription.cancel(); | |
199 } | |
200 | |
201 SendPortSync toSendPort() { | |
202 return new _LocalSendPortSync._internal(this); | |
203 } | |
204 | |
205 static SendPortSync _lookup(int isolateId, int portId) { | |
206 if (isolateId == _isolateId) { | |
207 return _portMap[portId].toSendPort(); | |
208 } else { | |
209 return new _RemoteSendPortSync(isolateId, portId); | |
210 } | |
211 } | |
212 } | |
213 | |
214 get _isolateId => ReceivePortSync._isolateId; | |
215 | |
216 void _dispatchEvent(String receiver, var message) { | |
217 var event = new CustomEvent(receiver, canBubble: false, cancelable:false, | |
218 detail: JSON.encode(message)); | |
219 window.dispatchEvent(event); | |
220 } | |
221 | |
222 String _getPortSyncEventData(CustomEvent event) => event.detail; | |
OLD | NEW |