• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <map>
20 #include <set>
21 #include <sstream>
22 
23 #include "src/compiler/config.h"
24 #include "src/compiler/objective_c_generator.h"
25 #include "src/compiler/objective_c_generator_helpers.h"
26 
27 #include <google/protobuf/compiler/objectivec/objectivec_helpers.h>
28 
29 using ::google::protobuf::compiler::objectivec::ClassName;
30 using ::grpc::protobuf::FileDescriptor;
31 using ::grpc::protobuf::MethodDescriptor;
32 using ::grpc::protobuf::ServiceDescriptor;
33 using ::grpc::protobuf::io::Printer;
34 using ::std::map;
35 using ::std::set;
36 
37 namespace grpc_objective_c_generator {
38 namespace {
39 
PrintProtoRpcDeclarationAsPragma(Printer * printer,const MethodDescriptor * method,map<::std::string,::std::string> vars)40 void PrintProtoRpcDeclarationAsPragma(Printer* printer,
41                                       const MethodDescriptor* method,
42                                       map< ::std::string, ::std::string> vars) {
43   vars["client_stream"] = method->client_streaming() ? "stream " : "";
44   vars["server_stream"] = method->server_streaming() ? "stream " : "";
45 
46   printer->Print(vars,
47                  "#pragma mark $method_name$($client_stream$$request_type$)"
48                  " returns ($server_stream$$response_type$)\n\n");
49 }
50 
51 template <typename DescriptorType>
PrintAllComments(const DescriptorType * desc,Printer * printer,bool deprecated=false)52 static void PrintAllComments(const DescriptorType* desc, Printer* printer,
53                              bool deprecated = false) {
54   std::vector<std::string> comments;
55   grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING_DETACHED,
56                              &comments);
57   grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING,
58                              &comments);
59   grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_TRAILING,
60                              &comments);
61   if (comments.empty()) {
62     return;
63   }
64   printer->Print("/**\n");
65   for (auto it = comments.begin(); it != comments.end(); ++it) {
66     printer->Print(" * ");
67     size_t start_pos = it->find_first_not_of(' ');
68     if (start_pos != std::string::npos) {
69       printer->PrintRaw(it->c_str() + start_pos);
70     }
71     printer->Print("\n");
72   }
73   if (deprecated) {
74     printer->Print(" *\n");
75     printer->Print(
76         " * This method belongs to a set of APIs that have been deprecated. "
77         "Using"
78         " the v2 API is recommended.\n");
79   }
80   printer->Print(" */\n");
81 }
82 
PrintMethodSignature(Printer * printer,const MethodDescriptor * method,const map<::std::string,::std::string> & vars)83 void PrintMethodSignature(Printer* printer, const MethodDescriptor* method,
84                           const map< ::std::string, ::std::string>& vars) {
85   // Print comment
86   PrintAllComments(method, printer, true);
87 
88   printer->Print(vars, "- ($return_type$)$method_name$With");
89   if (method->client_streaming()) {
90     printer->Print("RequestsWriter:(GRXWriter *)requestWriter");
91   } else {
92     printer->Print(vars, "Request:($request_class$ *)request");
93   }
94 
95   // TODO(jcanizales): Put this on a new line and align colons.
96   if (method->server_streaming()) {
97     printer->Print(vars,
98                    " eventHandler:(void(^)(BOOL done, "
99                    "$response_class$ *_Nullable response, NSError *_Nullable "
100                    "error))eventHandler");
101   } else {
102     printer->Print(vars,
103                    " handler:(void(^)($response_class$ *_Nullable response, "
104                    "NSError *_Nullable error))handler");
105   }
106 }
107 
PrintSimpleSignature(Printer * printer,const MethodDescriptor * method,map<::std::string,::std::string> vars)108 void PrintSimpleSignature(Printer* printer, const MethodDescriptor* method,
109                           map< ::std::string, ::std::string> vars) {
110   vars["method_name"] =
111       grpc_generator::LowercaseFirstLetter(vars["method_name"]);
112   vars["return_type"] = "void";
113   PrintMethodSignature(printer, method, vars);
114 }
115 
PrintAdvancedSignature(Printer * printer,const MethodDescriptor * method,map<::std::string,::std::string> vars)116 void PrintAdvancedSignature(Printer* printer, const MethodDescriptor* method,
117                             map< ::std::string, ::std::string> vars) {
118   vars["method_name"] = "RPCTo" + vars["method_name"];
119   vars["return_type"] = "GRPCProtoCall *";
120   PrintMethodSignature(printer, method, vars);
121 }
122 
PrintV2Signature(Printer * printer,const MethodDescriptor * method,map<::std::string,::std::string> vars)123 void PrintV2Signature(Printer* printer, const MethodDescriptor* method,
124                       map< ::std::string, ::std::string> vars) {
125   if (method->client_streaming()) {
126     vars["return_type"] = "GRPCStreamingProtoCall *";
127   } else {
128     vars["return_type"] = "GRPCUnaryProtoCall *";
129   }
130   vars["method_name"] =
131       grpc_generator::LowercaseFirstLetter(vars["method_name"]);
132 
133   PrintAllComments(method, printer);
134 
135   printer->Print(vars, "- ($return_type$)$method_name$With");
136   if (method->client_streaming()) {
137     printer->Print("ResponseHandler:(id<GRPCProtoResponseHandler>)handler");
138   } else {
139     printer->Print(vars,
140                    "Message:($request_class$ *)message "
141                    "responseHandler:(id<GRPCProtoResponseHandler>)handler");
142   }
143   printer->Print(" callOptions:(GRPCCallOptions *_Nullable)callOptions");
144 }
145 
GetMethodVars(const MethodDescriptor * method)146 inline map< ::std::string, ::std::string> GetMethodVars(
147     const MethodDescriptor* method) {
148   map< ::std::string, ::std::string> res;
149   res["method_name"] = method->name();
150   res["request_type"] = method->input_type()->name();
151   res["response_type"] = method->output_type()->name();
152   res["request_class"] = ClassName(method->input_type());
153   res["response_class"] = ClassName(method->output_type());
154   return res;
155 }
156 
PrintMethodDeclarations(Printer * printer,const MethodDescriptor * method)157 void PrintMethodDeclarations(Printer* printer, const MethodDescriptor* method) {
158   map< ::std::string, ::std::string> vars = GetMethodVars(method);
159 
160   PrintProtoRpcDeclarationAsPragma(printer, method, vars);
161 
162   PrintSimpleSignature(printer, method, vars);
163   printer->Print(";\n\n");
164   PrintAdvancedSignature(printer, method, vars);
165   printer->Print(";\n\n\n");
166 }
167 
PrintV2MethodDeclarations(Printer * printer,const MethodDescriptor * method)168 void PrintV2MethodDeclarations(Printer* printer,
169                                const MethodDescriptor* method) {
170   map< ::std::string, ::std::string> vars = GetMethodVars(method);
171 
172   PrintProtoRpcDeclarationAsPragma(printer, method, vars);
173 
174   PrintV2Signature(printer, method, vars);
175   printer->Print(";\n\n");
176 }
177 
PrintSimpleImplementation(Printer * printer,const MethodDescriptor * method,map<::std::string,::std::string> vars)178 void PrintSimpleImplementation(Printer* printer, const MethodDescriptor* method,
179                                map< ::std::string, ::std::string> vars) {
180   printer->Print("{\n");
181   printer->Print(vars, "  [[self RPCTo$method_name$With");
182   if (method->client_streaming()) {
183     printer->Print("RequestsWriter:requestWriter");
184   } else {
185     printer->Print("Request:request");
186   }
187   if (method->server_streaming()) {
188     printer->Print(" eventHandler:eventHandler] start];\n");
189   } else {
190     printer->Print(" handler:handler] start];\n");
191   }
192   printer->Print("}\n");
193 }
194 
PrintAdvancedImplementation(Printer * printer,const MethodDescriptor * method,map<::std::string,::std::string> vars)195 void PrintAdvancedImplementation(Printer* printer,
196                                  const MethodDescriptor* method,
197                                  map< ::std::string, ::std::string> vars) {
198   printer->Print("{\n");
199   printer->Print(vars, "  return [self RPCToMethod:@\"$method_name$\"\n");
200 
201   printer->Print("            requestsWriter:");
202   if (method->client_streaming()) {
203     printer->Print("requestWriter\n");
204   } else {
205     printer->Print("[GRXWriter writerWithValue:request]\n");
206   }
207 
208   printer->Print(vars, "             responseClass:[$response_class$ class]\n");
209 
210   printer->Print("        responsesWriteable:[GRXWriteable ");
211   if (method->server_streaming()) {
212     printer->Print("writeableWithEventHandler:eventHandler]];\n");
213   } else {
214     printer->Print("writeableWithSingleHandler:handler]];\n");
215   }
216 
217   printer->Print("}\n");
218 }
219 
PrintV2Implementation(Printer * printer,const MethodDescriptor * method,map<::std::string,::std::string> vars)220 void PrintV2Implementation(Printer* printer, const MethodDescriptor* method,
221                            map< ::std::string, ::std::string> vars) {
222   printer->Print(" {\n");
223   if (method->client_streaming()) {
224     printer->Print(vars, "  return [self RPCToMethod:@\"$method_name$\"\n");
225     printer->Print("           responseHandler:handler\n");
226     printer->Print("               callOptions:callOptions\n");
227     printer->Print(
228         vars, "             responseClass:[$response_class$ class]];\n}\n\n");
229   } else {
230     printer->Print(vars, "  return [self RPCToMethod:@\"$method_name$\"\n");
231     printer->Print("                   message:message\n");
232     printer->Print("           responseHandler:handler\n");
233     printer->Print("               callOptions:callOptions\n");
234     printer->Print(
235         vars, "             responseClass:[$response_class$ class]];\n}\n\n");
236   }
237 }
238 
PrintMethodImplementations(Printer * printer,const MethodDescriptor * method,const Parameters & generator_params)239 void PrintMethodImplementations(Printer* printer,
240                                 const MethodDescriptor* method,
241                                 const Parameters& generator_params) {
242   map< ::std::string, ::std::string> vars = GetMethodVars(method);
243 
244   PrintProtoRpcDeclarationAsPragma(printer, method, vars);
245 
246   if (!generator_params.no_v1_compatibility) {
247     // TODO(jcanizales): Print documentation from the method.
248     PrintSimpleSignature(printer, method, vars);
249     PrintSimpleImplementation(printer, method, vars);
250 
251     printer->Print("// Returns a not-yet-started RPC object.\n");
252     PrintAdvancedSignature(printer, method, vars);
253     PrintAdvancedImplementation(printer, method, vars);
254   }
255 
256   PrintV2Signature(printer, method, vars);
257   PrintV2Implementation(printer, method, vars);
258 }
259 
260 }  // namespace
261 
GetAllMessageClasses(const FileDescriptor * file)262 ::std::string GetAllMessageClasses(const FileDescriptor* file) {
263   ::std::string output;
264   set< ::std::string> classes;
265   for (int i = 0; i < file->service_count(); i++) {
266     const auto service = file->service(i);
267     for (int i = 0; i < service->method_count(); i++) {
268       const auto method = service->method(i);
269       classes.insert(ClassName(method->input_type()));
270       classes.insert(ClassName(method->output_type()));
271     }
272   }
273   for (auto one_class : classes) {
274     output += "@class " + one_class + ";\n";
275   }
276 
277   return output;
278 }
279 
GetProtocol(const ServiceDescriptor * service,const Parameters & generator_params)280 ::std::string GetProtocol(const ServiceDescriptor* service,
281                           const Parameters& generator_params) {
282   ::std::string output;
283 
284   if (generator_params.no_v1_compatibility) return output;
285 
286   // Scope the output stream so it closes and finalizes output to the string.
287   grpc::protobuf::io::StringOutputStream output_stream(&output);
288   Printer printer(&output_stream, '$');
289 
290   map< ::std::string, ::std::string> vars = {
291       {"service_class", ServiceClassName(service)}};
292 
293   printer.Print(vars,
294                 "/**\n"
295                 " * The methods in this protocol belong to a set of old APIs "
296                 "that have been deprecated. They do not\n"
297                 " * recognize call options provided in the initializer. Using "
298                 "the v2 protocol is recommended.\n"
299                 " */\n");
300   printer.Print(vars, "@protocol $service_class$ <NSObject>\n\n");
301   for (int i = 0; i < service->method_count(); i++) {
302     PrintMethodDeclarations(&printer, service->method(i));
303   }
304   printer.Print("@end\n\n");
305 
306   return output;
307 }
308 
GetV2Protocol(const ServiceDescriptor * service)309 ::std::string GetV2Protocol(const ServiceDescriptor* service) {
310   ::std::string output;
311 
312   // Scope the output stream so it closes and finalizes output to the string.
313   grpc::protobuf::io::StringOutputStream output_stream(&output);
314   Printer printer(&output_stream, '$');
315 
316   map< ::std::string, ::std::string> vars = {
317       {"service_class", ServiceClassName(service) + "2"}};
318 
319   printer.Print(vars, "@protocol $service_class$ <NSObject>\n\n");
320   for (int i = 0; i < service->method_count(); i++) {
321     PrintV2MethodDeclarations(&printer, service->method(i));
322   }
323   printer.Print("@end\n\n");
324 
325   return output;
326 }
327 
GetInterface(const ServiceDescriptor * service,const Parameters & generator_params)328 ::std::string GetInterface(const ServiceDescriptor* service,
329                            const Parameters& generator_params) {
330   ::std::string output;
331 
332   // Scope the output stream so it closes and finalizes output to the string.
333   grpc::protobuf::io::StringOutputStream output_stream(&output);
334   Printer printer(&output_stream, '$');
335 
336   map< ::std::string, ::std::string> vars = {
337       {"service_class", ServiceClassName(service)}};
338 
339   printer.Print(vars,
340                 "/**\n"
341                 " * Basic service implementation, over gRPC, that only does\n"
342                 " * marshalling and parsing.\n"
343                 " */\n");
344   printer.Print(vars,
345                 "@interface $service_class$ :"
346                 " GRPCProtoService<$service_class$2");
347   if (!generator_params.no_v1_compatibility) {
348     printer.Print(vars, ", $service_class$");
349   }
350   printer.Print(">\n");
351   printer.Print(
352       "- (instancetype)initWithHost:(NSString *)host "
353       "callOptions:(GRPCCallOptions "
354       "*_Nullable)callOptions"
355       " NS_DESIGNATED_INITIALIZER;\n");
356   printer.Print(
357       "+ (instancetype)serviceWithHost:(NSString *)host "
358       "callOptions:(GRPCCallOptions *_Nullable)callOptions;\n");
359   if (!generator_params.no_v1_compatibility) {
360     printer.Print(
361         "// The following methods belong to a set of old APIs that have been "
362         "deprecated.\n");
363     printer.Print("- (instancetype)initWithHost:(NSString *)host;\n");
364     printer.Print("+ (instancetype)serviceWithHost:(NSString *)host;\n");
365   }
366   printer.Print("@end\n");
367 
368   return output;
369 }
370 
GetSource(const ServiceDescriptor * service,const Parameters & generator_params)371 ::std::string GetSource(const ServiceDescriptor* service,
372                         const Parameters& generator_params) {
373   ::std::string output;
374   {
375     // Scope the output stream so it closes and finalizes output to the string.
376     grpc::protobuf::io::StringOutputStream output_stream(&output);
377     Printer printer(&output_stream, '$');
378 
379     map< ::std::string, ::std::string> vars = {
380         {"service_name", service->name()},
381         {"service_class", ServiceClassName(service)},
382         {"package", service->file()->package()}};
383 
384     printer.Print(vars,
385                   "@implementation $service_class$\n\n"
386                   "#pragma clang diagnostic push\n"
387                   "#pragma clang diagnostic ignored "
388                   "\"-Wobjc-designated-initializers\"\n\n"
389                   "// Designated initializer\n"
390                   "- (instancetype)initWithHost:(NSString *)host "
391                   "callOptions:(GRPCCallOptions *_Nullable)callOptions {\n"
392                   "  return [super initWithHost:host\n"
393                   "                 packageName:@\"$package$\"\n"
394                   "                 serviceName:@\"$service_name$\"\n"
395                   "                 callOptions:callOptions];\n"
396                   "}\n\n");
397     if (!generator_params.no_v1_compatibility) {
398       printer.Print(vars,
399                     "- (instancetype)initWithHost:(NSString *)host {\n"
400                     "  return [super initWithHost:host\n"
401                     "                 packageName:@\"$package$\"\n"
402                     "                 serviceName:@\"$service_name$\"];\n"
403                     "}\n\n");
404     }
405     printer.Print("#pragma clang diagnostic pop\n\n");
406 
407     if (!generator_params.no_v1_compatibility) {
408       printer.Print(
409           "// Override superclass initializer to disallow different"
410           " package and service names.\n"
411           "- (instancetype)initWithHost:(NSString *)host\n"
412           "                 packageName:(NSString *)packageName\n"
413           "                 serviceName:(NSString *)serviceName {\n"
414           "  return [self initWithHost:host];\n"
415           "}\n\n");
416     }
417     printer.Print(
418         "- (instancetype)initWithHost:(NSString *)host\n"
419         "                 packageName:(NSString *)packageName\n"
420         "                 serviceName:(NSString *)serviceName\n"
421         "                 callOptions:(GRPCCallOptions *)callOptions {\n"
422         "  return [self initWithHost:host callOptions:callOptions];\n"
423         "}\n\n");
424 
425     printer.Print("#pragma mark - Class Methods\n\n");
426     if (!generator_params.no_v1_compatibility) {
427       printer.Print(
428           "+ (instancetype)serviceWithHost:(NSString *)host {\n"
429           "  return [[self alloc] initWithHost:host];\n"
430           "}\n\n");
431     }
432     printer.Print(
433         "+ (instancetype)serviceWithHost:(NSString *)host "
434         "callOptions:(GRPCCallOptions *_Nullable)callOptions {\n"
435         "  return [[self alloc] initWithHost:host callOptions:callOptions];\n"
436         "}\n\n");
437 
438     printer.Print("#pragma mark - Method Implementations\n\n");
439 
440     for (int i = 0; i < service->method_count(); i++) {
441       PrintMethodImplementations(&printer, service->method(i),
442                                  generator_params);
443     }
444 
445     printer.Print("@end\n");
446   }
447   return output;
448 }
449 
450 }  // namespace grpc_objective_c_generator
451