• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <cstdio>
20 
21 #include "flatbuffers/code_generators.h"
22 #include "flatbuffers/flatbuffers.h"
23 #include "flatbuffers/idl.h"
24 #include "flatbuffers/util.h"
25 #include "src/compiler/cpp_generator.h"
26 #include "src/compiler/go_generator.h"
27 #include "src/compiler/java_generator.h"
28 #include "src/compiler/python_generator.h"
29 #include "src/compiler/swift_generator.h"
30 #include "src/compiler/ts_generator.h"
31 
32 #if defined(_MSC_VER)
33 #  pragma warning(push)
34 #  pragma warning(disable : 4512)  // C4512: 'class' : assignment operator could
35 // not be generated
36 #endif
37 
38 namespace flatbuffers {
39 
40 class FlatBufMethod : public grpc_generator::Method {
41  public:
42   enum Streaming { kNone, kClient, kServer, kBiDi };
43 
FlatBufMethod(const RPCCall * method)44   FlatBufMethod(const RPCCall *method) : method_(method) {
45     streaming_ = kNone;
46     auto val = method_->attributes.Lookup("streaming");
47     if (val) {
48       if (val->constant == "client") streaming_ = kClient;
49       if (val->constant == "server") streaming_ = kServer;
50       if (val->constant == "bidi") streaming_ = kBiDi;
51     }
52   }
53 
GetLeadingComments(const grpc::string) const54   grpc::string GetLeadingComments(const grpc::string) const { return ""; }
55 
GetTrailingComments(const grpc::string) const56   grpc::string GetTrailingComments(const grpc::string) const { return ""; }
57 
GetAllComments() const58   std::vector<grpc::string> GetAllComments() const {
59     return method_->doc_comment;
60   }
61 
name() const62   std::string name() const { return method_->name; }
63 
64   // TODO: This method need to incorporate namespace for C++ side. Other
65   // language bindings simply don't use this method.
GRPCType(const StructDef & sd) const66   std::string GRPCType(const StructDef &sd) const {
67     return "flatbuffers::grpc::Message<" + sd.name + ">";
68   }
69 
get_input_namespace_parts() const70   std::vector<std::string> get_input_namespace_parts() const {
71     return (*method_->request).defined_namespace->components;
72   }
73 
get_input_type_name() const74   std::string get_input_type_name() const { return (*method_->request).name; }
75 
get_output_namespace_parts() const76   std::vector<std::string> get_output_namespace_parts() const {
77     return (*method_->response).defined_namespace->components;
78   }
79 
get_output_type_name() const80   std::string get_output_type_name() const { return (*method_->response).name; }
81 
get_module_and_message_path_input(grpc::string *,grpc::string,bool,grpc::string) const82   bool get_module_and_message_path_input(grpc::string * /*str*/,
83                                          grpc::string /*generator_file_name*/,
84                                          bool /*generate_in_pb2_grpc*/,
85                                          grpc::string /*import_prefix*/) const {
86     return true;
87   }
88 
get_module_and_message_path_output(grpc::string *,grpc::string,bool,grpc::string) const89   bool get_module_and_message_path_output(
90       grpc::string * /*str*/, grpc::string /*generator_file_name*/,
91       bool /*generate_in_pb2_grpc*/, grpc::string /*import_prefix*/) const {
92     return true;
93   }
94 
get_fb_builder() const95   std::string get_fb_builder() const { return "builder"; }
96 
input_type_name() const97   std::string input_type_name() const { return GRPCType(*method_->request); }
98 
output_type_name() const99   std::string output_type_name() const { return GRPCType(*method_->response); }
100 
NoStreaming() const101   bool NoStreaming() const { return streaming_ == kNone; }
102 
ClientStreaming() const103   bool ClientStreaming() const { return streaming_ == kClient; }
104 
ServerStreaming() const105   bool ServerStreaming() const { return streaming_ == kServer; }
106 
BidiStreaming() const107   bool BidiStreaming() const { return streaming_ == kBiDi; }
108 
109  private:
110   const RPCCall *method_;
111   Streaming streaming_;
112 };
113 
114 class FlatBufService : public grpc_generator::Service {
115  public:
FlatBufService(const ServiceDef * service)116   FlatBufService(const ServiceDef *service) : service_(service) {}
117 
GetLeadingComments(const grpc::string) const118   grpc::string GetLeadingComments(const grpc::string) const { return ""; }
119 
GetTrailingComments(const grpc::string) const120   grpc::string GetTrailingComments(const grpc::string) const { return ""; }
121 
GetAllComments() const122   std::vector<grpc::string> GetAllComments() const {
123     return service_->doc_comment;
124   }
125 
namespace_parts() const126   std::vector<grpc::string> namespace_parts() const {
127     return service_->defined_namespace->components;
128   }
129 
name() const130   std::string name() const { return service_->name; }
is_internal() const131   bool is_internal() const {
132     return service_->Definition::attributes.Lookup("private") ? true : false;
133   }
134 
method_count() const135   int method_count() const {
136     return static_cast<int>(service_->calls.vec.size());
137   }
138 
method(int i) const139   std::unique_ptr<const grpc_generator::Method> method(int i) const {
140     return std::unique_ptr<const grpc_generator::Method>(
141         new FlatBufMethod(service_->calls.vec[i]));
142   }
143 
144  private:
145   const ServiceDef *service_;
146 };
147 
148 class FlatBufPrinter : public grpc_generator::Printer {
149  public:
FlatBufPrinter(std::string * str,const char indentation_type)150   FlatBufPrinter(std::string *str, const char indentation_type)
151       : str_(str),
152         escape_char_('$'),
153         indent_(0),
154         indentation_size_(2),
155         indentation_type_(indentation_type) {}
156 
Print(const std::map<std::string,std::string> & vars,const char * string_template)157   void Print(const std::map<std::string, std::string> &vars,
158              const char *string_template) {
159     std::string s = string_template;
160     // Replace any occurrences of strings in "vars" that are surrounded
161     // by the escape character by what they're mapped to.
162     size_t pos;
163     while ((pos = s.find(escape_char_)) != std::string::npos) {
164       // Found an escape char, must also find the closing one.
165       size_t pos2 = s.find(escape_char_, pos + 1);
166       // If placeholder not closed, ignore.
167       if (pos2 == std::string::npos) break;
168       auto it = vars.find(s.substr(pos + 1, pos2 - pos - 1));
169       // If unknown placeholder, ignore.
170       if (it == vars.end()) break;
171       // Subtitute placeholder.
172       s.replace(pos, pos2 - pos + 1, it->second);
173     }
174     Print(s.c_str());
175   }
176 
Print(const char * s)177   void Print(const char *s) {
178     if (s == nullptr || *s == '\0') { return; }
179     // Add this string, but for each part separated by \n, add indentation.
180     for (;;) {
181       // Current indentation.
182       str_->insert(str_->end(), indent_ * indentation_size_, indentation_type_);
183       // See if this contains more than one line.
184       const char *lf = strchr(s, '\n');
185       if (lf) {
186         (*str_) += std::string(s, lf + 1);
187         s = lf + 1;
188         if (!*s) break;  // Only continue if there's more lines.
189       } else {
190         (*str_) += s;
191         break;
192       }
193     }
194   }
195 
SetIndentationSize(const size_t size)196   void SetIndentationSize(const size_t size) {
197     FLATBUFFERS_ASSERT(str_->empty());
198     indentation_size_ = size;
199   }
200 
Indent()201   void Indent() { indent_++; }
202 
Outdent()203   void Outdent() {
204     FLATBUFFERS_ASSERT(indent_ > 0);
205     indent_--;
206   }
207 
208  private:
209   std::string *str_;
210   char escape_char_;
211   size_t indent_;
212   size_t indentation_size_;
213   char indentation_type_;
214 };
215 
216 class FlatBufFile : public grpc_generator::File {
217  public:
218   enum Language {
219     kLanguageGo,
220     kLanguageCpp,
221     kLanguageJava,
222     kLanguagePython,
223     kLanguageSwift,
224     kLanguageTS
225   };
226 
FlatBufFile(const Parser & parser,const std::string & file_name,Language language)227   FlatBufFile(const Parser &parser, const std::string &file_name,
228               Language language)
229       : parser_(parser), file_name_(file_name), language_(language) {}
230 
231   FlatBufFile &operator=(const FlatBufFile &);
232 
GetLeadingComments(const grpc::string) const233   grpc::string GetLeadingComments(const grpc::string) const { return ""; }
234 
GetTrailingComments(const grpc::string) const235   grpc::string GetTrailingComments(const grpc::string) const { return ""; }
236 
GetAllComments() const237   std::vector<grpc::string> GetAllComments() const {
238     return std::vector<grpc::string>();
239   }
240 
filename() const241   std::string filename() const { return file_name_; }
242 
filename_without_ext() const243   std::string filename_without_ext() const {
244     return StripExtension(file_name_);
245   }
246 
package() const247   std::string package() const {
248     return parser_.current_namespace_->GetFullyQualifiedName("");
249   }
250 
package_parts() const251   std::vector<std::string> package_parts() const {
252     return parser_.current_namespace_->components;
253   }
254 
additional_headers() const255   std::string additional_headers() const {
256     switch (language_) {
257       case kLanguageCpp: {
258         if (!parser_.opts.grpc_additional_headers.empty()) {
259           std::string result = "";
260           for (const std::string &header :
261                parser_.opts.grpc_additional_headers) {
262             if (!result.empty()) result += "\n";
263             result += "#include \"" + header + "\"";
264           }
265           return result;
266         }
267         return "#include \"flatbuffers/grpc.h\"\n";
268       }
269       case kLanguageGo: {
270         return "import \"github.com/google/flatbuffers/go\"";
271       }
272       case kLanguageJava: {
273         return "import com.google.flatbuffers.grpc.FlatbuffersUtils;";
274       }
275       case kLanguagePython: {
276         return "";
277       }
278       case kLanguageSwift: {
279         return "";
280       }
281       case kLanguageTS: {
282         return "";
283       }
284     }
285     return "";
286   }
287 
service_count() const288   int service_count() const {
289     return static_cast<int>(parser_.services_.vec.size());
290   }
291 
service(int i) const292   std::unique_ptr<const grpc_generator::Service> service(int i) const {
293     return std::unique_ptr<const grpc_generator::Service>(
294         new FlatBufService(parser_.services_.vec[i]));
295   }
296 
CreatePrinter(std::string * str,const char indentation_type=' ') const297   std::unique_ptr<grpc_generator::Printer> CreatePrinter(
298       std::string *str, const char indentation_type = ' ') const {
299     return std::unique_ptr<grpc_generator::Printer>(
300         new FlatBufPrinter(str, indentation_type));
301   }
302 
303  private:
304   const Parser &parser_;
305   const std::string &file_name_;
306   const Language language_;
307 };
308 
309 class GoGRPCGenerator : public flatbuffers::BaseGenerator {
310  public:
GoGRPCGenerator(const Parser & parser,const std::string & path,const std::string & file_name)311   GoGRPCGenerator(const Parser &parser, const std::string &path,
312                   const std::string &file_name)
313       : BaseGenerator(parser, path, file_name, "", "" /*Unused*/, "go"),
314         parser_(parser),
315         path_(path),
316         file_name_(file_name) {}
317 
generate()318   bool generate() {
319     FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageGo);
320     grpc_go_generator::Parameters p;
321     p.custom_method_io_type = "flatbuffers.Builder";
322     for (int i = 0; i < file.service_count(); i++) {
323       auto service = file.service(i);
324       const Definition *def = parser_.services_.vec[i];
325       p.package_name = LastNamespacePart(*(def->defined_namespace));
326       p.service_prefix =
327           def->defined_namespace->GetFullyQualifiedName("");  // file.package();
328       std::string output =
329           grpc_go_generator::GenerateServiceSource(&file, service.get(), &p);
330       std::string filename =
331           NamespaceDir(*def->defined_namespace) + def->name + "_grpc.go";
332       if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
333     }
334     return true;
335   }
336 
337  protected:
338   const Parser &parser_;
339   const std::string &path_, &file_name_;
340 };
341 
GenerateGoGRPC(const Parser & parser,const std::string & path,const std::string & file_name)342 bool GenerateGoGRPC(const Parser &parser, const std::string &path,
343                     const std::string &file_name) {
344   int nservices = 0;
345   for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
346        ++it) {
347     if (!(*it)->generated) nservices++;
348   }
349   if (!nservices) return true;
350   return GoGRPCGenerator(parser, path, file_name).generate();
351 }
352 
GenerateCppGRPC(const Parser & parser,const std::string & path,const std::string & file_name)353 bool GenerateCppGRPC(const Parser &parser, const std::string &path,
354                      const std::string &file_name) {
355   const auto &opts = parser.opts;
356   int nservices = 0;
357   for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
358        ++it) {
359     if (!(*it)->generated) nservices++;
360   }
361   if (!nservices) return true;
362 
363   std::string suffix = "";
364   suffix += opts.filename_suffix.empty() ? "_generated" : opts.filename_suffix;
365   suffix += ".";
366   suffix += opts.filename_extension.empty() ? "h" : opts.filename_extension;
367 
368   grpc_cpp_generator::Parameters generator_parameters;
369   // TODO(wvo): make the other parameters in this struct configurable.
370   generator_parameters.use_system_headers = opts.grpc_use_system_headers;
371   generator_parameters.message_header_extension = suffix;
372   generator_parameters.service_header_extension =
373       ".grpc" + opts.grpc_filename_suffix + ".h";
374   generator_parameters.grpc_search_path = opts.grpc_search_path;
375   std::string filename = flatbuffers::StripExtension(parser.file_being_parsed_);
376   if (!opts.keep_prefix) {
377     filename = flatbuffers::StripPath(filename);
378   }
379   FlatBufFile fbfile(parser, filename, FlatBufFile::kLanguageCpp);
380 
381   std::string header_code =
382       grpc_cpp_generator::GetHeaderPrologue(&fbfile, generator_parameters) +
383       grpc_cpp_generator::GetHeaderIncludes(&fbfile, generator_parameters) +
384       grpc_cpp_generator::GetHeaderServices(&fbfile, generator_parameters) +
385       grpc_cpp_generator::GetHeaderEpilogue(&fbfile, generator_parameters);
386 
387   std::string source_code =
388       grpc_cpp_generator::GetSourcePrologue(&fbfile, generator_parameters) +
389       grpc_cpp_generator::GetSourceIncludes(&fbfile, generator_parameters) +
390       grpc_cpp_generator::GetSourceServices(&fbfile, generator_parameters) +
391       grpc_cpp_generator::GetSourceEpilogue(&fbfile, generator_parameters);
392 
393   return flatbuffers::SaveFile(
394              (path + file_name + ".grpc" + opts.grpc_filename_suffix + ".h")
395                  .c_str(),
396              header_code, false) &&
397          flatbuffers::SaveFile(
398              (path + file_name + ".grpc" + opts.grpc_filename_suffix + ".cc")
399                  .c_str(),
400              source_code, false);
401 }
402 
403 class JavaGRPCGenerator : public flatbuffers::BaseGenerator {
404  public:
JavaGRPCGenerator(const Parser & parser,const std::string & path,const std::string & file_name)405   JavaGRPCGenerator(const Parser &parser, const std::string &path,
406                     const std::string &file_name)
407       : BaseGenerator(parser, path, file_name, "", "." /*separator*/, "java") {}
408 
generate()409   bool generate() {
410     FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageJava);
411     grpc_java_generator::Parameters p;
412     for (int i = 0; i < file.service_count(); i++) {
413       auto service = file.service(i);
414       const Definition *def = parser_.services_.vec[i];
415       p.package_name =
416           def->defined_namespace->GetFullyQualifiedName("");  // file.package();
417       std::string output =
418           grpc_java_generator::GenerateServiceSource(&file, service.get(), &p);
419       std::string filename =
420           NamespaceDir(*def->defined_namespace) + def->name + "Grpc.java";
421       if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
422     }
423     return true;
424   }
425 };
426 
GenerateJavaGRPC(const Parser & parser,const std::string & path,const std::string & file_name)427 bool GenerateJavaGRPC(const Parser &parser, const std::string &path,
428                       const std::string &file_name) {
429   int nservices = 0;
430   for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
431        ++it) {
432     if (!(*it)->generated) nservices++;
433   }
434   if (!nservices) return true;
435   return JavaGRPCGenerator(parser, path, file_name).generate();
436 }
437 
GeneratePythonGRPC(const Parser & parser,const std::string & path,const std::string &)438 bool GeneratePythonGRPC(const Parser &parser, const std::string &path,
439                         const std::string & /*file_name*/) {
440   int nservices = 0;
441   for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
442        ++it) {
443     if (!(*it)->generated) nservices++;
444   }
445   if (!nservices) return true;
446 
447   flatbuffers::python::Version version{parser.opts.python_version};
448   if (!version.IsValid()) return false;
449 
450   if (!flatbuffers::python::grpc::Generate(parser, path, version)) {
451     return false;
452   }
453   if (parser.opts.python_typing) {
454     return flatbuffers::python::grpc::GenerateStub(parser, path, version);
455   }
456   return true;
457 }
458 
459 class SwiftGRPCGenerator : public flatbuffers::BaseGenerator {
460  private:
461   CodeWriter code_;
462 
463  public:
SwiftGRPCGenerator(const Parser & parser,const std::string & path,const std::string & filename)464   SwiftGRPCGenerator(const Parser &parser, const std::string &path,
465                      const std::string &filename)
466       : BaseGenerator(parser, path, filename, "", "" /*Unused*/, "swift") {}
467 
generate()468   bool generate() {
469     code_.Clear();
470     code_ += "// Generated GRPC code for FlatBuffers swift!";
471     code_ += grpc_swift_generator::GenerateHeader();
472     FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageSwift);
473     for (int i = 0; i < file.service_count(); i++) {
474       auto service = file.service(i);
475       code_ += grpc_swift_generator::Generate(&file, service.get());
476     }
477     const auto final_code = code_.ToString();
478     const auto filename = GeneratedFileName(path_, file_name_);
479     return SaveFile(filename.c_str(), final_code, false);
480   }
481 
GeneratedFileName(const std::string & path,const std::string & file_name)482   static std::string GeneratedFileName(const std::string &path,
483                                        const std::string &file_name) {
484     return path + file_name + ".grpc.swift";
485   }
486 };
487 
GenerateSwiftGRPC(const Parser & parser,const std::string & path,const std::string & file_name)488 bool GenerateSwiftGRPC(const Parser &parser, const std::string &path,
489                        const std::string &file_name) {
490   int nservices = 0;
491   for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
492        ++it) {
493     if (!(*it)->generated) nservices++;
494   }
495   if (!nservices) return true;
496   return SwiftGRPCGenerator(parser, path, file_name).generate();
497 }
498 
499 class TSGRPCGenerator : public flatbuffers::BaseGenerator {
500  private:
501   CodeWriter code_;
502 
503  public:
TSGRPCGenerator(const Parser & parser,const std::string & path,const std::string & filename)504   TSGRPCGenerator(const Parser &parser, const std::string &path,
505                   const std::string &filename)
506       : BaseGenerator(parser, path, filename, "", "" /*Unused*/, "ts") {}
507 
generate()508   bool generate() {
509     code_.Clear();
510     FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageTS);
511 
512     for (int i = 0; i < file.service_count(); i++) {
513       auto service = file.service(i);
514       code_ += grpc_ts_generator::Generate(&file, service.get(), file_name_);
515       const auto ts_name = GeneratedFileName(path_, file_name_);
516       if (!SaveFile(ts_name.c_str(), code_.ToString(), false)) return false;
517 
518       code_.Clear();
519       code_ += grpc_ts_generator::GenerateInterface(&file, service.get(),
520                                                     file_name_);
521       const auto ts_interface_name = GeneratedFileName(path_, file_name_, true);
522       if (!SaveFile(ts_interface_name.c_str(), code_.ToString(), false))
523         return false;
524     }
525     return true;
526   }
527 
GeneratedFileName(const std::string & path,const std::string & file_name,const bool is_interface=false)528   static std::string GeneratedFileName(const std::string &path,
529                                        const std::string &file_name,
530                                        const bool is_interface = false) {
531     if (is_interface) return path + file_name + "_grpc.d.ts";
532     return path + file_name + "_grpc.js";
533   }
534 };
535 
GenerateTSGRPC(const Parser & parser,const std::string & path,const std::string & file_name)536 bool GenerateTSGRPC(const Parser &parser, const std::string &path,
537                     const std::string &file_name) {
538   int nservices = 0;
539   for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
540        ++it) {
541     if (!(*it)->generated) nservices++;
542   }
543   if (!nservices) return true;
544   return TSGRPCGenerator(parser, path, file_name).generate();
545 }
546 
547 }  // namespace flatbuffers
548 
549 #if defined(_MSC_VER)
550 #  pragma warning(pop)
551 #endif
552