mirror of
https://github.com/dart-lang/sdk
synced 2024-10-03 13:22:01 +00:00
4ba69c4df5
these tests on by default yet because of an issue in the stacktrace package (see issue 31764) Change-Id: Ifeb5dc4fc85e7d83b3011948133ce6d2a1998248 Reviewed-on: https://dart-review.googlesource.com/29480 Commit-Queue: Siva Annamalai <asiva@google.com> Reviewed-by: Régis Crelier <regis@google.com>
221 lines
6.2 KiB
Dart
221 lines
6.2 KiB
Dart
// 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");
|
|
}
|
|
|
|
Function createFuncToplevel() => (p, m) {
|
|
p.send(m);
|
|
};
|
|
|
|
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 createFuncStatic() => (p, m) {
|
|
throw "static expr";
|
|
};
|
|
void instanceMethod(p, m) {
|
|
throw "instanceMethod";
|
|
}
|
|
|
|
Function createFuncMember() => (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.instanceMethod);
|
|
testUnsendable("static context expression", createFuncToplevel());
|
|
testUnsendable("static context expression", C.createFuncStatic());
|
|
testUnsendable("initializer context expression", c.initializer);
|
|
testUnsendable("constructor context expression", c.body);
|
|
testUnsendable("instance method context expression", c.createFuncMember());
|
|
|
|
// The result of `toplevel.call` and `staticFunc.call` may or may not be
|
|
// identical to `toplevel` and `staticFunc` respectively. If they are not
|
|
// equal, they may or may not be considered toplevel/static functions anyway,
|
|
// and therefore sendable. The VM and dart2js currently disagree on whether
|
|
// `toplevel` and `toplevel.call` are identical, both allow them to be sent.
|
|
// If this is ever specified to something else, use:
|
|
// testUnsendable("toplevel.call", toplevel.call);
|
|
// testUnsendable("static.call", C.staticFunc.call);
|
|
// instead.
|
|
// These two tests should be considered canaries for accidental behavior
|
|
// change rather than requirements.
|
|
testSendable("toplevel", toplevel.call);
|
|
testSendable("static", C.staticFunc.call);
|
|
|
|
// Callable objects are sendable if general objects are (VM yes, dart2js no).
|
|
// It's unspecified whether arbitrary objects can be sent. If it is specified,
|
|
// add a test that `new Callable()` is either sendable or unsendable.
|
|
|
|
// The call method of a callable object is a closure holding the object,
|
|
// not a top-level or static function, so it should be blocked, just as
|
|
// a normal method.
|
|
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.
|
|
SendPort 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.
|
|
SendPort 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<SendPort> 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<SendPort> completer = new Completer<SendPort>();
|
|
SendPort initPort = singleMessagePort(completer.complete);
|
|
return Isolate.spawn(_call, initPort).then((_) => completer.future);
|
|
}
|
|
|
|
void _call(initPort) {
|
|
initPort.send(singleMessagePort(callFunc));
|
|
}
|
|
|
|
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: $_";
|
|
});
|
|
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((v) {
|
|
Expect.equals(0, v);
|
|
}).then((p) {
|
|
try {
|
|
p.send(func);
|
|
} finally {
|
|
p.send(0); // Closes echo port.
|
|
}
|
|
}).then((p) => throw "unreachable 2", onError: (e, s) {
|
|
asyncEnd();
|
|
});
|
|
}
|
|
|
|
void nop(_) {}
|
|
|
|
void callFunc(message) {
|
|
message[0](message[1], message[2]);
|
|
}
|