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 #if defined(FLATBUFFERS_CPP98_STL) 25 # include <cctype> 26 #endif // defined(FLATBUFFERS_CPP98_STL) 27 28 namespace flatbuffers { 29 30 // These arrays need to correspond to the IDLOptions::k enum. 31 32 struct LanguageParameters { 33 IDLOptions::Language language; 34 // Whether function names in the language typically start with uppercase. 35 bool first_camel_upper; 36 std::string file_extension; 37 std::string string_type; 38 std::string bool_type; 39 std::string open_curly; 40 std::string accessor_type; 41 std::string const_decl; 42 std::string unsubclassable_decl; 43 std::string enum_decl; 44 std::string enum_separator; 45 std::string getter_prefix; 46 std::string getter_suffix; 47 std::string inheritance_marker; 48 std::string namespace_ident; 49 std::string namespace_begin; 50 std::string namespace_end; 51 std::string set_bb_byteorder; 52 std::string get_bb_position; 53 std::string get_fbb_offset; 54 std::string accessor_prefix; 55 std::string accessor_prefix_static; 56 std::string optional_suffix; 57 std::string includes; 58 std::string class_annotation; 59 std::string generated_type_annotation; 60 CommentConfig comment_config; 61 const FloatConstantGenerator *float_gen; 62 }; 63 64 const LanguageParameters &GetLangParams(IDLOptions::Language lang) { 65 static TypedFloatConstantGenerator CSharpFloatGen( 66 "Double.", "Single.", "NaN", "PositiveInfinity", "NegativeInfinity"); 67 68 static TypedFloatConstantGenerator JavaFloatGen( 69 "Double.", "Float.", "NaN", "POSITIVE_INFINITY", "NEGATIVE_INFINITY"); 70 71 static const LanguageParameters language_parameters[] = { 72 { 73 IDLOptions::kJava, 74 false, 75 ".java", 76 "String", 77 "boolean ", 78 " {\n", 79 "class ", 80 " final ", 81 "final ", 82 "final class ", 83 ";\n", 84 "()", 85 "", 86 " extends ", 87 "package ", 88 ";", 89 "", 90 "_bb.order(ByteOrder.LITTLE_ENDIAN); ", 91 "position()", 92 "offset()", 93 "", 94 "", 95 "", 96 "import java.nio.*;\nimport java.lang.*;\nimport " 97 "java.util.*;\nimport com.google.flatbuffers.*;\n", 98 "\n@SuppressWarnings(\"unused\")\n", 99 "\n (at) javax.annotation.Generated(value=\"flatc\")\n", 100 { 101 "/**", 102 " *", 103 " */", 104 }, 105 &JavaFloatGen 106 }, 107 { 108 IDLOptions::kCSharp, 109 true, 110 ".cs", 111 "string", 112 "bool ", 113 "\n{\n", 114 "struct ", 115 " readonly ", 116 "", 117 "enum ", 118 ",\n", 119 " { get", 120 "} ", 121 " : ", 122 "namespace ", 123 "\n{", 124 "\n}\n", 125 "", 126 "Position", 127 "Offset", 128 "__p.", 129 "Table.", 130 "?", 131 "using global::System;\nusing global::FlatBuffers;\n\n", 132 "", 133 "", 134 { 135 nullptr, 136 "///", 137 nullptr, 138 }, 139 &CSharpFloatGen 140 }, 141 }; 142 143 if (lang == IDLOptions::kJava) { 144 return language_parameters[0]; 145 } else { 146 FLATBUFFERS_ASSERT(lang == IDLOptions::kCSharp); 147 return language_parameters[1]; 148 } 149 } 150 151 namespace general { 152 class GeneralGenerator : public BaseGenerator { 153 public: 154 GeneralGenerator(const Parser &parser, const std::string &path, 155 const std::string &file_name) 156 : BaseGenerator(parser, path, file_name, "", "."), 157 lang_(GetLangParams(parser_.opts.lang)), 158 cur_name_space_(nullptr) {} 159 160 GeneralGenerator &operator=(const GeneralGenerator &); 161 bool generate() { 162 std::string one_file_code; 163 cur_name_space_ = parser_.current_namespace_; 164 165 for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); 166 ++it) { 167 std::string enumcode; 168 auto &enum_def = **it; 169 if (!parser_.opts.one_file) cur_name_space_ = enum_def.defined_namespace; 170 GenEnum(enum_def, &enumcode); 171 if (parser_.opts.one_file) { 172 one_file_code += enumcode; 173 } else { 174 if (!SaveType(enum_def.name, *enum_def.defined_namespace, enumcode, 175 false)) 176 return false; 177 } 178 } 179 180 for (auto it = parser_.structs_.vec.begin(); 181 it != parser_.structs_.vec.end(); ++it) { 182 std::string declcode; 183 auto &struct_def = **it; 184 if (!parser_.opts.one_file) 185 cur_name_space_ = struct_def.defined_namespace; 186 GenStruct(struct_def, &declcode); 187 if (parser_.opts.one_file) { 188 one_file_code += declcode; 189 } else { 190 if (!SaveType(struct_def.name, *struct_def.defined_namespace, declcode, 191 true)) 192 return false; 193 } 194 } 195 196 if (parser_.opts.one_file) { 197 return SaveType(file_name_, *parser_.current_namespace_, one_file_code, 198 true); 199 } 200 return true; 201 } 202 203 // Save out the generated code for a single class while adding 204 // declaration boilerplate. 205 bool SaveType(const std::string &defname, const Namespace &ns, 206 const std::string &classcode, bool needs_includes) const { 207 if (!classcode.length()) return true; 208 209 std::string code; 210 if (lang_.language == IDLOptions::kCSharp) { 211 code = 212 "// <auto-generated>\n" 213 "// " + 214 std::string(FlatBuffersGeneratedWarning()) + 215 "\n" 216 "// </auto-generated>\n\n"; 217 } else { 218 code = "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n"; 219 } 220 221 std::string namespace_name = FullNamespace(".", ns); 222 if (!namespace_name.empty()) { 223 code += lang_.namespace_ident + namespace_name + lang_.namespace_begin; 224 code += "\n\n"; 225 } 226 if (needs_includes) { 227 code += lang_.includes; 228 if (parser_.opts.gen_nullable) { 229 code += "\nimport javax.annotation.Nullable;\n"; 230 } 231 code += lang_.class_annotation; 232 } 233 if (parser_.opts.gen_generated) { 234 code += lang_.generated_type_annotation; 235 } 236 code += classcode; 237 if (!namespace_name.empty()) code += lang_.namespace_end; 238 auto filename = NamespaceDir(ns) + defname + lang_.file_extension; 239 return SaveFile(filename.c_str(), code, false); 240 } 241 242 const Namespace *CurrentNameSpace() const { return cur_name_space_; } 243 244 std::string FunctionStart(char upper) const { 245 return std::string() + (lang_.language == IDLOptions::kJava 246 ? static_cast<char>(tolower(upper)) 247 : upper); 248 } 249 250 std::string GenNullableAnnotation(const Type &t) const { 251 return lang_.language == IDLOptions::kJava && parser_.opts.gen_nullable && 252 !IsScalar(DestinationType(t, true).base_type) 253 ? " @Nullable " 254 : ""; 255 } 256 257 static bool IsEnum(const Type &type) { 258 return type.enum_def != nullptr && IsInteger(type.base_type); 259 } 260 261 std::string GenTypeBasic(const Type &type, bool enableLangOverrides) const { 262 // clang-format off 263 static const char * const java_typename[] = { 264 #define FLATBUFFERS_TD(ENUM, IDLTYPE, \ 265 CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \ 266 #JTYPE, 267 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) 268 #undef FLATBUFFERS_TD 269 }; 270 271 static const char * const csharp_typename[] = { 272 #define FLATBUFFERS_TD(ENUM, IDLTYPE, \ 273 CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \ 274 #NTYPE, 275 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) 276 #undef FLATBUFFERS_TD 277 }; 278 // clang-format on 279 280 if (enableLangOverrides) { 281 if (lang_.language == IDLOptions::kCSharp) { 282 if (IsEnum(type)) return WrapInNameSpace(*type.enum_def); 283 if (type.base_type == BASE_TYPE_STRUCT) { 284 return "Offset<" + WrapInNameSpace(*type.struct_def) + ">"; 285 } 286 } 287 } 288 289 if (lang_.language == IDLOptions::kJava) { 290 return java_typename[type.base_type]; 291 } else { 292 FLATBUFFERS_ASSERT(lang_.language == IDLOptions::kCSharp); 293 return csharp_typename[type.base_type]; 294 } 295 } 296 297 std::string GenTypeBasic(const Type &type) const { 298 return GenTypeBasic(type, true); 299 } 300 301 std::string GenTypePointer(const Type &type) const { 302 switch (type.base_type) { 303 case BASE_TYPE_STRING: return lang_.string_type; 304 case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType()); 305 case BASE_TYPE_STRUCT: return WrapInNameSpace(*type.struct_def); 306 case BASE_TYPE_UNION: 307 // Unions in C# use a generic Table-derived type for better type safety 308 if (lang_.language == IDLOptions::kCSharp) return "TTable"; 309 FLATBUFFERS_FALLTHROUGH(); // else fall thru 310 default: return "Table"; 311 } 312 } 313 314 std::string GenTypeGet(const Type &type) const { 315 return IsScalar(type.base_type) ? GenTypeBasic(type) : GenTypePointer(type); 316 } 317 318 // Find the destination type the user wants to receive the value in (e.g. 319 // one size higher signed types for unsigned serialized values in Java). 320 Type DestinationType(const Type &type, bool vectorelem) const { 321 if (lang_.language != IDLOptions::kJava) return type; 322 switch (type.base_type) { 323 // We use int for both uchar/ushort, since that generally means less 324 // casting than using short for uchar. 325 case BASE_TYPE_UCHAR: return Type(BASE_TYPE_INT); 326 case BASE_TYPE_USHORT: return Type(BASE_TYPE_INT); 327 case BASE_TYPE_UINT: return Type(BASE_TYPE_LONG); 328 case BASE_TYPE_VECTOR: 329 if (vectorelem) return DestinationType(type.VectorType(), vectorelem); 330 FLATBUFFERS_FALLTHROUGH(); // else fall thru 331 default: return type; 332 } 333 } 334 335 std::string GenOffsetType(const StructDef &struct_def) const { 336 if (lang_.language == IDLOptions::kCSharp) { 337 return "Offset<" + WrapInNameSpace(struct_def) + ">"; 338 } else { 339 return "int"; 340 } 341 } 342 343 std::string GenOffsetConstruct(const StructDef &struct_def, 344 const std::string &variable_name) const { 345 if (lang_.language == IDLOptions::kCSharp) { 346 return "new Offset<" + WrapInNameSpace(struct_def) + ">(" + 347 variable_name + ")"; 348 } 349 return variable_name; 350 } 351 352 std::string GenVectorOffsetType() const { 353 if (lang_.language == IDLOptions::kCSharp) { 354 return "VectorOffset"; 355 } else { 356 return "int"; 357 } 358 } 359 360 // Generate destination type name 361 std::string GenTypeNameDest(const Type &type) const { 362 return GenTypeGet(DestinationType(type, true)); 363 } 364 365 // Mask to turn serialized value into destination type value. 366 std::string DestinationMask(const Type &type, bool vectorelem) const { 367 if (lang_.language != IDLOptions::kJava) return ""; 368 switch (type.base_type) { 369 case BASE_TYPE_UCHAR: return " & 0xFF"; 370 case BASE_TYPE_USHORT: return " & 0xFFFF"; 371 case BASE_TYPE_UINT: return " & 0xFFFFFFFFL"; 372 case BASE_TYPE_VECTOR: 373 if (vectorelem) return DestinationMask(type.VectorType(), vectorelem); 374 FLATBUFFERS_FALLTHROUGH(); // else fall thru 375 default: return ""; 376 } 377 } 378 379 // Casts necessary to correctly read serialized data 380 std::string DestinationCast(const Type &type) const { 381 if (type.base_type == BASE_TYPE_VECTOR) { 382 return DestinationCast(type.VectorType()); 383 } else { 384 switch (lang_.language) { 385 case IDLOptions::kJava: 386 // Cast necessary to correctly read serialized unsigned values. 387 if (type.base_type == BASE_TYPE_UINT) return "(long)"; 388 break; 389 390 case IDLOptions::kCSharp: 391 // Cast from raw integral types to enum. 392 if (IsEnum(type)) return "(" + WrapInNameSpace(*type.enum_def) + ")"; 393 break; 394 395 default: break; 396 } 397 } 398 return ""; 399 } 400 401 // Cast statements for mutator method parameters. 402 // In Java, parameters representing unsigned numbers need to be cast down to 403 // their respective type. For example, a long holding an unsigned int value 404 // would be cast down to int before being put onto the buffer. In C#, one cast 405 // directly cast an Enum to its underlying type, which is essential before 406 // putting it onto the buffer. 407 std::string SourceCast(const Type &type, bool castFromDest) const { 408 if (type.base_type == BASE_TYPE_VECTOR) { 409 return SourceCast(type.VectorType(), castFromDest); 410 } else { 411 switch (lang_.language) { 412 case IDLOptions::kJava: 413 if (castFromDest) { 414 if (type.base_type == BASE_TYPE_UINT) 415 return "(int)"; 416 else if (type.base_type == BASE_TYPE_USHORT) 417 return "(short)"; 418 else if (type.base_type == BASE_TYPE_UCHAR) 419 return "(byte)"; 420 } 421 break; 422 case IDLOptions::kCSharp: 423 if (IsEnum(type)) return "(" + GenTypeBasic(type, false) + ")"; 424 break; 425 default: break; 426 } 427 } 428 return ""; 429 } 430 431 std::string SourceCast(const Type &type) const { return SourceCast(type, true); } 432 433 std::string SourceCastBasic(const Type &type, bool castFromDest) const { 434 return IsScalar(type.base_type) ? SourceCast(type, castFromDest) : ""; 435 } 436 437 std::string SourceCastBasic(const Type &type) const { 438 return SourceCastBasic(type, true); 439 } 440 441 std::string GenEnumDefaultValue(const FieldDef &field) const { 442 auto& value = field.value; 443 auto enum_def = value.type.enum_def; 444 auto vec = enum_def->vals.vec; 445 auto default_value = StringToInt(value.constant.c_str()); 446 447 auto result = value.constant; 448 for (auto it = vec.begin(); it != vec.end(); ++it) { 449 auto enum_val = **it; 450 if (enum_val.value == default_value) { 451 result = WrapInNameSpace(*enum_def) + "." + enum_val.name; 452 break; 453 } 454 } 455 456 return result; 457 } 458 459 std::string GenDefaultValue(const FieldDef &field, bool enableLangOverrides) const { 460 auto& value = field.value; 461 if (enableLangOverrides) { 462 // handles both enum case and vector of enum case 463 if (lang_.language == IDLOptions::kCSharp && 464 value.type.enum_def != nullptr && 465 value.type.base_type != BASE_TYPE_UNION) { 466 return GenEnumDefaultValue(field); 467 } 468 } 469 470 auto longSuffix = lang_.language == IDLOptions::kJava ? "L" : ""; 471 switch (value.type.base_type) { 472 case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true"; 473 case BASE_TYPE_ULONG: { 474 if (lang_.language != IDLOptions::kJava) return value.constant; 475 // Converts the ulong into its bits signed equivalent 476 uint64_t defaultValue = StringToUInt(value.constant.c_str()); 477 return NumToString(static_cast<int64_t>(defaultValue)) + longSuffix; 478 } 479 case BASE_TYPE_UINT: 480 case BASE_TYPE_LONG: return value.constant + longSuffix; 481 default: 482 if(IsFloat(value.type.base_type)) 483 return lang_.float_gen->GenFloatConstant(field); 484 else 485 return value.constant; 486 } 487 } 488 489 std::string GenDefaultValue(const FieldDef &field) const { 490 return GenDefaultValue(field, true); 491 } 492 493 std::string GenDefaultValueBasic(const FieldDef &field, 494 bool enableLangOverrides) const { 495 auto& value = field.value; 496 if (!IsScalar(value.type.base_type)) { 497 if (enableLangOverrides) { 498 if (lang_.language == IDLOptions::kCSharp) { 499 switch (value.type.base_type) { 500 case BASE_TYPE_STRING: return "default(StringOffset)"; 501 case BASE_TYPE_STRUCT: 502 return "default(Offset<" + 503 WrapInNameSpace(*value.type.struct_def) + ">)"; 504 case BASE_TYPE_VECTOR: return "default(VectorOffset)"; 505 default: break; 506 } 507 } 508 } 509 return "0"; 510 } 511 return GenDefaultValue(field, enableLangOverrides); 512 } 513 514 std::string GenDefaultValueBasic(const FieldDef &field) const { 515 return GenDefaultValueBasic(field, true); 516 } 517 518 void GenEnum(EnumDef &enum_def, std::string *code_ptr) const { 519 std::string &code = *code_ptr; 520 if (enum_def.generated) return; 521 522 // Generate enum definitions of the form: 523 // public static (final) int name = value; 524 // In Java, we use ints rather than the Enum feature, because we want them 525 // to map directly to how they're used in C/C++ and file formats. 526 // That, and Java Enums are expensive, and not universally liked. 527 GenComment(enum_def.doc_comment, code_ptr, &lang_.comment_config); 528 if (enum_def.attributes.Lookup("private")) { 529 // For Java, we leave the enum unmarked to indicate package-private 530 // For C# we mark the enum as internal 531 if (lang_.language == IDLOptions::kCSharp) { 532 code += "internal "; 533 } 534 } else { 535 code += "public "; 536 } 537 code += lang_.enum_decl + enum_def.name; 538 if (lang_.language == IDLOptions::kCSharp) { 539 code += lang_.inheritance_marker + 540 GenTypeBasic(enum_def.underlying_type, false); 541 } 542 code += lang_.open_curly; 543 if (lang_.language == IDLOptions::kJava) { 544 code += " private " + enum_def.name + "() { }\n"; 545 } 546 for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); 547 ++it) { 548 auto &ev = **it; 549 GenComment(ev.doc_comment, code_ptr, &lang_.comment_config, " "); 550 if (lang_.language != IDLOptions::kCSharp) { 551 code += " public static"; 552 code += lang_.const_decl; 553 code += GenTypeBasic(enum_def.underlying_type, false); 554 } 555 code += " " + ev.name + " = "; 556 code += NumToString(ev.value); 557 code += lang_.enum_separator; 558 } 559 560 // Generate a generate string table for enum values. 561 // We do not do that for C# where this functionality is native. 562 if (lang_.language != IDLOptions::kCSharp) { 563 // Problem is, if values are very sparse that could generate really big 564 // tables. Ideally in that case we generate a map lookup instead, but for 565 // the moment we simply don't output a table at all. 566 auto range = enum_def.vals.vec.back()->value - 567 enum_def.vals.vec.front()->value + 1; 568 // Average distance between values above which we consider a table 569 // "too sparse". Change at will. 570 static const int kMaxSparseness = 5; 571 if (range / static_cast<int64_t>(enum_def.vals.vec.size()) < 572 kMaxSparseness) { 573 code += "\n public static"; 574 code += lang_.const_decl; 575 code += lang_.string_type; 576 code += "[] names = { "; 577 auto val = enum_def.vals.vec.front()->value; 578 for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); 579 ++it) { 580 while (val++ != (*it)->value) code += "\"\", "; 581 code += "\"" + (*it)->name + "\", "; 582 } 583 code += "};\n\n"; 584 code += " public static "; 585 code += lang_.string_type; 586 code += " " + MakeCamel("name", lang_.first_camel_upper); 587 code += "(int e) { return names[e"; 588 if (enum_def.vals.vec.front()->value) 589 code += " - " + enum_def.vals.vec.front()->name; 590 code += "]; }\n"; 591 } 592 } 593 594 // Close the class 595 code += "}"; 596 // Java does not need the closing semi-colon on class definitions. 597 code += (lang_.language != IDLOptions::kJava) ? ";" : ""; 598 code += "\n\n"; 599 } 600 601 // Returns the function name that is able to read a value of the given type. 602 std::string GenGetter(const Type &type) const { 603 switch (type.base_type) { 604 case BASE_TYPE_STRING: return lang_.accessor_prefix + "__string"; 605 case BASE_TYPE_STRUCT: return lang_.accessor_prefix + "__struct"; 606 case BASE_TYPE_UNION: return lang_.accessor_prefix + "__union"; 607 case BASE_TYPE_VECTOR: return GenGetter(type.VectorType()); 608 default: { 609 std::string getter = 610 lang_.accessor_prefix + "bb." + FunctionStart('G') + "et"; 611 if (type.base_type == BASE_TYPE_BOOL) { 612 getter = "0!=" + getter; 613 } else if (GenTypeBasic(type, false) != "byte") { 614 getter += MakeCamel(GenTypeBasic(type, false)); 615 } 616 return getter; 617 } 618 } 619 } 620 621 // Returns the function name that is able to read a value of the given type. 622 std::string GenGetterForLookupByKey(flatbuffers::FieldDef *key_field, 623 const std::string &data_buffer, 624 const char *num = nullptr) const { 625 auto type = key_field->value.type; 626 auto dest_mask = DestinationMask(type, true); 627 auto dest_cast = DestinationCast(type); 628 auto getter = data_buffer + "." + FunctionStart('G') + "et"; 629 if (GenTypeBasic(type, false) != "byte") { 630 getter += MakeCamel(GenTypeBasic(type, false)); 631 } 632 getter = dest_cast + getter + "(" + GenOffsetGetter(key_field, num) + ")" + 633 dest_mask; 634 return getter; 635 } 636 637 // Direct mutation is only allowed for scalar fields. 638 // Hence a setter method will only be generated for such fields. 639 std::string GenSetter(const Type &type) const { 640 if (IsScalar(type.base_type)) { 641 std::string setter = 642 lang_.accessor_prefix + "bb." + FunctionStart('P') + "ut"; 643 if (GenTypeBasic(type, false) != "byte" && 644 type.base_type != BASE_TYPE_BOOL) { 645 setter += MakeCamel(GenTypeBasic(type, false)); 646 } 647 return setter; 648 } else { 649 return ""; 650 } 651 } 652 653 // Returns the method name for use with add/put calls. 654 std::string GenMethod(const Type &type) const { 655 return IsScalar(type.base_type) ? MakeCamel(GenTypeBasic(type, false)) 656 : (IsStruct(type) ? "Struct" : "Offset"); 657 } 658 659 // Recursively generate arguments for a constructor, to deal with nested 660 // structs. 661 void GenStructArgs(const StructDef &struct_def, std::string *code_ptr, 662 const char *nameprefix) const { 663 std::string &code = *code_ptr; 664 for (auto it = struct_def.fields.vec.begin(); 665 it != struct_def.fields.vec.end(); ++it) { 666 auto &field = **it; 667 if (IsStruct(field.value.type)) { 668 // Generate arguments for a struct inside a struct. To ensure names 669 // don't clash, and to make it obvious these arguments are constructing 670 // a nested struct, prefix the name with the field name. 671 GenStructArgs(*field.value.type.struct_def, code_ptr, 672 (nameprefix + (field.name + "_")).c_str()); 673 } else { 674 code += ", "; 675 code += GenTypeBasic(DestinationType(field.value.type, false)); 676 code += " "; 677 code += nameprefix; 678 code += MakeCamel(field.name, lang_.first_camel_upper); 679 } 680 } 681 } 682 683 // Recusively generate struct construction statements of the form: 684 // builder.putType(name); 685 // and insert manual padding. 686 void GenStructBody(const StructDef &struct_def, std::string *code_ptr, 687 const char *nameprefix) const { 688 std::string &code = *code_ptr; 689 code += " builder." + FunctionStart('P') + "rep("; 690 code += NumToString(struct_def.minalign) + ", "; 691 code += NumToString(struct_def.bytesize) + ");\n"; 692 for (auto it = struct_def.fields.vec.rbegin(); 693 it != struct_def.fields.vec.rend(); ++it) { 694 auto &field = **it; 695 if (field.padding) { 696 code += " builder." + FunctionStart('P') + "ad("; 697 code += NumToString(field.padding) + ");\n"; 698 } 699 if (IsStruct(field.value.type)) { 700 GenStructBody(*field.value.type.struct_def, code_ptr, 701 (nameprefix + (field.name + "_")).c_str()); 702 } else { 703 code += " builder." + FunctionStart('P') + "ut"; 704 code += GenMethod(field.value.type) + "("; 705 code += SourceCast(field.value.type); 706 auto argname = 707 nameprefix + MakeCamel(field.name, lang_.first_camel_upper); 708 code += argname; 709 code += ");\n"; 710 } 711 } 712 } 713 714 std::string GenByteBufferLength(const char *bb_name) const { 715 std::string bb_len = bb_name; 716 if (lang_.language == IDLOptions::kCSharp) 717 bb_len += ".Length"; 718 else 719 bb_len += ".capacity()"; 720 return bb_len; 721 } 722 723 std::string GenOffsetGetter(flatbuffers::FieldDef *key_field, 724 const char *num = nullptr) const { 725 std::string key_offset = ""; 726 key_offset += lang_.accessor_prefix_static + "__offset(" + 727 NumToString(key_field->value.offset) + ", "; 728 if (num) { 729 key_offset += num; 730 key_offset += 731 (lang_.language == IDLOptions::kCSharp ? ".Value, builder.DataBuffer)" 732 : ", _bb)"); 733 } else { 734 key_offset += GenByteBufferLength("bb"); 735 key_offset += " - tableOffset, bb)"; 736 } 737 return key_offset; 738 } 739 740 std::string GenLookupKeyGetter(flatbuffers::FieldDef *key_field) const { 741 std::string key_getter = " "; 742 key_getter += "int tableOffset = " + lang_.accessor_prefix_static; 743 key_getter += "__indirect(vectorLocation + 4 * (start + middle)"; 744 key_getter += ", bb);\n "; 745 if (key_field->value.type.base_type == BASE_TYPE_STRING) { 746 key_getter += "int comp = " + lang_.accessor_prefix_static; 747 key_getter += FunctionStart('C') + "ompareStrings("; 748 key_getter += GenOffsetGetter(key_field); 749 key_getter += ", byteKey, bb);\n"; 750 } else { 751 auto get_val = GenGetterForLookupByKey(key_field, "bb"); 752 if (lang_.language == IDLOptions::kCSharp) { 753 key_getter += "int comp = " + get_val + ".CompareTo(key);\n"; 754 } else { 755 key_getter += GenTypeNameDest(key_field->value.type) + " val = "; 756 key_getter += get_val + ";\n"; 757 key_getter += " int comp = val > key ? 1 : val < key ? -1 : 0;\n"; 758 } 759 } 760 return key_getter; 761 } 762 763 std::string GenKeyGetter(flatbuffers::FieldDef *key_field) const { 764 std::string key_getter = ""; 765 auto data_buffer = 766 (lang_.language == IDLOptions::kCSharp) ? "builder.DataBuffer" : "_bb"; 767 if (key_field->value.type.base_type == BASE_TYPE_STRING) { 768 if (lang_.language == IDLOptions::kJava) key_getter += " return "; 769 key_getter += lang_.accessor_prefix_static; 770 key_getter += FunctionStart('C') + "ompareStrings("; 771 key_getter += GenOffsetGetter(key_field, "o1") + ", "; 772 key_getter += GenOffsetGetter(key_field, "o2") + ", " + data_buffer + ")"; 773 if (lang_.language == IDLOptions::kJava) key_getter += ";"; 774 } else { 775 auto field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o1"); 776 if (lang_.language == IDLOptions::kCSharp) { 777 key_getter += field_getter; 778 field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o2"); 779 key_getter += ".CompareTo(" + field_getter + ")"; 780 } else { 781 key_getter += 782 "\n " + GenTypeNameDest(key_field->value.type) + " val_1 = "; 783 key_getter += 784 field_getter + ";\n " + GenTypeNameDest(key_field->value.type); 785 key_getter += " val_2 = "; 786 field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o2"); 787 key_getter += field_getter + ";\n"; 788 key_getter += 789 " return val_1 > val_2 ? 1 : val_1 < val_2 ? -1 : 0;\n "; 790 } 791 } 792 return key_getter; 793 } 794 795 void GenStruct(StructDef &struct_def, std::string *code_ptr) const { 796 if (struct_def.generated) return; 797 std::string &code = *code_ptr; 798 799 // Generate a struct accessor class, with methods of the form: 800 // public type name() { return bb.getType(i + offset); } 801 // or for tables of the form: 802 // public type name() { 803 // int o = __offset(offset); return o != 0 ? bb.getType(o + i) : default; 804 // } 805 GenComment(struct_def.doc_comment, code_ptr, &lang_.comment_config); 806 if (struct_def.attributes.Lookup("private")) { 807 // For Java, we leave the struct unmarked to indicate package-private 808 // For C# we mark the struct as internal 809 if (lang_.language == IDLOptions::kCSharp) { 810 code += "internal "; 811 } 812 } else { 813 code += "public "; 814 } 815 if (lang_.language == IDLOptions::kCSharp && 816 struct_def.attributes.Lookup("csharp_partial")) { 817 // generate a partial class for this C# struct/table 818 code += "partial "; 819 } else { 820 code += lang_.unsubclassable_decl; 821 } 822 code += lang_.accessor_type + struct_def.name; 823 if (lang_.language == IDLOptions::kCSharp) { 824 code += " : IFlatbufferObject"; 825 code += lang_.open_curly; 826 code += " private "; 827 code += struct_def.fixed ? "Struct" : "Table"; 828 code += " __p;\n"; 829 830 if (lang_.language == IDLOptions::kCSharp) { 831 code += " public ByteBuffer ByteBuffer { get { return __p.bb; } }\n"; 832 } 833 834 } else { 835 code += lang_.inheritance_marker; 836 code += struct_def.fixed ? "Struct" : "Table"; 837 code += lang_.open_curly; 838 } 839 if (!struct_def.fixed) { 840 // Generate a special accessor for the table that when used as the root 841 // of a FlatBuffer 842 std::string method_name = 843 FunctionStart('G') + "etRootAs" + struct_def.name; 844 std::string method_signature = 845 " public static " + struct_def.name + " " + method_name; 846 847 // create convenience method that doesn't require an existing object 848 code += method_signature + "(ByteBuffer _bb) "; 849 code += "{ return " + method_name + "(_bb, new " + struct_def.name + 850 "()); }\n"; 851 852 // create method that allows object reuse 853 code += 854 method_signature + "(ByteBuffer _bb, " + struct_def.name + " obj) { "; 855 code += lang_.set_bb_byteorder; 856 code += "return (obj.__assign(_bb." + FunctionStart('G') + "etInt(_bb."; 857 code += lang_.get_bb_position; 858 code += ") + _bb."; 859 code += lang_.get_bb_position; 860 code += ", _bb)); }\n"; 861 if (parser_.root_struct_def_ == &struct_def) { 862 if (parser_.file_identifier_.length()) { 863 // Check if a buffer has the identifier. 864 code += " public static "; 865 code += lang_.bool_type + struct_def.name; 866 code += "BufferHasIdentifier(ByteBuffer _bb) { return "; 867 code += lang_.accessor_prefix_static + "__has_identifier(_bb, \""; 868 code += parser_.file_identifier_; 869 code += "\"); }\n"; 870 } 871 } 872 } 873 // Generate the __init method that sets the field in a pre-existing 874 // accessor object. This is to allow object reuse. 875 code += " public void __init(int _i, ByteBuffer _bb) "; 876 code += "{ " + lang_.accessor_prefix + "bb_pos = _i; "; 877 code += lang_.accessor_prefix + "bb = _bb; "; 878 if (!struct_def.fixed && lang_.language == IDLOptions::kJava) { 879 code += lang_.accessor_prefix + "vtable_start = " + lang_.accessor_prefix + "bb_pos - "; 880 code += lang_.accessor_prefix + "bb." + FunctionStart('G') + "etInt("; 881 code += lang_.accessor_prefix + "bb_pos); " + lang_.accessor_prefix + "vtable_size = "; 882 code += lang_.accessor_prefix + "bb." + FunctionStart('G') + "etShort("; 883 code += lang_.accessor_prefix + "vtable_start); "; 884 } 885 code += "}\n"; 886 code += 887 " public " + struct_def.name + " __assign(int _i, ByteBuffer _bb) "; 888 code += "{ __init(_i, _bb); return this; }\n\n"; 889 for (auto it = struct_def.fields.vec.begin(); 890 it != struct_def.fields.vec.end(); ++it) { 891 auto &field = **it; 892 if (field.deprecated) continue; 893 GenComment(field.doc_comment, code_ptr, &lang_.comment_config, " "); 894 std::string type_name = GenTypeGet(field.value.type); 895 std::string type_name_dest = GenTypeNameDest(field.value.type); 896 std::string conditional_cast = ""; 897 std::string optional = ""; 898 if (lang_.language == IDLOptions::kCSharp && !struct_def.fixed && 899 (field.value.type.base_type == BASE_TYPE_STRUCT || 900 field.value.type.base_type == BASE_TYPE_UNION || 901 (field.value.type.base_type == BASE_TYPE_VECTOR && 902 (field.value.type.element == BASE_TYPE_STRUCT || 903 field.value.type.element == BASE_TYPE_UNION)))) { 904 optional = lang_.optional_suffix; 905 conditional_cast = "(" + type_name_dest + optional + ")"; 906 } 907 std::string dest_mask = DestinationMask(field.value.type, true); 908 std::string dest_cast = DestinationCast(field.value.type); 909 std::string src_cast = SourceCast(field.value.type); 910 std::string method_start = " public " + 911 (field.required ? "" : GenNullableAnnotation(field.value.type)) + 912 type_name_dest + optional + " " + 913 MakeCamel(field.name, lang_.first_camel_upper); 914 std::string obj = lang_.language == IDLOptions::kCSharp 915 ? "(new " + type_name + "())" 916 : "obj"; 917 918 // Most field accessors need to retrieve and test the field offset first, 919 // this is the prefix code for that: 920 auto offset_prefix = " { int o = " + lang_.accessor_prefix + "__offset(" + 921 NumToString(field.value.offset) + 922 "); return o != 0 ? "; 923 // Generate the accessors that don't do object reuse. 924 if (field.value.type.base_type == BASE_TYPE_STRUCT) { 925 // Calls the accessor that takes an accessor object with a new object. 926 if (lang_.language != IDLOptions::kCSharp) { 927 code += method_start + "() { return "; 928 code += MakeCamel(field.name, lang_.first_camel_upper); 929 code += "(new "; 930 code += type_name + "()); }\n"; 931 } 932 } else if (field.value.type.base_type == BASE_TYPE_VECTOR && 933 field.value.type.element == BASE_TYPE_STRUCT) { 934 // Accessors for vectors of structs also take accessor objects, this 935 // generates a variant without that argument. 936 if (lang_.language != IDLOptions::kCSharp) { 937 code += method_start + "(int j) { return "; 938 code += MakeCamel(field.name, lang_.first_camel_upper); 939 code += "(new " + type_name + "(), j); }\n"; 940 } 941 } else if (field.value.type.base_type == BASE_TYPE_UNION || 942 (field.value.type.base_type == BASE_TYPE_VECTOR && 943 field.value.type.VectorType().base_type == BASE_TYPE_UNION)) { 944 if (lang_.language == IDLOptions::kCSharp) { 945 // Union types in C# use generic Table-derived type for better type 946 // safety. 947 method_start += "<TTable>"; 948 type_name = type_name_dest; 949 } 950 } 951 std::string getter = dest_cast + GenGetter(field.value.type); 952 code += method_start; 953 std::string default_cast = ""; 954 // only create default casts for c# scalars or vectors of scalars 955 if (lang_.language == IDLOptions::kCSharp && 956 (IsScalar(field.value.type.base_type) || 957 (field.value.type.base_type == BASE_TYPE_VECTOR && 958 IsScalar(field.value.type.element)))) { 959 // For scalars, default value will be returned by GetDefaultValue(). 960 // If the scalar is an enum, GetDefaultValue() returns an actual c# enum 961 // that doesn't need to be casted. However, default values for enum 962 // elements of vectors are integer literals ("0") and are still casted 963 // for clarity. 964 if (field.value.type.enum_def == nullptr || 965 field.value.type.base_type == BASE_TYPE_VECTOR) { 966 default_cast = "(" + type_name_dest + ")"; 967 } 968 } 969 std::string member_suffix = "; "; 970 if (IsScalar(field.value.type.base_type)) { 971 code += lang_.getter_prefix; 972 member_suffix += lang_.getter_suffix; 973 if (struct_def.fixed) { 974 code += " { return " + getter; 975 code += "(" + lang_.accessor_prefix + "bb_pos + "; 976 code += NumToString(field.value.offset) + ")"; 977 code += dest_mask; 978 } else { 979 code += offset_prefix + getter; 980 code += "(o + " + lang_.accessor_prefix + "bb_pos)" + dest_mask; 981 code += " : " + default_cast; 982 code += GenDefaultValue(field); 983 } 984 } else { 985 switch (field.value.type.base_type) { 986 case BASE_TYPE_STRUCT: 987 if (lang_.language != IDLOptions::kCSharp) { 988 code += "(" + type_name + " obj" + ")"; 989 } else { 990 code += lang_.getter_prefix; 991 member_suffix += lang_.getter_suffix; 992 } 993 if (struct_def.fixed) { 994 code += " { return " + obj + ".__assign(" + lang_.accessor_prefix; 995 code += "bb_pos + " + NumToString(field.value.offset) + ", "; 996 code += lang_.accessor_prefix + "bb)"; 997 } else { 998 code += offset_prefix + conditional_cast; 999 code += obj + ".__assign("; 1000 code += field.value.type.struct_def->fixed 1001 ? "o + " + lang_.accessor_prefix + "bb_pos" 1002 : lang_.accessor_prefix + "__indirect(o + " + 1003 lang_.accessor_prefix + "bb_pos)"; 1004 code += ", " + lang_.accessor_prefix + "bb) : null"; 1005 } 1006 break; 1007 case BASE_TYPE_STRING: 1008 code += lang_.getter_prefix; 1009 member_suffix += lang_.getter_suffix; 1010 code += offset_prefix + getter + "(o + " + lang_.accessor_prefix; 1011 code += "bb_pos) : null"; 1012 break; 1013 case BASE_TYPE_VECTOR: { 1014 auto vectortype = field.value.type.VectorType(); 1015 if (vectortype.base_type == BASE_TYPE_UNION && 1016 lang_.language == IDLOptions::kCSharp) { 1017 conditional_cast = "(TTable?)"; 1018 getter += "<TTable>"; 1019 } 1020 code += "("; 1021 if (vectortype.base_type == BASE_TYPE_STRUCT) { 1022 if (lang_.language != IDLOptions::kCSharp) 1023 code += type_name + " obj, "; 1024 getter = obj + ".__assign"; 1025 } else if (vectortype.base_type == BASE_TYPE_UNION) { 1026 if (lang_.language != IDLOptions::kCSharp) 1027 code += type_name + " obj, "; 1028 } 1029 code += "int j)"; 1030 const auto body = offset_prefix + conditional_cast + getter + "("; 1031 if (vectortype.base_type == BASE_TYPE_UNION) { 1032 if (lang_.language != IDLOptions::kCSharp) 1033 code += body + "obj, "; 1034 else 1035 code += " where TTable : struct, IFlatbufferObject" + body; 1036 } else { 1037 code += body; 1038 } 1039 auto index = lang_.accessor_prefix + "__vector(o) + j * " + 1040 NumToString(InlineSize(vectortype)); 1041 if (vectortype.base_type == BASE_TYPE_STRUCT) { 1042 code += vectortype.struct_def->fixed 1043 ? index 1044 : lang_.accessor_prefix + "__indirect(" + index + ")"; 1045 code += ", " + lang_.accessor_prefix + "bb"; 1046 } else if (vectortype.base_type == BASE_TYPE_UNION) { 1047 code += index + " - bb_pos"; 1048 } else { 1049 code += index; 1050 } 1051 code += ")" + dest_mask + " : "; 1052 1053 code += 1054 field.value.type.element == BASE_TYPE_BOOL 1055 ? "false" 1056 : (IsScalar(field.value.type.element) ? default_cast + "0" 1057 : "null"); 1058 break; 1059 } 1060 case BASE_TYPE_UNION: 1061 if (lang_.language == IDLOptions::kCSharp) { 1062 code += "() where TTable : struct, IFlatbufferObject"; 1063 code += offset_prefix + "(TTable?)" + getter; 1064 code += "<TTable>(o) : null"; 1065 } else { 1066 code += "(" + type_name + " obj)" + offset_prefix + getter; 1067 code += "(obj, o) : null"; 1068 } 1069 break; 1070 default: FLATBUFFERS_ASSERT(0); 1071 } 1072 } 1073 code += member_suffix; 1074 code += "}\n"; 1075 if (field.value.type.base_type == BASE_TYPE_VECTOR) { 1076 code += 1077 " public int " + MakeCamel(field.name, lang_.first_camel_upper); 1078 code += "Length"; 1079 code += lang_.getter_prefix; 1080 code += offset_prefix; 1081 code += lang_.accessor_prefix + "__vector_len(o) : 0; "; 1082 code += lang_.getter_suffix; 1083 code += "}\n"; 1084 // See if we should generate a by-key accessor. 1085 if (field.value.type.element == BASE_TYPE_STRUCT && 1086 !field.value.type.struct_def->fixed) { 1087 auto &sd = *field.value.type.struct_def; 1088 auto &fields = sd.fields.vec; 1089 for (auto kit = fields.begin(); kit != fields.end(); ++kit) { 1090 auto &key_field = **kit; 1091 if (key_field.key) { 1092 auto qualified_name = WrapInNameSpace(sd); 1093 code += " public " + qualified_name + lang_.optional_suffix + " "; 1094 code += MakeCamel(field.name, lang_.first_camel_upper) + "ByKey("; 1095 code += GenTypeNameDest(key_field.value.type) + " key)"; 1096 code += offset_prefix; 1097 code += qualified_name + ".__lookup_by_key("; 1098 if (lang_.language == IDLOptions::kJava) 1099 code += "null, "; 1100 code += lang_.accessor_prefix + "__vector(o), key, "; 1101 code += lang_.accessor_prefix + "bb) : null; "; 1102 code += "}\n"; 1103 if (lang_.language == IDLOptions::kJava) { 1104 code += " public " + qualified_name + lang_.optional_suffix + " "; 1105 code += MakeCamel(field.name, lang_.first_camel_upper) + "ByKey("; 1106 code += qualified_name + lang_.optional_suffix + " obj, "; 1107 code += GenTypeNameDest(key_field.value.type) + " key)"; 1108 code += offset_prefix; 1109 code += qualified_name + ".__lookup_by_key(obj, "; 1110 code += lang_.accessor_prefix + "__vector(o), key, "; 1111 code += lang_.accessor_prefix + "bb) : null; "; 1112 code += "}\n"; 1113 } 1114 break; 1115 } 1116 } 1117 } 1118 } 1119 // Generate a ByteBuffer accessor for strings & vectors of scalars. 1120 if ((field.value.type.base_type == BASE_TYPE_VECTOR && 1121 IsScalar(field.value.type.VectorType().base_type)) || 1122 field.value.type.base_type == BASE_TYPE_STRING) { 1123 switch (lang_.language) { 1124 case IDLOptions::kJava: 1125 code += " public ByteBuffer "; 1126 code += MakeCamel(field.name, lang_.first_camel_upper); 1127 code += "AsByteBuffer() { return "; 1128 code += lang_.accessor_prefix + "__vector_as_bytebuffer("; 1129 code += NumToString(field.value.offset) + ", "; 1130 code += 1131 NumToString(field.value.type.base_type == BASE_TYPE_STRING 1132 ? 1 1133 : InlineSize(field.value.type.VectorType())); 1134 code += "); }\n"; 1135 code += " public ByteBuffer "; 1136 code += MakeCamel(field.name, lang_.first_camel_upper); 1137 code += "InByteBuffer(ByteBuffer _bb) { return "; 1138 code += lang_.accessor_prefix + "__vector_in_bytebuffer(_bb, "; 1139 code += NumToString(field.value.offset) + ", "; 1140 code += 1141 NumToString(field.value.type.base_type == BASE_TYPE_STRING 1142 ? 1 1143 : InlineSize(field.value.type.VectorType())); 1144 code += "); }\n"; 1145 break; 1146 case IDLOptions::kCSharp: 1147 code += "#if ENABLE_SPAN_T\n"; 1148 code += " public Span<byte> Get"; 1149 code += MakeCamel(field.name, lang_.first_camel_upper); 1150 code += "Bytes() { return "; 1151 code += lang_.accessor_prefix + "__vector_as_span("; 1152 code += NumToString(field.value.offset); 1153 code += "); }\n"; 1154 code += "#else\n"; 1155 code += " public ArraySegment<byte>? Get"; 1156 code += MakeCamel(field.name, lang_.first_camel_upper); 1157 code += "Bytes() { return "; 1158 code += lang_.accessor_prefix + "__vector_as_arraysegment("; 1159 code += NumToString(field.value.offset); 1160 code += "); }\n"; 1161 code += "#endif\n"; 1162 1163 // For direct blockcopying the data into a typed array 1164 code += " public "; 1165 code += GenTypeBasic(field.value.type.VectorType()); 1166 code += "[] Get"; 1167 code += MakeCamel(field.name, lang_.first_camel_upper); 1168 code += "Array() { return "; 1169 code += lang_.accessor_prefix + "__vector_as_array<"; 1170 code += GenTypeBasic(field.value.type.VectorType()); 1171 code += ">("; 1172 code += NumToString(field.value.offset); 1173 code += "); }\n"; 1174 break; 1175 default: break; 1176 } 1177 } 1178 // generate object accessors if is nested_flatbuffer 1179 if (field.nested_flatbuffer) { 1180 auto nested_type_name = WrapInNameSpace(*field.nested_flatbuffer); 1181 auto nested_method_name = 1182 MakeCamel(field.name, lang_.first_camel_upper) + "As" + 1183 nested_type_name; 1184 auto get_nested_method_name = nested_method_name; 1185 if (lang_.language == IDLOptions::kCSharp) { 1186 get_nested_method_name = "Get" + nested_method_name; 1187 conditional_cast = 1188 "(" + nested_type_name + lang_.optional_suffix + ")"; 1189 } 1190 if (lang_.language != IDLOptions::kCSharp) { 1191 code += " public " + nested_type_name + lang_.optional_suffix + " "; 1192 code += nested_method_name + "() { return "; 1193 code += 1194 get_nested_method_name + "(new " + nested_type_name + "()); }\n"; 1195 } else { 1196 obj = "(new " + nested_type_name + "())"; 1197 } 1198 code += " public " + nested_type_name + lang_.optional_suffix + " "; 1199 code += get_nested_method_name + "("; 1200 if (lang_.language != IDLOptions::kCSharp) 1201 code += nested_type_name + " obj"; 1202 code += ") { int o = " + lang_.accessor_prefix + "__offset("; 1203 code += NumToString(field.value.offset) + "); "; 1204 code += "return o != 0 ? " + conditional_cast + obj + ".__assign("; 1205 code += lang_.accessor_prefix; 1206 code += "__indirect(" + lang_.accessor_prefix + "__vector(o)), "; 1207 code += lang_.accessor_prefix + "bb) : null; }\n"; 1208 } 1209 // Generate mutators for scalar fields or vectors of scalars. 1210 if (parser_.opts.mutable_buffer) { 1211 auto underlying_type = field.value.type.base_type == BASE_TYPE_VECTOR 1212 ? field.value.type.VectorType() 1213 : field.value.type; 1214 // Boolean parameters have to be explicitly converted to byte 1215 // representation. 1216 auto setter_parameter = underlying_type.base_type == BASE_TYPE_BOOL 1217 ? "(byte)(" + field.name + " ? 1 : 0)" 1218 : field.name; 1219 auto mutator_prefix = MakeCamel("mutate", lang_.first_camel_upper); 1220 // A vector mutator also needs the index of the vector element it should 1221 // mutate. 1222 auto mutator_params = 1223 (field.value.type.base_type == BASE_TYPE_VECTOR ? "(int j, " 1224 : "(") + 1225 GenTypeNameDest(underlying_type) + " " + field.name + ") { "; 1226 auto setter_index = 1227 field.value.type.base_type == BASE_TYPE_VECTOR 1228 ? lang_.accessor_prefix + "__vector(o) + j * " + 1229 NumToString(InlineSize(underlying_type)) 1230 : (struct_def.fixed 1231 ? lang_.accessor_prefix + "bb_pos + " + 1232 NumToString(field.value.offset) 1233 : "o + " + lang_.accessor_prefix + "bb_pos"); 1234 if (IsScalar(field.value.type.base_type) || 1235 (field.value.type.base_type == BASE_TYPE_VECTOR && 1236 IsScalar(field.value.type.VectorType().base_type))) { 1237 code += " public "; 1238 code += struct_def.fixed ? "void " : lang_.bool_type; 1239 code += mutator_prefix + MakeCamel(field.name, true); 1240 code += mutator_params; 1241 if (struct_def.fixed) { 1242 code += GenSetter(underlying_type) + "(" + setter_index + ", "; 1243 code += src_cast + setter_parameter + "); }\n"; 1244 } else { 1245 code += "int o = " + lang_.accessor_prefix + "__offset("; 1246 code += NumToString(field.value.offset) + ");"; 1247 code += " if (o != 0) { " + GenSetter(underlying_type); 1248 code += "(" + setter_index + ", " + src_cast + setter_parameter + 1249 "); return true; } else { return false; } }\n"; 1250 } 1251 } 1252 } 1253 } 1254 code += "\n"; 1255 flatbuffers::FieldDef *key_field = nullptr; 1256 if (struct_def.fixed) { 1257 // create a struct constructor function 1258 code += " public static " + GenOffsetType(struct_def) + " "; 1259 code += FunctionStart('C') + "reate"; 1260 code += struct_def.name + "(FlatBufferBuilder builder"; 1261 GenStructArgs(struct_def, code_ptr, ""); 1262 code += ") {\n"; 1263 GenStructBody(struct_def, code_ptr, ""); 1264 code += " return "; 1265 code += GenOffsetConstruct( 1266 struct_def, "builder." + std::string(lang_.get_fbb_offset)); 1267 code += ";\n }\n"; 1268 } else { 1269 // Generate a method that creates a table in one go. This is only possible 1270 // when the table has no struct fields, since those have to be created 1271 // inline, and there's no way to do so in Java. 1272 bool has_no_struct_fields = true; 1273 int num_fields = 0; 1274 for (auto it = struct_def.fields.vec.begin(); 1275 it != struct_def.fields.vec.end(); ++it) { 1276 auto &field = **it; 1277 if (field.deprecated) continue; 1278 if (IsStruct(field.value.type)) { 1279 has_no_struct_fields = false; 1280 } else { 1281 num_fields++; 1282 } 1283 } 1284 // JVM specifications restrict default constructor params to be < 255. 1285 // Longs and doubles take up 2 units, so we set the limit to be < 127. 1286 if (has_no_struct_fields && num_fields && num_fields < 127) { 1287 // Generate a table constructor of the form: 1288 // public static int createName(FlatBufferBuilder builder, args...) 1289 code += " public static " + GenOffsetType(struct_def) + " "; 1290 code += FunctionStart('C') + "reate" + struct_def.name; 1291 code += "(FlatBufferBuilder builder"; 1292 for (auto it = struct_def.fields.vec.begin(); 1293 it != struct_def.fields.vec.end(); ++it) { 1294 auto &field = **it; 1295 if (field.deprecated) continue; 1296 code += ",\n "; 1297 code += GenTypeBasic(DestinationType(field.value.type, false)); 1298 code += " "; 1299 code += field.name; 1300 if (!IsScalar(field.value.type.base_type)) code += "Offset"; 1301 1302 // Java doesn't have defaults, which means this method must always 1303 // supply all arguments, and thus won't compile when fields are added. 1304 if (lang_.language != IDLOptions::kJava) { 1305 code += " = "; 1306 code += GenDefaultValueBasic(field); 1307 } 1308 } 1309 code += ") {\n builder."; 1310 code += FunctionStart('S') + "tartObject("; 1311 code += NumToString(struct_def.fields.vec.size()) + ");\n"; 1312 for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1; 1313 size; size /= 2) { 1314 for (auto it = struct_def.fields.vec.rbegin(); 1315 it != struct_def.fields.vec.rend(); ++it) { 1316 auto &field = **it; 1317 if (!field.deprecated && 1318 (!struct_def.sortbysize || 1319 size == SizeOf(field.value.type.base_type))) { 1320 code += " " + struct_def.name + "."; 1321 code += FunctionStart('A') + "dd"; 1322 code += MakeCamel(field.name) + "(builder, " + field.name; 1323 if (!IsScalar(field.value.type.base_type)) code += "Offset"; 1324 code += ");\n"; 1325 } 1326 } 1327 } 1328 code += " return " + struct_def.name + "."; 1329 code += FunctionStart('E') + "nd" + struct_def.name; 1330 code += "(builder);\n }\n\n"; 1331 } 1332 // Generate a set of static methods that allow table construction, 1333 // of the form: 1334 // public static void addName(FlatBufferBuilder builder, short name) 1335 // { builder.addShort(id, name, default); } 1336 // Unlike the Create function, these always work. 1337 code += " public static void " + FunctionStart('S') + "tart"; 1338 code += struct_def.name; 1339 code += "(FlatBufferBuilder builder) { builder."; 1340 code += FunctionStart('S') + "tartObject("; 1341 code += NumToString(struct_def.fields.vec.size()) + "); }\n"; 1342 for (auto it = struct_def.fields.vec.begin(); 1343 it != struct_def.fields.vec.end(); ++it) { 1344 auto &field = **it; 1345 if (field.deprecated) continue; 1346 if (field.key) key_field = &field; 1347 code += " public static void " + FunctionStart('A') + "dd"; 1348 code += MakeCamel(field.name); 1349 code += "(FlatBufferBuilder builder, "; 1350 code += GenTypeBasic(DestinationType(field.value.type, false)); 1351 auto argname = MakeCamel(field.name, false); 1352 if (!IsScalar(field.value.type.base_type)) argname += "Offset"; 1353 code += " " + argname + ") { builder." + FunctionStart('A') + "dd"; 1354 code += GenMethod(field.value.type) + "("; 1355 code += NumToString(it - struct_def.fields.vec.begin()) + ", "; 1356 code += SourceCastBasic(field.value.type); 1357 code += argname; 1358 if (!IsScalar(field.value.type.base_type) && 1359 field.value.type.base_type != BASE_TYPE_UNION && 1360 lang_.language == IDLOptions::kCSharp) { 1361 code += ".Value"; 1362 } 1363 code += ", "; 1364 if (lang_.language == IDLOptions::kJava) 1365 code += SourceCastBasic(field.value.type); 1366 code += GenDefaultValue(field, false); 1367 code += "); }\n"; 1368 if (field.value.type.base_type == BASE_TYPE_VECTOR) { 1369 auto vector_type = field.value.type.VectorType(); 1370 auto alignment = InlineAlignment(vector_type); 1371 auto elem_size = InlineSize(vector_type); 1372 if (!IsStruct(vector_type)) { 1373 // Generate a method to create a vector from a Java array. 1374 code += " public static " + GenVectorOffsetType() + " "; 1375 code += FunctionStart('C') + "reate"; 1376 code += MakeCamel(field.name); 1377 code += "Vector(FlatBufferBuilder builder, "; 1378 code += GenTypeBasic(vector_type) + "[] data) "; 1379 code += "{ builder." + FunctionStart('S') + "tartVector("; 1380 code += NumToString(elem_size); 1381 code += ", data." + FunctionStart('L') + "ength, "; 1382 code += NumToString(alignment); 1383 code += "); for (int i = data."; 1384 code += FunctionStart('L') + "ength - 1; i >= 0; i--) builder."; 1385 code += FunctionStart('A') + "dd"; 1386 code += GenMethod(vector_type); 1387 code += "("; 1388 code += SourceCastBasic(vector_type, false); 1389 code += "data[i]"; 1390 if (lang_.language == IDLOptions::kCSharp && 1391 (vector_type.base_type == BASE_TYPE_STRUCT || 1392 vector_type.base_type == BASE_TYPE_STRING)) 1393 code += ".Value"; 1394 code += "); return "; 1395 code += "builder." + FunctionStart('E') + "ndVector(); }\n"; 1396 // For C#, include a block copy method signature. 1397 if (lang_.language == IDLOptions::kCSharp) { 1398 code += " public static " + GenVectorOffsetType() + " "; 1399 code += FunctionStart('C') + "reate"; 1400 code += MakeCamel(field.name); 1401 code += "VectorBlock(FlatBufferBuilder builder, "; 1402 code += GenTypeBasic(vector_type) + "[] data) "; 1403 code += "{ builder." + FunctionStart('S') + "tartVector("; 1404 code += NumToString(elem_size); 1405 code += ", data." + FunctionStart('L') + "ength, "; 1406 code += NumToString(alignment); 1407 code += "); builder.Add(data); return builder.EndVector(); }\n"; 1408 } 1409 } 1410 // Generate a method to start a vector, data to be added manually 1411 // after. 1412 code += " public static void " + FunctionStart('S') + "tart"; 1413 code += MakeCamel(field.name); 1414 code += "Vector(FlatBufferBuilder builder, int numElems) "; 1415 code += "{ builder." + FunctionStart('S') + "tartVector("; 1416 code += NumToString(elem_size); 1417 code += ", numElems, " + NumToString(alignment); 1418 code += "); }\n"; 1419 } 1420 } 1421 code += " public static " + GenOffsetType(struct_def) + " "; 1422 code += FunctionStart('E') + "nd" + struct_def.name; 1423 code += "(FlatBufferBuilder builder) {\n int o = builder."; 1424 code += FunctionStart('E') + "ndObject();\n"; 1425 for (auto it = struct_def.fields.vec.begin(); 1426 it != struct_def.fields.vec.end(); ++it) { 1427 auto &field = **it; 1428 if (!field.deprecated && field.required) { 1429 code += " builder." + FunctionStart('R') + "equired(o, "; 1430 code += NumToString(field.value.offset); 1431 code += "); // " + field.name + "\n"; 1432 } 1433 } 1434 code += " return " + GenOffsetConstruct(struct_def, "o") + ";\n }\n"; 1435 if (parser_.root_struct_def_ == &struct_def) { 1436 std::string size_prefix[] = { "", "SizePrefixed" }; 1437 for (int i = 0; i < 2; ++i) { 1438 code += " public static void "; 1439 code += FunctionStart('F') + "inish" + size_prefix[i] + 1440 struct_def.name; 1441 code += "Buffer(FlatBufferBuilder builder, " + 1442 GenOffsetType(struct_def); 1443 code += " offset) {"; 1444 code += " builder." + FunctionStart('F') + "inish" + size_prefix[i] + 1445 "(offset"; 1446 if (lang_.language == IDLOptions::kCSharp) { code += ".Value"; } 1447 1448 if (parser_.file_identifier_.length()) 1449 code += ", \"" + parser_.file_identifier_ + "\""; 1450 code += "); }\n"; 1451 } 1452 } 1453 } 1454 // Only generate key compare function for table, 1455 // because `key_field` is not set for struct 1456 if (struct_def.has_key && !struct_def.fixed) { 1457 if (lang_.language == IDLOptions::kJava) { 1458 code += "\n @Override\n protected int keysCompare("; 1459 code += "Integer o1, Integer o2, ByteBuffer _bb) {"; 1460 code += GenKeyGetter(key_field); 1461 code += " }\n"; 1462 } else { 1463 code += "\n public static VectorOffset "; 1464 code += "CreateSortedVectorOf" + struct_def.name; 1465 code += "(FlatBufferBuilder builder, "; 1466 code += "Offset<" + struct_def.name + ">"; 1467 code += "[] offsets) {\n"; 1468 code += " Array.Sort(offsets, (Offset<" + struct_def.name + 1469 "> o1, Offset<" + struct_def.name + "> o2) => " + 1470 GenKeyGetter(key_field); 1471 code += ");\n"; 1472 code += " return builder.CreateVectorOfTables(offsets);\n }\n"; 1473 } 1474 1475 code += "\n public static " + struct_def.name + lang_.optional_suffix; 1476 code += " __lookup_by_key("; 1477 if (lang_.language == IDLOptions::kJava) 1478 code += struct_def.name + " obj, "; 1479 code += "int vectorLocation, "; 1480 code += GenTypeNameDest(key_field->value.type); 1481 code += " key, ByteBuffer bb) {\n"; 1482 if (key_field->value.type.base_type == BASE_TYPE_STRING) { 1483 code += " byte[] byteKey = "; 1484 if (lang_.language == IDLOptions::kJava) 1485 code += "key.getBytes(Table.UTF8_CHARSET.get());\n"; 1486 else 1487 code += "System.Text.Encoding.UTF8.GetBytes(key);\n"; 1488 } 1489 code += " int span = "; 1490 code += "bb." + FunctionStart('G') + "etInt(vectorLocation - 4);\n"; 1491 code += " int start = 0;\n"; 1492 code += " while (span != 0) {\n"; 1493 code += " int middle = span / 2;\n"; 1494 code += GenLookupKeyGetter(key_field); 1495 code += " if (comp > 0) {\n"; 1496 code += " span = middle;\n"; 1497 code += " } else if (comp < 0) {\n"; 1498 code += " middle++;\n"; 1499 code += " start += middle;\n"; 1500 code += " span -= middle;\n"; 1501 code += " } else {\n"; 1502 code += " return "; 1503 if (lang_.language == IDLOptions::kJava) 1504 code += "(obj == null ? new " + struct_def.name + "() : obj)"; 1505 else 1506 code += "new " + struct_def.name + "()"; 1507 code += ".__assign(tableOffset, bb);\n"; 1508 code += " }\n }\n"; 1509 code += " return null;\n"; 1510 code += " }\n"; 1511 } 1512 code += "}"; 1513 // Java does not need the closing semi-colon on class definitions. 1514 code += (lang_.language != IDLOptions::kJava) ? ";" : ""; 1515 code += "\n\n"; 1516 } 1517 const LanguageParameters &lang_; 1518 // This tracks the current namespace used to determine if a type need to be 1519 // prefixed by its namespace 1520 const Namespace *cur_name_space_; 1521 }; 1522 } // namespace general 1523 1524 bool GenerateGeneral(const Parser &parser, const std::string &path, 1525 const std::string &file_name) { 1526 general::GeneralGenerator generator(parser, path, file_name); 1527 return generator.generate(); 1528 } 1529 1530 std::string GeneralMakeRule(const Parser &parser, const std::string &path, 1531 const std::string &file_name) { 1532 FLATBUFFERS_ASSERT(parser.opts.lang <= IDLOptions::kMAX); 1533 const auto &lang = GetLangParams(parser.opts.lang); 1534 1535 std::string make_rule; 1536 1537 for (auto it = parser.enums_.vec.begin(); it != parser.enums_.vec.end(); 1538 ++it) { 1539 auto &enum_def = **it; 1540 if (make_rule != "") make_rule += " "; 1541 std::string directory = 1542 BaseGenerator::NamespaceDir(parser, path, *enum_def.defined_namespace); 1543 make_rule += directory + enum_def.name + lang.file_extension; 1544 } 1545 1546 for (auto it = parser.structs_.vec.begin(); it != parser.structs_.vec.end(); 1547 ++it) { 1548 auto &struct_def = **it; 1549 if (make_rule != "") make_rule += " "; 1550 std::string directory = BaseGenerator::NamespaceDir( 1551 parser, path, *struct_def.defined_namespace); 1552 make_rule += directory + struct_def.name + lang.file_extension; 1553 } 1554 1555 make_rule += ": "; 1556 auto included_files = parser.GetIncludedFilesRecursive(file_name); 1557 for (auto it = included_files.begin(); it != included_files.end(); ++it) { 1558 make_rule += " " + *it; 1559 } 1560 return make_rule; 1561 } 1562 1563 std::string BinaryFileName(const Parser &parser, const std::string &path, 1564 const std::string &file_name) { 1565 auto ext = parser.file_extension_.length() ? parser.file_extension_ : "bin"; 1566 return path + file_name + "." + ext; 1567 } 1568 1569 bool GenerateBinary(const Parser &parser, const std::string &path, 1570 const std::string &file_name) { 1571 return !parser.builder_.GetSize() || 1572 flatbuffers::SaveFile( 1573 BinaryFileName(parser, path, file_name).c_str(), 1574 reinterpret_cast<char *>(parser.builder_.GetBufferPointer()), 1575 parser.builder_.GetSize(), true); 1576 } 1577 1578 std::string BinaryMakeRule(const Parser &parser, const std::string &path, 1579 const std::string &file_name) { 1580 if (!parser.builder_.GetSize()) return ""; 1581 std::string filebase = 1582 flatbuffers::StripPath(flatbuffers::StripExtension(file_name)); 1583 std::string make_rule = 1584 BinaryFileName(parser, path, filebase) + ": " + file_name; 1585 auto included_files = 1586 parser.GetIncludedFilesRecursive(parser.root_struct_def_->file); 1587 for (auto it = included_files.begin(); it != included_files.end(); ++it) { 1588 make_rule += " " + *it; 1589 } 1590 return make_rule; 1591 } 1592 1593 } // namespace flatbuffers 1594