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/python_private_generator.h"
28 #include "src/compiler/swift_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 
GRPCType(const StructDef & sd) const62   std::string GRPCType(const StructDef &sd) const {
63     return "flatbuffers::grpc::Message<" + sd.name + ">";
64   }
65 
get_input_type_name() const66   std::string get_input_type_name() const { return (*method_->request).name; }
67 
get_output_type_name() const68   std::string get_output_type_name() const { return (*method_->response).name; }
69 
get_module_and_message_path_input(grpc::string *,grpc::string,bool,grpc::string) const70   bool get_module_and_message_path_input(grpc::string * /*str*/,
71                                          grpc::string /*generator_file_name*/,
72                                          bool /*generate_in_pb2_grpc*/,
73                                          grpc::string /*import_prefix*/) const {
74     return true;
75   }
76 
get_module_and_message_path_output(grpc::string *,grpc::string,bool,grpc::string) const77   bool get_module_and_message_path_output(
78       grpc::string * /*str*/, grpc::string /*generator_file_name*/,
79       bool /*generate_in_pb2_grpc*/, grpc::string /*import_prefix*/) const {
80     return true;
81   }
82 
get_fb_builder() const83   std::string get_fb_builder() const { return "builder"; }
84 
input_type_name() const85   std::string input_type_name() const { return GRPCType(*method_->request); }
86 
output_type_name() const87   std::string output_type_name() const { return GRPCType(*method_->response); }
88 
NoStreaming() const89   bool NoStreaming() const { return streaming_ == kNone; }
90 
ClientStreaming() const91   bool ClientStreaming() const { return streaming_ == kClient; }
92 
ServerStreaming() const93   bool ServerStreaming() const { return streaming_ == kServer; }
94 
BidiStreaming() const95   bool BidiStreaming() const { return streaming_ == kBiDi; }
96 
97  private:
98   const RPCCall *method_;
99   Streaming streaming_;
100 };
101 
102 class FlatBufService : public grpc_generator::Service {
103  public:
FlatBufService(const ServiceDef * service)104   FlatBufService(const ServiceDef *service) : service_(service) {}
105 
GetLeadingComments(const grpc::string) const106   grpc::string GetLeadingComments(const grpc::string) const { return ""; }
107 
GetTrailingComments(const grpc::string) const108   grpc::string GetTrailingComments(const grpc::string) const { return ""; }
109 
GetAllComments() const110   std::vector<grpc::string> GetAllComments() const {
111     return service_->doc_comment;
112   }
113 
name() const114   std::string name() const { return service_->name; }
115 
method_count() const116   int method_count() const {
117     return static_cast<int>(service_->calls.vec.size());
118   }
119 
method(int i) const120   std::unique_ptr<const grpc_generator::Method> method(int i) const {
121     return std::unique_ptr<const grpc_generator::Method>(
122         new FlatBufMethod(service_->calls.vec[i]));
123   }
124 
125  private:
126   const ServiceDef *service_;
127 };
128 
129 class FlatBufPrinter : public grpc_generator::Printer {
130  public:
FlatBufPrinter(std::string * str)131   FlatBufPrinter(std::string *str) : str_(str), escape_char_('$'), indent_(0) {}
132 
Print(const std::map<std::string,std::string> & vars,const char * string_template)133   void Print(const std::map<std::string, std::string> &vars,
134              const char *string_template) {
135     std::string s = string_template;
136     // Replace any occurrences of strings in "vars" that are surrounded
137     // by the escape character by what they're mapped to.
138     size_t pos;
139     while ((pos = s.find(escape_char_)) != std::string::npos) {
140       // Found an escape char, must also find the closing one.
141       size_t pos2 = s.find(escape_char_, pos + 1);
142       // If placeholder not closed, ignore.
143       if (pos2 == std::string::npos) break;
144       auto it = vars.find(s.substr(pos + 1, pos2 - pos - 1));
145       // If unknown placeholder, ignore.
146       if (it == vars.end()) break;
147       // Subtitute placeholder.
148       s.replace(pos, pos2 - pos + 1, it->second);
149     }
150     Print(s.c_str());
151   }
152 
Print(const char * s)153   void Print(const char *s) {
154     if (s == nullptr || *s == '\0') { return; }
155     // Add this string, but for each part separated by \n, add indentation.
156     for (;;) {
157       // Current indentation.
158       str_->insert(str_->end(), indent_ * 2, ' ');
159       // See if this contains more than one line.
160       const char *lf = strchr(s, '\n');
161       if (lf) {
162         (*str_) += std::string(s, lf + 1);
163         s = lf + 1;
164         if (!*s) break;  // Only continue if there's more lines.
165       } else {
166         (*str_) += s;
167         break;
168       }
169     }
170   }
171 
Indent()172   void Indent() { indent_++; }
173 
Outdent()174   void Outdent() {
175     indent_--;
176     FLATBUFFERS_ASSERT(indent_ >= 0);
177   }
178 
179  private:
180   std::string *str_;
181   char escape_char_;
182   int indent_;
183 };
184 
185 class FlatBufFile : public grpc_generator::File {
186  public:
187   enum Language {
188     kLanguageGo,
189     kLanguageCpp,
190     kLanguageJava,
191     kLanguagePython,
192     kLanguageSwift
193   };
194 
FlatBufFile(const Parser & parser,const std::string & file_name,Language language)195   FlatBufFile(const Parser &parser, const std::string &file_name,
196               Language language)
197       : parser_(parser), file_name_(file_name), language_(language) {}
198 
199   FlatBufFile &operator=(const FlatBufFile &);
200 
GetLeadingComments(const grpc::string) const201   grpc::string GetLeadingComments(const grpc::string) const { return ""; }
202 
GetTrailingComments(const grpc::string) const203   grpc::string GetTrailingComments(const grpc::string) const { return ""; }
204 
GetAllComments() const205   std::vector<grpc::string> GetAllComments() const {
206     return std::vector<grpc::string>();
207   }
208 
filename() const209   std::string filename() const { return file_name_; }
210 
filename_without_ext() const211   std::string filename_without_ext() const {
212     return StripExtension(file_name_);
213   }
214 
message_header_ext() const215   std::string message_header_ext() const { return "_generated.h"; }
216 
service_header_ext() const217   std::string service_header_ext() const { return ".grpc.fb.h"; }
218 
package() const219   std::string package() const {
220     return parser_.current_namespace_->GetFullyQualifiedName("");
221   }
222 
package_parts() const223   std::vector<std::string> package_parts() const {
224     return parser_.current_namespace_->components;
225   }
226 
additional_headers() const227   std::string additional_headers() const {
228     switch (language_) {
229       case kLanguageCpp: {
230         return "#include \"flatbuffers/grpc.h\"\n";
231       }
232       case kLanguageGo: {
233         return "import \"github.com/google/flatbuffers/go\"";
234       }
235       case kLanguageJava: {
236         return "import com.google.flatbuffers.grpc.FlatbuffersUtils;";
237       }
238       case kLanguagePython: {
239         return "";
240       }
241       case kLanguageSwift: {
242         return "";
243       }
244     }
245     return "";
246   }
247 
service_count() const248   int service_count() const {
249     return static_cast<int>(parser_.services_.vec.size());
250   }
251 
service(int i) const252   std::unique_ptr<const grpc_generator::Service> service(int i) const {
253     return std::unique_ptr<const grpc_generator::Service>(
254         new FlatBufService(parser_.services_.vec[i]));
255   }
256 
CreatePrinter(std::string * str) const257   std::unique_ptr<grpc_generator::Printer> CreatePrinter(
258       std::string *str) const {
259     return std::unique_ptr<grpc_generator::Printer>(new FlatBufPrinter(str));
260   }
261 
262  private:
263   const Parser &parser_;
264   const std::string &file_name_;
265   const Language language_;
266 };
267 
268 class GoGRPCGenerator : public flatbuffers::BaseGenerator {
269  public:
GoGRPCGenerator(const Parser & parser,const std::string & path,const std::string & file_name)270   GoGRPCGenerator(const Parser &parser, const std::string &path,
271                   const std::string &file_name)
272       : BaseGenerator(parser, path, file_name, "", "" /*Unused*/, "go"),
273         parser_(parser),
274         path_(path),
275         file_name_(file_name) {}
276 
generate()277   bool generate() {
278     FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageGo);
279     grpc_go_generator::Parameters p;
280     p.custom_method_io_type = "flatbuffers.Builder";
281     for (int i = 0; i < file.service_count(); i++) {
282       auto service = file.service(i);
283       const Definition *def = parser_.services_.vec[i];
284       p.package_name = LastNamespacePart(*(def->defined_namespace));
285       p.service_prefix =
286           def->defined_namespace->GetFullyQualifiedName("");  // file.package();
287       std::string output =
288           grpc_go_generator::GenerateServiceSource(&file, service.get(), &p);
289       std::string filename =
290           NamespaceDir(*def->defined_namespace) + def->name + "_grpc.go";
291       if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
292     }
293     return true;
294   }
295 
296  protected:
297   const Parser &parser_;
298   const std::string &path_, &file_name_;
299 };
300 
GenerateGoGRPC(const Parser & parser,const std::string & path,const std::string & file_name)301 bool GenerateGoGRPC(const Parser &parser, const std::string &path,
302                     const std::string &file_name) {
303   int nservices = 0;
304   for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
305        ++it) {
306     if (!(*it)->generated) nservices++;
307   }
308   if (!nservices) return true;
309   return GoGRPCGenerator(parser, path, file_name).generate();
310 }
311 
GenerateCppGRPC(const Parser & parser,const std::string & path,const std::string & file_name)312 bool GenerateCppGRPC(const Parser &parser, const std::string &path,
313                      const std::string &file_name) {
314   int nservices = 0;
315   for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
316        ++it) {
317     if (!(*it)->generated) nservices++;
318   }
319   if (!nservices) return true;
320 
321   grpc_cpp_generator::Parameters generator_parameters;
322   // TODO(wvo): make the other parameters in this struct configurable.
323   generator_parameters.use_system_headers = true;
324 
325   FlatBufFile fbfile(parser, file_name, FlatBufFile::kLanguageCpp);
326 
327   std::string header_code =
328       grpc_cpp_generator::GetHeaderPrologue(&fbfile, generator_parameters) +
329       grpc_cpp_generator::GetHeaderIncludes(&fbfile, generator_parameters) +
330       grpc_cpp_generator::GetHeaderServices(&fbfile, generator_parameters) +
331       grpc_cpp_generator::GetHeaderEpilogue(&fbfile, generator_parameters);
332 
333   std::string source_code =
334       grpc_cpp_generator::GetSourcePrologue(&fbfile, generator_parameters) +
335       grpc_cpp_generator::GetSourceIncludes(&fbfile, generator_parameters) +
336       grpc_cpp_generator::GetSourceServices(&fbfile, generator_parameters) +
337       grpc_cpp_generator::GetSourceEpilogue(&fbfile, generator_parameters);
338 
339   return flatbuffers::SaveFile((path + file_name + ".grpc.fb.h").c_str(),
340                                header_code, false) &&
341          flatbuffers::SaveFile((path + file_name + ".grpc.fb.cc").c_str(),
342                                source_code, false);
343 }
344 
345 class JavaGRPCGenerator : public flatbuffers::BaseGenerator {
346  public:
JavaGRPCGenerator(const Parser & parser,const std::string & path,const std::string & file_name)347   JavaGRPCGenerator(const Parser &parser, const std::string &path,
348                     const std::string &file_name)
349       : BaseGenerator(parser, path, file_name, "", "." /*separator*/, "java") {}
350 
generate()351   bool generate() {
352     FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageJava);
353     grpc_java_generator::Parameters p;
354     for (int i = 0; i < file.service_count(); i++) {
355       auto service = file.service(i);
356       const Definition *def = parser_.services_.vec[i];
357       p.package_name =
358           def->defined_namespace->GetFullyQualifiedName("");  // file.package();
359       std::string output =
360           grpc_java_generator::GenerateServiceSource(&file, service.get(), &p);
361       std::string filename =
362           NamespaceDir(*def->defined_namespace) + def->name + "Grpc.java";
363       if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
364     }
365     return true;
366   }
367 };
368 
GenerateJavaGRPC(const Parser & parser,const std::string & path,const std::string & file_name)369 bool GenerateJavaGRPC(const Parser &parser, const std::string &path,
370                       const std::string &file_name) {
371   int nservices = 0;
372   for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
373        ++it) {
374     if (!(*it)->generated) nservices++;
375   }
376   if (!nservices) return true;
377   return JavaGRPCGenerator(parser, path, file_name).generate();
378 }
379 
GeneratePythonGRPC(const Parser & parser,const std::string &,const std::string & file_name)380 bool GeneratePythonGRPC(const Parser &parser, const std::string & /*path*/,
381                         const std::string &file_name) {
382   int nservices = 0;
383   for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
384        ++it) {
385     if (!(*it)->generated) nservices++;
386   }
387   if (!nservices) return true;
388 
389   grpc_python_generator::GeneratorConfiguration config;
390   config.grpc_package_root = "grpc";
391   config.beta_package_root = "grpc.beta";
392   config.import_prefix = "";
393 
394   FlatBufFile fbfile(parser, file_name, FlatBufFile::kLanguagePython);
395 
396   grpc_python_generator::PrivateGenerator generator(config, &fbfile);
397 
398   std::string code = generator.GetGrpcServices();
399   std::string namespace_dir;
400   auto &namespaces = parser.namespaces_.back()->components;
401   for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
402     if (it != namespaces.begin()) namespace_dir += kPathSeparator;
403     namespace_dir += *it;
404   }
405 
406   std::string grpc_py_filename =
407       namespace_dir + kPathSeparator + file_name + "_grpc_fb.py";
408   return flatbuffers::SaveFile(grpc_py_filename.c_str(), code, false);
409 }
410 
411 class SwiftGRPCGenerator : public flatbuffers::BaseGenerator {
412  private:
413   CodeWriter code_;
414 
415  public:
SwiftGRPCGenerator(const Parser & parser,const std::string & path,const std::string & filename)416   SwiftGRPCGenerator(const Parser &parser, const std::string &path,
417                      const std::string &filename)
418       : BaseGenerator(parser, path, filename, "", "" /*Unused*/, "swift") {}
419 
generate()420   bool generate() {
421     code_.Clear();
422     code_ += "// Generated GRPC code for FlatBuffers swift!";
423     code_ += grpc_swift_generator::GenerateHeader();
424     FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageSwift);
425     for (int i = 0; i < file.service_count(); i++) {
426       auto service = file.service(i);
427       code_ += grpc_swift_generator::Generate(&file, service.get());
428     }
429     const auto final_code = code_.ToString();
430     const auto filename = GeneratedFileName(path_, file_name_);
431     return SaveFile(filename.c_str(), final_code, false);
432   }
433 
GeneratedFileName(const std::string & path,const std::string & file_name)434   static std::string GeneratedFileName(const std::string &path,
435                                        const std::string &file_name) {
436     return path + file_name + ".grpc.swift";
437   }
438 };
439 
GenerateSwiftGRPC(const Parser & parser,const std::string & path,const std::string & file_name)440 bool GenerateSwiftGRPC(const Parser &parser, const std::string &path,
441                        const std::string &file_name) {
442   int nservices = 0;
443   for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
444        ++it) {
445     if (!(*it)->generated) nservices++;
446   }
447   if (!nservices) return true;
448   return SwiftGRPCGenerator(parser, path, file_name).generate();
449 }
450 
451 }  // namespace flatbuffers
452 
453 #if defined(_MSC_VER)
454 #  pragma warning(pop)
455 #endif
456