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