diff --git a/compiler/src/test/java/org/capnproto/EncodingTest.java b/compiler/src/test/java/org/capnproto/EncodingTest.java index fde64a1..f4c48a3 100644 --- a/compiler/src/test/java/org/capnproto/EncodingTest.java +++ b/compiler/src/test/java/org/capnproto/EncodingTest.java @@ -844,6 +844,19 @@ public class EncodingTest { Assert.assertEquals(listReader.get(0).getInt8Field(), 11); TestUtil.checkTestMessage(listReader.get(1)); } + + @org.junit.Test + public void testCopyAnyPointer() { + MessageBuilder message1 = new MessageBuilder(); + Test.TestAllTypes.Builder root1 = message1.initRoot(Test.TestAllTypes.factory); + TestUtil.initTestMessage(root1); + + MessageBuilder message2 = new MessageBuilder(); + AnyPointer.Builder root2 = message2.initRoot(AnyPointer.factory); + root2.set(message1.getRoot(AnyPointer.factory).asReader()); + + TestUtil.checkTestMessage(root2.getAs(Test.TestAllTypes.factory)); + } } diff --git a/runtime-rpc/src/main/java/org/capnproto/RpcState.java b/runtime-rpc/src/main/java/org/capnproto/RpcState.java index 4791c87..28a1315 100644 --- a/runtime-rpc/src/main/java/org/capnproto/RpcState.java +++ b/runtime-rpc/src/main/java/org/capnproto/RpcState.java @@ -1353,7 +1353,38 @@ final class RpcState { @Override public ClientHook.VoidPromiseAndPipeline directTailCall(RequestHook request) { - return null; + assert this.response == null: "Can't call tailCall() after initializing the results struct."; + + if (request.getBrand() == RpcState.this && !this.redirectResults) { + // The tail call is headed towards the peer that called us in the first place, so we can + // optimize out the return trip. + + var tailInfo = ((RpcRequest)request).tailSend(); + if (tailInfo != null) { + if (isFirstResponder()) { + if (isConnected()) { + var message = connection.newOutgoingMessage( + messageSizeHint() + + RpcProtocol.Return.factory.structSize().total()); + var builder = message.getBody().initAs(RpcProtocol.Message.factory).initReturn(); + builder.setAnswerId(this.answerId); + builder.setReleaseParamCaps(false); + builder.setTakeFromOtherQuestion(tailInfo.questionId); + message.send(); + } + + cleanupAnswerTable(null, false); + } + return new ClientHook.VoidPromiseAndPipeline(tailInfo.promise, tailInfo.pipeline); + } + } + + // Just forward to another local call + var promise = request.send(); + var voidPromise = promise.thenAccept(results -> { + getResults(0).set(results); + }); + return new ClientHook.VoidPromiseAndPipeline(voidPromise, promise.pipeline().hook); } private RpcResponse consumeRedirectedResponse() { diff --git a/runtime/src/main/java/org/capnproto/AnyPointer.java b/runtime/src/main/java/org/capnproto/AnyPointer.java index 7acabbc..c4d2312 100644 --- a/runtime/src/main/java/org/capnproto/AnyPointer.java +++ b/runtime/src/main/java/org/capnproto/AnyPointer.java @@ -128,6 +128,18 @@ public final class AnyPointer { factory.setPointerBuilder(this.segment, this.capTable, this.pointer, reader); } + public void set(AnyPointer.Reader reader) { + if (reader.isNull()) { + WireHelpers.zeroObject(this.segment, this.capTable, this.pointer); + WireHelpers.zeroPointerAndFars(this.segment, this.pointer); + } + else { + WireHelpers.copyPointer( + this.segment, this.capTable, this.pointer, + reader.segment, reader.capTable, reader.pointer, reader.nestingLimit); + } + } + final void setAsCap(Capability.Client cap) { WireHelpers.setCapabilityPointer(this.segment, capTable, this.pointer, cap.getHook()); }