• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <functional>
20 #include <unordered_set>
21 
22 #include "flatbuffers/code_generators.h"
23 #include "flatbuffers/flatbuffers.h"
24 #include "flatbuffers/idl.h"
25 #include "flatbuffers/util.h"
26 #if defined(FLATBUFFERS_CPP98_STL)
27 #  include <cctype>
28 #endif  // defined(FLATBUFFERS_CPP98_STL)
29 
30 namespace flatbuffers {
31 
32 namespace kotlin {
33 
34 typedef std::map<std::string, std::pair<std::string, std::string> > FbbParamMap;
35 static TypedFloatConstantGenerator KotlinFloatGen("Double.", "Float.", "NaN",
36                                                   "POSITIVE_INFINITY",
37                                                   "NEGATIVE_INFINITY");
38 
39 static const CommentConfig comment_config = { "/**", " *", " */" };
40 static const std::string ident_pad = "    ";
41 static const char *keywords[] = {
42   "package",  "as",     "typealias", "class",  "this",   "super",
43   "val",      "var",    "fun",       "for",    "null",   "true",
44   "false",    "is",     "in",        "throw",  "return", "break",
45   "continue", "object", "if",        "try",    "else",   "while",
46   "do",       "when",   "interface", "typeof", "Any",    "Character"
47 };
48 
49 // Escape Keywords
Esc(const std::string & name)50 static std::string Esc(const std::string &name) {
51   for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
52     if (name == keywords[i]) { return MakeCamel(name + "_", false); }
53   }
54 
55   return MakeCamel(name, false);
56 }
57 
58 class KotlinGenerator : public BaseGenerator {
59  public:
KotlinGenerator(const Parser & parser,const std::string & path,const std::string & file_name)60   KotlinGenerator(const Parser &parser, const std::string &path,
61                   const std::string &file_name)
62       : BaseGenerator(parser, path, file_name, "", ".", "kt"),
63         cur_name_space_(nullptr) {}
64 
65   KotlinGenerator &operator=(const KotlinGenerator &);
generate()66   bool generate() FLATBUFFERS_OVERRIDE {
67     std::string one_file_code;
68 
69     cur_name_space_ = parser_.current_namespace_;
70     for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
71          ++it) {
72       CodeWriter enumWriter(ident_pad);
73       auto &enum_def = **it;
74       if (!parser_.opts.one_file) cur_name_space_ = enum_def.defined_namespace;
75       GenEnum(enum_def, enumWriter);
76       if (parser_.opts.one_file) {
77         one_file_code += enumWriter.ToString();
78       } else {
79         if (!SaveType(enum_def.name, *enum_def.defined_namespace,
80                       enumWriter.ToString(), false))
81           return false;
82       }
83     }
84 
85     for (auto it = parser_.structs_.vec.begin();
86          it != parser_.structs_.vec.end(); ++it) {
87       CodeWriter structWriter(ident_pad);
88       auto &struct_def = **it;
89       if (!parser_.opts.one_file)
90         cur_name_space_ = struct_def.defined_namespace;
91       GenStruct(struct_def, structWriter);
92       if (parser_.opts.one_file) {
93         one_file_code += structWriter.ToString();
94       } else {
95         if (!SaveType(struct_def.name, *struct_def.defined_namespace,
96                       structWriter.ToString(), true))
97           return false;
98       }
99     }
100 
101     if (parser_.opts.one_file) {
102       return SaveType(file_name_, *parser_.current_namespace_, one_file_code,
103                       true);
104     }
105     return true;
106   }
107 
108   // Save out the generated code for a single class while adding
109   // declaration boilerplate.
SaveType(const std::string & defname,const Namespace & ns,const std::string & classcode,bool needs_includes) const110   bool SaveType(const std::string &defname, const Namespace &ns,
111                 const std::string &classcode, bool needs_includes) const {
112     if (!classcode.length()) return true;
113 
114     std::string code =
115         "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
116 
117     std::string namespace_name = FullNamespace(".", ns);
118     if (!namespace_name.empty()) {
119       code += "package " + namespace_name;
120       code += "\n\n";
121     }
122     if (needs_includes) {
123       code += "import java.nio.*\n";
124       code += "import kotlin.math.sign\n";
125       code += "import com.google.flatbuffers.*\n\n";
126     }
127     code += classcode;
128     auto filename = NamespaceDir(ns) + defname + ".kt";
129     return SaveFile(filename.c_str(), code, false);
130   }
131 
CurrentNameSpace() const132   const Namespace *CurrentNameSpace() const FLATBUFFERS_OVERRIDE {
133     return cur_name_space_;
134   }
135 
IsEnum(const Type & type)136   static bool IsEnum(const Type &type) {
137     return type.enum_def != nullptr && IsInteger(type.base_type);
138   }
139 
GenTypeBasic(const BaseType & type)140   static std::string GenTypeBasic(const BaseType &type) {
141     // clang-format off
142     static const char * const kotlin_typename[] = {
143       #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
144               CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, ...) \
145         #KTYPE,
146         FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
147       #undef FLATBUFFERS_TD
148     };
149     // clang-format on
150     return kotlin_typename[type];
151   }
152 
GenTypePointer(const Type & type) const153   std::string GenTypePointer(const Type &type) const {
154     switch (type.base_type) {
155       case BASE_TYPE_STRING: return "String";
156       case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType());
157       case BASE_TYPE_STRUCT: return WrapInNameSpace(*type.struct_def);
158       default: return "Table";
159     }
160   }
161 
GenTypeGet(const Type & type) const162   std::string GenTypeGet(const Type &type) const {
163     return IsScalar(type.base_type) ? GenTypeBasic(type.base_type)
164                                     : GenTypePointer(type);
165   }
166 
GenEnumDefaultValue(const FieldDef & field) const167   std::string GenEnumDefaultValue(const FieldDef &field) const {
168     auto &value = field.value;
169     FLATBUFFERS_ASSERT(value.type.enum_def);
170     auto &enum_def = *value.type.enum_def;
171     auto enum_val = enum_def.FindByValue(value.constant);
172     return enum_val ? (WrapInNameSpace(enum_def) + "." + enum_val->name)
173                     : value.constant;
174   }
175 
176   // Generate default values to compare against a default value when
177   // `force_defaults` is `false`.
178   // Main differences are:
179   // - Floats are upcasted to doubles
180   // - Unsigned are casted to signed
GenFBBDefaultValue(const FieldDef & field) const181   std::string GenFBBDefaultValue(const FieldDef &field) const {
182     auto out = GenDefaultValue(field, true);
183     // All FlatBufferBuilder default floating point values are doubles
184     if (field.value.type.base_type == BASE_TYPE_FLOAT) {
185       if (out.find("Float") != std::string::npos) {
186         out.replace(0, 5, "Double");
187       }
188     }
189     // Guarantee all values are doubles
190     if (out.back() == 'f') out.pop_back();
191     return out;
192   }
193 
194   // FlatBufferBuilder only store signed types, so this function
195   // returns a cast for unsigned values
GenFBBValueCast(const FieldDef & field) const196   std::string GenFBBValueCast(const FieldDef &field) const {
197     if (IsUnsigned(field.value.type.base_type)) {
198       return CastToSigned(field.value.type);
199     }
200     return "";
201   }
202 
GenDefaultValue(const FieldDef & field,bool force_signed=false) const203   std::string GenDefaultValue(const FieldDef &field,
204                               bool force_signed = false) const {
205     auto &value = field.value;
206     auto base_type = field.value.type.base_type;
207     if (IsFloat(base_type)) {
208       auto val = KotlinFloatGen.GenFloatConstant(field);
209       if (base_type == BASE_TYPE_DOUBLE && val.back() == 'f') {
210         val.pop_back();
211       }
212       return val;
213     }
214 
215     if (base_type == BASE_TYPE_BOOL) {
216       return value.constant == "0" ? "false" : "true";
217     }
218 
219     std::string suffix = "";
220 
221     if (base_type == BASE_TYPE_LONG || !force_signed) {
222       suffix = LiteralSuffix(base_type);
223     }
224     return value.constant + suffix;
225   }
226 
GenEnum(EnumDef & enum_def,CodeWriter & writer) const227   void GenEnum(EnumDef &enum_def, CodeWriter &writer) const {
228     if (enum_def.generated) return;
229 
230     GenerateComment(enum_def.doc_comment, writer, &comment_config);
231 
232     writer += "@Suppress(\"unused\")";
233     writer += "@ExperimentalUnsignedTypes";
234     writer += "class " + Esc(enum_def.name) + " private constructor() {";
235     writer.IncrementIdentLevel();
236 
237     GenerateCompanionObject(writer, [&]() {
238       // Write all properties
239       auto vals = enum_def.Vals();
240       for (auto it = vals.begin(); it != vals.end(); ++it) {
241         auto &ev = **it;
242         auto field_type = GenTypeBasic(enum_def.underlying_type.base_type);
243         auto val = enum_def.ToString(ev);
244         auto suffix = LiteralSuffix(enum_def.underlying_type.base_type);
245         writer.SetValue("name", Esc(ev.name));
246         writer.SetValue("type", field_type);
247         writer.SetValue("val", val + suffix);
248         GenerateComment(ev.doc_comment, writer, &comment_config);
249         writer += "const val {{name}}: {{type}} = {{val}}";
250       }
251 
252       // Generate a generate string table for enum values.
253       // Problem is, if values are very sparse that could generate really
254       // big tables. Ideally in that case we generate a map lookup
255       // instead, but for the moment we simply don't output a table at all.
256       auto range = enum_def.Distance();
257       // Average distance between values above which we consider a table
258       // "too sparse". Change at will.
259       static const uint64_t kMaxSparseness = 5;
260       if (range / static_cast<uint64_t>(enum_def.size()) < kMaxSparseness) {
261         GeneratePropertyOneLine(writer, "names", "Array<String>", [&]() {
262           writer += "arrayOf(\\";
263           auto val = enum_def.Vals().front();
264           for (auto it = vals.begin(); it != vals.end(); ++it) {
265             auto ev = *it;
266             for (auto k = enum_def.Distance(val, ev); k > 1; --k)
267               writer += "\"\", \\";
268             val = ev;
269             writer += "\"" + (*it)->name + "\"\\";
270             if (it + 1 != vals.end()) { writer += ", \\"; }
271           }
272           writer += ")";
273         });
274         GenerateFunOneLine(writer, "name", "e: Int", "String", [&]() {
275           writer += "names[e\\";
276           if (enum_def.MinValue()->IsNonZero())
277             writer += " - " + enum_def.MinValue()->name + ".toInt()\\";
278           writer += "]";
279         });
280       }
281     });
282     writer.DecrementIdentLevel();
283     writer += "}";
284   }
285 
286   // Returns the function name that is able to read a value of the given type.
ByteBufferGetter(const Type & type,std::string bb_var_name) const287   std::string ByteBufferGetter(const Type &type,
288                                std::string bb_var_name) const {
289     switch (type.base_type) {
290       case BASE_TYPE_STRING: return "__string";
291       case BASE_TYPE_STRUCT: return "__struct";
292       case BASE_TYPE_UNION: return "__union";
293       case BASE_TYPE_VECTOR:
294         return ByteBufferGetter(type.VectorType(), bb_var_name);
295       case BASE_TYPE_INT:
296       case BASE_TYPE_UINT: return bb_var_name + ".getInt";
297       case BASE_TYPE_SHORT:
298       case BASE_TYPE_USHORT: return bb_var_name + ".getShort";
299       case BASE_TYPE_ULONG:
300       case BASE_TYPE_LONG: return bb_var_name + ".getLong";
301       case BASE_TYPE_FLOAT: return bb_var_name + ".getFloat";
302       case BASE_TYPE_DOUBLE: return bb_var_name + ".getDouble";
303       case BASE_TYPE_CHAR:
304       case BASE_TYPE_UCHAR:
305       case BASE_TYPE_NONE:
306       case BASE_TYPE_UTYPE: return bb_var_name + ".get";
307       case BASE_TYPE_BOOL: return "0.toByte() != " + bb_var_name + ".get";
308       default:
309         return bb_var_name + ".get" + MakeCamel(GenTypeBasic(type.base_type));
310     }
311   }
312 
ByteBufferSetter(const Type & type) const313   std::string ByteBufferSetter(const Type &type) const {
314     if (IsScalar(type.base_type)) {
315       switch (type.base_type) {
316         case BASE_TYPE_INT:
317         case BASE_TYPE_UINT: return "bb.putInt";
318         case BASE_TYPE_SHORT:
319         case BASE_TYPE_USHORT: return "bb.putShort";
320         case BASE_TYPE_ULONG:
321         case BASE_TYPE_LONG: return "bb.putLong";
322         case BASE_TYPE_FLOAT: return "bb.putFloat";
323         case BASE_TYPE_DOUBLE: return "bb.putDouble";
324         case BASE_TYPE_CHAR:
325         case BASE_TYPE_UCHAR:
326         case BASE_TYPE_BOOL:
327         case BASE_TYPE_NONE:
328         case BASE_TYPE_UTYPE: return "bb.put";
329         default: return "bb.put" + MakeCamel(GenTypeBasic(type.base_type));
330       }
331     }
332     return "";
333   }
334 
335   // Returns the function name that is able to read a value of the given type.
GenLookupByKey(flatbuffers::FieldDef * key_field,const std::string & bb_var_name,const char * num=nullptr) const336   std::string GenLookupByKey(flatbuffers::FieldDef *key_field,
337                              const std::string &bb_var_name,
338                              const char *num = nullptr) const {
339     auto type = key_field->value.type;
340     return ByteBufferGetter(type, bb_var_name) + "(" +
341            GenOffsetGetter(key_field, num) + ")";
342   }
343 
344   // Returns the method name for use with add/put calls.
GenMethod(const Type & type)345   static std::string GenMethod(const Type &type) {
346     return IsScalar(type.base_type) ? ToSignedType(type)
347                                     : (IsStruct(type) ? "Struct" : "Offset");
348   }
349 
350   // Recursively generate arguments for a constructor, to deal with nested
351   // structs.
GenStructArgs(const StructDef & struct_def,CodeWriter & writer,const char * nameprefix)352   static void GenStructArgs(const StructDef &struct_def, CodeWriter &writer,
353                             const char *nameprefix) {
354     for (auto it = struct_def.fields.vec.begin();
355          it != struct_def.fields.vec.end(); ++it) {
356       auto &field = **it;
357       if (IsStruct(field.value.type)) {
358         // Generate arguments for a struct inside a struct. To ensure
359         // names don't clash, and to make it obvious these arguments are
360         // constructing a nested struct, prefix the name with the field
361         // name.
362         GenStructArgs(*field.value.type.struct_def, writer,
363                       (nameprefix + (field.name + "_")).c_str());
364       } else {
365         writer += std::string(", ") + nameprefix + "\\";
366         writer += MakeCamel(field.name) + ": \\";
367         writer += GenTypeBasic(field.value.type.base_type) + "\\";
368       }
369     }
370   }
371 
372   // Recusively generate struct construction statements of the form:
373   // builder.putType(name);
374   // and insert manual padding.
GenStructBody(const StructDef & struct_def,CodeWriter & writer,const char * nameprefix)375   static void GenStructBody(const StructDef &struct_def, CodeWriter &writer,
376                             const char *nameprefix) {
377     writer.SetValue("align", NumToString(struct_def.minalign));
378     writer.SetValue("size", NumToString(struct_def.bytesize));
379     writer += "builder.prep({{align}}, {{size}})";
380     auto fields_vec = struct_def.fields.vec;
381     for (auto it = fields_vec.rbegin(); it != fields_vec.rend(); ++it) {
382       auto &field = **it;
383 
384       if (field.padding) {
385         writer.SetValue("pad", NumToString(field.padding));
386         writer += "builder.pad({{pad}})";
387       }
388       if (IsStruct(field.value.type)) {
389         GenStructBody(*field.value.type.struct_def, writer,
390                       (nameprefix + (field.name + "_")).c_str());
391       } else {
392         writer.SetValue("type", GenMethod(field.value.type));
393         writer.SetValue("argname", nameprefix + MakeCamel(field.name, false));
394         writer.SetValue("cast", CastToSigned(field.value.type));
395         writer += "builder.put{{type}}({{argname}}{{cast}})";
396       }
397     }
398   }
399 
GenByteBufferLength(const char * bb_name) const400   std::string GenByteBufferLength(const char *bb_name) const {
401     std::string bb_len = bb_name;
402     bb_len += ".capacity()";
403     return bb_len;
404   }
405 
GenOffsetGetter(flatbuffers::FieldDef * key_field,const char * num=nullptr) const406   std::string GenOffsetGetter(flatbuffers::FieldDef *key_field,
407                               const char *num = nullptr) const {
408     std::string key_offset =
409         "__offset(" + NumToString(key_field->value.offset) + ", ";
410     if (num) {
411       key_offset += num;
412       key_offset += ", _bb)";
413     } else {
414       key_offset += GenByteBufferLength("bb");
415       key_offset += " - tableOffset, bb)";
416     }
417     return key_offset;
418   }
419 
GenStruct(StructDef & struct_def,CodeWriter & writer) const420   void GenStruct(StructDef &struct_def, CodeWriter &writer) const {
421     if (struct_def.generated) return;
422 
423     GenerateComment(struct_def.doc_comment, writer, &comment_config);
424     auto fixed = struct_def.fixed;
425 
426     writer.SetValue("struct_name", Esc(struct_def.name));
427     writer.SetValue("superclass", fixed ? "Struct" : "Table");
428 
429     writer += "@Suppress(\"unused\")";
430     writer += "@ExperimentalUnsignedTypes";
431     writer += "class {{struct_name}} : {{superclass}}() {\n";
432 
433     writer.IncrementIdentLevel();
434 
435     {
436       // Generate the __init() method that sets the field in a pre-existing
437       // accessor object. This is to allow object reuse.
438       GenerateFun(writer, "__init", "_i: Int, _bb: ByteBuffer", "",
439                   [&]() { writer += "__reset(_i, _bb)"; });
440 
441       // Generate assign method
442       GenerateFun(writer, "__assign", "_i: Int, _bb: ByteBuffer",
443                   Esc(struct_def.name), [&]() {
444                     writer += "__init(_i, _bb)";
445                     writer += "return this";
446                   });
447 
448       // Generate all getters
449       GenerateStructGetters(struct_def, writer);
450 
451       // Generate Static Fields
452       GenerateCompanionObject(writer, [&]() {
453         if (!struct_def.fixed) {
454           FieldDef *key_field = nullptr;
455 
456           // Generate verson check method.
457           // Force compile time error if not using the same version
458           // runtime.
459           GenerateFunOneLine(writer, "validateVersion", "", "", [&]() {
460             writer += "Constants.FLATBUFFERS_1_12_0()";
461           });
462 
463           GenerateGetRootAsAccessors(Esc(struct_def.name), writer);
464           GenerateBufferHasIdentifier(struct_def, writer);
465           GenerateTableCreator(struct_def, writer);
466 
467           GenerateStartStructMethod(struct_def, writer);
468 
469           // Static Add for fields
470           auto fields = struct_def.fields.vec;
471           int field_pos = -1;
472           for (auto it = fields.begin(); it != fields.end(); ++it) {
473             auto &field = **it;
474             field_pos++;
475             if (field.deprecated) continue;
476             if (field.key) key_field = &field;
477             GenerateAddField(NumToString(field_pos), field, writer);
478 
479             if (field.value.type.base_type == BASE_TYPE_VECTOR) {
480               auto vector_type = field.value.type.VectorType();
481               if (!IsStruct(vector_type)) {
482                 GenerateCreateVectorField(field, writer);
483               }
484               GenerateStartVectorField(field, writer);
485             }
486           }
487 
488           GenerateEndStructMethod(struct_def, writer);
489           auto file_identifier = parser_.file_identifier_;
490           if (parser_.root_struct_def_ == &struct_def) {
491             GenerateFinishStructBuffer(struct_def, file_identifier, writer);
492             GenerateFinishSizePrefixed(struct_def, file_identifier, writer);
493           }
494 
495           if (struct_def.has_key) {
496             GenerateLookupByKey(key_field, struct_def, writer);
497           }
498         } else {
499           GenerateStaticConstructor(struct_def, writer);
500         }
501       });
502     }
503 
504     // class closing
505     writer.DecrementIdentLevel();
506     writer += "}";
507   }
508 
509   // TODO: move key_field to reference instead of pointer
GenerateLookupByKey(FieldDef * key_field,StructDef & struct_def,CodeWriter & writer) const510   void GenerateLookupByKey(FieldDef *key_field, StructDef &struct_def,
511                            CodeWriter &writer) const {
512     std::stringstream params;
513     params << "obj: " << Esc(struct_def.name) << "?"
514            << ", ";
515     params << "vectorLocation: Int, ";
516     params << "key: " << GenTypeGet(key_field->value.type) << ", ";
517     params << "bb: ByteBuffer";
518 
519     auto statements = [&]() {
520       auto base_type = key_field->value.type.base_type;
521       writer.SetValue("struct_name", Esc(struct_def.name));
522       if (base_type == BASE_TYPE_STRING) {
523         writer +=
524             "val byteKey = key."
525             "toByteArray(java.nio.charset.StandardCharsets.UTF_8)";
526       }
527       writer += "var span = bb.getInt(vectorLocation - 4)";
528       writer += "var start = 0";
529       writer += "while (span != 0) {";
530       writer.IncrementIdentLevel();
531       writer += "var middle = span / 2";
532       writer +=
533           "val tableOffset = __indirect(vector"
534           "Location + 4 * (start + middle), bb)";
535       if (key_field->value.type.base_type == BASE_TYPE_STRING) {
536         writer += "val comp = compareStrings(\\";
537         writer += GenOffsetGetter(key_field) + "\\";
538         writer += ", byteKey, bb)";
539       } else {
540         auto cast = CastToUsigned(key_field->value.type);
541         auto get_val = GenLookupByKey(key_field, "bb");
542         writer += "val value = " + get_val + cast;
543         writer += "val comp = value.compareTo(key)";
544       }
545       writer += "when {";
546       writer.IncrementIdentLevel();
547       writer += "comp > 0 -> span = middle";
548       writer += "comp < 0 -> {";
549       writer.IncrementIdentLevel();
550       writer += "middle++";
551       writer += "start += middle";
552       writer += "span -= middle";
553       writer.DecrementIdentLevel();
554       writer += "}";  // end comp < 0
555       writer += "else -> {";
556       writer.IncrementIdentLevel();
557       writer += "return (obj ?: {{struct_name}}()).__assign(tableOffset, bb)";
558       writer.DecrementIdentLevel();
559       writer += "}";  // end else
560       writer.DecrementIdentLevel();
561       writer += "}";  // end when
562       writer.DecrementIdentLevel();
563       writer += "}";  // end while
564       writer += "return null";
565     };
566     GenerateFun(writer, "__lookup_by_key", params.str(),
567                 Esc(struct_def.name) + "?", statements);
568   }
569 
GenerateFinishSizePrefixed(StructDef & struct_def,const std::string & identifier,CodeWriter & writer) const570   void GenerateFinishSizePrefixed(StructDef &struct_def,
571                                   const std::string &identifier,
572                                   CodeWriter &writer) const {
573     auto id = identifier.length() > 0 ? ", \"" + identifier + "\"" : "";
574     auto params = "builder: FlatBufferBuilder, offset: Int";
575     auto method_name = "finishSizePrefixed" + Esc(struct_def.name) + "Buffer";
576     GenerateFunOneLine(writer, method_name, params, "", [&]() {
577       writer += "builder.finishSizePrefixed(offset" + id + ")";
578     });
579   }
GenerateFinishStructBuffer(StructDef & struct_def,const std::string & identifier,CodeWriter & writer) const580   void GenerateFinishStructBuffer(StructDef &struct_def,
581                                   const std::string &identifier,
582                                   CodeWriter &writer) const {
583     auto id = identifier.length() > 0 ? ", \"" + identifier + "\"" : "";
584     auto params = "builder: FlatBufferBuilder, offset: Int";
585     auto method_name = "finish" + Esc(struct_def.name) + "Buffer";
586     GenerateFunOneLine(writer, method_name, params, "",
587                        [&]() { writer += "builder.finish(offset" + id + ")"; });
588   }
589 
GenerateEndStructMethod(StructDef & struct_def,CodeWriter & writer) const590   void GenerateEndStructMethod(StructDef &struct_def,
591                                CodeWriter &writer) const {
592     // Generate end{{TableName}}(builder: FlatBufferBuilder) method
593     auto name = "end" + Esc(struct_def.name);
594     auto params = "builder: FlatBufferBuilder";
595     auto returns = "Int";
596     auto field_vec = struct_def.fields.vec;
597 
598     GenerateFun(writer, name, params, returns, [&]() {
599       writer += "val o = builder.endTable()";
600       writer.IncrementIdentLevel();
601       for (auto it = field_vec.begin(); it != field_vec.end(); ++it) {
602         auto &field = **it;
603         if (field.deprecated || !field.required) { continue; }
604         writer.SetValue("offset", NumToString(field.value.offset));
605         writer += "builder.required(o, {{offset}})";
606       }
607       writer.DecrementIdentLevel();
608       writer += "return o";
609     });
610   }
611 
612   // Generate a method to create a vector from a Kotlin array.
GenerateCreateVectorField(FieldDef & field,CodeWriter & writer) const613   void GenerateCreateVectorField(FieldDef &field, CodeWriter &writer) const {
614     auto vector_type = field.value.type.VectorType();
615     auto method_name = "create" + MakeCamel(Esc(field.name)) + "Vector";
616     auto params = "builder: FlatBufferBuilder, data: " +
617                   GenTypeBasic(vector_type.base_type) + "Array";
618     writer.SetValue("size", NumToString(InlineSize(vector_type)));
619     writer.SetValue("align", NumToString(InlineAlignment(vector_type)));
620     writer.SetValue("root", GenMethod(vector_type));
621     writer.SetValue("cast", CastToSigned(vector_type));
622 
623     GenerateFun(writer, method_name, params, "Int", [&]() {
624       writer += "builder.startVector({{size}}, data.size, {{align}})";
625       writer += "for (i in data.size - 1 downTo 0) {";
626       writer.IncrementIdentLevel();
627       writer += "builder.add{{root}}(data[i]{{cast}})";
628       writer.DecrementIdentLevel();
629       writer += "}";
630       writer += "return builder.endVector()";
631     });
632   }
633 
GenerateStartVectorField(FieldDef & field,CodeWriter & writer) const634   void GenerateStartVectorField(FieldDef &field, CodeWriter &writer) const {
635     // Generate a method to start a vector, data to be added manually
636     // after.
637     auto vector_type = field.value.type.VectorType();
638     auto params = "builder: FlatBufferBuilder, numElems: Int";
639     writer.SetValue("size", NumToString(InlineSize(vector_type)));
640     writer.SetValue("align", NumToString(InlineAlignment(vector_type)));
641 
642     GenerateFunOneLine(
643         writer, "start" + MakeCamel(Esc(field.name) + "Vector", true), params,
644         "", [&]() {
645           writer += "builder.startVector({{size}}, numElems, {{align}})";
646         });
647   }
648 
GenerateAddField(std::string field_pos,FieldDef & field,CodeWriter & writer) const649   void GenerateAddField(std::string field_pos, FieldDef &field,
650                         CodeWriter &writer) const {
651     auto field_type = GenTypeBasic(field.value.type.base_type);
652     auto secondArg = MakeCamel(Esc(field.name), false) + ": " + field_type;
653     GenerateFunOneLine(writer, "add" + MakeCamel(Esc(field.name), true),
654                        "builder: FlatBufferBuilder, " + secondArg, "", [&]() {
655                          auto method = GenMethod(field.value.type);
656                          writer.SetValue("field_name",
657                                          MakeCamel(Esc(field.name), false));
658                          writer.SetValue("method_name", method);
659                          writer.SetValue("pos", field_pos);
660                          writer.SetValue("default", GenFBBDefaultValue(field));
661                          writer.SetValue("cast", GenFBBValueCast(field));
662 
663                          writer += "builder.add{{method_name}}({{pos}}, \\";
664                          writer += "{{field_name}}{{cast}}, {{default}})";
665                        });
666   }
667 
ToSignedType(const Type & type)668   static std::string ToSignedType(const Type &type) {
669     switch (type.base_type) {
670       case BASE_TYPE_UINT: return GenTypeBasic(BASE_TYPE_INT);
671       case BASE_TYPE_ULONG: return GenTypeBasic(BASE_TYPE_LONG);
672       case BASE_TYPE_UCHAR:
673       case BASE_TYPE_NONE:
674       case BASE_TYPE_UTYPE: return GenTypeBasic(BASE_TYPE_CHAR);
675       case BASE_TYPE_USHORT: return GenTypeBasic(BASE_TYPE_SHORT);
676       case BASE_TYPE_VECTOR: return ToSignedType(type.VectorType());
677       default: return GenTypeBasic(type.base_type);
678     }
679   }
680 
FlexBufferBuilderCast(const std::string & method,FieldDef & field,bool isFirst)681   static std::string FlexBufferBuilderCast(const std::string &method,
682                                            FieldDef &field, bool isFirst) {
683     auto field_type = GenTypeBasic(field.value.type.base_type);
684     std::string to_type;
685     if (method == "Boolean")
686       to_type = "Boolean";
687     else if (method == "Long")
688       to_type = "Long";
689     else if (method == "Int" || method == "Offset" || method == "Struct")
690       to_type = "Int";
691     else if (method == "Byte" || method.empty())
692       to_type = isFirst ? "Byte" : "Int";
693     else if (method == "Short")
694       to_type = isFirst ? "Short" : "Int";
695     else if (method == "Double")
696       to_type = "Double";
697     else if (method == "Float")
698       to_type = isFirst ? "Float" : "Double";
699     else if (method == "UByte")
700 
701       if (field_type != to_type) return ".to" + to_type + "()";
702     return "";
703   }
704 
705   // fun startMonster(builder: FlatBufferBuilder) = builder.startTable(11)
GenerateStartStructMethod(StructDef & struct_def,CodeWriter & code) const706   void GenerateStartStructMethod(StructDef &struct_def,
707                                  CodeWriter &code) const {
708     GenerateFunOneLine(code, "start" + Esc(struct_def.name),
709                        "builder: FlatBufferBuilder", "", [&]() {
710                          code += "builder.startTable(" +
711                                  NumToString(struct_def.fields.vec.size()) +
712                                  ")";
713                        });
714   }
715 
GenerateTableCreator(StructDef & struct_def,CodeWriter & writer) const716   void GenerateTableCreator(StructDef &struct_def, CodeWriter &writer) const {
717     // Generate a method that creates a table in one go. This is only possible
718     // when the table has no struct fields, since those have to be created
719     // inline, and there's no way to do so in Java.
720     bool has_no_struct_fields = true;
721     int num_fields = 0;
722     auto fields_vec = struct_def.fields.vec;
723 
724     for (auto it = fields_vec.begin(); it != fields_vec.end(); ++it) {
725       auto &field = **it;
726       if (field.deprecated) continue;
727       if (IsStruct(field.value.type)) {
728         has_no_struct_fields = false;
729       } else {
730         num_fields++;
731       }
732     }
733     // JVM specifications restrict default constructor params to be < 255.
734     // Longs and doubles take up 2 units, so we set the limit to be < 127.
735     if (has_no_struct_fields && num_fields && num_fields < 127) {
736       // Generate a table constructor of the form:
737       // public static int createName(FlatBufferBuilder builder, args...)
738 
739       auto name = "create" + Esc(struct_def.name);
740       std::stringstream params;
741       params << "builder: FlatBufferBuilder";
742       for (auto it = fields_vec.begin(); it != fields_vec.end(); ++it) {
743         auto &field = **it;
744         if (field.deprecated) continue;
745         params << ", " << MakeCamel(Esc(field.name), false);
746         if (!IsScalar(field.value.type.base_type)) {
747           params << "Offset: ";
748         } else {
749           params << ": ";
750         }
751         params << GenTypeBasic(field.value.type.base_type);
752       }
753 
754       GenerateFun(writer, name, params.str(), "Int", [&]() {
755         writer.SetValue("vec_size", NumToString(fields_vec.size()));
756 
757         writer += "builder.startTable({{vec_size}})";
758 
759         auto sortbysize = struct_def.sortbysize;
760         auto largest = sortbysize ? sizeof(largest_scalar_t) : 1;
761         for (size_t size = largest; size; size /= 2) {
762           for (auto it = fields_vec.rbegin(); it != fields_vec.rend(); ++it) {
763             auto &field = **it;
764             auto base_type_size = SizeOf(field.value.type.base_type);
765             if (!field.deprecated && (!sortbysize || size == base_type_size)) {
766               writer.SetValue("camel_field_name",
767                               MakeCamel(Esc(field.name), true));
768               writer.SetValue("field_name", MakeCamel(Esc(field.name), false));
769 
770               writer += "add{{camel_field_name}}(builder, {{field_name}}\\";
771               if (!IsScalar(field.value.type.base_type)) {
772                 writer += "Offset\\";
773               }
774               writer += ")";
775             }
776           }
777         }
778         writer += "return end{{struct_name}}(builder)";
779       });
780     }
781   }
GenerateBufferHasIdentifier(StructDef & struct_def,CodeWriter & writer) const782   void GenerateBufferHasIdentifier(StructDef &struct_def,
783                                    CodeWriter &writer) const {
784     auto file_identifier = parser_.file_identifier_;
785     // Check if a buffer has the identifier.
786     if (parser_.root_struct_def_ != &struct_def || !file_identifier.length())
787       return;
788     auto name = MakeCamel(Esc(struct_def.name), false);
789     GenerateFunOneLine(writer, name + "BufferHasIdentifier", "_bb: ByteBuffer",
790                        "Boolean", [&]() {
791                          writer += "__has_identifier(_bb, \"" +
792                                    file_identifier + "\")";
793                        });
794   }
795 
GenerateStructGetters(StructDef & struct_def,CodeWriter & writer) const796   void GenerateStructGetters(StructDef &struct_def, CodeWriter &writer) const {
797     auto fields_vec = struct_def.fields.vec;
798     FieldDef *key_field = nullptr;
799     for (auto it = fields_vec.begin(); it != fields_vec.end(); ++it) {
800       auto &field = **it;
801       if (field.deprecated) continue;
802       if (field.key) key_field = &field;
803 
804       GenerateComment(field.doc_comment, writer, &comment_config);
805 
806       auto field_name = MakeCamel(Esc(field.name), false);
807       auto field_type = GenTypeGet(field.value.type);
808       auto field_default_value = GenDefaultValue(field);
809       auto return_type = GenTypeGet(field.value.type);
810       auto bbgetter = ByteBufferGetter(field.value.type, "bb");
811       auto ucast = CastToUsigned(field);
812       auto offset_val = NumToString(field.value.offset);
813       auto offset_prefix =
814           "val o = __offset(" + offset_val + "); return o != 0 ? ";
815       auto value_base_type = field.value.type.base_type;
816       // Most field accessors need to retrieve and test the field offset
817       // first, this is the offset value for that:
818       writer.SetValue("offset", NumToString(field.value.offset));
819       writer.SetValue("return_type", return_type);
820       writer.SetValue("field_type", field_type);
821       writer.SetValue("field_name", field_name);
822       writer.SetValue("field_default", field_default_value);
823       writer.SetValue("bbgetter", bbgetter);
824       writer.SetValue("ucast", ucast);
825 
826       auto opt_ret_type = return_type + "?";
827       // Generate the accessors that don't do object reuse.
828       if (value_base_type == BASE_TYPE_STRUCT) {
829         // Calls the accessor that takes an accessor object with a
830         // new object.
831         // val pos
832         //     get() = pos(Vec3())
833         GenerateGetterOneLine(writer, field_name, opt_ret_type, [&]() {
834           writer += "{{field_name}}({{field_type}}())";
835         });
836       } else if (value_base_type == BASE_TYPE_VECTOR &&
837                  field.value.type.element == BASE_TYPE_STRUCT) {
838         // Accessors for vectors of structs also take accessor objects,
839         // this generates a variant without that argument.
840         // ex: fun weapons(j: Int) = weapons(Weapon(), j)
841         GenerateFunOneLine(writer, field_name, "j: Int", opt_ret_type, [&]() {
842           writer += "{{field_name}}({{return_type}}(), j)";
843         });
844       }
845 
846       if (IsScalar(value_base_type)) {
847         if (struct_def.fixed) {
848           GenerateGetterOneLine(writer, field_name, return_type, [&]() {
849             writer += "{{bbgetter}}(bb_pos + {{offset}}){{ucast}}";
850           });
851         } else {
852           GenerateGetter(writer, field_name, return_type, [&]() {
853             writer += "val o = __offset({{offset}})";
854             writer +=
855                 "return if(o != 0) {{bbgetter}}"
856                 "(o + bb_pos){{ucast}} else "
857                 "{{field_default}}";
858           });
859         }
860       } else {
861         switch (value_base_type) {
862           case BASE_TYPE_STRUCT:
863             if (struct_def.fixed) {
864               // create getter with object reuse
865               // ex:
866               // fun pos(obj: Vec3) : Vec3? = obj.__assign(bb_pos + 4, bb)
867               // ? adds nullability annotation
868               GenerateFunOneLine(
869                   writer, field_name, "obj: " + field_type, return_type + "?",
870                   [&]() { writer += "obj.__assign(bb_pos + {{offset}}, bb)"; });
871             } else {
872               // create getter with object reuse
873               // ex:
874               //  fun pos(obj: Vec3) : Vec3? {
875               //      val o = __offset(4)
876               //      return if(o != 0) {
877               //          obj.__assign(o + bb_pos, bb)
878               //      else {
879               //          null
880               //      }
881               //  }
882               // ? adds nullability annotation
883               GenerateFun(
884                   writer, field_name, "obj: " + field_type, return_type + "?",
885                   [&]() {
886                     auto fixed = field.value.type.struct_def->fixed;
887 
888                     writer.SetValue("seek", Indirect("o + bb_pos", fixed));
889                     OffsetWrapper(
890                         writer, offset_val,
891                         [&]() { writer += "obj.__assign({{seek}}, bb)"; },
892                         [&]() { writer += "null"; });
893                   });
894             }
895             break;
896           case BASE_TYPE_STRING:
897             // create string getter
898             // e.g.
899             // val Name : String?
900             //     get() = {
901             //         val o = __offset(10)
902             //         return if (o != 0) __string(o + bb_pos) else null
903             //     }
904             // ? adds nullability annotation
905             GenerateGetter(writer, field_name, return_type + "?", [&]() {
906               writer += "val o = __offset({{offset}})";
907               writer += "return if (o != 0) __string(o + bb_pos) else null";
908             });
909             break;
910           case BASE_TYPE_VECTOR: {
911             // e.g.
912             // fun inventory(j: Int) : UByte {
913             //     val o = __offset(14)
914             //     return if (o != 0) {
915             //         bb.get(__vector(o) + j * 1).toUByte()
916             //     } else {
917             //        0
918             //     }
919             // }
920 
921             auto vectortype = field.value.type.VectorType();
922             std::string params = "j: Int";
923             std::string nullable = IsScalar(vectortype.base_type) ? "" : "?";
924 
925             if (vectortype.base_type == BASE_TYPE_STRUCT ||
926                 vectortype.base_type == BASE_TYPE_UNION) {
927               params = "obj: " + field_type + ", j: Int";
928             }
929 
930             auto ret_type = return_type + nullable;
931             GenerateFun(writer, field_name, params, ret_type, [&]() {
932               auto inline_size = NumToString(InlineSize(vectortype));
933               auto index = "__vector(o) + j * " + inline_size;
934               auto not_found = NotFoundReturn(field.value.type.element);
935               auto found = "";
936               writer.SetValue("index", index);
937               switch (vectortype.base_type) {
938                 case BASE_TYPE_STRUCT: {
939                   bool fixed = vectortype.struct_def->fixed;
940                   writer.SetValue("index", Indirect(index, fixed));
941                   found = "obj.__assign({{index}}, bb)";
942                   break;
943                 }
944                 case BASE_TYPE_UNION:
945                   found = "{{bbgetter}}(obj, {{index}} - bb_pos){{ucast}}";
946                   break;
947                 default: found = "{{bbgetter}}({{index}}){{ucast}}";
948               }
949               OffsetWrapper(
950                   writer, offset_val, [&]() { writer += found; },
951                   [&]() { writer += not_found; });
952             });
953             break;
954           }
955           case BASE_TYPE_UNION:
956             GenerateFun(writer, field_name, "obj: " + field_type,
957                         return_type + "?", [&]() {
958                           writer += OffsetWrapperOneLine(
959                               offset_val, bbgetter + "(obj, o + bb_pos)",
960                               "null");
961                         });
962             break;
963           default: FLATBUFFERS_ASSERT(0);
964         }
965       }
966 
967       if (value_base_type == BASE_TYPE_VECTOR) {
968         // Generate Lenght functions for vectors
969         GenerateGetter(writer, field_name + "Length", "Int", [&]() {
970           writer += OffsetWrapperOneLine(offset_val, "__vector_len(o)", "0");
971         });
972 
973         // See if we should generate a by-key accessor.
974         if (field.value.type.element == BASE_TYPE_STRUCT &&
975             !field.value.type.struct_def->fixed) {
976           auto &sd = *field.value.type.struct_def;
977           auto &fields = sd.fields.vec;
978           for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
979             auto &kfield = **kit;
980             if (kfield.key) {
981               auto qualified_name = WrapInNameSpace(sd);
982               auto name = MakeCamel(Esc(field.name), false) + "ByKey";
983               auto params = "key: " + GenTypeGet(kfield.value.type);
984               auto rtype = qualified_name + "?";
985               GenerateFun(writer, name, params, rtype, [&]() {
986                 OffsetWrapper(
987                     writer, offset_val,
988                     [&]() {
989                       writer += qualified_name +
990                                 ".__lookup_by_key(null, __vector(o), key, bb)";
991                     },
992                     [&]() { writer += "null"; });
993               });
994 
995               auto param2 = "obj: " + qualified_name +
996                             ", key: " + GenTypeGet(kfield.value.type);
997               GenerateFun(writer, name, param2, rtype, [&]() {
998                 OffsetWrapper(
999                     writer, offset_val,
1000                     [&]() {
1001                       writer += qualified_name +
1002                                 ".__lookup_by_key(obj, __vector(o), key, bb)";
1003                     },
1004                     [&]() { writer += "null"; });
1005               });
1006 
1007               break;
1008             }
1009           }
1010         }
1011       }
1012 
1013       if ((value_base_type == BASE_TYPE_VECTOR &&
1014            IsScalar(field.value.type.VectorType().base_type)) ||
1015           value_base_type == BASE_TYPE_STRING) {
1016         auto end_idx =
1017             NumToString(value_base_type == BASE_TYPE_STRING
1018                             ? 1
1019                             : InlineSize(field.value.type.VectorType()));
1020         // Generate a ByteBuffer accessor for strings & vectors of scalars.
1021         // e.g.
1022         // val inventoryByteBuffer: ByteBuffer
1023         //     get =  __vector_as_bytebuffer(14, 1)
1024 
1025         GenerateGetterOneLine(
1026             writer, field_name + "AsByteBuffer", "ByteBuffer", [&]() {
1027               writer.SetValue("end", end_idx);
1028               writer += "__vector_as_bytebuffer({{offset}}, {{end}})";
1029             });
1030 
1031         // Generate a ByteBuffer accessor for strings & vectors of scalars.
1032         // e.g.
1033         // fun inventoryInByteBuffer(_bb: Bytebuffer):
1034         //     ByteBuffer = __vector_as_bytebuffer(_bb, 14, 1)
1035         GenerateFunOneLine(
1036             writer, field_name + "InByteBuffer", "_bb: ByteBuffer",
1037             "ByteBuffer", [&]() {
1038               writer.SetValue("end", end_idx);
1039               writer += "__vector_in_bytebuffer(_bb, {{offset}}, {{end}})";
1040             });
1041       }
1042 
1043       // generate object accessors if is nested_flatbuffer
1044       // fun testnestedflatbufferAsMonster() : Monster?
1045       //{ return testnestedflatbufferAsMonster(new Monster()); }
1046 
1047       if (field.nested_flatbuffer) {
1048         auto nested_type_name = WrapInNameSpace(*field.nested_flatbuffer);
1049         auto nested_method_name =
1050             field_name + "As" + field.nested_flatbuffer->name;
1051 
1052         GenerateGetterOneLine(
1053             writer, nested_method_name, nested_type_name + "?", [&]() {
1054               writer += nested_method_name + "(" + nested_type_name + "())";
1055             });
1056 
1057         GenerateFun(writer, nested_method_name, "obj: " + nested_type_name,
1058                     nested_type_name + "?", [&]() {
1059                       OffsetWrapper(
1060                           writer, offset_val,
1061                           [&]() {
1062                             writer +=
1063                                 "obj.__assign(__indirect(__vector(o)), bb)";
1064                           },
1065                           [&]() { writer += "null"; });
1066                     });
1067       }
1068 
1069       // Generate mutators for scalar fields or vectors of scalars.
1070       if (parser_.opts.mutable_buffer) {
1071         auto value_type = field.value.type;
1072         auto underlying_type = value_base_type == BASE_TYPE_VECTOR
1073                                    ? value_type.VectorType()
1074                                    : value_type;
1075         auto name = "mutate" + MakeCamel(Esc(field.name), true);
1076         auto size = NumToString(InlineSize(underlying_type));
1077         auto params = Esc(field.name) + ": " + GenTypeGet(underlying_type);
1078         // A vector mutator also needs the index of the vector element it should
1079         // mutate.
1080         if (value_base_type == BASE_TYPE_VECTOR) params.insert(0, "j: Int, ");
1081 
1082         // Boolean parameters have to be explicitly converted to byte
1083         // representation.
1084         auto setter_parameter =
1085             underlying_type.base_type == BASE_TYPE_BOOL
1086                 ? "(if(" + Esc(field.name) + ") 1 else 0).toByte()"
1087                 : Esc(field.name);
1088 
1089         auto setter_index =
1090             value_base_type == BASE_TYPE_VECTOR
1091                 ? "__vector(o) + j * " + size
1092                 : (struct_def.fixed ? "bb_pos + " + offset_val : "o + bb_pos");
1093         if (IsScalar(value_base_type) ||
1094             (value_base_type == BASE_TYPE_VECTOR &&
1095              IsScalar(value_type.VectorType().base_type))) {
1096           auto statements = [&]() {
1097             writer.SetValue("bbsetter", ByteBufferSetter(underlying_type));
1098             writer.SetValue("index", setter_index);
1099             writer.SetValue("params", setter_parameter);
1100             writer.SetValue("cast", CastToSigned(field));
1101             if (struct_def.fixed) {
1102               writer += "{{bbsetter}}({{index}}, {{params}}{{cast}})";
1103             } else {
1104               OffsetWrapper(
1105                   writer, offset_val,
1106                   [&]() {
1107                     writer += "{{bbsetter}}({{index}}, {{params}}{{cast}})";
1108                     writer += "true";
1109                   },
1110                   [&]() { writer += "false"; });
1111             }
1112           };
1113 
1114           if (struct_def.fixed) {
1115             GenerateFunOneLine(writer, name, params, "ByteBuffer", statements);
1116           } else {
1117             GenerateFun(writer, name, params, "Boolean", statements);
1118           }
1119         }
1120       }
1121     }
1122     if (struct_def.has_key && !struct_def.fixed) {
1123       // Key Comparison method
1124       GenerateOverrideFun(
1125           writer, "keysCompare", "o1: Int, o2: Int, _bb: ByteBuffer", "Int",
1126           [&]() {
1127             if (key_field->value.type.base_type == BASE_TYPE_STRING) {
1128               writer.SetValue("offset", NumToString(key_field->value.offset));
1129               writer +=
1130                   " return compareStrings(__offset({{offset}}, o1, "
1131                   "_bb), __offset({{offset}}, o2, _bb), _bb)";
1132 
1133             } else {
1134               auto getter1 = GenLookupByKey(key_field, "_bb", "o1");
1135               auto getter2 = GenLookupByKey(key_field, "_bb", "o2");
1136               writer += "val val_1 = " + getter1;
1137               writer += "val val_2 = " + getter2;
1138               writer += "return (val_1 - val_2).sign";
1139             }
1140           });
1141     }
1142   }
1143 
CastToUsigned(const FieldDef & field)1144   static std::string CastToUsigned(const FieldDef &field) {
1145     return CastToUsigned(field.value.type);
1146   }
1147 
CastToUsigned(const Type type)1148   static std::string CastToUsigned(const Type type) {
1149     switch (type.base_type) {
1150       case BASE_TYPE_UINT: return ".toUInt()";
1151       case BASE_TYPE_UCHAR:
1152       case BASE_TYPE_UTYPE: return ".toUByte()";
1153       case BASE_TYPE_USHORT: return ".toUShort()";
1154       case BASE_TYPE_ULONG: return ".toULong()";
1155       case BASE_TYPE_VECTOR: return CastToUsigned(type.VectorType());
1156       default: return "";
1157     }
1158   }
1159 
CastToSigned(const FieldDef & field)1160   static std::string CastToSigned(const FieldDef &field) {
1161     return CastToSigned(field.value.type);
1162   }
1163 
CastToSigned(const Type type)1164   static std::string CastToSigned(const Type type) {
1165     switch (type.base_type) {
1166       case BASE_TYPE_UINT: return ".toInt()";
1167       case BASE_TYPE_UCHAR:
1168       case BASE_TYPE_UTYPE: return ".toByte()";
1169       case BASE_TYPE_USHORT: return ".toShort()";
1170       case BASE_TYPE_ULONG: return ".toLong()";
1171       case BASE_TYPE_VECTOR: return CastToSigned(type.VectorType());
1172       default: return "";
1173     }
1174   }
1175 
LiteralSuffix(const BaseType type)1176   static std::string LiteralSuffix(const BaseType type) {
1177     switch (type) {
1178       case BASE_TYPE_UINT:
1179       case BASE_TYPE_UCHAR:
1180       case BASE_TYPE_UTYPE:
1181       case BASE_TYPE_USHORT: return "u";
1182       case BASE_TYPE_ULONG: return "UL";
1183       case BASE_TYPE_LONG: return "L";
1184       default: return "";
1185     }
1186   }
1187 
GenerateCompanionObject(CodeWriter & code,const std::function<void ()> & callback) const1188   void GenerateCompanionObject(CodeWriter &code,
1189                                const std::function<void()> &callback) const {
1190     code += "companion object {";
1191     code.IncrementIdentLevel();
1192     callback();
1193     code.DecrementIdentLevel();
1194     code += "}";
1195   }
1196 
1197   // Generate a documentation comment, if available.
GenerateComment(const std::vector<std::string> & dc,CodeWriter & writer,const CommentConfig * config) const1198   void GenerateComment(const std::vector<std::string> &dc, CodeWriter &writer,
1199                        const CommentConfig *config) const {
1200     if (dc.begin() == dc.end()) {
1201       // Don't output empty comment blocks with 0 lines of comment content.
1202       return;
1203     }
1204 
1205     if (config != nullptr && config->first_line != nullptr) {
1206       writer += std::string(config->first_line);
1207     }
1208     std::string line_prefix =
1209         ((config != nullptr && config->content_line_prefix != nullptr)
1210              ? config->content_line_prefix
1211              : "///");
1212     for (auto it = dc.begin(); it != dc.end(); ++it) {
1213       writer += line_prefix + *it;
1214     }
1215     if (config != nullptr && config->last_line != nullptr) {
1216       writer += std::string(config->last_line);
1217     }
1218   }
1219 
GenerateGetRootAsAccessors(const std::string & struct_name,CodeWriter & writer)1220   static void GenerateGetRootAsAccessors(const std::string &struct_name,
1221                                          CodeWriter &writer) {
1222     // Generate a special accessor for the table that when used as the root
1223     // ex: fun getRootAsMonster(_bb: ByteBuffer): Monster {...}
1224     writer.SetValue("gr_name", struct_name);
1225     writer.SetValue("gr_method", "getRootAs" + struct_name);
1226 
1227     // create convenience method that doesn't require an existing object
1228     writer += "fun {{gr_method}}(_bb: ByteBuffer): {{gr_name}} = \\";
1229     writer += "{{gr_method}}(_bb, {{gr_name}}())";
1230 
1231     // create method that allows object reuse
1232     // ex: fun Monster getRootAsMonster(_bb: ByteBuffer, obj: Monster) {...}
1233     writer +=
1234         "fun {{gr_method}}"
1235         "(_bb: ByteBuffer, obj: {{gr_name}}): {{gr_name}} {";
1236     writer.IncrementIdentLevel();
1237     writer += "_bb.order(ByteOrder.LITTLE_ENDIAN)";
1238     writer +=
1239         "return (obj.__assign(_bb.getInt(_bb.position())"
1240         " + _bb.position(), _bb))";
1241     writer.DecrementIdentLevel();
1242     writer += "}";
1243   }
1244 
GenerateStaticConstructor(const StructDef & struct_def,CodeWriter & code)1245   static void GenerateStaticConstructor(const StructDef &struct_def,
1246                                         CodeWriter &code) {
1247     // create a struct constructor function
1248     auto params = StructConstructorParams(struct_def);
1249     GenerateFun(code, "create" + Esc(struct_def.name), params, "Int", [&]() {
1250       GenStructBody(struct_def, code, "");
1251       code += "return builder.offset()";
1252     });
1253   }
1254 
StructConstructorParams(const StructDef & struct_def,const std::string & prefix="")1255   static std::string StructConstructorParams(const StructDef &struct_def,
1256                                              const std::string &prefix = "") {
1257     // builder: FlatBufferBuilder
1258     std::stringstream out;
1259     auto field_vec = struct_def.fields.vec;
1260     if (prefix.empty()) { out << "builder: FlatBufferBuilder"; }
1261     for (auto it = field_vec.begin(); it != field_vec.end(); ++it) {
1262       auto &field = **it;
1263       if (IsStruct(field.value.type)) {
1264         // Generate arguments for a struct inside a struct. To ensure
1265         // names don't clash, and to make it obvious these arguments are
1266         // constructing a nested struct, prefix the name with the field
1267         // name.
1268         out << StructConstructorParams(*field.value.type.struct_def,
1269                                        prefix + (Esc(field.name) + "_"));
1270       } else {
1271         out << ", " << prefix << MakeCamel(Esc(field.name), false) << ": "
1272             << GenTypeBasic(field.value.type.base_type);
1273       }
1274     }
1275     return out.str();
1276   }
1277 
GeneratePropertyOneLine(CodeWriter & writer,const std::string & name,const std::string & type,const std::function<void ()> & body)1278   static void GeneratePropertyOneLine(CodeWriter &writer,
1279                                       const std::string &name,
1280                                       const std::string &type,
1281                                       const std::function<void()> &body) {
1282     // Generates Kotlin getter for properties
1283     // e.g.:
1284     // val prop: Mytype = x
1285     writer.SetValue("_name", name);
1286     writer.SetValue("_type", type);
1287     writer += "val {{_name}} : {{_type}} = \\";
1288     body();
1289   }
GenerateGetterOneLine(CodeWriter & writer,const std::string & name,const std::string & type,const std::function<void ()> & body)1290   static void GenerateGetterOneLine(CodeWriter &writer, const std::string &name,
1291                                     const std::string &type,
1292                                     const std::function<void()> &body) {
1293     // Generates Kotlin getter for properties
1294     // e.g.:
1295     // val prop: Mytype get() = x
1296     writer.SetValue("_name", name);
1297     writer.SetValue("_type", type);
1298     writer += "val {{_name}} : {{_type}} get() = \\";
1299     body();
1300   }
1301 
GenerateGetter(CodeWriter & writer,const std::string & name,const std::string & type,const std::function<void ()> & body)1302   static void GenerateGetter(CodeWriter &writer, const std::string &name,
1303                              const std::string &type,
1304                              const std::function<void()> &body) {
1305     // Generates Kotlin getter for properties
1306     // e.g.:
1307     // val prop: Mytype
1308     //     get() = {
1309     //       return x
1310     //     }
1311     writer.SetValue("name", name);
1312     writer.SetValue("type", type);
1313     writer += "val {{name}} : {{type}}";
1314     writer.IncrementIdentLevel();
1315     writer += "get() {";
1316     writer.IncrementIdentLevel();
1317     body();
1318     writer.DecrementIdentLevel();
1319     writer += "}";
1320     writer.DecrementIdentLevel();
1321   }
1322 
GenerateFun(CodeWriter & writer,const std::string & name,const std::string & params,const std::string & returnType,const std::function<void ()> & body)1323   static void GenerateFun(CodeWriter &writer, const std::string &name,
1324                           const std::string &params,
1325                           const std::string &returnType,
1326                           const std::function<void()> &body) {
1327     // Generates Kotlin function
1328     // e.g.:
1329     // fun path(j: Int): Vec3 {
1330     //     return path(Vec3(), j)
1331     // }
1332     auto noreturn = returnType.empty();
1333     writer.SetValue("name", name);
1334     writer.SetValue("params", params);
1335     writer.SetValue("return_type", noreturn ? "" : ": " + returnType);
1336     writer += "fun {{name}}({{params}}) {{return_type}} {";
1337     writer.IncrementIdentLevel();
1338     body();
1339     writer.DecrementIdentLevel();
1340     writer += "}";
1341   }
1342 
GenerateFunOneLine(CodeWriter & writer,const std::string & name,const std::string & params,const std::string & returnType,const std::function<void ()> & body)1343   static void GenerateFunOneLine(CodeWriter &writer, const std::string &name,
1344                                  const std::string &params,
1345                                  const std::string &returnType,
1346                                  const std::function<void()> &body) {
1347     // Generates Kotlin function
1348     // e.g.:
1349     // fun path(j: Int): Vec3 = return path(Vec3(), j)
1350     writer.SetValue("name", name);
1351     writer.SetValue("params", params);
1352     writer.SetValue("return_type_p",
1353                     returnType.empty() ? "" : " : " + returnType);
1354     writer += "fun {{name}}({{params}}){{return_type_p}} = \\";
1355     body();
1356   }
1357 
GenerateOverrideFun(CodeWriter & writer,const std::string & name,const std::string & params,const std::string & returnType,const std::function<void ()> & body)1358   static void GenerateOverrideFun(CodeWriter &writer, const std::string &name,
1359                                   const std::string &params,
1360                                   const std::string &returnType,
1361                                   const std::function<void()> &body) {
1362     // Generates Kotlin function
1363     // e.g.:
1364     // override fun path(j: Int): Vec3 = return path(Vec3(), j)
1365     writer += "override \\";
1366     GenerateFun(writer, name, params, returnType, body);
1367   }
1368 
GenerateOverrideFunOneLine(CodeWriter & writer,const std::string & name,const std::string & params,const std::string & returnType,const std::string & statement)1369   static void GenerateOverrideFunOneLine(CodeWriter &writer,
1370                                          const std::string &name,
1371                                          const std::string &params,
1372                                          const std::string &returnType,
1373                                          const std::string &statement) {
1374     // Generates Kotlin function
1375     // e.g.:
1376     // override fun path(j: Int): Vec3 = return path(Vec3(), j)
1377     writer.SetValue("name", name);
1378     writer.SetValue("params", params);
1379     writer.SetValue("return_type",
1380                     returnType.empty() ? "" : " : " + returnType);
1381     writer += "override fun {{name}}({{params}}){{return_type}} = \\";
1382     writer += statement;
1383   }
1384 
OffsetWrapperOneLine(const std::string & offset,const std::string & found,const std::string & not_found)1385   static std::string OffsetWrapperOneLine(const std::string &offset,
1386                                           const std::string &found,
1387                                           const std::string &not_found) {
1388     return "val o = __offset(" + offset + "); return if (o != 0) " + found +
1389            " else " + not_found;
1390   }
1391 
OffsetWrapper(CodeWriter & code,const std::string & offset,const std::function<void ()> & found,const std::function<void ()> & not_found)1392   static void OffsetWrapper(CodeWriter &code, const std::string &offset,
1393                             const std::function<void()> &found,
1394                             const std::function<void()> &not_found) {
1395     code += "val o = __offset(" + offset + ")";
1396     code += "return if (o != 0) {";
1397     code.IncrementIdentLevel();
1398     found();
1399     code.DecrementIdentLevel();
1400     code += "} else {";
1401     code.IncrementIdentLevel();
1402     not_found();
1403     code.DecrementIdentLevel();
1404     code += "}";
1405   }
1406 
Indirect(const std::string & index,bool fixed)1407   static std::string Indirect(const std::string &index, bool fixed) {
1408     // We apply __indirect() and struct is not fixed.
1409     if (!fixed) return "__indirect(" + index + ")";
1410     return index;
1411   }
1412 
NotFoundReturn(BaseType el)1413   static std::string NotFoundReturn(BaseType el) {
1414     switch (el) {
1415       case BASE_TYPE_FLOAT: return "0.0f";
1416       case BASE_TYPE_DOUBLE: return "0.0";
1417       case BASE_TYPE_BOOL: return "false";
1418       case BASE_TYPE_LONG:
1419       case BASE_TYPE_INT:
1420       case BASE_TYPE_CHAR:
1421       case BASE_TYPE_SHORT: return "0";
1422       case BASE_TYPE_UINT:
1423       case BASE_TYPE_UCHAR:
1424       case BASE_TYPE_USHORT:
1425       case BASE_TYPE_UTYPE: return "0u";
1426       case BASE_TYPE_ULONG: return "0uL";
1427       default: return "null";
1428     }
1429   }
1430 
1431   // This tracks the current namespace used to determine if a type need to be
1432   // prefixed by its namespace
1433   const Namespace *cur_name_space_;
1434 };
1435 }  // namespace kotlin
1436 
GenerateKotlin(const Parser & parser,const std::string & path,const std::string & file_name)1437 bool GenerateKotlin(const Parser &parser, const std::string &path,
1438                     const std::string &file_name) {
1439   kotlin::KotlinGenerator generator(parser, path, file_name);
1440   return generator.generate();
1441 }
1442 }  // namespace flatbuffers
1443