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 int size)194 void SetIndentationSize(const int size) {
195 FLATBUFFERS_ASSERT(str_->empty());
196 indentation_size_ = size;
197 }
198
Indent()199 void Indent() { indent_++; }
200
Outdent()201 void Outdent() {
202 indent_--;
203 FLATBUFFERS_ASSERT(indent_ >= 0);
204 }
205
206 private:
207 std::string *str_;
208 char escape_char_;
209 int indent_;
210 int 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
message_header_ext() const245 std::string message_header_ext() const { return "_generated.h"; }
246
service_header_ext() const247 std::string service_header_ext() const { return ".grpc.fb.h"; }
248
package() const249 std::string package() const {
250 return parser_.current_namespace_->GetFullyQualifiedName("");
251 }
252
package_parts() const253 std::vector<std::string> package_parts() const {
254 return parser_.current_namespace_->components;
255 }
256
additional_headers() const257 std::string additional_headers() const {
258 switch (language_) {
259 case kLanguageCpp: {
260 return "#include \"flatbuffers/grpc.h\"\n";
261 }
262 case kLanguageGo: {
263 return "import \"github.com/google/flatbuffers/go\"";
264 }
265 case kLanguageJava: {
266 return "import com.google.flatbuffers.grpc.FlatbuffersUtils;";
267 }
268 case kLanguagePython: {
269 return "";
270 }
271 case kLanguageSwift: {
272 return "";
273 }
274 case kLanguageTS: {
275 return "";
276 }
277 }
278 return "";
279 }
280
service_count() const281 int service_count() const {
282 return static_cast<int>(parser_.services_.vec.size());
283 }
284
service(int i) const285 std::unique_ptr<const grpc_generator::Service> service(int i) const {
286 return std::unique_ptr<const grpc_generator::Service>(
287 new FlatBufService(parser_.services_.vec[i]));
288 }
289
CreatePrinter(std::string * str,const char indentation_type=' ') const290 std::unique_ptr<grpc_generator::Printer> CreatePrinter(
291 std::string *str, const char indentation_type = ' ') const {
292 return std::unique_ptr<grpc_generator::Printer>(
293 new FlatBufPrinter(str, indentation_type));
294 }
295
296 private:
297 const Parser &parser_;
298 const std::string &file_name_;
299 const Language language_;
300 };
301
302 class GoGRPCGenerator : public flatbuffers::BaseGenerator {
303 public:
GoGRPCGenerator(const Parser & parser,const std::string & path,const std::string & file_name)304 GoGRPCGenerator(const Parser &parser, const std::string &path,
305 const std::string &file_name)
306 : BaseGenerator(parser, path, file_name, "", "" /*Unused*/, "go"),
307 parser_(parser),
308 path_(path),
309 file_name_(file_name) {}
310
generate()311 bool generate() {
312 FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageGo);
313 grpc_go_generator::Parameters p;
314 p.custom_method_io_type = "flatbuffers.Builder";
315 for (int i = 0; i < file.service_count(); i++) {
316 auto service = file.service(i);
317 const Definition *def = parser_.services_.vec[i];
318 p.package_name = LastNamespacePart(*(def->defined_namespace));
319 p.service_prefix =
320 def->defined_namespace->GetFullyQualifiedName(""); // file.package();
321 std::string output =
322 grpc_go_generator::GenerateServiceSource(&file, service.get(), &p);
323 std::string filename =
324 NamespaceDir(*def->defined_namespace) + def->name + "_grpc.go";
325 if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
326 }
327 return true;
328 }
329
330 protected:
331 const Parser &parser_;
332 const std::string &path_, &file_name_;
333 };
334
GenerateGoGRPC(const Parser & parser,const std::string & path,const std::string & file_name)335 bool GenerateGoGRPC(const Parser &parser, const std::string &path,
336 const std::string &file_name) {
337 int nservices = 0;
338 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
339 ++it) {
340 if (!(*it)->generated) nservices++;
341 }
342 if (!nservices) return true;
343 return GoGRPCGenerator(parser, path, file_name).generate();
344 }
345
GenerateCppGRPC(const Parser & parser,const std::string & path,const std::string & file_name)346 bool GenerateCppGRPC(const Parser &parser, const std::string &path,
347 const std::string &file_name) {
348 int nservices = 0;
349 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
350 ++it) {
351 if (!(*it)->generated) nservices++;
352 }
353 if (!nservices) return true;
354
355 grpc_cpp_generator::Parameters generator_parameters;
356 // TODO(wvo): make the other parameters in this struct configurable.
357 generator_parameters.use_system_headers = true;
358
359 FlatBufFile fbfile(parser, file_name, FlatBufFile::kLanguageCpp);
360
361 std::string header_code =
362 grpc_cpp_generator::GetHeaderPrologue(&fbfile, generator_parameters) +
363 grpc_cpp_generator::GetHeaderIncludes(&fbfile, generator_parameters) +
364 grpc_cpp_generator::GetHeaderServices(&fbfile, generator_parameters) +
365 grpc_cpp_generator::GetHeaderEpilogue(&fbfile, generator_parameters);
366
367 std::string source_code =
368 grpc_cpp_generator::GetSourcePrologue(&fbfile, generator_parameters) +
369 grpc_cpp_generator::GetSourceIncludes(&fbfile, generator_parameters) +
370 grpc_cpp_generator::GetSourceServices(&fbfile, generator_parameters) +
371 grpc_cpp_generator::GetSourceEpilogue(&fbfile, generator_parameters);
372
373 return flatbuffers::SaveFile((path + file_name + ".grpc.fb.h").c_str(),
374 header_code, false) &&
375 flatbuffers::SaveFile((path + file_name + ".grpc.fb.cc").c_str(),
376 source_code, false);
377 }
378
379 class JavaGRPCGenerator : public flatbuffers::BaseGenerator {
380 public:
JavaGRPCGenerator(const Parser & parser,const std::string & path,const std::string & file_name)381 JavaGRPCGenerator(const Parser &parser, const std::string &path,
382 const std::string &file_name)
383 : BaseGenerator(parser, path, file_name, "", "." /*separator*/, "java") {}
384
generate()385 bool generate() {
386 FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageJava);
387 grpc_java_generator::Parameters p;
388 for (int i = 0; i < file.service_count(); i++) {
389 auto service = file.service(i);
390 const Definition *def = parser_.services_.vec[i];
391 p.package_name =
392 def->defined_namespace->GetFullyQualifiedName(""); // file.package();
393 std::string output =
394 grpc_java_generator::GenerateServiceSource(&file, service.get(), &p);
395 std::string filename =
396 NamespaceDir(*def->defined_namespace) + def->name + "Grpc.java";
397 if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
398 }
399 return true;
400 }
401 };
402
GenerateJavaGRPC(const Parser & parser,const std::string & path,const std::string & file_name)403 bool GenerateJavaGRPC(const Parser &parser, const std::string &path,
404 const std::string &file_name) {
405 int nservices = 0;
406 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
407 ++it) {
408 if (!(*it)->generated) nservices++;
409 }
410 if (!nservices) return true;
411 return JavaGRPCGenerator(parser, path, file_name).generate();
412 }
413
414 class PythonGRPCGenerator : public flatbuffers::BaseGenerator {
415 private:
416 CodeWriter code_;
417
418 public:
PythonGRPCGenerator(const Parser & parser,const std::string & filename)419 PythonGRPCGenerator(const Parser &parser, const std::string &filename)
420 : BaseGenerator(parser, "", filename, "", "" /*Unused*/, "swift") {}
421
generate()422 bool generate() {
423 code_.Clear();
424 code_ +=
425 "# Generated by the gRPC Python protocol compiler plugin. "
426 "DO NOT EDIT!\n";
427 code_ += "import grpc\n";
428
429 FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguagePython);
430
431 for (int i = 0; i < file.service_count(); i++) {
432 auto service = file.service(i);
433 code_ += grpc_python_generator::Generate(&file, service.get());
434 }
435 const auto final_code = code_.ToString();
436 const auto filename = GenerateFileName();
437 return SaveFile(filename.c_str(), final_code, false);
438 }
439
GenerateFileName()440 std::string GenerateFileName() {
441 std::string namespace_dir;
442 auto &namespaces = parser_.namespaces_.back()->components;
443 for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
444 if (it != namespaces.begin()) namespace_dir += kPathSeparator;
445 namespace_dir += *it;
446 }
447 std::string grpc_py_filename = namespace_dir;
448 if (!namespace_dir.empty()) grpc_py_filename += kPathSeparator;
449 return grpc_py_filename + file_name_ + "_grpc_fb.py";
450 }
451 };
452
GeneratePythonGRPC(const Parser & parser,const std::string &,const std::string & file_name)453 bool GeneratePythonGRPC(const Parser &parser, const std::string & /*path*/,
454 const std::string &file_name) {
455 int nservices = 0;
456 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
457 ++it) {
458 if (!(*it)->generated) nservices++;
459 }
460 if (!nservices) return true;
461
462 return PythonGRPCGenerator(parser, file_name).generate();
463 }
464
465 class SwiftGRPCGenerator : public flatbuffers::BaseGenerator {
466 private:
467 CodeWriter code_;
468
469 public:
SwiftGRPCGenerator(const Parser & parser,const std::string & path,const std::string & filename)470 SwiftGRPCGenerator(const Parser &parser, const std::string &path,
471 const std::string &filename)
472 : BaseGenerator(parser, path, filename, "", "" /*Unused*/, "swift") {}
473
generate()474 bool generate() {
475 code_.Clear();
476 code_ += "// Generated GRPC code for FlatBuffers swift!";
477 code_ += grpc_swift_generator::GenerateHeader();
478 FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageSwift);
479 for (int i = 0; i < file.service_count(); i++) {
480 auto service = file.service(i);
481 code_ += grpc_swift_generator::Generate(&file, service.get());
482 }
483 const auto final_code = code_.ToString();
484 const auto filename = GeneratedFileName(path_, file_name_);
485 return SaveFile(filename.c_str(), final_code, false);
486 }
487
GeneratedFileName(const std::string & path,const std::string & file_name)488 static std::string GeneratedFileName(const std::string &path,
489 const std::string &file_name) {
490 return path + file_name + ".grpc.swift";
491 }
492 };
493
GenerateSwiftGRPC(const Parser & parser,const std::string & path,const std::string & file_name)494 bool GenerateSwiftGRPC(const Parser &parser, const std::string &path,
495 const std::string &file_name) {
496 int nservices = 0;
497 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
498 ++it) {
499 if (!(*it)->generated) nservices++;
500 }
501 if (!nservices) return true;
502 return SwiftGRPCGenerator(parser, path, file_name).generate();
503 }
504
505 class TSGRPCGenerator : public flatbuffers::BaseGenerator {
506 private:
507 CodeWriter code_;
508
509 public:
TSGRPCGenerator(const Parser & parser,const std::string & path,const std::string & filename)510 TSGRPCGenerator(const Parser &parser, const std::string &path,
511 const std::string &filename)
512 : BaseGenerator(parser, path, filename, "", "" /*Unused*/, "ts") {}
513
generate()514 bool generate() {
515 code_.Clear();
516 FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageTS);
517
518 for (int i = 0; i < file.service_count(); i++) {
519 auto service = file.service(i);
520 code_ += grpc_ts_generator::Generate(&file, service.get(), file_name_);
521 const auto ts_name = GeneratedFileName(path_, file_name_);
522 if (!SaveFile(ts_name.c_str(), code_.ToString(), false)) return false;
523
524 code_.Clear();
525 code_ += grpc_ts_generator::GenerateInterface(&file, service.get(),
526 file_name_);
527 const auto ts_interface_name = GeneratedFileName(path_, file_name_, true);
528 if (!SaveFile(ts_interface_name.c_str(), code_.ToString(), false))
529 return false;
530 }
531 return true;
532 }
533
GeneratedFileName(const std::string & path,const std::string & file_name,const bool is_interface=false)534 static std::string GeneratedFileName(const std::string &path,
535 const std::string &file_name,
536 const bool is_interface = false) {
537 if (is_interface) return path + file_name + "_grpc.d.ts";
538 return path + file_name + "_grpc.js";
539 }
540 };
541
GenerateTSGRPC(const Parser & parser,const std::string & path,const std::string & file_name)542 bool GenerateTSGRPC(const Parser &parser, const std::string &path,
543 const std::string &file_name) {
544 int nservices = 0;
545 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
546 ++it) {
547 if (!(*it)->generated) nservices++;
548 }
549 if (!nservices) return true;
550 return TSGRPCGenerator(parser, path, file_name).generate();
551 }
552
553 } // namespace flatbuffers
554
555 #if defined(_MSC_VER)
556 # pragma warning(pop)
557 #endif
558