Imported Upstream version 1.18.0
[platform/upstream/grpc.git] / src / compiler / csharp_generator.cc
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_BIDI_STREAMING;
39 using grpc_generator::METHODTYPE_CLIENT_STREAMING;
40 using grpc_generator::METHODTYPE_NO_STREAMING;
41 using grpc_generator::METHODTYPE_SERVER_STREAMING;
42 using grpc_generator::MethodType;
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.
55 bool GenerateDocCommentBodyImpl(grpc::protobuf::io::Printer* printer,
56                                 grpc::protobuf::SourceLocation location) {
57   grpc::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, "&", "&amp;", true);
67   comments = grpc_generator::StringReplace(comments, "<", "&lt;", true);
68
69   std::vector<grpc::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<grpc::string>::iterator it = lines.begin();
85        it != lines.end(); ++it) {
86     grpc::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>
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
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
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
175 std::string GetServiceClassName(const ServiceDescriptor* service) {
176   return service->name();
177 }
178
179 std::string GetClientClassName(const ServiceDescriptor* service) {
180   return service->name() + "Client";
181 }
182
183 std::string GetServerClassName(const ServiceDescriptor* service) {
184   return service->name() + "Base";
185 }
186
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
202 std::string GetServiceNameFieldName() { return "__ServiceName"; }
203
204 std::string GetMarshallerFieldName(const Descriptor* message) {
205   return "__Marshaller_" +
206          grpc_generator::StringReplace(message->full_name(), ".", "_", true);
207 }
208
209 std::string GetMethodFieldName(const MethodDescriptor* method) {
210   return "__Method_" + method->name();
211 }
212
213 std::string GetMethodRequestParamMaybe(const MethodDescriptor* method,
214                                        bool invocation_param = false) {
215   if (method->client_streaming()) {
216     return "";
217   }
218   if (invocation_param) {
219     return "request, ";
220   }
221   return GetClassName(method->input_type()) + " request, ";
222 }
223
224 std::string GetAccessLevel(bool internal_access) {
225   return internal_access ? "internal" : "public";
226 }
227
228 std::string GetMethodReturnTypeClient(const MethodDescriptor* method) {
229   switch (GetMethodType(method)) {
230     case METHODTYPE_NO_STREAMING:
231       return "grpc::AsyncUnaryCall<" + GetClassName(method->output_type()) +
232              ">";
233     case METHODTYPE_CLIENT_STREAMING:
234       return "grpc::AsyncClientStreamingCall<" +
235              GetClassName(method->input_type()) + ", " +
236              GetClassName(method->output_type()) + ">";
237     case METHODTYPE_SERVER_STREAMING:
238       return "grpc::AsyncServerStreamingCall<" +
239              GetClassName(method->output_type()) + ">";
240     case METHODTYPE_BIDI_STREAMING:
241       return "grpc::AsyncDuplexStreamingCall<" +
242              GetClassName(method->input_type()) + ", " +
243              GetClassName(method->output_type()) + ">";
244   }
245   GOOGLE_LOG(FATAL) << "Can't get here.";
246   return "";
247 }
248
249 std::string GetMethodRequestParamServer(const MethodDescriptor* method) {
250   switch (GetMethodType(method)) {
251     case METHODTYPE_NO_STREAMING:
252     case METHODTYPE_SERVER_STREAMING:
253       return GetClassName(method->input_type()) + " request";
254     case METHODTYPE_CLIENT_STREAMING:
255     case METHODTYPE_BIDI_STREAMING:
256       return "grpc::IAsyncStreamReader<" + GetClassName(method->input_type()) +
257              "> requestStream";
258   }
259   GOOGLE_LOG(FATAL) << "Can't get here.";
260   return "";
261 }
262
263 std::string GetMethodReturnTypeServer(const MethodDescriptor* method) {
264   switch (GetMethodType(method)) {
265     case METHODTYPE_NO_STREAMING:
266     case METHODTYPE_CLIENT_STREAMING:
267       return "global::System.Threading.Tasks.Task<" +
268              GetClassName(method->output_type()) + ">";
269     case METHODTYPE_SERVER_STREAMING:
270     case METHODTYPE_BIDI_STREAMING:
271       return "global::System.Threading.Tasks.Task";
272   }
273   GOOGLE_LOG(FATAL) << "Can't get here.";
274   return "";
275 }
276
277 std::string GetMethodResponseStreamMaybe(const MethodDescriptor* method) {
278   switch (GetMethodType(method)) {
279     case METHODTYPE_NO_STREAMING:
280     case METHODTYPE_CLIENT_STREAMING:
281       return "";
282     case METHODTYPE_SERVER_STREAMING:
283     case METHODTYPE_BIDI_STREAMING:
284       return ", grpc::IServerStreamWriter<" +
285              GetClassName(method->output_type()) + "> responseStream";
286   }
287   GOOGLE_LOG(FATAL) << "Can't get here.";
288   return "";
289 }
290
291 // Gets vector of all messages used as input or output types.
292 std::vector<const Descriptor*> GetUsedMessages(
293     const ServiceDescriptor* service) {
294   std::set<const Descriptor*> descriptor_set;
295   std::vector<const Descriptor*>
296       result;  // vector is to maintain stable ordering
297   for (int i = 0; i < service->method_count(); i++) {
298     const MethodDescriptor* method = service->method(i);
299     if (descriptor_set.find(method->input_type()) == descriptor_set.end()) {
300       descriptor_set.insert(method->input_type());
301       result.push_back(method->input_type());
302     }
303     if (descriptor_set.find(method->output_type()) == descriptor_set.end()) {
304       descriptor_set.insert(method->output_type());
305       result.push_back(method->output_type());
306     }
307   }
308   return result;
309 }
310
311 void GenerateMarshallerFields(Printer* out, const ServiceDescriptor* service) {
312   std::vector<const Descriptor*> used_messages = GetUsedMessages(service);
313   for (size_t i = 0; i < used_messages.size(); i++) {
314     const Descriptor* message = used_messages[i];
315     out->Print(
316         "static readonly grpc::Marshaller<$type$> $fieldname$ = "
317         "grpc::Marshallers.Create((arg) => "
318         "global::Google.Protobuf.MessageExtensions.ToByteArray(arg), "
319         "$type$.Parser.ParseFrom);\n",
320         "fieldname", GetMarshallerFieldName(message), "type",
321         GetClassName(message));
322   }
323   out->Print("\n");
324 }
325
326 void GenerateStaticMethodField(Printer* out, const MethodDescriptor* method) {
327   out->Print(
328       "static readonly grpc::Method<$request$, $response$> $fieldname$ = new "
329       "grpc::Method<$request$, $response$>(\n",
330       "fieldname", GetMethodFieldName(method), "request",
331       GetClassName(method->input_type()), "response",
332       GetClassName(method->output_type()));
333   out->Indent();
334   out->Indent();
335   out->Print("$methodtype$,\n", "methodtype",
336              GetCSharpMethodType(GetMethodType(method)));
337   out->Print("$servicenamefield$,\n", "servicenamefield",
338              GetServiceNameFieldName());
339   out->Print("\"$methodname$\",\n", "methodname", method->name());
340   out->Print("$requestmarshaller$,\n", "requestmarshaller",
341              GetMarshallerFieldName(method->input_type()));
342   out->Print("$responsemarshaller$);\n", "responsemarshaller",
343              GetMarshallerFieldName(method->output_type()));
344   out->Print("\n");
345   out->Outdent();
346   out->Outdent();
347 }
348
349 void GenerateServiceDescriptorProperty(Printer* out,
350                                        const ServiceDescriptor* service) {
351   std::ostringstream index;
352   index << service->index();
353   out->Print("/// <summary>Service descriptor</summary>\n");
354   out->Print(
355       "public static global::Google.Protobuf.Reflection.ServiceDescriptor "
356       "Descriptor\n");
357   out->Print("{\n");
358   out->Print("  get { return $umbrella$.Descriptor.Services[$index$]; }\n",
359              "umbrella", GetReflectionClassName(service->file()), "index",
360              index.str());
361   out->Print("}\n");
362   out->Print("\n");
363 }
364
365 void GenerateServerClass(Printer* out, const ServiceDescriptor* service) {
366   out->Print(
367       "/// <summary>Base class for server-side implementations of "
368       "$servicename$</summary>\n",
369       "servicename", GetServiceClassName(service));
370   out->Print("public abstract partial class $name$\n", "name",
371              GetServerClassName(service));
372   out->Print("{\n");
373   out->Indent();
374   for (int i = 0; i < service->method_count(); i++) {
375     const MethodDescriptor* method = service->method(i);
376     GenerateDocCommentServerMethod(out, method);
377     out->Print(
378         "public virtual $returntype$ "
379         "$methodname$($request$$response_stream_maybe$, "
380         "grpc::ServerCallContext context)\n",
381         "methodname", method->name(), "returntype",
382         GetMethodReturnTypeServer(method), "request",
383         GetMethodRequestParamServer(method), "response_stream_maybe",
384         GetMethodResponseStreamMaybe(method));
385     out->Print("{\n");
386     out->Indent();
387     out->Print(
388         "throw new grpc::RpcException("
389         "new grpc::Status(grpc::StatusCode.Unimplemented, \"\"));\n");
390     out->Outdent();
391     out->Print("}\n\n");
392   }
393   out->Outdent();
394   out->Print("}\n");
395   out->Print("\n");
396 }
397
398 void GenerateClientStub(Printer* out, const ServiceDescriptor* service) {
399   out->Print("/// <summary>Client for $servicename$</summary>\n", "servicename",
400              GetServiceClassName(service));
401   out->Print("public partial class $name$ : grpc::ClientBase<$name$>\n", "name",
402              GetClientClassName(service));
403   out->Print("{\n");
404   out->Indent();
405
406   // constructors
407   out->Print(
408       "/// <summary>Creates a new client for $servicename$</summary>\n"
409       "/// <param name=\"channel\">The channel to use to make remote "
410       "calls.</param>\n",
411       "servicename", GetServiceClassName(service));
412   out->Print("public $name$(grpc::Channel channel) : base(channel)\n", "name",
413              GetClientClassName(service));
414   out->Print("{\n");
415   out->Print("}\n");
416   out->Print(
417       "/// <summary>Creates a new client for $servicename$ that uses a custom "
418       "<c>CallInvoker</c>.</summary>\n"
419       "/// <param name=\"callInvoker\">The callInvoker to use to make remote "
420       "calls.</param>\n",
421       "servicename", GetServiceClassName(service));
422   out->Print(
423       "public $name$(grpc::CallInvoker callInvoker) : base(callInvoker)\n",
424       "name", GetClientClassName(service));
425   out->Print("{\n");
426   out->Print("}\n");
427   out->Print(
428       "/// <summary>Protected parameterless constructor to allow creation"
429       " of test doubles.</summary>\n");
430   out->Print("protected $name$() : base()\n", "name",
431              GetClientClassName(service));
432   out->Print("{\n");
433   out->Print("}\n");
434   out->Print(
435       "/// <summary>Protected constructor to allow creation of configured "
436       "clients.</summary>\n"
437       "/// <param name=\"configuration\">The client configuration.</param>\n");
438   out->Print(
439       "protected $name$(ClientBaseConfiguration configuration)"
440       " : base(configuration)\n",
441       "name", GetClientClassName(service));
442   out->Print("{\n");
443   out->Print("}\n\n");
444
445   for (int i = 0; i < service->method_count(); i++) {
446     const MethodDescriptor* method = service->method(i);
447     MethodType method_type = GetMethodType(method);
448
449     if (method_type == METHODTYPE_NO_STREAMING) {
450       // unary calls have an extra synchronous stub method
451       GenerateDocCommentClientMethod(out, method, true, false);
452       out->Print(
453           "public virtual $response$ $methodname$($request$ request, "
454           "grpc::Metadata "
455           "headers = null, global::System.DateTime? deadline = null, "
456           "global::System.Threading.CancellationToken "
457           "cancellationToken = "
458           "default(global::System.Threading.CancellationToken))\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 $methodname$(request, new grpc::CallOptions(headers, "
466           "deadline, "
467           "cancellationToken));\n",
468           "methodname", method->name());
469       out->Outdent();
470       out->Print("}\n");
471
472       // overload taking CallOptions as a param
473       GenerateDocCommentClientMethod(out, method, true, true);
474       out->Print(
475           "public virtual $response$ $methodname$($request$ request, "
476           "grpc::CallOptions options)\n",
477           "methodname", method->name(), "request",
478           GetClassName(method->input_type()), "response",
479           GetClassName(method->output_type()));
480       out->Print("{\n");
481       out->Indent();
482       out->Print(
483           "return CallInvoker.BlockingUnaryCall($methodfield$, null, options, "
484           "request);\n",
485           "methodfield", GetMethodFieldName(method));
486       out->Outdent();
487       out->Print("}\n");
488     }
489
490     std::string method_name = method->name();
491     if (method_type == METHODTYPE_NO_STREAMING) {
492       method_name += "Async";  // prevent name clash with synchronous method.
493     }
494     GenerateDocCommentClientMethod(out, method, false, false);
495     out->Print(
496         "public virtual $returntype$ "
497         "$methodname$($request_maybe$grpc::Metadata "
498         "headers = null, global::System.DateTime? deadline = null, "
499         "global::System.Threading.CancellationToken "
500         "cancellationToken = "
501         "default(global::System.Threading.CancellationToken))\n",
502         "methodname", method_name, "request_maybe",
503         GetMethodRequestParamMaybe(method), "returntype",
504         GetMethodReturnTypeClient(method));
505     out->Print("{\n");
506     out->Indent();
507
508     out->Print(
509         "return $methodname$($request_maybe$new grpc::CallOptions(headers, "
510         "deadline, "
511         "cancellationToken));\n",
512         "methodname", method_name, "request_maybe",
513         GetMethodRequestParamMaybe(method, true));
514     out->Outdent();
515     out->Print("}\n");
516
517     // overload taking CallOptions as a param
518     GenerateDocCommentClientMethod(out, method, false, true);
519     out->Print(
520         "public virtual $returntype$ "
521         "$methodname$($request_maybe$grpc::CallOptions "
522         "options)\n",
523         "methodname", method_name, "request_maybe",
524         GetMethodRequestParamMaybe(method), "returntype",
525         GetMethodReturnTypeClient(method));
526     out->Print("{\n");
527     out->Indent();
528     switch (GetMethodType(method)) {
529       case METHODTYPE_NO_STREAMING:
530         out->Print(
531             "return CallInvoker.AsyncUnaryCall($methodfield$, null, options, "
532             "request);\n",
533             "methodfield", GetMethodFieldName(method));
534         break;
535       case METHODTYPE_CLIENT_STREAMING:
536         out->Print(
537             "return CallInvoker.AsyncClientStreamingCall($methodfield$, null, "
538             "options);\n",
539             "methodfield", GetMethodFieldName(method));
540         break;
541       case METHODTYPE_SERVER_STREAMING:
542         out->Print(
543             "return CallInvoker.AsyncServerStreamingCall($methodfield$, null, "
544             "options, request);\n",
545             "methodfield", GetMethodFieldName(method));
546         break;
547       case METHODTYPE_BIDI_STREAMING:
548         out->Print(
549             "return CallInvoker.AsyncDuplexStreamingCall($methodfield$, null, "
550             "options);\n",
551             "methodfield", GetMethodFieldName(method));
552         break;
553       default:
554         GOOGLE_LOG(FATAL) << "Can't get here.";
555     }
556     out->Outdent();
557     out->Print("}\n");
558   }
559
560   // override NewInstance method
561   out->Print(
562       "/// <summary>Creates a new instance of client from given "
563       "<c>ClientBaseConfiguration</c>.</summary>\n");
564   out->Print(
565       "protected override $name$ NewInstance(ClientBaseConfiguration "
566       "configuration)\n",
567       "name", GetClientClassName(service));
568   out->Print("{\n");
569   out->Indent();
570   out->Print("return new $name$(configuration);\n", "name",
571              GetClientClassName(service));
572   out->Outdent();
573   out->Print("}\n");
574
575   out->Outdent();
576   out->Print("}\n");
577   out->Print("\n");
578 }
579
580 void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor* service) {
581   out->Print(
582       "/// <summary>Creates service definition that can be registered with a "
583       "server</summary>\n");
584   out->Print(
585       "/// <param name=\"serviceImpl\">An object implementing the server-side"
586       " handling logic.</param>\n");
587   out->Print(
588       "public static grpc::ServerServiceDefinition BindService($implclass$ "
589       "serviceImpl)\n",
590       "implclass", GetServerClassName(service));
591   out->Print("{\n");
592   out->Indent();
593
594   out->Print("return grpc::ServerServiceDefinition.CreateBuilder()");
595   out->Indent();
596   out->Indent();
597   for (int i = 0; i < service->method_count(); i++) {
598     const MethodDescriptor* method = service->method(i);
599     out->Print("\n.AddMethod($methodfield$, serviceImpl.$methodname$)",
600                "methodfield", GetMethodFieldName(method), "methodname",
601                method->name());
602   }
603   out->Print(".Build();\n");
604   out->Outdent();
605   out->Outdent();
606
607   out->Outdent();
608   out->Print("}\n");
609   out->Print("\n");
610 }
611
612 void GenerateBindServiceWithBinderMethod(Printer* out,
613                                          const ServiceDescriptor* service) {
614   out->Print(
615       "/// <summary>Register service method implementations with a service "
616       "binder. Useful when customizing the service binding logic.\n"
617       "/// Note: this method is part of an experimental API that can change or "
618       "be "
619       "removed without any prior notice.</summary>\n");
620   out->Print(
621       "/// <param name=\"serviceBinder\">Service methods will be bound by "
622       "calling <c>AddMethod</c> on this object."
623       "</param>\n");
624   out->Print(
625       "/// <param name=\"serviceImpl\">An object implementing the server-side"
626       " handling logic.</param>\n");
627   out->Print(
628       "public static void BindService(grpc::ServiceBinderBase serviceBinder, "
629       "$implclass$ "
630       "serviceImpl)\n",
631       "implclass", GetServerClassName(service));
632   out->Print("{\n");
633   out->Indent();
634
635   for (int i = 0; i < service->method_count(); i++) {
636     const MethodDescriptor* method = service->method(i);
637     out->Print(
638         "serviceBinder.AddMethod($methodfield$, serviceImpl.$methodname$);\n",
639         "methodfield", GetMethodFieldName(method), "methodname",
640         method->name());
641   }
642
643   out->Outdent();
644   out->Print("}\n");
645   out->Print("\n");
646 }
647
648 void GenerateService(Printer* out, const ServiceDescriptor* service,
649                      bool generate_client, bool generate_server,
650                      bool internal_access) {
651   GenerateDocCommentBody(out, service);
652   out->Print("$access_level$ static partial class $classname$\n",
653              "access_level", GetAccessLevel(internal_access), "classname",
654              GetServiceClassName(service));
655   out->Print("{\n");
656   out->Indent();
657   out->Print("static readonly string $servicenamefield$ = \"$servicename$\";\n",
658              "servicenamefield", GetServiceNameFieldName(), "servicename",
659              service->full_name());
660   out->Print("\n");
661
662   GenerateMarshallerFields(out, service);
663   for (int i = 0; i < service->method_count(); i++) {
664     GenerateStaticMethodField(out, service->method(i));
665   }
666   GenerateServiceDescriptorProperty(out, service);
667
668   if (generate_server) {
669     GenerateServerClass(out, service);
670   }
671   if (generate_client) {
672     GenerateClientStub(out, service);
673   }
674   if (generate_server) {
675     GenerateBindServiceMethod(out, service);
676     GenerateBindServiceWithBinderMethod(out, service);
677   }
678
679   out->Outdent();
680   out->Print("}\n");
681 }
682
683 }  // anonymous namespace
684
685 grpc::string GetServices(const FileDescriptor* file, bool generate_client,
686                          bool generate_server, bool internal_access) {
687   grpc::string output;
688   {
689     // Scope the output stream so it closes and finalizes output to the string.
690
691     StringOutputStream output_stream(&output);
692     Printer out(&output_stream, '$');
693
694     // Don't write out any output if there no services, to avoid empty service
695     // files being generated for proto files that don't declare any.
696     if (file->service_count() == 0) {
697       return output;
698     }
699
700     // Write out a file header.
701     out.Print("// <auto-generated>\n");
702     out.Print(
703         "//     Generated by the protocol buffer compiler.  DO NOT EDIT!\n");
704     out.Print("//     source: $filename$\n", "filename", file->name());
705     out.Print("// </auto-generated>\n");
706
707     // use C++ style as there are no file-level XML comments in .NET
708     grpc::string leading_comments = GetCsharpComments(file, true);
709     if (!leading_comments.empty()) {
710       out.Print("// Original file comments:\n");
711       out.PrintRaw(leading_comments.c_str());
712     }
713
714     out.Print("#pragma warning disable 0414, 1591\n");
715
716     out.Print("#region Designer generated code\n");
717     out.Print("\n");
718     out.Print("using grpc = global::Grpc.Core;\n");
719     out.Print("\n");
720
721     grpc::string file_namespace = GetFileNamespace(file);
722     if (file_namespace != "") {
723       out.Print("namespace $namespace$ {\n", "namespace", file_namespace);
724       out.Indent();
725     }
726     for (int i = 0; i < file->service_count(); i++) {
727       GenerateService(&out, file->service(i), generate_client, generate_server,
728                       internal_access);
729     }
730     if (file_namespace != "") {
731       out.Outdent();
732       out.Print("}\n");
733     }
734     out.Print("#endregion\n");
735   }
736   return output;
737 }
738
739 }  // namespace grpc_csharp_generator