From 3b0e3f37a0d67ed2a4dbbe9eca76580d8a60755e Mon Sep 17 00:00:00 2001 From: Vaci Koblizek Date: Tue, 6 Oct 2020 18:26:50 +0100 Subject: [PATCH] generate (non-generic) interfaces and capability accessors --- compiler/src/main/cpp/capnpc-java.c++ | 192 +++++++++++++++++++++++++- 1 file changed, 189 insertions(+), 3 deletions(-) diff --git a/compiler/src/main/cpp/capnpc-java.c++ b/compiler/src/main/cpp/capnpc-java.c++ index 86d558b..e1ea8d0 100644 --- a/compiler/src/main/cpp/capnpc-java.c++ +++ b/compiler/src/main/cpp/capnpc-java.c++ @@ -199,6 +199,10 @@ private: bool hasInterfaces = false; kj::StringTree javaFullName(Schema schema) { + return javaFullName(schema, nullptr); + } + + kj::StringTree javaFullName(Schema schema, kj::Maybe method) { auto node = schema.getProto(); if (node.getScopeId() == 0) { usedImports.insert(node.getId()); @@ -1022,8 +1026,41 @@ private: }; } else if (kind == FieldKind::INTERFACE) { - KJ_FAIL_REQUIRE("interfaces unimplemented"); + auto factoryArg = makeFactoryArg(schema::Type::ANY_POINTER); + auto capType = typeName(field.getType(), kj::str("Client")).flatten(); + + return FieldText { + kj::strTree( + kj::mv(unionDiscrim.readerIsDef), + spaces(indent), " public boolean has", titleCase, "() {\n", + unionDiscrim.has, + spaces(indent), " return !_pointerFieldIsNull(", offset, ");\n", + spaces(indent), " }\n", + + spaces(indent), " public ", capType, " get", titleCase, "() {\n", + unionDiscrim.check, + spaces(indent), " return new ", capType, "(_getPointerField(", factoryArg, ", ", offset, ").getAsCap());\n", + spaces(indent), " }\n"), + + kj::strTree( + kj::mv(unionDiscrim.builderIsDef), + spaces(indent), " public final boolean has", titleCase, "() {\n", + spaces(indent), " return !_pointerFieldIsNull(", offset, ");\n", + spaces(indent), " }\n", + + spaces(indent), " public ", capType, " get", titleCase, "() {\n", + unionDiscrim.check, + spaces(indent), " return new ", capType, "(\n", + spaces(indent), " _getPointerField(", factoryArg, ", ", offset, ").getAsCap());\n", + spaces(indent), " }\n", + + spaces(indent), " public void set", titleCase, "(", capType, " value) {\n", + unionDiscrim.set, + spaces(indent), " _initPointerField(", factoryArg, ", ", offset, ", 0).setAsCap(value);\n", + spaces(indent), " }\n", + "\n") + }; } else if (kind == FieldKind::ANY_POINTER) { auto factoryArg = makeFactoryArg(field.getType()); @@ -1535,7 +1572,130 @@ private: kj::strTree() }; } + // ----------------------------------------------------------------- + struct InterfaceText { + kj::StringTree outerTypeDef; + kj::StringTree clientServerDefs; + }; + + InterfaceText makeInterfaceText(kj::StringPtr scope, kj::StringPtr name, InterfaceSchema schema, + kj::Array nestedTypeDecls, int indent) { + auto fullName = kj::str(scope, name); + auto methods = KJ_MAP(m, schema.getMethods()) { + return makeMethodText(fullName, m); + }; + + auto proto = schema.getProto(); + auto hexId = kj::hex(proto.getId()); + + auto typeName = javaFullName(schema); + + return InterfaceText { + kj::strTree( + spaces(indent), "public static class ", name, "{\n", + spaces(indent), "\n", + spaces(indent), " public static class Client extends org.capnproto.Capability.Client {\n", + spaces(indent), " public Client() {}\n", + spaces(indent), " public Client(org.capnproto.ClientHook hook) { super(hook); }\n", + spaces(indent), " public Client(org.capnproto.Capability.Client cap) { super(cap.getHook()); }\n", + spaces(indent), " public Client(Server server) { super(server); }\n", + spaces(indent), " public Client(java.util.concurrent.CompletableFuture promise) {\n", + spaces(indent), " super(promise);\n", + spaces(indent), " }\n", + spaces(indent), " }\n", + spaces(indent), " public static abstract class Server extends org.capnproto.Capability.Server {\n", + spaces(indent), " protected org.capnproto.DispatchCallResult dispatchCall(\n", + spaces(indent), " long interfaceId, short methodId,\n", + spaces(indent), " org.capnproto.CallContext context) {\n", + spaces(indent), " if (interfaceId == 0x", hexId, "L) {\n", + spaces(indent), " return dispatchCallInternal(methodId, context);\n", + spaces(indent), " }\n", + spaces(indent), " return org.capnproto.Capability.Server.result(\n", + spaces(indent), " org.capnproto.Capability.Server.internalUnimplemented(\"", name, "\", interfaceId));\n", + spaces(indent), " }\n", + spaces(indent), "\n", + spaces(indent), " protected org.capnproto.DispatchCallResult dispatchCallInternal(short methodId, org.capnproto.CallContext context) {\n", + spaces(indent), " switch (methodId) {\n", + KJ_MAP(m, methods) { return kj::mv(m.dispatchCase); }, + spaces(indent), " default:\n", + spaces(indent), " return org.capnproto.Capability.Server.result(\n", + spaces(indent), " org.capnproto.Capability.Server.internalUnimplemented(\"", name, "\", 0x", hexId, "L, methodId));\n", + spaces(indent), " }\n", + spaces(indent), " }\n\n", + KJ_MAP(m, methods) { return kj::mv(m.sourceDefs); }, + spaces(indent), " }\n", + spaces(indent), "\n", + KJ_MAP(n, nestedTypeDecls) { return kj::mv(n); }, + spaces(indent), "\n", + spaces(indent), "}\n", + spaces(indent), "\n") + }; + } + + // ----------------------------------------------------------------- + + struct MethodText { + kj::StringTree sourceDefs; + kj::StringTree dispatchCase; + }; + + MethodText makeMethodText(kj::StringPtr interfaceName, InterfaceSchema::Method method) { + auto proto = method.getProto(); + auto name = proto.getName(); + auto titleCase = toTitleCase(name); + auto paramSchema = method.getParamType(); + auto resultSchema = method.getResultType(); + auto identifierName = safeIdentifier(name); + + auto paramProto = paramSchema.getProto(); + auto resultProto = resultSchema.getProto(); + + bool isStreaming = method.isStreaming(); + + auto interfaceTypeName = javaFullName(method.getContainingInterface()); + + kj::String paramType = (paramProto.getScopeId() == 0) + ? kj::str(interfaceTypeName, "Params") + : kj::str(javaFullName(paramSchema, method)); + + kj::String resultType = (resultProto.getScopeId() == 0) + ? kj::str(interfaceTypeName, "Results") + : kj::str(javaFullName(resultSchema, method)); + + auto interfaceProto = method.getContainingInterface().getProto(); + uint64_t interfaceId = interfaceProto.getId(); + auto interfaceIdHex = kj::hex(interfaceId); + uint16_t methodId = method.getIndex(); + + auto requestMethodImpl = kj::strTree( + isStreaming ? kj::strTree("org.capnproto.StreamingRequest<", paramType, ">") + : kj::strTree("org.capnproto.Request<", paramType, ", ", resultType, ">"), + name, "Request() {\n", + isStreaming + ? kj::strTree(" return newStreamingCall<", paramType, ">(\n") + : kj::strTree(" return newCall<", paramType, ", ", resultType, ">(\n"), + " 0x", interfaceIdHex, "L, ", methodId, ");\n" + "}\n"); + + return MethodText { + + kj::strTree( + " protected java.util.concurrent.CompletableFuture ", identifierName, "(org.capnproto.CallContext<", titleCase, "Params.Reader, ", titleCase, "Results.Builder> context) {\n" + " return org.capnproto.Capability.Server.internalUnimplemented(\n" + " \"", interfaceProto.getDisplayName(), "\", \"", name, "\",\n" + " 0x", interfaceIdHex, "L, (short)", methodId, ");\n" + " }\n\n"), + + kj::strTree( + " case ", methodId, ":\n", + " return org.capnproto.Capability.Server.", + isStreaming ? "streamResult" : "result", "(\n", + " this.", identifierName, "(org.capnproto.Capability.Server.typedContext(\n" + " ", paramType, ".factory,\n", + " ", resultType, ".factory, context)));\n") + }; + } // ----------------------------------------------------------------- @@ -1668,7 +1828,24 @@ private: } } } else if (proto.isInterface()) { - KJ_FAIL_REQUIRE("interfaces not implemented"); + for (auto method: proto.getInterface().getMethods()) { + { + Schema params = schemaLoader.getUnbound(method.getParamStructType()); + auto paramsProto = schemaLoader.getUnbound(method.getParamStructType()).getProto(); + if (paramsProto.getScopeId() == 0) { + nestedTexts.add(makeNodeText(subScope, + toTitleCase(kj::str(method.getName(), "Params")), params, indent + 1)); + } + } + { + Schema results = schemaLoader.getUnbound(method.getResultStructType()); + auto resultsProto = schemaLoader.getUnbound(method.getResultStructType()).getProto(); + if (resultsProto.getScopeId() == 0) { + nestedTexts.add(makeNodeText(subScope, + toTitleCase(kj::str(method.getName(), "Results")), results, indent + 1)); + } + } + } } // Convert the encoded schema to a literal byte array. @@ -1834,7 +2011,16 @@ private: case schema::Node::INTERFACE: { hasInterfaces = true; - KJ_FAIL_REQUIRE("unimplemented"); + auto interfaceText = + makeInterfaceText(scope, name, schema.asInterface(), kj::mv(nestedTypeDecls), indent); + + return NodeTextNoSchema { + kj::mv(interfaceText.outerTypeDef), + kj::mv(interfaceText.clientServerDefs), + kj::strTree(), + kj::strTree(), + kj::strTree() + }; } case schema::Node::CONST: {