objective_c_generator.cc 16 KB

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