diff --git a/compiler/java/com/google/dart/compiler/backend/isolate/DartIsolateStubGenerator.java b/compiler/java/com/google/dart/compiler/backend/isolate/DartIsolateStubGenerator.java index eebbc511636..a8324db68c8 100644 --- a/compiler/java/com/google/dart/compiler/backend/isolate/DartIsolateStubGenerator.java +++ b/compiler/java/com/google/dart/compiler/backend/isolate/DartIsolateStubGenerator.java @@ -317,6 +317,7 @@ public class DartIsolateStubGenerator extends AbstractBackend { final boolean isVoid = isVoid(returnTypeNode); final boolean isSimple = isSimpleType(returnTypeNode); final boolean isProxy = isProxyType(returnTypeNode); + final boolean isPromise = isPromise(returnTypeNode); if (!isVoid) { p("return "); if (!isSimple) { @@ -328,6 +329,16 @@ public class DartIsolateStubGenerator extends AbstractBackend { p("Impl("); } } + if (isProxy) { + // Note that Promises are Proxies. + p("new PromiseProxy"); + if (isPromise) { + printTypeArguments(returnTypeNode); + } else { + p(""); + } + p("("); + } p("this."); if (isVoid) { p("send"); @@ -353,6 +364,9 @@ public class DartIsolateStubGenerator extends AbstractBackend { }; params.acceptList(func.getParams()); p("])"); + if (isProxy) { + p(")"); + } if (!isSimple) { p(")"); } @@ -396,8 +410,23 @@ public class DartIsolateStubGenerator extends AbstractBackend { printFunctionName(member); p("\") {"); nl(); - unpackParams(member); - callTarget((DartMethodDefinition)member); + int proxies = unpackParams(member); + String extra = ""; + if (proxies != 0) { + p(" Promise done = new Promise();"); + nl(); + p(" done.waitFor(promises, " + proxies + ");"); + nl(); + p(" done.addCompleteHandler((_) {"); + nl(); + gatherProxies(member); + extra = " "; + } + callTarget((DartMethodDefinition)member, extra); + if (proxies != 0) { + p(" });"); + nl(); + } p(" }"); first = false; } @@ -411,11 +440,11 @@ public class DartIsolateStubGenerator extends AbstractBackend { nl(); } - private void callTarget(DartMethodDefinition member) { + private void callTarget(DartMethodDefinition member, String extra) { if (isConstructor(member)) { return; } - p(" "); + p(extra + " "); boolean isVoid = isVoid(member.getFunction().getReturnTypeNode()); if (!isVoid) { printReturnType(member); @@ -431,14 +460,14 @@ public class DartIsolateStubGenerator extends AbstractBackend { nl(); String returnType = stringReturnType(member); if (stubInterfaces.contains(returnType)) { - p(" SendPort port = Dispatcher.serve(new " + returnType + "$Dispatcher("); + p(extra + " SendPort port = Dispatcher.serve(new " + returnType + "$Dispatcher("); printFunctionName(member); p("));"); nl(); - p(" reply(port);"); + p(extra + " reply(port);"); nl(); } else if (!isVoid) { - p(" reply("); + p(extra + " reply("); printFunctionName(member); p(");"); nl(); @@ -521,9 +550,10 @@ public class DartIsolateStubGenerator extends AbstractBackend { return strType.toString(); } - private void unpackParams(DartNode member) { - DartVisitor visitor = new DartVisitor() { + private int unpackParams(DartNode member) { + class UnpackVisitor extends DartVisitor { private int pos; + int proxies; @Override public boolean visit(DartTypeNode x, DartContext ctx) { @@ -541,6 +571,7 @@ public class DartIsolateStubGenerator extends AbstractBackend { @Override public boolean visit(DartMethodDefinition x, DartContext ctx) { pos = 1; + proxies = 0; for (DartParameter param : x.getFunction().getParams()) { p(" "); accept(param); @@ -554,23 +585,85 @@ public class DartIsolateStubGenerator extends AbstractBackend { public boolean visit(DartParameter x, DartContext ctx) { boolean isSimpleType = isSimpleType(x.getTypeNode()); - accept(x.getTypeNode()); - p(" "); - accept(x.getName()); - p(" = "); - if (!isSimpleType) { - p("new "); + if (isSimpleType) { accept(x.getTypeNode()); - p("Impl(new Promise.fromValue("); + p(" "); + accept(x.getName()); + p(" = "); + } else { + if (proxies == 0) { + p("List> promises = new List>();"); + nl(); + p(" "); + } + p("promises.add(new PromiseProxy(new Promise.fromValue("); + ++proxies; + //p("new "); + //accept(x.getTypeNode()); + //p("Impl(new Promise.fromValue("); } p("message[" + pos + "]"); if (!isSimpleType) { - p("))"); + p(")))"); } p(";"); return false; } }; + + UnpackVisitor visitor = new UnpackVisitor(); + + visitor.accept(member); + return visitor.proxies; + } + + private void gatherProxies(DartNode member) { + class GatherVisitor extends DartVisitor { + private int proxies; + + @Override + public boolean visit(DartTypeNode x, DartContext ctx) { + accept(x.getIdentifier()); + printTypeArguments(x); + return false; + } + + @Override + public boolean visit(DartIdentifier x, DartContext ctx) { + p(x.getTargetName()); + return false; + } + + @Override + public boolean visit(DartMethodDefinition x, DartContext ctx) { + proxies = 0; + for (DartParameter param : x.getFunction().getParams()) { + accept(param); + } + return false; + } + + @Override + public boolean visit(DartParameter x, DartContext ctx) { + boolean isSimpleType = isSimpleType(x.getTypeNode()); + + if (!isSimpleType) { + p(" "); + accept(x.getTypeNode()); + p(" "); + accept(x.getName()); + p(" = new "); + accept(x.getTypeNode()); + p("Impl(promises[" + proxies + "]);"); + ++proxies; + nl(); + } + return false; + } + }; + + GatherVisitor visitor = new GatherVisitor(); + visitor.accept(member); } @@ -608,8 +701,14 @@ public class DartIsolateStubGenerator extends AbstractBackend { * reply(port); * } else if (command == "deposit") { * int amount = message[1]; - * Proxy source = new Proxy.forPort(message[2]); - * target.deposit(amount, source); + * Promise port = + * new PromiseProxy(new Promise.fromValue(message[2])); + * port.addCompletionHandler((_) { + * Purse$Proxy source = new Purse$ProxyImpl(port); + * target.deposit(amount, source); + * }); + * //Proxy source = new Proxy.forPort(message[2]); + * //target.deposit(amount, source); * } else { * // TODO(kasperl,benl): Somehow throw an exception instead. * reply("Exception: command not understood."); diff --git a/compiler/lib/implementation/isolate.dart b/compiler/lib/implementation/isolate.dart index c016d8e2187..0680c740d1c 100644 --- a/compiler/lib/implementation/isolate.dart +++ b/compiler/lib/implementation/isolate.dart @@ -7,17 +7,8 @@ class SendPortImpl implements SendPort { const SendPortImpl(this._workerId, this._isolateId, this._receivePortId); void send(var message, [SendPort replyTo = null]) { - if (PromiseQueue.isEmpty()) { - this._sendNow(message, replyTo); - } else { - _enqueueSend(message, replyTo); - } - } - - void _enqueueSend(var message, SendPort replyTo) { - PromiseQueue.enqueue(const []).then((ignored) { - this._sendNow(message, replyTo); - }); + // TODO(kasperl): get rid of _sendNow. + this._sendNow(message, replyTo); } void _sendNow(var message, SendPort replyTo) native; @@ -179,10 +170,6 @@ class IsolateNatives { class _IsolateJsUtil { - static void _promiseQueueProcess() native { - PromiseQueue.process(); - } - static void _startIsolate(Isolate isolate, SendPort replyTo) native { ReceivePort port = new ReceivePort(); replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); diff --git a/compiler/lib/implementation/isolate.js b/compiler/lib/implementation/isolate.js index 1783ce0272b..046de8f44e0 100644 --- a/compiler/lib/implementation/isolate.js +++ b/compiler/lib/implementation/isolate.js @@ -66,7 +66,6 @@ function isolate$receiveMessage(port, isolate, var message = isolate$deserializeMessage(serializedMessage); var replyTo = isolate$deserializeMessage(serializedReplyTo); native_ReceivePortImpl__invokeCallback(port, message, replyTo); - native__IsolateJsUtil__promiseQueueProcess(); }); } diff --git a/corelib/src/implementation/promise_implementation.dart b/corelib/src/implementation/promise_implementation.dart index f4ca0135552..dbc2912245c 100644 --- a/corelib/src/implementation/promise_implementation.dart +++ b/corelib/src/implementation/promise_implementation.dart @@ -275,6 +275,19 @@ class ProxyImpl { _promise = port; } + // Note that comparing proxies or using them in maps is illegal + // until they complete. + bool operator ==(var other) { + return (other is ProxyImpl) && _promise.value == other._promise.value; + } + + int hashCode() => _promise.value.hashCode(); + + // TODO: consider making this extend Promise instead? + void addCompleteHandler(void completeHandler()) { + _promise.addCompleteHandler((_) => completeHandler()); + } + static ReceivePort register(Dispatcher dispatcher) { if (_dispatchers === null) { _dispatchers = new Map(); @@ -289,7 +302,7 @@ class ProxyImpl { Dispatcher dispatcher = _dispatchers[_promise.value]; if (dispatcher !== null) return dispatcher.target; } - throw "Cannot access object of non-local proxy."; + throw new Exception("Cannot access object of non-local proxy."); } void send(List message) { @@ -315,39 +328,46 @@ class ProxyImpl { }); } - bool operator ==(var other) { - return this === other; - } - - // FIXME(benl): generate a more useful hashCode. - int hashCode() => 0; - // Marshal the [message] and pass it to the [process] callback - // function once this proxy and all proxies in the message are - // resolved. - Promise _marshal(List message, process(List marshalled)) { - final promises = new List(); - promises.add(_promise); + // function. Any promises are converted to a port which expects to + // receive a port from the other side down which the remote promise + // can be completed by sending the promise's completion value. + Promise _marshal(List message, process(List marshalled)) { + return _promise.then((SendPort port) { + List marshalled = new List(message.length); - var marshalled = new List(message.length); - for (int i = 0; i < marshalled.length; i++) { - var entry = message[i]; - marshalled[i] = entry; // TODO(kasperl): We probably have to copy here. - if (entry is Proxy) { - promises.add(entry._promise); - } else if (entry is Promise) { - promises.add(entry); - } - } - - return PromiseQueue.enqueue(promises).then((ignored) { for (int i = 0; i < marshalled.length; i++) { - var entry = marshalled[i]; + var entry = message[i]; if (entry is Proxy) { - marshalled[i] = entry._promise.value; - } else if (entry is Promise) { - marshalled[i] = entry.value; + entry = entry._promise; } + // Obviously this will be true if [entry] was a Proxy. + if (entry is Promise) { + // Note that we could optimise this by just sending the value + // if the promise is already complete. Let's get this working + // first! + + // This port will receive a SendPort that can be used to + // signal completion of this promise to the corresponding + // promise that the other end has created. + ReceivePort receiveCompleter = new ReceivePort.singleShot(); + marshalled[i] = receiveCompleter.toSendPort(); + Promise completer = new Promise(); + receiveCompleter.receive((var msg, SendPort replyPort) { + completer.complete(msg[0]); + }); + entry.addCompleteHandler((value) { + completer.addCompleteHandler((SendPort port) { + port.send([value], null); + }); + }); + } else { + // FIXME(kasperl, benl): this should probably be a copy? + marshalled[i] = entry; + } + if (marshalled[i] is ReceivePort) { + throw new Exception("Despite the documentation, you cannot send a ReceivePort"); + } } return process(marshalled); }).flatten(); @@ -357,60 +377,3 @@ class ProxyImpl { static Map _dispatchers; } - - -class PromiseQueue { - - // Enqueue an element that depends on a list of promises. The - // returned promise is resolved when all the input promises have - // been resolved. - static Promise enqueue(List dependencies) { - if (_queue === null) { - _queue = new Queue(); - } - - // Keep track of how many unresolved promises we're waiting for. - int unresolved = dependencies.length; - void notifyResolved(ignored) { - assert(unresolved > 0); - unresolved--; - } - - // Register a callback on each of the dependencies. - for (Promise promise in dependencies) { - promise.then(notifyResolved); - } - - final Promise result = new Promise(); - _queue.addLast(() { - if (unresolved > 0) return false; - // TODO(kasperl): It seems a bit weird to pass back null. Maybe - // the resulting promise should be a Promise that - // indicates whether or not we successfully got the enqueued - // element through the queue? - result.complete(null); - return true; - }); - - // Before returning the resulting promise, we make sure to process - // any fully resolved enqueued elements. - process(); - return result; - } - - static bool isEmpty() { - return (_queue === null) ? true : _queue.isEmpty(); - } - - static void process() { - if (_queue === null) { - return; - } - while (!_queue.isEmpty() && (_queue.first())()) { - _queue.removeFirst(); - } - } - - static Queue _queue; - -} diff --git a/corelib/src/promise.dart b/corelib/src/promise.dart index f656d0ef50a..4b4fcdf375b 100644 --- a/corelib/src/promise.dart +++ b/corelib/src/promise.dart @@ -143,3 +143,20 @@ class Dispatcher { T target; } + +// When a promise is sent across a port, it is converted to a +// Promise down which we must send a port to receive the +// completion value. Hand the Promise to this class to deal +// with it. + +class PromiseProxy extends PromiseImpl { + PromiseProxy(Promise sendCompleter) { + ReceivePort completer = new ReceivePort.singleShot(); + completer.receive((var msg, SendPort _) { + complete(msg[0]); + }); + sendCompleter.addCompleteHandler((SendPort port) { + port.send([completer.toSendPort()], null); + }); + } +} diff --git a/runtime/lib/isolate.dart b/runtime/lib/isolate.dart index 2935da6f733..5d9d8b23455 100644 --- a/runtime/lib/isolate.dart +++ b/runtime/lib/isolate.dart @@ -48,7 +48,6 @@ class ReceivePortImpl implements ReceivePort { ReceivePort port = _portMap[id]; SendPort replyTo = (replyId == 0) ? null : new SendPortImpl(replyId); (port._onMessage)(message, replyTo); - PromiseQueue.process(); } // Call into the VM to close the VM maintained mappings. @@ -89,17 +88,7 @@ class ReceivePortSingleShotImpl implements ReceivePort { class SendPortImpl implements SendPort { /*--- public interface ---*/ void send(var message, [SendPort replyTo = null]) { - if (PromiseQueue.isEmpty()) { - this._sendNow(message, replyTo); - } else { - _enqueueSend(message, replyTo); - } - } - - void _enqueueSend(var message, SendPort replyTo) { - PromiseQueue.enqueue(const []).then((ignored) { - this._sendNow(message, replyTo); - }); + this._sendNow(message, replyTo); } void _sendNow(var message, SendPort replyTo) { diff --git a/tests/isolate/src/MintMakerPromiseTest.dart b/tests/isolate/src/MintMakerPromiseTest.dart index edf3e1e987b..eeb5c5ac6d6 100644 --- a/tests/isolate/src/MintMakerPromiseTest.dart +++ b/tests/isolate/src/MintMakerPromiseTest.dart @@ -26,7 +26,7 @@ interface Purse { int queryBalance(); Purse sproutPurse(); - void deposit(int amount, Purse$Proxy source); + int deposit(int amount, Purse$Proxy source); } @@ -43,12 +43,14 @@ class PurseImpl implements Purse { return _mint.createPurse(0); } - void deposit(int amount, Purse$Proxy purse) { + int deposit(int amount, Purse$Proxy purse) { Purse$ProxyImpl impl = purse.dynamic; // TODO: Get rid of this 'cast'. PurseImpl source = impl.local; if (source._balance < amount) throw "Not enough dough."; _balance += amount; source._balance -= amount; + print("Moved $amount, leaving ${source._balance}"); + return _balance; } Mint _mint; @@ -67,15 +69,25 @@ class MintMakerPromiseTest { Purse$Proxy sprouted = purse.sproutPurse(); expectEquals(0, sprouted.queryBalance()); - sprouted.deposit(5, purse); - expectEquals(0 + 5, sprouted.queryBalance()); - expectEquals(100 - 5, purse.queryBalance()); + Promise balance = sprouted.deposit(5, purse); + expectEquals(0 + 5, balance); + // FIXME(benl): because we have no ordering constraints we have to + // manually order the messages or it all falls apart. We should + // implement E-ORDER. + balance.addCompleteHandler((_) { + expectEquals(0 + 5, sprouted.queryBalance()); + expectEquals(100 - 5, purse.queryBalance()); - sprouted.deposit(42, purse); - expectEquals(0 + 5 + 42, sprouted.queryBalance()); - expectEquals(100 - 5 - 42, purse.queryBalance()); + balance = sprouted.deposit(42, purse); + expectEquals(0 + 5 + 42, balance); + balance.addCompleteHandler((_) { + expectEquals(0 + 5 + 42, sprouted.queryBalance()); + expectEquals(100 - 5 - 42, purse.queryBalance()); + // FIXME(benl): once more we could "pass" by not running anything much. + expectDone(8); + }); + }); - expectDone(6); } static Mint$Proxy createMint() { @@ -91,6 +103,7 @@ class MintMakerPromiseTest { results = new List(); } results.add(promise.then((int actual) { + print("done $expected/$actual"); Expect.equals(expected, actual); })); } @@ -102,6 +115,7 @@ class MintMakerPromiseTest { Promise done = new Promise(); done.waitFor(results, results.length); done.then((ignored) { + print("expectDone $n/${results.length}"); Expect.equals(n, results.length); }); } @@ -166,7 +180,7 @@ interface Purse$Proxy { Promise queryBalance(); Purse$Proxy sproutPurse(); - void deposit(int amount, Purse$Proxy source); // Promise amount. + Promise deposit(int amount, Purse$Proxy source); // Promise amount. } @@ -179,8 +193,8 @@ class Purse$ProxyImpl extends Proxy implements Purse$Proxy { return this.call(["balance"]); } - void deposit(int amount, Purse$Proxy source) { - this.send(["deposit", amount, source]); + Promise deposit(int amount, Purse$Proxy source) { + return this.call(["deposit", amount, source]); } Purse$Proxy sproutPurse() { @@ -196,14 +210,19 @@ class Purse$Dispatcher extends Dispatcher { void process(var message, void reply(var response)) { String command = message[0]; + print("command $command"); if (command == "balance") { int balance = target.queryBalance(); reply(balance); } else if (command == "deposit") { int amount = message[1]; - Promise port = new Promise.fromValue(message[2]); - Purse$Proxy source = new Purse$ProxyImpl(port); - target.deposit(amount, source); + Promise port = + new PromiseProxy(new Promise.fromValue(message[2])); + port.addCompleteHandler((_) { + Purse$Proxy source = new Purse$ProxyImpl(port); + int balance = target.deposit(amount, source); + reply(balance); + }); } else if (command == "sprout") { Purse purse = target.sproutPurse(); SendPort port = Dispatcher.serve(new Purse$Dispatcher(purse)); @@ -212,6 +231,7 @@ class Purse$Dispatcher extends Dispatcher { // TODO: Send an exception back. reply("Exception: Command not understood"); } + print("command $command done"); } } diff --git a/tests/isolate/src/PromiseBasedTest.dart b/tests/isolate/src/PromiseBasedTest.dart index 91b3e9c0325..aa957a69ffb 100644 --- a/tests/isolate/src/PromiseBasedTest.dart +++ b/tests/isolate/src/PromiseBasedTest.dart @@ -12,12 +12,15 @@ class TestIsolate extends Isolate { void main() { int seed = 0; this.port.receive((var message, SendPort replyTo) { + print("Got ${message[0]}"); if (seed == 0) { seed = message[0]; } else { Promise response = new Promise(); var proxy = new Proxy.forPort(replyTo); + print("send to proxy"); proxy.send([response]); + print("sent"); response.complete(seed + message[0]); this.port.close(); } @@ -29,16 +32,45 @@ class TestIsolate extends Isolate { void test(TestExpectation expect) { Proxy proxy = new Proxy.forIsolate(new TestIsolate()); proxy.send([42]); // Seed the isolate. - Promise promise = expect.completes(proxy.call([87])).then((int value) { + Promise result = new PromiseProxy(proxy.call([87])); + Promise promise = expect.completes(result).then((int value) { + print("expect 1: $value"); Expect.equals(42 + 87, value); return 99; }); expect.completes(promise).then((int value) { + print("expect 2: $value"); + Expect.equals(99, value); + expect.succeeded(); + }); +} + +void expandedTest(TestExpectation expect) { + Proxy proxy = new Proxy.forIsolate(new TestIsolate()); + proxy.send([42]); // Seed the isolate. + Promise sendCompleter = proxy.call([87]); + Promise result = new Promise(); + ReceivePort completer = new ReceivePort.singleShot(); + completer.receive((var msg, SendPort _) { + print("test completer"); + result.complete(msg[0]); + }); + sendCompleter.addCompleteHandler((SendPort port) { + print("test send"); + port.send([completer.toSendPort()], null); + }); + Promise promise = expect.completes(result).then((int value) { + print("expect 1: $value"); + Expect.equals(42 + 87, value); + return 99; + }); + expect.completes(promise).then((int value) { + print("expect 2: $value"); Expect.equals(99, value); expect.succeeded(); }); } void main() { - runTests([test]); + runTests([test, expandedTest]); } diff --git a/tests/isolate/src/TestFramework.dart b/tests/isolate/src/TestFramework.dart index 3cd960f8974..cb93843603e 100644 --- a/tests/isolate/src/TestFramework.dart +++ b/tests/isolate/src/TestFramework.dart @@ -9,8 +9,8 @@ typedef void AsynchronousTestFunction(TestExpectation check); -void runTests(List tests) { - TestRunner runner = new TestRunner(new TestSuite(tests)); +void runTests(List tests, [bool guarded = true]) { + TestRunner runner = new TestRunner(new TestSuite(tests), guarded); TestResult result = new TestResult(runner); runner.run(result); } @@ -87,6 +87,9 @@ class TestResult { } runGuarded(TestCase testCase, Function fn) { + if (!runner.guarded) { + return fn(); + } var result = null; try { result = fn(); @@ -113,7 +116,7 @@ class TestResult { class TestRunner { - TestRunner(this.suite); + TestRunner(this.suite, this.guarded); void run(TestResult result) { if (waitForDoneCallback !== null) { @@ -150,6 +153,7 @@ class TestRunner { } final TestSuite suite; + final bool guarded; static Function waitForDoneCallback; static Function doneCallback; diff --git a/tests/stub-generator/src/MintMakerFullyIsolatedTest.dart b/tests/stub-generator/src/MintMakerFullyIsolatedTest.dart index 241d0f79d87..3cbfd63036b 100644 --- a/tests/stub-generator/src/MintMakerFullyIsolatedTest.dart +++ b/tests/stub-generator/src/MintMakerFullyIsolatedTest.dart @@ -30,9 +30,61 @@ interface Mint factory MintImpl { PowerfulPurse$Proxy promote(Purse$Proxy purse); } +// Because promises can't be used as keys in maps until they have +// completed, provide a wrapper. Note that if any key promise fails to +// resolve, then get()'s return may also fail to resolve. Also, +// although the logic is fine, this can't be used for a +// ProxyMap. Perhaps both Proxy and Promise should inherit from +// Completable? +// NB: not tested and known to be buggy. Will fix in a future change. +class PromiseMap { + + PromiseMap() { + _map = new Map(); + _incomplete = new Set(); + } + + T add(S s, T t) { + _incomplete.add(s); + s.addCompleteHandler((_) { + _map[s] = t; + _incomplete.remove(s); + }); + return t; + } + + Promise find(S s) { + T t = _map[s]; + if (t != null) + return new Promise.fromValue(t); + Promise p = new Promise(); + int counter = _incomplete.length; + p.join(_incomplete, bool (S completed) { + if (completed != s) { + if (--counter == 0) { + p.complete(null); + return true; + } + return false; + } + p.complete(_map[s]); + return true; + }); + return p; + } + + Set _incomplete; + Map _map; + +} + class MintImpl implements Mint { - MintImpl() { print('mint'); } + MintImpl() { + print('mint'); + if (_power == null) + _power = new Map(); + } Purse$Proxy createPurse(int balance) { print('createPurse'); @@ -42,17 +94,18 @@ class MintImpl implements Mint { purse.init(thisProxy, balance); Purse$Proxy weakPurse = purse.weak(); - if (_power === null) - _power = new Map(); - print('cP1'); - print(weakPurse.hashCode()); - _power[weakPurse] = purse; - print('cP2'); + weakPurse.addCompleteHandler(() { + print('cP1'); + _power[weakPurse] = purse; + print('cP2'); + }); return weakPurse; } PowerfulPurse$Proxy promote(Purse$Proxy purse) { - print('promote'); + // FIXME(benl): we should be using a PromiseMap here. But we get + // away with it in this test for now. + print('promote $purse/${_power[purse]}'); return _power[purse]; } @@ -128,16 +181,16 @@ class MintMakerFullyIsolatedTest { done.then((int) { expectEquals(0 + 5, sprouted.queryBalance()); expectEquals(100 - 5, purse.queryBalance()); - }); - done = sprouted.deposit(42, purse); - expectEquals(42, done); - done.then((int) { - expectEquals(0 + 5 + 42, sprouted.queryBalance()); - expectEquals(100 - 5 - 42, purse.queryBalance()); - }); + done = sprouted.deposit(42, purse); + expectEquals(5 + 42, done); + done.then((int) { + expectEquals(0 + 5 + 42, sprouted.queryBalance()); + expectEquals(100 - 5 - 42, purse.queryBalance()); - expectDone(8); + expectDone(8); + }); + }); } static List results; diff --git a/tests/stub-generator/src/MintMakerPromiseWithStubsTest.dart b/tests/stub-generator/src/MintMakerPromiseWithStubsTest.dart index ca8d5b0a51a..2cc6abf8c16 100644 --- a/tests/stub-generator/src/MintMakerPromiseWithStubsTest.dart +++ b/tests/stub-generator/src/MintMakerPromiseWithStubsTest.dart @@ -18,7 +18,7 @@ interface Purse factory PurseImpl { int queryBalance(); Purse sproutPurse(); - void deposit(int amount, Purse$Proxy source); + int deposit(int amount, Purse$Proxy source); } @@ -55,7 +55,7 @@ class PurseImpl implements Purse { return _mint.createPurse(0); } - void deposit(int amount, Purse$Proxy proxy) { + int deposit(int amount, Purse$Proxy proxy) { if (amount < 0) throw "Ha ha"; // Because we are in the same isolate as the other purse, we can // retrieve the proxy's local PurseImpl object and act on it @@ -65,6 +65,7 @@ class PurseImpl implements Purse { if (source._balance < amount) throw "Not enough dough."; _balance += amount; source._balance -= amount; + return _balance; } Mint _mint; @@ -82,15 +83,22 @@ class MintMakerPromiseWithStubsTest { Purse$Proxy sprouted = purse.sproutPurse(); expectEquals(0, sprouted.queryBalance()); - sprouted.deposit(5, purse); - expectEquals(0 + 5, sprouted.queryBalance()); - expectEquals(100 - 5, purse.queryBalance()); + // FIXME(benl): We should not have to manually order the calls + // like this. + Promise result = sprouted.deposit(5, purse); + expectEquals(5, result); + result.addCompleteHandler((_) { + expectEquals(0 + 5, sprouted.queryBalance()); + expectEquals(100 - 5, purse.queryBalance()); - sprouted.deposit(42, purse); - expectEquals(0 + 5 + 42, sprouted.queryBalance()); - expectEquals(100 - 5 - 42, purse.queryBalance()); - - expectDone(6); + result = sprouted.deposit(42, purse); + expectEquals(5 + 42, result); + result.addCompleteHandler((_) { + expectEquals(0 + 5 + 42, sprouted.queryBalance()); + expectEquals(100 - 5 - 42, purse.queryBalance()); + expectDone(8); + }); + }); } static List results; @@ -100,6 +108,7 @@ class MintMakerPromiseWithStubsTest { results = new List(); } results.add(promise.then((int actual) { + print("done $expected/$actual"); Expect.equals(expected, actual); })); } diff --git a/tests/stub-generator/stub-generator.status b/tests/stub-generator/stub-generator.status index d5ac2399ff8..f50e10f59de 100644 --- a/tests/stub-generator/stub-generator.status +++ b/tests/stub-generator/stub-generator.status @@ -5,11 +5,9 @@ prefix stub-generator [ $arch == ia32 ] -MintMakerFullyIsolatedTest: Skip # Bug 5283149 -MintMakerPromiseWithStubsTest: Skip # Bug 5384756 +MintMakerFullyIsolatedTest: Skip # issue 115 [ $arch == dartc ] -MintMakerFullyIsolatedTest: Fail # benl [ $arch == x64 ] *: Skip diff --git a/tests/stub-generator/testcfg.py b/tests/stub-generator/testcfg.py index 0d436fac060..88cf11ad53b 100644 --- a/tests/stub-generator/testcfg.py +++ b/tests/stub-generator/testcfg.py @@ -79,6 +79,7 @@ class DartStubTestConfiguration(test_configuration.StandardTestConfiguration): def ListTests(self, current_path, path, mode, arch): dartc = self.context.GetDartC(mode, 'dartc') if not os.access(dartc[0], os.X_OK): + print "Can't find dartc at", str(dartc) + ", skipping" return [] tests = [] for root, dirs, files in os.walk(join(self.root, 'src')): diff --git a/tools/test.py b/tools/test.py index e3896d3f3ea..7e30166dc43 100755 --- a/tools/test.py +++ b/tools/test.py @@ -612,7 +612,8 @@ class Context(object): def GetDartC(self, mode, arch): """Returns the path to the Dart --> JS compiler.""" dartc = os.path.abspath(os.path.join( - self.GetBuildRoot(mode, arch), 'compiler', 'bin', 'dartc')) + utils.GetBaseDir(), 'compiler', self.GetBuildRoot(mode, arch), + 'compiler', 'bin', 'dartc')) if utils.IsWindows(): dartc += '.exe' command = [dartc] diff --git a/tools/utils.py b/tools/utils.py index e3b1d386159..b2105802f72 100644 --- a/tools/utils.py +++ b/tools/utils.py @@ -134,6 +134,9 @@ def GetBuildConf(mode, arch): # instead of 'dartc'. RUN_FROM_TOP_DIR = os.path.basename(os.path.abspath(os.curdir)) == 'dart' ARCH_GUESS = GuessArchitecture() +BASE_DIR = os.path.abspath(os.path.join(os.curdir, '..')) +if RUN_FROM_TOP_DIR: + BASE_DIR = os.path.abspath(os.curdir) def GetBuildRoot(target_os, mode=None, arch=None): if arch == 'dartc' and RUN_FROM_TOP_DIR: arch = ARCH_GUESS @@ -143,6 +146,8 @@ def GetBuildRoot(target_os, mode=None, arch=None): else: return BUILD_ROOT[target_os] +def GetBaseDir(): + return BASE_DIR def RewritePathSeparator(path, workspace): # Paths in test files are always specified using '/'