xref: /aosp_15_r20/external/flatbuffers/src/idl_gen_grpc.cpp (revision 890232f25432b36107d06881e0a25aaa6b473652)
1 /*
2  * Copyright 2014 Google Inc. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 // independent from idl_parser, since this code is not needed for most clients
18 
19 #include "flatbuffers/code_generators.h"
20 #include "flatbuffers/flatbuffers.h"
21 #include "flatbuffers/idl.h"
22 #include "flatbuffers/util.h"
23 #include "src/compiler/cpp_generator.h"
24 #include "src/compiler/go_generator.h"
25 #include "src/compiler/java_generator.h"
26 #include "src/compiler/python_generator.h"
27 #include "src/compiler/swift_generator.h"
28 #include "src/compiler/ts_generator.h"
29 
30 #if defined(_MSC_VER)
31 #  pragma warning(push)
32 #  pragma warning(disable : 4512)  // C4512: 'class' : assignment operator could
33 // not be generated
34 #endif
35 
36 namespace flatbuffers {
37 
38 class FlatBufMethod : public grpc_generator::Method {
39  public:
40   enum Streaming { kNone, kClient, kServer, kBiDi };
41 
FlatBufMethod(const RPCCall * method)42   FlatBufMethod(const RPCCall *method) : method_(method) {
43     streaming_ = kNone;
44     auto val = method_->attributes.Lookup("streaming");
45     if (val) {
46       if (val->constant == "client") streaming_ = kClient;
47       if (val->constant == "server") streaming_ = kServer;
48       if (val->constant == "bidi") streaming_ = kBiDi;
49     }
50   }
51 
GetLeadingComments(const grpc::string) const52   grpc::string GetLeadingComments(const grpc::string) const { return ""; }
53 
GetTrailingComments(const grpc::string) const54   grpc::string GetTrailingComments(const grpc::string) const { return ""; }
55 
GetAllComments() const56   std::vector<grpc::string> GetAllComments() const {
57     return method_->doc_comment;
58   }
59 
name() const60   std::string name() const { return method_->name; }
61 
62   // TODO: This method need to incorporate namespace for C++ side. Other
63   // language bindings simply don't use this method.
GRPCType(const StructDef & sd) const64   std::string GRPCType(const StructDef &sd) const {
65     return "flatbuffers::grpc::Message<" + sd.name + ">";
66   }
67 
get_input_namespace_parts() const68   std::vector<std::string> get_input_namespace_parts() const {
69     return (*method_->request).defined_namespace->components;
70   }
71 
get_input_type_name() const72   std::string get_input_type_name() const { return (*method_->request).name; }
73 
get_output_namespace_parts() const74   std::vector<std::string> get_output_namespace_parts() const {
75     return (*method_->response).defined_namespace->components;
76   }
77 
get_output_type_name() const78   std::string get_output_type_name() const { return (*method_->response).name; }
79 
get_module_and_message_path_input(grpc::string *,grpc::string,bool,grpc::string) const80   bool get_module_and_message_path_input(grpc::string * /*str*/,
81                                          grpc::string /*generator_file_name*/,
82                                          bool /*generate_in_pb2_grpc*/,
83                                          grpc::string /*import_prefix*/) const {
84     return true;
85   }
86 
get_module_and_message_path_output(grpc::string *,grpc::string,bool,grpc::string) const87   bool get_module_and_message_path_output(
88       grpc::string * /*str*/, grpc::string /*generator_file_name*/,
89       bool /*generate_in_pb2_grpc*/, grpc::string /*import_prefix*/) const {
90     return true;
91   }
92 
get_fb_builder() const93   std::string get_fb_builder() const { return "builder"; }
94 
input_type_name() const95   std::string input_type_name() const { return GRPCType(*method_->request); }
96 
output_type_name() const97   std::string output_type_name() const { return GRPCType(*method_->response); }
98 
NoStreaming() const99   bool NoStreaming() const { return streaming_ == kNone; }
100 
ClientStreaming() const101   bool ClientStreaming() const { return streaming_ == kClient; }
102 
ServerStreaming() const103   bool ServerStreaming() const { return streaming_ == kServer; }
104 
BidiStreaming() const105   bool BidiStreaming() const { return streaming_ == kBiDi; }
106 
107  private:
108   const RPCCall *method_;
109   Streaming streaming_;
110 };
111 
112 class FlatBufService : public grpc_generator::Service {
113  public:
FlatBufService(const ServiceDef * service)114   FlatBufService(const ServiceDef *service) : service_(service) {}
115 
GetLeadingComments(const grpc::string) const116   grpc::string GetLeadingComments(const grpc::string) const { return ""; }
117 
GetTrailingComments(const grpc::string) const118   grpc::string GetTrailingComments(const grpc::string) const { return ""; }
119 
GetAllComments() const120   std::vector<grpc::string> GetAllComments() const {
121     return service_->doc_comment;
122   }
123 
namespace_parts() const124   std::vector<grpc::string> namespace_parts() const {
125     return service_->defined_namespace->components;
126   }
127 
name() const128   std::string name() const { return service_->name; }
is_internal() const129   bool is_internal() const {
130     return service_->Definition::attributes.Lookup("private") ? true : false;
131   }
132 
method_count() const133   int method_count() const {
134     return static_cast<int>(service_->calls.vec.size());
135   }
136 
method(int i) const137   std::unique_ptr<const grpc_generator::Method> method(int i) const {
138     return std::unique_ptr<const grpc_generator::Method>(
139         new FlatBufMethod(service_->calls.vec[i]));
140   }
141 
142  private:
143   const ServiceDef *service_;
144 };
145 
146 class FlatBufPrinter : public grpc_generator::Printer {
147  public:
FlatBufPrinter(std::string * str,const char indentation_type)148   FlatBufPrinter(std::string *str, const char indentation_type)
149       : str_(str),
150         escape_char_('$'),
151         indent_(0),
152         indentation_size_(2),
153         indentation_type_(indentation_type) {}
154 
Print(const std::map<std::string,std::string> & vars,const char * string_template)155   void Print(const std::map<std::string, std::string> &vars,
156              const char *string_template) {
157     std::string s = string_template;
158     // Replace any occurrences of strings in "vars" that are surrounded
159     // by the escape character by what they're mapped to.
160     size_t pos;
161     while ((pos = s.find(escape_char_)) != std::string::npos) {
162       // Found an escape char, must also find the closing one.
163       size_t pos2 = s.find(escape_char_, pos + 1);
164       // If placeholder not closed, ignore.
165       if (pos2 == std::string::npos) break;
166       auto it = vars.find(s.substr(pos + 1, pos2 - pos - 1));
167       // If unknown placeholder, ignore.
168       if (it == vars.end()) break;
169       // Subtitute placeholder.
170       s.replace(pos, pos2 - pos + 1, it->second);
171     }
172     Print(s.c_str());
173   }
174 
Print(const char * s)175   void Print(const char *s) {
176     if (s == nullptr || *s == '\0') { return; }
177     // Add this string, but for each part separated by \n, add indentation.
178     for (;;) {
179       // Current indentation.
180       str_->insert(str_->end(), indent_ * indentation_size_, indentation_type_);
181       // See if this contains more than one line.
182       const char *lf = strchr(s, '\n');
183       if (lf) {
184         (*str_) += std::string(s, lf + 1);
185         s = lf + 1;
186         if (!*s) break;  // Only continue if there's more lines.
187       } else {
188         (*str_) += s;
189         break;
190       }
191     }
192   }
193 
SetIndentationSize(const size_t size)194   void SetIndentationSize(const size_t size) {
195     FLATBUFFERS_ASSERT(str_->empty());
196     indentation_size_ = size;
197   }
198 
Indent()199   void Indent() { indent_++; }
200 
Outdent()201   void Outdent() {
202     FLATBUFFERS_ASSERT(indent_ > 0);
203     indent_--;
204   }
205 
206  private:
207   std::string *str_;
208   char escape_char_;
209   size_t indent_;
210   size_t indentation_size_;
211   char indentation_type_;
212 };
213 
214 class FlatBufFile : public grpc_generator::File {
215  public:
216   enum Language {
217     kLanguageGo,
218     kLanguageCpp,
219     kLanguageJava,
220     kLanguagePython,
221     kLanguageSwift,
222     kLanguageTS
223   };
224 
FlatBufFile(const Parser & parser,const std::string & file_name,Language language)225   FlatBufFile(const Parser &parser, const std::string &file_name,
226               Language language)
227       : parser_(parser), file_name_(file_name), language_(language) {}
228 
229   FlatBufFile &operator=(const FlatBufFile &);
230 
GetLeadingComments(const grpc::string) const231   grpc::string GetLeadingComments(const grpc::string) const { return ""; }
232 
GetTrailingComments(const grpc::string) const233   grpc::string GetTrailingComments(const grpc::string) const { return ""; }
234 
GetAllComments() const235   std::vector<grpc::string> GetAllComments() const {
236     return std::vector<grpc::string>();
237   }
238 
filename() const239   std::string filename() const { return file_name_; }
240 
filename_without_ext() const241   std::string filename_without_ext() const {
242     return StripExtension(file_name_);
243   }
244 
package() const245   std::string package() const {
246     return parser_.current_namespace_->GetFullyQualifiedName("");
247   }
248 
package_parts() const249   std::vector<std::string> package_parts() const {
250     return parser_.current_namespace_->components;
251   }
252 
additional_headers() const253   std::string additional_headers() const {
254     switch (language_) {
255       case kLanguageCpp: {
256         return "#include \"flatbuffers/grpc.h\"\n";
257       }
258       case kLanguageGo: {
259         return "import \"github.com/google/flatbuffers/go\"";
260       }
261       case kLanguageJava: {
262         return "import com.google.flatbuffers.grpc.FlatbuffersUtils;";
263       }
264       case kLanguagePython: {
265         return "";
266       }
267       case kLanguageSwift: {
268         return "";
269       }
270       case kLanguageTS: {
271         return "";
272       }
273     }
274     return "";
275   }
276 
service_count() const277   int service_count() const {
278     return static_cast<int>(parser_.services_.vec.size());
279   }
280 
service(int i) const281   std::unique_ptr<const grpc_generator::Service> service(int i) const {
282     return std::unique_ptr<const grpc_generator::Service>(
283         new FlatBufService(parser_.services_.vec[i]));
284   }
285 
CreatePrinter(std::string * str,const char indentation_type=' ') const286   std::unique_ptr<grpc_generator::Printer> CreatePrinter(
287       std::string *str, const char indentation_type = ' ') const {
288     return std::unique_ptr<grpc_generator::Printer>(
289         new FlatBufPrinter(str, indentation_type));
290   }
291 
292  private:
293   const Parser &parser_;
294   const std::string &file_name_;
295   const Language language_;
296 };
297 
298 class GoGRPCGenerator : public flatbuffers::BaseGenerator {
299  public:
GoGRPCGenerator(const Parser & parser,const std::string & path,const std::string & file_name)300   GoGRPCGenerator(const Parser &parser, const std::string &path,
301                   const std::string &file_name)
302       : BaseGenerator(parser, path, file_name, "", "" /*Unused*/, "go"),
303         parser_(parser),
304         path_(path),
305         file_name_(file_name) {}
306 
generate()307   bool generate() {
308     FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageGo);
309     grpc_go_generator::Parameters p;
310     p.custom_method_io_type = "flatbuffers.Builder";
311     for (int i = 0; i < file.service_count(); i++) {
312       auto service = file.service(i);
313       const Definition *def = parser_.services_.vec[i];
314       p.package_name = LastNamespacePart(*(def->defined_namespace));
315       p.service_prefix =
316           def->defined_namespace->GetFullyQualifiedName("");  // file.package();
317       std::string output =
318           grpc_go_generator::GenerateServiceSource(&file, service.get(), &p);
319       std::string filename =
320           NamespaceDir(*def->defined_namespace) + def->name + "_grpc.go";
321       if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
322     }
323     return true;
324   }
325 
326  protected:
327   const Parser &parser_;
328   const std::string &path_, &file_name_;
329 };
330 
GenerateGoGRPC(const Parser & parser,const std::string & path,const std::string & file_name)331 bool GenerateGoGRPC(const Parser &parser, const std::string &path,
332                     const std::string &file_name) {
333   int nservices = 0;
334   for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
335        ++it) {
336     if (!(*it)->generated) nservices++;
337   }
338   if (!nservices) return true;
339   return GoGRPCGenerator(parser, path, file_name).generate();
340 }
341 
GenerateCppGRPC(const Parser & parser,const std::string & path,const std::string & file_name)342 bool GenerateCppGRPC(const Parser &parser, const std::string &path,
343                      const std::string &file_name) {
344   const auto &opts = parser.opts;
345   int nservices = 0;
346   for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
347        ++it) {
348     if (!(*it)->generated) nservices++;
349   }
350   if (!nservices) return true;
351 
352   std::string suffix = "";
353   suffix += opts.filename_suffix.empty() ? "_generated" : opts.filename_suffix;
354   suffix += ".";
355   suffix += opts.filename_extension.empty() ? "h" : opts.filename_extension;
356 
357   grpc_cpp_generator::Parameters generator_parameters;
358   // TODO(wvo): make the other parameters in this struct configurable.
359   generator_parameters.use_system_headers = true;
360   generator_parameters.message_header_extension = suffix;
361 
362   FlatBufFile fbfile(parser, file_name, FlatBufFile::kLanguageCpp);
363 
364   std::string header_code =
365       grpc_cpp_generator::GetHeaderPrologue(&fbfile, generator_parameters) +
366       grpc_cpp_generator::GetHeaderIncludes(&fbfile, generator_parameters) +
367       grpc_cpp_generator::GetHeaderServices(&fbfile, generator_parameters) +
368       grpc_cpp_generator::GetHeaderEpilogue(&fbfile, generator_parameters);
369 
370   std::string source_code =
371       grpc_cpp_generator::GetSourcePrologue(&fbfile, generator_parameters) +
372       grpc_cpp_generator::GetSourceIncludes(&fbfile, generator_parameters) +
373       grpc_cpp_generator::GetSourceServices(&fbfile, generator_parameters) +
374       grpc_cpp_generator::GetSourceEpilogue(&fbfile, generator_parameters);
375 
376   return flatbuffers::SaveFile((path + file_name + ".grpc.fb.h").c_str(),
377                                header_code, false) &&
378          flatbuffers::SaveFile((path + file_name + ".grpc.fb.cc").c_str(),
379                                source_code, false);
380 }
381 
382 class JavaGRPCGenerator : public flatbuffers::BaseGenerator {
383  public:
JavaGRPCGenerator(const Parser & parser,const std::string & path,const std::string & file_name)384   JavaGRPCGenerator(const Parser &parser, const std::string &path,
385                     const std::string &file_name)
386       : BaseGenerator(parser, path, file_name, "", "." /*separator*/, "java") {}
387 
generate()388   bool generate() {
389     FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageJava);
390     grpc_java_generator::Parameters p;
391     for (int i = 0; i < file.service_count(); i++) {
392       auto service = file.service(i);
393       const Definition *def = parser_.services_.vec[i];
394       p.package_name =
395           def->defined_namespace->GetFullyQualifiedName("");  // file.package();
396       std::string output =
397           grpc_java_generator::GenerateServiceSource(&file, service.get(), &p);
398       std::string filename =
399           NamespaceDir(*def->defined_namespace) + def->name + "Grpc.java";
400       if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
401     }
402     return true;
403   }
404 };
405 
GenerateJavaGRPC(const Parser & parser,const std::string & path,const std::string & file_name)406 bool GenerateJavaGRPC(const Parser &parser, const std::string &path,
407                       const std::string &file_name) {
408   int nservices = 0;
409   for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
410        ++it) {
411     if (!(*it)->generated) nservices++;
412   }
413   if (!nservices) return true;
414   return JavaGRPCGenerator(parser, path, file_name).generate();
415 }
416 
417 class PythonGRPCGenerator : public flatbuffers::BaseGenerator {
418  private:
419   CodeWriter code_;
420 
421  public:
PythonGRPCGenerator(const Parser & parser,const std::string & filename)422   PythonGRPCGenerator(const Parser &parser, const std::string &filename)
423       : BaseGenerator(parser, "", filename, "", "" /*Unused*/, "swift") {}
424 
generate()425   bool generate() {
426     code_.Clear();
427     code_ +=
428         "# Generated by the gRPC Python protocol compiler plugin. "
429         "DO NOT EDIT!\n";
430     code_ += "import grpc\n";
431 
432     FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguagePython);
433 
434     for (int i = 0; i < file.service_count(); i++) {
435       auto service = file.service(i);
436       code_ += grpc_python_generator::Generate(&file, service.get());
437     }
438     const auto final_code = code_.ToString();
439     const auto filename = GenerateFileName();
440     return SaveFile(filename.c_str(), final_code, false);
441   }
442 
GenerateFileName()443   std::string GenerateFileName() {
444     std::string namespace_dir;
445     auto &namespaces = parser_.namespaces_.back()->components;
446     for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
447       if (it != namespaces.begin()) namespace_dir += kPathSeparator;
448       namespace_dir += *it;
449     }
450     std::string grpc_py_filename = namespace_dir;
451     if (!namespace_dir.empty()) grpc_py_filename += kPathSeparator;
452     return grpc_py_filename + file_name_ + "_grpc_fb.py";
453   }
454 };
455 
GeneratePythonGRPC(const Parser & parser,const std::string &,const std::string & file_name)456 bool GeneratePythonGRPC(const Parser &parser, const std::string & /*path*/,
457                         const std::string &file_name) {
458   int nservices = 0;
459   for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
460        ++it) {
461     if (!(*it)->generated) nservices++;
462   }
463   if (!nservices) return true;
464 
465   return PythonGRPCGenerator(parser, file_name).generate();
466 }
467 
468 class SwiftGRPCGenerator : public flatbuffers::BaseGenerator {
469  private:
470   CodeWriter code_;
471 
472  public:
SwiftGRPCGenerator(const Parser & parser,const std::string & path,const std::string & filename)473   SwiftGRPCGenerator(const Parser &parser, const std::string &path,
474                      const std::string &filename)
475       : BaseGenerator(parser, path, filename, "", "" /*Unused*/, "swift") {}
476 
generate()477   bool generate() {
478     code_.Clear();
479     code_ += "// Generated GRPC code for FlatBuffers swift!";
480     code_ += grpc_swift_generator::GenerateHeader();
481     FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageSwift);
482     for (int i = 0; i < file.service_count(); i++) {
483       auto service = file.service(i);
484       code_ += grpc_swift_generator::Generate(&file, service.get());
485     }
486     const auto final_code = code_.ToString();
487     const auto filename = GeneratedFileName(path_, file_name_);
488     return SaveFile(filename.c_str(), final_code, false);
489   }
490 
GeneratedFileName(const std::string & path,const std::string & file_name)491   static std::string GeneratedFileName(const std::string &path,
492                                        const std::string &file_name) {
493     return path + file_name + ".grpc.swift";
494   }
495 };
496 
GenerateSwiftGRPC(const Parser & parser,const std::string & path,const std::string & file_name)497 bool GenerateSwiftGRPC(const Parser &parser, const std::string &path,
498                        const std::string &file_name) {
499   int nservices = 0;
500   for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
501        ++it) {
502     if (!(*it)->generated) nservices++;
503   }
504   if (!nservices) return true;
505   return SwiftGRPCGenerator(parser, path, file_name).generate();
506 }
507 
508 class TSGRPCGenerator : public flatbuffers::BaseGenerator {
509  private:
510   CodeWriter code_;
511 
512  public:
TSGRPCGenerator(const Parser & parser,const std::string & path,const std::string & filename)513   TSGRPCGenerator(const Parser &parser, const std::string &path,
514                   const std::string &filename)
515       : BaseGenerator(parser, path, filename, "", "" /*Unused*/, "ts") {}
516 
generate()517   bool generate() {
518     code_.Clear();
519     FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageTS);
520 
521     for (int i = 0; i < file.service_count(); i++) {
522       auto service = file.service(i);
523       code_ += grpc_ts_generator::Generate(&file, service.get(), file_name_);
524       const auto ts_name = GeneratedFileName(path_, file_name_);
525       if (!SaveFile(ts_name.c_str(), code_.ToString(), false)) return false;
526 
527       code_.Clear();
528       code_ += grpc_ts_generator::GenerateInterface(&file, service.get(),
529                                                     file_name_);
530       const auto ts_interface_name = GeneratedFileName(path_, file_name_, true);
531       if (!SaveFile(ts_interface_name.c_str(), code_.ToString(), false))
532         return false;
533     }
534     return true;
535   }
536 
GeneratedFileName(const std::string & path,const std::string & file_name,const bool is_interface=false)537   static std::string GeneratedFileName(const std::string &path,
538                                        const std::string &file_name,
539                                        const bool is_interface = false) {
540     if (is_interface) return path + file_name + "_grpc.d.ts";
541     return path + file_name + "_grpc.js";
542   }
543 };
544 
GenerateTSGRPC(const Parser & parser,const std::string & path,const std::string & file_name)545 bool GenerateTSGRPC(const Parser &parser, const std::string &path,
546                     const std::string &file_name) {
547   int nservices = 0;
548   for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
549        ++it) {
550     if (!(*it)->generated) nservices++;
551   }
552   if (!nservices) return true;
553   return TSGRPCGenerator(parser, path, file_name).generate();
554 }
555 
556 }  // namespace flatbuffers
557 
558 #if defined(_MSC_VER)
559 #  pragma warning(pop)
560 #endif
561