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