csharp_generator.cc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. /*
  2. *
  3. * Copyright 2015 gRPC authors.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. #include <cctype>
  19. #include <map>
  20. #include <sstream>
  21. #include <vector>
  22. #include "src/compiler/config.h"
  23. #include "src/compiler/csharp_generator.h"
  24. #include "src/compiler/csharp_generator_helpers.h"
  25. using google::protobuf::compiler::csharp::GetClassName;
  26. using google::protobuf::compiler::csharp::GetFileNamespace;
  27. using google::protobuf::compiler::csharp::GetReflectionClassName;
  28. using grpc::protobuf::Descriptor;
  29. using grpc::protobuf::FileDescriptor;
  30. using grpc::protobuf::MethodDescriptor;
  31. using grpc::protobuf::ServiceDescriptor;
  32. using grpc::protobuf::io::Printer;
  33. using grpc::protobuf::io::StringOutputStream;
  34. using grpc_generator::GetMethodType;
  35. using grpc_generator::METHODTYPE_BIDI_STREAMING;
  36. using grpc_generator::METHODTYPE_CLIENT_STREAMING;
  37. using grpc_generator::METHODTYPE_NO_STREAMING;
  38. using grpc_generator::METHODTYPE_SERVER_STREAMING;
  39. using grpc_generator::MethodType;
  40. using grpc_generator::StringReplace;
  41. using std::map;
  42. using std::vector;
  43. namespace grpc_csharp_generator {
  44. namespace {
  45. // This function is a massaged version of
  46. // https://github.com/google/protobuf/blob/master/src/google/protobuf/compiler/csharp/csharp_doc_comment.cc
  47. // Currently, we cannot easily reuse the functionality as
  48. // google/protobuf/compiler/csharp/csharp_doc_comment.h is not a public header.
  49. // TODO(jtattermusch): reuse the functionality from google/protobuf.
  50. bool GenerateDocCommentBodyImpl(grpc::protobuf::io::Printer* printer,
  51. grpc::protobuf::SourceLocation location) {
  52. grpc::string comments = location.leading_comments.empty()
  53. ? location.trailing_comments
  54. : location.leading_comments;
  55. if (comments.empty()) {
  56. return false;
  57. }
  58. // XML escaping... no need for apostrophes etc as the whole text is going to
  59. // be a child
  60. // node of a summary element, not part of an attribute.
  61. comments = grpc_generator::StringReplace(comments, "&", "&amp;", true);
  62. comments = grpc_generator::StringReplace(comments, "<", "&lt;", true);
  63. std::vector<grpc::string> lines;
  64. grpc_generator::Split(comments, '\n', &lines);
  65. // TODO: We really should work out which part to put in the summary and which
  66. // to put in the remarks...
  67. // but that needs to be part of a bigger effort to understand the markdown
  68. // better anyway.
  69. printer->Print("/// <summary>\n");
  70. bool last_was_empty = false;
  71. // We squash multiple blank lines down to one, and remove any trailing blank
  72. // lines. We need
  73. // to preserve the blank lines themselves, as this is relevant in the
  74. // markdown.
  75. // Note that we can't remove leading or trailing whitespace as *that's*
  76. // relevant in markdown too.
  77. // (We don't skip "just whitespace" lines, either.)
  78. for (std::vector<grpc::string>::iterator it = lines.begin();
  79. it != lines.end(); ++it) {
  80. grpc::string line = *it;
  81. if (line.empty()) {
  82. last_was_empty = true;
  83. } else {
  84. if (last_was_empty) {
  85. printer->Print("///\n");
  86. }
  87. last_was_empty = false;
  88. printer->Print("///$line$\n", "line", *it);
  89. }
  90. }
  91. printer->Print("/// </summary>\n");
  92. return true;
  93. }
  94. template <typename DescriptorType>
  95. bool GenerateDocCommentBody(grpc::protobuf::io::Printer* printer,
  96. const DescriptorType* descriptor) {
  97. grpc::protobuf::SourceLocation location;
  98. if (!descriptor->GetSourceLocation(&location)) {
  99. return false;
  100. }
  101. return GenerateDocCommentBodyImpl(printer, location);
  102. }
  103. void GenerateDocCommentServerMethod(grpc::protobuf::io::Printer* printer,
  104. const MethodDescriptor* method) {
  105. if (GenerateDocCommentBody(printer, method)) {
  106. if (method->client_streaming()) {
  107. printer->Print(
  108. "/// <param name=\"requestStream\">Used for reading requests from "
  109. "the client.</param>\n");
  110. } else {
  111. printer->Print(
  112. "/// <param name=\"request\">The request received from the "
  113. "client.</param>\n");
  114. }
  115. if (method->server_streaming()) {
  116. printer->Print(
  117. "/// <param name=\"responseStream\">Used for sending responses back "
  118. "to the client.</param>\n");
  119. }
  120. printer->Print(
  121. "/// <param name=\"context\">The context of the server-side call "
  122. "handler being invoked.</param>\n");
  123. if (method->server_streaming()) {
  124. printer->Print(
  125. "/// <returns>A task indicating completion of the "
  126. "handler.</returns>\n");
  127. } else {
  128. printer->Print(
  129. "/// <returns>The response to send back to the client (wrapped by a "
  130. "task).</returns>\n");
  131. }
  132. }
  133. }
  134. void GenerateDocCommentClientMethod(grpc::protobuf::io::Printer* printer,
  135. const MethodDescriptor* method,
  136. bool is_sync, bool use_call_options) {
  137. if (GenerateDocCommentBody(printer, method)) {
  138. if (!method->client_streaming()) {
  139. printer->Print(
  140. "/// <param name=\"request\">The request to send to the "
  141. "server.</param>\n");
  142. }
  143. if (!use_call_options) {
  144. printer->Print(
  145. "/// <param name=\"headers\">The initial metadata to send with the "
  146. "call. This parameter is optional.</param>\n");
  147. printer->Print(
  148. "/// <param name=\"deadline\">An optional deadline for the call. The "
  149. "call will be cancelled if deadline is hit.</param>\n");
  150. printer->Print(
  151. "/// <param name=\"cancellationToken\">An optional token for "
  152. "canceling the call.</param>\n");
  153. } else {
  154. printer->Print(
  155. "/// <param name=\"options\">The options for the call.</param>\n");
  156. }
  157. if (is_sync) {
  158. printer->Print(
  159. "/// <returns>The response received from the server.</returns>\n");
  160. } else {
  161. printer->Print("/// <returns>The call object.</returns>\n");
  162. }
  163. }
  164. }
  165. std::string GetServiceClassName(const ServiceDescriptor* service) {
  166. return service->name();
  167. }
  168. std::string GetClientClassName(const ServiceDescriptor* service) {
  169. return service->name() + "Client";
  170. }
  171. std::string GetServerClassName(const ServiceDescriptor* service) {
  172. return service->name() + "Base";
  173. }
  174. std::string GetCSharpMethodType(MethodType method_type) {
  175. switch (method_type) {
  176. case METHODTYPE_NO_STREAMING:
  177. return "grpc::MethodType.Unary";
  178. case METHODTYPE_CLIENT_STREAMING:
  179. return "grpc::MethodType.ClientStreaming";
  180. case METHODTYPE_SERVER_STREAMING:
  181. return "grpc::MethodType.ServerStreaming";
  182. case METHODTYPE_BIDI_STREAMING:
  183. return "grpc::MethodType.DuplexStreaming";
  184. }
  185. GOOGLE_LOG(FATAL) << "Can't get here.";
  186. return "";
  187. }
  188. std::string GetCSharpServerMethodType(MethodType method_type) {
  189. switch (method_type) {
  190. case METHODTYPE_NO_STREAMING:
  191. return "grpc::UnaryServerMethod";
  192. case METHODTYPE_CLIENT_STREAMING:
  193. return "grpc::ClientStreamingServerMethod";
  194. case METHODTYPE_SERVER_STREAMING:
  195. return "grpc::ServerStreamingServerMethod";
  196. case METHODTYPE_BIDI_STREAMING:
  197. return "grpc::DuplexStreamingServerMethod";
  198. }
  199. GOOGLE_LOG(FATAL) << "Can't get here.";
  200. return "";
  201. }
  202. std::string GetServiceNameFieldName() { return "__ServiceName"; }
  203. std::string GetMarshallerFieldName(const Descriptor* message) {
  204. return "__Marshaller_" +
  205. grpc_generator::StringReplace(message->full_name(), ".", "_", true);
  206. }
  207. std::string GetMethodFieldName(const MethodDescriptor* method) {
  208. return "__Method_" + method->name();
  209. }
  210. std::string GetMethodRequestParamMaybe(const MethodDescriptor* method,
  211. bool invocation_param = false) {
  212. if (method->client_streaming()) {
  213. return "";
  214. }
  215. if (invocation_param) {
  216. return "request, ";
  217. }
  218. return GetClassName(method->input_type()) + " request, ";
  219. }
  220. std::string GetAccessLevel(bool internal_access) {
  221. return internal_access ? "internal" : "public";
  222. }
  223. std::string GetMethodReturnTypeClient(const MethodDescriptor* method) {
  224. switch (GetMethodType(method)) {
  225. case METHODTYPE_NO_STREAMING:
  226. return "grpc::AsyncUnaryCall<" + GetClassName(method->output_type()) +
  227. ">";
  228. case METHODTYPE_CLIENT_STREAMING:
  229. return "grpc::AsyncClientStreamingCall<" +
  230. GetClassName(method->input_type()) + ", " +
  231. GetClassName(method->output_type()) + ">";
  232. case METHODTYPE_SERVER_STREAMING:
  233. return "grpc::AsyncServerStreamingCall<" +
  234. GetClassName(method->output_type()) + ">";
  235. case METHODTYPE_BIDI_STREAMING:
  236. return "grpc::AsyncDuplexStreamingCall<" +
  237. GetClassName(method->input_type()) + ", " +
  238. GetClassName(method->output_type()) + ">";
  239. }
  240. GOOGLE_LOG(FATAL) << "Can't get here.";
  241. return "";
  242. }
  243. std::string GetMethodRequestParamServer(const MethodDescriptor* method) {
  244. switch (GetMethodType(method)) {
  245. case METHODTYPE_NO_STREAMING:
  246. case METHODTYPE_SERVER_STREAMING:
  247. return GetClassName(method->input_type()) + " request";
  248. case METHODTYPE_CLIENT_STREAMING:
  249. case METHODTYPE_BIDI_STREAMING:
  250. return "grpc::IAsyncStreamReader<" + GetClassName(method->input_type()) +
  251. "> requestStream";
  252. }
  253. GOOGLE_LOG(FATAL) << "Can't get here.";
  254. return "";
  255. }
  256. std::string GetMethodReturnTypeServer(const MethodDescriptor* method) {
  257. switch (GetMethodType(method)) {
  258. case METHODTYPE_NO_STREAMING:
  259. case METHODTYPE_CLIENT_STREAMING:
  260. return "global::System.Threading.Tasks.Task<" +
  261. GetClassName(method->output_type()) + ">";
  262. case METHODTYPE_SERVER_STREAMING:
  263. case METHODTYPE_BIDI_STREAMING:
  264. return "global::System.Threading.Tasks.Task";
  265. }
  266. GOOGLE_LOG(FATAL) << "Can't get here.";
  267. return "";
  268. }
  269. std::string GetMethodResponseStreamMaybe(const MethodDescriptor* method) {
  270. switch (GetMethodType(method)) {
  271. case METHODTYPE_NO_STREAMING:
  272. case METHODTYPE_CLIENT_STREAMING:
  273. return "";
  274. case METHODTYPE_SERVER_STREAMING:
  275. case METHODTYPE_BIDI_STREAMING:
  276. return ", grpc::IServerStreamWriter<" +
  277. GetClassName(method->output_type()) + "> responseStream";
  278. }
  279. GOOGLE_LOG(FATAL) << "Can't get here.";
  280. return "";
  281. }
  282. // Gets vector of all messages used as input or output types.
  283. std::vector<const Descriptor*> GetUsedMessages(
  284. const ServiceDescriptor* service) {
  285. std::set<const Descriptor*> descriptor_set;
  286. std::vector<const Descriptor*>
  287. result; // vector is to maintain stable ordering
  288. for (int i = 0; i < service->method_count(); i++) {
  289. const MethodDescriptor* method = service->method(i);
  290. if (descriptor_set.find(method->input_type()) == descriptor_set.end()) {
  291. descriptor_set.insert(method->input_type());
  292. result.push_back(method->input_type());
  293. }
  294. if (descriptor_set.find(method->output_type()) == descriptor_set.end()) {
  295. descriptor_set.insert(method->output_type());
  296. result.push_back(method->output_type());
  297. }
  298. }
  299. return result;
  300. }
  301. void GenerateMarshallerFields(Printer* out, const ServiceDescriptor* service) {
  302. std::vector<const Descriptor*> used_messages = GetUsedMessages(service);
  303. for (size_t i = 0; i < used_messages.size(); i++) {
  304. const Descriptor* message = used_messages[i];
  305. out->Print(
  306. "static readonly grpc::Marshaller<$type$> $fieldname$ = "
  307. "grpc::Marshallers.Create((arg) => "
  308. "global::Google.Protobuf.MessageExtensions.ToByteArray(arg), "
  309. "$type$.Parser.ParseFrom);\n",
  310. "fieldname", GetMarshallerFieldName(message), "type",
  311. GetClassName(message));
  312. }
  313. out->Print("\n");
  314. }
  315. void GenerateStaticMethodField(Printer* out, const MethodDescriptor* method) {
  316. out->Print(
  317. "static readonly grpc::Method<$request$, $response$> $fieldname$ = new "
  318. "grpc::Method<$request$, $response$>(\n",
  319. "fieldname", GetMethodFieldName(method), "request",
  320. GetClassName(method->input_type()), "response",
  321. GetClassName(method->output_type()));
  322. out->Indent();
  323. out->Indent();
  324. out->Print("$methodtype$,\n", "methodtype",
  325. GetCSharpMethodType(GetMethodType(method)));
  326. out->Print("$servicenamefield$,\n", "servicenamefield",
  327. GetServiceNameFieldName());
  328. out->Print("\"$methodname$\",\n", "methodname", method->name());
  329. out->Print("$requestmarshaller$,\n", "requestmarshaller",
  330. GetMarshallerFieldName(method->input_type()));
  331. out->Print("$responsemarshaller$);\n", "responsemarshaller",
  332. GetMarshallerFieldName(method->output_type()));
  333. out->Print("\n");
  334. out->Outdent();
  335. out->Outdent();
  336. }
  337. void GenerateServiceDescriptorProperty(Printer* out,
  338. const ServiceDescriptor* service) {
  339. std::ostringstream index;
  340. index << service->index();
  341. out->Print("/// <summary>Service descriptor</summary>\n");
  342. out->Print(
  343. "public static global::Google.Protobuf.Reflection.ServiceDescriptor "
  344. "Descriptor\n");
  345. out->Print("{\n");
  346. out->Print(" get { return $umbrella$.Descriptor.Services[$index$]; }\n",
  347. "umbrella", GetReflectionClassName(service->file()), "index",
  348. index.str());
  349. out->Print("}\n");
  350. out->Print("\n");
  351. }
  352. void GenerateServerClass(Printer* out, const ServiceDescriptor* service) {
  353. out->Print(
  354. "/// <summary>Base class for server-side implementations of "
  355. "$servicename$</summary>\n",
  356. "servicename", GetServiceClassName(service));
  357. out->Print("public abstract partial class $name$\n", "name",
  358. GetServerClassName(service));
  359. out->Print("{\n");
  360. out->Indent();
  361. for (int i = 0; i < service->method_count(); i++) {
  362. const MethodDescriptor* method = service->method(i);
  363. GenerateDocCommentServerMethod(out, method);
  364. out->Print(
  365. "public virtual $returntype$ "
  366. "$methodname$($request$$response_stream_maybe$, "
  367. "grpc::ServerCallContext context)\n",
  368. "methodname", method->name(), "returntype",
  369. GetMethodReturnTypeServer(method), "request",
  370. GetMethodRequestParamServer(method), "response_stream_maybe",
  371. GetMethodResponseStreamMaybe(method));
  372. out->Print("{\n");
  373. out->Indent();
  374. out->Print(
  375. "throw new grpc::RpcException("
  376. "new grpc::Status(grpc::StatusCode.Unimplemented, \"\"));\n");
  377. out->Outdent();
  378. out->Print("}\n\n");
  379. }
  380. out->Outdent();
  381. out->Print("}\n");
  382. out->Print("\n");
  383. }
  384. void GenerateClientStub(Printer* out, const ServiceDescriptor* service) {
  385. out->Print("/// <summary>Client for $servicename$</summary>\n", "servicename",
  386. GetServiceClassName(service));
  387. out->Print("public partial class $name$ : grpc::ClientBase<$name$>\n", "name",
  388. GetClientClassName(service));
  389. out->Print("{\n");
  390. out->Indent();
  391. // constructors
  392. out->Print(
  393. "/// <summary>Creates a new client for $servicename$</summary>\n"
  394. "/// <param name=\"channel\">The channel to use to make remote "
  395. "calls.</param>\n",
  396. "servicename", GetServiceClassName(service));
  397. out->Print("public $name$(grpc::Channel channel) : base(channel)\n", "name",
  398. GetClientClassName(service));
  399. out->Print("{\n");
  400. out->Print("}\n");
  401. out->Print(
  402. "/// <summary>Creates a new client for $servicename$ that uses a custom "
  403. "<c>CallInvoker</c>.</summary>\n"
  404. "/// <param name=\"callInvoker\">The callInvoker to use to make remote "
  405. "calls.</param>\n",
  406. "servicename", GetServiceClassName(service));
  407. out->Print(
  408. "public $name$(grpc::CallInvoker callInvoker) : base(callInvoker)\n",
  409. "name", GetClientClassName(service));
  410. out->Print("{\n");
  411. out->Print("}\n");
  412. out->Print(
  413. "/// <summary>Protected parameterless constructor to allow creation"
  414. " of test doubles.</summary>\n");
  415. out->Print("protected $name$() : base()\n", "name",
  416. GetClientClassName(service));
  417. out->Print("{\n");
  418. out->Print("}\n");
  419. out->Print(
  420. "/// <summary>Protected constructor to allow creation of configured "
  421. "clients.</summary>\n"
  422. "/// <param name=\"configuration\">The client configuration.</param>\n");
  423. out->Print(
  424. "protected $name$(ClientBaseConfiguration configuration)"
  425. " : base(configuration)\n",
  426. "name", GetClientClassName(service));
  427. out->Print("{\n");
  428. out->Print("}\n\n");
  429. for (int i = 0; i < service->method_count(); i++) {
  430. const MethodDescriptor* method = service->method(i);
  431. MethodType method_type = GetMethodType(method);
  432. if (method_type == METHODTYPE_NO_STREAMING) {
  433. // unary calls have an extra synchronous stub method
  434. GenerateDocCommentClientMethod(out, method, true, false);
  435. out->Print(
  436. "public virtual $response$ $methodname$($request$ request, "
  437. "grpc::Metadata "
  438. "headers = null, global::System.DateTime? deadline = null, "
  439. "global::System.Threading.CancellationToken "
  440. "cancellationToken = "
  441. "default(global::System.Threading.CancellationToken))\n",
  442. "methodname", method->name(), "request",
  443. GetClassName(method->input_type()), "response",
  444. GetClassName(method->output_type()));
  445. out->Print("{\n");
  446. out->Indent();
  447. out->Print(
  448. "return $methodname$(request, new grpc::CallOptions(headers, "
  449. "deadline, "
  450. "cancellationToken));\n",
  451. "methodname", method->name());
  452. out->Outdent();
  453. out->Print("}\n");
  454. // overload taking CallOptions as a param
  455. GenerateDocCommentClientMethod(out, method, true, true);
  456. out->Print(
  457. "public virtual $response$ $methodname$($request$ request, "
  458. "grpc::CallOptions options)\n",
  459. "methodname", method->name(), "request",
  460. GetClassName(method->input_type()), "response",
  461. GetClassName(method->output_type()));
  462. out->Print("{\n");
  463. out->Indent();
  464. out->Print(
  465. "return CallInvoker.BlockingUnaryCall($methodfield$, null, options, "
  466. "request);\n",
  467. "methodfield", GetMethodFieldName(method));
  468. out->Outdent();
  469. out->Print("}\n");
  470. }
  471. std::string method_name = method->name();
  472. if (method_type == METHODTYPE_NO_STREAMING) {
  473. method_name += "Async"; // prevent name clash with synchronous method.
  474. }
  475. GenerateDocCommentClientMethod(out, method, false, false);
  476. out->Print(
  477. "public virtual $returntype$ "
  478. "$methodname$($request_maybe$grpc::Metadata "
  479. "headers = null, global::System.DateTime? deadline = null, "
  480. "global::System.Threading.CancellationToken "
  481. "cancellationToken = "
  482. "default(global::System.Threading.CancellationToken))\n",
  483. "methodname", method_name, "request_maybe",
  484. GetMethodRequestParamMaybe(method), "returntype",
  485. GetMethodReturnTypeClient(method));
  486. out->Print("{\n");
  487. out->Indent();
  488. out->Print(
  489. "return $methodname$($request_maybe$new grpc::CallOptions(headers, "
  490. "deadline, "
  491. "cancellationToken));\n",
  492. "methodname", method_name, "request_maybe",
  493. GetMethodRequestParamMaybe(method, true));
  494. out->Outdent();
  495. out->Print("}\n");
  496. // overload taking CallOptions as a param
  497. GenerateDocCommentClientMethod(out, method, false, true);
  498. out->Print(
  499. "public virtual $returntype$ "
  500. "$methodname$($request_maybe$grpc::CallOptions "
  501. "options)\n",
  502. "methodname", method_name, "request_maybe",
  503. GetMethodRequestParamMaybe(method), "returntype",
  504. GetMethodReturnTypeClient(method));
  505. out->Print("{\n");
  506. out->Indent();
  507. switch (GetMethodType(method)) {
  508. case METHODTYPE_NO_STREAMING:
  509. out->Print(
  510. "return CallInvoker.AsyncUnaryCall($methodfield$, null, options, "
  511. "request);\n",
  512. "methodfield", GetMethodFieldName(method));
  513. break;
  514. case METHODTYPE_CLIENT_STREAMING:
  515. out->Print(
  516. "return CallInvoker.AsyncClientStreamingCall($methodfield$, null, "
  517. "options);\n",
  518. "methodfield", GetMethodFieldName(method));
  519. break;
  520. case METHODTYPE_SERVER_STREAMING:
  521. out->Print(
  522. "return CallInvoker.AsyncServerStreamingCall($methodfield$, null, "
  523. "options, request);\n",
  524. "methodfield", GetMethodFieldName(method));
  525. break;
  526. case METHODTYPE_BIDI_STREAMING:
  527. out->Print(
  528. "return CallInvoker.AsyncDuplexStreamingCall($methodfield$, null, "
  529. "options);\n",
  530. "methodfield", GetMethodFieldName(method));
  531. break;
  532. default:
  533. GOOGLE_LOG(FATAL) << "Can't get here.";
  534. }
  535. out->Outdent();
  536. out->Print("}\n");
  537. }
  538. // override NewInstance method
  539. out->Print(
  540. "/// <summary>Creates a new instance of client from given "
  541. "<c>ClientBaseConfiguration</c>.</summary>\n");
  542. out->Print(
  543. "protected override $name$ NewInstance(ClientBaseConfiguration "
  544. "configuration)\n",
  545. "name", GetClientClassName(service));
  546. out->Print("{\n");
  547. out->Indent();
  548. out->Print("return new $name$(configuration);\n", "name",
  549. GetClientClassName(service));
  550. out->Outdent();
  551. out->Print("}\n");
  552. out->Outdent();
  553. out->Print("}\n");
  554. out->Print("\n");
  555. }
  556. void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor* service) {
  557. out->Print(
  558. "/// <summary>Creates service definition that can be registered with a "
  559. "server</summary>\n");
  560. out->Print(
  561. "/// <param name=\"serviceImpl\">An object implementing the server-side"
  562. " handling logic.</param>\n");
  563. out->Print(
  564. "public static grpc::ServerServiceDefinition BindService($implclass$ "
  565. "serviceImpl)\n",
  566. "implclass", GetServerClassName(service));
  567. out->Print("{\n");
  568. out->Indent();
  569. out->Print("return grpc::ServerServiceDefinition.CreateBuilder()");
  570. out->Indent();
  571. out->Indent();
  572. for (int i = 0; i < service->method_count(); i++) {
  573. const MethodDescriptor* method = service->method(i);
  574. out->Print("\n.AddMethod($methodfield$, serviceImpl.$methodname$)",
  575. "methodfield", GetMethodFieldName(method), "methodname",
  576. method->name());
  577. }
  578. out->Print(".Build();\n");
  579. out->Outdent();
  580. out->Outdent();
  581. out->Outdent();
  582. out->Print("}\n");
  583. out->Print("\n");
  584. }
  585. void GenerateBindServiceWithBinderMethod(Printer* out,
  586. const ServiceDescriptor* service) {
  587. out->Print(
  588. "/// <summary>Register service method with a service "
  589. "binder with or without implementation. Useful when customizing the "
  590. "service binding logic.\n"
  591. "/// Note: this method is part of an experimental API that can change or "
  592. "be "
  593. "removed without any prior notice.</summary>\n");
  594. out->Print(
  595. "/// <param name=\"serviceBinder\">Service methods will be bound by "
  596. "calling <c>AddMethod</c> on this object."
  597. "</param>\n");
  598. out->Print(
  599. "/// <param name=\"serviceImpl\">An object implementing the server-side"
  600. " handling logic.</param>\n");
  601. out->Print(
  602. "public static void BindService(grpc::ServiceBinderBase serviceBinder, "
  603. "$implclass$ "
  604. "serviceImpl)\n",
  605. "implclass", GetServerClassName(service));
  606. out->Print("{\n");
  607. out->Indent();
  608. for (int i = 0; i < service->method_count(); i++) {
  609. const MethodDescriptor* method = service->method(i);
  610. out->Print(
  611. "serviceBinder.AddMethod($methodfield$, serviceImpl == null ? null : "
  612. "new $servermethodtype$<$inputtype$, $outputtype$>("
  613. "serviceImpl.$methodname$));\n",
  614. "methodfield", GetMethodFieldName(method), "servermethodtype",
  615. GetCSharpServerMethodType(GetMethodType(method)), "inputtype",
  616. GetClassName(method->input_type()), "outputtype",
  617. GetClassName(method->output_type()), "methodname", method->name());
  618. }
  619. out->Outdent();
  620. out->Print("}\n");
  621. out->Print("\n");
  622. }
  623. void GenerateService(Printer* out, const ServiceDescriptor* service,
  624. bool generate_client, bool generate_server,
  625. bool internal_access) {
  626. GenerateDocCommentBody(out, service);
  627. out->Print("$access_level$ static partial class $classname$\n",
  628. "access_level", GetAccessLevel(internal_access), "classname",
  629. GetServiceClassName(service));
  630. out->Print("{\n");
  631. out->Indent();
  632. out->Print("static readonly string $servicenamefield$ = \"$servicename$\";\n",
  633. "servicenamefield", GetServiceNameFieldName(), "servicename",
  634. service->full_name());
  635. out->Print("\n");
  636. GenerateMarshallerFields(out, service);
  637. for (int i = 0; i < service->method_count(); i++) {
  638. GenerateStaticMethodField(out, service->method(i));
  639. }
  640. GenerateServiceDescriptorProperty(out, service);
  641. if (generate_server) {
  642. GenerateServerClass(out, service);
  643. }
  644. if (generate_client) {
  645. GenerateClientStub(out, service);
  646. }
  647. if (generate_server) {
  648. GenerateBindServiceMethod(out, service);
  649. GenerateBindServiceWithBinderMethod(out, service);
  650. }
  651. out->Outdent();
  652. out->Print("}\n");
  653. }
  654. } // anonymous namespace
  655. grpc::string GetServices(const FileDescriptor* file, bool generate_client,
  656. bool generate_server, bool internal_access) {
  657. grpc::string output;
  658. {
  659. // Scope the output stream so it closes and finalizes output to the string.
  660. StringOutputStream output_stream(&output);
  661. Printer out(&output_stream, '$');
  662. // Don't write out any output if there no services, to avoid empty service
  663. // files being generated for proto files that don't declare any.
  664. if (file->service_count() == 0) {
  665. return output;
  666. }
  667. // Write out a file header.
  668. out.Print("// <auto-generated>\n");
  669. out.Print(
  670. "// Generated by the protocol buffer compiler. DO NOT EDIT!\n");
  671. out.Print("// source: $filename$\n", "filename", file->name());
  672. out.Print("// </auto-generated>\n");
  673. // use C++ style as there are no file-level XML comments in .NET
  674. grpc::string leading_comments = GetCsharpComments(file, true);
  675. if (!leading_comments.empty()) {
  676. out.Print("// Original file comments:\n");
  677. out.PrintRaw(leading_comments.c_str());
  678. }
  679. out.Print("#pragma warning disable 0414, 1591\n");
  680. out.Print("#region Designer generated code\n");
  681. out.Print("\n");
  682. out.Print("using grpc = global::Grpc.Core;\n");
  683. out.Print("\n");
  684. grpc::string file_namespace = GetFileNamespace(file);
  685. if (file_namespace != "") {
  686. out.Print("namespace $namespace$ {\n", "namespace", file_namespace);
  687. out.Indent();
  688. }
  689. for (int i = 0; i < file->service_count(); i++) {
  690. GenerateService(&out, file->service(i), generate_client, generate_server,
  691. internal_access);
  692. }
  693. if (file_namespace != "") {
  694. out.Outdent();
  695. out.Print("}\n");
  696. }
  697. out.Print("#endregion\n");
  698. }
  699. return output;
  700. }
  701. } // namespace grpc_csharp_generator