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