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 
GetLangParams(IDLOptions::Language lang)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@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:
GeneralGenerator(const Parser & parser,const std::string & path,const std::string & file_name)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 &);
generate()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.
SaveType(const std::string & defname,const Namespace & ns,const std::string & classcode,bool needs_includes) const205   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 
CurrentNameSpace() const242   const Namespace *CurrentNameSpace() const { return cur_name_space_; }
243 
FunctionStart(char upper) const244   std::string FunctionStart(char upper) const {
245     return std::string() + (lang_.language == IDLOptions::kJava
246                                 ? static_cast<char>(tolower(upper))
247                                 : upper);
248   }
249 
GenNullableAnnotation(const Type & t) const250   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 
GenTypeBasic(const Type & type,bool enableLangOverrides) const257   std::string GenTypeBasic(const Type &type, bool enableLangOverrides) const {
258     // clang-format off
259   static const char * const java_typename[] = {
260     #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
261         CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
262         #JTYPE,
263       FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
264     #undef FLATBUFFERS_TD
265   };
266 
267   static const char * const csharp_typename[] = {
268     #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
269         CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
270         #NTYPE,
271       FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
272     #undef FLATBUFFERS_TD
273   };
274     // clang-format on
275 
276     if (enableLangOverrides) {
277       if (lang_.language == IDLOptions::kCSharp) {
278         if (IsEnum(type)) return WrapInNameSpace(*type.enum_def);
279         if (type.base_type == BASE_TYPE_STRUCT) {
280           return "Offset<" + WrapInNameSpace(*type.struct_def) + ">";
281         }
282       }
283     }
284 
285     if (lang_.language == IDLOptions::kJava) {
286       return java_typename[type.base_type];
287     } else {
288       FLATBUFFERS_ASSERT(lang_.language == IDLOptions::kCSharp);
289       return csharp_typename[type.base_type];
290     }
291   }
292 
GenTypeBasic(const Type & type) const293   std::string GenTypeBasic(const Type &type) const {
294     return GenTypeBasic(type, true);
295   }
296 
GenTypePointer(const Type & type) const297   std::string GenTypePointer(const Type &type) const {
298     switch (type.base_type) {
299       case BASE_TYPE_STRING: return lang_.string_type;
300       case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType());
301       case BASE_TYPE_STRUCT: return WrapInNameSpace(*type.struct_def);
302       case BASE_TYPE_UNION:
303         // Unions in C# use a generic Table-derived type for better type safety
304         if (lang_.language == IDLOptions::kCSharp) return "TTable";
305         FLATBUFFERS_FALLTHROUGH();  // else fall thru
306       default: return "Table";
307     }
308   }
309 
GenTypeGet(const Type & type) const310   std::string GenTypeGet(const Type &type) const {
311     return IsScalar(type.base_type)
312                ? GenTypeBasic(type)
313                : (IsArray(type) ? GenTypeGet(type.VectorType())
314                                 : GenTypePointer(type));
315   }
316 
317   // Find the destination type the user wants to receive the value in (e.g.
318   // one size higher signed types for unsigned serialized values in Java).
DestinationType(const Type & type,bool vectorelem) const319   Type DestinationType(const Type &type, bool vectorelem) const {
320     if (lang_.language != IDLOptions::kJava) return type;
321     switch (type.base_type) {
322       // We use int for both uchar/ushort, since that generally means less
323       // casting than using short for uchar.
324       case BASE_TYPE_UCHAR: return Type(BASE_TYPE_INT);
325       case BASE_TYPE_USHORT: return Type(BASE_TYPE_INT);
326       case BASE_TYPE_UINT: return Type(BASE_TYPE_LONG);
327       case BASE_TYPE_ARRAY:
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 
GenOffsetType(const StructDef & struct_def) const335   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 
GenOffsetConstruct(const StructDef & struct_def,const std::string & variable_name) const343   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 
GenVectorOffsetType() const352   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
GenTypeNameDest(const Type & type) const361   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.
DestinationMask(const Type & type,bool vectorelem) const366   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
DestinationCast(const Type & type) const380   std::string DestinationCast(const Type &type) const {
381     if (IsSeries(type)) {
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.
SourceCast(const Type & type,bool castFromDest) const407   std::string SourceCast(const Type &type, bool castFromDest) const {
408     if (IsSeries(type)) {
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 
SourceCast(const Type & type) const431   std::string SourceCast(const Type &type) const { return SourceCast(type, true); }
432 
SourceCastBasic(const Type & type,bool castFromDest) const433   std::string SourceCastBasic(const Type &type, bool castFromDest) const {
434     return IsScalar(type.base_type) ? SourceCast(type, castFromDest) : "";
435   }
436 
SourceCastBasic(const Type & type) const437   std::string SourceCastBasic(const Type &type) const {
438     return SourceCastBasic(type, true);
439   }
440 
GenEnumDefaultValue(const FieldDef & field) const441   std::string GenEnumDefaultValue(const FieldDef &field) const {
442     auto &value = field.value;
443     FLATBUFFERS_ASSERT(value.type.enum_def);
444     auto &enum_def = *value.type.enum_def;
445     auto enum_val = enum_def.FindByValue(value.constant);
446     return enum_val ? (WrapInNameSpace(enum_def) + "." + enum_val->name)
447                     : value.constant;
448   }
449 
GenDefaultValue(const FieldDef & field,bool enableLangOverrides) const450   std::string GenDefaultValue(const FieldDef &field, bool enableLangOverrides) const {
451     auto& value = field.value;
452     if (enableLangOverrides) {
453       // handles both enum case and vector of enum case
454       if (lang_.language == IDLOptions::kCSharp &&
455           value.type.enum_def != nullptr &&
456           value.type.base_type != BASE_TYPE_UNION) {
457         return GenEnumDefaultValue(field);
458       }
459     }
460 
461     auto longSuffix = lang_.language == IDLOptions::kJava ? "L" : "";
462     switch (value.type.base_type) {
463       case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
464       case BASE_TYPE_ULONG: {
465         if (lang_.language != IDLOptions::kJava) return value.constant;
466         // Converts the ulong into its bits signed equivalent
467         uint64_t defaultValue = StringToUInt(value.constant.c_str());
468         return NumToString(static_cast<int64_t>(defaultValue)) + longSuffix;
469       }
470       case BASE_TYPE_UINT:
471       case BASE_TYPE_LONG: return value.constant + longSuffix;
472       default:
473         if(IsFloat(value.type.base_type))
474           return lang_.float_gen->GenFloatConstant(field);
475         else
476           return value.constant;
477     }
478   }
479 
GenDefaultValue(const FieldDef & field) const480   std::string GenDefaultValue(const FieldDef &field) const {
481     return GenDefaultValue(field, true);
482   }
483 
GenDefaultValueBasic(const FieldDef & field,bool enableLangOverrides) const484   std::string GenDefaultValueBasic(const FieldDef &field,
485                                    bool enableLangOverrides) const {
486     auto& value = field.value;
487     if (!IsScalar(value.type.base_type)) {
488       if (enableLangOverrides) {
489         if (lang_.language == IDLOptions::kCSharp) {
490           switch (value.type.base_type) {
491             case BASE_TYPE_STRING: return "default(StringOffset)";
492             case BASE_TYPE_STRUCT:
493               return "default(Offset<" +
494                      WrapInNameSpace(*value.type.struct_def) + ">)";
495             case BASE_TYPE_VECTOR: return "default(VectorOffset)";
496             default: break;
497           }
498         }
499       }
500       return "0";
501     }
502     return GenDefaultValue(field, enableLangOverrides);
503   }
504 
GenDefaultValueBasic(const FieldDef & field) const505   std::string GenDefaultValueBasic(const FieldDef &field) const {
506     return GenDefaultValueBasic(field, true);
507   }
508 
GenEnum(EnumDef & enum_def,std::string * code_ptr) const509   void GenEnum(EnumDef &enum_def, std::string *code_ptr) const {
510     std::string &code = *code_ptr;
511     if (enum_def.generated) return;
512 
513     // Generate enum definitions of the form:
514     // public static (final) int name = value;
515     // In Java, we use ints rather than the Enum feature, because we want them
516     // to map directly to how they're used in C/C++ and file formats.
517     // That, and Java Enums are expensive, and not universally liked.
518     GenComment(enum_def.doc_comment, code_ptr, &lang_.comment_config);
519 
520     // In C# this indicates enumeration values can be treated as bit flags.
521     if (lang_.language == IDLOptions::kCSharp && enum_def.attributes.Lookup("bit_flags")) {
522       code += "[System.FlagsAttribute]\n";
523     }
524     if (enum_def.attributes.Lookup("private")) {
525       // For Java, we leave the enum unmarked to indicate package-private
526       // For C# we mark the enum as internal
527       if (lang_.language == IDLOptions::kCSharp) {
528         code += "internal ";
529       }
530     } else {
531       code += "public ";
532     }
533     code += lang_.enum_decl + enum_def.name;
534     if (lang_.language == IDLOptions::kCSharp) {
535       code += lang_.inheritance_marker +
536               GenTypeBasic(enum_def.underlying_type, false);
537     }
538     code += lang_.open_curly;
539     if (lang_.language == IDLOptions::kJava) {
540       code += "  private " + enum_def.name + "() { }\n";
541     }
542     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
543       auto &ev = **it;
544       GenComment(ev.doc_comment, code_ptr, &lang_.comment_config, "  ");
545       if (lang_.language != IDLOptions::kCSharp) {
546         code += "  public static";
547         code += lang_.const_decl;
548         code += GenTypeBasic(enum_def.underlying_type, false);
549       }
550       code += (lang_.language == IDLOptions::kJava) ? " " : "  ";
551       code += ev.name + " = ";
552       code += enum_def.ToString(ev);
553       code += lang_.enum_separator;
554     }
555 
556     // Generate a generate string table for enum values.
557     // We do not do that for C# where this functionality is native.
558     if (lang_.language != IDLOptions::kCSharp) {
559       // Problem is, if values are very sparse that could generate really big
560       // tables. Ideally in that case we generate a map lookup instead, but for
561       // the moment we simply don't output a table at all.
562       auto range = enum_def.Distance();
563       // Average distance between values above which we consider a table
564       // "too sparse". Change at will.
565       static const uint64_t kMaxSparseness = 5;
566       if (range / static_cast<uint64_t>(enum_def.size()) < kMaxSparseness) {
567         code += "\n  public static";
568         code += lang_.const_decl;
569         code += lang_.string_type;
570         code += "[] names = { ";
571         auto val = enum_def.Vals().front();
572         for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
573              ++it) {
574           auto ev = *it;
575           for (auto k = enum_def.Distance(val, ev); k > 1; --k)
576             code += "\"\", ";
577           val = ev;
578           code += "\"" + (*it)->name + "\", ";
579         }
580         code += "};\n\n";
581         code += "  public static ";
582         code += lang_.string_type;
583         code += " " + MakeCamel("name", lang_.first_camel_upper);
584         code += "(int e) { return names[e";
585         if (enum_def.MinValue()->IsNonZero())
586           code += " - " + enum_def.MinValue()->name;
587         code += "]; }\n";
588       }
589     }
590 
591     // Close the class
592     code += "}";
593     // Java does not need the closing semi-colon on class definitions.
594     code += (lang_.language != IDLOptions::kJava) ? ";" : "";
595     code += "\n\n";
596   }
597 
598   // Returns the function name that is able to read a value of the given type.
GenGetter(const Type & type) const599   std::string GenGetter(const Type &type) const {
600     switch (type.base_type) {
601       case BASE_TYPE_STRING: return lang_.accessor_prefix + "__string";
602       case BASE_TYPE_STRUCT: return lang_.accessor_prefix + "__struct";
603       case BASE_TYPE_UNION: return lang_.accessor_prefix + "__union";
604       case BASE_TYPE_VECTOR: return GenGetter(type.VectorType());
605       case BASE_TYPE_ARRAY: return GenGetter(type.VectorType());
606       default: {
607         std::string getter =
608             lang_.accessor_prefix + "bb." + FunctionStart('G') + "et";
609         if (type.base_type == BASE_TYPE_BOOL) {
610           getter = "0!=" + getter;
611         } else if (GenTypeBasic(type, false) != "byte") {
612           getter += MakeCamel(GenTypeBasic(type, false));
613         }
614         return getter;
615       }
616     }
617   }
618 
619   // Returns the function name that is able to read a value of the given type.
GenGetterForLookupByKey(flatbuffers::FieldDef * key_field,const std::string & data_buffer,const char * num=nullptr) const620   std::string GenGetterForLookupByKey(flatbuffers::FieldDef *key_field,
621                                       const std::string &data_buffer,
622                                       const char *num = nullptr) const {
623     auto type = key_field->value.type;
624     auto dest_mask = DestinationMask(type, true);
625     auto dest_cast = DestinationCast(type);
626     auto getter = data_buffer + "." + FunctionStart('G') + "et";
627     if (GenTypeBasic(type, false) != "byte") {
628       getter += MakeCamel(GenTypeBasic(type, false));
629     }
630     getter = dest_cast + getter + "(" + GenOffsetGetter(key_field, num) + ")" +
631              dest_mask;
632     return getter;
633   }
634 
635   // Direct mutation is only allowed for scalar fields.
636   // Hence a setter method will only be generated for such fields.
GenSetter(const Type & type) const637   std::string GenSetter(const Type &type) const {
638     if (IsScalar(type.base_type)) {
639       std::string setter =
640           lang_.accessor_prefix + "bb." + FunctionStart('P') + "ut";
641       if (GenTypeBasic(type, false) != "byte" &&
642           type.base_type != BASE_TYPE_BOOL) {
643         setter += MakeCamel(GenTypeBasic(type, false));
644       }
645       return setter;
646     } else {
647       return "";
648     }
649   }
650 
651   // Returns the method name for use with add/put calls.
GenMethod(const Type & type) const652   std::string GenMethod(const Type &type) const {
653     return IsScalar(type.base_type) ? MakeCamel(GenTypeBasic(type, false))
654                                     : (IsStruct(type) ? "Struct" : "Offset");
655   }
656 
657   // Recursively generate arguments for a constructor, to deal with nested
658   // structs.
GenStructArgs(const StructDef & struct_def,std::string * code_ptr,const char * nameprefix,size_t array_count=0) const659   void GenStructArgs(const StructDef &struct_def, std::string *code_ptr,
660                      const char *nameprefix, size_t array_count = 0) const {
661     std::string &code = *code_ptr;
662     for (auto it = struct_def.fields.vec.begin();
663          it != struct_def.fields.vec.end(); ++it) {
664       auto &field = **it;
665       const auto &field_type = field.value.type;
666       const auto array_field = IsArray(field_type);
667       const auto &type = array_field ? field_type.VectorType()
668                                      : DestinationType(field_type, false);
669       const auto array_cnt = array_field ? (array_count + 1) : array_count;
670       if (IsStruct(type)) {
671         // Generate arguments for a struct inside a struct. To ensure names
672         // don't clash, and to make it obvious these arguments are constructing
673         // a nested struct, prefix the name with the field name.
674         GenStructArgs(*field_type.struct_def, code_ptr,
675                       (nameprefix + (field.name + "_")).c_str(), array_cnt);
676       } else {
677         code += ", ";
678         code += GenTypeBasic(type);
679         if (lang_.language == IDLOptions::kJava) {
680           for (size_t i = 0; i < array_cnt; i++) code += "[]";
681         } else if (lang_.language == IDLOptions::kCSharp) {
682           if (array_cnt > 0) {
683             code += "[";
684             for (size_t i = 1; i < array_cnt; i++) code += ",";
685             code += "]";
686           }
687         } else {
688           FLATBUFFERS_ASSERT(0);
689         }
690         code += " ";
691         code += nameprefix;
692         code += MakeCamel(field.name, lang_.first_camel_upper);
693       }
694     }
695   }
696 
697   // Recusively generate struct construction statements of the form:
698   // builder.putType(name);
699   // and insert manual padding.
GenStructBody(const StructDef & struct_def,std::string * code_ptr,const char * nameprefix,size_t index=0,bool in_array=false) const700   void GenStructBody(const StructDef &struct_def, std::string *code_ptr,
701                      const char *nameprefix, size_t index = 0,
702                      bool in_array = false) const {
703     std::string &code = *code_ptr;
704     std::string indent((index + 1) * 2, ' ');
705     code += indent + "  builder." + FunctionStart('P') + "rep(";
706     code += NumToString(struct_def.minalign) + ", ";
707     code += NumToString(struct_def.bytesize) + ");\n";
708     for (auto it = struct_def.fields.vec.rbegin();
709          it != struct_def.fields.vec.rend(); ++it) {
710       auto &field = **it;
711       const auto &field_type = field.value.type;
712       if (field.padding) {
713         code += indent + "  builder." + FunctionStart('P') + "ad(";
714         code += NumToString(field.padding) + ");\n";
715       }
716       if (IsStruct(field_type)) {
717         GenStructBody(*field_type.struct_def, code_ptr,
718                       (nameprefix + (field.name + "_")).c_str(), index,
719                       in_array);
720       } else {
721         const auto &type =
722             IsArray(field_type) ? field_type.VectorType() : field_type;
723         const auto index_var = "_idx" + NumToString(index);
724         if (IsArray(field_type)) {
725           code += indent + "  for (int " + index_var + " = ";
726           code += NumToString(field_type.fixed_length);
727           code += "; " + index_var + " > 0; " + index_var + "--) {\n";
728           in_array = true;
729         }
730         if (IsStruct(type)) {
731           GenStructBody(*field_type.struct_def, code_ptr,
732                         (nameprefix + (field.name + "_")).c_str(), index + 1,
733                         in_array);
734         } else {
735           code += IsArray(field_type) ? "  " : "";
736           code += indent + "  builder." + FunctionStart('P') + "ut";
737           code += GenMethod(type) + "(";
738           code += SourceCast(type);
739           auto argname =
740               nameprefix + MakeCamel(field.name, lang_.first_camel_upper);
741           code += argname;
742           size_t array_cnt = index + (IsArray(field_type) ? 1 : 0);
743           if (lang_.language == IDLOptions::kJava) {
744             for (size_t i = 0; in_array && i < array_cnt; i++) {
745               code += "[_idx" + NumToString(i) + "-1]";
746             }
747           } else if (lang_.language == IDLOptions::kCSharp) {
748             if (array_cnt > 0) {
749               code += "[";
750               for (size_t i = 0; in_array && i < array_cnt; i++) {
751                 code += "_idx" + NumToString(i) + "-1";
752                 if (i != (array_cnt - 1)) code += ",";
753               }
754               code += "]";
755             }
756           } else {
757             FLATBUFFERS_ASSERT(0);
758           }
759           code += ");\n";
760         }
761         if (IsArray(field_type)) { code += indent + "  }\n"; }
762       }
763     }
764   }
765 
GenByteBufferLength(const char * bb_name) const766   std::string GenByteBufferLength(const char *bb_name) const {
767     std::string bb_len = bb_name;
768     if (lang_.language == IDLOptions::kCSharp)
769       bb_len += ".Length";
770     else
771       bb_len += ".capacity()";
772     return bb_len;
773   }
774 
GenOffsetGetter(flatbuffers::FieldDef * key_field,const char * num=nullptr) const775   std::string GenOffsetGetter(flatbuffers::FieldDef *key_field,
776                               const char *num = nullptr) const {
777     std::string key_offset = "";
778     key_offset += lang_.accessor_prefix_static + "__offset(" +
779                   NumToString(key_field->value.offset) + ", ";
780     if (num) {
781       key_offset += num;
782       key_offset +=
783           (lang_.language == IDLOptions::kCSharp ? ".Value, builder.DataBuffer)"
784                                                  : ", _bb)");
785     } else {
786       key_offset += GenByteBufferLength("bb");
787       key_offset += " - tableOffset, bb)";
788     }
789     return key_offset;
790   }
791 
GenLookupKeyGetter(flatbuffers::FieldDef * key_field) const792   std::string GenLookupKeyGetter(flatbuffers::FieldDef *key_field) const {
793     std::string key_getter = "      ";
794     key_getter += "int tableOffset = " + lang_.accessor_prefix_static;
795     key_getter += "__indirect(vectorLocation + 4 * (start + middle)";
796     key_getter += ", bb);\n      ";
797     if (key_field->value.type.base_type == BASE_TYPE_STRING) {
798       key_getter += "int comp = " + lang_.accessor_prefix_static;
799       key_getter += FunctionStart('C') + "ompareStrings(";
800       key_getter += GenOffsetGetter(key_field);
801       key_getter += ", byteKey, bb);\n";
802     } else {
803       auto get_val = GenGetterForLookupByKey(key_field, "bb");
804       if (lang_.language == IDLOptions::kCSharp) {
805         key_getter += "int comp = " + get_val + ".CompareTo(key);\n";
806       } else {
807         key_getter += GenTypeNameDest(key_field->value.type) + " val = ";
808         key_getter += get_val + ";\n";
809         key_getter += "      int comp = val > key ? 1 : val < key ? -1 : 0;\n";
810       }
811     }
812     return key_getter;
813   }
814 
GenKeyGetter(flatbuffers::FieldDef * key_field) const815   std::string GenKeyGetter(flatbuffers::FieldDef *key_field) const {
816     std::string key_getter = "";
817     auto data_buffer =
818         (lang_.language == IDLOptions::kCSharp) ? "builder.DataBuffer" : "_bb";
819     if (key_field->value.type.base_type == BASE_TYPE_STRING) {
820       if (lang_.language == IDLOptions::kJava) key_getter += " return ";
821       key_getter += lang_.accessor_prefix_static;
822       key_getter += FunctionStart('C') + "ompareStrings(";
823       key_getter += GenOffsetGetter(key_field, "o1") + ", ";
824       key_getter += GenOffsetGetter(key_field, "o2") + ", " + data_buffer + ")";
825       if (lang_.language == IDLOptions::kJava) key_getter += ";";
826     } else {
827       auto field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o1");
828       if (lang_.language == IDLOptions::kCSharp) {
829         key_getter += field_getter;
830         field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o2");
831         key_getter += ".CompareTo(" + field_getter + ")";
832       } else {
833         key_getter +=
834             "\n    " + GenTypeNameDest(key_field->value.type) + " val_1 = ";
835         key_getter +=
836             field_getter + ";\n    " + GenTypeNameDest(key_field->value.type);
837         key_getter += " val_2 = ";
838         field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o2");
839         key_getter += field_getter + ";\n";
840         key_getter +=
841             "    return val_1 > val_2 ? 1 : val_1 < val_2 ? -1 : 0;\n ";
842       }
843     }
844     return key_getter;
845   }
846 
GenStruct(StructDef & struct_def,std::string * code_ptr) const847   void GenStruct(StructDef &struct_def, std::string *code_ptr) const {
848     if (struct_def.generated) return;
849     std::string &code = *code_ptr;
850 
851     // Generate a struct accessor class, with methods of the form:
852     // public type name() { return bb.getType(i + offset); }
853     // or for tables of the form:
854     // public type name() {
855     //   int o = __offset(offset); return o != 0 ? bb.getType(o + i) : default;
856     // }
857     GenComment(struct_def.doc_comment, code_ptr, &lang_.comment_config);
858     if (struct_def.attributes.Lookup("private")) {
859       // For Java, we leave the struct unmarked to indicate package-private
860       // For C# we mark the struct as internal
861       if (lang_.language == IDLOptions::kCSharp) {
862         code += "internal ";
863       }
864     } else {
865       code += "public ";
866     }
867     if (lang_.language == IDLOptions::kCSharp &&
868         struct_def.attributes.Lookup("csharp_partial")) {
869       // generate a partial class for this C# struct/table
870       code += "partial ";
871     } else {
872       code += lang_.unsubclassable_decl;
873     }
874     code += lang_.accessor_type + struct_def.name;
875     if (lang_.language == IDLOptions::kCSharp) {
876       code += " : IFlatbufferObject";
877       code += lang_.open_curly;
878       code += "  private ";
879       code += struct_def.fixed ? "Struct" : "Table";
880       code += " __p;\n";
881 
882       if (lang_.language == IDLOptions::kCSharp) {
883         code += "  public ByteBuffer ByteBuffer { get { return __p.bb; } }\n";
884       }
885 
886     } else {
887       code += lang_.inheritance_marker;
888       code += struct_def.fixed ? "Struct" : "Table";
889       code += lang_.open_curly;
890     }
891 
892     if (!struct_def.fixed) {
893       // Generate verson check method.
894       // Force compile time error if not using the same version runtime.
895       code += "  public static void ValidateVersion() {";
896       if (lang_.language == IDLOptions::kCSharp)
897         code += " FlatBufferConstants.";
898       else
899         code += " Constants.";
900       code += "FLATBUFFERS_1_11_1(); ";
901       code += "}\n";
902 
903       // Generate a special accessor for the table that when used as the root
904       // of a FlatBuffer
905       std::string method_name =
906           FunctionStart('G') + "etRootAs" + struct_def.name;
907       std::string method_signature =
908           "  public static " + struct_def.name + " " + method_name;
909 
910       // create convenience method that doesn't require an existing object
911       code += method_signature + "(ByteBuffer _bb) ";
912       code += "{ return " + method_name + "(_bb, new " + struct_def.name +
913               "()); }\n";
914 
915       // create method that allows object reuse
916       code +=
917           method_signature + "(ByteBuffer _bb, " + struct_def.name + " obj) { ";
918       code += lang_.set_bb_byteorder;
919       code += "return (obj.__assign(_bb." + FunctionStart('G') + "etInt(_bb.";
920       code += lang_.get_bb_position;
921       code += ") + _bb.";
922       code += lang_.get_bb_position;
923       code += ", _bb)); }\n";
924       if (parser_.root_struct_def_ == &struct_def) {
925         if (parser_.file_identifier_.length()) {
926           // Check if a buffer has the identifier.
927           code += "  public static ";
928           code += lang_.bool_type + struct_def.name;
929           code += "BufferHasIdentifier(ByteBuffer _bb) { return ";
930           code += lang_.accessor_prefix_static + "__has_identifier(_bb, \"";
931           code += parser_.file_identifier_;
932           code += "\"); }\n";
933         }
934       }
935     }
936     // Generate the __init method that sets the field in a pre-existing
937     // accessor object. This is to allow object reuse.
938     code += "  public void __init(int _i, ByteBuffer _bb) ";
939     code += "{ ";
940     if (lang_.language == IDLOptions::kCSharp) {
941       code += "__p = new ";
942       code += struct_def.fixed ? "Struct" : "Table";
943       code += "(_i, _bb); ";
944     } else {
945       code += "__reset(_i, _bb); ";
946     }
947     code += "}\n";
948     code +=
949         "  public " + struct_def.name + " __assign(int _i, ByteBuffer _bb) ";
950     code += "{ __init(_i, _bb); return this; }\n\n";
951     for (auto it = struct_def.fields.vec.begin();
952          it != struct_def.fields.vec.end(); ++it) {
953       auto &field = **it;
954       if (field.deprecated) continue;
955       GenComment(field.doc_comment, code_ptr, &lang_.comment_config, "  ");
956       std::string type_name = GenTypeGet(field.value.type);
957       std::string type_name_dest = GenTypeNameDest(field.value.type);
958       std::string conditional_cast = "";
959       std::string optional = "";
960       if (lang_.language == IDLOptions::kCSharp && !struct_def.fixed &&
961           (field.value.type.base_type == BASE_TYPE_STRUCT ||
962            field.value.type.base_type == BASE_TYPE_UNION ||
963            (field.value.type.base_type == BASE_TYPE_VECTOR &&
964             (field.value.type.element == BASE_TYPE_STRUCT ||
965              field.value.type.element == BASE_TYPE_UNION)))) {
966         optional = lang_.optional_suffix;
967         conditional_cast = "(" + type_name_dest + optional + ")";
968       }
969       std::string dest_mask = DestinationMask(field.value.type, true);
970       std::string dest_cast = DestinationCast(field.value.type);
971       std::string src_cast = SourceCast(field.value.type);
972       std::string method_start = "  public " +
973                                  (field.required ? "" : GenNullableAnnotation(field.value.type)) +
974                                  type_name_dest + optional + " " +
975                                  MakeCamel(field.name, lang_.first_camel_upper);
976       std::string obj = lang_.language == IDLOptions::kCSharp
977                             ? "(new " + type_name + "())"
978                             : "obj";
979 
980       // Most field accessors need to retrieve and test the field offset first,
981       // this is the prefix code for that:
982       auto offset_prefix =
983           IsArray(field.value.type)
984               ? " { return "
985               : (" { int o = " + lang_.accessor_prefix + "__offset(" +
986                  NumToString(field.value.offset) + "); return o != 0 ? ");
987       // Generate the accessors that don't do object reuse.
988       if (field.value.type.base_type == BASE_TYPE_STRUCT) {
989         // Calls the accessor that takes an accessor object with a new object.
990         if (lang_.language != IDLOptions::kCSharp) {
991           code += method_start + "() { return ";
992           code += MakeCamel(field.name, lang_.first_camel_upper);
993           code += "(new ";
994           code += type_name + "()); }\n";
995         }
996       } else if (field.value.type.base_type == BASE_TYPE_VECTOR &&
997                  field.value.type.element == BASE_TYPE_STRUCT) {
998         // Accessors for vectors of structs also take accessor objects, this
999         // generates a variant without that argument.
1000         if (lang_.language != IDLOptions::kCSharp) {
1001           code += method_start + "(int j) { return ";
1002           code += MakeCamel(field.name, lang_.first_camel_upper);
1003           code += "(new " + type_name + "(), j); }\n";
1004         }
1005       } else if (field.value.type.base_type == BASE_TYPE_UNION ||
1006           (field.value.type.base_type == BASE_TYPE_VECTOR &&
1007            field.value.type.VectorType().base_type == BASE_TYPE_UNION)) {
1008         if (lang_.language == IDLOptions::kCSharp) {
1009           // Union types in C# use generic Table-derived type for better type
1010           // safety.
1011           method_start += "<TTable>";
1012           type_name = type_name_dest;
1013         }
1014       }
1015       std::string getter = dest_cast + GenGetter(field.value.type);
1016       code += method_start;
1017       std::string default_cast = "";
1018       // only create default casts for c# scalars or vectors of scalars
1019       if (lang_.language == IDLOptions::kCSharp &&
1020           (IsScalar(field.value.type.base_type) ||
1021            (field.value.type.base_type == BASE_TYPE_VECTOR &&
1022             IsScalar(field.value.type.element)))) {
1023         // For scalars, default value will be returned by GetDefaultValue().
1024         // If the scalar is an enum, GetDefaultValue() returns an actual c# enum
1025         // that doesn't need to be casted. However, default values for enum
1026         // elements of vectors are integer literals ("0") and are still casted
1027         // for clarity.
1028         if (field.value.type.enum_def == nullptr ||
1029             field.value.type.base_type == BASE_TYPE_VECTOR) {
1030           default_cast = "(" + type_name_dest + ")";
1031         }
1032       }
1033       std::string member_suffix = "; ";
1034       if (IsScalar(field.value.type.base_type)) {
1035         code += lang_.getter_prefix;
1036         member_suffix += lang_.getter_suffix;
1037         if (struct_def.fixed) {
1038           code += " { return " + getter;
1039           code += "(" + lang_.accessor_prefix + "bb_pos + ";
1040           code += NumToString(field.value.offset) + ")";
1041           code += dest_mask;
1042         } else {
1043           code += offset_prefix + getter;
1044           code += "(o + " + lang_.accessor_prefix + "bb_pos)" + dest_mask;
1045           code += " : " + default_cast;
1046           code += GenDefaultValue(field);
1047         }
1048       } else {
1049         switch (field.value.type.base_type) {
1050           case BASE_TYPE_STRUCT:
1051             if (lang_.language != IDLOptions::kCSharp) {
1052               code += "(" + type_name + " obj" + ")";
1053             } else {
1054               code += lang_.getter_prefix;
1055               member_suffix += lang_.getter_suffix;
1056             }
1057             if (struct_def.fixed) {
1058               code += " { return " + obj + ".__assign(" + lang_.accessor_prefix;
1059               code += "bb_pos + " + NumToString(field.value.offset) + ", ";
1060               code += lang_.accessor_prefix + "bb)";
1061             } else {
1062               code += offset_prefix + conditional_cast;
1063               code += obj + ".__assign(";
1064               code += field.value.type.struct_def->fixed
1065                           ? "o + " + lang_.accessor_prefix + "bb_pos"
1066                           : lang_.accessor_prefix + "__indirect(o + " +
1067                                 lang_.accessor_prefix + "bb_pos)";
1068               code += ", " + lang_.accessor_prefix + "bb) : null";
1069             }
1070             break;
1071           case BASE_TYPE_STRING:
1072             code += lang_.getter_prefix;
1073             member_suffix += lang_.getter_suffix;
1074             code += offset_prefix + getter + "(o + " + lang_.accessor_prefix;
1075             code += "bb_pos) : null";
1076             break;
1077           case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH();  // fall thru
1078           case BASE_TYPE_VECTOR: {
1079             auto vectortype = field.value.type.VectorType();
1080             if (vectortype.base_type == BASE_TYPE_UNION &&
1081                 lang_.language == IDLOptions::kCSharp) {
1082                   conditional_cast = "(TTable?)";
1083                   getter += "<TTable>";
1084             }
1085             code += "(";
1086             if (vectortype.base_type == BASE_TYPE_STRUCT) {
1087               if (lang_.language != IDLOptions::kCSharp)
1088                 code += type_name + " obj, ";
1089               getter = obj + ".__assign";
1090             } else if (vectortype.base_type == BASE_TYPE_UNION) {
1091               if (lang_.language != IDLOptions::kCSharp)
1092                 code += type_name + " obj, ";
1093             }
1094             code += "int j)";
1095             const auto body = offset_prefix + conditional_cast + getter + "(";
1096             if (vectortype.base_type == BASE_TYPE_UNION) {
1097               if (lang_.language != IDLOptions::kCSharp)
1098                 code += body + "obj, ";
1099               else
1100                 code += " where TTable : struct, IFlatbufferObject" + body;
1101             } else {
1102               code += body;
1103             }
1104             auto index = lang_.accessor_prefix;
1105             if (IsArray(field.value.type)) {
1106               index += "bb_pos + " + NumToString(field.value.offset) + " + ";
1107             } else {
1108               index += "__vector(o) + ";
1109             }
1110             index += "j * " + NumToString(InlineSize(vectortype));
1111             if (vectortype.base_type == BASE_TYPE_STRUCT) {
1112               code += vectortype.struct_def->fixed
1113                           ? index
1114                           : lang_.accessor_prefix + "__indirect(" + index + ")";
1115               code += ", " + lang_.accessor_prefix + "bb";
1116             } else if (vectortype.base_type == BASE_TYPE_UNION) {
1117               code += index + " - " + lang_.accessor_prefix +  "bb_pos";
1118             } else {
1119               code += index;
1120             }
1121             code += ")" + dest_mask;
1122             if (!IsArray(field.value.type)) {
1123               code += " : ";
1124               code +=
1125                   field.value.type.element == BASE_TYPE_BOOL
1126                       ? "false"
1127                       : (IsScalar(field.value.type.element) ? default_cast + "0"
1128                                                             : "null");
1129             }
1130 
1131             break;
1132           }
1133           case BASE_TYPE_UNION:
1134             if (lang_.language == IDLOptions::kCSharp) {
1135               code += "() where TTable : struct, IFlatbufferObject";
1136               code += offset_prefix + "(TTable?)" + getter;
1137               code += "<TTable>(o) : null";
1138             } else {
1139               code += "(" + type_name + " obj)" + offset_prefix + getter;
1140               code += "(obj, o) : null";
1141             }
1142             break;
1143           default: FLATBUFFERS_ASSERT(0);
1144         }
1145       }
1146       code += member_suffix;
1147       code += "}\n";
1148       if (field.value.type.base_type == BASE_TYPE_VECTOR) {
1149         code +=
1150             "  public int " + MakeCamel(field.name, lang_.first_camel_upper);
1151         code += "Length";
1152         code += lang_.getter_prefix;
1153         code += offset_prefix;
1154         code += lang_.accessor_prefix + "__vector_len(o) : 0; ";
1155         code += lang_.getter_suffix;
1156         code += "}\n";
1157         // See if we should generate a by-key accessor.
1158         if (field.value.type.element == BASE_TYPE_STRUCT &&
1159             !field.value.type.struct_def->fixed) {
1160           auto &sd = *field.value.type.struct_def;
1161           auto &fields = sd.fields.vec;
1162           for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
1163             auto &key_field = **kit;
1164             if (key_field.key) {
1165               auto qualified_name = WrapInNameSpace(sd);
1166               code += "  public " + qualified_name + lang_.optional_suffix + " ";
1167               code += MakeCamel(field.name, lang_.first_camel_upper) + "ByKey(";
1168               code += GenTypeNameDest(key_field.value.type) + " key)";
1169               code += offset_prefix;
1170               code += qualified_name + ".__lookup_by_key(";
1171               if (lang_.language == IDLOptions::kJava)
1172                 code += "null, ";
1173               code += lang_.accessor_prefix + "__vector(o), key, ";
1174               code += lang_.accessor_prefix + "bb) : null; ";
1175               code += "}\n";
1176               if (lang_.language == IDLOptions::kJava) {
1177                 code += "  public " + qualified_name + lang_.optional_suffix + " ";
1178                 code += MakeCamel(field.name, lang_.first_camel_upper) + "ByKey(";
1179                 code += qualified_name + lang_.optional_suffix + " obj, ";
1180                 code += GenTypeNameDest(key_field.value.type) + " key)";
1181                 code += offset_prefix;
1182                 code += qualified_name + ".__lookup_by_key(obj, ";
1183                 code += lang_.accessor_prefix + "__vector(o), key, ";
1184                 code += lang_.accessor_prefix + "bb) : null; ";
1185                 code += "}\n";
1186               }
1187               break;
1188             }
1189           }
1190         }
1191       }
1192       // Generate a ByteBuffer accessor for strings & vectors of scalars.
1193       if ((field.value.type.base_type == BASE_TYPE_VECTOR &&
1194            IsScalar(field.value.type.VectorType().base_type)) ||
1195           field.value.type.base_type == BASE_TYPE_STRING) {
1196         switch (lang_.language) {
1197           case IDLOptions::kJava:
1198             code += "  public ByteBuffer ";
1199             code += MakeCamel(field.name, lang_.first_camel_upper);
1200             code += "AsByteBuffer() { return ";
1201             code += lang_.accessor_prefix + "__vector_as_bytebuffer(";
1202             code += NumToString(field.value.offset) + ", ";
1203             code +=
1204                 NumToString(field.value.type.base_type == BASE_TYPE_STRING
1205                                 ? 1
1206                                 : InlineSize(field.value.type.VectorType()));
1207             code += "); }\n";
1208             code += "  public ByteBuffer ";
1209             code += MakeCamel(field.name, lang_.first_camel_upper);
1210             code += "InByteBuffer(ByteBuffer _bb) { return ";
1211             code += lang_.accessor_prefix + "__vector_in_bytebuffer(_bb, ";
1212             code += NumToString(field.value.offset) + ", ";
1213             code +=
1214                 NumToString(field.value.type.base_type == BASE_TYPE_STRING
1215                                 ? 1
1216                                 : InlineSize(field.value.type.VectorType()));
1217             code += "); }\n";
1218             break;
1219           case IDLOptions::kCSharp:
1220             code += "#if ENABLE_SPAN_T\n";
1221             code += "  public Span<byte> Get";
1222             code += MakeCamel(field.name, lang_.first_camel_upper);
1223             code += "Bytes() { return ";
1224             code += lang_.accessor_prefix + "__vector_as_span(";
1225             code += NumToString(field.value.offset);
1226             code += "); }\n";
1227             code += "#else\n";
1228             code += "  public ArraySegment<byte>? Get";
1229             code += MakeCamel(field.name, lang_.first_camel_upper);
1230             code += "Bytes() { return ";
1231             code += lang_.accessor_prefix + "__vector_as_arraysegment(";
1232             code += NumToString(field.value.offset);
1233             code += "); }\n";
1234             code += "#endif\n";
1235 
1236             // For direct blockcopying the data into a typed array
1237             code += "  public ";
1238             code += GenTypeBasic(field.value.type.VectorType());
1239             code += "[] Get";
1240             code += MakeCamel(field.name, lang_.first_camel_upper);
1241             code += "Array() { return ";
1242             code += lang_.accessor_prefix + "__vector_as_array<";
1243             code += GenTypeBasic(field.value.type.VectorType());
1244             code += ">(";
1245             code += NumToString(field.value.offset);
1246             code += "); }\n";
1247             break;
1248           default: break;
1249         }
1250       }
1251       // generate object accessors if is nested_flatbuffer
1252       if (field.nested_flatbuffer) {
1253         auto nested_type_name = WrapInNameSpace(*field.nested_flatbuffer);
1254         auto nested_method_name =
1255             MakeCamel(field.name, lang_.first_camel_upper) + "As" +
1256             field.nested_flatbuffer->name;
1257         auto get_nested_method_name = nested_method_name;
1258         if (lang_.language == IDLOptions::kCSharp) {
1259           get_nested_method_name = "Get" + nested_method_name;
1260           conditional_cast =
1261               "(" + nested_type_name + lang_.optional_suffix + ")";
1262         }
1263         if (lang_.language != IDLOptions::kCSharp) {
1264           code += "  public " + nested_type_name + lang_.optional_suffix + " ";
1265           code += nested_method_name + "() { return ";
1266           code +=
1267               get_nested_method_name + "(new " + nested_type_name + "()); }\n";
1268         } else {
1269           obj = "(new " + nested_type_name + "())";
1270         }
1271         code += "  public " + nested_type_name + lang_.optional_suffix + " ";
1272         code += get_nested_method_name + "(";
1273         if (lang_.language != IDLOptions::kCSharp)
1274           code += nested_type_name + " obj";
1275         code += ") { int o = " + lang_.accessor_prefix + "__offset(";
1276         code += NumToString(field.value.offset) + "); ";
1277         code += "return o != 0 ? " + conditional_cast + obj + ".__assign(";
1278         code += lang_.accessor_prefix;
1279         code += "__indirect(" + lang_.accessor_prefix + "__vector(o)), ";
1280         code += lang_.accessor_prefix + "bb) : null; }\n";
1281       }
1282       // Generate mutators for scalar fields or vectors of scalars.
1283       if (parser_.opts.mutable_buffer) {
1284         auto is_series = (IsSeries(field.value.type));
1285         const auto &underlying_type =
1286             is_series ? field.value.type.VectorType() : field.value.type;
1287         // Boolean parameters have to be explicitly converted to byte
1288         // representation.
1289         auto setter_parameter = underlying_type.base_type == BASE_TYPE_BOOL
1290                                     ? "(byte)(" + field.name + " ? 1 : 0)"
1291                                     : field.name;
1292         auto mutator_prefix = MakeCamel("mutate", lang_.first_camel_upper);
1293         // A vector mutator also needs the index of the vector element it should
1294         // mutate.
1295         auto mutator_params = (is_series ? "(int j, " : "(") +
1296                               GenTypeNameDest(underlying_type) + " " +
1297                               field.name + ") { ";
1298         auto setter_index =
1299             is_series
1300                 ? lang_.accessor_prefix +
1301                       (IsArray(field.value.type)
1302                            ? "bb_pos + " + NumToString(field.value.offset)
1303                            : "__vector(o)") +
1304                       +" + j * " + NumToString(InlineSize(underlying_type))
1305                 : (struct_def.fixed
1306                        ? lang_.accessor_prefix + "bb_pos + " +
1307                              NumToString(field.value.offset)
1308                        : "o + " + lang_.accessor_prefix + "bb_pos");
1309         if (IsScalar(underlying_type.base_type)) {
1310           code += "  public ";
1311           code += struct_def.fixed ? "void " : lang_.bool_type;
1312           code += mutator_prefix + MakeCamel(field.name, true);
1313           code += mutator_params;
1314           if (struct_def.fixed) {
1315             code += GenSetter(underlying_type) + "(" + setter_index + ", ";
1316             code += src_cast + setter_parameter + "); }\n";
1317           } else {
1318             code += "int o = " + lang_.accessor_prefix + "__offset(";
1319             code += NumToString(field.value.offset) + ");";
1320             code += " if (o != 0) { " + GenSetter(underlying_type);
1321             code += "(" + setter_index + ", " + src_cast + setter_parameter +
1322                     "); return true; } else { return false; } }\n";
1323           }
1324         }
1325       }
1326     }
1327     code += "\n";
1328     flatbuffers::FieldDef *key_field = nullptr;
1329     if (struct_def.fixed) {
1330       // create a struct constructor function
1331       code += "  public static " + GenOffsetType(struct_def) + " ";
1332       code += FunctionStart('C') + "reate";
1333       code += struct_def.name + "(FlatBufferBuilder builder";
1334       GenStructArgs(struct_def, code_ptr, "");
1335       code += ") {\n";
1336       GenStructBody(struct_def, code_ptr, "");
1337       code += "    return ";
1338       code += GenOffsetConstruct(
1339           struct_def, "builder." + std::string(lang_.get_fbb_offset));
1340       code += ";\n  }\n";
1341     } else {
1342       // Generate a method that creates a table in one go. This is only possible
1343       // when the table has no struct fields, since those have to be created
1344       // inline, and there's no way to do so in Java.
1345       bool has_no_struct_fields = true;
1346       int num_fields = 0;
1347       for (auto it = struct_def.fields.vec.begin();
1348            it != struct_def.fields.vec.end(); ++it) {
1349         auto &field = **it;
1350         if (field.deprecated) continue;
1351         if (IsStruct(field.value.type)) {
1352           has_no_struct_fields = false;
1353         } else {
1354           num_fields++;
1355         }
1356       }
1357       // JVM specifications restrict default constructor params to be < 255.
1358       // Longs and doubles take up 2 units, so we set the limit to be < 127.
1359       if (has_no_struct_fields && num_fields && num_fields < 127) {
1360         // Generate a table constructor of the form:
1361         // public static int createName(FlatBufferBuilder builder, args...)
1362         code += "  public static " + GenOffsetType(struct_def) + " ";
1363         code += FunctionStart('C') + "reate" + struct_def.name;
1364         code += "(FlatBufferBuilder builder";
1365         for (auto it = struct_def.fields.vec.begin();
1366              it != struct_def.fields.vec.end(); ++it) {
1367           auto &field = **it;
1368           if (field.deprecated) continue;
1369           code += ",\n      ";
1370           code += GenTypeBasic(DestinationType(field.value.type, false));
1371           code += " ";
1372           code += field.name;
1373           if (!IsScalar(field.value.type.base_type)) code += "Offset";
1374 
1375           // Java doesn't have defaults, which means this method must always
1376           // supply all arguments, and thus won't compile when fields are added.
1377           if (lang_.language != IDLOptions::kJava) {
1378             code += " = ";
1379             code += GenDefaultValueBasic(field);
1380           }
1381         }
1382         code += ") {\n    builder.";
1383         code += FunctionStart('S') + "tartTable(";
1384         code += NumToString(struct_def.fields.vec.size()) + ");\n";
1385         for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
1386              size; size /= 2) {
1387           for (auto it = struct_def.fields.vec.rbegin();
1388                it != struct_def.fields.vec.rend(); ++it) {
1389             auto &field = **it;
1390             if (!field.deprecated &&
1391                 (!struct_def.sortbysize ||
1392                  size == SizeOf(field.value.type.base_type))) {
1393               code += "    " + struct_def.name + ".";
1394               code += FunctionStart('A') + "dd";
1395               code += MakeCamel(field.name) + "(builder, " + field.name;
1396               if (!IsScalar(field.value.type.base_type)) code += "Offset";
1397               code += ");\n";
1398             }
1399           }
1400         }
1401         code += "    return " + struct_def.name + ".";
1402         code += FunctionStart('E') + "nd" + struct_def.name;
1403         code += "(builder);\n  }\n\n";
1404       }
1405       // Generate a set of static methods that allow table construction,
1406       // of the form:
1407       // public static void addName(FlatBufferBuilder builder, short name)
1408       // { builder.addShort(id, name, default); }
1409       // Unlike the Create function, these always work.
1410       code += "  public static void " + FunctionStart('S') + "tart";
1411       code += struct_def.name;
1412       code += "(FlatBufferBuilder builder) { builder.";
1413       code += FunctionStart('S') + "tartTable(";
1414       code += NumToString(struct_def.fields.vec.size()) + "); }\n";
1415       for (auto it = struct_def.fields.vec.begin();
1416            it != struct_def.fields.vec.end(); ++it) {
1417         auto &field = **it;
1418         if (field.deprecated) continue;
1419         if (field.key) key_field = &field;
1420         code += "  public static void " + FunctionStart('A') + "dd";
1421         code += MakeCamel(field.name);
1422         code += "(FlatBufferBuilder builder, ";
1423         code += GenTypeBasic(DestinationType(field.value.type, false));
1424         auto argname = MakeCamel(field.name, false);
1425         if (!IsScalar(field.value.type.base_type)) argname += "Offset";
1426         code += " " + argname + ") { builder." + FunctionStart('A') + "dd";
1427         code += GenMethod(field.value.type) + "(";
1428         code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
1429         code += SourceCastBasic(field.value.type);
1430         code += argname;
1431         if (!IsScalar(field.value.type.base_type) &&
1432             field.value.type.base_type != BASE_TYPE_UNION &&
1433             lang_.language == IDLOptions::kCSharp) {
1434           code += ".Value";
1435         }
1436         code += ", ";
1437         if (lang_.language == IDLOptions::kJava)
1438           code += SourceCastBasic(field.value.type);
1439         code += GenDefaultValue(field, false);
1440         code += "); }\n";
1441         if (field.value.type.base_type == BASE_TYPE_VECTOR) {
1442           auto vector_type = field.value.type.VectorType();
1443           auto alignment = InlineAlignment(vector_type);
1444           auto elem_size = InlineSize(vector_type);
1445           if (!IsStruct(vector_type)) {
1446             // Generate a method to create a vector from a Java array.
1447             code += "  public static " + GenVectorOffsetType() + " ";
1448             code += FunctionStart('C') + "reate";
1449             code += MakeCamel(field.name);
1450             code += "Vector(FlatBufferBuilder builder, ";
1451             code += GenTypeBasic(vector_type) + "[] data) ";
1452             code += "{ builder." + FunctionStart('S') + "tartVector(";
1453             code += NumToString(elem_size);
1454             code += ", data." + FunctionStart('L') + "ength, ";
1455             code += NumToString(alignment);
1456             code += "); for (int i = data.";
1457             code += FunctionStart('L') + "ength - 1; i >= 0; i--) builder.";
1458             code += FunctionStart('A') + "dd";
1459             code += GenMethod(vector_type);
1460             code += "(";
1461             code += SourceCastBasic(vector_type, false);
1462             code += "data[i]";
1463             if (lang_.language == IDLOptions::kCSharp &&
1464                 (vector_type.base_type == BASE_TYPE_STRUCT ||
1465                  vector_type.base_type == BASE_TYPE_STRING))
1466               code += ".Value";
1467             code += "); return ";
1468             code += "builder." + FunctionStart('E') + "ndVector(); }\n";
1469             // For C#, include a block copy method signature.
1470             if (lang_.language == IDLOptions::kCSharp) {
1471               code += "  public static " + GenVectorOffsetType() + " ";
1472               code += FunctionStart('C') + "reate";
1473               code += MakeCamel(field.name);
1474               code += "VectorBlock(FlatBufferBuilder builder, ";
1475               code += GenTypeBasic(vector_type) + "[] data) ";
1476               code += "{ builder." + FunctionStart('S') + "tartVector(";
1477               code += NumToString(elem_size);
1478               code += ", data." + FunctionStart('L') + "ength, ";
1479               code += NumToString(alignment);
1480               code += "); builder.Add(data); return builder.EndVector(); }\n";
1481             }
1482           }
1483           // Generate a method to start a vector, data to be added manually
1484           // after.
1485           code += "  public static void " + FunctionStart('S') + "tart";
1486           code += MakeCamel(field.name);
1487           code += "Vector(FlatBufferBuilder builder, int numElems) ";
1488           code += "{ builder." + FunctionStart('S') + "tartVector(";
1489           code += NumToString(elem_size);
1490           code += ", numElems, " + NumToString(alignment);
1491           code += "); }\n";
1492         }
1493       }
1494       code += "  public static " + GenOffsetType(struct_def) + " ";
1495       code += FunctionStart('E') + "nd" + struct_def.name;
1496       code += "(FlatBufferBuilder builder) {\n    int o = builder.";
1497       code += FunctionStart('E') + "ndTable();\n";
1498       for (auto it = struct_def.fields.vec.begin();
1499            it != struct_def.fields.vec.end(); ++it) {
1500         auto &field = **it;
1501         if (!field.deprecated && field.required) {
1502           code += "    builder." + FunctionStart('R') + "equired(o, ";
1503           code += NumToString(field.value.offset);
1504           code += ");  // " + field.name + "\n";
1505         }
1506       }
1507       code += "    return " + GenOffsetConstruct(struct_def, "o") + ";\n  }\n";
1508       if (parser_.root_struct_def_ == &struct_def) {
1509         std::string size_prefix[] = { "", "SizePrefixed" };
1510         for (int i = 0; i < 2; ++i) {
1511           code += "  public static void ";
1512           code += FunctionStart('F') + "inish" + size_prefix[i] +
1513                   struct_def.name;
1514           code += "Buffer(FlatBufferBuilder builder, " +
1515                   GenOffsetType(struct_def);
1516           code += " offset) {";
1517           code += " builder." + FunctionStart('F') + "inish" + size_prefix[i] +
1518                   "(offset";
1519           if (lang_.language == IDLOptions::kCSharp) { code += ".Value"; }
1520 
1521           if (parser_.file_identifier_.length())
1522             code += ", \"" + parser_.file_identifier_ + "\"";
1523           code += "); }\n";
1524         }
1525       }
1526     }
1527     // Only generate key compare function for table,
1528     // because `key_field` is not set for struct
1529     if (struct_def.has_key && !struct_def.fixed) {
1530       FLATBUFFERS_ASSERT(key_field);
1531       if (lang_.language == IDLOptions::kJava) {
1532         code += "\n  @Override\n  protected int keysCompare(";
1533         code += "Integer o1, Integer o2, ByteBuffer _bb) {";
1534         code += GenKeyGetter(key_field);
1535         code += " }\n";
1536       } else {
1537         code += "\n  public static VectorOffset ";
1538         code += "CreateSortedVectorOf" + struct_def.name;
1539         code += "(FlatBufferBuilder builder, ";
1540         code += "Offset<" + struct_def.name + ">";
1541         code += "[] offsets) {\n";
1542         code += "    Array.Sort(offsets, (Offset<" + struct_def.name +
1543                 "> o1, Offset<" + struct_def.name + "> o2) => " +
1544                 GenKeyGetter(key_field);
1545         code += ");\n";
1546         code += "    return builder.CreateVectorOfTables(offsets);\n  }\n";
1547       }
1548 
1549       code += "\n  public static " + struct_def.name + lang_.optional_suffix;
1550       code += " __lookup_by_key(";
1551       if (lang_.language == IDLOptions::kJava)
1552         code +=  struct_def.name + " obj, ";
1553       code += "int vectorLocation, ";
1554       code += GenTypeNameDest(key_field->value.type);
1555       code += " key, ByteBuffer bb) {\n";
1556       if (key_field->value.type.base_type == BASE_TYPE_STRING) {
1557         code += "    byte[] byteKey = ";
1558         if (lang_.language == IDLOptions::kJava)
1559           code += "key.getBytes(Table.UTF8_CHARSET.get());\n";
1560         else
1561           code += "System.Text.Encoding.UTF8.GetBytes(key);\n";
1562       }
1563       code += "    int span = ";
1564       code += "bb." + FunctionStart('G') + "etInt(vectorLocation - 4);\n";
1565       code += "    int start = 0;\n";
1566       code += "    while (span != 0) {\n";
1567       code += "      int middle = span / 2;\n";
1568       code += GenLookupKeyGetter(key_field);
1569       code += "      if (comp > 0) {\n";
1570       code += "        span = middle;\n";
1571       code += "      } else if (comp < 0) {\n";
1572       code += "        middle++;\n";
1573       code += "        start += middle;\n";
1574       code += "        span -= middle;\n";
1575       code += "      } else {\n";
1576       code += "        return ";
1577       if (lang_.language == IDLOptions::kJava)
1578         code += "(obj == null ? new " + struct_def.name + "() : obj)";
1579       else
1580         code += "new " + struct_def.name + "()";
1581       code += ".__assign(tableOffset, bb);\n";
1582       code += "      }\n    }\n";
1583       code += "    return null;\n";
1584       code += "  }\n";
1585     }
1586     code += "}";
1587     // Java does not need the closing semi-colon on class definitions.
1588     code += (lang_.language != IDLOptions::kJava) ? ";" : "";
1589     code += "\n\n";
1590   }
1591   const LanguageParameters &lang_;
1592   // This tracks the current namespace used to determine if a type need to be
1593   // prefixed by its namespace
1594   const Namespace *cur_name_space_;
1595 };
1596 }  // namespace general
1597 
GenerateGeneral(const Parser & parser,const std::string & path,const std::string & file_name)1598 bool GenerateGeneral(const Parser &parser, const std::string &path,
1599                      const std::string &file_name) {
1600   general::GeneralGenerator generator(parser, path, file_name);
1601   return generator.generate();
1602 }
1603 
GeneralMakeRule(const Parser & parser,const std::string & path,const std::string & file_name)1604 std::string GeneralMakeRule(const Parser &parser, const std::string &path,
1605                             const std::string &file_name) {
1606   FLATBUFFERS_ASSERT(parser.opts.lang <= IDLOptions::kMAX);
1607   const auto &lang = GetLangParams(parser.opts.lang);
1608 
1609   std::string make_rule;
1610 
1611   for (auto it = parser.enums_.vec.begin(); it != parser.enums_.vec.end();
1612        ++it) {
1613     auto &enum_def = **it;
1614     if (!make_rule.empty()) make_rule += " ";
1615     std::string directory =
1616         BaseGenerator::NamespaceDir(parser, path, *enum_def.defined_namespace);
1617     make_rule += directory + enum_def.name + lang.file_extension;
1618   }
1619 
1620   for (auto it = parser.structs_.vec.begin(); it != parser.structs_.vec.end();
1621        ++it) {
1622     auto &struct_def = **it;
1623     if (!make_rule.empty()) make_rule += " ";
1624     std::string directory = BaseGenerator::NamespaceDir(
1625         parser, path, *struct_def.defined_namespace);
1626     make_rule += directory + struct_def.name + lang.file_extension;
1627   }
1628 
1629   make_rule += ": ";
1630   auto included_files = parser.GetIncludedFilesRecursive(file_name);
1631   for (auto it = included_files.begin(); it != included_files.end(); ++it) {
1632     make_rule += " " + *it;
1633   }
1634   return make_rule;
1635 }
1636 
BinaryFileName(const Parser & parser,const std::string & path,const std::string & file_name)1637 std::string BinaryFileName(const Parser &parser, const std::string &path,
1638                            const std::string &file_name) {
1639   auto ext = parser.file_extension_.length() ? parser.file_extension_ : "bin";
1640   return path + file_name + "." + ext;
1641 }
1642 
GenerateBinary(const Parser & parser,const std::string & path,const std::string & file_name)1643 bool GenerateBinary(const Parser &parser, const std::string &path,
1644                     const std::string &file_name) {
1645   return !parser.builder_.GetSize() ||
1646          flatbuffers::SaveFile(
1647              BinaryFileName(parser, path, file_name).c_str(),
1648              reinterpret_cast<char *>(parser.builder_.GetBufferPointer()),
1649              parser.builder_.GetSize(), true);
1650 }
1651 
BinaryMakeRule(const Parser & parser,const std::string & path,const std::string & file_name)1652 std::string BinaryMakeRule(const Parser &parser, const std::string &path,
1653                            const std::string &file_name) {
1654   if (!parser.builder_.GetSize()) return "";
1655   std::string filebase =
1656       flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
1657   std::string make_rule =
1658       BinaryFileName(parser, path, filebase) + ": " + file_name;
1659   auto included_files =
1660       parser.GetIncludedFilesRecursive(parser.root_struct_def_->file);
1661   for (auto it = included_files.begin(); it != included_files.end(); ++it) {
1662     make_rule += " " + *it;
1663   }
1664   return make_rule;
1665 }
1666 
1667 }  // namespace flatbuffers
1668