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