blob: 7fc027721e9ceaf24eecf46f227d0795cc8a7e24 [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
_serialize(var message) {
return new _JsSerializer().traverse(message);
}
class _JsSerializer extends _Serializer {
visitSendPortSync(SendPortSync x) {
if (x is _JsSendPortSync) return visitJsSendPortSync(x);
if (x is _LocalSendPortSync) return visitLocalSendPortSync(x);
if (x is _RemoteSendPortSync) return visitRemoteSendPortSync(x);
throw "Unknown port type $x";
}
visitJsSendPortSync(_JsSendPortSync x) {
return [ 'sendport', 'nativejs', x._id ];
}
visitLocalSendPortSync(_LocalSendPortSync x) {
return [ 'sendport', 'dart',
ReceivePortSync._isolateId, x._receivePort._portId ];
}
visitRemoteSendPortSync(_RemoteSendPortSync x) {
return [ 'sendport', 'dart',
x._receivePort._isolateId, x._receivePort._portId ];
}
}
_deserialize(var message) {
return new _JsDeserializer().deserialize(message);
}
class _JsDeserializer extends _Deserializer {
static const _UNSPECIFIED = const Object();
deserializeSendPort(List x) {
String tag = x[1];
switch (tag) {
case 'nativejs':
num id = x[2];
return new _JsSendPortSync(id);
case 'dart':
num isolateId = x[2];
num portId = x[3];
return ReceivePortSync._lookup(isolateId, portId);
default:
throw 'Illegal SendPortSync type: $tag';
}
}
}
// The receiver is JS.
class _JsSendPortSync implements SendPortSync {
num _id;
_JsSendPortSync(this._id);
callSync(var message) {
var serialized = _serialize(message);
var result = _callPortSync(_id, serialized);
return _deserialize(result);
}
}
// TODO(vsm): Differentiate between Dart2Js and Dartium isolates.
// The receiver is a different Dart isolate, compiled to JS.
class _RemoteSendPortSync implements SendPortSync {
int _isolateId;
int _portId;
_RemoteSendPortSync(this._isolateId, this._portId);
callSync(var message) {
var serialized = _serialize(message);
var result = _call(_isolateId, _portId, serialized);
return _deserialize(result);
}
static _call(int isolateId, int portId, var message) {
var target = 'dart-port-$isolateId-$portId';
// TODO(vsm): Make this re-entrant.
// TODO(vsm): Set this up set once, on the first call.
var source = '$target-result';
var result = null;
var listener = (Event e) {
result = JSON.parse(_getPortSyncEventData(e));
};
window.on[source].add(listener);
_dispatchEvent(target, [source, message]);
window.on[source].remove(listener);
return result;
}
}
// The receiver is in the same Dart isolate, compiled to JS.
class _LocalSendPortSync implements SendPortSync {
ReceivePortSync _receivePort;
_LocalSendPortSync._internal(this._receivePort);
callSync(var message) {
// TODO(vsm): Do a more efficient deep copy.
var copy = _deserialize(_serialize(message));
var result = _receivePort._callback(copy);
return _deserialize(_serialize(result));
}
}
// TODO(vsm): Move this to dart:isolate. This will take some
// refactoring as there are dependences here on the DOM. Users
// interact with this class (or interface if we change it) directly -
// new ReceivePortSync. I think most of the DOM logic could be
// delayed until the corresponding SendPort is registered on the
// window.
// A Dart ReceivePortSync (tagged 'dart' when serialized) is
// identifiable / resolvable by the combination of its isolateid and
// portid. When a corresponding SendPort is used within the same
// isolate, the _portMap below can be used to obtain the
// ReceivePortSync directly. Across isolates (or from JS), an
// EventListener can be used to communicate with the port indirectly.
class ReceivePortSync {
static Map<int, ReceivePortSync> _portMap;
static int _portIdCount;
static int _cachedIsolateId;
num _portId;
Function _callback;
EventListener _listener;
ReceivePortSync() {
if (_portIdCount == null) {
_portIdCount = 0;
_portMap = new Map<int, ReceivePortSync>();
}
_portId = _portIdCount++;
_portMap[_portId] = this;
}
static int get _isolateId {
// TODO(vsm): Make this coherent with existing isolate code.
if (_cachedIsolateId == null) {
_cachedIsolateId = _getNewIsolateId();
}
return _cachedIsolateId;
}
static String _getListenerName(isolateId, portId) =>
'dart-port-$isolateId-$portId';
String get _listenerName => _getListenerName(_isolateId, _portId);
void receive(callback(var message)) {
_callback = callback;
if (_listener == null) {
_listener = (Event e) {
var data = JSON.parse(_getPortSyncEventData(e));
var replyTo = data[0];
var message = _deserialize(data[1]);
var result = _callback(message);
_dispatchEvent(replyTo, _serialize(result));
};
window.on[_listenerName].add(_listener);
}
}
void close() {
_portMap.remove(_portId);
if (_listener != null) window.on[_listenerName].remove(_listener);
}
SendPortSync toSendPort() {
return new _LocalSendPortSync._internal(this);
}
static SendPortSync _lookup(int isolateId, int portId) {
if (isolateId == _isolateId) {
return _portMap[portId].toSendPort();
} else {
return new _RemoteSendPortSync(isolateId, portId);
}
}
}
get _isolateId => ReceivePortSync._isolateId;
void _dispatchEvent(String receiver, var message) {
var event = new CustomEvent(receiver, false, false, JSON.stringify(message));
window.$dom_dispatchEvent(event);
}
String _getPortSyncEventData(CustomEvent event) => event.detail;