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