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