OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2014, 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 import "dart:isolate"; |
| 6 import "dart:async"; |
| 7 import "package:expect/expect.dart"; |
| 8 import "package:async_helper/async_helper.dart"; |
| 9 |
| 10 void toplevel(port, message) { port.send("toplevel:$message"); } |
| 11 Function createFuncToplevel() => (p, m) { p.send(m); }; |
| 12 class C { |
| 13 Function initializer; |
| 14 Function body; |
| 15 C() : initializer = ((p, m) { throw "initializer"; }) { |
| 16 body = (p, m) { throw "body"; }; |
| 17 } |
| 18 static void staticFunc(port, message) { port.send("static:$message"); } |
| 19 static Function createFuncStatic() => (p, m) { throw "static expr"; }; |
| 20 void instanceMethod(p, m) { throw "instanceMethod"; } |
| 21 Function createFuncMember() => (p, m) { throw "instance expr"; }; |
| 22 void call(n, p) { throw "C"; } |
| 23 } |
| 24 |
| 25 class Callable { |
| 26 void call(p, m) { p.send(["callable", m]); } |
| 27 } |
| 28 |
| 29 |
| 30 void main() { |
| 31 asyncStart(); |
| 32 |
| 33 // Sendables are top-level functions and static functions only. |
| 34 testSendable("toplevel", toplevel); |
| 35 testSendable("static", C.staticFunc); |
| 36 |
| 37 // Unsendables are any closure - instance methods or function expression. |
| 38 var c = new C(); |
| 39 testUnsendable("instance method", c.instanceMethod); |
| 40 testUnsendable("static context expression", createFuncToplevel()); |
| 41 testUnsendable("static context expression", C.createFuncStatic()); |
| 42 testUnsendable("initializer context expression", c.initializer); |
| 43 testUnsendable("constructor context expression", c.body); |
| 44 testUnsendable("instance method context expression", c.createFuncMember()); |
| 45 |
| 46 // The result of `toplevel.call` and `staticFunc.call` may or may not be |
| 47 // identical to `toplevel` and `staticFunc` respectively. If they are not |
| 48 // equal, they may or may not be considered toplevel/static functions anyway, |
| 49 // and therefore sendable. The VM and dart2js curretnly disagrees on whether |
| 50 // `toplevel` and `toplevel.call` are identical, both allow them to be sent. |
| 51 // If this is ever specified to something else, use: |
| 52 // testUnsendable("toplevel.call", toplevel.call); |
| 53 // testUnsendable("static.call", C.staticFunc.call); |
| 54 // instead. |
| 55 // These two tests should be considered canaries for accidental behavior |
| 56 // change rather than requirements. |
| 57 testSendable("toplevel", toplevel.call); |
| 58 testSendable("static", C.staticFunc.call); |
| 59 |
| 60 // Callable objects are sendable if general objects are (VM yes, dart2js no). |
| 61 // It's unspecified whether arbitrary objects can be sent. If it is specified, |
| 62 // add a test that `new Callable()` is either sendable or unsendable. |
| 63 |
| 64 // The call method of a callable object is a closure holding the object, |
| 65 // not a top-level or static function, so it should be blocked, just as |
| 66 // a normal method. |
| 67 testUnsendable("callable object", new Callable().call); |
| 68 asyncEnd(); |
| 69 return; |
| 70 } |
| 71 |
| 72 // Create a receive port that expects exactly one message. |
| 73 // Pass the message to `callback` and return the sendPort. |
| 74 SendPort singleMessagePort(callback) { |
| 75 var p; |
| 76 p = new RawReceivePort((v) { p.close(); callback(v); }); |
| 77 return p.sendPort; |
| 78 } |
| 79 |
| 80 // A singleMessagePort that expects the message to be a specific value. |
| 81 SendPort expectMessagePort(message) { |
| 82 asyncStart(); |
| 83 return singleMessagePort((v) { |
| 84 Expect.equals(message, v); |
| 85 asyncEnd(); |
| 86 }); |
| 87 } |
| 88 |
| 89 void testSendable(name, func) { |
| 90 // Function as spawn message. |
| 91 Isolate.spawn(callFunc, [func, expectMessagePort("$name:spawn"), "spawn"]); |
| 92 |
| 93 // Send function to same isolate. |
| 94 var reply = expectMessagePort("$name:direct"); |
| 95 singleMessagePort(callFunc).send([func, reply, "direct"]); |
| 96 |
| 97 // Send function to other isolate, call it there. |
| 98 reply = expectMessagePort("$name:other isolate"); |
| 99 callPort().then((p) { |
| 100 p.send([func, reply, "other isolate"]); |
| 101 }); |
| 102 |
| 103 // Round-trip function trough other isolate. |
| 104 echoPort((roundtripFunc) { |
| 105 Expect.identical(func, roundtripFunc, "$name:send through isolate"); |
| 106 }).then((port) { port.send(func); }); |
| 107 } |
| 108 |
| 109 // Creates a new isolate and a pair of ports that expect a single message |
| 110 // to be sent to the other isolate and back to the callback function. |
| 111 Future<SendPort> echoPort(callback(value)) { |
| 112 Completer completer = new Completer<SendPort>(); |
| 113 SendPort replyPort = singleMessagePort(callback); |
| 114 RawReceivePort initPort; |
| 115 initPort = new RawReceivePort((p) { |
| 116 completer.complete(p); |
| 117 initPort.close(); |
| 118 }); |
| 119 return Isolate.spawn(_echo, [replyPort, initPort.sendPort]) |
| 120 .then((isolate) => completer.future); |
| 121 } |
| 122 |
| 123 void _echo(msg) { |
| 124 var replyPort = msg[0]; |
| 125 RawReceivePort requestPort; |
| 126 requestPort = new RawReceivePort((msg) { |
| 127 replyPort.send(msg); |
| 128 requestPort.close(); // Single echo only. |
| 129 }); |
| 130 msg[1].send(requestPort.sendPort); |
| 131 } |
| 132 |
| 133 // Creates other isolate that waits for a single message, `msg`, on the returned |
| 134 // port, and executes it as `msg[0](msg[1],msg[2])` in the other isolate. |
| 135 Future<SendPort> callPort() { |
| 136 Completer completer = new Completer<SendPort>(); |
| 137 SendPort initPort = singleMessagePort(completer.complete); |
| 138 return Isolate.spawn(_call, initPort) |
| 139 .then((_) => completer.future); |
| 140 } |
| 141 |
| 142 void _call(initPort) { |
| 143 initPort.send(singleMessagePort(callFunc)); |
| 144 } |
| 145 // Expect.throws(() { |
| 146 // noReply.sendPort.send([func]); |
| 147 // }, null, "send wrapped"); |
| 148 |
| 149 |
| 150 void testUnsendable(name, func) { |
| 151 asyncStart(); |
| 152 Isolate.spawn(nop, func).then((v) => throw "allowed spawn direct?", |
| 153 onError: (e,s){ asyncEnd(); }); |
| 154 asyncStart(); |
| 155 Isolate.spawn(nop, [func]).then((v) => throw "allowed spawn wrapped?", |
| 156 onError: (e,s){ asyncEnd(); }); |
| 157 |
| 158 asyncStart(); |
| 159 var noReply = new RawReceivePort((_) { throw "Unexpected message: $_"; }); |
| 160 // Currently succeedes incorrectly in dart2js. |
| 161 Expect.throws(() { /// 01: ok |
| 162 noReply.sendPort.send(func); /// 01: continued |
| 163 }, null, "send direct"); /// 01: continued |
| 164 Expect.throws(() { /// 01: continued |
| 165 noReply.sendPort.send([func]); /// 01: continued |
| 166 }, null, "send wrapped"); /// 01: continued |
| 167 scheduleMicrotask(() { |
| 168 noReply.close(); |
| 169 asyncEnd(); |
| 170 }); |
| 171 |
| 172 // Try sending through other isolate. |
| 173 asyncStart(); |
| 174 echoPort((_) => throw "unreachable") |
| 175 .then((p) => p.send(func)) |
| 176 .then((p) => throw "unreachable 2", |
| 177 onError: (e, s) {asyncEnd();}); |
| 178 } |
| 179 |
| 180 void nop(_) {} |
| 181 |
| 182 void callFunc(message) { |
| 183 message[0](message[1], message[2]); |
| 184 } |
OLD | NEW |