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