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