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