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