Chromium Code Reviews| Index: tests/isolate/function_send_test.dart |
| diff --git a/tests/isolate/function_send_test.dart b/tests/isolate/function_send_test.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..03767f3ec990fc6d95c1e1a869836126b40596bb |
| --- /dev/null |
| +++ b/tests/isolate/function_send_test.dart |
| @@ -0,0 +1,170 @@ |
| +// Copyright (c) 2014, 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. |
| + |
| +import "dart:isolate"; |
| +import "dart:async"; |
| +import "package:expect/expect.dart"; |
| +import "package:async_helper/async_helper.dart"; |
| + |
| +void toplevel(port, message) { port.send("toplevel:$message"); } |
| +void mkToplevelFunc() => (p, m) { p.send(m); }; |
|
floitsch
2014/09/19 19:52:45
mkFuncToplevel
or
funcGeneratorToplevel
Lasse Reichstein Nielsen
2014/09/23 07:25:07
I'll go for createFuncToplevel.
|
| +class C { |
| + Function initializer; |
| + Function body; |
| + C() : initializer = ((p, m) { throw "initializer"; }) { |
| + body = (p, m) { throw "body"; }; |
| + } |
| + static void staticFunc(port, message) { port.send("static:$message"); } |
| + static Function mkStaticFunc() => (p, m) { throw "static expr"; }; |
| + void member(p, m) { throw "member"; } |
| + void mkFunc() => (p, m) { throw "instance expr"; }; |
| + void call(n, p) { throw "C"; } |
| +} |
| + |
| +class Callable { |
| + void call(p, m) { p.send(["callable", m]); } |
| +} |
| + |
| + |
| +void main() { |
| + asyncStart(); |
| + |
| + // Sendables are top-level functions and static functions only. |
| + testSendable("toplevel", toplevel); |
| + testSendable("static", C.staticFunc); |
| + |
| + // Unsendables are any closure - instance methods or function expression. |
| + var c = new C(); |
| + testUnsendable("instance method", c.member); |
| + testUnsendable("static context expression", mkToplevelFunc()); |
| + testUnsendable("static context expression", C.mkStaticFunc()); |
| + testUnsendable("initializer context expression", c.initializer); |
| + testUnsendable("constructor context expression", c.body); |
| + testUnsendable("instance method context expression", c.mkFunc()); |
| + |
| + // Actually allowed. Identical in VM, different, but still static, in dart2js. |
|
floitsch
2014/09/19 19:52:45
If it's allowed, why is it commented?
Lasse Reichstein Nielsen
2014/09/23 07:25:07
I can't say whether it should be allowed or not, a
|
| + // testUnsendable("toplevel.call closure", toplevel.call); |
| + // testUnsendable("static.call closure", C.staticFunc.call); |
| + |
| + // Callable objects are sendable if general objects are (VM yes, dart2js no). |
| + //testSendable("callable object", new Callable()); /// 01: ok |
|
floitsch
2014/09/19 19:52:45
Why is it commented?
Especially with "/// 01: ok".
Lasse Reichstein Nielsen
2014/09/23 07:25:07
Again I can't decide on whether it should be one o
|
| + testUnsendable("callable object", new Callable()); /// 02: ok |
|
floitsch
2014/09/19 19:52:45
That looks like the same as above...
Lasse Reichstein Nielsen
2014/09/23 07:25:07
This one is test*Un*sendablem the one above is tes
|
| + testUnsendable("callable object", new Callable().call); |
| + asyncEnd(); |
| + return; |
| +} |
| + |
| +// Create a receive port that expects exactly one message. |
| +// Pass the message to `callback` and return the sendPort. |
| +void singleMessagePort(callback) { |
| + var p; |
| + p = new RawReceivePort((v) { p.close(); callback(v); }); |
| + return p.sendPort; |
| +} |
| + |
| +// A singleMessagePort that expects the message to be a specific value. |
| +void expectMessagePort(message) { |
| + asyncStart(); |
| + return singleMessagePort((v) { |
| + Expect.equals(message, v); |
| + asyncEnd(); |
| + }); |
| +} |
| + |
| +void testSendable(name, func) { |
| + // Function as spawn message. |
| + Isolate.spawn(callFunc, [func, expectMessagePort("$name:spawn"), "spawn"]); |
| + |
| + // Send function to same isolate. |
| + var reply = expectMessagePort("$name:direct"); |
| + singleMessagePort(callFunc).send([func, reply, "direct"]); |
| + |
| + // Send function to other isolate, call it there. |
| + reply = expectMessagePort("$name:other isolate"); |
| + callPort().then((p) { |
| + p.send([func, reply, "other isolate"]); |
| + }); |
| + |
| + // Round-trip function trough other isolate. |
| + echoPort((roundtripFunc) { |
| + Expect.identical(func, roundtripFunc, "$name:send through isolate"); |
| + }).then((port) { port.send(func); }); |
| +} |
| + |
| +// Creates a new isolate and a pair of ports that expect a single message |
| +// to be sent to the other isolate and back to the callback function. |
| +Future<SendPort> echoPort(callback(value)) { |
| + Completer completer = new Completer<SendPort>(); |
| + SendPort replyPort = singleMessagePort(callback); |
| + RawReceivePort initPort; |
| + initPort = new RawReceivePort((p) { |
| + completer.complete(p); |
| + initPort.close(); |
| + }); |
| + return Isolate.spawn(_echo, [replyPort, initPort.sendPort]) |
| + .then((isolate) => completer.future); |
| +} |
| + |
| +void _echo(msg) { |
| + var replyPort = msg[0]; |
| + RawReceivePort requestPort; |
| + requestPort = new RawReceivePort((msg) { |
| + replyPort.send(msg); |
| + requestPort.close(); // Single echo only. |
| + }); |
| + msg[1].send(requestPort.sendPort); |
| +} |
| + |
| +// Creates other isolate that waits for a single message, `msg`, on the returned |
| +// port, and executes it as `msg[0](msg[1],msg[2])` in the other isolate. |
| +Future<SendPort> callPort() { |
| + Completer completer = new Completer<SendPort>(); |
| + SendPort initPort = singleMessagePort(completer.complete); |
| + return Isolate.spawn(_call, initPort) |
| + .then((_) => completer.future); |
| +} |
| + |
| +void _call(initPort) { |
| + initPort.send(singleMessagePort(callFunc)); |
| +} |
| + // Expect.throws(() { |
| + // noReply.sendPort.send([func]); |
| + // }, null, "send wrapped"); |
| + |
| + |
| +void testUnsendable(name, func) { |
| + asyncStart(); |
| + Isolate.spawn(nop, func).then((v) => throw "allowed spawn direct?", |
| + onError: (e,s){ asyncEnd(); }); |
| + asyncStart(); |
| + Isolate.spawn(nop, [func]).then((v) => throw "allowed spawn wrapped?", |
| + onError: (e,s){ asyncEnd(); }); |
| + |
| + asyncStart(); |
| + var noReply = new RawReceivePort((_) { throw "Unexpected message: $_"; }); |
| + // Currently succeedes incorrectly in dart2js. |
| + // Expect.throws(() { |
| + // noReply.sendPort.send(func); |
| + // }, null, "send direct"); |
| + // Expect.throws(() { |
| + // noReply.sendPort.send([func]); |
| + // }, null, "send wrapped"); |
| + scheduleMicrotask(() { |
| + noReply.close(); |
| + asyncEnd(); |
| + }); |
| + |
| + // Try sending through other isolate. |
| + asyncStart(); |
| + echoPort((_) => throw "unreachable") |
| + .then((p) => p.send(func)) |
| + .then((p) => throw "unreachable 2", |
| + onError: (e, s) {asyncEnd();}); |
| +} |
| + |
| +void nop(_) {} |
| + |
| +void callFunc(message) { |
| + message[0](message[1], message[2]); |
| +} |