Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame^] | 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 | |
| 24 | #include "src/compiler/cpp_generator.h" |
| 25 | #include "src/compiler/go_generator.h" |
| 26 | #include "src/compiler/java_generator.h" |
| 27 | |
| 28 | #if defined(_MSC_VER) |
| 29 | # pragma warning(push) |
| 30 | # pragma warning(disable : 4512) // C4512: 'class' : assignment operator could |
| 31 | // not be generated |
| 32 | #endif |
| 33 | |
| 34 | namespace flatbuffers { |
| 35 | |
| 36 | class FlatBufMethod : public grpc_generator::Method { |
| 37 | public: |
| 38 | enum Streaming { |
| 39 | kNone, kClient, kServer, kBiDi |
| 40 | }; |
| 41 | |
| 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 | |
| 52 | grpc::string GetLeadingComments(const grpc::string) const { return ""; } |
| 53 | |
| 54 | grpc::string GetTrailingComments(const grpc::string) const { return ""; } |
| 55 | |
| 56 | std::vector<grpc::string> GetAllComments() const { |
| 57 | return method_->doc_comment; |
| 58 | } |
| 59 | |
| 60 | std::string name() const { return method_->name; } |
| 61 | |
| 62 | std::string GRPCType(const StructDef &sd) const { |
| 63 | return "flatbuffers::grpc::Message<" + sd.name + ">"; |
| 64 | } |
| 65 | |
| 66 | std::string get_input_type_name() const { return (*method_->request).name; } |
| 67 | |
| 68 | std::string get_output_type_name() const { return (*method_->response).name; } |
| 69 | |
| 70 | 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 | |
| 77 | 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 | |
| 83 | std::string input_type_name() const { return GRPCType(*method_->request); } |
| 84 | |
| 85 | std::string output_type_name() const { return GRPCType(*method_->response); } |
| 86 | |
| 87 | bool NoStreaming() const { return streaming_ == kNone; } |
| 88 | |
| 89 | bool ClientStreaming() const { return streaming_ == kClient; } |
| 90 | |
| 91 | bool ServerStreaming() const { return streaming_ == kServer; } |
| 92 | |
| 93 | bool BidiStreaming() const { return streaming_ == kBiDi; } |
| 94 | |
| 95 | private: |
| 96 | const RPCCall *method_; |
| 97 | Streaming streaming_; |
| 98 | }; |
| 99 | |
| 100 | class FlatBufService : public grpc_generator::Service { |
| 101 | public: |
| 102 | FlatBufService(const ServiceDef *service) : service_(service) {} |
| 103 | |
| 104 | grpc::string GetLeadingComments(const grpc::string) const { return ""; } |
| 105 | |
| 106 | grpc::string GetTrailingComments(const grpc::string) const { return ""; } |
| 107 | |
| 108 | std::vector<grpc::string> GetAllComments() const { |
| 109 | return service_->doc_comment; |
| 110 | } |
| 111 | |
| 112 | std::string name() const { return service_->name; } |
| 113 | |
| 114 | int method_count() const { |
| 115 | return static_cast<int>(service_->calls.vec.size()); |
| 116 | } |
| 117 | |
| 118 | std::unique_ptr<const grpc_generator::Method> method(int i) const { |
| 119 | return std::unique_ptr<const grpc_generator::Method>( |
| 120 | new FlatBufMethod(service_->calls.vec[i])); |
| 121 | } |
| 122 | |
| 123 | private: |
| 124 | const ServiceDef *service_; |
| 125 | }; |
| 126 | |
| 127 | class FlatBufPrinter : public grpc_generator::Printer { |
| 128 | public: |
| 129 | FlatBufPrinter(std::string *str) : str_(str), escape_char_('$'), indent_(0) {} |
| 130 | |
| 131 | void Print(const std::map<std::string, std::string> &vars, |
| 132 | const char *string_template) { |
| 133 | std::string s = string_template; |
| 134 | // Replace any occurrences of strings in "vars" that are surrounded |
| 135 | // by the escape character by what they're mapped to. |
| 136 | size_t pos; |
| 137 | while ((pos = s.find(escape_char_)) != std::string::npos) { |
| 138 | // Found an escape char, must also find the closing one. |
| 139 | size_t pos2 = s.find(escape_char_, pos + 1); |
| 140 | // If placeholder not closed, ignore. |
| 141 | if (pos2 == std::string::npos) break; |
| 142 | auto it = vars.find(s.substr(pos + 1, pos2 - pos - 1)); |
| 143 | // If unknown placeholder, ignore. |
| 144 | if (it == vars.end()) break; |
| 145 | // Subtitute placeholder. |
| 146 | s.replace(pos, pos2 - pos + 1, it->second); |
| 147 | } |
| 148 | Print(s.c_str()); |
| 149 | } |
| 150 | |
| 151 | void Print(const char *s) { |
| 152 | if (s == nullptr || *s == '\0') { return; } |
| 153 | // Add this string, but for each part separated by \n, add indentation. |
| 154 | for (;;) { |
| 155 | // Current indentation. |
| 156 | str_->insert(str_->end(), indent_ * 2, ' '); |
| 157 | // See if this contains more than one line. |
| 158 | const char *lf = strchr(s, '\n'); |
| 159 | if (lf) { |
| 160 | (*str_) += std::string(s, lf + 1); |
| 161 | s = lf + 1; |
| 162 | if (!*s) break; // Only continue if there's more lines. |
| 163 | } else { |
| 164 | (*str_) += s; |
| 165 | break; |
| 166 | } |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | void Indent() { indent_++; } |
| 171 | |
| 172 | void Outdent() { |
| 173 | indent_--; |
| 174 | FLATBUFFERS_ASSERT(indent_ >= 0); |
| 175 | } |
| 176 | |
| 177 | private: |
| 178 | std::string *str_; |
| 179 | char escape_char_; |
| 180 | int indent_; |
| 181 | }; |
| 182 | |
| 183 | class FlatBufFile : public grpc_generator::File { |
| 184 | public: |
| 185 | enum Language { |
| 186 | kLanguageGo, kLanguageCpp, kLanguageJava |
| 187 | }; |
| 188 | |
| 189 | FlatBufFile(const Parser &parser, const std::string &file_name, |
| 190 | Language language) |
| 191 | : parser_(parser), file_name_(file_name), language_(language) {} |
| 192 | |
| 193 | FlatBufFile &operator=(const FlatBufFile &); |
| 194 | |
| 195 | grpc::string GetLeadingComments(const grpc::string) const { return ""; } |
| 196 | |
| 197 | grpc::string GetTrailingComments(const grpc::string) const { return ""; } |
| 198 | |
| 199 | std::vector<grpc::string> GetAllComments() const { |
| 200 | return std::vector<grpc::string>(); |
| 201 | } |
| 202 | |
| 203 | std::string filename() const { return file_name_; } |
| 204 | |
| 205 | std::string filename_without_ext() const { |
| 206 | return StripExtension(file_name_); |
| 207 | } |
| 208 | |
| 209 | std::string message_header_ext() const { return "_generated.h"; } |
| 210 | |
| 211 | std::string service_header_ext() const { return ".grpc.fb.h"; } |
| 212 | |
| 213 | std::string package() const { |
| 214 | return parser_.current_namespace_->GetFullyQualifiedName(""); |
| 215 | } |
| 216 | |
| 217 | std::vector<std::string> package_parts() const { |
| 218 | return parser_.current_namespace_->components; |
| 219 | } |
| 220 | |
| 221 | std::string additional_headers() const { |
| 222 | switch (language_) { |
| 223 | case kLanguageCpp: { |
| 224 | return "#include \"flatbuffers/grpc.h\"\n"; |
| 225 | } |
| 226 | case kLanguageGo: { |
| 227 | return "import \"github.com/google/flatbuffers/go\""; |
| 228 | } |
| 229 | case kLanguageJava: { |
| 230 | return "import com.google.flatbuffers.grpc.FlatbuffersUtils;"; |
| 231 | } |
| 232 | } |
| 233 | return ""; |
| 234 | } |
| 235 | |
| 236 | int service_count() const { |
| 237 | return static_cast<int>(parser_.services_.vec.size()); |
| 238 | } |
| 239 | |
| 240 | std::unique_ptr<const grpc_generator::Service> service(int i) const { |
| 241 | return std::unique_ptr<const grpc_generator::Service>( |
| 242 | new FlatBufService(parser_.services_.vec[i])); |
| 243 | } |
| 244 | |
| 245 | std::unique_ptr<grpc_generator::Printer> CreatePrinter( |
| 246 | std::string *str) const { |
| 247 | return std::unique_ptr<grpc_generator::Printer>(new FlatBufPrinter(str)); |
| 248 | } |
| 249 | |
| 250 | private: |
| 251 | const Parser &parser_; |
| 252 | const std::string &file_name_; |
| 253 | const Language language_; |
| 254 | }; |
| 255 | |
| 256 | class GoGRPCGenerator : public flatbuffers::BaseGenerator { |
| 257 | public: |
| 258 | GoGRPCGenerator(const Parser &parser, const std::string &path, |
| 259 | const std::string &file_name) |
| 260 | : BaseGenerator(parser, path, file_name, "", "" /*Unused*/), |
| 261 | parser_(parser), |
| 262 | path_(path), |
| 263 | file_name_(file_name) {} |
| 264 | |
| 265 | bool generate() { |
| 266 | FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageGo); |
| 267 | grpc_go_generator::Parameters p; |
| 268 | p.custom_method_io_type = "flatbuffers.Builder"; |
| 269 | for (int i = 0; i < file.service_count(); i++) { |
| 270 | auto service = file.service(i); |
| 271 | const Definition *def = parser_.services_.vec[i]; |
| 272 | p.package_name = LastNamespacePart(*(def->defined_namespace)); |
| 273 | p.service_prefix = def->defined_namespace->GetFullyQualifiedName(""); // file.package(); |
| 274 | std::string output = |
| 275 | grpc_go_generator::GenerateServiceSource(&file, service.get(), &p); |
| 276 | std::string filename = |
| 277 | NamespaceDir(*def->defined_namespace) + def->name + "_grpc.go"; |
| 278 | if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false; |
| 279 | } |
| 280 | return true; |
| 281 | } |
| 282 | |
| 283 | protected: |
| 284 | const Parser &parser_; |
| 285 | const std::string &path_, &file_name_; |
| 286 | }; |
| 287 | |
| 288 | bool GenerateGoGRPC(const Parser &parser, const std::string &path, |
| 289 | const std::string &file_name) { |
| 290 | int nservices = 0; |
| 291 | for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end(); |
| 292 | ++it) { |
| 293 | if (!(*it)->generated) nservices++; |
| 294 | } |
| 295 | if (!nservices) return true; |
| 296 | return GoGRPCGenerator(parser, path, file_name).generate(); |
| 297 | } |
| 298 | |
| 299 | bool GenerateCppGRPC(const Parser &parser, const std::string &path, |
| 300 | const std::string &file_name) { |
| 301 | int nservices = 0; |
| 302 | for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end(); |
| 303 | ++it) { |
| 304 | if (!(*it)->generated) nservices++; |
| 305 | } |
| 306 | if (!nservices) return true; |
| 307 | |
| 308 | grpc_cpp_generator::Parameters generator_parameters; |
| 309 | // TODO(wvo): make the other parameters in this struct configurable. |
| 310 | generator_parameters.use_system_headers = true; |
| 311 | |
| 312 | FlatBufFile fbfile(parser, file_name, FlatBufFile::kLanguageCpp); |
| 313 | |
| 314 | std::string header_code = |
| 315 | grpc_cpp_generator::GetHeaderPrologue(&fbfile, generator_parameters) + |
| 316 | grpc_cpp_generator::GetHeaderIncludes(&fbfile, generator_parameters) + |
| 317 | grpc_cpp_generator::GetHeaderServices(&fbfile, generator_parameters) + |
| 318 | grpc_cpp_generator::GetHeaderEpilogue(&fbfile, generator_parameters); |
| 319 | |
| 320 | std::string source_code = |
| 321 | grpc_cpp_generator::GetSourcePrologue(&fbfile, generator_parameters) + |
| 322 | grpc_cpp_generator::GetSourceIncludes(&fbfile, generator_parameters) + |
| 323 | grpc_cpp_generator::GetSourceServices(&fbfile, generator_parameters) + |
| 324 | grpc_cpp_generator::GetSourceEpilogue(&fbfile, generator_parameters); |
| 325 | |
| 326 | return flatbuffers::SaveFile((path + file_name + ".grpc.fb.h").c_str(), |
| 327 | header_code, false) && |
| 328 | flatbuffers::SaveFile((path + file_name + ".grpc.fb.cc").c_str(), |
| 329 | source_code, false); |
| 330 | } |
| 331 | |
| 332 | class JavaGRPCGenerator : public flatbuffers::BaseGenerator { |
| 333 | public: |
| 334 | JavaGRPCGenerator(const Parser &parser, const std::string &path, |
| 335 | const std::string &file_name) |
| 336 | : BaseGenerator(parser, path, file_name, "", "." /*separator*/) {} |
| 337 | |
| 338 | bool generate() { |
| 339 | FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageJava); |
| 340 | grpc_java_generator::Parameters p; |
| 341 | for (int i = 0; i < file.service_count(); i++) { |
| 342 | auto service = file.service(i); |
| 343 | const Definition *def = parser_.services_.vec[i]; |
| 344 | p.package_name = |
| 345 | def->defined_namespace->GetFullyQualifiedName(""); // file.package(); |
| 346 | std::string output = |
| 347 | grpc_java_generator::GenerateServiceSource(&file, service.get(), &p); |
| 348 | std::string filename = |
| 349 | NamespaceDir(*def->defined_namespace) + def->name + "Grpc.java"; |
| 350 | if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false; |
| 351 | } |
| 352 | return true; |
| 353 | } |
| 354 | }; |
| 355 | |
| 356 | bool GenerateJavaGRPC(const Parser &parser, const std::string &path, |
| 357 | const std::string &file_name) { |
| 358 | int nservices = 0; |
| 359 | for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end(); |
| 360 | ++it) { |
| 361 | if (!(*it)->generated) nservices++; |
| 362 | } |
| 363 | if (!nservices) return true; |
| 364 | return JavaGRPCGenerator(parser, path, file_name).generate(); |
| 365 | } |
| 366 | |
| 367 | } // namespace flatbuffers |
| 368 | |
| 369 | #if defined(_MSC_VER) |
| 370 | # pragma warning(pop) |
| 371 | #endif |