mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 11:03:19 +00:00
Support general serializing of functions between JS and Dart
Anton: This expands on your earlier CL. Do you mind taking a look? Review URL: https://chromiumcodereview.appspot.com//10834426 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@11066 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
ce718e65fb
commit
9259b263fd
|
@ -46,6 +46,33 @@ function ReceivePortSync() {
|
|||
}
|
||||
|
||||
(function() {
|
||||
// Track proxied functions.
|
||||
// TODO: Fix leaks, particularly in dart2js case.
|
||||
var functionRefMap = {};
|
||||
|
||||
var nextFunctionRefId = 0;
|
||||
|
||||
function functionRefDispatch(message) {
|
||||
var id = message[0];
|
||||
var args = message[1];
|
||||
var f = functionRefMap[id];
|
||||
// TODO: Should we capture this automatically?
|
||||
return f.apply(null, args);
|
||||
}
|
||||
|
||||
var functionRefPort = null;
|
||||
|
||||
function makeFunctionRef(f) {
|
||||
if (functionRefPort == null) {
|
||||
var port = new ReceivePortSync();
|
||||
port.receive(functionRefDispatch);
|
||||
functionRefPort = port.toSendPort();
|
||||
}
|
||||
var ref = 'func-ref-' + (nextFunctionRefId++);
|
||||
functionRefMap[ref] = f;
|
||||
return ref;
|
||||
}
|
||||
|
||||
function serialize(message) {
|
||||
var visited = [];
|
||||
function checkedSerialization(obj, serializer) {
|
||||
|
@ -79,6 +106,9 @@ function ReceivePortSync() {
|
|||
return [ 'sendport', 'nativejs', message.receivePort.id ];
|
||||
} else if (message instanceof DartSendPortSync) {
|
||||
return [ 'sendport', 'dart', message.isolateId, message.portId ];
|
||||
} else if (message instanceof Function) {
|
||||
return [ 'funcref', makeFunctionRef(message),
|
||||
doSerialize(functionRefPort) ];
|
||||
} else {
|
||||
return checkedSerialization(message, function(id) {
|
||||
var keys = Object.getOwnPropertyNames(message);
|
||||
|
|
|
@ -37121,6 +37121,12 @@ class _JsSerializer extends _Serializer {
|
|||
x._receivePort._isolateId, x._receivePort._portId ];
|
||||
}
|
||||
|
||||
visitObject(Object x) {
|
||||
if (x is Function) return visitFunction(x);
|
||||
// TODO: Handle DOM elements and proxy other objects.
|
||||
throw "Unserializable object $x";
|
||||
}
|
||||
|
||||
visitFunction(Function func) {
|
||||
return [ 'funcref',
|
||||
_makeFunctionRef(func), visitSendPortSync(_sendPort()), null ];
|
||||
|
@ -37179,6 +37185,8 @@ _deserialize(var message) {
|
|||
|
||||
class _JsDeserializer extends _Deserializer {
|
||||
|
||||
static final _UNSPECIFIED = const Object();
|
||||
|
||||
deserializeSendPort(List x) {
|
||||
String tag = x[1];
|
||||
switch (tag) {
|
||||
|
@ -37194,6 +37202,27 @@ class _JsDeserializer extends _Deserializer {
|
|||
}
|
||||
}
|
||||
|
||||
deserializeObject(List x) {
|
||||
String tag = x[0];
|
||||
switch (tag) {
|
||||
case 'funcref': return deserializeFunction(x);
|
||||
default: throw 'Illegal object type: $x';
|
||||
}
|
||||
}
|
||||
|
||||
deserializeFunction(List x) {
|
||||
var id = x[1];
|
||||
SendPortSync port = deserializeSendPort(x[2]);
|
||||
// TODO: Support varargs when there is support in the language.
|
||||
return ([arg0 = _UNSPECIFIED, arg1 = _UNSPECIFIED,
|
||||
arg2 = _UNSPECIFIED, arg3 = _UNSPECIFIED]) {
|
||||
var args = [arg0, arg1, arg2, arg3];
|
||||
var last = args.indexOf(_UNSPECIFIED);
|
||||
if (last >= 0) args = args.getRange(0, last);
|
||||
var message = [id, args];
|
||||
return port.callSync(message);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// The receiver is JS.
|
||||
|
@ -38067,10 +38096,9 @@ class _MessageTraverser {
|
|||
if (x is Map) return visitMap(x);
|
||||
if (x is SendPort) return visitSendPort(x);
|
||||
if (x is SendPortSync) return visitSendPortSync(x);
|
||||
if (x is Function) return visitFunction(x);
|
||||
|
||||
// TODO(floitsch): make this a real exception. (which one)?
|
||||
throw "Message serialization: Illegal value $x passed";
|
||||
// Overridable fallback.
|
||||
return visitObject(x);
|
||||
}
|
||||
|
||||
abstract visitPrimitive(x);
|
||||
|
@ -38079,8 +38107,9 @@ class _MessageTraverser {
|
|||
abstract visitSendPort(SendPort x);
|
||||
abstract visitSendPortSync(SendPortSync x);
|
||||
|
||||
visitFunction(Function func) {
|
||||
throw "Serialization of functions is not allowed.";
|
||||
visitObject(Object x) {
|
||||
// TODO(floitsch): make this a real exception. (which one)?
|
||||
throw "Message serialization: Illegal value $x passed";
|
||||
}
|
||||
|
||||
static bool isPrimitive(x) {
|
||||
|
@ -38188,8 +38217,7 @@ class _Deserializer {
|
|||
case 'list': return _deserializeList(x);
|
||||
case 'map': return _deserializeMap(x);
|
||||
case 'sendport': return deserializeSendPort(x);
|
||||
// TODO(floitsch): Use real exception (which one?).
|
||||
default: throw "Unexpected serialized object";
|
||||
default: return deserializeObject(x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38230,4 +38258,8 @@ class _Deserializer {
|
|||
|
||||
abstract deserializeSendPort(List x);
|
||||
|
||||
deserializeObject(List x) {
|
||||
// TODO(floitsch): Use real exception (which one?).
|
||||
throw "Unexpected serialized object";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40763,6 +40763,12 @@ class _JsSerializer extends _Serializer {
|
|||
x._receivePort._isolateId, x._receivePort._portId ];
|
||||
}
|
||||
|
||||
visitObject(Object x) {
|
||||
if (x is Function) return visitFunction(x);
|
||||
// TODO: Handle DOM elements and proxy other objects.
|
||||
throw "Unserializable object $x";
|
||||
}
|
||||
|
||||
visitFunction(Function func) {
|
||||
return [ 'funcref',
|
||||
_makeFunctionRef(func), visitSendPortSync(_sendPort()), null ];
|
||||
|
@ -40821,6 +40827,8 @@ _deserialize(var message) {
|
|||
|
||||
class _JsDeserializer extends _Deserializer {
|
||||
|
||||
static final _UNSPECIFIED = const Object();
|
||||
|
||||
deserializeSendPort(List x) {
|
||||
String tag = x[1];
|
||||
switch (tag) {
|
||||
|
@ -40836,6 +40844,27 @@ class _JsDeserializer extends _Deserializer {
|
|||
}
|
||||
}
|
||||
|
||||
deserializeObject(List x) {
|
||||
String tag = x[0];
|
||||
switch (tag) {
|
||||
case 'funcref': return deserializeFunction(x);
|
||||
default: throw 'Illegal object type: $x';
|
||||
}
|
||||
}
|
||||
|
||||
deserializeFunction(List x) {
|
||||
var id = x[1];
|
||||
SendPortSync port = deserializeSendPort(x[2]);
|
||||
// TODO: Support varargs when there is support in the language.
|
||||
return ([arg0 = _UNSPECIFIED, arg1 = _UNSPECIFIED,
|
||||
arg2 = _UNSPECIFIED, arg3 = _UNSPECIFIED]) {
|
||||
var args = [arg0, arg1, arg2, arg3];
|
||||
var last = args.indexOf(_UNSPECIFIED);
|
||||
if (last >= 0) args = args.getRange(0, last);
|
||||
var message = [id, args];
|
||||
return port.callSync(message);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// The receiver is JS.
|
||||
|
@ -41276,10 +41305,9 @@ class _MessageTraverser {
|
|||
if (x is Map) return visitMap(x);
|
||||
if (x is SendPort) return visitSendPort(x);
|
||||
if (x is SendPortSync) return visitSendPortSync(x);
|
||||
if (x is Function) return visitFunction(x);
|
||||
|
||||
// TODO(floitsch): make this a real exception. (which one)?
|
||||
throw "Message serialization: Illegal value $x passed";
|
||||
// Overridable fallback.
|
||||
return visitObject(x);
|
||||
}
|
||||
|
||||
abstract visitPrimitive(x);
|
||||
|
@ -41288,8 +41316,9 @@ class _MessageTraverser {
|
|||
abstract visitSendPort(SendPort x);
|
||||
abstract visitSendPortSync(SendPortSync x);
|
||||
|
||||
visitFunction(Function func) {
|
||||
throw "Serialization of functions is not allowed.";
|
||||
visitObject(Object x) {
|
||||
// TODO(floitsch): make this a real exception. (which one)?
|
||||
throw "Message serialization: Illegal value $x passed";
|
||||
}
|
||||
|
||||
static bool isPrimitive(x) {
|
||||
|
@ -41397,8 +41426,7 @@ class _Deserializer {
|
|||
case 'list': return _deserializeList(x);
|
||||
case 'map': return _deserializeMap(x);
|
||||
case 'sendport': return deserializeSendPort(x);
|
||||
// TODO(floitsch): Use real exception (which one?).
|
||||
default: throw "Unexpected serialized object";
|
||||
default: return deserializeObject(x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41439,6 +41467,10 @@ class _Deserializer {
|
|||
|
||||
abstract deserializeSendPort(List x);
|
||||
|
||||
deserializeObject(List x) {
|
||||
// TODO(floitsch): Use real exception (which one?).
|
||||
throw "Unexpected serialized object";
|
||||
}
|
||||
}
|
||||
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
|
|
|
@ -29,6 +29,12 @@ class _JsSerializer extends _Serializer {
|
|||
x._receivePort._isolateId, x._receivePort._portId ];
|
||||
}
|
||||
|
||||
visitObject(Object x) {
|
||||
if (x is Function) return visitFunction(x);
|
||||
// TODO: Handle DOM elements and proxy other objects.
|
||||
throw "Unserializable object $x";
|
||||
}
|
||||
|
||||
visitFunction(Function func) {
|
||||
return [ 'funcref',
|
||||
_makeFunctionRef(func), visitSendPortSync(_sendPort()), null ];
|
||||
|
@ -87,6 +93,8 @@ _deserialize(var message) {
|
|||
|
||||
class _JsDeserializer extends _Deserializer {
|
||||
|
||||
static final _UNSPECIFIED = const Object();
|
||||
|
||||
deserializeSendPort(List x) {
|
||||
String tag = x[1];
|
||||
switch (tag) {
|
||||
|
@ -102,6 +110,27 @@ class _JsDeserializer extends _Deserializer {
|
|||
}
|
||||
}
|
||||
|
||||
deserializeObject(List x) {
|
||||
String tag = x[0];
|
||||
switch (tag) {
|
||||
case 'funcref': return deserializeFunction(x);
|
||||
default: throw 'Illegal object type: $x';
|
||||
}
|
||||
}
|
||||
|
||||
deserializeFunction(List x) {
|
||||
var id = x[1];
|
||||
SendPortSync port = deserializeSendPort(x[2]);
|
||||
// TODO: Support varargs when there is support in the language.
|
||||
return ([arg0 = _UNSPECIFIED, arg1 = _UNSPECIFIED,
|
||||
arg2 = _UNSPECIFIED, arg3 = _UNSPECIFIED]) {
|
||||
var args = [arg0, arg1, arg2, arg3];
|
||||
var last = args.indexOf(_UNSPECIFIED);
|
||||
if (last >= 0) args = args.getRange(0, last);
|
||||
var message = [id, args];
|
||||
return port.callSync(message);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// The receiver is JS.
|
||||
|
|
|
@ -37,10 +37,9 @@ class _MessageTraverser {
|
|||
if (x is Map) return visitMap(x);
|
||||
if (x is SendPort) return visitSendPort(x);
|
||||
if (x is SendPortSync) return visitSendPortSync(x);
|
||||
if (x is Function) return visitFunction(x);
|
||||
|
||||
// TODO(floitsch): make this a real exception. (which one)?
|
||||
throw "Message serialization: Illegal value $x passed";
|
||||
// Overridable fallback.
|
||||
return visitObject(x);
|
||||
}
|
||||
|
||||
abstract visitPrimitive(x);
|
||||
|
@ -49,8 +48,9 @@ class _MessageTraverser {
|
|||
abstract visitSendPort(SendPort x);
|
||||
abstract visitSendPortSync(SendPortSync x);
|
||||
|
||||
visitFunction(Function func) {
|
||||
throw "Serialization of functions is not allowed.";
|
||||
visitObject(Object x) {
|
||||
// TODO(floitsch): make this a real exception. (which one)?
|
||||
throw "Message serialization: Illegal value $x passed";
|
||||
}
|
||||
|
||||
static bool isPrimitive(x) {
|
||||
|
@ -158,8 +158,7 @@ class _Deserializer {
|
|||
case 'list': return _deserializeList(x);
|
||||
case 'map': return _deserializeMap(x);
|
||||
case 'sendport': return deserializeSendPort(x);
|
||||
// TODO(floitsch): Use real exception (which one?).
|
||||
default: throw "Unexpected serialized object";
|
||||
default: return deserializeObject(x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,4 +199,8 @@ class _Deserializer {
|
|||
|
||||
abstract deserializeSendPort(List x);
|
||||
|
||||
deserializeObject(List x) {
|
||||
// TODO(floitsch): Use real exception (which one?).
|
||||
throw "Unexpected serialized object";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,25 +15,83 @@ injectSource(code) {
|
|||
document.body.nodes.add(script);
|
||||
}
|
||||
|
||||
var isolateTest = """
|
||||
var dartToJsTest = """
|
||||
var port = new ReceivePortSync();
|
||||
port.receive(function (f) {
|
||||
return f('fromJS');
|
||||
});
|
||||
window.registerPort('test', port.toSendPort());
|
||||
window.registerPort('test1', port.toSendPort());
|
||||
""";
|
||||
|
||||
var jsToDartTest = """
|
||||
var port1 = window.lookupPort('test2a');
|
||||
var result = port1.callSync(function (x) {
|
||||
return x*2;
|
||||
});
|
||||
|
||||
var port2 = window.lookupPort('test2b');
|
||||
port2.callSync(result);
|
||||
""";
|
||||
|
||||
var dartToJsToDartTest = """
|
||||
var port = new ReceivePortSync();
|
||||
port.receive(function (f) {
|
||||
return f;
|
||||
});
|
||||
window.registerPort('test3', port.toSendPort());
|
||||
""";
|
||||
|
||||
main() {
|
||||
useHtmlConfiguration();
|
||||
|
||||
test('dart-to-js-function', () {
|
||||
injectSource(isolateTest);
|
||||
injectSource(dartToJsTest);
|
||||
|
||||
SendPortSync port = window.lookupPort('test');
|
||||
SendPortSync port = window.lookupPort('test1');
|
||||
var result = port.callSync((msg) {
|
||||
Expect.equals('fromJS', msg);
|
||||
return 'received';
|
||||
});
|
||||
Expect.equals('received', result);
|
||||
});
|
||||
|
||||
test('js-to-dart-function', () {
|
||||
var port1 = new ReceivePortSync();
|
||||
int invoked1 = 0;
|
||||
port1.receive((f) {
|
||||
++invoked1;
|
||||
var data = f(21);
|
||||
expect(data, equals(42));
|
||||
return 'fromDart';
|
||||
});
|
||||
window.registerPort('test2a', port1.toSendPort());
|
||||
|
||||
var port2 = new ReceivePortSync();
|
||||
int invoked2 = 0;
|
||||
port2.receive((x) {
|
||||
++invoked2;
|
||||
expect(x, equals('fromDart'));
|
||||
});
|
||||
window.registerPort('test2b', port2.toSendPort());
|
||||
|
||||
injectSource(jsToDartTest);
|
||||
|
||||
expect(1, equals(invoked1));
|
||||
expect(1, equals(invoked2));
|
||||
});
|
||||
|
||||
test('dart-to-js-to-dart-function', () {
|
||||
injectSource(dartToJsToDartTest);
|
||||
|
||||
validate(x) {
|
||||
expect(x, equals('fromCaller'));
|
||||
return 'fromCallee';
|
||||
}
|
||||
|
||||
SendPortSync port = window.lookupPort('test3');
|
||||
var f = port.callSync(validate);
|
||||
var result = f('fromCaller');
|
||||
Expect.equals('fromCallee', result);
|
||||
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue