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